56

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...

cazgp
  • 1,538
  • 1
  • 12
  • 26
  • 3
    I'd say keep it KISS and use `s///` if you just want to change tag1 => tag2. – timss May 02 '13 at 14:42
  • Good point! It's just a bit of extra work if the tag content spans multiple lines, I was hoping there'd be an answer with less movement / line number fiddling. Thanks :) – cazgp May 02 '13 at 17:08
  • @timss no, you have to do that twice (once for the opening and once for the closing tag) which is needlessly manual. –  Feb 10 '15 at 09:05
  • @рытфолд Not at all, just use `s/h1/h2/g` or `set gdefault`. – timss Feb 10 '15 at 12:55

5 Answers5

134

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

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

Wojtek Kruszewski
  • 13,940
  • 6
  • 38
  • 38
45

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

in this case, cstth2 and hit enter.

Surahman
  • 1,040
  • 9
  • 15
  • 2
    This is great! I'd also like to know why this works. `cst` from vim-surround makes sense, but what does the extra `t` do? – brianz Jul 03 '19 at 20:38
  • I think extra t comes from tag. Like change tag with tag – Alexandru Oct 01 '19 at 09:26
  • Cant create a hotkey for it without using feedkeys, i.e.: `vim.keymap.set({"n", "x"}, "w", function() vim.fn.feedkeys("cstt") end, {noremap = true})` – run_the_race Feb 28 '23 at 11:21
7

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.

Kent
  • 189,393
  • 32
  • 233
  • 301
2

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.

Peter Rincker
  • 43,539
  • 9
  • 74
  • 101
1

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

ernix
  • 3,442
  • 1
  • 17
  • 23