Bagaimana cara menentukan operator yang memperbarui saat sedang mengambil input?

9

Saya telah menetapkan pemetaan operator yang mengambil wilayah teks, lalu meminta string input dan kemudian menyelaraskan wilayah dengan Tabular menggunakan string input sebagai argumen. Ini bekerja dengan baik.

Saya telah mengimplementasikannya seperti ini, menggunakan vim-operator-pengguna untuk membantu mendefinisikan operator baru:

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

Lalu saya bertanya-tanya apakah saya bisa memperbaruinya dengan cepat sambil memasukkan ekspresi reguler untuk disejajarkan. Masalah dengan pendekatan saat ini adalah Anda harus membatalkan dan kemudian mengulang jika Anda tidak menggunakan ekspresi yang benar.

Untuk upaya interaktif, saya membuat ini:

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

Ini seharusnya bekerja tetapi pelurusan tidak dilakukan sebelum saya menekan enter, yaitu setelah saya selesai memasukkan input, secara efektif berarti bahwa ia bekerja dengan cara yang sama seperti fungsi non-interaktif. Saya bertanya-tanya apakah masalahnya bisa seperti layar / konten tidak diperbarui selama eksekusi operator, hanya setelahnya.

Setiap ide tentang apa masalahnya bisa dihargai!

tusj
sumber

Jawaban:

8

Anda perlu undodan redrawjika Anda ingin melihat perubahan dalam buffer segera.

Inilah yang berhasil:

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

Penjelasan

The rangevariabel menyimpan '[dan ']tanda. Ini demi kewarasan.

Variabel yang dipanggil undodiatur berdasarkan panjang sebelumnyaexpr . Ini berarti setiap kali ada input dari pengguna, iterasi berikutnya dapat dengan aman dibatalkan sebelum dieksekusi Tabularize.

call match('', expr)menguji ekspresi untuk kesalahan pola. Jika gagal, undosebaiknya tidak dieksekusi. Kegagalan pola dapat terjadi saat Anda mengetik atom seperti \zs.

redrawakan menghapus baris perintah. Inilah sebabnya mengapa prompt penuh dicetak pada setiap iterasi.

Jika pola berisi kesalahan, itu disorot dengan ErrorMsg.

acceptdiatur saat <cr>ditekan. Jika salah, batalkan perubahan (jika ada). Hal lain yang mematahkan loop membatalkan.

Plugin yang ada

Ada sebuah plugin bernama vim-easy-align yang dapat melakukan apa yang Anda coba. Anda dapat mengambil inspirasi dari skripnya .

Tommy A
sumber
Terima kasih banyak atas penjelasan yang jelas! Saya tidak tahu bahwa pelurusan mudah dapat melakukannya. Fungsi undo adalah hal berikutnya yang ingin saya terapkan, tetapi saya terjebak pada pembaruan.
tusj
Tidak masalah 🙂 Saya baru saja memperbarui jawaban untuk menyoroti pola jika ada kesalahan.
Tommy A
1
Ada bug kecil dalam implementasi saat ini, karena persyaratan tidak tertulis: jika Esc atau <CC> telah diketik, operasi harus dibatalkan. Saya telah memperbaiki case pertama dengan menambahkan: if c == "\<Esc>" && undo silent! undo endif Saya tidak yakin bagaimana cara mendeteksi <CC>.
tusj
@tusj Saya memperbarui jawabannya.
Tommy A