Comando per scorrere i suggerimenti ortografici


12

Ho mappato zza 1z=, che è grande la maggior parte del tempo, ma di tanto in tanto il primo suggerimento non è quella giusta.

Quindi mi piacerebbe continuare a ripetere zz(o .) per scorrere gli altri suggerimenti.

Un secondo zzsulla stessa parola, quindi, avrebbe funzionato come u2z=, un terzo zzavrebbe funzionato come u3z=e così via.

Qualche idea su come farlo?


Modificare:

Sulla base della fantastica risposta di @ nobe4 sono riuscito a fare quello che voglio, ma lo lascerò qui per un po 'nel caso in cui qualcuno abbia qualche miglioramento o suggerimento:

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>

(Ho cambiato la mappatura a <c-m>causa del commento di @Vitor. Anche questo mi permette di tenere premuti quei tasti e di scorrere i suggerimenti molto velocemente. Ci sto pensando come <c-mistake>.)


2
Ti suggerirei di controllare questo plugin creato da un utente di questo sito. Migliora il flusso di lavoro realmente il controllo ortografico: per iniziare a correggere si utilizza il :Correctcomando: sarete in grado di navigare attraverso le parole per correggere con ne N, una finestra divisa si apre con tutti i suggerimenti di correzione si può semplicemente navigare attraverso di loro con je ke <CR>volontà applicare la correzione.
statox

@statox Grazie per il suggerimento. Lo controllerò, ma voglio ancora che il mio zzcomando risolva rapidamente cose specifiche.
dbmrq,

3
Spero che tu sia consapevole che originariamente zzcentra la finestra attorno alla linea corrente. È probabilmente una delle scorciatoie che uso più spesso. Dovresti anche fare il checkout zbe zt.
Vitor,

@Vitor Interessante, non lo sapevo! Di solito tengo scrolloffabbastanza in alto, ma sembra comunque utile, prenderò in considerazione un'altra mappatura. Grazie!
dbmrq,

Questo script vim esegue il completamento delle parole / correzione ortografica / sinonimi (usando aspell, thesaurus, dizionario) stackoverflow.com/a/46645434/476175
mosh

Risposte:


6

Ecco cosa mi è venuto in mente:

Ruota incantesimo

l'incantesimo ruota

Caratteristiche

  • I segni '[e ']sono usati per tenere traccia del testo su cui si sta lavorando. Apportare una modifica altrove "accetterà" effettivamente la modifica suggerita.
  • Accetta un conteggio.
  • Torna indietro usando zp
  • Ripetibile usando vim-repeat .
  • Annulla una volta per ripristinare la parola originale indipendentemente da quanti suggerimenti sono stati ciclicati.
  • Funziona in modalità visiva per ottenere suggerimenti per le parole divise (ad esempio "titolo" -> "titolo")
    • Usa '<e '>segna per tenere traccia del testo.
    • Nota : non sembra essere ripetibile con vim-repeat .
  • La parola originale che viene modificata viene mantenuta nel registro senza nome.
  • I suggerimenti originali, precedenti, attuali e successivi vengono visualizzati nella riga di comando.
  • Comando ingenuo :SpellRotateSubAllper sostituire tutto il testo corrispondente all'originale con il suggerimento corrente.

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, adesso stiamo parlando! Dovresti trasformarlo in un plug-in autonomo in modo da poter mantenere le modifiche e i miglioramenti futuri tutti nello stesso posto. Oppure posso provare a farlo se non sei interessato.
dbmrq,

@danielbmarques Abbastanza facile, eccoti qui: github.com/tweekmonster/spellrotate.vim
Tommy A

Fantastico, grazie! Accetterò la tua risposta come quella giusta poiché è esattamente quello che volevo e di più, e darò la generosità a @ nobe4 per tutti i suoi sforzi e il suo aiuto.
dbmrq,

@danielbmarques Nessun problema. Ci sono dentro per le domande e le soluzioni interessanti 😄
Tommy A

5

Come suggerito da @statox, puoi usare il plugin che ho scritto: vimcorrect .

Spiegherò sostanzialmente come funziona, quindi se vuoi riutilizzarne una parte, puoi farlo.

Per concentrarmi sulla prossima parola errata uso direttamente ]se [smentre salgono alla partita successiva / precedente. Ho definito una funzione di corrispondenza personalizzata per evidenziare la parola corrente:

inserisci qui la descrizione dell'immagine

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

Che aggiungono al gruppo di corrispondenze errorla parola corrente nella riga / colonna corrente (per impedire la corrispondenza multipla sulla stessa riga).


La spellbadword()funzione restituisce un elenco di possibili correzioni per la parola sotto il cursore.

Visualizzo semplicemente questo elenco in un buffer e mappa <CR>per sostituire la parola errata con la riga corrente (ovvero una possibile parola corretta).


Inoltre mappa ne Nverso ]se [s, come sono abituato a premerli per la ricerca.

q viene mappato per uscire dal plug-in, chiudere la divisione e rimuovere l'evidenziazione.

Nota : è ancora altamente instabile, ma ho intenzione di apportare presto alcune modifiche. Se ritieni di poter / voler migliorare questo plugin, sentiti libero di fork / aprire una richiesta pull.


Grazie per la spiegazione. Il tuo plugin ha un bell'aspetto, lo userò sicuramente. Voglio comunque il mio zzcomando, quindi posso sistemare le cose rapidamente senza entrare in una modalità speciale. Forse possiamo aggiungerlo a vimcorrectse mai lo capissi. :)
dbmrq,

Bene, ho sicuramente bisogno di aggiungere più personalizzazione. Quindi la definizione della mappatura personalizzata può essere un miglioramento che puoi aggiungere se vuoi :) (se inizi a sviluppare in vimscript potrebbe essere un buon modo per imparare)
nobe4

