-
Notifications
You must be signed in to change notification settings - Fork 7
/
tilde_autoexpand.lua
196 lines (168 loc) · 6.71 KB
/
tilde_autoexpand.lua
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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
--------------------------------------------------------------------------------
-- When the tilde.autoexpand setting is enabled, any tilde (~) by itself or any
-- tilde and path separator (~\) at the beginning of a word is expanded to the
-- user's home directory.
--
-- The tilde.escape setting specifies a string that is always expanded to a
-- single tilde after tilde expansion has been applied.
--
-- The color.autoexpandtilde setting is the color for tildes that will be
-- automatically expanded into the user's home directory.
--
-- The color.escapetilde setting is the color for the tilde escape string that
-- is always expanded to a single tilde after tilde expansion has been applied.
--
-- Unlink Readline's tilde expansion, this properly respects quotes:
-- - This works inside quoted words such as "~\foo".
-- - This properly only expands tilde at the beginning of a quoted word such
-- as "foo ~\bar" or "~\foo ~\bar".
--------------------------------------------------------------------------------
-- Version check.
if not clink.onfilterinput then
print("tilde_autoexpand.lua requires a newer version of Clink; please upgrade.")
return
end
--------------------------------------------------------------------------------
-- Settings.
settings.add("tilde.autoexpand", false, "Automatically expand ~ to home directory",
"When enabled, a tilde (~) by itself or a tilde and path separator (~\\) at\n"..
"the beginning of a word is expanded to the user's home directory.")
settings.add("tilde.escape", "~~", "Replace this with ~ after autoexpand",
"This string is replaced by ~ after expanding tildes in the input line.")
settings.add("color.autoexpandtilde", "bri cya", "Color for home dir tilde")
settings.add("color.escapetilde", "bri cya on blu", "Color for escape for plain tilde")
--------------------------------------------------------------------------------
-- Helper functions.
local tilde_escape = {}
local function ensure_escape()
local s = settings.get("tilde.escape")
if s == "" then
s = nil
end
if s ~= tilde_escape.setting then
if s then
tilde_escape.setting = s
local pat = ""
for i = 1, #s do
local b = s:byte(i)
if not (b >= 65 and b <= 90) and not (b >= 97 and b <= 122) then
pat = pat .. "%"
end
pat = pat .. s:sub(i, i)
end
tilde_escape.pattern = pat
else
tilde_escape.setting = nil
tilde_escape.pattern = nil
end
end
return tilde_escape.pattern
end
local function apply_escape(line)
local pat = ensure_escape()
if pat then
line = line:gsub(pat, "~")
end
return line
end
--------------------------------------------------------------------------------
-- Input filter function, to expand tildes.
local tilde_autoexpand
if clink.parseline then
local function need_quote(word)
return word and word:find("[ &()[%]{}^=;!%'+,`~]") and true
end
local function maybe_quote(word)
if need_quote(word) then
word = '"' .. word .. '"'
end
return word
end
tilde_autoexpand = function (line)
if settings.get("tilde.autoexpand") and line:find("~") then
local out = ""
local commands = clink.parseline(line)
local next = 1
local expanded
for _, c in ipairs(commands) do
local ls = c.line_state
for i = 1, ls:getwordcount() do
local info = ls:getwordinfo(i)
local word = line:sub(info.offset, info.offset + info.length - 1)
word, expanded = rl.expandtilde(word)
if expanded and not info.quoted then
word = maybe_quote(word)
end
out = out .. line:sub(next, info.offset - 1) .. word
next = info.offset + info.length
end
end
out = out .. line:sub(next, #line)
return apply_escape(out)
end
end
else
tilde_autoexpand = function (line)
if settings.get("tilde.autoexpand") and line:find("~") then
local expanded = rl.expandtilde(line)
return apply_escape(expanded)
end
end
end
clink.onfilterinput(tilde_autoexpand)
--------------------------------------------------------------------------------
-- Input line coloring for tilde expansion.
if clink.classifier then
local clf = clink.classifier(999)
function clf:classify(commands) -- luacheck: no unused
if not settings.get("tilde.autoexpand") then
return
end
-- Color tildes that will expand.
local color = settings.get("color.autoexpandtilde")
if color and color ~= "" then
for _, c in ipairs(commands) do
local ls = c.line_state
local first_word = true
for i = 1, ls:getwordcount() do
local word = ls:getword(i)
local info = ls:getwordinfo(i)
if word:sub(1, 1) == "~" then
local expanded
word, expanded = rl.expandtilde(word)
if expanded then
if first_word and not info.redir and clink.recognizecommand then
word = apply_escape(word)
local cl = clink.recognizecommand(ls:getline(), word, info.quoted)
if cl then
local m = clink.getargmatcher(word) and "m" or ""
c.classifications:classifyword(i, m..cl, true)
end
end
c.classifications:applycolor(info.offset, 1, color)
end
end
first_word = false
end
end
end
-- Color escapes that will turn into tildes.
local pat = ensure_escape()
if pat then
local i = 1
local line = commands[1] and commands[1].line_state:getline() or ""
local classifications = commands[1] and commands[1].classifications
color = settings.get("color.escapetilde")
if color and color ~= "" then
while true do
local s, e = line:find(pat, i)
if not s then
break
end
classifications:applycolor(s, e + 1 - s, color)
i = e + 1
end
end
end
end
end