DEV Community

Corey Alexander
Corey Alexander

Posted on • Updated on • Originally published at coreyja.com

 

VIM Spelling Suggestions with fzf

This was originally posted on coreyja.com

Example GIF

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>

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

Collapse
 
maxdevjs profile image
maxdevjs

I use fzf for a lot of canonical things in (n)vim, but this one is something new. Thank you :)

Collapse
 
coreyja profile image
Corey Alexander

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!

Collapse
 
maxdevjs profile image
maxdevjs

I checked your .vimrc and it is pretty clean :D Mine is a working in progress mess (better to rely on Ctrl-F)

Thread Thread
 
coreyja profile image
Corey Alexander

Oh I like vim-devicons that's really cool, I might have to steal that fzf integration with it!

Thread Thread
 
maxdevjs profile image
maxdevjs

In that case, take a look at the FZF Support? #106 updated issue on ryanoasis/vim-devicons ...

Thread Thread
 
coreyja profile image
Corey Alexander

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!

Thread Thread
 
coreyja profile image
Corey Alexander

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

Thread Thread
 
maxdevjs profile image
maxdevjs

Great, thank you... Effectively, I came back to check if it was ready, and going to read it now :D

Collapse
 
derek profile image
derek

šŸ˜Thank you so much!!!

This improved my workflow++

pretty much me now

Collapse
 
coreyja profile image
Corey Alexander

Awesome!!

Glad it helped you out! šŸ™ƒ

Collapse
 
delbetu profile image
M Bellucci

Great post!
It would be nice to see a gif showing this in action.

Collapse
 
coreyja profile image
Corey Alexander

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!

Collapse
 
anarchyrucks profile image
Ashish Acharya

TIL: z=

Collapse
 
sourabhjains profile image
Sourabh jain

Thanks.

Regex for lazy developers

regex for lazy devs

You know who you are. Sorry for the callout šŸ˜†