Всем привет!
Хочу еще раз поблагодарить всех, кто помогал советами. У меня все получилось как задумывал. Первая моя программа на Lua, равно как и, собственно, far-скрипт. Готов поделиться сделанным, но не знаю на сколько это будет востребовано.
Кратко по функционалу, что сделано... Макро работает только в редакторе. Выводит из своего конфигурационного файла список тамошних команд, которые, собственно, можно исполнить. Команды могут быть четырех видов:
Filter - записываем временный файл, обрабатываем его командой, потом загружаем в редактор
Pipe - записываем временный файл, обрабатываем его командой, потом загружаем в редактор STDOUT выполненной команды
Compiler - сохраняем файл, запускаем команду компилятора, по результату или диалог с Ok, либо диалог с ошибкой от компилятора
Execute - сохраняем файл, запускаем команду интерпретатора, который обрабатывает содержимое редактора - результат выводим в виде диалога
Вот пример моего теперешнего конфига
EditorTools.conf:
Code: Select all
; Поля конфига следующие:
;
; Режим, "Название-инструмента", "командная строка"
;
; Режим:
;
; [F]ilter - записываем временный файл, обрабатываем его командой, потом загружаем в редактор
; [P]ipe - записываем временный файл, обрабатываем его командой, но загружаем STDOUT команды
; [C]ompiler - сохраняем файл, запускаем команду компилятора
; [E]xecute - сохраняем файл, запускаем команду интерпретатора, результат выводим в виде диалога
;
; Плейсхолдеры:
;
; %p - путь к файлу в редакторе
; %n - имя файла в редакторе (без расширения)
; %e - расширение имени файла в редакторе
; %t - полное имя временного файла
; %o - полное имя временного файла-результата (куда пишется из пайпа)
; %r - полное имя временного файла-результата (куда может писать сама программа)
;
; Пример:
;
; F,'Инструмент-1','comm-1.cmd'
; P,'Инструмент-2','comm-2.cmd'
; C,'Инструмент-3','comm-3.cmd'
; E,'Инструмент-4','comm-4.cmd'
;
P,'AStyle (Space-P)','C:\Tools\AStyle\AStyle.exe --options=C:\Tools\AStyle\.astylerc-space --stdin=%t'
F,'AStyle (Space-F)','C:\Tools\AStyle\AStyle.exe --options=C:\Tools\AStyle\.astylerc-space --stdin=%t --stdout=%r'
P,'TidyP','C:\Tools\tidy.exe -config c:\Tools\tidy.conf "%t"'
P,'Hello Ruby','ruby.exe C:\Tools\Far3-x64\Profile\Macros\scripts\EditorTools\hello.rb'
P,'Test Perl-P','perl.exe C:\Tools\Far3-x64\Profile\Macros\scripts\EditorTools\test-p.pl "%p%n.%e"'
F,'Test Perl-F','perl.exe C:\Tools\Far3-x64\Profile\Macros\scripts\EditorTools\test-f.pl "%t" "%r"'
C,'Компилировать FASM программу','C:\Tools\Fasm\FASM.CMD "%t" "%p%n.exe"'
E,'Исполнить Perl скрипт','perl.exe %t'
E,'Исполнить SWI-Prolog скрипт','C:\Tools\Prolog\bin\swipl.exe -q -f %t'
Сам скрипт
EditorTools.lua:
Code: Select all
------------------------------------------------------------------------------------------------------------------------
-- Скрипт EditorTools v.1.1
-- подключение внешних инструментов в редакторе Far'а
--
-- 2020 (C) Автор Majestio aka JoeUser
--
-- http://majestio.info
--
------------------------------------------------------------------------------------------------------------------------
-- local inspect = require('inspect')
local ScriptName = ...
local EOPT_BOM = 0x00000200
local BTYPE_STREAM = 1
local SW_HIDE = 0
------------------------------------------------------------------------------------------------------------------------
local EditorTools = function ()
local CurPath = mf.fsplit(ScriptName,3)
local ConfigName = ScriptName:gsub("[^.]+$","conf")
local Conf = io.open(ConfigName,"r")
-- читаем файл конфигурации ------------------------------------------------------------------------------------------
if Conf == nil then
far.Message('Файл конфигурации нельзя прочесть!', 'Ошибка', ';Ok', 'w')
return
else
local Cmd = {}
local Idx = 1
-- разбираем файл конфигурации на список "инструментов" ------------------------------------------------------------
for line in Conf:lines() do
if regex.match(line,'^(F|P|C|E)') then
for w1,w2,w3 in regex.gmatch(line, "^(F|P|C|E)\\s*,\\s*'\\s*([^']+?)\\s*'\\s*,\\s*'\\s*([^']+?)\\s*'\\s*$") do
Cmd[Idx] = {w1,w2,w3}
end
Idx = Idx + 1
end
end
Conf:close()
-- формируем и выводим диалог выбора "инструментов" ----------------------------------------------------------------
local Choice = ''
for Idx = 1, #Cmd do
Choice = Choice..Cmd[Idx][2]..'\n';
end
local ChoiceIdx = Menu.Show(Choice, nil, 8+0x200)
if ChoiceIdx == 0 then return end
-- формируем имя временного файла для сброса содержимого редактора -------------------------------------------------
math.randomseed(os.time())
local RndNumber = math.random(1000)
local EditorFullName = editor.GetFileName()
local EditorFilePath = regex.match(EditorFullName,"^(.+\\\\).+$")
local EditorFileName = regex.match(EditorFullName,"^.+\\\\([^\\.]+)\\.*.*$")
local EditorFileExt = regex.match(EditorFullName,"^.+?\\.([^\\.]+)$")
if EditorFileExt == nil then EditorFileExt = "" end
local TmpName = EditorFilePath..EditorFileName..'-tmp-'..RndNumber..'.'..EditorFileExt
local OutName = EditorFilePath..EditorFileName..'-tmp-'..RndNumber..'.'.."out"
local ResName = EditorFilePath..EditorFileName..'-tmp-'..RndNumber..'.'.."res"
local ErrName = EditorFilePath..EditorFileName..'-tmp-'..RndNumber..'.'.."err"
-- в командной строке производим замены в плэйсхолдерах ------------------------------------------------------------
local CmdName = regex.gsub(Cmd[ChoiceIdx][3],"%p",EditorFilePath) -- %p на значение EditorFilePath
CmdName = regex.gsub(CmdName,"%n",EditorFileName) -- %n на значение EditorFileName
CmdName = regex.gsub(CmdName,"%e",EditorFileExt) -- %e на значение EditorFileExt
CmdName = regex.gsub(CmdName,"%t",TmpName) -- %t на значение TmpName
CmdName = regex.gsub(CmdName,"%o",OutName) -- %o на значение OutName
CmdName = regex.gsub(CmdName,"%r",ResName) -- %r на значение ResName
-- запускаем внешнюю обработку в зависимости от "режима" выбранного инструмента ------------------------------------
if WriteFile(TmpName) ~= nil then
local Out, Err = ExecCommand(CmdName, TmpName, OutName, ResName, ErrName, Cmd[ChoiceIdx][1])
if Cmd[ChoiceIdx][1] == "C" then
if Err == "" or Err == nil then
far.Message("Компиляция прошла удачно", 'Результат компиляции', ';Ok', 'k')
else
far.Message(win.OemToUtf8(Err), 'Ошибка', ';Ok', 'w')
end
else
if Out ~= "" and Out ~= nil then
if Cmd[ChoiceIdx][1] == "E" then
far.Message(win.WideCharToMultiByte(win.MultiByteToWideChar(Out,1251),65001), 'Результат выполнения', ';Ok', 'k')
else
LoadContent(Out)
end
else
if Err == "" or Err == nil then
far.Message('Обработчик вернул пустой вывод!', 'Ошибка', ';Ok', 'w')
end
end
end
else
far.Message('Ошибка записи временного файла!!', 'Ошибка', ';Ok', 'w')
end
end
end
------------------------------------------------------------------------------------------------------------------------
-- Загрузка файла в редактор
------------------------------------------------------------------------------------------------------------------------
function LoadContent(Content)
local Ei = editor.GetInfo()
local EId = Ei.EditorID
local Flags = far.Flags
editor.UndoRedo(EId, Flags.EUR_BEGIN)
editor.Select(EId, BTYPE_STREAM, 1, 1, 1, Ei.TotalLines+1)
editor.DeleteBlock(EId)
Content = regex.gsub(Content,"^(\xef\xbb\xbf)","")
editor.InsertText(EId,Content)
editor.UndoRedo(EId,Flags.EUR_END)
editor.SetPosition(EId, 1, 1)
end
------------------------------------------------------------------------------------------------------------------------
-- Запуск процесса по обработке файла
------------------------------------------------------------------------------------------------------------------------
function ExecCommand(CmdName, TmpName, OutName, ResName, ErrName, Mode)
local RetOut, RetErr
local File = io.popen(('"%s >"%s" 2>"%s"'):format(CmdName, OutName, ErrName),"w")
if File == nil then
far.Message('Ошибка при запуске команды!', 'Ошибка', ';Ok', 'w')
return nil, nil
end
File:close()
local TheName = OutName
if Mode ~= "P" and Mode ~= "E" then TheName = ResName end
if win.GetFileInfo(TheName) ~= nil then
OutFile = io.open(TheName)
if OutFile~= nil then
RetOut = OutFile:read("*a")
OutFile:close()
end
end
if win.GetFileInfo(ErrName) ~= nil then
ErrFile = io.open(ErrName)
if ErrFile~= nil then
RetErr = ErrFile:read("*a")
ErrFile:close()
end
end
win.ShellExecute(nil,nil,win.GetEnv"COMSPEC",('/c "del "%s" "%s" "%s" "%s"'):format(TmpName, OutName, ResName, ErrName),nil,SW_HIDE)
return RetOut, RetErr
end
------------------------------------------------------------------------------------------------------------------------
-- Запись файла
------------------------------------------------------------------------------------------------------------------------
function WriteFile (FileName)
local Info = editor.GetInfo()
local File = io.open(FileName,"wb")
if File == nil then
far.Message('Ошибка записи временного файла!', 'Ошибка', ';Ok', 'w')
return nil
else
if (bit.band(Info.Options, EOPT_BOM) == EOPT_BOM) then
File:write(string.char(239,187,191))
end
for Line = 1, Info.TotalLines do
local Gs = editor.GetString(Info.EditorID, Line, 0)
File:write(Gs.StringText, Gs.StringEOL)
end
File:close()
return Info.TotalLines
end
end
------------------------------------------------------------------------------------------------------------------------
-- Macro
------------------------------------------------------------------------------------------------------------------------
Macro {
area="Editor"; key="CtrlE"; flags=""; description="EditorTools";
action = function()
EditorTools()
end;
}