This was originally posted on coreyja.com
TlDr
Use fzf to show VIM spelling suggestions, and override the built in z=
shortcut
function! FzfSpellSink(word)
exe 'normal! "_ciw'.a:word
endfunction
function! FzfSpell()
let suggestions = spellsuggest(expand("<cword>"))
return fzf#run({'source': suggestions, 'sink': function("FzfSpellSink"), 'down': 10 })
endfunction
nnoremap z= :call FzfSpell()<CR>
Background
Recently I was looking to add spell-checking to VIM and came across this great ThoughtBot article, Vim Spell-Checking that got me started.
This article showed me how to turn on spellchecking, and also to turn it on for specific files. Here are the snippets from the .vimrc
that are directly inspired from this blog post.
autocmd BufRead,BufNewFile *.md setlocal spell spelllang=en_us
autocmd FileType gitcommit setlocal spell spelllang=en_us
set complete+=kspell
The Problem
This served me well for quite awhile, but when I ran across misspelled words, it often left me looking for a better way to replace them.
VIM has a built in solution to this in the form of the z=
keyboard shortcut. This takes the current word under cursor and shows spelling suggestions, where selecting a selection will replace the current word. This is the functionality I wanted to keep, but I didn't love the interface that the suggestions appeared in. They take up the entire VIM screen and force you to pick by entering the number corresponding to the word you would like. I am a big fan of fzf, and I wanted to use this for spelling suggestions!
The Solution
The first thing I needed was a list of the spelling suggestions for the word under the current cursor. Getting the current word is simple enough with expand('<cword>')
so now I just needed to get the spelling suggestions for it.
After a bit of digging 1 I found the VIM function spellsuggest
. This function takes the word we want suggestions for as its first arguments. It also takes an optional second and third argument, which we are not currently using. The second argument is the number of suggestions to return, the default is 25. The third argument is a flag for whether we should filter to only capitalized words.
spellsuggest(expand('<cword>'))
So I could now use the above command to get a VIM list of spelling suggestions. Next step was to get fzf to let me select an option from this list.
The fzf repo has a readme that details how to use fzf in VIM. I was mostly interested in how to use the fzf#run
function, which is the main function for calling into fzf. This can take a VIM list as input, so it fits really nicely with the list of spelling suggestions we already generated. We pass this is as the source
to fzf#run
. The other important option is sink
which tells fzf what to do after we have selected a suggestion. Now its time to replace the word under the cursor with our suggestion! One of the accepted types for sink
is a VIM function reference so I needed another function to call as a callback, which will responsible for actually replacing the word under the cursor. So far we have the following
function! FzfSpell()
let suggestions = spellsuggest(expand("<cword>"))
return fzf#run({'source': suggestions, 'sink': function("FzfSpellSink"), 'down': 10 })
endfunction
For this we can use the VIM command ciw
to change the current word to what we selected. To execute that from vimscript we use exe
and normal!
giving us the following. This uses the normal mode ciw
and sends the old value to the black hole register "_
, it then inserts the new word.
function! FzfSpellSink(word)
exe 'normal! "_ciw'.a:word
endfunction
The last thing we need is a keyboard shortcut, so I can access this quickly. I want to use this instead of the default z=
behavior so I decided to just remap that shortcut
nnoremap z= :call FzfSpell()<CR>
And finally putting it all together we have... :drumroll:
function! FzfSpellSink(word)
exe 'normal! "_ciw'.a:word
endfunction
function! FzfSpell()
let suggestions = spellsuggest(expand("<cword>"))
return fzf#run({'source': suggestions, 'sink': function("FzfSpellSink"), 'down': 10 })
endfunction
nnoremap z= :call FzfSpell()<CR>
-
I eventually found this function by digging into the source code of the kopischke/unite-spell-suggest repo. This plugin did a similar thing with Unite as the fuzzy finding tool.Ā ā©
Top comments (14)
I use fzf for a lot of canonical things in (n)vim, but this one is something new. Thank you :)
No problem! Glad I was able to show someone something new!
Got any interesting usages of fzf you want to share? :-) fzf is one of my favorite tools, so I'm always looking for new ways to use it!
I checked your .vimrc and it is pretty clean :D Mine is a working in progress mess (better to rely on Ctrl-F)
Oh I like
vim-devicons
that's really cool, I might have to steal that fzf integration with it!In that case, take a look at the FZF Support? #106 updated issue on ryanoasis/vim-devicons ...
Hehe ya I found that issue from your dotfiles too! I took that idea and ran with it! I'll get another blog post up in a day or two explaining where I ended up! Thanks for the inspiration!
Here is the post I mentioned that I got posted today! Some performance improvements for fzf with Dev Icons: dev.to/coreyja/vim-fzf-with-devico...
Great, thank you... Effectively, I came back to check if it was ready, and going to read it now :D
šThank you so much!!!
This improved my
workflow++
Awesome!!
Glad it helped you out! š
Great post!
It would be nice to see a gif showing this in action.
Agreed! I got one added in here! I might try to replace it here soon though cause the statusbar at the bottom flashing isn't my favorite!
TIL: z=
Thanks.