In one of the first chapters we talked about how to set options in Vim. For
boolean options we can use
set someoption! to "toggle" the option. This is
especially nice when we create a mapping for that command.
Run the following command:
:nnoremap <leader>N :setlocal number!<cr>
Try it out by pressing
<leader>N in normal mode. Vim will toggle the line
numbers for the current window off and on. Creating a "toggle" mapping like
this is really handy, because we don't need to have two separate keys to turn
something off and on.
Unfortunately this only works for boolean options. If we want to toggle a non-boolean option we'll need to do a bit more work.
Let's start by creating a function that will toggle an option for us, and
a mapping that will call it. Put the following into your
~/.vimrc file (or
a separate file in
~/.vim/plugin/ if you prefer):
nnoremap <leader>f :call FoldColumnToggle()<cr> function! FoldColumnToggle() echom &foldcolumn endfunction
Write and source the file, then try it out by pressing
<leader>f Vim will
display the current value of the
foldcolumn option. Go ahead and read
foldcolumn if you're unfamiliar with this option.
Let's add in the actual toggling functionality. Edit the code to look like this:
nnoremap <leader>f :call FoldColumnToggle()<cr> function! FoldColumnToggle() if &foldcolumn setlocal foldcolumn=0 else setlocal foldcolumn=4 endif endfunction
Write and source the file and try it out. Each time you press it Vim will either show or hide the fold column.
if statement simply checks if
&foldcolumn is truthy (remember that Vim
treats the integer 0 as falsy and any other number as truthy). If so, it sets
it to zero (which hides it). Otherwise it sets it to four. Pretty simple.
You can use a simple function like this to toggle any option where
"off" and any other number is "on".
Options aren't the only thing we might want to toggle. One particularly nice thing to have a mapping for is the quickfix window. Let's start with the same skeleton as before. Add the following code to your file:
nnoremap <leader>q :call QuickfixToggle()<cr> function! QuickfixToggle() return endfunction
This mapping doesn't do anything yet. Let's transform it into something slightly more useful (but not completely finished yet). Change the code to look like this:
nnoremap <leader>q :call QuickfixToggle()<cr> function! QuickfixToggle() copen endfunction
Write and source the file. If you try out the mapping now you'll see that it simply opens the quickfix window.
To get the "toggling" behavior we're looking for we'll use a quick, dirty solution: a global variable. Change the code to look like this:
nnoremap <leader>q :call QuickfixToggle()<cr> function! QuickfixToggle() if g:quickfix_is_open cclose let g:quickfix_is_open = 0 else copen let g:quickfix_is_open = 1 endif endfunction
What we've done is pretty simple -- we're simply storing a global variable describing the open/closed state of the quickfix window whenever we call the function.
Write and source the file, and try to run the mapping. Vim will complain that the variable is not defined yet! Let's fix that by initializing it once:
nnoremap <leader>q :call QuickfixToggle()<cr> let g:quickfix_is_open = 0 function! QuickfixToggle() if g:quickfix_is_open cclose let g:quickfix_is_open = 0 else copen let g:quickfix_is_open = 1 endif endfunction
Write and source the file, and try the mapping. It works!
Our toggle function works, but has a few problems.
The first is that if the user manually opens or closes the window with
:cclose our global variable doesn't get updated. This isn't really a huge
problem in practice because most of the time the user will probably be opening
the window with the mapping, and if not they can always just press it again.
This illustrates an important point about writing Vimscript code: if you try to handle every single edge case you'll get bogged down in it and never get any work done.
Getting something that works most of the time (and doesn't explode when it doesn't work) and getting back to coding is usually better than spending hours getting it 100% perfect. The exception is when you're writing a plugin you expect many people to use. In that case it's best to spend the time and make it bulletproof to keep your users happy and reduce bug reports.
The other problem with our function is that if the user runs the mapping when they're already in the quickfix window, Vim closes it and dumps them into the last split instead of sending them back where they were. This is annoying if you just want to check the quickfix window really quick and get back to working.
To solve this we'll introduce an idiom that comes in handy a lot when writing Vim plugins. Edit your code to look like this:
nnoremap <leader>q :call QuickfixToggle()<cr> let g:quickfix_is_open = 0 function! QuickfixToggle() if g:quickfix_is_open cclose let g:quickfix_is_open = 0 execute g:quickfix_return_to_window . "wincmd w" else let g:quickfix_return_to_window = winnr() copen let g:quickfix_is_open = 1 endif endfunction
We've added two new lines in this mapping. One of them (in the
sets another global variable which saves the current window number before we run
The second line (in the
if clause) executes
wincmd w with that number
prepended as a count, which tells Vim to go to that window.
Once again our solution isn't bulletproof, because the user might open or close new split between runs of the mapping. Even so, it handles the majority of cases so it's good enough for now.
This strategy of manually saving global state would be frowned upon in most serious programs, but for tiny little Vimscript functions it's a quick and dirty way of getting something mostly working and moving on with your life.
Namespace the functions by adding
<SID> where necessary.