Commande pour parcourir les suggestions d'orthographe

12

Je cartographié zzà 1z=, qui est grande la plupart du temps, mais de temps en temps la première suggestion n'est pas la bonne.

Je voudrais donc continuer à répéter zz(ou .) pour parcourir les autres suggestions.

Une seconde zzsur le même mot fonctionnerait donc comme u2z=, une troisième zzfonctionnerait comme u3z=et ainsi de suite.

Des idées sur la façon de procéder?


Éditer:

Sur la base de la réponse impressionnante de @ nobe4, j'ai réussi à faire ce que je veux, mais je vais le laisser ici pendant un moment au cas où quelqu'un aurait des améliorations ou des suggestions:

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>

(J'ai changé le mappage en <c-m>raison du commentaire de @ Vitor. Cela me permet également de maintenir ces touches enfoncées et de faire défiler les suggestions très rapidement. J'y pense comme <c-mistake>.)

dbmrq
la source
2
Je vous suggère de vérifier ce plugin qui a été créé par un utilisateur de ce site. Il améliore vraiment le flux de travail de vérification orthographique: pour commencer à corriger vous utilisez la :Correctcommande: vous serez en mesure de naviguer creux les mots correcte avec net N, une fenêtre divisée en deux ouvre toutes les suggestions de correction , vous pouvez simplement naviguer à travers eux avec jet ket <CR>sera appliquer la correction.
statox
@statox Merci pour la suggestion. Je vais le vérifier, mais je veux toujours que ma zzcommande corrige rapidement des choses spécifiques.
dbmrq
3
J'espère que vous savez que l'origine zzcentre la fenêtre autour de la ligne actuelle. C'est probablement l'un des raccourcis que j'utilise le plus souvent. Vous devriez également commander zbet zt.
Vitor
@Vitor Intéressant, je ne le savais pas! Je garde généralement mon scrolloffassez haut, mais cela semble toujours utile, je vais envisager une autre cartographie. Merci!
dbmrq
Ce script vim fait la complétion des mots / correction orthographique / synonymes (en utilisant aspell, thesaurus, dictionnaire) stackoverflow.com/a/46645434/476175
mosh

Réponses:

6

Voici ce que j'ai trouvé:

Rotation des sorts

rotation du sort

traits

  • Les marques '[et ']sont utilisées pour garder une trace du texte en cours de traitement. Faire un changement ailleurs "acceptera" effectivement le changement suggéré.
  • Accepte un décompte.
  • Revient en arrière en utilisant zp
  • Répétable à l'aide de vim-repeat .
  • Annulez une fois pour restaurer le mot d'origine quel que soit le nombre de suggestions qui ont été parcourues.
  • Fonctionne en mode visuel pour obtenir des suggestions de mots fractionnés (par exemple, "titre" -> "titre")
    • Utilise '<et '>marque pour garder une trace du texte.
    • Remarque : ne semble pas reproductible avec vim-repeat .
  • Le mot d'origine en cours de modification est conservé dans le registre sans nom.
  • Les suggestions d'origine, précédente, actuelle et suivante s'affichent sur la ligne de commande.
  • Commande naïve :SpellRotateSubAllpour remplacer tout le texte correspondant à l'original par la suggestion actuelle.

Plugin: 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)
Tommy A
la source
1
Wow, maintenant nous parlons! Vous devriez en faire un plugin autonome afin que nous puissions garder les modifications et améliorations futures au même endroit. Ou je peux essayer de le faire si cela ne vous intéresse pas.
dbmrq
@danielbmarques Assez simple, c'est parti
Tommy A
Fantastique, merci! J'accepterai votre réponse comme la bonne puisque c'est exactement ce que je voulais et plus encore, et je donnerai la prime à @ nobe4 pour tous ses efforts et son aide.
dbmrq
@danielbmarques Aucun problème. Je suis dedans pour les questions et solutions intéressantes 😄
Tommy A
5

Comme l'a suggéré @statox, vous pouvez utiliser le plugin que j'ai écrit: vimcorrect .

Je vais expliquer en gros comment cela fonctionne, donc si vous voulez en réutiliser une partie, vous pouvez.

Pour me concentrer sur le prochain mot mal orthographié, j'utilise directement ]set au [sfur et à mesure qu'ils passent au match suivant / précédent. J'ai défini une fonction de correspondance personnalisée pour mettre en surbrillance le mot actuel:

entrez la description de l'image ici

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

Qui ajoutent au groupe de correspondance errorle mot actuel à la ligne / colonne actuelle (pour empêcher plusieurs correspondances sur la même ligne).


La spellbadword()fonction renvoie une liste de corrections possibles pour le mot sous le curseur.

J'affiche simplement cette liste dans un tampon, et je mappe <CR>pour remplacer le mot mal orthographié par la ligne courante (c'est-à-dire un mot corrigé possible).


