スペル候補をループするコマンド


12

私はにマップzzしましたが1z=、これはほとんどの場合素晴らしいことですが、ときどき最初の提案は正しいものではありません。

したがって、他の提案を繰り返すために繰り返しzz(または.)続けたいと思います。

zz同じ単語の2番目はのようu2z=に機能し、3番目zzは同じu3z=ように機能します。

それを行う方法に関するアイデアはありますか?


編集:

@ nobe4の素晴らしい答えに基づいて、私は自分がやりたいことをやり遂げることができましたが、改善や提案がある場合に備えて、しばらくここに置いておきます。

let s:spell_position = []
let s:spell_count = 0
let s:spell_word = ""

function! LoopSpell()

    if s:spell_position != getpos('.') ||
            \ (s:spell_count > 0 && s:spell_word !~ expand("<cword>"))
        let s:spell_count = 0
        let s:spell_position = getpos('.')
    endif

    if s:spell_count > 0
        silent execute "normal! u"
    endif

    let s:current_word = expand("<cword>")
    if len(s:current_word) <= 0
        return
    endif

    let s:spell_suggestions = spellsuggest(expand(s:current_word))
    if len(s:spell_suggestions) <= 0
        return
    endif

    if s:spell_count >= len(s:spell_suggestions)
        let s:spell_word = s:current_word
        let s:spell_count = 0
    else
        let s:spell_word = s:spell_suggestions[s:spell_count]
        let s:spell_count += 1
    endif
    silent execute "normal! ciw" . s:spell_word
    let s:spell_position = getpos('.')

endfunction

nnoremap <c-m> :call LoopSpell()<CR>

(私はへのマッピングを変更する<c-m>ためにビトーさんのコメント@の。また、これは私が本当に速いの提案を通じて、それらのキーダウン、ソート、スクロールのを保持することができます。私のようにそれを考えています<c-mistake>。)


2
このサイトのユーザーが作成したこのプラグインを確認することをお勧めします。それは本当に、ワークフローをスペルチェックが向上:使用修正起動する:Correctコマンドを:あなたと正しいに単語をトラフナビゲートできるようになりますnN、分割されたウィンドウは、あなたができるだけでナビゲートそれらを介してのすべての修正提案を開きjkし、<CR>意志修正を適用します。
statox

@statox提案をありがとう。私はそれをチェックアウトしますが、zzコマンドに特定の事柄を迅速に修正してほしいです。
dbmrq

3
もともとzzウィンドウを現在の行の中心に置いていることを知っていることを願っています。おそらく、私がよく使うショートカットの1つです。また、チェックアウトする必要がzbありztます。
ビトー

@Vitor興味深いことに、私はそれを知りませんでした!私は通常scrolloffかなり高く保ちますが、それでも有用なようです。別のマッピングを検討します。ありがとう!
dbmrq

このvimスクリプトは、単語補完/スペル修正/類義語(aspell、シソーラス、辞書を使用)を行います。stackoverflow.com/a/46645434/ 476175
mosh

回答:


6

ここに私が思いついたものがあります:

スペル回転

スペルローテーション

特徴

  • '[そして']マークは作業中のテキストを追跡するために使用されています。他の場所で変更を加えると、提案された変更が効果的に「受け入れ」られます。
  • カウントを受け入れます。
  • を使用して後方に移動します zp
  • vim-repeatを使用して繰り返し可能。
  • 一度繰り返して元に戻すと、いくつの提案が循環したかに関係なく元の単語が復元されます。
  • ビジュアルモードで動作して、分割された単語の候補を取得します(例:「ヘッドライン」->「ヘッドライン」)
    • を使用'<して'>マークを付け、テキストを追跡します。
    • vim-repeatで再現性があるようには見えません。
  • 変更される元の単語は、名前のないレジスタに保持されます。
  • 元の提案、前の提案、現在の提案、および次の提案がコマンドラインに表示されます。
  • :SpellRotateSubAllオリジナルに一致するすべてのテキストを現在の提案に置き換える単純なコマンド。

プラグイン:spellrotate.vim

