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

Vimscript Case Study: Grep Operator, Part III


May 24, 2021 Vim


Table of contents


Our new "grep operator" works well, but the purpose of writing Vimscript is to thoughtfully improve the lives of your users. There are two additional things we can do to make our operators more in line with the requirements of the Vim ecosystem.

Protect the register

By copying the text to an unnamed register, we destroyed what was there before.

This is not what our users want, so let's save the contents of the register and reload it before copying it. Modify the code to this:

nnoremap <leader>g :set operatorfunc=GrepOperator<cr>g@
vnoremap <leader>g :<c-u>call GrepOperator(visualmode())<cr>

function! GrepOperator(type)
    let saved_unnamed_register = @@

    if a:type ==# 'v'
        normal! `<v`>y
    elseif a:type ==# 'char'
        normal! `[v`]y
    else
        return
    endif

    silent execute "grep! -R " . shellescape(@@) . " ."
    copen

    let @@ = saved_unnamed_register
endfunction

We added two let statements at the beginning and end let function. The first saves the contents of @@ variable, and the second reloads the saved content.

Save and source files. Test it, copy some text, then press the operator by <leader>giw and then p the previously copied text.

When writing a Vim plug-in, you should try to save the original settings and register values before modifying them and load them back later. This avoids the possibility of panicking users.

Namespace

Our script created the function GrepOperator That's probably not a big deal, but when you write Vimscript, don't apologize in the event that it's any better.

With just a few lines of code, we can avoid contaminating the global namespace. Modify the code to this:

nnoremap <leader>g :set operatorfunc=<SID>GrepOperator<cr>g@
vnoremap <leader>g :<c-u>call <SID>GrepOperator(visualmode())<cr>

function! s:GrepOperator(type)
    let saved_unnamed_register = @@

    if a:type ==# 'v'
        normal! `<v`>y
    elseif a:type ==# 'char'
        normal! `[v`]y
    else
        return
    endif

    silent execute "grep! -R " . shellescape(@@) . " ."
    copen

    let @@ = saved_unnamed_register
endfunction

The first three lines of the script have been changed. First, we add the prefix s: before the function name so that it is in the namespace of the current script.

We've also modified the mapping to add <SID> before GrepOperator so Vim can find this function. If we don't, Vim will try to find the function in the global namespace, which is impossible to find.

Cheers, our grep-operator.vim is not only very useful, but also a good Vimscript citizen!

Practice

Read: :help <SID>

Enjoy it, eat some snacks and treat yourself.