-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathutils.lua.1
More file actions
296 lines (282 loc) · 8.43 KB
/
utils.lua.1
File metadata and controls
296 lines (282 loc) · 8.43 KB
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
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
local Common = ... -- State, tempdir, name, cfgpath, O
local function mload (profile, key)
local s, err = mf.mload("jd", Common.name)
if err then return nil, err end
assert(type(s)=="table", "Error in settings")
if not profile then return s end
s = s[profile]
if s and key then
assert(type(s)=="table", "Error in profile")
return s[key]
end
return s
end
local function msave (profile, key, value)
local s
if type(profile)~="string" then
assert(type(profile)=="table")
s = profile
else
s = mf.mload("jd", Common.name) or {}
if type(key)~="string" then
assert(key==nil or type(key)=="table")
s[profile] = key
else
local p = s[assert(profile)]
if not p then
p = {}
s[profile] = p
end
p[key] = value
end
end
assert(mf.msave("jd", Common.name, s), "Error writing settings")
end
local path_sep = package.config:sub(1,1)
local function pathjoin (...) --same as win.JoinPath since Luafar 857
local args = {...}
assert(#args==select("#",...), "arg cannot be nil")
return table.concat(args, path_sep)
end
local function check (key)
repeat
local k = win.ExtractKeyEx()
if k and far.InputRecordToName(k)==key then return true end
until not k
end
local openUrl; do
if win.ShellExecute then
function openUrl (url)
win.ShellExecute(nil,nil,url)
end
else
for _,fname in ipairs{"xdg-open","wslview","open"} do
if 0==os.execute("which "..fname) then
function openUrl (url)
os.execute(("%s '%s'"):format(fname,url))
end
break
end
end
openUrl = openUrl or 0==os.execute("which cmd.exe") and function(url)
os.execute(('cmd.exe /c start "" "%s"'):format(url:gsub("&","^&")))
end or function(url,fallback)
if fallback then fallback(url) end
far.CopyToClipboard(url)
local handle = far.SaveScreen()
far.Message(url, "Copied to clipboard")
far.RestoreScreen(handle)
end
end
end
local function loadLua (filename, env)
if not (env and getmetatable(env)) then
env = setmetatable(env or {}, {__index=_G})
end
local pathname = filename:match(path_sep) and filename --presets
or pathjoin(Common.cfgpath, filename)
local fn = assert(loadfile(pathname))
return setfenv(fn, env)
end
local function loadMoon (filename, env)
if not (env and getmetatable(env)) then
env = setmetatable(env or {}, {__index=_G})
end
local pathname = pathjoin(Common.cfgpath, filename)
return assert(require"moonscript".loadfile(pathname,nil,env,nil))
end
local cache = {}
local function cached (fn)
if fn=="clear" then
cache = {}; return
end
return function (key, ...)
if not cache[key] then
cache[key] = fn(key,...)
end
return cache[key]
end
end
local function HelpTopic (name)
return ("<%s%s>%s"):format(Common.cfgpath, path_sep, name)
end
local HK = {
finish=function(self)
self.finished = true
end,
iter=function(self,skip)
if skip or self.finished then return self.spc end
self.i = self.i+1
if self.i==36 then self.finished = true end
local _hk = self.i==10 and "0" or self.i<10 and self.i or string.char(self.i + string.byte"a" - 11)
_hk = "&".._hk
return _hk..self.fmt
end,
}
local _hkmt = { __index=HK }
function HK.new (fmt)
return setmetatable({ i=0, fmt=fmt, spc=string.rep(" ",fmt:len()+1) }, _hkmt)
end
local readHistory, cleanHistory
if far.CreateSettings then
function readHistory (name)
--https://forum.farmanager.com/viewtopic.php?t=13407
local hSettings = assert(far.CreateSettings("far"), "Error accessing setings")
local subkey = hSettings:OpenSubkey(0,name)
local historyArr = assert(hSettings:Enum(subkey), "Error enumerating settings")
hSettings:Free()
local history = {}
for _,item in ipairs(historyArr) do
history[#history+1] = item.Name
end
return history
end
function cleanHistory (profile)
if pcall(require, "lsqlite3") then
local sqlite3 = require "lsqlite3"
local db,_,errmsg = sqlite3.open(pathjoin(win.GetEnv"FARLOCALPROFILE", "history.db"), sqlite3.OPEN_READWRITE)
if not db then return nil,errmsg end
db:exec(("DELETE FROM history WHERE kind = 3 AND key LIKE 'AskAI:%s %%';"):format(profile))
db:close()
end
end
else -- far2m
local function splitV1 (s,lines)
local pos = 1
local length = #s
while pos<=length do
local nlStart,nlEnd = string.find(s, '\n', pos)
lines[#lines+1] = string.sub(s, pos, (nlStart or 0)-1)
if not nlStart then break end
pos = nlEnd+1
end
end
local function splitV2 (s,lines)
local pos = 1
local length = #s
while pos<=length do
local _,lenEnd,len = string.find(s, "^%[(%d+)%]", pos)
if not len then break end --invalid
local lineStart = lenEnd+1
local lineEnd = lineStart+tonumber(len)-1
lines[#lines+1] = string.sub(s, lineStart, lineEnd)
pos = lineEnd+1
end
end
local splitLines = {["1"]=splitV1, ["2"]=splitV2}
local unescape = {
--https://github.com/shmuz/far2m/blob/b770d1fd61cfe703d3c68b2b8da94feefced0ad5/utils/src/KeyFileHelper.cpp#L169
["\\r"]="\r",
["\\n"]="\n",
["\\t"]="\t",
["\\0"]="\0",
["\\\\"]="\\",
}
local historypath = far.InMyConfig("history/dialogs.hst") --luacheck: globals far.InMyConfig
function readHistory (name)
local file = assert(io.open(historypath, "r"))
if file then
local history = {}
local section = "[SavedDialogHistory/"..name.."]"
for line in file:lines() do
if line==section then
local Lines,Version
repeat
local nextLine = file:read()
if not nextLine or #nextLine==0 then break end
Lines = Lines or nextLine:match"^Lines=(.+)"
Version = Version or nextLine:match"^Version=(.+)"
until Lines and Version
if Lines then
local quoted = Lines:match'^"(.+)"$'
Lines = quoted and quoted:gsub("\\.", unescape) or Lines
splitLines[Version or "1"](Lines, history)
end
break
end
end
file:close()
return history
end
end
function cleanHistory (profile)
local file,errmsg = io.open(historypath, "r")
if not file then return nil,errmsg end
local tmppath = far.InMyConfig("history/dialogs.hst.tmp") --luacheck: globals far.InMyConfig
local tmpfile,errmsg2 = io.open(tmppath, "w")
if not tmpfile then
file:close()
return nil,errmsg2
end
local section = "[SavedDialogHistory/AskAI:"..profile.." "
local len = #section
local skip
for line in file:lines() do
if string.sub(line,1,len)==section then
skip = true
elseif skip then
skip = #line~=0
else
tmpfile:write(line.."\n")
end
end
file:close(); tmpfile:close()
win.MoveFile(tmppath,historypath,"r")
end
end
local F = far.Flags
local function openOutput (outputFilename, mode)
local CP = 65001
local curModal = bit64.band(actl.GetWindowInfo().Flags, F.WIF_MODAL)==F.WIF_MODAL
local opened
for i=actl.GetWindowCount(),1,-1 do
local wi = actl.GetWindowInfo(i)
if wi.Type==F.WTYPE_EDITOR and wi.Name==outputFilename then
opened = true
if curModal then
editor.SaveFile(wi.Id)
editor.Quit(wi.Id)
end
break
end
end
if mode=="existing" then
if not (opened or win.GetFileAttr(outputFilename)) then
mf.beep(); return
end
elseif not opened then
win.DeleteFile(outputFilename)
end
local res
if not curModal then
local tryNotModal = F.EF_DISABLEHISTORY +F.EF_NONMODAL +F.EF_IMMEDIATERETURN +F.EF_OPENMODE_USEEXISTING
res = editor.Editor(outputFilename, nil, nil, nil, nil, nil, tryNotModal, nil, nil, CP)
end
if curModal or res==F.EEC_LOADING_INTERRUPTED then
editor.Editor(outputFilename, nil, nil, nil, nil, nil, F.EF_DISABLEHISTORY +F.EF_OPENMODE_NEWIFOPEN, nil, nil, CP)
end
end
--make use of actl.Synchro after Luafar 859
local synchro = pcall(actl.Synchro, function()end) and actl.Synchro or function(fn, ...)
far.Timer(1, function(t, ...)
t:Close()
fn(...)
end, ...)
end
return {
mload=mload,
msave=msave,
pathjoin=pathjoin,
cached=cached,
load=cached(function(filename, ...) return loadLua(filename)(Common,...) end),
loadLua=loadLua,
loadMoon=loadMoon,
HelpTopic=HelpTopic,
HK=HK,
synchro=synchro,
readHistory=readHistory,
cleanHistory=cleanHistory,
check=check,
openUrl=openUrl,
openOutput=openOutput,
}