Change HTML tag in vim, but keeping the attributes (surround)

Let's say I have a tag (and cursor at *):

<h1 class="blah" id="moo">H*ello!</h1>

I want to change it to:

*<h2 class="blah" id="moo">Hello</h2>

i.e. Change the type of tag, but keep all the elements.

Using surround.vim, I could do:

cst<h2>

but that changes the HTML to:

*<h2>Hello</h2>

Is just changing the tag possible, but keeping all the attributes? Surround documentation doesn't seem to contain anything like this...


Solution 1:

Replacing tag while keeping attributes has been added to Surround.vim

cst<p> replaces whole tag, while cst<p (without closing bracket) keeps attributes.

Solution 2:

You can use cstt and it'll display < on the bottom and type the tag name without >

in this case, cstth2 and hit enter.

Solution 3:

I have xml.vim plugin (https://github.com/othree/xml.vim) . If you had it too, your requirement is rather easy.

Just move cursor to tag, press <leader>c (lowercase c), then input new tagname, only tag name will be changed.

If you press <leader>C (Big C), also rename the tag/element, but also original attributes are removed.

Solution 4:

Surround does not have this built in. You can yank the attributes and then use <c-r>" when typing out the replacement tag to bring them back, but that is a bit lame.

I propose a new mapping and function that will automate this task. The following will provide the cse mapping, aka change surrounding element. Put this in a your ~/.vimrc file or maybe ~/.vim/after/plugin/surround_change_element.vim if you feel overly orangized.

function! s:ChangeElement()
  execute "normal! vat\<esc>"
  call setpos('.', getpos("'<"))
  let restore = @"
  normal! yi>
  let attributes = substitute(@", '^[^ ]*', '', '')
  let @" = restore
  let dounmapb = 0
  if !maparg(">","c")
    let dounmapb = 1
    " Hide from AsNeeded
    exe "cn"."oremap > <CR>"
  endif
  let tag = input('<', '')
  if dounmapb
    silent! cunmap >
  endif
  let tag = substitute(tag, '>*$', '', '')
  exe "normal cst<" . tag . attributes . ">"
endfunction
nnoremap cse :call <SID>ChangeElement()<cr>

Note: this will shadow some cases of surroundings with e if you have any created via g:surround_101 or b:surround_101. If that is the case change the mapping form cse to something else maybe csn for change surrounding node.

EDIT

As of February 22, 2015 this answer is out of date. Please see @Wojtek Kruszewski post or :h surround-replacements for how to do this natively with surround.

Solution 5:

In my case, I would try matchit.vim or text-object.

matchit.vim solution:

matchit.vim is included in vim.

source $VIMRUNTIME/macros/matchit.vim

and then 0l%%lr2<Ctrl-o>llr20.

text-object solution:

You can also evacuate html content to the register before replace them.

dit:.s/h1/h2/g<Ctrl-o>P0