入力を取得中に更新される演算子を定義するにはどうすればよいですか?


9

テキストの領域を取り、入力文字列を要求し、入力文字列を引数として使用して領域を表形式に揃える演算子マッピングを定義しました。それはうまくいきます。

私は、vim-operator-user使用して新しい演算子を定義するのに役立つように、次のように実装しました。

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

次に、整列する正規表現を入力しながら、その場で更新できるかどうか疑問に思いました。現在のアプローチの問題は、正しい式を使用していない場合、元に戻してからやり直す必要があることです。

インタラクティブな試みのために、私はこれを行いました:

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

これ機能するはずですが、Enterを押す前、つまり入力の入力が終了した後では、整列は行われません。つまり、非インタラクティブ機能と同じように機能します。問題は、オペレーターの実行中に画面/コンテンツが更新されない後のようなものなのだろうか。

問題が何であるかについてのアイデアは高く評価されます!

回答:


8

する必要がundoありredraw、バッファの変更をすぐに確認したい場合。

機能するものは次のとおりです。

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

説明

range変数を格納'[および']マーク。これは正気のためです。

呼び出される変数undoは、の以前の長さに基づいて設定されexprます。これは、ユーザーからの入力があるときはいつでも、次の反復が実行前に安全に元に戻せることを意味しますTabularize

call match('', expr)式のパターンエラーをテストします。失敗した場合はundo実行しないでください。のようなアトムを入力しているときに、パターンエラーが発生する可能性があります\zs

redrawコマンドラインをクリアします。これが、すべての反復で完全なプロンプトが出力される理由です。

パターンにエラーが含まれている場合は、で強調表示されErrorMsgます。

accept<cr>押すと設定されます。falseの場合は、変更を元に戻します(ある場合)。ループを壊す他のものはキャンセルします。

既存のプラグイン

あなたがしようとしていることを行うことができるvim-easy-alignと呼ばれるプラグインがあります。スクリプトからインスピレーションを得ることができます


分かりやすい説明ありがとうございます!easy-alignがそれを実行できることを知りませんでした。次に取り消す機能が実装したかったのですが、更新に行き詰まりました。
tusj

問題ありません。パターンにエラーがある場合、パターンを強調表示するように回答を更新しました。
トミーA

1
現在の実装には、書かれていない要件による小さなバグがあります。Escまたは<CC>が入力された場合、操作はキャンセルされます。最初のケースを次のように追加して修正しましたif c == "\<Esc>" && undo silent! undo endif 。ただし、<CC>を検出する方法がわかりません。
tusj

@tusj回答を更新しました。
トミーA
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.