local F = far.Flags
local MoveKeys = '|Up|Down|PgUp|PgDn|Left|Right|Home|End|CtrlRight|CtrlLeft|'
local SelectKeys = MoveKeys:gsub('|(%a)', '|Shift%1',8):gsub('Ctrl','CtrlShift')
local ChangeKeys = {['|BackSlash|']=true; ['|Space|']=true; ['|BS|']=true}
local TerminateKeys = '|F10|Esc|'
local OperatingFuncKeys = '|F1|F10|F11|F7|CtrlF7|ShiftF7|AltF7|AltF8|'
local aEditors = {}
local CmdHistory = {}
local Cfg = {
AutoCompl_Start = 3
}
Macro{
id="710E66E5-D204-4F02-A3B3-F544A8980349";
area="Shell";
key="CtrlAltF4";
description="Редактирование командной строки в редакторе";
action=function(data)
local function InitEd()
local function Label(Key,Text,LongText)
local rec = far.NameToInputRecord(Key)
return {VirtualKeyCode=rec.VirtualKeyCode,ControlKeyState=rec.ControlKeyState,Text=Text,LongText=LongText or Text}
end
local SelStart, SelEnd = panel.GetCmdLineSelection()
aEditors[tostring(editor.GetInfo().EditorID)] = {Value=CmdLine.Value, Pos= CmdLine.CurPos, SelStart=SelStart, SelEnd=SelEnd}
if CmdLine.Value ~= '' then
editor.SetString(nil, 1, CmdLine.Value)
editor.SetPosition(nil, 1, CmdLine.CurPos)
if CmdLine.Selected then
editor.Select(nil, F.BTYPE_STREAM, 1, SelStart, SelEnd-SelStart+1, 1)
end
end
local aKeyBar = {Label("F1","Help"),Label("F10","Save&Close")}
for i=1,12 do
local sKey = 'F' .. i
if not OperatingFuncKeys:find('|'..sKey..'|',1,true) then
aKeyBar[#aKeyBar+1] = Label(sKey, '')
end
end
for _,v in ipairs({'Ctrl','Shift','Alt','CtrlShift','CtrlAlt','AltShift','CtrlAltShift'}) do
for i=1,12 do
local sKey = v .. 'F'..i
if not OperatingFuncKeys:find('|'..sKey..'|',1,true) then aKeyBar[#aKeyBar+1] = Label(sKey, '') end
end
end
editor.SetKeyBar(nil, aKeyBar)
-- Чтение истории в массив
local obj = far.CreateSettings('far')
local enum = obj:Enum(far.Flags.FSSF_HISTORY_CMD)
obj:Free()
CmdHistory = {}
for _,v in ipairs(enum) do CmdHistory[#CmdHistory+1] = v.Name end
end
mf.postmacro(InitEd)
editor.Editor(far.MkTemp('frc'), 'Командная строка',nil,nil,nil,nil,
bor(F.EF_CREATENEW,F.EF_DELETEONCLOSE,F.EF_DISABLEHISTORY,F.EF_DISABLESAVEPOS,F.EF_NONMODAL),
1, panel.GetCmdLinePos(), win.GetConsoleCP() )
end;
}
---------------------------------------------------------------------------------
local function ChangingKey(key)
return ChangeKeys[key] or key:len()==3
end
Event {
id = "BD6FED70-78A0-4E6B-B30F-2858ACC7AE1A";
group = "EditorInput"; --export.ProcessEditorInput
description = "Event example";
condition = function(Rec)
if next(aEditors) then
local key = far.InputRecordToName(Rec)
if key then
key = '|'..key..'|'
if MoveKeys:find(key,1,true) or
SelectKeys:find(key,1,true) or
ChangingKey(key) or
TerminateKeys:find(key,1,true) or
('|CtrlSpace|CtrlEnd|'):find(key,1,true) or
key == '|AltDown|' or
key:match"^|F%d%d?|$"
then
local id = editor.GetInfo().EditorID
if aEditors[tostring(id)] then
Rec.Key = key
Rec.EditorID = id
return true
end
end
end
end
end; -- condition
action = function(Rec)
local function CurrCursorPos()
local ei = editor.GetInfo(nil)
local yDlg = 1 + ei.CurLine-ei.TopScreenLine - (band(ei.Options, F.EOPT_SHOWTITLEBAR)==0 and 1 or 0)
local xDlg = ei.CurPos - ei.LeftPos - 1 ----------------------------------------------------------------------------------------------------------------------------------------------
return xDlg, yDlg
end -- CurrCursorPos
local function CurrValue()
local Value = {}
for i=1, Editor.Lines do
local l = Editor.GetStr(i)
if l ~= '' then Value[#Value+1] = l end
end
for i,l in ipairs(Value) do
if i < #Value and
l:sub(-1) ~= ' ' and
not (l:sub(-1) .. Value[i+1]:sub(1,1)):match"[^%w_]" then
Value[i] = l .. ' '
end
end
return table.concat(Value, '')
end -- CurrValue
local function FindMacro(key)
local function KeyMatch(k,kk)
if kk:sub(1,1) == '/' then
return not not regex.match(kk,k)
else
return not not (' '..kk..' '):find(' '..k..' ',1,true)
end
end
for Scr in mf.EnumScripts("Macro") do
if Scr.area == "Editor" and not Scr.disabled and KeyMatch(key, Scr.key) then return true end
end
end
local key, EditorID = Rec.Key, Rec.EditorID
local aSave
local function CalcPos(Line, Char)
local iPos = 0
for i=1,Line-1 do
local l = editor.GetString(EditorID, i, 3)
iPos = iPos + l:len() + (l:sub(-1)==' ' and 0 or 1)
end
return iPos + Char
end -- CalcPos
local function MoveCursor()
local iPos = CalcPos(Editor.CurLine, Editor.CurPos)
panel.SetCmdLinePos(nil, iPos)
end -- MoveCursor
local function SelCmdLine()
local aSel = editor.GetSelection(EditorID)
local SelStart, SelEnd = CalcPos(aSel.StartLine, aSel.StartPos), CalcPos(aSel.EndLine, aSel.EndPos)
panel.SetCmdLineSelection(EditorID, SelStart, SelEnd)
end
local function EdToCmdLine(ArgKey)
local Value, iPos, SelStart, SelEnd = CurrValue(), CalcPos(Editor.CurLine, Editor.CurPos), 0, 0
local function AddHistory(sAdd)
local AddHistoryRet = false
local function DlgFill(a)
for i=1,6 do if a[i] == nil then a[i] = 0 end end
return a
end
local xDlg, yDlg = CurrCursorPos()
local aDlg = {
DlgFill{F.DI_TEXT,1,0,sAdd:len()+1,0,[10]=sAdd};
DlgFill{F.DI_EDIT,1,0,1,0,[10]=sAdd:sub(1,1)};
}
far.Dialog('',xDlg,yDlg,xDlg+sAdd:len(),yDlg,nil,aDlg,bor(F.FDLG_KEEPCONSOLETITLE,F.FDLG_NODRAWSHADOW,F.FDLG_SMALLDIALOG,F.FDLG_NODRAWPANEL),
function(hDlg, Msg, Param1, Param2)
if Msg == F.DN_INITDIALOG then
hDlg:send(F.DM_EDITUNCHANGEDFLAG, 2, 0)
hDlg:send(F.DM_SETEDITPOSITION, 2, {CurPos=1})
elseif Msg == F.DN_CTLCOLORDLGITEM then
--local Clr = far.AdvControl(F.ACTL_GETCOLOR, far.Colors.COL_EDITORTEXT)
--Clr.ForegroundColor = 13
--Clr.BackgroundColor = bor(Clr.BackgroundColor, 8)
local Clr = far.AdvControl(F.ACTL_GETCOLOR, far.Colors.COL_MENUHIGHLIGHT)
for i=1, #Param2 do Param2[1] = Clr end
return Param2
elseif Msg == F.DN_CONTROLINPUT
and (Param2.EventType == F.KEY_EVENT) then
local sKey = far.InputRecordToName(Param2)
if sKey then
if ((sKey:gsub('[LR]?Shift','')):gsub('[LR]?Alt','')):gsub('[LR]?Ctrl','') == '' then return end
if sKey == 'Tab' or sKey == 'End' then
editor.SetString(EditorID, 0, Editor.GetStr() .. sAdd)
editor.SetPosition(EditorID, {CurPos = Editor.GetStr():len()+1})
elseif sKey == 'Right' then
editor.SetString(EditorID, 0, Editor.GetStr() .. sAdd:sub(1,1))
Value = Value .. sAdd:sub(1,1)
iPos = iPos + 1
panel.SetCmdLine(nil, Value)
panel.SetCmdLinePos(nil, panel.GetCmdLinePos(nil)+1)
sAdd = sAdd:sub(2)
editor.SetPosition(EditorID, {CurPos = iPos})
hDlg:send(F.DM_RESIZEDIALOG, 0, {X=sAdd:len(),Y=0})
hDlg:send(F.DM_MOVEDIALOG, 0, {X=1,Y=0})
hDlg:send(F.DM_SETTEXT,1,sAdd)
hDlg:send(F.DM_SETTEXT,2,sAdd:sub(1,1))
return true
elseif sKey == 'CtrlEnd' or sKey == 'CtrlSpace' then
AddHistoryRet = true
elseif sKey ~= 'Esc' then
editor.ProcessInput(nil,Param2)
elseif sKey == 'CtrlTab' then
mf.postmacro(editor.ProcessInput, Param2)
end
hDlg:send(F.DM_CLOSE)
return true
end -- id sKey
end
end
)
return AddHistoryRet
end -- AddHistory
--local Value, iPos, SelStart, SelEnd = CurrValue(), CmdLine.CurPos, 0, 0
local aSel = editor.GetSelection(EditorID)
for i=1, Editor.Lines do
local l = editor.GetString(EditorID, i, 3)
if i < Editor.Lines and l:sub(-1) ~= ' ' then l = l .. ' ' end
if i < Editor.CurLine then iPos = iPos + l:len() end
if aSel then
if i < aSel.StartLine then SelStart = iPos + l:len() end
if i < aSel.EndLine then SelEnd = iPos + l:len() end
end
end
if aSel then
SelStart = SelStart + aSel.StartPos
SelEnd = SelEnd + aSel.EndPos
end
if
--ArgKey ~= '|BS|' and
(Value:len() >= Cfg.AutoCompl_Start or ArgKey == '') and
Editor.CurLine == Editor.Lines and
Editor.CurPos == Editor.GetStr(Editor.Lines):len()+1
then
for i=#CmdHistory, 1, -1 do
if mf.index(CmdHistory[i], Value) == 0 and CmdHistory[i]:len() > Value:len() then -- Чтоб поиск в истории был регистронезависимым
local sAdd = CmdHistory[i]:sub(Value:len()+1)
if not AddHistory(sAdd) then break end
end
end
end
panel.SetCmdLine(nil, Value)
panel.SetCmdLinePos(nil, iPos)
if aSel and aSel.BlockType == F.BTYPE_STREAM then
panel.SetCmdLineSelection(nil, SelStart, SelEnd)
end
end -- EdToCmdLine
if MoveKeys:find(key,1,true) or
SelectKeys:find(key,1,true) then
if SelectKeys:find(key,1,true) then
mf.postmacro(function()MoveCursor(); SelCmdLine(); end)
else
mf.postmacro(MoveCursor)
end
elseif TerminateKeys:find(key,1,true) then
aSave = aEditors[tostring(EditorID)]
local bQuit = true
if key == '|Esc|' then
local iCh
local State = editor.GetInfo(EditorID).CurState
if band(State, F.ECSTATE_MODIFIED) == F.ECSTATE_MODIFIED and
band(State, F.ECSTATE_SAVED) == 0 then
--iCh = far.Message("Что делать?", "Выход", "Ничего не делать;Восстановить ком. строку;Экспортировать", 'w')
mf.postmacro(Keys, 'Tab')
iCh = far.Message("Что делать?", "Выход", "Экспортировать;Восстановить ком. строку;Вернуться в редактор", 'w')
else
iCh = 1
end
if iCh == 2 then
panel.SetCmdLine(nil, aSave.Value)
panel.SetCmdLineSelection(nil, aSave.SelStart, aSave.SelEnd)
panel.SetCmdLinePos(nil, aSave.Pos)
elseif iCh ~= 1 then
bQuit = false
end
end
if bQuit then
aEditors[tostring(EditorID)] = nil
mf.postmacro(editor.Quit, EditorID)
end
return true
elseif ChangingKey(key) then -- Изменение
mf.postmacro(EdToCmdLine, key)
elseif ('|CtrlSpace|CtrlEnd|'):find(key,1,true) then
mf.postmacro(EdToCmdLine, '')
elseif key == '|AltDown|' then
if Editor.CurLine == Editor.Lines and
Editor.CurPos == Editor.GetStr(Editor.Lines):len()+1 then -- в конце последней строки
local Value = CurrValue()
if next(CmdHistory) then
local aMenu = {}
--for i=#CmdHistory,1,-1 do
for i=1, #CmdHistory do
if mf.index(CmdHistory[i], Value) == 0 then
--aMenu[#aMenu+1] = CmdHistory[i]
aMenu[#aMenu+1] = {text=CmdHistory[i]}
end
end
if next(aMenu) then
--Menu.Show(table.concat(aMenu, '\n'), ' История: ', 0x3) -- far.Menu
local XMenu, YMenu = CurrCursorPos()
local aSel =
far.Menu({ Title = 'История команд:';
SelectIndex = #aMenu;
X=XMenu+1; Y=YMenu;
MaxHeight = 10;
},
aMenu)
if aSel then
aSel = aSel.text:sub(Value:len()+1)
local NewPos = {CurLine=Editor.Lines; CurPos=Editor.CurPos + aSel:len()}
Editor.SetStr(Editor.GetStr() .. aSel)
editor.SetPosition(EditorID, NewPos)
mf.postmacro(EdToCmdLine, '')
end
end
--CmdHistory
end
end -- в конце последней строки
return true
elseif key== '|F1|' then
return
far.Message([[
Изменение позиции, отметки и содержимого отражается в комстроке немедленно.
Для выхода с сохранением нажмите F10.
Для восстановления первоначальной комстроки выходите из редактора по Esc.
Если редактор содержит несколько строк, то ко всем строкам, кроме последней,
дописывается пробел, если они не оканечиваются пробелом.
Кодовая страница выставляется такой же, каку консоли.
Работа с историей команд.
-------------------------
В процессе вврда команды проверяется история команд. Если в истории команд
имеются начинающиеся с уже введённой части, то окончание дописывается к
введённой команде. В этом контексте доступны клавиши: - занести один символ;
End - занести всю команду из истории; Esc - погасить историю; Ctrl+Space или
Ctrl+End - подставить следующую подходящую команду.
В любой момент можно нажать Alt+ для выбора команды из списка.
]],
"Редактирование командной строки в редакторе", "Понятно;Справка по редактору", 'l') ~= 2
else
return not ( OperatingFuncKeys:find(key,1,true) --key:match"^|F%d%d?|$"
or FindMacro(key:sub(2,-2) ) )
end
end; -- action
}