Je mappe également net Nvers ]set [s, comme j'ai l'habitude de les appuyer pour rechercher.

q est mappé pour quitter le plugin, fermer la division et supprimer la surbrillance.

Remarque : il est toujours très instable, mais je prévois de faire des changements bientôt. Si vous sentez que vous pouvez / voulez améliorer ce plugin, n'hésitez pas à bifurquer / ouvrir une demande de pull.

nobe4
la source
Merci pour l'explication. Votre plugin est superbe, je vais certainement l'utiliser. Je veux toujours ma zzcommande, donc je peux réparer les choses rapidement sans entrer dans un mode spécial. Peut-être que nous pouvons ajouter cela à vimcorrectsi jamais je le découvre. :)
dbmrq
Eh bien, j'ai certainement besoin d'ajouter plus de personnalisation. La définition d'un mappage personnalisé peut donc être une amélioration que vous pouvez ajouter si vous le souhaitez :) (si vous commencez à développer en vimscript, cela peut être un bon moyen d'apprendre)
nobe4
2

Voici une fonction qui devrait fonctionner:

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>

L'idée de base est de mapper chaque mot modifié sur une paire ligne / colonne (afin qu'il ne fonctionne pas uniquement pour un élément) et de vérifier si l'élément a déjà été modifié.

Pour faire le remplacement, c'est à peu près ce que fait mon plugin:

  • récupérer le mot mal orthographié actuel
  • vérifier s'il existe des corrections
  • remplacer le mot par une suggestion corrigée

Lorsque vous utilisez cela, si vous souhaitez revenir au mot mal orthographié, vous pouvez simplement appuyer sur u.

La LoopConfirmfonction réinitialise le dictionnaire, donc si vous changez votre texte, vous pouvez l'appeler pour éviter les collisions.

Faites-moi savoir si vous rencontrez un problème / si vous avez des questions.

nobe4
la source
Uuh, ça a l'air bien. Cependant, il a encore beaucoup de problèmes. Prenez une phrase comme «teh qick borwn foz jums ofer teh lazi dor» et essayez de corriger chaque mot de cette façon. Je ne peux jamais mettre "teh" dans "the", bien que ce soit le numéro 4 sur la liste. "qick" fonctionne, mais "borwn" change pour quelque chose d'autre, même si "brown" est d'abord sur la liste, puis il passe directement à "foz". Je n'ai jamais dépassé ça. De plus, je n'aime pas la z=partie supplémentaire , mais je pourrais probablement trouver un moyen de contourner cela moi-même si le reste fonctionnait. Cependant, cela se rapproche beaucoup de ce que je veux. Je continuerai d'essayer de le réparer. Merci!
dbmrq
Voir ma mise à jour, j'ajoute un incrément trop tôt :) Ouais je ne suis pas content non z=plus. Mais avec cette méthode, vous devez garder une référence de l'endroit où vous vous trouvez. Mais si vous n'avez pas besoin de garder toutes les références en même temps, je peux simplifier cela :)
nobe4
Je ne sais pas ce que vous entendez par "garder toutes les références en même temps"… mais ne pourrions-nous pas simplement réinitialiser le dictionnaire chaque fois que le curseur se déplace? La fonction vérifierait si le curseur est au même endroit que lors de son dernier appel et s'il ne l'est pas, il se réinitialise.
dbmrq
De plus, tel qu'il est, il ne semble pas fonctionner correctement lorsque le curseur n'est pas au début du mot. Essayez de corriger chaque erreur dans cette phrase en plaçant le curseur au milieu de chaque mot. Je passe immédiatement au suivant.
dbmrq
1
Ok, je pense que je l'ai! Vérifiez ma dernière modification. Cela semble fonctionner à peu près parfaitement. Je vais laisser la question ouverte un peu plus longtemps pour voir si quelqu'un d'autre a quelque chose à ajouter, mais votre réponse était excellente, merci. :)
dbmrq
2

Mis à part les autres réponses, il est en fait un moyen construit juste pour Vim: <C-x>s. Cela utilisera le menu de fin du mode d'insertion de Vim.

Appuyez sur <C-x>sdepuis le mode d'insertion pour corriger le mot sous le curseur à la première suggestion et afficher le menu de fin avec d'autres suggestions (le cas échéant). Vous pouvez utiliser le 'completeopt'paramètre pour personnaliser certains paramètres du menu d'achèvement.

C'est un peu ennuyeux que cela ne fonctionne que depuis le mode insertion et l'utilisation de l'aide <C-x><C-s>peut être problématique (voir la note ci-dessous), vous pouvez donc définir votre propre mappage pour cela:

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

<C-@> est Control + Space.

Regarde aussi :help ins-completion :help i_CTRL-X_s


J'utilise personnellement une version plus avancée qui "devinera" si nous voulons soit vérifier l'orthographe du travail, soit utiliser l'auto-complétion régulière pour le code:

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()

Je pense qu'il existe également des plugins qui font des choses à peu près similaires (comme SuperTab, qui est assez populaire), mais je ne pourrais jamais les faire se comporter comme je le veux.


Mise en garde : Si vous utilisez Vim à partir d'un terminal, <C-s>cela signifiera "arrêter la sortie". C'est pourquoi les deux <C-x><C-s> et <C-x>s sont mappés par défaut. Utilisez <C-q>pour continuer la sortie si vous appuyez <C-s>par accident. Vous pouvez également désactiver <C-s>si vous ne l'utilisez pas (voir cette question ). Si vous utilisez GVim, vous pouvez ignorer cela.

Martin Tournoij
la source