Команда, чтобы пройтись по предложениям правописания

12

Я сопоставляются zzс 1z=, который является большим большую часть времени, но каждый сейчас и тогда первое предложение не является правильным.

Поэтому я хотел бы повторять zz(или .) циклически проходить другие предложения.

Тогда секунда zzна том же слове будет работать как u2z=, третья zzбудет работать как u3z=и так далее.

Есть идеи, как это сделать?


Редактировать:

Основываясь на потрясающем ответе @ nobe4, мне удалось сделать то, что я хочу, но я оставлю это здесь на некоторое время, если у кого-то есть какие-либо улучшения или предложения:

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>

(Я изменил отображение на <c-m>из-за комментария @ Vitor. Также это позволяет мне удерживать эти клавиши и вроде бы быстро пролистывать предложения. Я думаю об этом как <c-mistake>.)

dbmrq
источник
2
Я бы посоветовал вам проверить этот плагин, который был сделан пользователем этого сайта. Это действительно улучшает рабочий процесс проверки орфографии: для начала коррекции использовать :Correctкоманду: вы будете иметь возможность перемещаться корытом слова , чтобы правильно с nи Nрасколом открывается окно со всеми предложениями коррекции вы можете просто перемещаться по ним с jи kи <CR>волевым применить коррекцию.
statox
@statox Спасибо за предложение. Я проверю это, но я все еще хочу, чтобы моя zzкоманда быстро исправляла определенные вещи.
dbmrq
3
Надеюсь, вы знаете, что изначально zzцентрирует окно вокруг текущей строки. Вероятно, это один из ярлыков, которые я использую чаще. Вы должны также проверка zbи zt.
Витор
@Vitor Интересно, я этого не знал! Я обычно держу свое scrolloffдовольно высокое, но это все еще кажется полезным, я рассмотрю другое отображение. Благодарность!
dbmrq
Этот скрипт vim выполняет завершение / исправление слов / синонимы (используя aspell, тезаурус, словарь) stackoverflow.com/a/46645434/476175
mosh

Ответы:

6

Вот что я придумал:

Заклинание Поворот

заклинание вращаться

Характеристики

  • '[И ']метки используются для отслеживания текста , в котором работал над. Внесение изменений в другом месте эффективно «примет» предлагаемое изменение.
  • Принимает количество.
  • Идет назад, используя zp
  • Повторяется с помощью vim-repeat .
  • Отмените один раз, чтобы восстановить исходное слово независимо от того, сколько предложений было зациклено.
  • Работает в визуальном режиме, чтобы получить предложения для разделенных слов (например, "заголовок" -> "заголовок")
    • Использует '<и '>отмечает, чтобы отслеживать текст.
    • Примечание : похоже, не повторяется с vim-repeat .
  • Изменяемое исходное слово сохраняется в безымянном регистре.
  • Исходные, предыдущие, текущие и последующие предложения отображаются в командной строке.
  • Наивная команда :SpellRotateSubAllдля замены всего текста, соответствующего оригиналу, с текущим предложением.

Плагин: 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
Вау, теперь мы говорим! Вы должны превратить это в отдельный плагин, чтобы мы могли хранить будущие изменения и улучшения в одном месте. Или я могу попытаться сделать это, если вам не интересно.
dbmrq
@danielbmarques Достаточно просто, вот и все: github.com/tweekmonster/spellrotate.vim
Томми А
Фантастика, спасибо! Я приму ваш ответ как правильный, поскольку это именно то, что я хотел, и даже больше, и я дам вознаграждение @ nobe4 за все его усилия и помощь.
dbmrq
@danielbmarques Нет проблем. Я нахожусь в этом для интересных вопросов и решений 😄
Томми
5

Как предложил @statox, вы можете использовать плагин, который я написал: vimcorrect .

Я объясню в основном, как это работает, поэтому, если вы хотите использовать какую-то его часть, вы можете.

Чтобы сосредоточиться на следующем слове с ошибками, я использую непосредственно ]sи [sкогда они переходят к следующему / предыдущему совпадению. Я определил пользовательскую функцию соответствия, чтобы выделить текущее слово:

введите описание изображения здесь

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

Которые добавляют в группу совпадений errorтекущее слово в текущей строке / столбце (чтобы предотвратить многократное сопоставление в одной строке).


spellbadword()Функция возвращает список возможной коррекции для слова под курсором.

Я просто отображаю этот список в буфере и сопоставляю <CR>слово с ошибкой с текущей строкой (т. Е. С возможным исправленным словом).


Я также сопоставляю nи Nс ]sи [s, поскольку я привык нажимать их для поиска.

q отображается, чтобы выйти из плагина, закрыть разделение и удалить выделение.

Примечание : это все еще очень нестабильно, но я планирую внести некоторые изменения в ближайшее время. Если вы чувствуете, что можете / хотите улучшить этот плагин, не стесняйтесь раскошелиться / открыть запрос на извлечение.

nobe4
источник
Спасибо за объяснение. Ваш плагин выглядит великолепно, я обязательно буду его использовать. zzТем не менее, я все еще хочу свою команду, чтобы я мог быстро исправить ситуацию, не переходя в специальный режим. Может быть, мы можем добавить это, vimcorrectесли я когда-нибудь это пойму. :)
dbmrq
Ну, мне определенно нужно добавить больше настроек. Таким образом, определение пользовательского сопоставления может быть улучшением, которое вы можете добавить, если хотите :) (если вы начинаете разрабатывать в vimscript, это может быть хорошим способом обучения)
nobe4
2

