That's a great opportunity to leverage text objects (see :help text-objects
). The following code extends the normal behavior of the it
text object (see :help it
and :help tag-blocks
) to make it consider multi-line HTML tags content as described in your post. In that way, you could also type in vit>
, >it
, dit
, cit
, yit
and so on, with the extended behavior in place. You can put this code at the end of the vimrc
file:
" 0: Strict, consider non-blank characters before and after the tags.
" 1: Flexible, ignore non-blank characters before and after the tags.
let g:innerMultilineHTMLTagMode = 0
function! InnerMultilineHTMLTag()
" Get the position of the first line of the last selected Visual area.
let openingMark = getpos("'<")
" Get the position of the last line of the last selected Visual area.
let closingMark = getpos("'>")
" Check whether both marks are on the same line.
if openingMark[1] != closingMark[1]
" Get the lines where the marks are on.
let openingLine = getline(openingMark[1])
let closingLine = getline(closingMark[1])
" Check whether there's nothing appended to the opening tag.
if g:innerMultilineHTMLTagMode == 1 ||
\ match( openingLine, '\S', openingMark[2] - 1) == -1
" Check whether the closing tag is at the beginning of the line.
if match( closingLine, "$" ) + 1 == closingMark[2]
" Restore and adjust the last Visual area.
normal! gvVojo
return
" Check whether there's nothing prepended to the closing tag.
elseif g:innerMultilineHTMLTagMode == 1 ||
\ match( closingLine, '\S\%<' . closingMark[2] . "c" ) == -1
" Restore and adjust the last Visual area.
normal! gvVkojo
return
endif
endif
endif
" Do nothing. Restore the last Visual area.
normal! gv
endfunction
function! ToggleInnerMultilineHTMLTagMode()
if g:innerMultilineHTMLTagMode == 0
let g:innerMultilineHTMLTagMode = 1
echo "it text object is now flexible"
else
let g:innerMultilineHTMLTagMode = 0
echo "it text object is now strict"
endif
endfunction
" Map to set the multi-line HTML tag mode.
nnoremap - :call ToggleInnerMultilineHTMLTagMode()<CR>
" Map to extend the behavior of the 'it' text object to create linewise
" visual areas within multi-line HTML tags.
" See https://vi.stackexchange.com/q/13050/6698
vnoremap it it:<C-U>call InnerMultilineHTMLTag()<CR>
omap it :normal vit<CR>
There are two modes that can be toggled by hitting -:
Strict mode: (default) Consider the text before and after the targeted tags. If there are non-blank characters after the opening tag or before the closing tag, the it
text object will fall back on the normal behavior. Otherwise It'll make the visual area linewise and only select the content within the tags, ignoring blanks both after the opening tag and before the closing tag.
Flexible mode: Ignore the text before and after the targeted tags.
The flexible mode is useful when the code has the following form:
<div class="one" > <div class="two" >
<p>This is some paragraph.</p>
<p>And this is other paragraph.</p>
</div> </div>
and some action is targeted to the indented lines regardless of whether the cursor is on either <div class="one" >
or <div class="two" >
tag.
Bonus
It's also useful to create a text object to allow indented text blocks to be treated as text objects. For example, if the cursor is on the word some
in the first <p>
tag of the example given by Robert, you could use a custom ai
text object, which stands for “an indent”, to select the line where the cursor is on as well as the next one (both with the same indentation level) and re-indent them with >ai
. Read Indent text object at Vim Tips Wiki for more information.
Reference
<p>
tag lines, correct? At first I was thinking this must be super easy, but when I went to try it I realized I have no idea how to do it.<p>
tag lines :).>it
to do just that, but apparently it also indents the start<div>
tag.