home *** CD-ROM | disk | FTP | other *** search
/ 73.234.73.94.ip.orionnet.ru / 73.234.73.94.ip.orionnet.ru.tar / 73.234.73.94.ip.orionnet.ru / Far30b3000.x86.20121208.msi / luamacro.lua < prev    next >
Text File  |  2012-12-08  |  8KB  |  263 lines

  1. -- started: 2012-04-20
  2.  
  3. local function LOG (fmt, ...)
  4.   local log = io.open("c:\\lua.log","at")
  5.   if log then
  6.     log:write("LUA: ", fmt:format(...), "\n")
  7.     log:close()
  8.   end
  9. end
  10.  
  11. local F = far.Flags
  12.  
  13. local co_create, co_yield, co_resume, co_status, co_wrap =
  14.   coroutine.create, coroutine.yield, coroutine.resume, coroutine.status, coroutine.wrap
  15.  
  16. -- A unique value, inaccessible to scripts.
  17. local PROPAGATE={}
  18.  
  19. local function pack (...)
  20.   return { n=select("#",...), ... }
  21. end
  22.  
  23. -- Override coroutine.resume for scripts, making it possible to call Keys(),
  24. -- print(), Plugin.Call(), exit(), etc. from nested coroutines.
  25. function coroutine.resume(co, ...)
  26.   local t = pack(co_resume(co, ...))
  27.   while t[1]==true and t[2]==PROPAGATE do
  28.     t = pack(co_resume(co, co_yield(unpack(t, 2, t.n))))
  29.   end
  30.   return unpack(t, 1, t.n)
  31. end
  32.  
  33. local ErrMsg = function(msg) far.Message(msg, "LuaMacro", nil, "wl") end
  34.  
  35. local MacroTable = {}
  36. local LastMessage = {}
  37. local gmeta = { __index=_G }
  38.  
  39. local function checkarg (arg, argnum, reftype)
  40.   if type(arg) ~= reftype then
  41.     error(("arg. #%d: %s expected, got %s"):format(argnum, reftype, type(arg)), 3)
  42.   end
  43. end
  44.  
  45. -------------------------------------------------------------------------------
  46. -- Functions implemented via "returning a key" to Far
  47. -------------------------------------------------------------------------------
  48.  
  49. function _G.Keys (...)
  50.   for n=1,select("#",...) do
  51.     local str=select(n,...)
  52.     if type(str)=="string" then
  53.       for key in str:gmatch("%S+") do
  54.         co_yield(PROPAGATE, F.MPRT_KEYS, key)
  55.       end
  56.     end
  57.   end
  58. end
  59.  
  60. function _G.print (str)
  61.   co_yield(PROPAGATE, F.MPRT_PRINT, tostring(str))
  62. end
  63.  
  64. function _G.printf (fmt, ...)
  65.   checkarg(fmt,1,"string")
  66.   return _G.print(fmt:format(...))
  67. end
  68.  
  69. local function PluginCall    (...) return co_yield(PROPAGATE, F.MPRT_PLUGINCALL,    pack(...)) end
  70. local function PluginMenu    (...) return co_yield(PROPAGATE, F.MPRT_PLUGINMENU,    pack(...)) end
  71. local function PluginConfig  (...) return co_yield(PROPAGATE, F.MPRT_PLUGINCONFIG,  pack(...)) end
  72. local function PluginCommand (...) return co_yield(PROPAGATE, F.MPRT_PLUGINCOMMAND, pack(...)) end
  73.  
  74. function _G.exit ()
  75.   co_yield(PROPAGATE, "exit")
  76. end
  77.  
  78. -------------------------------------------------------------------------------
  79. -- END: Functions implemented via "returning a key" to Far
  80. -------------------------------------------------------------------------------
  81.  
  82. local PluginInfo = {
  83.   Flags = F.PF_PRELOAD,
  84.   CommandPrefix = "lm",
  85. }
  86. function export.GetPluginInfo()
  87.   return PluginInfo
  88. end
  89.  
  90. local function loadmacro (Text)
  91.   if string.sub(Text,1,1) == "@" then
  92.     Text = string.sub(Text,2):gsub("%%(.-)%%", win.GetEnv)
  93.     return loadfile(Text)
  94.   else
  95.     return loadstring(Text)
  96.   end
  97. end
  98.  
  99. local function MacroInit (Text)
  100.   if type(Text)=="string" then
  101.     local chunk, msg = loadmacro(Text)
  102.     if chunk then
  103.       local env = setmetatable({}, gmeta)
  104.       setfenv(chunk, env)
  105.       local macro = { coro=co_create(chunk), store={} }
  106.       table.insert(MacroTable, macro)
  107.       --far.Message("Init: created handle "..#MacroTable)
  108.       return #MacroTable
  109.     else
  110.       ErrMsg(msg)
  111.     end
  112.   end
  113. end
  114.  
  115. local function MacroStep (handle, ...)
  116.   local macro = MacroTable[handle]
  117.   if macro then
  118.     local status = co_status(macro.coro)
  119.     if status == "suspended" then
  120.       local ok, ret1, ret_type, ret_values = co_resume(macro.coro, ...)
  121.       if ok then
  122.         status = co_status(macro.coro)
  123.         if status == "suspended" and ret1 == PROPAGATE and ret_type ~= "exit" then
  124.           macro.store[1] = ret_values
  125.           if ret_type==F.MPRT_PLUGINCALL or ret_type==F.MPRT_PLUGINMENU or
  126.              ret_type==F.MPRT_PLUGINCONFIG or ret_type==F.MPRT_PLUGINCOMMAND then
  127.             return ret_type, ret_values
  128.           else
  129.             return ret_type, macro.store
  130.           end
  131.         else
  132.           MacroTable[handle] = false
  133.           LastMessage[1] = ""
  134.           return F.MPRT_NORMALFINISH, LastMessage
  135.         end
  136.       else
  137.         ErrMsg(ret1)
  138.         MacroTable[handle] = false
  139.         LastMessage[1] = ret1
  140.         return F.MPRT_ERRORFINISH, LastMessage
  141.       end
  142.     else
  143.       ErrMsg("Step: called on macro in "..status.." status")
  144.     end
  145.   else
  146.     -- Far debug only: should not be here
  147.     ErrMsg(("Step: handle %d does not exist"):format(handle))
  148.   end
  149. end
  150.  
  151. local function MacroFinal (handle)
  152.   if MacroTable[handle] then
  153.     MacroTable[handle] = false -- false, not nil!
  154.     --far.Message("Final: closed handle "..handle)
  155.     return 1
  156.   else
  157.     -- Far debug only: should not be here
  158.     ErrMsg(("Final: handle %d does not exist"):format(handle))
  159.   end
  160. end
  161.  
  162. local function MacroParse (text, onlyCheck, skipFile, title, buttons)
  163.   local isFile = string.sub(text,1,1) == "@"
  164.   if not (isFile and skipFile) then
  165.     local chunk, msg = loadmacro(text)
  166.     if not chunk then
  167.       if not onlyCheck then
  168.         far.Message(msg, title, buttons, "lw")
  169.       end
  170.       LastMessage = pack(
  171.         msg, -- keep alive from gc
  172.         tonumber(msg:match(":(%d+): ")) or 0)
  173.       return F.MPRT_ERRORPARSE, LastMessage
  174.     end
  175.   end
  176.   LastMessage[1] = ""
  177.   return F.MPRT_NORMALFINISH, LastMessage
  178. end
  179.  
  180. local function ProcessCommandLine (CmdLine)
  181.   local op, text = CmdLine:match("(%S+)%s*(.*)")
  182.   if op then
  183.     local op = op:lower()
  184.     if     op=="post"  and text~="" then far.MacroPost(text, F.KMFLAGS_DISABLEOUTPUT)
  185.     elseif op=="check" and text~="" then far.MacroCheck(text)
  186.     elseif op=="load" then far.MacroLoadAll()
  187.     elseif op=="save" then far.MacroSaveAll()
  188.     end
  189.   end
  190. end
  191.  
  192. function export.Open (OpenFrom, ...)
  193.   if OpenFrom == F.OPEN_LUAMACRO then
  194.     local calltype, handle, args = ...
  195.     if     calltype==F.MCT_MACROINIT  then return MacroInit (args[1])
  196.     elseif calltype==F.MCT_MACROSTEP  then return MacroStep (handle, unpack(args))
  197.     elseif calltype==F.MCT_MACROFINAL then return MacroFinal(handle)
  198.     elseif calltype==F.MCT_MACROPARSE then return MacroParse(unpack(args))
  199.     end
  200.  
  201.   elseif OpenFrom == F.OPEN_COMMANDLINE then
  202.     local guid, cmdline = ...
  203.     return ProcessCommandLine(cmdline)
  204.  
  205.   elseif OpenFrom == F.OPEN_FROMMACRO then
  206.     local guid, args = ...
  207.     if args[1]=="argtest" then return unpack(args,2) end -- argtest: return received arguments
  208.   end
  209. end
  210.  
  211. local function ReadConsts()
  212.   while true do
  213.     local sName,sValue,sType = far.MacroCallFar(0x80C65)
  214.     if not sName then break end
  215.     if _G[sName] == nil then -- protect existing globals
  216.       if     sType=="text"    then _G[sName]=sValue
  217.       elseif sType=="real"    then _G[sName]=tonumber(sValue)
  218.       elseif sType=="integer" then _G[sName]=bit64.new(sValue)
  219.       end
  220.     end
  221.   end
  222. end
  223.  
  224. -- Add function unicode.utf8.cfind:
  225. -- same as find, but offsets are in characters rather than bytes
  226. local function AddCfindFunction()
  227.   local usub, ssub = unicode.utf8.sub, string.sub
  228.   local ulen, slen = unicode.utf8.len, string.len
  229.   local ufind = unicode.utf8.find
  230.   unicode.utf8.cfind = function(s, patt, init, plain)
  231.     init = init and slen(usub(s, 1, init-1)) + 1
  232.     local t = { ufind(s, patt, init, plain) }
  233.     if t[1] == nil then return nil end
  234.     return ulen(ssub(s, 1, t[1]-1)) + 1, ulen(ssub(s, 1, t[2])), unpack(t, 3)
  235.   end
  236. end
  237.  
  238. local function DBLoader (name)
  239.   local str = far.MacroCallFar(0x80C66,name)
  240.   if str then
  241.     local f, msg = loadstring(str, name)
  242.     return f or msg
  243.   end
  244.   return ("\n\tModule '%s' not found in database"):format(name)
  245. end
  246.  
  247. do
  248.   local func,msg = loadfile(far.PluginStartupInfo().ModuleDir.."api.lua")
  249.   if func then
  250.     func { checkarg=checkarg, loadmacro=loadmacro }
  251.     Plugin.Call=PluginCall
  252.     Plugin.Menu=PluginMenu
  253.     Plugin.Config=PluginConfig
  254.     Plugin.Command=PluginCommand
  255.   else
  256.     ErrMsg(msg)
  257.   end
  258.  
  259.   AddCfindFunction()
  260.   ReadConsts()
  261.   table.insert(package.loaders, 2, DBLoader)
  262. end
  263.