Вот функция, которая должна работать:

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>

Основная идея состоит в том, чтобы сопоставить каждое измененное слово с парой строка / столбец (чтобы оно не работало только для одного элемента) и проверить, был ли элемент уже изменен.

Чтобы сделать замену, это в значительной степени то, что делает мой плагин:

  • получить текущее слово с ошибкой
  • проверьте, есть ли исправления
  • заменить слово исправленным предложением

При использовании этого, если вы хотите вернуться к слову с ошибкой, вы можете просто нажать u.

LoopConfirmФункция сброса словаря, так что если вы измените текст, вы можете вызвать его для предотвращения столкновений.

Дайте мне знать, если у вас возникнут какие-либо проблемы / если у вас есть вопросы.

nobe4
источник
Это выглядит хорошо. У него все еще много проблем. Возьмите фразу типа «я тебя люблю» и попробую исправить каждое слово таким образом. Я никогда не могу получить "те" на "," хотя это номер 4 в списке. «qick» работает, но «borwn» меняется на что-то другое, даже если «brown» находится сначала в списке, а затем сразу переходит к «foz». Я никогда не проходил мимо этого. Также мне не нравится дополнительная z=часть, но я, вероятно, мог бы найти способ обойти это сам, если бы все остальное работало. Это очень близко к тому, что я хочу. Я буду продолжать пытаться это исправить. Благодарность!
dbmrq
Смотрите мое обновление, я добавляю прирост слишком рано :) Да, я тоже не доволен z=. Но с этим методом вам нужно сохранить ссылку на то, где вы находитесь. Но если вам не нужно хранить все ссылки одновременно, я могу упростить это :)
nobe4
Я не уверен, что вы подразумеваете под «держать все ссылки в одно и то же время»… но не могли бы мы просто сбросить словарь при каждом перемещении курсора? Функция проверит, находится ли курсор в том же месте, где он был в прошлый раз, когда он был вызван, и если это не так, он сбрасывается.
dbmrq
Кроме того, похоже, что он не работает должным образом, когда курсор находится не в начале слова. Попытайтесь исправить каждую ошибку в этом предложении, поместив курсор в середину каждого слова. Я сразу перехожу к следующему.
dbmrq
1
Хорошо, я думаю, что понял! Проверьте мое последнее редактирование. Кажется, это работает почти идеально. Я оставлю вопрос открытым немного дольше, чтобы узнать, есть ли у кого-то еще что добавить, но ваш ответ был отличным, спасибо. :)
dbmrq
2

Помимо других ответов, есть на самом деле путь построен прямо в Vim: <C-x>s. Это будет использовать меню завершения режима вставки Vim.

Нажатие <C-x>sиз режима вставки должно исправить слово под курсором до первого предложения и показать меню завершения с дополнительными предложениями (если есть). Вы можете использовать эту 'completeopt'настройку, чтобы настроить некоторые параметры для меню завершения.

Немного раздражает, что это работает только из режима вставки, и использование с использованием <C-x><C-s>может быть проблематичным (см. Примечание ниже), поэтому вы можете определить собственное отображение для этого:

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

<C-@> это контроль + пробел.

Также см :help ins-completion :help i_CTRL-X_s


Я лично использую более продвинутую версию, которая будет «догадываться», если мы хотим либо проверить орфографию работы, либо использовать регулярное автозаполнение для кода:

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

Я считаю, что есть также некоторые плагины, которые делают примерно одинаковые вещи (например, SuperTab, который довольно популярен), но я никогда не мог заставить их вести себя так, как я хочу.


Предостережение : если вы используете Vim с терминала, это <C-s>будет означать «остановка вывода». Вот почему оба <C-x><C-s> и <C-x>s отображаются по умолчанию. Используйте <C-q>для продолжения вывода, если вы нажали <C-s>случайно. Вы также можете отключить, <C-s>если вы не используете его (см. Этот вопрос ). Если вы используете GVim, вы можете игнорировать это.

Мартин Турной
источник