Comando para recorrer las sugerencias de ortografía


12

Me asignan zza 1z=, que es grande la mayor parte del tiempo, pero de vez en cuando la primera sugerencia no es la correcta.

Así que me gustaría seguir repitiendo zz(o .) para recorrer las otras sugerencias.

Un segundo zzen la misma palabra, entonces, funcionaría como u2z=, un tercero zzfuncionaría como u3z=y así sucesivamente.

¿Alguna idea sobre cómo hacer eso?


Editar:

Basado en la increíble respuesta de @nobe4, logré hacer lo que quiero, pero lo dejaré aquí por un tiempo en caso de que alguien tenga alguna mejora o sugerencia:

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>

(Cambié el mapeo <c-m>por el comentario de @ Vitor. Además, esto me permite mantener presionadas esas teclas y desplazarme por las sugerencias realmente rápido. Estoy pensando en ello como <c-mistake>).


2
Le sugiero que revise este complemento que fue creado por un usuario de este sitio. Realmente mejora el flujo de trabajo de la corrección ortográfica: para empezar a corregir utiliza el :Correctcomando: serás capaz de navegar a través de las palabras correctas con ny N, una ventana dividida se abre con todas las sugerencias de corrección puede simplemente navegar a través de ellos con jy ky <CR>voluntad Aplica la corrección.
statox

@statox Gracias por la sugerencia. Lo comprobaré, pero todavía quiero que mi zzcomando arregle cosas específicas rápidamente.
dbmrq

3
Espero que sepa que originalmente zzcentra la ventana alrededor de la línea actual. Es probablemente uno de los atajos que uso con más frecuencia. También debe pagar zby zt.
Vitor

@Vitor ¡Interesante, no lo sabía! Por lo general, mantengo mi nivel scrolloffbastante alto, pero eso todavía parece útil, consideraré otra asignación. ¡Gracias!
dbmrq

Este script vim completa palabras / corrección ortográfica / sinónimos (usando aspell, diccionario de sinónimos, diccionario) stackoverflow.com/a/46645434/476175
mosh

Respuestas:


6

Esto es lo que se me ocurrió:

Rotar hechizo

hechizo rotar

Caracteristicas

  • Las marcas '[y ']se utilizan para realizar un seguimiento del texto en el que se trabaja. Hacer un cambio en otra parte efectivamente "aceptará" el cambio sugerido.
  • Acepta un recuento.
  • Retrocede usando zp
  • Repetible usando vim-repeat .
  • Deshacer una vez para restaurar la palabra original independientemente de cuántas sugerencias se hayan ciclado.
  • Funciona en modo visual para obtener sugerencias para palabras divididas (por ejemplo, "título" -> "título")
    • Utiliza '<y '>marca para realizar un seguimiento del texto.
    • Nota : No parece ser repetible con vim-repeat .
  • La palabra original que se cambia se mantiene en el registro sin nombre.
  • Las sugerencias originales, anteriores, actuales y siguientes se muestran en la línea de comandos.
  • Comando ingenuo :SpellRotateSubAllpara reemplazar todo el texto que coincida con el original con la sugerencia actual.

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, ahora estamos hablando! Debe convertir esto en un complemento independiente para que podamos mantener futuros cambios y mejoras en el mismo lugar. O puedo intentar hacerlo si no estás interesado.
dbmrq

@danielbmarques Muy fácil, aquí tienes: github.com/tweekmonster/spellrotate.vim
Tommy A

¡Fantástico, gracias! Aceptaré tu respuesta como la correcta ya que es exactamente lo que quería y más, y le daré la recompensa a @ nobe4 por todo su esfuerzo y ayuda.
dbmrq

@danielbmarques No hay problema. Estoy preparado para las preguntas y soluciones interesantes 😄
Tommy A

5

Como sugirió @statox, puede usar el complemento que escribí: vimcorrect .

Explicaré básicamente cómo funciona, así que si quieres reutilizar alguna parte, puedes hacerlo.

Para centrarme en la siguiente palabra mal escrita, uso directamente ]sy a [smedida que saltan al siguiente / anterior partido. Definí una función de coincidencia personalizada para resaltar la palabra actual:

ingrese la descripción de la imagen aquí

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

Que agregan al grupo de coincidencia errorla palabra actual en la línea / columna actual (para evitar coincidencias múltiples en la misma línea).


La spellbadword()función devuelve una lista de posibles correcciones para la palabra debajo del cursor.

Simplemente visualizo esta lista en un búfer, y la asigno <CR>para reemplazar la palabra mal escrita por la línea actual (es decir, una posible palabra corregida).


También mapeo ny Nhacia ]sy [s, ya que estoy acostumbrado a presionarlos para buscar.

q se asigna para salir del complemento, cerrar la división y eliminar el resaltado.

Nota : todavía es muy inestable, pero planeo hacer algún cambio pronto. Si cree que puede / desea mejorar este complemento, no dude en bifurcar / abrir una solicitud de extracción.


Gracias por la explicación. Su complemento se ve muy bien, definitivamente lo usaré. Sin embargo, todavía quiero mi zzcomando, por lo que puedo arreglar las cosas rápidamente sin entrar en un modo especial. Tal vez podamos agregar eso vimcorrectsi alguna vez lo descubro. :)
dbmrq

