In a file I'd like to replace some occurrences of triplets of 0 with F, 1 with E, ..., F with 0. Only some, so I can't just pipe the whole file through a bash tr filter; I need a text editor to examine each occurrence.

This is to invert the grayscale colors in some 1990's HTML from light-on-dark to dark-on-light, #000 to/from #FFF, #222 to/from #DDD,... .

What almost works: cursor to the first digit, ! tr 0123456789ABCDEF FEDCBA9876543210. After that I can just hit dot to repeat. But that also reverses any other digits on the line. For example, it changes

border: 2px solid #555;


border: Dpx solid #AAA;

  • Can I restrict the command to affect just the 555?
  • Or can I write a macro purely within vim that reverses this range of values, just like tr?
  • Or can a command other than tr, applied to the whole line, limit its effect to /#[0-9]{3}/? Maybe some sed magic?

(If the last of these three turns out to be the solution, then this question may be off topic here.)

  • 2
    The ! filter unfortunately always works linewise, so you’d need some vimscript to handle grabbing the appropriate text, filter it, and put it back. – D. Ben Knoble May 9 '19 at 23:01

use :h :s\=

s/\v\#[0-9A-F]{3}[0-9A-F]@!/\=tr(submatch(0), '0123456789ABCDEF','FEDCBA9876543210')/g
  • \#[0-9A-F]{3} match #000, #111, ... #FFF
  • [0-9A-F]@! look forward to make sure #0000 doesn't match. check :h /\@!
  • \= replace with an expression
  • tr(submatch(0), '0123456789ABCDEF','FEDCBA9876543210') transform matching text

Since you want to do it manually, a command or map would help:

command InvertGrayScale s/\v\#[0-9A-F]{3}[0-9A-F]@!/\=tr(submatch(0), '0123456789ABCDEF','FEDCBA9876543210')/g

nnoremap <leader>ig :InvertGrayScale<cr>
| improve this answer | |

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.