-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathconfigs.lua.1
More file actions
484 lines (458 loc) · 13.5 KB
/
configs.lua.1
File metadata and controls
484 lines (458 loc) · 13.5 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
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
local Common,openDialog = ... -- name, O, tempdir
local State = Common.State
local utils = Common.utils
local json = Common.json
local cfgpath = Common.cfgpath
local F = far.Flags
local _isWindows = package.config:sub(1,1)=="\\"
local function _quote (str)
return str and '"'..str..'"' or ""
end
local function _optional (...)
if select("#", ...)==1 then
return ... or ""
end
local key, value, arg = ...
if not value then return "" end
if arg then -- see bito.lua.cfg
if type(arg)=="function" then
value = arg(select(4, ...))
else--if arg~='"' then??
value = arg:format(value)
end
end
return key.." ".._quote(value)
end
local todel = {}
local function cleanup()
for _, pathname in ipairs(todel) do
win.DeleteFile(pathname)
end
todel = {}
end
local function writeFile (pathname, content)
local fp = assert(io.open(pathname, "w"))
fp:write(content)
fp:close()
end
local function _tmpfile (filename, content)
local pathname = utils.pathjoin(Common.tempdir, filename)
writeFile(pathname, content)
table.insert(todel, pathname)
return '"'..pathname..'"'
end
local function _pipeOut (exe, context)
return function (args)
args[0] = _quote(exe)
local cmd = table.concat(args, " ", 0)
if context then -- env
cmd = (_isWindows and "type %s|" or "cat %s|"):format(_tmpfile("file.input", context))..cmd
end
return function (cb)
if _isWindows then cmd = _quote(cmd) end
local pipe = io.popen(cmd.." 2>&1", "r")
repeat
if utils.check"Esc" then break end
local chunk = pipe:read(5)
if not chunk then break end
while not chunk:isvalid() do --ensure valid utf-8
local extra = pipe:read(1)
if extra then chunk = chunk..extra; else break; end
end
cb(chunk)
until false
pipe:close()
cleanup()
end
end
end
if pcall(require, "piper") then
local STILL_ACTIVE = 259
local _shell = win.GetEnv"ComSpec".." /c"
function _pipeOut (exe, context)
return function (args)
args[0] = _quote(exe)
local cmd = table.concat(args, " ", 0)
return function (cb)
local prc = require"piper"(_shell.._quote(cmd), {input=context})
if not prc then return end
if prc.ExitCode==STILL_ACTIVE or prc.ExitCode==0 then
for chunk in prc.utf8chunks do
if utils.check"Esc" then break end
cb(chunk)
end
cb()
else -- debug
far.Message("ExitCode: "..prc.ExitCode.."\n\1\n"..prc.all:gsub("\r\n","\n"), Common.name, nil, "wl")
end
cleanup()
end
end
end
end
local function isReachable (fname, source)
if fname==nil or type(fname)=="boolean" then return fname end
local _type = type(fname)
if _type~="string" then
far.Message("exe: string or boolean expected, got ".._type, source, nil, "w")
return
end
if not _isWindows then
return 0==os.execute("which "..fname)
end
for ext in win.GetEnv("PATHEXT"):gmatch"%.[^;]+" do
--https://learn.microsoft.com/en-us/windows/win32/api/processenv/nf-processenv-searchpathw
local filename = win.SearchPath(nil,fname,ext)
if filename then
return not win.GetFileAttr(filename):find"d"
end
end
end
local export
if Common.restapi then
local function getHistory (historyName)
State[historyName] = State[historyName] or {}
return State[historyName]
end
local function setHistory (historyName,t)
State[historyName] = t
end
local function boolean (str, default)
if type(str)=="number" then
return str==1
elseif type(str)=="boolean" then
return str
elseif str=="null" then
return nil
elseif str=="false" then
return false
else
return str=="true" or default
end
end
local function number (str)
return str and tonumber(str) or str
end
local function obj (str)
if type(str)=="string" then
local fn = require("moonscript").loadstring(str)
fn = fn or assert(loadstring("return "..str))
str = setfenv(fn,{})()
end
return str
end
local function wrapCode (code, codetype)
return ("```%s\n%s\n```"):format(codetype or "", code)
end
local function formatJson (data)
return wrapCode(json.pretty_encode(data), "json")
end
local function formatJsonMsg (data,getMsgText)
local msg = getMsgText and getMsgText(data)
if type(msg)=="string" then
if select(2, msg:gsub("\n","\n"))>2 then
msg = wrapCode(msg, "error")
end
msg = msg.."\n"
else
msg = ""
end
return msg..formatJson(data)
end
local function formatErrMsg (meta,sep,getMsgText)
local data = meta.data
if not data then
return meta.status_code or meta.statusline
elseif type(data)=="table" then
data = formatJsonMsg(data,getMsgText)
elseif meta.statusline:find(data,1,"plain") then
return meta.statusline
end
return table.concat({meta.statusline, data}, sep or "\n")
end
export = {
getHistory=getHistory,
setHistory=setHistory,
boolean=boolean,
number=number,
obj=obj,
formatJson=formatJson,
formatJsonMsg=formatJsonMsg,
formatErrMsg=formatErrMsg,
}
end
export = setmetatable(export or {}, {__index=Common})
local function after (choreFn, ...) choreFn(); return ... end
local loadCfg --fwd decl.
local function _import (filename, ...)
local cfg = loadCfg(utils.pathjoin(Common.cfgpath, filename), filename)
local env = getfenv(2) -- parent _ENV
local name = env.name
return after(function()
if name and name~=env.name then
env.name = ("%s [%s]"):format(name, env.name)
end
end, setfenv(cfg.sourceFn, env)(export, ...))
end
local function _readLines (args, filter, field)
return function (data) -- getModels
local args = args --luacheck: ignore 432/args
if type(args)=="function" then
local success
success, args = pcall(args,data)
if not success then return nil, args end
end
local pipe,err = io.popen(args)
if not pipe then return nil, err end
local list,state = {},{}
local function cb (model) table.insert(list,model) end
for line in pipe:lines() do
table.insert(list, not filter and line or filter(line,state,cb))
if state.finished then break end
end
pipe:close()
return list, field
end
end
local function clearSessionCmd (self, session)
win.system(self.clearSessionCmd:format(session))
end
local function clearSessionFile (self, session)
local pathname = self.sessionFile:format(session)
win.DeleteFile(pathname)
end
local function clearSessionHistory (self)
export.setHistory(self.historyName, nil)
end
local function clearSession (info)
if info.clearSession then
if type(info.clearSession)=="function" then
return info.clearSession
end
info.clearSessionCmd = info.clearSession
return clearSessionCmd
elseif info.sessionFile then
return clearSessionFile
elseif info.historyName then
return clearSessionHistory
end
end
local function showSessionState (self)
local historyName = self.historyName
if Common.O.debug then
historyName = historyName.."_debug"
end
local session = State[historyName]
if session and next(session) then
require"le"(session)
end
end
local function showSessionFile (self, session)
local pathname = self.sessionFile:format(session)
if win.GetFileAttr(pathname) then
editor.Editor(pathname)
end
end
local function showSession (info)
if info.historyName then
return showSessionState
elseif info.sessionFile then
return showSessionFile
end
end
local function sessionExists (self, session)
if self.sessionFile then
return win.GetFileAttr(self.sessionFile:format(session)) and true
elseif self.historyName then
local history = State[self.historyName]
return history and next(history) and true
end
return self.clearSession and true
end
local function assert (success, ...) -- to prevent "error object is not a string"
if success then return success, ... end
error(...==nil and "assertion failed!" or tostring(...), 2)
end
local mt = setmetatable({
assert=assert,
_import=_import,
_isWindows=_isWindows,
_optional=_optional,
_pathjoin=utils.pathjoin,
_pipeOut=_pipeOut,
_quote=_quote,
_readLines=_readLines,
_tmpfile=_tmpfile,
_env=win.GetEnv,
}, { __index=_G })
local function _loadCfg (pathname, filename)
local env = setmetatable({}, { __index=mt })
local fn = assert(loadfile(pathname))
local applyParamsFn, getModels, authFn = setfenv(fn, env)(export)
if not env.exe then
env.url = "https://github.com/FarManagerLegacy/LuaBinaries"
end
local function toMap (t) for _,v in ipairs(t) do t[v] = true end end
if env.serviceFields then
toMap(env.serviceFields)
if not Common.O.debug then
local function merge (dest,source) for _,v in ipairs(source) do table.insert(dest,v) end end
env.hidden = env.hidden or {}
merge(env.hidden, env.serviceFields)
end
end
if env.hidden then
toMap(env.hidden)
end
env.clearSession = clearSession(env)
env.showSession = env.showSession or showSession(env)
env.sessionExists = sessionExists
return {
applyParamsFn = applyParamsFn,
authFn = authFn,
getModels = getModels,
info = env,
filename = filename,
pathname = pathname,
reachable = isReachable(env.exe, pathname),
sourceFn=fn
}
end
loadCfg = utils.cached(_loadCfg)
local function toggleHidden (items)
for _,item in ipairs(items) do
if item.hidden~=nil then
item.hidden = not item.hidden
end
end
end
local function chooseCfg (opts)
local curcfgfile = type(opts.cfg)=="table" and opts.cfg.filename or opts.cfg
local items1,items2 = {visibleN=0}, {visibleN=0}
local empty = true
local hk = utils.HK.new(".")
far.RecursiveSearch(cfgpath, "*.lua.cfg>>D", function (file, pathname)
local success, cfg = pcall(loadCfg, pathname, file.FileName)
if not success then
far.Message(cfg,"Error loading "..file.FileName,nil,"wl")
return nil
end
empty = empty and not cfg.reachable
local items = type(cfg.info.exe)=="string" and items2 or items1
if cfg.reachable then items.visibleN = items.visibleN+1 end
table.insert(items, {
hidden = file.FileAttributes:find"h" or not cfg.reachable or nil,
grayed = not cfg.reachable,
selected = file.FileName==curcfgfile,
FileName = file.FileName,
pathname = pathname,
cfg = cfg,
})
return nil
end)
if #items1 + #items2 == 0 then
far.Message("No config found", Common.name, nil ,"w")
return
end
local function sortFn (a, b)
return a.FileName:lower() < b.FileName:lower()
end
table.sort(items1, sortFn)
table.sort(items2, sortFn)
local items = items1
local notBothGroupsPresent = items1.visibleN==0 or items2.visibleN==0 or nil
table.insert(items, {separator=true, hidden=notBothGroupsPresent, grayed=notBothGroupsPresent})
for _,item in ipairs(items2) do
item.checked = utf8.char(0xf4b5) -- "" nf-oct-command_palette
table.insert(items, item)
end
for _,item in ipairs(items) do
if not item.separator then
item.text = hk:iter(item.hidden~=nil).." "..item.cfg.info.name
end
end
if empty then
toggleHidden(items)
end
--
local props = {
Title="Choose provider",
Bottom="F4, Alt+F4, Ctrl+H, Alt+F1, F1",
HelpTopic=utils.HelpTopic "ProvidersMenu",
Id=win.Uuid("7EF7D50A-9CF2-4053-97E5-041C39DE1774"),
}
if opts.profile~="default" then
props.Title = props.Title.." - "..opts.profile
end
local bkeys = "Enter CtrlEnter F4 AltF4 CtrlH AltF1"
repeat
local item, pos = far.Menu(props, items, bkeys)
if item then
local bk = item.BreakKey
item = items[pos]
local info = item.cfg.info
if bk=="Enter" or bk=="CtrlEnter" or not bk then
if item.grayed then
if info.url then
utils.openUrl(info.url)
end
else
if bk~="CtrlEnter" then
utils.msave(opts.profile, "cfgfile", item.FileName)
end
opts.cfg = item.cfg
return openDialog(opts)
end
elseif bk=="CtrlH" then
toggleHidden(items)
elseif bk=="F4" then
if info.config then
editor.Editor(info.config)
end
elseif bk=="AltF4" then
if F.EEC_MODIFIED==editor.Editor(item.pathname) then
opts.cfg = item.FileName
return chooseCfg(opts)
end
elseif bk=="AltF1" then
if info.url then
utils.openUrl(info.url)
end
end
end
props.SelectIndex = pos
until not item
end
local function getCfg (profile, cfgname)
if not cfgname then
local filename = utils.mload(profile, "cfgfile")
local pathname = filename and utils.pathjoin(cfgpath,filename)
local cfg = pathname and win.GetFileAttr(pathname) and loadCfg(pathname,filename)
return cfg and cfg.reachable and cfg
elseif type(cfgname)=="string" then
if cfgname=="" then return end -- use chooseCfg menu
local pathname, filename
if cfgname:match"[\\/]" then
pathname = far.ConvertPath(cfgname)
if string.sub(pathname,1,#cfgpath)==cfgpath then
filename = string.sub(pathname,#cfgpath+2)
end
else
filename = cfgname
if not cfgname:match"%.lua%.cfg$" then
filename = filename..".lua.cfg"
end
pathname = utils.pathjoin(cfgpath,filename)
end
local cfg = loadCfg(pathname,filename)
if not cfg.reachable then
error(("%s: dependencies not found\nSee help and %s"):format(cfg.info.name, cfg.info.url))
end
return cfg
end
end
return {
clearSession=clearSession,
chooseCfg=chooseCfg,
getCfg=getCfg,
}