Polecenie do przechodzenia między sugestiami pisowni

12

Mapowałem zzna 1z=, co jest świetne przez większość czasu, ale od czasu do czasu pierwsza sugestia nie jest właściwa.

Chciałbym więc powtarzać zz(lub .), aby przełączać się między innymi sugestiami.

Drugie zzsłowo na tym samym słowie działałoby tak u2z=, trzecie zzdziałało jak u3z=i tak dalej.

Wszelkie pomysły, jak to zrobić?


Edytować:

Na podstawie niesamowitej odpowiedzi @ nobe4 udało mi się zrobić to, co chcę, ale zostawię to na chwilę na wypadek, gdyby ktoś miał jakieś ulepszenia lub sugestie:

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>

(Zmieniłem mapowanie na z <c-m>powodu komentarza @ Vitora. To także pozwala mi przytrzymać te klawisze i dość szybko przewijać sugestie. Myślę o tym jak o <c-mistake>.)

dbmrq
źródło
2
Sugeruję sprawdzenie tej wtyczki, która została wykonana przez użytkownika tej strony. To naprawdę poprawia obieg sprawdzanie pisowni: aby rozpocząć korygowanie użyć :Correctpolecenia: będziesz w stanie poruszać się koryta słowa poprawne z na N, otwiera się okno podzielone ze wszystkimi sugestiami korekcyjnych może po prostu żeglować przez nich ji ki <CR>będzie zastosuj poprawkę.
statox
@statox Dzięki za sugestię. Sprawdzę to, ale nadal chcę, aby moje zzpolecenie szybko naprawiało określone rzeczy.
dbmrq
3
Mam nadzieję, że wiesz, że pierwotnie zzcentruje okno wokół bieżącej linii. To prawdopodobnie jeden ze skrótów, których używam częściej. Powinieneś również sprawdzić kasę zbi zt.
Vitor,
@Vitor Ciekawe, nie wiedziałem o tym! Zwykle trzymam scrolloffdość wysoko, ale nadal wydaje się to przydatne, rozważę inne mapowanie. Dzięki!
dbmrq
Ten skrypt vim wykonuje uzupełnianie słów / poprawianie pisowni / synonimy (przy użyciu aspell, tezaurusa, słownika) stackoverflow.com/a/46645434/476175
mosh

Odpowiedzi:

6

Oto, co wymyśliłem:

Obrót zaklęcia

zaklęcie obraca się

funkcje

  • '[I ']znaki towarowe są używane do śledzenia tekstu czym pracował. Dokonanie zmiany w innym miejscu skutecznie „zaakceptuje” sugerowaną zmianę.
  • Akceptuje liczbę.
  • Cofa się za pomocą zp
  • Powtarzalne za pomocą vim-repeat .
  • Cofnij raz, aby przywrócić oryginalne słowo bez względu na liczbę sugestii, które zostały przetworzone.
  • Działa w trybie wizualnym, aby uzyskać sugestie dotyczące dzielonych słów (np. „Nagłówek” -> „nagłówek”)
    • Używa '<i '>oznacza, aby śledzić tekst.
    • Uwaga : Nie wydaje się powtarzalne z vim-repeat .
  • Pierwotne zmieniane słowo jest przechowywane w rejestrze bez nazwy.
  • Oryginalne, poprzednie, bieżące i następne sugestie są wyświetlane w wierszu polecenia.
  • Naiwne polecenie :SpellRotateSubAllzastąpienia całego tekstu pasującego do oryginału bieżącą sugestią.

Wtyczka: 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.
źródło
1
Wow, teraz rozmawiamy! Powinieneś zamienić to w samodzielną wtyczkę, abyśmy mogli zachować przyszłe zmiany i ulepszenia w tym samym miejscu. Lub mogę spróbować to zrobić, jeśli nie jesteś zainteresowany.
dbmrq
@danielbmarques Dość łatwo, proszę bardzo: github.com/tweekmonster/spellrotate.vim
Tommy A
Fantastycznie, dziękuję! Zaakceptuję twoją odpowiedź jako właściwą, ponieważ jest dokładnie tym, czego chciałem i jeszcze więcej, i dam nagrodę @ nobe4 za cały jego wysiłek i pomoc.
dbmrq
@danielbmarques Nie ma problemu. Mam ochotę na ciekawe pytania i rozwiązania 😄
Tommy A
5

Jak sugerował @statox, możesz użyć wtyczki, którą napisałem: vimcorrect .

Wyjaśnię w zasadzie, jak to działa, więc jeśli chcesz ponownie użyć części, możesz.

Aby skupić się na następnym błędnie napisanym słowie, używam go bezpośrednio ]si [sprzechodzą one do następnego / poprzedniego dopasowania. Zdefiniowałem niestandardową funkcję dopasowania, aby podświetlić bieżące słowo:

wprowadź opis zdjęcia tutaj

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

Które dodają do grupy dopasowań errorbieżące słowo w bieżącym wierszu / kolumnie (aby zapobiec wielokrotnemu dopasowaniu w tym samym wierszu).


spellbadword()Funkcja zwróci listę ewentualnej korekty dla słowa pod kursorem.

Po prostu wyświetlam tę listę w buforze i mapuję, <CR>aby zastąpić błędnie napisane słowo bieżącym wierszem (tj. Możliwe poprawione słowo).


Ja również map ni Ndo ]si [s, jak jestem przyzwyczajony do naciśnij je przeszukać.

q jest zamapowany, aby zamknąć wtyczkę, zamknąć podział i usunąć wyróżnienie.

Uwaga : wciąż jest bardzo niestabilna, ale planuję wkrótce wprowadzić pewne zmiany. Jeśli czujesz, że możesz / chcesz ulepszyć tę wtyczkę, możesz rozwidlić / otworzyć żądanie ściągnięcia.

nobe4
źródło
Dziękuję za wyjaśnienie. Twoja wtyczka wygląda świetnie, na pewno jej użyję. Nadal jednak chcę mieć swoje zzpolecenie, więc mogę szybko naprawić rzeczy bez wchodzenia w tryb specjalny. Może możemy to dodać, vimcorrectjeśli kiedykolwiek to wymyślę. :)
dbmrq
Cóż, zdecydowanie muszę dodać więcej opcji dostosowywania. Zdefiniowanie niestandardowego mapowania może być ulepszeniem, które możesz dodać, jeśli chcesz :) (jeśli zaczniesz pisać w vimscript, może to być dobry sposób na naukę)
nobe4
2