Bueno, definitivamente necesito agregar más personalización. Así la definición de asignación personalizada puede ser una mejora se puede añadir si quieres :) (si comienza a desarrollar en Vimscript puede ser una buena manera de aprender)
nobe4

2

Aquí hay una función que debería funcionar:

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>

La idea básica es asignar cada palabra cambiada a un par de línea / columna (para que no funcione solo para un elemento) y verificar si el elemento ya se ha modificado.

Para hacer el reemplazo, es más o menos lo que hace mi complemento:

  • buscar la palabra mal escrita actual
  • verificar si existen correcciones
  • reemplazar palabra con sugerencia corregida

Al usar esto, si desea volver a la palabra mal escrita, simplemente presione u.

La LoopConfirmfunción restablece el diccionario, por lo que si cambia el texto, puede llamarlo para evitar colisiones.

Avíseme si tiene algún problema / si tiene alguna pregunta.


Uuh, eso se ve bien. Sin embargo, todavía tiene muchos problemas. Tome una frase como "qh borwn foz jums ofer teh lazi dor" y trate de corregir cada palabra de esa manera. Nunca puedo obtener "teh" para "the", aunque es el número 4 en la lista. "qick" funciona, pero "borwn" cambia a otra cosa, incluso si "brown" está primero en la lista y luego pasa directamente a "foz". Nunca supere eso. Además, no me gusta la z=parte extra , pero probablemente podría encontrar una manera de evitarlo si el resto funciona. Sin embargo, esto se está acercando mucho a lo que quiero. Seguiré intentando arreglarlo. ¡Gracias!
dbmrq

Vea mi actualización, agrego un incremento demasiado pronto :) Sí, tampoco estoy contento con el z=. Pero con este método debe mantener una referencia de dónde se encuentra. Pero si no necesita mantener todas las referencias al mismo tiempo, puedo simplificar eso :)
nobe4

No estoy seguro de lo que quiere decir con "mantener todas las referencias al mismo tiempo" ... pero ¿no podríamos simplemente restablecer el diccionario cada vez que se mueve el cursor? La función verificará si el cursor está en el mismo lugar que la última vez que lo llamó, y si no lo está, se reinicia.
dbmrq

Además, parece que no funciona correctamente cuando el cursor no está al comienzo de la palabra. Intenta corregir cada error en esa oración colocando el cursor en el medio de cada palabra. Salto al siguiente de inmediato.
dbmrq

1
Ok, creo que lo tengo! Mira mi última edición. Esto parece funcionar bastante perfectamente. Dejaré la pregunta abierta un poco más para ver si alguien más tiene algo que agregar, pero su respuesta fue excelente, gracias. :)
dbmrq

2

Aparte de las otras respuestas, en realidad hay una manera construido en el derecho a Vim: <C-x>s. Esto usará el menú de finalización del modo de inserción de Vim.

Al presionar <C-x>sdesde el modo de inserción, se debe corregir la palabra debajo del cursor hasta la primera sugerencia y mostrar el menú de finalización con más sugerencias (si corresponde). Puede usar la 'completeopt'configuración para personalizar algunas configuraciones para el menú de finalización.

Es un poco molesto que esto solo funcione desde el modo de inserción y usarlo <C-x><C-s>puede ser problemático (consulte la nota a continuación), por lo que puede definir su propia asignación para esto:

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

<C-@> es Control + Espacio.

Ver también :help ins-completion :help i_CTRL-X_s


Personalmente uso una versión más avanzada que "adivinará" si queremos revisar la ortografía del trabajo o usar el autocompletado regular para el código:

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

Creo que también hay algunos complementos que hacen cosas más o menos similares (como SuperTab, que es bastante popular), pero nunca pude hacer que se comporten como quiero.


Advertencia : si está utilizando Vim desde un terminal, <C-s>significará "detener salida". Es por eso que ambos <C-x><C-s> y <C-x>s están asignados por defecto. Use <C-q>para continuar la salida si presiona <C-s>por accidente. También puede deshabilitarlo <C-s>si no lo usa (vea esta pregunta ). Si está utilizando GVim, puede ignorar esto.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.