4

In hope of answering Using the % command on a line without a bracket I tried to write my first Vim script.

My thought was to check if there was a { on the current line. If there was one I wanted % to behave as usual. If not % would be called on the next line by doing j%. This is what I wrote

function Percent_nextline()
    let line = getline('.')
    if line =~ "{"
        normal %
    else
        normal j%
    endif
endfunction

nnoremap g% :call Percent_nextline()<cr>

Then I tried it on this file

foo
{
    { }
}

With the cursor on the first line typing g% does indeed move the cursor to the last }. Then I tried dg%, meaning to delete all the block. This failed, and made the bell sound. The buffer was left unchanged.

So I have two questions:

  • how could I make commands like c, y, d, etc. work with this new mapping?

  • if I want to use just % rather than g% how could I stop the function from recursing?

3

how could I make commands like c, y, d, etc. work with this new mapping?

In Vim, it's all in the modes. The one after commands like c, y, etc., where Vim is waiting for the {motion}, is called operator-pending mode. :help omap-info explains that you can visually select the block to work upon. Likewise, you probably want to define a visual-mode mapping, too:

:nnoremap % :<C-u>call Percent_nextline()<cr>
:xnoremap % :<C-u>execute 'normal! gv'<Bar>call Percent_nextline()<cr>
:onoremap % :<C-u>execute 'normal! V'<Bar>call Percent_nextline()<cr>
  • the <C-u> removes the range inserted in visual mode (and if you use a [count])
  • the visual mode mapping first re-creates the existing selection with gv
  • likewise, the :omap goes into line-wise (I think this makes the most sense here) selection

if I want to use just % rather than g% how could I stop the function from recursing?

By using :normal! (with !), existing mappings are ignored. Like :noremap, you should always use this, unless you explicitly need mappings to apply. Here, you only lose the functionality of the matchit plugin (in case you have that installed), but for pure {...} matching, you don't need that. You might consider sticking to the g% alternative mapping (or using a filetype-specific :map <buffer> % only for C/C++).

Open issues

  • When there's no matching {...}, your mappings go one line down, which I find irritating. I think you can fix that easily yourself.
  • Drop the : sigil; in a script, this isn't needed.
  • Thank you very much. With your answer I learned about normal!, onoremap and that the : is not needed in the script. I will edit my question and remove the :s. You can read my asnwer on vi.stackexchange.com/a/2823/356. Feel free to edit it if you think it can be improved. – Gonçalo Ribeiro Apr 3 '15 at 18:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.