Skip to content

Instantly share code, notes, and snippets.

@johnd0e johnd0e/MacroCheck.lua
Last active Dec 11, 2019

Embed
What would you like to do?
[Far Manager macro] MacroCheck
--todo stack trace util
local Info = Info or package.loaded.regscript or function(...) return ... end --luacheck: ignore 113/Info
local nfo = Info { _filename or ...,
name = "MacroCheck";
description = "Check macro in editor [Reload/Execute/Variables]";
version = "1.2";
author = "jd";
url = "http://forum.farmanager.com/viewtopic.php?f=60&t=8008";
id = "DA9B41E0-3896-4533-94E9-D5CE10BB7968";
minfarversion = {3,0,0,4261,0}; --far.FarClock
files = "scriptscfg.*.sample",
options = {
check = "[ check macro ]",
eval = "[ eval macro ]",
reload= "[ reload macros ]",
reloadmenu="Plugins Disks Config",
useselection={ --todo
Eval=true,
MoonToLua=true
},
--todo pos to err (?moon)
--todo macro keys
--todo run cached f
};
}
if not nfo then return end
local O = nfo.options
local le_success,le = pcall(require,"le")
if not le_success then le = function() far.Message([[
Lua Explorer „Advanced“ is required for this function.
http://forum.farmanager.com/viewtopic.php?f=60&t=7988]],"")
end end
local F = far.Flags
local function ProcessName(mask,file)
return far.ProcessName(F.PN_CMPNAMELIST,mask,file,F.PN_SKIPPATH)
end
local meta = {__index=_G}
local function sandbox(f,env,mt)
return setfenv(f,setmetatable(env or {}, mt or meta))
end
local function env_prompt(Title,Prompt,History,Src)
repeat
local expr = far.InputBox(nil,Title,Prompt,History or Title,Src,nil,nil,F.FIB_ENABLEEMPTY)--??id
if not expr then return end
local f,err = loadstring(expr)
if f then
local success,err2 = pcall(sandbox(f))
if success then
return getfenv(f)
else
far.Message(err2,'Error',nil,'w')
end
else
far.Message(err,'Error in expression',nil,'wl')
end
until false
end
local function getText(ei,sel)
local str = {}
local from = ei.UseSelection and sel.StartLine or 1
local to = ei.UseSelection and sel.EndLine or ei.TotalLines
for i=from,to do
local cur = editor.GetString(ei.EditorID,i)
str[i] = ei.UseSelection and cur.StringText:sub(cur.SelStart,cur.SelEnd) or cur.StringText
end
return table.concat(str,"\n",from,to)
end
--[[
local function getAllLocals(level)
local locals = {}
for i=1,1000 do
local k,v = debug.getlocal (level+1, i)
if not k then return locals, i-1 end
locals[k] = v
end
end
local function getinfo(thread)--todo
local f = debug.getinfo(thread,1,"f").func
local locals,nl = {}
for i=1,1000 do
local k,v = debug.getlocal(thread,1,i)
if not k then nl=i-1; break end--todo
locals[k] = v
end
local env = getfenv(f)
if nl>0 then
locals.__ENV__ = locals.__ENV__ or next(env) and env or nil
le(locals,"locals")
else
if next(env) then le(env,"environment") end
end
end
local function sethook(f)
local function hook()
--todo catch error??
if f==debug.getinfo(2, "f").func then
debug.sethook()
local locals,nl = getAllLocals(2)
local env = getfenv(f)
if nl>0 then
locals.__ENV__ = locals.__ENV__ or next(env) and env or nil
le(locals,"locals")
else
if next(env) then le(env,"environment") end
end
end
end
debug.sethook(hook,"r")
end
--]]
local function ReloadMacro()
far.Message("Reloading..","","")
local start = far.FarClock()
if far.MacroLoadAll() then
local elapsed = math.floor((far.FarClock()-start) / 1000)
--far.Message(" ","","","k")
--far.Message(elapsed,"","","k")
require"tooltip" ("(re)loaded in "..elapsed.." ms","Macros",1000)
end
end
local function setEditorPos(ei) editor.SetPosition(ei.EditorID,ei); editor.Redraw(ei.EditorID) end --??
local function ErrMessage(msg,line,ei)
if not ei then
far.Message(msg,nfo.name,nil,"lw")
else
if ei.UseSelection then line = line+ei.BlockStartLine-1 end
editor.Select(ei.EditorID,F.BTYPE_STREAM,line,1,-1,1)
setEditorPos{CurLine=line}
if -1==far.Message(msg,nfo.name,nil,"lw") then
editor.Select(ei.EditorID,ei.UseSelection and F.BTYPE_STREAM or F.BTYPE_NONE)
setEditorPos(ei)
end
end
end
local function ErrorCurrent(Err,ei)
local line,err,err1,err2
if ei.isMoon then
err1,line,err2 = Err:match"^(.-) %[(%d+)%] >>%s*(.+)$"
else
line,err = Err:match":(%d+): (.+)$"
end
if not line then
return ErrMessage(Err)
else
return ErrMessage(err or err1..err2,line,ei)
end
end
--[[
if file and file:sub(1,3)=="..." then
local trimpos = 4
local valid,len = file:utf8valid()
if not valid and len==3 then trimpos = trimpos+1 end
local ending = file:sub(trimpos)
if not valid then far.Show(ending,ending:utf8valid()) end
if ending==ei.FileName:sub(-ending:len()) then
file = ei.FileName
end
end
--]]
local SELECTION = "selection"
local function ErrorRuntime(thread,Err,ei)
local level,di = 0
repeat
di = debug.getinfo(thread,level)
level = level+1
until not di or di.what~="C"
if di and (di.source==SELECTION or di.source:match"^@(.+)$"==ei.FileName) then
ErrorCurrent(Err,ei)
else
--?? open in editor
ErrMessage(Err)
end
end
--[[
local function a()
local b = 4
far.Menu()
end
a()
--]]
--todo local stacktrace = export.CustomErrHandler or debug.traceback
local function inspectRet(success,...)
if not success then return nil,... end
local n = select("#",...)
if n==1 then
le(...,type(...)) --todo check if not le
elseif n>0 then
repeat
local ret,pos = far.Show(...)
if ret then le(ret.arg,"value "..tostring(pos)) end
until not ret
end
return true,n
end
local function call(f,ei)
local thread = coroutine.create(f)
local success,res = inspectRet(coroutine.resume(thread))
if not success then
ErrorRuntime(thread,res,ei)--todo inspect stack
--ErrorRuntime(thread,stacktrace(thread,res),ei)
end
return thread,res
end
local scriptspath = win.GetEnv"FARPROFILE":lower().."\\macros\\scripts\\"
local sp_len = scriptspath:len()
local last_co
local function checkMacro(mode)
local ei = editor.GetInfo()
local sel = editor.GetSelection()
if sel then --save for further restoring
ei.Selection = {
BlockType = ei.BlockType,
BlockStartLine = ei.BlockStartLine,
BlockStartPos = sel.StartPos,
BlockWidth = sel.EndPos-sel.StartPos+1,
BlockHeight = sel.EndLine-sel.StartLine+1,
}
if not mode or O.useselection[mode] then
ei.UseSelection = true
end
end
ei.isMoon = ProcessName("*.moon",ei.FileName)
local filename = not ei.UseSelection and ei.FileName or nil --name chunk only if selection is absent
local src = getText(ei,sel)
--todo return
if mode=="Eval" and not ei.isMoon and not string.find(src,"\n") and not string.find(src,"%f[%w]return%f[%W]") then
src = "return "..src
end
local loadstring = ei.isMoon and require"moonscript".loadstring or loadstring
local f,Err = loadstring(src,(filename and "@"..filename or SELECTION))
if Err then
ErrorCurrent(Err,ei)
last_co = nil
return
end
if not mode then
if not (ei.FileName:lower():sub(1,sp_len)==scriptspath) then mf.postmacro(Keys,"Tab") end
local btns = "Reload &All;&Execute;&Variables"
local ans = far.Message("Syntax is Ok",nfo.name,btns)
if ans==-1 then
return
else
mode = ({"Reload","Execute","Variables"})[ans]
end
end
local exec_modes = {
Execute=true,Selection=true,Variables=true,Eval=true,
--EvalFile=true, --??
}
if mode=="MoonToLua" and ei.isMoon then
--todo selection
--todo button
local lua = require"moonscript".to_lua(src)
if far.Message(lua,nfo.name,"Copy;","l") then
far.CopyToClipboard(lua)
end
elseif mode=="Reload" then
if band(ei.CurState,F.ECSTATE_SAVED)==0 then editor.SaveFile() end
if Area.Editor then ReloadMacro() end --если при сохранении не возникло вопросов
elseif not exec_modes[mode] then
far.Message("wrong mode specified",nfo.name,nil,"w")
else
--?? optionally persist
local env
if mode=="Eval" and ei.UseSelection then --ask for env
local title = ("call: "..src):gsub("[\r\n].+","...")
env = env_prompt(title,"env: ... (type as Lua code or leave empty)","evalSelection")
if not env then return end
end
env = env or {_filename=filename} --todo --\\?\C
local n
last_co,n = call(sandbox(f,env),ei)
if mode=="Eval" or mode=="EvalFile" then
if n==0 then far.Message("no values returned") end
elseif mode=="Variables" then
if not le_success then le(); return end
--
end
end
end
Macro { description="Check macro in editor [Reload/Execute/Variables]";
area="Editor"; key="CtrlEnter"; filemask="*.lua;*.moon;far_standards.lua.cfg";
id="8FCF8185-A490-41D3-9F95-19E7669252D9";
action=function()
checkMacro()
end;
}
Macro { description="Eval selected text and inspect returned values";
area="Editor"; key="CtrlShiftEnter"; flags="EVSelection"; filemask="*.lua;*.moon";
id="3BCEADCB-FF93-4B7D-9EC3-97B76FAE0ABA";
action=function()
checkMacro"Eval"
end;
}
Macro { description="moon to lua"; --todo
area="Editor"; key="F1"; filemask="*.moon";
id="3B5508B4-A0A5-4B74-9573-D2505DBF71D5";
action=function()
checkMacro"MoonToLua"
end;
}
if O.check then MenuItem { description="Check lua/moon script";
menu="Plugins"; area="Editor";
text=function()
return ProcessName("*.lua;*.moon",editor.GetFileName()) and O.check
end;
guid="11958400-9BD7-4173-ABE0-7181682A282D";
action=function() checkMacro() end;
} end
if O.eval then MenuItem { description="Eval lua/moon script";
menu="Plugins"; area="Editor";
text=function()
return ProcessName("*.lua;*.moon",editor.GetFileName()) and O.eval
end;
guid="01ABE288-8C0D-4824-94C4-3F30065D9085";
action=function() checkMacro"Eval" end;
} end
MenuItem { description="Reload macros";
menu=O.reloadmenu; area="Common";
text=O.reload;
guid="6F3144E6-38A4-4C5D-B9A2-2B38C4DE83C8";
action=function(OpenFrom)
if OpenFrom==F.OPEN_EDITOR and ProcessName("*.lua;*.moon",editor.GetFileName()) then
checkMacro"Reload"
else
ReloadMacro()
end
end;
}
-----------------------todo todo todo
NoMacro { description="";
area="Editor"; key="F1";
--priority=50;
id="7E57D345-65DD-49F2-AD3D-21A506854B80";
condition=function() return last_co end;
action=function()
far.Show(
coroutine.resume(last_co))
end;
}
Config {
id="DA9B41E0-3896-4533-94E9-D5CE10BB7968";
--disabled=true;
options={
check = false, --"[ check macro ]",
eval = false, --"[ eval macro ]",
reload= false, --"[ reload macros ]",
--reloadmenu="Plugins",
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.