Oto funkcja, która powinna działać:

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>

Podstawową ideą jest mapowanie każdego zmienionego słowa na parę linia / kolumna (aby nie działało tylko dla jednego elementu) i sprawdzenie, czy element został już zmodyfikowany.

Aby wykonać zamianę, to właściwie to, co robi moja wtyczka:

  • pobierz bieżące błędnie napisane słowo
  • sprawdź, czy istnieją poprawki
  • zamień słowo na poprawioną sugestię

Podczas korzystania z niego, jeśli chcesz wrócić do błędnie napisanego słowa, możesz po prostu nacisnąć u.

LoopConfirmFunkcja resetowania słownika, więc jeśli zmienisz tekst, można nazwać to, aby uniknąć kolizji.

Daj mi znać, jeśli napotkasz jakiś problem / masz pytania.

nobe4
źródło
Uuh, to dobrze wygląda. Nadal jednak ma wiele problemów. Weź frazę w rodzaju „teh qick borwn foz jums ofer teh lazi dor” i spróbuj poprawić każde słowo w ten sposób. Nigdy nie mogę dostać „teh” do „the”, chociaż jest to numer 4 na liście. „qick” działa, ale „borwn” zmienia się na coś innego, nawet jeśli najpierw „brown” jest na liście, a następnie przeskakuje prosto do „foz”. Nigdy tego nie przeszedłem. Nie podoba mi się też dodatkowa z=część, ale prawdopodobnie mogłabym znaleźć sposób na obejście tego, jeśli reszta zadziałałaby. Jednak zbliża się to do tego, czego chcę. Będę próbował to naprawić. Dzięki!
dbmrq
Zobacz moją aktualizację, dodaję przyrost zbyt wcześnie :) Tak, nie jestem zadowolony z z=żadnego z nich. Ale dzięki tej metodzie musisz zachować odniesienie do tego, gdzie jesteś. Ale jeśli nie musisz zachować wszystkich odnośników jednocześnie, mogę to uprościć :)
nobe4
Nie jestem pewien, co masz na myśli mówiąc „zachowaj wszystkie odniesienia w tym samym czasie”… ale czy nie możemy po prostu zresetować słownika, gdy kursor się porusza? Funkcja sprawdzi, czy kursor znajduje się w tym samym miejscu, co podczas ostatniego wywołania, a jeśli nie, zostanie zresetowany.
dbmrq
Wygląda na to, że nie działa poprawnie, gdy kursor nie znajduje się na początku słowa. Spróbuj poprawić każdy błąd w tym zdaniu, umieszczając kursor na środku każdego słowa. Od razu przechodzę do następnego.
dbmrq
1
Ok, chyba to mam! Sprawdź moją ostatnią edycję. To wydaje się działać całkiem idealnie. Pozostawię pytanie otwarte dłużej, aby zobaczyć, czy ktoś jeszcze ma coś do dodania, ale twoja odpowiedź była świetna, dziękuję. :)
dbmrq
2

Oprócz innych odpowiedzi, nie jest w rzeczywistości sposobem zbudowany w prawo w vim: <C-x>s. Użyje to menu zakończenia trybu wstawiania Vima.

Naciśnięcie <C-x>sz trybu wstawiania powinno poprawić słowo pod kursorem do pierwszej sugestii i wyświetlić menu uzupełniania z dalszymi sugestiami (jeśli istnieją). Możesz użyć tego 'completeopt'ustawienia, aby dostosować niektóre ustawienia menu zakończenia.

To trochę irytujące, że działa to tylko z trybu wstawiania, a korzystanie z <C-x><C-s>może być problematyczne (patrz uwaga poniżej), więc możesz zdefiniować własne mapowanie:

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

<C-@> to Control + Space.

Zobacz także :help ins-completion :help i_CTRL-X_s


Osobiście używam bardziej zaawansowanej wersji, która „zgadnie”, jeśli chcemy albo sprawdzić pisownię pracy, albo użyć regularnego automatycznego uzupełniania kodu:

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

Sądzę, że istnieją również wtyczki, które robią z grubsza podobne rzeczy (jak SuperTab, która jest dość popularna), ale nigdy nie udało mi się sprawić, by zachowywały się tak, jak chcę.


Zastrzeżenie : Jeśli używasz Vima z terminala, <C-s>oznacza to „zatrzymaj wyjście”. Dlatego oba <C-x><C-s> i <C-x>s są przypisane domyślnie. Użyj, <C-q>aby kontynuować drukowanie, jeśli naciśniesz <C-s>przypadkowo. Możesz także wyłączyć, <C-s>jeśli go nie używasz (patrz to pytanie ). Jeśli używasz GVim, możesz to zignorować.

Martin Tournoij
źródło