function! s:spell_rotate(dir, visual) abort
  if a:visual
    " Restore selection.  This line is seen throughout the function if the
    " selection is cleared right before a potential return.
    normal! gv
    if getline("'<") != getline("'>")
      echo 'Spell Rotate: can''t give suggestions for multiple lines'
      return
    endif
  endif

  if !&spell
    echo 'Spell Rotate: spell not enabled.'
    return
  endif

  " Keep the view to restore after a possible jump using the change marks.
  let view = winsaveview()
  let on_spell_word = 0

  if exists('b:_spell') && getline("'[") == getline("']")
    let bounds = b:_spell.bounds
    " Confirm that the cursor is between the bounds being tracked.
    let on_spell_word = bounds[0][0] == bounds[1][0]
          \ && view.lnum == bounds[0][0]
          \ && view.col >= bounds[0][1]
          \ && view.col <= bounds[1][1]
  endif

  " Make sure the correct register is used
  let register = &clipboard == 'unnamed'
        \ ? '*' : &clipboard == 'unnamedplus'
        \ ? '+' : '"'

  " Store the text in the unnamed register.  Note that yanking will clear
  " the visual selection.
  if on_spell_word
    if a:visual
      keepjumps normal! y
    else
      keepjumps normal! `[v`]y
    endif
    call winrestview(view)
  elseif a:visual
    keepjumps normal! y
  else
    keepjumps normal! viwy
  endif

  let cword = getreg(register)

  if !on_spell_word || b:_spell.alts[b:_spell.index] != cword
    " Start a new list of suggestions.  The word being replaced will
    " always be at index 0.
    let spell_list = [cword] + spellsuggest(cword)
    let b:_spell = {
          \ 'index': 0,
          \ 'bounds': [[0, 0], [0, 0]],
          \ 'cword': cword,
          \ 'alts': spell_list,
          \ 'n_alts': len(spell_list),
          \ }

    if len(b:_spell.alts) > 1
      " Do something to change the buffer and force a new undo point to be
      " created.  This is because `undojoin` is used below and it won't
      " work if we're not at the last point of the undo history.
      if a:visual
        normal! xP
      else
        normal! ix
        normal! x
      endif
    endif
  endif

  if a:visual
    normal! gv
  endif

  if len(b:_spell.alts) < 2
    echo 'Spell Rotate: No suggestions'
    return
  endif

  " Force the next changes to be part of the last undo point
  undojoin

  " Setup vim-repeat if it exists.
  silent! call repeat#set(printf("\<Plug>(SpellRotate%s%s)",
        \ a:dir < 0 ? 'Backward' : 'Forward', a:visual ? 'V' : ''))

  " Get the suggested, previous, and next text
  let i = (b:_spell.index + (a:dir * v:count1)) % b:_spell.n_alts
  if i < 0
    let i += b:_spell.n_alts
  endif

  let next = (i + 1) % b:_spell.n_alts
  let prev = (i - 1) % b:_spell.n_alts
  if prev < 0
    let prev += b:_spell.n_alts
  endif

  let next_word = b:_spell.alts[next]
  let prev_word = b:_spell.alts[prev]

  let b:_spell.index = i
  call setreg(register, b:_spell.alts[i])

  if a:visual
    normal! p`[v`]
  else
    keepjumps normal! gvp
  endif

  " Keep the original word in the unnamed register
  call setreg(register, b:_spell.cword)

  let b:_spell.bounds = [
        \ getpos(a:visual ? "'<" : "'[")[1:2],
        \ getpos(a:visual ? "'>" : "']")[1:2],
        \ ]

  echon printf('Suggestion %*s of %s for "', strlen(b:_spell.n_alts - 1), b:_spell.index, b:_spell.n_alts - 1)
  echohl Title
  echon b:_spell.cword
  echohl None
  echon '":  '

  if a:dir < 0
    echohl String
  else
    echohl Comment
  endif
  echon prev_word
  echohl None

  echon ' < '

  echohl Keyword
  echon b:_spell.alts[i]
  echohl None

  echon ' > '

  if a:dir > 0
    echohl String
  else
    echohl Comment
  endif
  echon next_word
  echohl None

  redraw
endfunction


function! s:spell_rotate_suball() abort
  if !exists('b:_spell') || len(b:_spell.alts) < 2
    return
  endif
  execute '%s/'.b:_spell.cword.'/'.b:_spell.alts[b:_spell.index].'/g'
endfunction


command! SpellRotateSubAll call s:spell_rotate_suball()

nnoremap <silent> <Plug>(SpellRotateForward) :<c-u>call <sid>spell_rotate(v:count1, 0)<cr>
nnoremap <silent> <Plug>(SpellRotateBackward) :<c-u>call <sid>spell_rotate(-v:count1, 0)<cr>
vnoremap <silent> <Plug>(SpellRotateForwardV) :<c-u>call <sid>spell_rotate(v:count1, 1)<cr>
vnoremap <silent> <Plug>(SpellRotateBackwardV) :<c-u>call <sid>spell_rotate(-v:count1, 1)<cr>

