Befehl zum Durchlaufen von Rechtschreibvorschlägen


12

Ich kartiert zzauf 1z=, die die meiste Zeit ist groß, aber ab und zu dem ersten Vorschlag ist nicht die richtige.

Also möchte ich immer wieder wiederholen zz(oder .), um die anderen Vorschläge durchzugehen.

Eine Sekunde zzfür dasselbe Wort würde also funktionieren u2z=, eine dritte zzwürde funktionieren u3z=und so weiter.

Irgendwelche Ideen, wie man das macht?


Bearbeiten:

Basierend auf der fantastischen Antwort von @nobe4 habe ich es geschafft, das zu tun, was ich will, aber ich werde es hier für eine Weile belassen, falls jemand irgendwelche Verbesserungen oder Vorschläge hat:

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>

(Ich habe die Zuordnung <c-m>aufgrund von @ Vitors Kommentar in geändert . Dadurch kann ich auch diese Tasten gedrückt halten und sozusagen schnell durch die Vorschläge scrollen. Ich denke, dass dies so ist <c-mistake>.)


2
Ich würde Ihnen empfehlen, dieses Plugin zu überprüfen, das von einem Benutzer dieser Website erstellt wurde. Es verbessert wirklich die Rechtschreibprüfung Workflow: beginnen Sie die Verwendung Korrektur :CorrectBefehl: Sie werden in der Lage sein , mit den Worten richtig zu navigieren Trog nund Nein zweigeteiltes Fenster mit allen Korrekturvorschläge können Sie einfach durch sie navigieren mit jund kund <CR>wird Wenden Sie die Korrektur an.
statox

@statox Danke für den Vorschlag. Ich werde es überprüfen, aber ich möchte immer noch, dass mein zzBefehl bestimmte Dinge schnell behebt.
Dbmrq

3
Ich hoffe, Sie wissen, dass zzdas Fenster ursprünglich um die aktuelle Zeile zentriert ist. Es ist wahrscheinlich eine der Abkürzungen, die ich häufiger benutze. Sie sollten auch zur Kasse zbund zt.
Vitor

@Vitor Interessant, das wusste ich nicht! Normalerweise halte ich mein scrolloffziemlich hohes Niveau, aber das scheint immer noch nützlich zu sein. Ich werde ein anderes Mapping in Betracht ziehen. Vielen Dank!
Dbmrq

Dieses VIM-Skript führt Wortvervollständigung / Rechtschreibkorrektur / Synonyme (unter Verwendung von Aspell, Thesaurus, Wörterbuch) durch. Stackoverflow.com/a/46645434/476175
mosh

Antworten:


6

Folgendes habe ich mir ausgedacht:

Zauber drehen

Zauber drehen

Eigenschaften

  • Die Markierungen '[und ']werden verwendet, um den Text zu verfolgen, an dem gearbeitet wird. Wenn Sie eine Änderung an einer anderen Stelle vornehmen, wird die vorgeschlagene Änderung effektiv "akzeptiert".
  • Akzeptiert eine Zählung.
  • Geht rückwärts mit zp
  • Wiederholbar mit vim-repeat .
  • Einmal rückgängig machen, um das ursprüngliche Wort wiederherzustellen, unabhängig davon, wie viele Vorschläge durchlaufen wurden.
  • Arbeitet im visuellen Modus, um Vorschläge für geteilte Wörter zu erhalten (zB "Überschrift" -> "Überschrift")
  • Das ursprüngliche Wort, das geändert wird, wird im unbenannten Register aufbewahrt.
  • Die ursprünglichen, vorherigen, aktuellen und nächsten Vorschläge werden in der Befehlszeile angezeigt.
  • Naiver Befehl :SpellRotateSubAll, um den gesamten mit dem Original übereinstimmenden Text durch den aktuellen Vorschlag zu ersetzen.

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)

1
Wow, jetzt reden wir! Sie sollten dies in ein eigenständiges Plugin verwandeln, damit wir zukünftige Änderungen und Verbesserungen an derselben Stelle behalten können. Oder ich kann es versuchen, wenn Sie nicht interessiert sind.
Dbmrq

