cedaei.com/content/posts/vim-sub-replace-expression.md

2.3 KiB

+++ title = "Vim: Expressions in replacement text" date = "2020-06-14" author = "Ceda EI" tags = ["vim", "neovim", "replacements"] keywords = ["vim", "neovim", "replacements"] description = "Modifying the selection in a :s[ubstitute]// command" showFullContent = false +++

Often times I find myself needing to modify the selection in a substitute command in a non-trivial way. From a recent example, I needed to convert the numbers in the id of the divs to hexadecimal after decrementing them by 1.

Basically, I had to convert

<div id="someprefix1">
	 ...
</div>
<div id="someprefix2">
	...
</div>
<div id="someprefix3">
	...
</div>
<div id="someprefix4">
	...
</div>
...
<div id="someprefix80">
	...
</div>

to

<div id="someprefix0">
	 ...
</div>
<div id="someprefix1">
	...
</div>
<div id="someprefix2">
	...
</div>
<div id="someprefix3">
	...
</div>
...
<div id="someprefix4F">
	...
</div>

Doing this manually is tiring, I could have used macros but I instead chose an even simpler approach.

Vim allows to use expressions in the replacement part of the substitute command if it starts with \=. So, the replacement can be simply done using \= and printf. What I did was simply use the following command after selecting the text in a visual selection.

:'<,'>s/\v(someprefix)(\d+)/\=printf("%s%X", submatch(1), submatch(2) - 1)

The breakdown is as follows:

  • :'<,'>: Sets the range to visual selection.
  • s/: Starts the substitution.
  • \v: Enables "very magic mode". This allows for writing simpler regular expressions without having to escape special characters.
  • (someprefix)(\d+): Captures the string someprefix in group 1 and the number that follows in group 2.
  • /: Ends the search and starts replacement.
  • \=: Tells Vim to treat the following text as an expression.
  • printf(: printf is a function that takes a format string and values for the format parameters.
  • "%s%X": The format string. %s means to print the parameter as a string. %X means to convert the parameter to a hexadecimal number in capital letters (%x for small).
  • submatch(1): It retrieves the first matched group.
  • submatch(2) - 1: It retrieves the second matched group and subtracts one from it.

References

  • :help sub-replace-expression
  • :help submatch
  • :help printf
  • :help /magic
  • :help :s