2

Ecco una funzione che dovrebbe funzionare:

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'idea di base è mappare ogni parola modificata su una coppia linea / colonna (in modo che non funzioni solo per un elemento) e verificare se l'elemento è già stato modificato.

Per fare il rimpiazzo, è praticamente quello che fa il mio plugin:

  • recuperare l'attuale parola errata
  • controlla se esistono correzioni
  • sostituire la parola con un suggerimento corretto

Quando lo usi, se vuoi tornare alla parola errata puoi semplicemente premere u.

La LoopConfirmfunzione ripristina il dizionario, quindi se si modifica il testo, è possibile chiamarlo per evitare collisioni.

Fammi sapere se riscontri qualche problema / se hai domande.


Uh, sta bene. Tuttavia ha ancora molti problemi. Prendi una frase del tipo "il qick nato a causa di jums ofer theh lazi dor" e cerca di correggere ogni parola in quel modo. Non riesco mai a ottenere "the" in "the", sebbene sia il numero 4 nell'elenco. "qick" funziona, ma "borwn" cambia in qualcos'altro, anche se "brown" è il primo della lista, e quindi passa direttamente a "foz". Non l'ho mai superato. Inoltre non mi piace la z=parte extra , ma probabilmente potrei trovare un modo per aggirare me stesso se il resto funzionasse. Questo però si sta avvicinando molto a quello che voglio. Continuerò a provare a risolverlo. Grazie!
dbmrq,

Vedi il mio aggiornamento, aggiungo un incremento troppo presto :) Sì, non sono contento di z=nessuno dei due. Ma con questo metodo è necessario mantenere un riferimento a dove ti trovi. Ma se non hai bisogno di conservare tutti i riferimenti allo stesso tempo, posso semplificarlo :)
nobe4

Non sono sicuro di cosa intendi per "mantieni tutti i riferimenti contemporaneamente" ... ma non potremmo semplicemente resettare il dizionario ogni volta che si sposta il cursore? La funzione controlla se il cursore si trova nello stesso punto in cui era stato l'ultima volta che è stato chiamato e se non lo è, si ripristina.
dbmrq,

Inoltre, sembra che non funzioni correttamente quando il cursore non è all'inizio della parola. Cerca di correggere ogni errore in quella frase posizionando il cursore al centro di ogni parola. Salto subito al prossimo.
dbmrq,

1
Ok, penso di averlo capito! Controlla la mia ultima modifica. Questo sembra funzionare praticamente perfettamente. Lascerò la domanda un po 'più a lungo per vedere se qualcun altro ha qualcosa da aggiungere, ma la tua risposta è stata fantastica, grazie. :)
dbmrq,

2

A parte le altre risposte, v'è in realtà un modo integrato a destra in Vim: <C-x>s. Questo utilizzerà il menu di completamento della modalità di inserimento di Vim.

Premendo <C-x>sdalla modalità di inserimento si dovrebbe correggere la parola sotto il cursore al primo suggerimento e mostrare il menu di completamento con ulteriori suggerimenti (se presenti). È possibile utilizzare l' 'completeopt'impostazione per personalizzare alcune impostazioni per il menu di completamento.

È un po 'fastidioso che questo funzioni solo dalla modalità di inserimento e l'utilizzo di <C-x><C-s>può essere problematico (vedi la nota sotto), quindi puoi definire la tua mappatura per questo:

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

<C-@> è Control + Space.

Vedi anche :help ins-completion :help i_CTRL-X_s


Personalmente utilizzo una versione più avanzata che "indovinerà" se vogliamo controllare l'ortografia del lavoro o utilizzare il completamento automatico regolare per il codice:

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

Credo che ci siano anche alcuni plugin che fanno cose più o meno simili (come SuperTab, che è abbastanza popolare), ma non potrei mai farli comportare come voglio.


Avvertenza : se si utilizza Vim da un terminale, <C-s>significa "stop output". Questo è il motivo per cui entrambi <C-x><C-s> e <C-x>s sono mappati per impostazione predefinita. Utilizzare <C-q>per continuare l'uscita se si preme <C-s>per errore. Puoi anche disabilitare <C-s>se non lo usi (vedi questa domanda ). Se stai usando GVim puoi ignorarlo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.