@ Danielbmarques Einfach genug, hier geht's: github.com/tweekmonster/spellrotate.vim
Tommy A

Fantastisch, danke! Ich akzeptiere Ihre Antwort als die richtige, da es genau das ist, was ich wollte und mehr, und ich gebe @ nobe4 die Prämie für all seine Bemühungen und seine Hilfe.
Dbmrq

@ Danielbmarques Kein Problem. Ich bin dabei für die interessanten Fragen und Lösungen in
Tommy A

5

Wie @statox vorschlug, können Sie das Plugin verwenden, das ich geschrieben habe: vimcorrect .

Ich werde im Grunde erklären, wie es funktioniert. Wenn Sie einen Teil davon wiederverwenden möchten, können Sie dies tun.

Um mich auf das nächste falsch geschriebene Wort zu konzentrieren, verwende ich direkt ]sund [swährend sie zum nächsten / vorherigen Treffer springen. Ich habe eine benutzerdefinierte Übereinstimmungsfunktion definiert, um das aktuelle Wort hervorzuheben:

Bildbeschreibung hier eingeben

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

Womit der Übereinstimmungsgruppe errordas aktuelle Wort in der aktuellen Zeile / Spalte hinzugefügt wird (um eine Mehrfachübereinstimmung in derselben Zeile zu verhindern).


Die spellbadword()Funktion gibt eine Liste möglicher Korrekturen für das Wort unter dem Cursor zurück.

Ich zeige diese Liste einfach in einem Puffer an und ordne <CR>zu, dass das falsch geschriebene Wort durch die aktuelle Zeile (dh ein möglicherweise korrigiertes Wort) ersetzt wird.


Ich kartiere auch nund Nzu ]sund [s, wie ich es gewohnt bin, sie zum Suchen zu drücken.

q wird gemappt, um das Plugin zu beenden, den Split zu schließen und die Markierung zu entfernen.

Hinweis : Es ist immer noch sehr instabil, aber ich plane, bald einige Änderungen vorzunehmen. Wenn Sie das Plugin verbessern möchten oder können, können Sie eine Pull-Anfrage stellen.


Danke für die Erklärung. Dein Plugin sieht gut aus, ich werde es definitiv benutzen. Ich möchte trotzdem meinen zzBefehl, damit ich die Probleme schnell beheben kann, ohne in einen speziellen Modus zu wechseln. Vielleicht können wir das hinzufügen, vimcorrectwenn ich es jemals herausfinde. :)
Dbmrq

Nun, ich muss definitiv mehr Anpassungen hinzufügen. Das Definieren von benutzerdefinierten Zuordnungen kann eine Verbesserung sein, die Sie hinzufügen können, wenn Sie möchten :) (wenn Sie anfangen, Vimscript zu entwickeln, ist dies möglicherweise eine gute Möglichkeit, dies zu lernen)
nobe4

2

Hier ist eine Funktion, die funktionieren sollte:

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>

Die Grundidee ist, jedes geänderte Wort einem Zeilen- / Spaltenpaar zuzuordnen (damit es nicht nur für ein Element funktioniert) und zu prüfen, ob das Element bereits geändert wurde.

Um das zu ersetzen, ist es ziemlich genau das, was mein Plugin macht:

  • Holen Sie sich das aktuelle falsch geschriebene Wort
  • Überprüfen Sie, ob Korrekturen vorhanden sind
  • Wort durch korrigierten Vorschlag ersetzen

Wenn Sie in diesem Fall zum falsch geschriebenen Wort zurückkehren möchten, drücken Sie einfach u.

Die LoopConfirmFunktion setzt das Wörterbuch zurück. Wenn Sie also Ihren Text ändern, können Sie ihn aufrufen, um Kollisionen zu vermeiden.

Lassen Sie mich wissen, wenn Sie auf ein Problem stoßen oder Fragen haben.


