命令遍历拼写建议


12

我映射zz1z=,这在大多数情况下是很好的,但是时不时地,第一个建议不是正确的建议。

因此,我想继续重复zz(或.)循环浏览其他建议。

zz那么,在同一个单词上的第二u2z=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>由于@Vitor的注释,我将映射更改为。这也使我可以按住这些键并快速滚动浏览建议。我认为是<c-mistake>。)


2
我建议您检查由该站点的用户制作的此插件。它确实提高了拼写检查的工作流程:开始纠正你使用的:Correct命令:你可以的话导航槽以解决nN,分割窗口,你可以简单地通过浏览他们的修正建议打开jk<CR>会应用更正。
statox

@statox感谢您的建议。我将进行检查,但是我仍然希望我的zz命令快速修复特定问题。
dbmrq '16

3
希望您知道最初zz使窗口围绕当前行居中。这可能是我经常使用的快捷方式之一。您还应该结帐zbzt
Vitor

@Vitor有趣,我不知道!我通常会保持scrolloff很高的水平,但这似乎仍然有用,我将考虑另一个映射。谢谢!
dbmrq '16

这个vim脚本可以完成单词补全/拼写校正/同义词(使用aspell,词库,词典) stackoverflow.com/a/46645434/476175
mosh

Answers:


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 '16

@danielbmarques很简单,到这里开始:github.com/tweekmonster/spellrotate.vim
Tommy,

太好了,谢谢!我会接受您的答案是正确的答案,因为这正是我想要的,还有更多,我将感谢@ nobe4的所有努力和帮助。
dbmrq

@danielbmarques没问题。我正在寻找有趣的问题和解决方案
汤米(Tommy 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 '16

好吧,我当然需要添加更多的自定义。因此,定义自定义映射可能是一种改进,您可以根据需要添加:)(如果您开始在vimscript中进行开发,则可能是学习的一种好方法)
nobe4 2016年

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>

基本思想是将每个更改的单词映射到行/列对(这样它就不能仅对一个元素起作用),并检查该元素是否已被修改。

要进行替换,这几乎就是我的插件的作用:

  • 获取当前拼写错误的单词
  • 检查是否存在更正
  • 用更正的建议替换单词

使用此功能时,如果您想返回拼写错误的单词,只需按一下u

LoopConfirm函数重置词典,因此,如果您更改文本,则可以调用它以防止冲突。

如果您遇到任何问题/有任何疑问,请告诉我。


嗯,看起来不错。但是,它仍然存在许多问题。采取“像这样的短语”这样的短语,并尝试以这种方式纠正每个单词。尽管它在列表中排在第4位,但我永远无法将“ teh”变成“ the”。“快速”有效,但是“无聊”更改为其他内容,即使“棕色”首先出现在列表中,然后直接跳至“ foz”。我从来没有过去。另外,我不喜欢多余的z=部分,但如果其余部分都可行的话,我可能可以找到一种解决该问题的方法。不过,这已经非常接近我想要的了。我会继续尝试修复它。谢谢!
dbmrq

看到我的更新,我太快增加了一个增量:)是的,我对任何一个都不满意z=。但是使用这种方法时,您需要保留自己所在位置的参考。但是,如果您不需要同时保留所有引用,则可以简化一下:)
nobe4 '16

我不确定“同时保留所有引用”是什么意思……但是,当光标移动时,我们不能仅重置字典吗?该函数将检查游标是否与上次调用时位于同一位置,如果不是,则将其重置。
dbmrq

同样,当光标不在单词开头时,它似乎无法正常工作。尝试纠正该句子中的每个错误,将光标置于每个单词的中间。我马上跳到下一个。
dbmrq '16

1
好吧,我想我明白了!检查我的上一次编辑。这似乎非常完美。我将问题悬而未决,看看是否还有其他人要添加,但是您的回答很好,谢谢。:)
dbmrq

2

除了其他答案,实际上Vim还内置了一种方法:<C-x>s。这将使用Vim的插入模式完成菜单。

<C-x>s从插入模式下按应该将光标下的单词更正为第一个建议,并显示带有其他建议(如果有)的完成菜单。您可以使用该'completeopt'设置来自定义完成菜单的某些设置。

有点烦人的是,这仅适用于插入模式,使用using <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,则可以忽略它。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.