diff options
Diffstat (limited to 'pkgs/profpatsch')
-rw-r--r-- | pkgs/profpatsch/xdg-open/config.dhall | 414 | ||||
-rw-r--r-- | pkgs/profpatsch/xdg-open/default.nix | 4 | ||||
-rw-r--r-- | pkgs/profpatsch/xdg-open/types.dhall | 50 | ||||
-rw-r--r-- | pkgs/profpatsch/xdg-open/xdg-open.dhall | 124 |
4 files changed, 374 insertions, 218 deletions
diff --git a/pkgs/profpatsch/xdg-open/config.dhall b/pkgs/profpatsch/xdg-open/config.dhall index a3586dda..45ef45d1 100644 --- a/pkgs/profpatsch/xdg-open/config.dhall +++ b/pkgs/profpatsch/xdg-open/config.dhall @@ -1,161 +1,261 @@ -let Mime = List Text - -let - -- TODO use library like with shell commands - Executable = - Text - -let Arg = < String : Text | Variable : Text > - -let CommandTemplate = - λ(templates : Type) → { exe : Executable, args : templates → List Arg } - -let Command = CommandTemplate Arg - -let mime = - { text = - { html = [ "text", "html" ] - , gemini = [ "text", "gemini" ] - , gopher = [ "text", "gopher" ] - , xml = [ "text", "xml" ] - , any = [ "text", "*" ] - } - , mail-address = [ "special", "mailaddress" ] - , torrent = [ "application", "x-bittorrent" ] - , irc = [ "x-scheme-handler", "irc" ] - , file = [ "x-scheme-handler", "file" ] - , image = - { gif = [ "image", "gif" ] - , svg = [ "image", "svg+xml" ] - , any = [ "image", "*" ] - } - , pdf = [ "application", "pdf" ] - , pgp-key = [ "application", "pgp-keys" ] - , directory = [ "inode", "directory" ] - , any = [ "*" ] - } - -let renderMime = λ(m : Mime) → 32 - -let - -- Handler of an uri glob. Mime maps the uri to a file handler. Transparent is a command which, when run, returns a mimetype of the file. - UriGlobHandler = - < Transparent : Command | Mime : Mime > - -let UriMimeGlob = { desc : Text, glob : List Text, handler : UriGlobHandler } - -let Special = - { open-in-editor : Command - , open-in-browser : Command - , fetch-http-url-mime : Command - , compose-mail-to : Command - , exec-in-terminal-emulator : ∀(args : Command) → Command - , dmenu-list-binaries-and-exec : Command - } - -let uriMimeGlobs - : Special → List UriMimeGlob - = λ(special : Special) → - [ { desc = "http link" - , glob = [ "http://*", "https://*" ] - , handler = - let TODO = UriGlobHandler.Transparent special.fetch-http-url-mime - - in UriGlobHandler.Mime mime.text.html - } - , { desc = "gemini link" - , glob = [ "gemini://*" ] - , handler = UriGlobHandler.Mime mime.text.gemini - } - , { desc = "gemini link" - , glob = [ "gopher://*", "gophers://*" ] - , handler = UriGlobHandler.Mime mime.text.gopher - } - , { glob = [ "mailto:*" ] - , desc = "mail address" - , handler = UriGlobHandler.Mime mime.mail-address - } - , { glob = [ "magnet:*" ] - , desc = "bittorrent magnet link" - , handler = UriGlobHandler.Mime mime.torrent - } - , { desc = "irc channel" - , glob = [ "irc:*" ] - , handler = UriGlobHandler.Mime mime.irc - } - , { desc = "local file" - , glob = [ "file://*" ] - , handler = UriGlobHandler.Mime mime.file - } - ] - -let MimeMatch = { match : Mime, cmd : Command } - -let mimeMatcher = - λ(pkgs : { package : Text, binary : Text } → Executable) → - λ(special : Special) → - let pkgSame = - λ(packageAndBinaryName : Text) → - pkgs - { package = packageAndBinaryName - , binary = packageAndBinaryName - } +let types = ./types.dhall - let wrapCommand = - λ(wrapper : Command) → - λ(cmd : Command) → - { exe = wrapper.exe - , args = - λ(template : Arg) → - wrapper.args template - # [ Arg.String cmd.exe ] - # cmd.args template - } +let Executable = types.Executable + +let Special = types.Special + +let Command = types.Command + +let Arg = types.Arg + +let Mime = types.Mime + +let UriGlobHandler = types.UriGlobHandler + +let UriMimeGlob = types.UriMimeGlob + +let MimeMatch = types.MimeMatch + +in λ(pkgs : { package : Text, binary : Text } → Executable) → + λ(special : Special) → + let mime = + let pkgSame = + λ(packageAndBinaryName : Text) → + pkgs + { package = packageAndBinaryName + , binary = packageAndBinaryName + } + + let wrapCommand = + λ(wrapper : Command) → + λ(cmd : Command) → + { exe = wrapper.exe + , args = + λ(template : Arg) → + wrapper.args template + # [ Arg.String cmd.exe ] + # cmd.args template + } + + let + -- An executable that takes as its one argument the file to run + oneArg = + λ(exe : Executable) → { exe, args = λ(file : Arg) → [ file ] } - let oneArg = - λ(exe : Executable) → { exe, args = λ(file : Arg) → [ file ] } - - let m = λ(match : Mime) → λ(cmd : Command) → { match, cmd } - - in [ { match = mime.mail-address, cmd = special.compose-mail-to } - , { match = mime.text.html, cmd = special.open-in-browser } - , { match = mime.text.gemini, cmd = oneArg (pkgSame "lagrange") } - , { match = mime.text.gopher, cmd = oneArg (pkgSame "lagrange") } - , { match = mime.text.xml, cmd = special.open-in-browser } - , { match = mime.text.any, cmd = special.open-in-editor } - , { match = mime.image.gif, cmd = special.open-in-browser } - , { match = mime.image.svg, cmd = oneArg (pkgSame "inkscape") } - , { match = mime.image.any, cmd = oneArg (pkgSame "imv") } - , { match = mime.pdf, cmd = oneArg (pkgSame "zathura") } - , { match = mime.pgp-key - , cmd = - { exe = pkgs { package = "gnupg", binary = "gpg" } - , args = - λ(file : Arg) → - [ Arg.String "--import" - , Arg.String "--import-options" - , Arg.String "show-only" - , file - ] + in { text = + { html = + { mime = [ "text", "html" ], cmd = special.compose-mail-to } + , gemini = + { mime = [ "text", "gemini" ] + , cmd = oneArg (pkgSame "lagrange") + } + , gopher = + { mime = [ "text", "gopher" ] + , cmd = oneArg (pkgSame "lagrange") + } + , xml = + { mime = [ "text", "xml" ], cmd = special.open-in-browser } + , any = + { mime = [ "text", "any" ], cmd = special.open-in-editor } } + , mail-address = + { mime = [ "special", "mailaddress" ] + , cmd = special.compose-mail-to + } + , torrent = + { mime = [ "application", "x-bittorrent" ] + , cmd = special.notify "No xdg-open handler for the torrent" + } + , irc = + { mime = [ "x-scheme-handler", "irc" ] + , cmd = special.notify "No xdg-open handler for the irc link" + } + , file = + { mime = [ "x-scheme-handler", "file" ] + , cmd = + special.notify + "No xdg-open handler for the x-scheme-handler/file" + } + , image = + { gif = + { mime = [ "image", "gif" ], cmd = special.open-in-browser } + , svg = + { mime = [ "image", "svg+xml" ] + , cmd = oneArg (pkgSame "inkscape") + } + , any = + { mime = [ "image", "*" ], cmd = oneArg (pkgSame "imv") } + } + , pdf = + { mime = [ "application", "pdf" ] + , cmd = oneArg (pkgSame "zathura") + } + , pgp-key = + { mime = [ "application", "pgp-keys" ] + , cmd = + { exe = pkgs { package = "gnupg", binary = "gpg" } + , args = + λ(file : Arg) → + [ Arg.String "--import" + , Arg.String "--import-options" + , Arg.String "show-only" + , file + ] + } + } + , directory = + { mime = [ "inode", "directory" ] + , cmd = + special.exec-in-terminal-emulator + (oneArg (pkgSame "ranger")) + } + , any = + { mime = [ "*" ], cmd = special.dmenu-list-binaries-and-exec } } - , { match = mime.directory - , cmd = - special.exec-in-terminal-emulator - (oneArg (pkgSame "ranger")) - } - , { match = mime.any, cmd = special.dmenu-list-binaries-and-exec } - ] - : List MimeMatch - -in { mimeMatcher - , uriMimeGlobs - , UriMimeGlob - , Executable - , Command - , UriGlobHandler - , MimeMatch - , Special - , Mime - , Arg - } + + let matchOrder = + [ mime.text.html + , mime.text.gemini + , mime.text.gopher + , mime.text.xml + , mime.text.any + , mime.mail-address + , mime.torrent + , mime.irc + , mime.file + , mime.image.gif + , mime.image.svg + , mime.image.any + , mime.pdf + , mime.pgp-key + , mime.directory + , mime.any + ] + + let uriMimeGlobs + : List UriMimeGlob + = [ { desc = "http link" + , glob = [ "http://*", "https://*" ] + , schema-prefix = [ "http", "https" ] + , handler = + let TODO = + UriGlobHandler.Transparent special.fetch-http-url-mime + + in UriGlobHandler.Mime mime.text.html.mime + } + , { desc = "gemini link" + , glob = [ "gemini://*" ] + , schema-prefix = [ "gemini" ] + , handler = UriGlobHandler.Mime mime.text.gemini.mime + } + , { desc = "gemini link" + , glob = [ "gopher://*", "gophers://*" ] + , schema-prefix = [ "gopher", "gophers" ] + , handler = UriGlobHandler.Mime mime.text.gopher.mime + } + , { glob = [ "mailto:*" ] + , desc = "mail address" + , schema-prefix = [ "mailto" ] + , handler = UriGlobHandler.Mime mime.mail-address.mime + } + , { glob = [ "magnet:*" ] + , desc = "bittorrent magnet link" + , schema-prefix = [ "magnet" ] + , handler = UriGlobHandler.Mime mime.torrent.mime + } + , { desc = "irc channel" + , glob = [ "irc:*", "ircs:*" ] + , schema-prefix = [ "irc", "ircs" ] + , handler = UriGlobHandler.Mime mime.irc.mime + } + , { desc = "local file" + , glob = [ "file://*" ] + , schema-prefix = [ "file" ] + , handler = UriGlobHandler.Mime mime.file.mime + } + ] + + let mimeMatcher = + let pkgSame = + λ(packageAndBinaryName : Text) → + pkgs + { package = packageAndBinaryName + , binary = packageAndBinaryName + } + + let wrapCommand = + λ(wrapper : Command) → + λ(cmd : Command) → + { exe = wrapper.exe + , args = + λ(template : Arg) → + wrapper.args template + # [ Arg.String cmd.exe ] + # cmd.args template + } + + let oneArg = + λ(exe : Executable) → { exe, args = λ(file : Arg) → [ file ] } + + let m = λ(match : Mime) → λ(cmd : Command) → { match, cmd } + + in [ { match = mime.mail-address.mime + , cmd = special.compose-mail-to + } + , { match = mime.text.html.mime + , cmd = special.open-in-browser + } + , { match = mime.text.gemini.mime + , cmd = oneArg (pkgSame "lagrange") + } + , { match = mime.text.gopher.mime + , cmd = oneArg (pkgSame "lagrange") + } + , { match = mime.text.xml.mime + , cmd = special.open-in-browser + } + , { match = mime.text.any.mime, cmd = special.open-in-editor } + , { match = mime.image.gif.mime + , cmd = special.open-in-browser + } + , { match = mime.image.svg.mime + , cmd = oneArg (pkgSame "inkscape") + } + , { match = mime.image.any.mime + , cmd = oneArg (pkgSame "imv") + } + , { match = mime.pdf.mime, cmd = oneArg (pkgSame "zathura") } + , { match = mime.pgp-key.mime + , cmd = + { exe = pkgs { package = "gnupg", binary = "gpg" } + , args = + λ(file : Arg) → + [ Arg.String "--import" + , Arg.String "--import-options" + , Arg.String "show-only" + , file + ] + } + } + , { match = mime.directory.mime + , cmd = + special.exec-in-terminal-emulator + (oneArg (pkgSame "ranger")) + } + , { match = mime.any.mime + , cmd = special.dmenu-list-binaries-and-exec + } + ] + : List MimeMatch + + in { mimeMatcher + , uriMimeGlobs + , UriMimeGlob + , Executable + , Command + , UriGlobHandler + , MimeMatch + , Special + , Mime + , Arg + } diff --git a/pkgs/profpatsch/xdg-open/default.nix b/pkgs/profpatsch/xdg-open/default.nix index 89114b92..2b265318 100644 --- a/pkgs/profpatsch/xdg-open/default.nix +++ b/pkgs/profpatsch/xdg-open/default.nix @@ -54,6 +54,8 @@ let args = file: [ file ]; }; + + open-in-browser = { exe = bins.firefox; args = file: [ file ]; @@ -117,6 +119,7 @@ let , fetch-http-url-mime : Command , open-in-browser : Command , open-in-editor : Command + , notify : Text -> Command } ) → Text @@ -125,6 +128,7 @@ let main = "xdg-open.dhall"; files = [ "config.dhall" + "types.dhall" "imports/Prelude/Text/concatSep" "imports/Prelude/Text/concatMap" "imports/Prelude/Text/concat" diff --git a/pkgs/profpatsch/xdg-open/types.dhall b/pkgs/profpatsch/xdg-open/types.dhall new file mode 100644 index 00000000..74e55d32 --- /dev/null +++ b/pkgs/profpatsch/xdg-open/types.dhall @@ -0,0 +1,50 @@ +let Mime = List Text + +let + -- TODO use library like with shell commands + Executable = + Text + +let Arg = < String : Text | Variable : Text > + +let CommandTemplate = + λ(templates : Type) → { exe : Executable, args : templates → List Arg } + +let Command = CommandTemplate Arg + +let Special = + { open-in-editor : Command + , open-in-browser : Command + , fetch-http-url-mime : Command + , compose-mail-to : Command + , exec-in-terminal-emulator : ∀(args : Command) → Command + , dmenu-list-binaries-and-exec : Command + , notify : ∀(message : Text) → Command + } + +let + -- Handler of an uri glob. Mime maps the uri to a file handler. Transparent is a command which, when run, returns a mimetype of the file. + UriGlobHandler = + < Transparent : Command | Mime : Mime > + +let UriMimeGlob = + { desc : Text + , -- less specific than glob, used by firefox to refer to the schema + schema-prefix : List Text + , -- schema shell glob to check whether a link corresponds to the schema + glob : List Text + , handler : UriGlobHandler + } + +let MimeMatch = { match : Mime, cmd : Command } + +in { Mime + , Executable + , Arg + , CommandTemplate + , Command + , Special + , UriGlobHandler + , UriMimeGlob + , MimeMatch + } diff --git a/pkgs/profpatsch/xdg-open/xdg-open.dhall b/pkgs/profpatsch/xdg-open/xdg-open.dhall index 30cf45e0..5090f8f1 100644 --- a/pkgs/profpatsch/xdg-open/xdg-open.dhall +++ b/pkgs/profpatsch/xdg-open/xdg-open.dhall @@ -11,28 +11,28 @@ let Executable = Text -let config = ./config.dhall +let types = ./types.dhall let renderMime = Text/concatSep "/" let shellEscapeCommand = λ(shellEscape : Text → Text) → λ(file : Text) → - λ(cmd : config.Command) → + λ(cmd : types.Command) → Text/concatSep " " ( [ shellEscape cmd.exe ] # List/map - config.Arg + types.Arg Text - ( λ(arg : config.Arg) → + ( λ(arg : types.Arg) → merge { String = λ(t : Text) → shellEscape t , Variable = λ(t : Text) → t } arg ) - (cmd.args (config.Arg.Variable file)) + (cmd.args (types.Arg.Variable file)) ) : Text @@ -54,7 +54,7 @@ let xdg-open = let mimeMatcherCase = λ(shellEscape2 : Text → Text) → λ(file2 : Text) → - λ(m : config.MimeMatch) → + λ(m : types.MimeMatch) → [ "${renderMime m.match})" , "${shellEscapeCommand shellEscape2 file2 m.cmd}" , ";;" @@ -63,17 +63,17 @@ let xdg-open = let mimeGlobCase = λ(shellEscape2 : Text → Text) → λ(file2 : Text) → - λ(g : config.UriMimeGlob) → + λ(g : types.UriMimeGlob) → List/concatMap Text Text ( λ(match : Text) → merge { Mime = - λ(mime : config.Mime) → + λ(mime : types.Mime) → [ "${match})", "mime=${renderMime mime}", ";;" ] , Transparent = - λ(cmd : config.Command) → + λ(cmd : types.Command) → [ "${match})" , "mime=\"\$(${shellEscapeCommand shellEscape2 @@ -91,57 +91,59 @@ let xdg-open = λ(write-dash : Text → Text → Executable) → λ(shellEscape : Text → Text) → λ(pkgs : { package : Text, binary : Text } → Executable) → - λ(special : config.Special) → - write-dash - "xdg-open" - ( '' - # TODO: --dry-run to display what would be opened and why - - # partially taken from - # https://github.com/march-linux/mimi/blob/master/xdg-open - - set -e - file="$1" - mime= - - # match on protocols - # if you want to match files reliably, start with file:// - case "$file" in - ${prettyLines - { indent = 2 - , lines = - List/concatMap - config.UriMimeGlob - Text - (mimeGlobCase shellEscape "\"\$file\"") - (config.uriMimeGlobs special) - }} - *) - # it’s a file - - # strip possible protocol - file='' - ++ "\$" - ++ '' - {file#file://} - mime=$(file -E --brief --mime-type "$file") \ - || (echo "$mime" 1>&2; exit 1) - # ^ echo the error message of file - ;; - esac - - case "$mime" in - ${prettyLines - { indent = 2 - , lines = - List/concatMap - config.MimeMatch - Text - (mimeMatcherCase shellEscape "\"\$file\"") - (config.mimeMatcher pkgs special) - }} - esac - '' - ) + λ(special : types.Special) → + let config = ./config.dhall pkgs special + + in write-dash + "xdg-open" + ( '' + # TODO: --dry-run to display what would be opened and why + + # partially taken from + # https://github.com/march-linux/mimi/blob/master/xdg-open + + set -e + file="$1" + mime= + + # match on protocols + # if you want to match files reliably, start with file:// + case "$file" in + ${prettyLines + { indent = 2 + , lines = + List/concatMap + types.UriMimeGlob + Text + (mimeGlobCase shellEscape "\"\$file\"") + config.uriMimeGlobs + }} + *) + # it’s a file + + # strip possible protocol + file='' + ++ "\$" + ++ '' + {file#file://} + mime=$(file -E --brief --mime-type "$file") \ + || (echo "$mime" 1>&2; exit 1) + # ^ echo the error message of file + ;; + esac + + case "$mime" in + ${prettyLines + { indent = 2 + , lines = + List/concatMap + types.MimeMatch + Text + (mimeMatcherCase shellEscape "\"\$file\"") + config.mimeMatcher + }} + esac + '' + ) in xdg-open |