Äh, das sieht gut aus. Es gibt jedoch immer noch viele Probleme. Nehmen Sie einen Satz wie "Der schnell geborene Foz Jums Ofer The Lazi Dor" und versuchen Sie, jedes Wort auf diese Weise zu korrigieren. Ich kann nie "die" bekommen, obwohl es Nummer 4 auf der Liste ist. "qick" funktioniert, aber "borwn" ändert sich zu etwas anderem, auch wenn "brown" zuerst auf der Liste steht, und springt dann direkt zu "foz". Daran bin ich nie vorbei gekommen. Auch ich mag den zusätzlichen z=Teil nicht, aber ich könnte wahrscheinlich einen Weg finden, das selbst zu umgehen, wenn der Rest funktioniert. Dies kommt jedoch dem sehr nahe, was ich will. Ich werde weiter versuchen, es zu beheben. Vielen Dank!
Dbmrq

Siehe mein Update, ich füge ein Inkrement zu früh hinzu :) Ja, ich bin auch nicht zufrieden mit dem z=. Bei dieser Methode müssen Sie jedoch einen Verweis darauf aufbewahren, wo Sie sich befinden. Aber wenn Sie nicht alle Referenzen gleichzeitig behalten müssen, kann ich das vereinfachen :)
nobe4

Ich bin mir nicht sicher, was Sie unter "Alle Referenzen gleichzeitig behalten" verstehen. Aber konnten wir das Wörterbuch nicht einfach zurücksetzen, wenn sich der Cursor bewegt? Die Funktion prüft, ob sich der Cursor an der Stelle befindet, an der er zuletzt aufgerufen wurde, und ob er nicht zurückgesetzt wird.
Dbmrq

Auch so, wie es ist, scheint es nicht richtig zu funktionieren, wenn sich der Cursor nicht am Wortanfang befindet. Versuchen Sie, jeden Fehler in diesem Satz zu korrigieren, indem Sie den Cursor in die Mitte jedes Wortes setzen. Ich gehe sofort zum nächsten.
dbmrq

1
Ok, ich glaube ich habe es verstanden! Überprüfen Sie meine letzte Bearbeitung. Dies scheint ziemlich perfekt zu funktionieren. Ich lasse die Frage etwas länger offen, um zu sehen, ob jemand anderes etwas hinzuzufügen hat, aber Ihre Antwort war großartig, danke. :)
Dbmrq

2

Abgesehen von den anderen Antworten, ist es tatsächlich ein Weg nach rechts in Vim gebaut: <C-x>s. Dadurch wird das Einfügemodus-Abschlussmenü von Vim verwendet.

Durch Drücken von <C-x>saus dem Einfügemodus sollte das Wort unter dem Cursor auf den ersten Vorschlag korrigiert und das Abschlussmenü mit weiteren Vorschlägen (falls vorhanden) angezeigt werden. Mit dieser 'completeopt'Einstellung können Sie einige Einstellungen für das Abschlussmenü anpassen.

Es ist etwas ärgerlich, dass dies nur im Einfügemodus funktioniert und die Verwendung von <C-x><C-s>problematisch sein kann (siehe Hinweis unten). Sie können daher Ihr eigenes Mapping dafür definieren:

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

<C-@> ist Kontrolle + Leertaste.

Siehe auch :help ins-completion :help i_CTRL-X_s


Ich persönlich verwende eine fortgeschrittenere Version, die "erraten" wird, ob wir entweder die Arbeit auf Rechtschreibprüfung überprüfen oder die reguläre automatische Vervollständigung für Code verwenden möchten:

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

Ich glaube, dass es auch einige Plugins gibt, die ungefähr ähnliche Dinge tun (wie SuperTab, das ziemlich beliebt ist), aber ich könnte sie niemals dazu bringen, sich so zu verhalten, wie ich es möchte.


Vorsichtsmaßnahme : Wenn Sie Vim von einem Terminal aus verwenden, <C-s>bedeutet dies "Ausgabe stoppen". Aus diesem Grund sind beide <C-x><C-s> und <C-x>s standardmäßig zugeordnet. Verwenden <C-q>Sie diese Taste , um die Ausgabe fortzusetzen, wenn Sie versehentlich drücken <C-s>. Sie können es auch deaktivieren, <C-s>wenn Sie es nicht verwenden (siehe diese Frage ). Wenn Sie GVim verwenden, können Sie dies ignorieren.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.