คำสั่งเพื่อวนซ้ำคำแนะนำการสะกดคำ


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


2
ฉันขอแนะนำให้คุณตรวจสอบปลั๊กอินนี้ซึ่งสร้างโดยผู้ใช้ของเว็บไซต์นี้ จริงๆมันช่วยเพิ่มเวิร์กโฟลว์การตรวจสอบการสะกด: การเริ่มต้นการแก้ไขคุณใช้:Correctคำสั่ง: คุณจะสามารถที่จะนำทางรางคำที่ถูกต้องด้วยnและNหน้าต่างแยกที่เปิดกับทุกข้อเสนอแนะการแก้ไขที่คุณก็สามารถนำทางผ่านพวกเขาด้วยjและkและ<CR>จะ ใช้การแก้ไข
statox

@ statox ขอบคุณสำหรับคำแนะนำ ฉันจะตรวจสอบมัน แต่ฉันยังต้องการให้zzคำสั่งของฉันแก้ไขสิ่งที่เฉพาะเจาะจงอย่างรวดเร็ว
dbmrq

3
หวังว่าคุณจะทราบว่าเดิมzzจัดกึ่งกลางหน้าต่างรอบบรรทัดปัจจุบัน อาจเป็นหนึ่งในทางลัดที่ฉันใช้บ่อยขึ้น นอกจากนี้คุณควรชำระเงินและzb zt
Vitor

@Vitor ที่น่าสนใจฉันไม่รู้! ฉันมักจะรักษาระดับของฉันscrolloffค่อนข้างสูง แต่ก็ยังมีประโยชน์ฉันจะพิจารณาการทำแผนที่อื่น ขอบคุณ!
dbmrq

สคริปต์ที่เป็นกลุ่มนี้ทำคำให้สมบูรณ์ / แก้ไขคำสะกด / คำพ้องความหมาย (ใช้ aspell, อรรถาภิธาน, พจนานุกรม) stackoverflow.com/a/46645434/476175
mosh

คำตอบ:


6

นี่คือสิ่งที่ฉันมาด้วย:

คาถาหมุน

คาถาหมุน

คุณสมบัติ

  • '[และ']เครื่องหมายที่ใช้ในการติดตามของข้อความที่กำลังทำงานอยู่ การเปลี่ยนแปลงที่อื่นอย่างมีประสิทธิภาพจะ "ยอมรับ" การเปลี่ยนแปลงที่แนะนำ
  • ยอมรับการนับ
  • ย้อนกลับโดยใช้ zp
  • ทำซ้ำโดยใช้เสียงเรียกซ้ำ
  • เลิกทำหนึ่งครั้งเพื่อกู้คืนคำเดิมโดยไม่คำนึงถึงจำนวนคำแนะนำที่ถูกขี่จักรยาน
  • ใช้งานได้ในโหมดภาพเพื่อรับคำแนะนำสำหรับคำที่แยก (เช่น "บรรทัดแรก" -> "บรรทัดแรก")
    • ใช้'<และ'>ทำเครื่องหมายเพื่อติดตามข้อความ
    • หมายเหตุ : ไม่ได้ดูเหมือนจะทำซ้ำกับเสียงเรียกซ้ำ
  • คำดั้งเดิมที่ถูกเปลี่ยนแปลงจะถูกเก็บไว้ในการลงทะเบียนที่ไม่มีชื่อ
  • คำแนะนำดั้งเดิมก่อนหน้าปัจจุบันและคำแนะนำถัดไปจะแสดงในบรรทัดคำสั่ง
  • คำสั่ง Naive :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
ทอมมี่ A

เยี่ยมมากขอบคุณ! ฉันจะยอมรับคำตอบของคุณว่าเป็นคำตอบที่ถูกต้องเพราะเป็นสิ่งที่ฉันต้องการและอื่น ๆ อีกมากมายและฉันจะมอบรางวัลให้กับ @ nobe4 สำหรับความพยายามและความช่วยเหลือทั้งหมดของเขา
dbmrq

@danielbmarques ไม่มีปัญหา ฉันอยู่ในนั้นสำหรับคำถามและวิธีแก้ปัญหาที่น่าสนใจ😄
Tommy A

5

ในฐานะที่เป็น @statox แนะนำคุณสามารถใช้ปลั๊กอินที่ผมเขียน: vimcorrect

ฉันจะอธิบายโดยทั่วไปว่ามันทำงานอย่างไรดังนั้นหากคุณต้องการนำส่วนหนึ่ง ๆ มาใช้ใหม่คุณสามารถทำได้

เพื่อมุ่งเน้นไปที่คำที่สะกดผิดต่อไปฉันใช้โดยตรง]sและ[sขณะที่พวกเขาข้ามไปที่การแข่งขันถัดไป / ก่อนหน้า ฉันกำหนดฟังก์ชันจับคู่แบบกำหนดเองเพื่อเน้นคำปัจจุบัน:

ป้อนคำอธิบายรูปภาพที่นี่

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

คำใดที่เพิ่มให้กับกลุ่มการแข่งขันerrorคำปัจจุบันที่บรรทัด / คอลัมน์ปัจจุบัน (เพื่อป้องกันการจับคู่หลายรายการในบรรทัดเดียวกัน)


spellbadword()ฟังก์ชั่นกลับรายการของการแก้ไขที่เป็นไปได้สำหรับคำว่าภายใต้เคอร์เซอร์

ฉันเพียงแค่แสดงรายการนี้ในบัฟเฟอร์และฉันแมป<CR>เพื่อแทนที่คำที่สะกดผิดด้วยบรรทัดปัจจุบัน (เช่นคำที่ถูกแก้ไขที่เป็นไปได้)


ฉันยังทำแผนที่nและNไปยัง]sและ[sตามที่ฉันคุ้นเคยกับการกดเพื่อค้นหา

q ถูกแมปเพื่อออกจากปลั๊กอินปิดการแยกและลบการเน้น

หมายเหตุ : มันยังไม่เสถียรอย่างมาก แต่ฉันวางแผนที่จะทำการเปลี่ยนแปลงในไม่ช้า หากคุณรู้สึกว่าคุณสามารถ / ต้องการปรับปรุงปลั๊กอินนี้รู้สึกอิสระที่จะแยก / เปิดคำขอดึง


ขอบคุณสำหรับคำอธิบาย ปลั๊กอินของคุณดูดีมากฉันจะใช้มันอย่างแน่นอน ฉันยังคงต้องการ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ฟังก์ชั่นรีเซ็ตพจนานุกรมดังนั้นหากคุณเปลี่ยนข้อความของคุณคุณสามารถเรียกมันว่าเพื่อป้องกันไม่ให้ชนกัน

แจ้งให้เราทราบหากคุณพบปัญหาใด ๆ / หากคุณมีคำถาม


อืมนั่นดูดีนะ มันยังคงมีปัญหามากมาย ใช้วลีเช่น "teh qick borwn foz jums ofer teh lazi dor" และลองแก้ไขแต่ละคำด้วยวิธีนั้น ฉันไม่สามารถรับ "teh" เป็น "the" ได้แม้ว่าจะเป็นหมายเลข 4 ในรายการ "qick" ใช้งานได้ แต่ "borwn" เปลี่ยนไปเป็นอย่างอื่นแม้ว่า "สีน้ำตาล" จะอยู่ในรายการก่อนแล้วจึงข้ามไปที่ "foz" ฉันไม่เคยผ่านมา นอกจากนี้ฉันไม่ชอบz=ส่วนเพิ่มเติมแต่ฉันอาจหาวิธีที่จะหลีกเลี่ยงตัวเองได้หากคนอื่นทำงาน นี่คือการใกล้เคียงกับสิ่งที่ฉันต้องการ ฉันจะพยายามแก้ไขต่อไป ขอบคุณ!
dbmrq

ดูการอัปเดตของฉันฉันเพิ่มส่วนเพิ่มในไม่ช้า :) ใช่ฉันไม่พอใจz=ทั้ง แต่ด้วยวิธีนี้คุณจะต้องอ้างอิงถึงตำแหน่งของคุณ แต่ถ้าคุณไม่จำเป็นต้องให้ข้อมูลทั้งหมดในเวลาเดียวกันผมสามารถลดความซับซ้อนที่ :)
nobe4

ฉันไม่แน่ใจว่าคุณหมายถึง "เก็บการอ้างอิงทั้งหมดในเวลาเดียวกัน" ... แต่เราไม่สามารถรีเซ็ตพจนานุกรมเมื่อใดก็ตามที่เคอร์เซอร์เคลื่อนที่หรือไม่ ฟังก์ชั่นจะตรวจสอบว่าเคอร์เซอร์อยู่ในตำแหน่งเดียวกับครั้งก่อนที่มันถูกเรียกใช้หรือไม่และหากไม่มีการตั้งค่าใหม่
dbmrq

นอกจากนี้วิธีการที่มันไม่ทำงานอย่างถูกต้องเมื่อเคอร์เซอร์ไม่เริ่มต้นคำ พยายามแก้ไขทุกข้อผิดพลาดในประโยคนั้นโดยวางเคอร์เซอร์ไว้ตรงกลางของแต่ละคำ ฉันข้ามไปยังถัดไปทันที
dbmrq

1
ตกลงฉันคิดว่าฉันเข้าใจแล้ว! ตรวจสอบการแก้ไขล่าสุดของฉัน ดูเหมือนว่าจะทำงานได้อย่างสมบูรณ์แบบมาก ฉันจะเปิดคำถามทิ้งไว้อีกสักครู่เพื่อดูว่ามีคนอื่นที่จะเพิ่มหรือไม่ แต่คำตอบของคุณยอดเยี่ยมขอบคุณ :)
dbmrq

2

นอกเหนือจากคำตอบอื่น ๆ <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-@> คือ Control + Space

ยังดู :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 ซึ่งเป็นที่นิยมพอสมควร) แต่ฉันไม่สามารถทำให้พวกมันทำงานได้อย่างที่ต้องการ


Caveat : หากคุณใช้ Vim จากเทอร์มินัล<C-s>จะหมายถึง "stop output" นี่คือสาเหตุที่ทั้งคู่<C-x><C-s> และ <C-x>sถูกแม็พโดยดีฟอลต์ ใช้<C-q>เพื่อส่งออกต่อไปหากคุณกด<C-s>โดยไม่ตั้งใจ คุณสามารถปิดการใช้งาน<C-s>หากคุณไม่ได้ใช้งาน (ดูคำถามนี้ ) หากคุณใช้ GVim คุณสามารถเพิกเฉยได้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.