Come definire un operatore che si aggiorna mentre riceve input?


9

Ho definito una mappatura operatore che accetta una regione di testo, quindi chiede una stringa di input e quindi allinea la regione con Tabular usando la stringa di input come argomento. Funziona bene.

L'ho implementato in questo modo, usando vim-operator-user per aiutare a definire un nuovo operatore:

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

Quindi mi chiedevo se potevo farlo aggiornare al volo mentre inserivo l'espressione regolare con cui allinearmi. Il problema con l'approccio attuale è che devi annullare e ripetere se non stai usando l'espressione corretta.

Per il tentativo interattivo, ho fatto questo:

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

Si dovrebbe lavorare, ma l'allineamento non viene fatto prima mi ha colpito entrare, che è dopo ho ingresso ingresso finita, il che significa in modo efficace che funziona allo stesso modo la funzione non interattivo. Mi chiedo se il problema potrebbe essere qualcosa di simile allo schermo / al contenuto che non viene aggiornato durante l'esecuzione di un operatore, solo dopo.

Qualche idea su quale potrebbe essere il problema è apprezzata!

Risposte:


8

È necessario undoe redrawse si desidera visualizzare subito le modifiche nel buffer.

Ecco cosa funziona:

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

Spiegazione

La rangevariabile memorizza i segni '[e ']. Questo è per il senno.

Una variabile chiamata undoviene impostata in base alla lunghezza precedente di expr. Ciò significa che ogni volta che viene immesso un input dall'utente, l'iterazione successiva può essere annullata in modo sicuro prima dell'esecuzione Tabularize.

call match('', expr)verifica l'espressione per errori del modello. Se fallisce, undonon dovrebbe essere eseguito. I fallimenti del modello possono accadere mentre digiti atomi come \zs.

redrawcancellerà la riga di comando. Questo è il motivo per cui il prompt completo viene stampato su ogni iterazione.

Se il motivo contiene un errore, viene evidenziato con ErrorMsg.

acceptviene impostato quando <cr>viene premuto. Se è falso, annulla le modifiche (se presenti). Qualsiasi altra cosa che interrompe il ciclo si annulla.

Plug-in esistente

C'è un plugin chiamato vim-easy-align che può fare quello che stai tentando. Potresti trarre ispirazione dalla sua sceneggiatura .


Grazie mille per le chiare spiegazioni! Non sapevo che easy-align potesse farlo. La funzionalità di annullamento è stata la prossima cosa che volevo implementare, ma mi sono bloccato durante l'aggiornamento.
tusj,

Nessun problema 🙂 Ho appena aggiornato la risposta per evidenziare lo schema se ha un errore.
Tommy A

1
C'è un piccolo bug nell'implementazione corrente, dovuto a requisiti non scritti: se è stato digitato Esc o <CC>, l'operazione dovrebbe essere annullata. Ho risolto il primo caso aggiungendo: if c == "\<Esc>" && undo silent! undo endif Non sono sicuro di come rilevare per <CC> però.
tusj,

@tusj Ho aggiornato la risposta.
Tommy A
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.