Coding With Fun
Home Docker Django Node.js Articles Python pip guide FAQ Policy

Vimscript switch


May 24, 2021 Vim


Table of contents


In the first few chapters we talked about how to set options in Vim. F or the Boolean option, we set someoption! t o "switch" the option. If we could create a map for this command, that would be great.

Execute the following command:

:nnoremap <leader>N :setlocal number!<cr>

In normal mode, <leader>N see. V im switches between turning the line number display on and off. Switching maps like this is convenient, so we don't need two separate keys to turn on/off.

Unfortunately, this only works for the Boolean option. If we want to switch a non-Boolean option, more needs to be done.

Switch options

Start by creating a function where you can switch options and calling a map of the function. Add the following code to ~/.vimrc (or a separate file ~/.vim/plugin/ you want):

nnoremap <leader>f :call FoldColumnToggle()<cr>

function! FoldColumnToggle()
    echom &foldcolumn
endfunction

Save and source the file, and then press <leader>f try it. V im displays the foldcolumn option. If you are not familiar with this :help foldcolumn and continue.

Let's add the real switching feature. Modify the code to this:

nnoremap <leader>f :call FoldColumnToggle()<cr>

function! FoldColumnToggle()
    if &foldcolumn
        setlocal foldcolumn=0
    else
        setlocal foldcolumn=4
    endif
endfunction

Save and source the file, and then try it. Each time you press it Vim will display or hide the folding status bar.

if statement &foldcolumn is true (remember that Vim treats 0 as false and other numbers as true). I f so, set it to 0 (hide it). O therwise, set it to 4. It's as simple as that.

You can use a simple function like this to switch any option 0 at 0 and open with other numbers.

Switch something else

Our dreams should not stop at switching options. A nother thing we want to switch to is the quickfix window. T he previous skeleton code is still used as a starting point. Add the following code to your file:

nnoremap <leader>q :call QuickfixToggle()<cr>

function! QuickfixToggle()
    return
endfunction

This mapping does nothing for the time being. L et's turn it into something else that's a little bit useful (but it's not completely done yet). Change the code to this:

nnoremap <leader>q :call QuickfixToggle()<cr>

function! QuickfixToggle()
    copen
endfunction

Save and source files. If you try this mapping now, you'll see an empty quickfix window.

In order to achieve the switching function, we will choose a quick and dirty means: global variables. Change the code to 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 do is simple -- each time we call a function, we use a global variable to store the switch state of the quickfix window.

Save and source the file, then perform a mapping try. V im will complain that variables are not yet defined! So let's initialize the variables first.

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

Save and source the file, then try the mapping. It worked!

Improved

Our switch function works, but there are still some problems.

The first problem is that assuming that the :copen window :cclose our global variables will not be refreshed. In fact, this is not a big problem, because most of the time users will use this mapping switch window, in case it is not open, they will press again.

This is another important experience with writing Vimscript code: if you try to deal with every marginal condition, you will be trapped in it and there will be no progress.

In most cases, it's much better to roll out code that works (and doesn't break it) and then go back and improve it, much better than spending many hours demanding perfection. E xcept that you're developing a plug-in that's likely to be used by a lot of people. In this case it is worth the time to reach an unassailable level, satisfy users, and reduce bug reporting.

Reload the window/buffer

Another problem with our function is that when the user has opened the quickfix window and performed this mapping, Vim closes the window and then bounces them to the previous split instead of sending them back to where they were before. If you just want to take a quick look at the quickfix window and get back to work, it's annoying that this is happening.

To solve this problem, we'll introduce a usage that is useful when writing Vim plug-ins. Change your code to 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 added two new lines to the map. One line (in else branch) sets another global variable to hold the current window serial number when :copen

Another line (in if wincmd w with that serial wincmd w tell Vim to jump to the corresponding window.

Once again, our solution is not unassailable, with users likely to open or close new segments between execution maps. Even so, it's suitable for most occasions, so it's good enough at the moment.

In most programs, this trick of manually saving global state is condemned, but for a very short Vimscript function, it's fast and dirty, but it's mission-free and fulfills the task.

Practice

Read :help foldcolumn .

Read :help winnr()

Read :help ctrl-w_w .

Read :help wincmd .

Add s: and <SID> to limit the function to a private namespace. s: