Просто для записи, для создания нового режима, который принимает команды различной длины, вы обычно используете что-то вроде дерева разбора с ключами и командами.Вот простая версия в vim:
let g:EvalTree = { 'root': {} }
function! s:AddKeyMapRecursive(root, keys, command) abort
if !has_key(a:root, a:keys[0])
let a:root[a:keys[0]] = { 'command': '', 'children': {} }
endif
if len(a:keys) == 1
let a:root[a:keys[0]].command = a:command
else
call s:AddKeyMapRecursive(a:root[a:keys[0]].children, a:keys[1 : ], a:command)
endif
endfunction
function! g:EvalTree.AddMap(keys, command) abort
call s:AddKeyMapRecursive(l:self.root, a:keys, a:command)
endfunction
function! s:GetNodeRecursive(root, keys) abort
if !has_key(a:root, a:keys[0])
return 0
endif
if len(a:keys) == 1
return a:root[a:keys[0]]
else
return s:GetNodeRecursive(a:root[a:keys[0]].children, a:keys[1 : ])
endif
endfunction
function! g:EvalTree.GetNode(keys) abort
return s:GetNodeRecursive(l:self.root, a:keys)
endfunction
Вы можете вставить в это дерево, например:
call g:EvalTree.AddMap('hw', ":echo 'hello world'<CR>")
call g:EvalTree.AddMap('DA', 'ggdG')
Позже вы можете использовать это дерево для оценки вещей в цикле, который вы упомянули.Хотя вам, вероятно, понадобится еще и очередь для этого.Вы можете определить его как:
let g:TextQueue = { 'text': '', 'index': 0 }
function! g:TextQueue.Push(c) abort
let l:self.text .= a:c
endfunction
function! g:TextQueue.Pop(...) abort
let l:self.index += get(a:, 1, 1)
endfunction
function! g:TextQueue.CheckFirst() abort
return l:self.text[l:self.index]
endfunction
function! g:TextQueue.Text() abort
return l:self.text[l:self.index : ]
endfunction
function! g:TextQueue.Empty() abort
return l:self.index >= strlen(l:self.text)
endfunction
function! g:TextQueue.ReInitialize() abort
let [l:self.text, l:self.index] = ['', 0]
endfunction
И используя эту очередь и дерево, создайте цикл оценки.Это та часть, в которой я сам не знал, как сделать это правильно, но после некоторого времени попытка пришла к функциональному (хотя и некрасивому и очень неэффективному, так как некоторые вещи гораздо больше, чем необходимо) коду:
function! s:Execute(map) abort
execute 'normal! ' . a:map.command
redraw
call g:TextQueue.Pop(strlen(a:map.keys))
let [a:map.keys, a:map.command] = ['', '']
endfunction
function! s:LimitTimeHasElapsed(time) abort
return (a:time + 1 < localtime())
endfunction
function! g:EvalTree.Start() abort
let l:time = localtime()
let l:stored = { 'keys': '', 'command': '' }
while g:TextQueue.CheckFirst() !=# "\<Esc>"
let l:char_code = getchar(0)
if l:char_code
call g:TextQueue.Push(nr2char(l:char_code))
endif
if !g:TextQueue.Empty()
let l:possible_maps = g:EvalTree.GetNode(g:TextQueue.Text())
if type(l:possible_maps) != type({})
if !empty(l:stored.command)
call s:Execute(l:stored)
elseif g:TextQueue.CheckFirst() !=# "\<Esc>"
call g:TextQueue.Pop()
endif
let l:time = localtime()
continue
endif
if l:possible_maps.command !=# ''
let l:stored.keys = g:TextQueue.Text()
let l:stored.command = l:possible_maps.command
if l:possible_maps.children == {} || s:LimitTimeHasElapsed(l:time)
call s:Execute(l:stored)
let l:time = localtime()
endif
elseif s:LimitTimeHasElapsed(l:time)
let [l:stored.keys, l:stored.command] = ['', '']
call g:TextQueue.pop()
let l:time = localtime()
endif
else
call g:TextQueue.ReInitialize()
let l:time = localtime()
endif
sleep 20m
endwhile
endfunction
Теперь вам просто нужно определить несколько отображений.Т.е.:
call g:EvalTree.AddMap('hw', ":echo 'hello workd'\<CR>")
call g:EvalTree.AddMap('h', 'h')
call g:EvalTree.AddMap('l', 'l')
call g:EvalTree.AddMap('j', 'j')
call g:EvalTree.AddMap('k', 'k')
call g:EvalTree.AddMap('H', '0')
call g:EvalTree.AddMap('L', '$')
call g:EvalTree.AddMap('K', 'H')
call g:EvalTree.AddMap('J', 'L')
call g:EvalTree.AddMap('dd', 'dd')
call g:EvalTree.AddMap('Z', 'yap')
call g:EvalTree.AddMap('D', 'dap')
call g:EvalTree.AddMap('ab', ":echo 'hello'\<CR>")
call g:EvalTree.AddMap('abc', ":echo 'world'\<CR>")
call g:EvalTree.AddMap('abcd', ":echo 'hello world'\<CR>")
И наконец, начните оценку:
call g:EvalTree.Start()
Вам всем также может быть интересно посмотреть мой плагин 'EXtend.vim' и егоФункция ReadLine ().Он не оценивает многосимвольные команды, но делает несколько действительно интересных вещей, таких как эмуляция курсоров и выделений внутри своего подрежима.