Wie definiere ich einen Operator, der während der Eingabe aktualisiert wird?

9

Ich habe eine Operatorzuordnung definiert, die einen Textbereich verwendet, dann nach einer Eingabezeichenfolge fragt und dann den Bereich mit Tabellarisch ausrichtet, wobei die Eingabezeichenfolge als Argument verwendet wird. Es funktioniert gut.

Ich habe es so implementiert und vim-operator-user verwendet , um einen neuen Operator zu definieren:

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

Dann fragte ich mich, ob ich es im laufenden Betrieb aktualisieren könnte, während ich den regulären Ausdruck eingebe, mit dem ich mich ausrichten möchte. Das Problem beim aktuellen Ansatz ist, dass Sie rückgängig machen und dann wiederholen müssen, wenn Sie nicht den richtigen Ausdruck verwenden.

Für den interaktiven Versuch habe ich Folgendes gemacht:

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

Es sollte funktionieren, aber das Ausrichten erfolgt nicht, bevor ich die Eingabetaste drücke, dh nachdem ich die Eingabe eingegeben habe, was effektiv bedeutet, dass es genauso funktioniert wie die nicht interaktive Funktion. Ich frage mich, ob das Problem so etwas wie der Bildschirm / Inhalt sein könnte, der während einer Operatorausführung erst danach aktualisiert wird.

Irgendwelche Ideen, was das Problem sein könnte, werden geschätzt!

tusj
quelle

Antworten:

8

Sie müssen undound redrawwenn Sie Änderungen im Puffer sofort sehen möchten.

Folgendes funktioniert:

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

Erläuterung

Die rangeVariable speichert die Zeichen '[und ']. Dies ist der Vernunft zuliebe.

Eine aufgerufene Variable undowird basierend auf der vorherigen Länge von festgelegt expr. Dies bedeutet, dass die nächste Iteration bei jeder Eingabe durch den Benutzer sicher rückgängig gemacht werden kann, bevor sie ausgeführt wird Tabularize.

call match('', expr)testet den Ausdruck auf Musterfehler. Wenn es fehlschlägt, undosollte nicht ausgeführt werden. Musterfehler können auftreten, wenn Sie Atome wie z \zs.

redrawlöscht die Befehlszeile. Aus diesem Grund wird bei jeder Iteration die vollständige Eingabeaufforderung gedruckt.

Wenn das Muster einen Fehler enthält, wird es mit hervorgehoben ErrorMsg.

acceptwird eingestellt, wenn <cr>gedrückt wird. Wenn es falsch ist, machen Sie die Änderungen rückgängig (falls vorhanden). Alles andere, was die Schleife durchbricht, wird abgebrochen.

Vorhandenes Plugin

Es gibt ein Plugin namens vim-easy-align , das genau das kann, was Sie versuchen. Sie könnten sich von seinem Drehbuch inspirieren lassen .

Tommy A.
quelle
Vielen Dank für die klaren Erklärungen! Ich wusste nicht, dass Easy-Align das kann. Die Rückgängig-Funktion war das nächste, was ich implementieren wollte, aber ich blieb bei der Aktualisierung hängen.
Tusj
Kein Problem 🙂 Ich habe gerade die Antwort aktualisiert, um das Muster hervorzuheben, wenn ein Fehler vorliegt.
Tommy A
1
In der aktuellen Implementierung gibt es einen kleinen Fehler aufgrund nicht geschriebener Anforderungen: Wenn Esc oder <CC> eingegeben wurde, sollte der Vorgang abgebrochen werden. Ich habe den ersten Fall behoben, indem if c == "\<Esc>" && undo silent! undo endif ich hinzugefügt habe: Ich bin mir nicht sicher, wie ich <CC> erkennen soll.
Tusj
@tusj Ich habe die Antwort aktualisiert.
Tommy A