2

In Vim, using the text object i>, you can edit an inner tag, and using the built-in matchit plugin, you can even use % to jump between matching opening and closing tags.

Let's say you want to change matching div tags to something else, such as section. One way to go about it would be to get to the opening tag with a / search, reach the closing one with n or [count]n, and press ..

What if the contents of the div are very lengthy and comprise many other div tags? For example:

<main>
    <div>
    </div>
    <div id="foo">
        <div class="bar">
        </div>
        <div class="bar">
        </div>
        <div class="bar">
        </div>
    </div> <!-- Closing tag out of view  -->
</main> 

I navigate to the opening tag and change div to section. But now that I've changed the opening tag, I can't use % anymore to jump to the closing one and change it. One way to get to the closing one, then, would be to search for it using its indentation e.g. / < but that seems overly complicated.

Is there a builtin feature or existing plugin that would allow something like jumping to a matching tag with % even after modifying one of them, or (better) a text-object (say mt) that selects the name of matching tags? e.g.

<main>
    <div>
    </div>
    <di█ id="foo">
        ...
    </div> 
</main>

Input: cmtsection<Esc> Result:

<main>
    <div>
    </div>
    <section id="foo">
        ....
    </section> 
</main>

2 Answers 2

2

text-object (say mt) that selects the name of matching tags?

It's hard to think about handling this one with a text object, because a text object is all about selecting some text and then the operation will act on that selection. There's not really a way to select both matching tags at the same time, or to apply the same operation to both...

Having said that, there are ways you can handle that tag replacement. (With or without plug-ins.)

vim-surround plug-in

One plug-in that can help you here is vim-surround by Tim Pope, which includes a cs operation to "change surroundings".

If your cursor is anywhere over your <div id="foo">, you can use cst followed by <section id="foo"> to execute your desired replacement. vim-surround is smart to locate the matching closing tag and replace it with </section> as you would expect.

One downside of this approach is that you need to write the whole new tag, including the id="foo" attribute that you were keeping from the original tag. For this particular example, it's not that bad, but it can get quite inconvenient if you have a long list of long attributes...

UPDATE: From your own answer, I see that using a tag replacement without the ending > will preserve the attributes. So using cst followed by <section and pressing "Enter" does exactly what you want. That's awesome!

vim-surround is very generic, in fact it allows you to manage about all kinds of surroundings... You could even replace the tags with double quotes or with a pair of { and } brackets if you wanted to. So that comes at the cost of its support for tags not going as far as you might have liked it to...

using % from matchit

It's true that once you replace a tag, you're no longer able to use % to jump to the matching closing tag to fix it anymore...

But that doesn't mean you can't use it to fix this particular case!

Simply jump first. Then fix the end tag, and jump back to your previous location (not using the % motion, but '' or Ctrl+O) and then replace the matching block.

For example, with the cursor on top of div, you can use

  • %: Jump to matching closing tag, cursor will be on / of </div>
  • l: Move cursor right, inside the div tag name.
  • ciw: Replace a word. To change the tag name.
  • section: Enter the new tag name.
  • <Esc>: Leave insert mode.
  • <C-O>: Move to last cursor position in jump list.
  • .: Repeat the last ciw operation.

You're done!

So while it's true that % will be broken to get you to the matching tag after you replace one of them, it doesn't mean you can't use it for this purpose, just use it first, while it works, then use a different method to jump back to the original location after it will have stopped working.

honorable mention: emmet-vim plug-in

If you're editing HTML, you should strongly consider adopting the emmet-vim plug-in by Yasuhiro Matsumoto, which makes generating HTML tag structures much easier by allowing you to expand abbreviations.

While emmet-vim doesn't seem to have a direct operation to handle this particular case, you should be able to use a combination of removing a tag followed by adding a new tag, or a similar sequence of commands to achieve this particular goal.

One advantage of emmet-vim over vim-surround is that it's specifically tailored for HTML tags, so you can abbreviate a lot of the syntax and it will expand tags and attributes for you.

In this particular case, using the % from matchit to find the closing tag and a jump back motion still seems like the best alternative. But for complex HTML manipulation, you'll want emmet-vim.

2
  • 1
    Thanks for the diligence and complete answer. I didn't think to use the jumplist! I also didn't know about emmet-vim, and it is awesome. I've selected your answer.
    – lpf
    Commented Apr 22, 2020 at 12:52
  • @lpf Glad it was useful! And hey, you found the magic incantation of vim-surround that I missed, so thanks for reaching me that one today! 👍
    – filbranden
    Commented Apr 22, 2020 at 14:32
2

I found that something like that is possible with the plugin tpope/vim-surround.

<di█ id="foo">
    <div class="bar">
</div>

Or:

<div id="foo">
  █ <div class="bar"></div>
</div>

Input: cst<section | Result:

<section id="foo">
  █ <div class="bar"></div>
</section>

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.