¿Cómo definir un operador que está actualizando mientras está recibiendo información?

9

He definido un mapeo de operador que toma una región de texto, luego pide una cadena de entrada y luego alinea la región con Tabular usando la cadena de entrada como argumento. Funciona muy bien

Lo he implementado así, usando vim-operator-user para ayudar a definir un nuevo 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

Luego me pregunté si podría actualizarlo sobre la marcha mientras ingresaba la expresión regular para alinearme. El problema con el enfoque actual es que debe deshacer y rehacer si no está utilizando la expresión correcta.

Para el intento interactivo, hice esto:

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

Se debe trabajar, pero la alineación no se hace antes de que golpee entrar, es decir después de que tenga entrada que entran acabada, lo que significa que efectivamente funciona del mismo modo que la función no interactivo. Me pregunto si el problema podría ser algo así como la pantalla / contenido que no se actualiza durante la ejecución de un operador, solo después.

¡Se agradece cualquier idea sobre cuál podría ser el problema!

tusj
fuente

Respuestas:

8

Es necesario undoy redrawsi desea ver cambios en el búfer de inmediato.

Esto es lo 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

Explicación

La rangevariable almacena las marcas '[y ']. Esto es por el bien de la cordura.

Una variable llamada undose establece en función de la longitud anterior de expr. Esto significa que siempre que haya una entrada del usuario, la siguiente iteración se puede deshacer de manera segura antes de ejecutarla Tabularize.

call match('', expr)prueba la expresión para errores de patrón. Si falla, undono debe ejecutarse. Las fallas de patrones pueden suceder a medida que escribe átomos como \zs.

redrawborrará la línea de comando. Es por eso que la solicitud completa se imprime en cada iteración.

Si el patrón contiene un error, se resalta con ErrorMsg.

acceptse establece cuando <cr>se presiona. Si es falso, deshaga los cambios (si los hay). Cualquier otra cosa que rompa el ciclo se cancela.

Complemento existente

Hay un complemento llamado vim-easy-align que puede hacer lo que está intentando. Podrías inspirarte en su guión .

Tommy A
fuente
¡Muchas gracias por las explicaciones claras! No sabía que la alineación fácil podría hacer eso. La funcionalidad de deshacer fue lo siguiente que quería implementar, pero me quedé estancado en la actualización.
tusj
No hay problema just Acabo de actualizar la respuesta para resaltar el patrón si tiene un error.
Tommy A
1
Hay un pequeño error en la implementación actual, debido a un requisito no escrito: si se ha escrito Esc o <CC>, la operación debería cancelarse. He solucionado el primer caso agregando: Sin if c == "\<Esc>" && undo silent! undo endif embargo, no estoy seguro de cómo detectar <CC>.
tusj
@tusj Actualicé la respuesta.
Tommy A