nmap <silent> zz <Plug>(SpellRotateForward)
nmap <silent> zp <Plug>(SpellRotateBackward)
vmap <silent> zz <Plug>(SpellRotateForwardV)
vmap <silent> zp <Plug>(SpellRotateBackwardV)

1
うわー、今私たちは話している!これをスタンドアロンのプラグインに変更して、将来の変更と改善をすべて同じ場所で維持できるようにする必要があります。または、興味がなければ私はそれを試みることができます。
-dbmrq

@danielbmarques簡単です、どうぞ。github.com/ tweekmonster
トミー

素晴らしい、ありがとう!それがまさに私が望んだものであり、それ以上であるため、正しい答えとしてあなたの答えを受け入れます。
dbmrq

@danielbmarques問題ありません。おもしろい質問と解決策を探していますit
トミーA

5

@statoxが示唆したように、私が書いたプラグインvimcorrectを使用できます。

基本的にどのように機能するかを説明しますので、その一部を再利用したい場合は可能です。

私が直接使用次のスペルミスの単語に集中する]s[s、彼らは前/次のマッチにジャンプして。現在の単語を強調表示するカスタムマッチ関数を定義しました。

ここに画像の説明を入力してください

matchadd('error', '\%'.line('.').'l'.'\%'.col('.').'c'.s:current_word)

error現在の行/列で現在の単語を一致グループに追加します(同じ行で複数の一致を防ぐため)。


このspellbadword()関数は、カーソルの下の単語に対して可能な修正のリストを返します。

このリストをバッファに表示するだけで<CR>、つづりの間違った単語を現在の行(つまり、修正された可能性のある単語)に置き換えるようにマッピングします。


私はまた、マップnNする]s[s、私は検索し、それらを押すために使用していて、。

q プラグインを終了し、スプリットを閉じてハイライトを削除するためにマップされます。

:まだ非常に不安定ですが、すぐに変更を加える予定です。このプラグインを改善したい/したい場合は、気軽にプルリクエストをフォーク/オープンしてください。


説明してくれてありがとう。プラグインは素晴らしく、私は間違いなくそれを使用します。zzただし、コマンドは引き続き必要なので、特別なモードに入らずにすばやく修正できます。vimcorrectもし私がそれを見つけ出せば、それを追加できるかもしれません。:)
dbmrq

まあ、私は間違いなくカスタマイズを追加する必要があります。したがって、カスタムマッピングの定義は、必要に応じて追加できる改善点かもしれません:)(vimscriptで開発を開始する場合、学習するのに良い方法です)
-nobe4

2

動作するはずの関数は次のとおりです。

let s:last_spell_changedtick = {}

function! LoopSpell()
  " Save current line and column
  let l:line = line('.')
  let l:col = col('.')

  " check if the current line/column is already in the last_spell_changedtick
  if has_key(s:last_spell_changedtick, l:line) == 0
    let s:last_spell_changedtick[l:line] = {}
  endif

  if has_key(s:last_spell_changedtick[l:line], l:col) == 0
    let s:last_spell_changedtick[l:line][l:col] = 0
  endif

  " If the value already exists, undo the change
  if s:last_spell_changedtick[l:line][l:col] != 0
    normal u
  endif

  " Get the current word
  let l:current_word = spellbadword()
  if len(l:current_word) == 0
    call <SID>Quit()
  endif

  " Get suggestions for the current word
  let s:current_word = l:current_word[0]
  let l:suggestions = spellsuggest(expand(s:current_word))

  " If the current word present no spelling suggestions, pass
  if len(suggestions) <= 0
    return
  endif

  " Replace the word with suggestion
  silent execute "normal! ce" . l:suggestions[s:last_spell_changedtick[l:line][l:col]]
  normal! b

  " Increment the count
  let s:last_spell_changedtick[l:line][l:col] = s:last_spell_changedtick[l:line][l:col] + 1

endfunction

function! LoopConfirm()
  let s:last_spell_changedtick = {}
endfunction

nnoremap zz :call LoopSpell()<CR>
nnoremap z= :call LoopConfirm()<CR>

基本的な考え方は、変更されたすべての単語を行/列のペアにマップし(1つの要素に対してのみ機能しないようにするため)、要素が既に変更されているかどうかを確認することです。

置換を行うには、ほとんど私のプラグインが何をするのですか:

  • 現在のスペルミスのある単語を取得する
  • 修正が存在するかどうかを確認します
  • 単語を修正された提案に置き換えます

