Как создать «новый режим» для vim (т.е. зациклить и получить пользовательский ввод, затем выполнить связанные действия) - PullRequest
4 голосов
/ 23 февраля 2011

Я пытаюсь создать что-то вроде "нового режима" в vim.Детали режима не важны, но есть одна вещь, которую мне нужно сделать.

Мне нужно сделать что-то вроде следующего псевдокода:

get user input (movement keys like "j" or complex keys like "dd")
while user_input != <esc>
   execute the user input
endwhile

В другомслова, мне нужен цикл, который будет читать, что пользователь делает, а затем выполнить соответствующее действие.

Я уже получил следующий код:

let char = nr2char(getchar())
while char =~ '^\w$'
    execute "normal ". char
    let char = nr2char(getchar())
endwhile

Это прекрасно работает для пользователядвижения (j, k и т. д.), но не для более сложных многосимвольных команд, таких как dd.

Кроме того, это немного раздражает, но курсор исчезает во время getchar ()Это означает, что вы фактически не можете видеть курсор (это менее важно из-за того, что я пытаюсь сделать, но, надеюсь, также есть решение).

Кто-нибудь знает, как я могу получитьмногосимвольные действия для работы?

Ответы [ 3 ]

4 голосов
/ 23 февраля 2011

Думаю, вас может заинтересовать submode.vim , если не использовать его, чтобы хотя бы посмотреть, как они реализовали эту функцию.

1 голос
/ 23 февраля 2011

Я обычно переопределяю локально (например, :h map-<buffer>), что должен изменить этот новый режим.И я также отменяю <esc>, чтобы отменить регистрацию этих вещей в режиме.

Это более легкий подход IMO.

0 голосов
/ 02 сентября 2018

Просто для записи, для создания нового режима, который принимает команды различной длины, вы обычно используете что-то вроде дерева разбора с ключами и командами.Вот простая версия в 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 ().Он не оценивает многосимвольные команды, но делает несколько действительно интересных вещей, таких как эмуляция курсоров и выделений внутри своего подрежима.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...