I edit DocBook XML files. In my plugin, i want to implement a function that would increase or decrease the section numbers recursively, assuming that cursor is at the toplevel section. For example, when my cursor is at the top <sect2 element, it means to turn

  <para>some text</para>


  <para>some text</para>

i have this code in my script, but it does not work:

execute 'normal vat' # select the text up to the closing tag
execute 'normal :s/sect\(\d\?\)>/\="sect" . (submatch(1) - 1). ">"/g'

but it does not work. any idea?

  • 1
    :s is an ex command, it can be executed directly.
    – dedowsdi
    Commented May 17, 2019 at 8:40
  • 1
    @dedowsdi so is normal; none of the commands here require execute at all. Plus we should really use normal! unless we know what we are doing
    – D. Ben Knoble
    Commented May 17, 2019 at 8:53

2 Answers 2


Visually select your lines and press CTRL-x to decrease them.

From :help v_CTRL-x:

v_CTRL-X                                             {Visual}CTRL-X
                    Subtract [count] from the number or alphabetic                                                                                        
                    character in the highlighted text.  {not in Vi} 

See also :help CTRL-x. And you can use CTRL-a to increment.

In your vimscript, it will that (tested):

" select the text up to the closing tag
execute 'normal vat'
" Here the double quotes are important for the <C-X> to be executed
execute "normal \<C-X>"

EDIT: To decrement only the "sect", you can use this function, however you need to do the selection before calling it (I haven't found a way to make a selection and use '< and '> both from within the function, if anyone has a suggestion):

function! DecSect()
    silent! substitute/sect\(\d\)>/\="sect".(submatch(1) - 1).">"/g
command -range -nargs=0 DecSect <line1>,<line2>call DecSect()

On the other hand, externalizing the visual selection gives you more flexibility to use it (you can use it on a tag, paragraph, document, line...), cf below.

To use it, first visual select the section you want to apply it on:


Then call it:


Or just call it directly to execute it on the current line:

  • But that increments all the numbers, right? I only need to increase/decrease <sectX> parts. Commented May 17, 2019 at 8:38
  • Indeed. If you have potentially other numbers, let me see what else can be done. Also, your lines containing the sectX, do they contain other numbers?
    – padawin
    Commented May 17, 2019 at 8:41
  • Unfortunately, they might, i cannot control users' formatting. My regexp works perfectly when run manually from the buffer cmdline, but refuses to run from the script. I suspect that problem is in the substitute command, it probably does not respect the previous vat and has no range to run on maybe? Commented May 17, 2019 at 8:45
  • Edited with a function you can put in your .vimrc
    – padawin
    Commented May 17, 2019 at 9:14

Simply try

normal! vat
substitute/sect\(\d\)>/\="sect".(submatch(1) - 1).">"/

I’ve removed the executes, because the commands provides are valid vimscript commands and don’t need it.

I used normal! to avoid mappings interfering.

Finally, I removed the \? option on the digits, since your example always has digits after the sects.

There are other ways to accomplish the same idea. For example:

  1. qa start a macro
  2. <C-X> increment
  3. q stop macro
  4. (Optional) if step (2) caused changes in the buffer, undo them with u
  5. g/sect\d/normal! @a Run the macro on section lines. <C-X> by default will jump to the first number on a line for decrements.

From a script, steps (1)-(4) can be replaced by

  1. let @a="\<C-X>"
  • Thanks for the hint. Now the range seems to be the problem, and prepending '<,'> causes an error message. I simply need to find out the line number of the matching XML tag, maybe even without the vat, that would solve everything. Commented May 17, 2019 at 9:08
  • @TomášBažant can you try the script version of the macro? See my edits.
    – D. Ben Knoble
    Commented May 17, 2019 at 9:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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