これを使用するときに、スペルミスの単語に戻りたい場合は、単にを押しuます。

このLoopConfirm関数は辞書をリセットするので、テキストを変更した場合、衝突を防ぐためにそれを呼び出すことができます。

問題が発生した場合/質問がある場合はお知らせください。


うーん、それはよさそうだ。ただし、まだ多くの問題があります。「teh qick borwn foz jums ofer teh lazi dor」のようなフレーズを取り、各単語をそのように修正してみてください。「the」を「the」に変えることはできませんが、リストでは4位です。「qick」は機能しますが、「brown」がリストの最初にある場合でも「borwn」は他の何かに変更され、その後すぐに「foz」にスキップします。私はそれを決して乗り越えなかった。また、私は余分なz=部分が好きではありませんが、残りがうまくいけば、おそらく自分でそれを回避する方法を見つけることができます。しかし、これは私が望むものに非常に近づいています。私はそれを修正しようとし続けます。ありがとう!
dbmrq

私の更新を参照してください、私はすぐに増分を追加します:)ええ、私はz=どちらにも満足していません。ただし、この方法では、現在地の参照を保持する必要があります。しかし、すべての参照を同時に保持する必要がない場合は、それを単純化できます:)
nobe4

「すべての参照を同時に保持する」という意味がわかりませんが、カーソルが移動するたびに辞書をリセットすることはできませんか?この関数は、カーソルが前回呼び出されたときと同じ場所にあるかどうかをチェックし、そうでない場合はリセットします。
dbmrq

また、カーソルが単語の先頭にない場合は、正しく機能していないようです。その文のすべての間違いを修正して、各単語の中央にカーソルを置きます。すぐに次へスキップします。
dbmrq

1
わかった、わかった!最後の編集を確認してください。これはほぼ完璧に機能するようです。他の誰かが追加するものがあるかどうかを確認するために、質問をもう少し開いたままにしますが、あなたの答えは素晴らしかったです、ありがとう。:)
dbmrq

2

他の答えとは別に、実際にはVimに組み込まれた方法があります<C-x>s。これは、Vimの挿入モード完了メニューを使用します。

<C-x>s挿入モードからを押すと、カーソルの下の単語が最初の候補に修正され、補完メニューが表示され、さらに候補が表示されます(ある場合)。この'completeopt'設定を使用して、完了メニューの一部の設定をカスタマイズできます。

これは挿入モードでのみ機能し、使用を使用<C-x><C-s>すると問題が発生する可能性があるため(以下の注を参照)、少し面倒なので、独自のマッピングを定義できます。

inoremap <expr> <C-@>  pumvisible() ?  "\<C-n>" : "\<C-x>s"
nnoremap <expr> <C-@> pumvisible() ?  "i\<C-n>" : "i\<C-x>s"

<C-@> Control + Spaceです。

こちらもご覧ください :help ins-completion :help i_CTRL-X_s


個人的には、作業のスペルチェックを行うか、コードに通常の自動補完を使用する場合に「推測」する、より高度なバージョンを使用します。

fun! GuessType()
    " Use omnicomplete for Go
    if &filetype == 'go'
        let l:def = "\<C-x>\<C-o>"
    " Keyword complete for anything else
    else
        let l:def = "\<C-x>\<C-n>"
    endif

    " If we have spell suggestions for the current word, use that. Otherwise use
    " whatever we figured out above.
    try
        if spellbadword()[1] != ''
            return "\<C-x>s"
        else
            return l:def
        endif
    catch
        return l:def
    endtry
endfun

inoremap <expr> <C-@>  pumvisible() ?  "\<C-n>" : GuessType()
inoremap <expr> <Down> pumvisible() ? "\<C-n>" : "\<Down>"
inoremap <expr> <Up> pumvisible() ? "\<C-p>" : "\<Up>"
nnoremap <expr> <C-@> pumvisible() ?  "i\<C-n>" : 'i' . GuessType()

ほぼ同様のことを行うプラグインもいくつかあると思います(SuperTabはかなり人気があります)が、期待どおりに動作させることはできませんでした。


警告:端末からVimを使用している場合、<C-s>「出力を停止する」ことを意味します。これが両方で<C-x><C-s> あり <C-x>s、デフォルトでマッピングされる理由です。誤っ<C-q>て押し<C-s>た場合に出力を続行するために使用します。<C-s>使用しない場合は無効にすることもできます(この質問を参照)。GVimを使用している場合、これは無視できます。

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