I'm working on some old HTML documentation, where are multiple code snippets (several lines long). I need to replace this old HTML-ized code with new one using modern styling.

What I have now:

... ...
<font color="ff0000">void</font> main() {
  <font color="00ff00">return</font> <font color="00ffff">0</font>;
... ...

Now I can select this fragment between pre tags and remove all HTML using some keystrokes which I defined. The result is pure C code which looks like this:

... ...
void main() {
  return 0;
... ...

The final step, which is time consuming, is to select this clean code again, and run :TOhtml command, which generates the whole new HTML document (in new unsaved buffer) with proper highlighting of the code. I have to scroll thru this document, find pre tag and cut the styled code fragment. Then I have to close this new buffer, return to my old buffer, paste new code, and select and delete the old code AGAIN. This is awkard and very time consuming.

Question: how to select the old "bad" code and press one keystroke to have this work done? It has to be just cleaned from old HTML to pure cod and then colorized and the result should replace in-place the old code.

Desired result:

... ...
<pre class="code">
<span class="keyword">void</span> <span class="funcname">main</span>() {
  <span class="return">return</span> <span class="number">0</span>;
... ...

CSS class names are irrelevant - I can style them in my external CSS file, they only have to be inserted. I prefer not to load any huge/junky/bloated/bundle plugin system.

  • One solution which I found is rather dirty, but sometimes works - unfortunately it uses defaults, even when I configure variables for TOhtml script. Anyway, here it is: vmap K :TOhtml<CR>ggV/Code<CR>kd/<\/pre><CR>jdGVgg"*y:q!<CR>>pkV<d<CR>
    – piotao
    Oct 25, 2020 at 22:14
  • Can you edit to show a (small, please!) before-and-after? That would go a long way towards making your question answerable
    – D. Ben Knoble
    Oct 25, 2020 at 23:11
  • Not a vim solution, though it can be run from within vim if you like: have you tried HTML Tidy? You can run it on thousands of files with a single command.
    – MDeBusk
    Nov 15, 2022 at 23:40

2 Answers 2


OK, I need to think about something else than freaking Magento.

Pandoc would help with the first step but the rest can be done with pure Vim commands…

  1. Tell :help :TOhtml to only generate fragments:

    :let g:html_no_doc = 1
  2. Turn the legacy HTML into plain text:

    :'<,'>!pandoc -f html -t plain

    See :help v, :help :range and :help :range!.

  3. Indent the snippet properly:


    See :help = and :help '].

  4. Execute :TOhtml on the relevant lines:

  5. Yank the <pre> element:


    See :help at.

  6. Get rid of the TOhtml buffer:


    See :help :bwipeout.

  7. Replace the plain text block with the HTML:


    See :help V.

…and automated relatively easily:

xnoremap <key> :!pandoc -f html -t plain<CR>=']:,']TOhtml<CR>yat:bwipeout!<CR>V']p

In action:



So far, I managed to solve this in two steps and one selection. Here is how it goes:

  1. I select the fragment I wish to clean from HTML then I press my leader key and d. All HTML is cleaned, and the result is pure C code.

  2. I select this code and press another key - in my case - shift+K. All code is changed in-place and now it contains coloring inserted as HTML spans with some classes I can then style.

Step one is done by defining a key which performs replacement on selection:

" d remove all HTML tags from  selection
vnoremap <leader>d :s/<\_.\{-1,\}>//g<CR>:'<,'>s/&quot;/"/g<CR>:'<,'>s/&lt;/</g<CR>:'<,'>s/&gt;/>/g<CR>

In this keystroke I defined few noticed html entities, like & lt; etc.

Step two was done by recording a macro and the by assign it to keystroke. First I carefully recorded a macro with qq, and then I copied it from :registers window to my config. After some tuning I get to this:

" this mapping IS converting any selected code to its HTML syntax highlighting
vmap K :TOhtml<CR>ggV/Code<CR>kd/<\/pre><CR>jdGVgg"*y:q!<CR>`>pkV`<d:.s/ id='vimCodeElement'/ class="code"/<CR>

This "solution" is not complete, because (CONS):

  1. It requires to configure TOhtml first (for disabling line numbers, expanding tab, etc. - so I put global variables configuring that to my vimrc file, which is not needed. I think you should be done with augroup and autocmd maybe, because this context is valid only for HTML documents and conversion to HTML from C

  2. To achieve in-place replacement, I have to select text twice - first for cleaning, then for conversion. It may be good when I would like to investigate how old code was cleaned up (maybe some entities remain, who knows), but in general, in proper and complete HTML removal, this would not be necessary. It should be converted at once.

  3. Keystrokes and general solution I've found seems vulnerable. There are <CR>'s in the command and command like typing - somehow this is rather hacky and nasty way of doing job. And it is 'readonly' - it's hard to change anything inside those commands.

So, I think that there could be a nicer solution to that, and more universal. However, it has a huge PRO: it is done with just two selection actions and two keystrokes. For me it's a huge improvement.

  • Also, another CON is that this operation is actually messing up the buffers positions: for example, if I have a buffer which is only 7 lines high, opening a new buffer forces all buffers to recalculate and their widths and heights are 'averaged' like vim is normally doing. So after having two buffers, on 15 lines high, the other 5 lines high, I end up with those buffers exactly 10 lines high after every single conversion. This is somehow awkard. In I only know, how to perform operations on hidden, buffer...
    – piotao
    Oct 28, 2020 at 12:09
  • Yet another CON I found recently - when only one single line of code is selected, this solution do not work and additional junky HTML is inserted into the document. Few lines above proper content are added, and they should be manually deleted, and pre class="code" should be entered manually. So, in order to have this working, one has to select at least TWO lines.
    – piotao
    Oct 31, 2020 at 12:47

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.