Como definir um operador que está atualizando enquanto recebe entradas?

9

Eu defini um mapeamento de operador que pega uma região de texto, solicita uma string de entrada e alinha a região com Tabular usando a string de entrada como argumento. Funciona bem.

Eu o implementei assim, usando o vim-operator-user para ajudar a definir um novo operador:

map \aa <Plug>(operator-align)
call operator#user#define('align', 'Align')
function! Align(motion_wiseness)
    let expr = input("align: ")
    if len(expr) != 0
        execute "'[,']Tabularize /".expr
    endif
endfunction


function! Getchar()
    let c = getchar()
    if c =~ '^\d\+$'
        let c = nr2char(c)
    endif
    return c
endfunction

Então me perguntei se poderia atualizá-lo rapidamente, enquanto inseria a expressão regular para alinhar. O problema com a abordagem atual é que você precisa desfazer e refazer se não estiver usando a expressão correta.

Para a tentativa interativa, fiz o seguinte:

map \== <Plug>(operator-align-interactive)
call operator#user#define('align-interactive', 'AlignInteractive')
function! AlignInteractive(motion_wiseness)
    let prompt = "Align: "
    echon prompt
    let expr = ""
    let c = Getchar()
     " CR has to be checked for separately as it acts as putting the cursor back to zero position
    while c != "\<Esc>" && c != "\<CR>"
        if c == "\<BS>"
            if len(expr) != 0
                let expr = expr[0:-2]
                echon "\<CR>".substitute(expr, ".", " ", "g")
                echon "\<CR>".prompt.expr
            endif
        else
            let expr .= c
            echon c
            let cmd = "'[,']Tabularize /".expr
            execute cmd 
        endif
        let c = Getchar()
    endwhile
endfunction

Ele deve funcionar, mas o alinhamento não é feito antes de eu pressionar enter, ou seja, depois de terminar de inserir a entrada, o que significa efetivamente que funciona da mesma maneira que a função não interativa. Gostaria de saber se o problema poderia ser algo como a tela / conteúdo não sendo atualizado durante a execução do operador, somente depois.

Todas as idéias sobre o que o problema poderia ser apreciado!

tusj
fonte

Respostas:

8

Você precisa undoe redrawse deseja ver as alterações no buffer imediatamente.

Aqui está o que funciona:

function! AlignInteractive(motion_wiseness)
  let prompt = "Align: "
  let undo = 0
  let expr = ""
  let range = line("'[").','.line("']")
  let failed = 0
  let accept = 0

  echon prompt
  let c = Getchar()

  while c != "\<Esc>" && c != "\<c-c>"
    let undo = len(expr)

    if c == "\<CR>"
      let accept = 1
      break
    elseif c == "\<BS>"
      if len(expr)
        let expr = expr[0:-2]
      endif
    else
      let expr .= c
    endif

    if undo && !failed
      silent! undo
    endif

    if len(expr)
      try
        call match('', expr)
        let failed = 0
        execute range."Tabularize /".expr
      catch //
        let failed = 1
      endtry
    endif

    redraw

    echon prompt
    if failed
      echohl ErrorMsg
    endif
    echon expr
    echohl None
    let c = Getchar()
  endwhile

  if !accept && len(expr) && !failed
    silent! undo
  endif
endfunction

Explicação

A rangevariável armazena as marcas '[e ']. Isto é pelo bem da sanidade.

Uma variável chamada undoé definida com base no comprimento anterior de expr. Isso significa que sempre que houver entrada do usuário, a próxima iteração poderá ser desfeita com segurança antes de executar Tabularize.

call match('', expr)testa a expressão para erros de padrão. Se falhar, undonão deve ser executado. Falhas de padrão podem acontecer quando você digita átomos como \zs.

redrawirá limpar a linha de comando. É por isso que o prompt completo é impresso em todas as iterações.

Se o padrão contiver um erro, ele será destacado com ErrorMsg.

accepté definido quando <cr>pressionado. Se for falso, desfaça as alterações (se houver). Qualquer outra coisa que interrompa o ciclo é cancelada.

Plugin existente

Existe um plugin chamado vim-easy-align que pode fazer o que você está tentando. Você pode se inspirar em seu roteiro .

Tommy A
fonte
Muito obrigado pelas explicações claras! Eu não sabia que o alinhamento fácil poderia fazer isso. A funcionalidade desfazer foi a próxima coisa que eu queria implementar, mas fiquei paralisada na atualização.
tusj
Sem problemas just Acabei de atualizar a resposta para destacar o padrão se houver um erro.
Tommy A
11
Há um pequeno erro na implementação atual, devido a requisitos não escritos: se Esc ou <CC> tiver sido digitado, a operação deverá ser cancelada. Corrigi o primeiro caso adicionando: if c == "\<Esc>" && undo silent! undo endif embora não tenha certeza de como detectar o <CC>.
tusj
@tusj Atualizei a resposta.
Tommy A