1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
|
let Text/concatSep = ./imports/Prelude/Text/concatSep
let Text/concatMap = ./imports/Prelude/Text/concatMap
let List/concatMap = ./imports/Prelude/List/concatMap
let List/map = ./imports/Prelude/List/map
let
-- TODO use library like with shell commands
Executable =
Text
let types = ./types.dhall
let renderMime = Text/concatSep "/"
let
-- Escape the given shell command, at least the String arguments of it.
-- Passes `$file` as variable argument.
-- The final shell command is executed into.
shellEscapeExecCommand =
λ(shellEscape : Text → Text) →
λ(file : Text) →
λ(cmd : types.Command) →
Text/concatSep
" "
( [ "exec", shellEscape cmd.exe ]
# List/map
types.Arg
Text
( λ(arg : types.Arg) →
merge
{ String = λ(t : Text) → shellEscape t
, Variable = λ(t : Text) → t
}
arg
)
(cmd.args (types.Arg.Variable file))
)
: Text
let repeatText =
λ(t : Text) →
λ(n : Natural) →
Natural/fold n Text (λ(t2 : Text) → t ++ t2) ""
let Lines = { indent : Natural, lines : List Text }
let prettyLines =
λ(lines : Lines) →
Text/concatMap
Text
(λ(line : Text) → repeatText " " lines.indent ++ line ++ "\n")
lines.lines
let xdg-open =
let mimeMatcherCase =
λ(shellEscape2 : Text → Text) →
λ(file2 : Text) →
λ(m : types.MimeMatch) →
[ "${renderMime m.mime})"
, "${shellEscapeExecCommand shellEscape2 file2 m.cmd}"
, ";;"
]
let mimeGlobCase =
λ(shellEscape2 : Text → Text) →
λ(file2 : Text) →
λ(g : types.UriMimeGlob) →
List/concatMap
Text
Text
( λ(match : Text) →
[ "${match})"
, shellEscapeExecCommand shellEscape2 file2 g.handler.cmd
, ";;"
]
)
g.glob
: List Text
in λ(bins : { get-mime-type : Executable }) →
λ(write-dash : Text → Text → Executable) →
λ(shellEscape : Text → Text) →
λ(pkgs : { package : Text, binary : Text } → Executable) →
λ(pkgsOnDemand : { package : Text, binary : Text } → Executable) →
λ(special : types.Special) →
let config = ./config.dhall pkgs pkgsOnDemand special
in write-dash
"xdg-open"
( ''
# partially taken from
# https://github.com/march-linux/mimi/blob/master/xdg-open
set -e
file="$1"
mime=
# TODO: --dry-run to display what would be opened and why
notify-send --expire-time=500 -- "xdg-open: $1"
# 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.orderedMimeMatchers
}}
esac
''
)
in xdg-open
|