Jak zdefiniować operatora, który aktualizuje się, gdy pobiera dane?

9

Zdefiniowałem mapowanie operatora, które pobiera region tekstu, a następnie prosi o ciąg wejściowy, a następnie wyrównuje region z tabelą, używając argumentu wejściowego jako argumentu. Działa ładnie.

Zaimplementowałem to w ten sposób, używając vim-operator-user, aby pomóc zdefiniować nowego operatora:

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

Potem zastanawiałem się, czy mogę zaktualizować go w locie, wprowadzając wyrażenie regularne w celu dostosowania. Problem z obecnym podejściem polega na tym, że musisz cofnąć, a następnie powtórzyć, jeśli nie używasz poprawnego wyrażenia.

Dla interaktywnej próby zrobiłem to:

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

To powinno działać, ale nie odbywa ustawiające zanim uderzę wejść, czyli po Skończyłem wejście przystawienia, skutecznie co oznacza, że działa w ten sam sposób jak nieinterakcyjnym funkcji. Zastanawiam się, czy problemem może być coś takiego, że ekran / treść nie jest aktualizowana podczas wykonywania operatora, dopiero po.

Doceniamy wszelkie pomysły na problem.

tusj
źródło

Odpowiedzi:

8

Musisz undoi redrawjeśli chcesz od razu zobaczyć zmiany w buforze.

Oto, co działa:

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

Wyjaśnienie

rangeZmienna przechowuje '[i ']znaki. To dla zdrowia psychicznego.

Wywoływana zmienna undojest ustawiana na podstawie poprzedniej długości expr. Oznacza to, że ilekroć użytkownik wprowadzi dane, następną iterację można bezpiecznie cofnąć przed wykonaniem Tabularize.

call match('', expr)testuje wyrażenie pod kątem błędów wzorca. Jeśli zawiedzie, undonie należy go wykonywać. Błędy wzorców mogą się zdarzyć podczas pisania atomów, takich jak \zs.

redrawwyczyści wiersz poleceń. Dlatego na każdej iteracji drukowany jest pełny monit.

Jeśli wzór zawiera błąd, jest podświetlony za pomocą ErrorMsg.

acceptustawia się po <cr>naciśnięciu. Jeśli jest to fałsz, cofnij zmiany (jeśli istnieją). Cokolwiek innego, co przerywa pętlę, zostaje anulowane.

Istniejąca wtyczka

Istnieje wtyczka o nazwie vim-easy-align, która może robić to, co próbujesz. Możesz czerpać inspirację z jego scenariusza .

Tommy A.
źródło
Dziękuję bardzo za jasne wyjaśnienia! Nie wiedziałem, że easy-align może to zrobić. Funkcja cofania była kolejną rzeczą, którą chciałem wdrożyć, ale utknąłem przy aktualizacji.
tusj
Nie ma problemu just Właśnie zaktualizowałem odpowiedź, aby podświetlić wzór, jeśli wystąpił błąd.
Tommy A
1
W bieżącej implementacji występuje niewielki błąd związany z niepisanym wymaganiem: jeśli zostało wpisane Esc lub <CC>, operacja powinna zostać anulowana. Naprawiłem pierwszy przypadek, dodając: if c == "\<Esc>" && undo silent! undo endif Nie jestem pewien, jak wykryć dla <CC>.
tusj
@tusj Zaktualizowałem odpowiedź.
Tommy A