,<>>,{,},!^F
" Needed for % to work when finding start/end of a tag.
setlocal matchpairs+=<:>
let b:undo_indent = "setlocal inde< indk<"
" b:hi_indent keeps state to speed up indenting consecutive lines.
let b:hi_indent = {"lnum": -1}
"""""" Code below this is loaded only once. """""
if exists("*HtmlIndent") && !exists('g:force_reload_html')
call HtmlIndent_CheckUserSettings()
finish
endif
" Allow for line continuation below.
let s:cpo_save = &cpo
set cpo-=C
"}}}
" Pattern to match the name of a tag, including custom elements.
let s:tagname = '\w\+\(-\w\+\)*'
" Check and process settings from b:html_indent and g:html_indent... variables.
" Prefer using buffer-local settings over global settings, so that there can
" be defaults for all HTML files and exceptions for specific types of HTML
" files.
func HtmlIndent_CheckUserSettings()
"{{{
let inctags = ''
if exists("b:html_indent_inctags")
let inctags = b:html_indent_inctags
elseif exists("g:html_indent_inctags")
let inctags = g:html_indent_inctags
endif
let b:hi_tags = {}
if len(inctags) > 0
call s:AddITags(b:hi_tags, split(inctags, ","))
endif
let autotags = ''
if exists("b:html_indent_autotags")
let autotags = b:html_indent_autotags
elseif exists("g:html_indent_autotags")
let autotags = g:html_indent_autotags
endif
let b:hi_removed_tags = {}
if len(autotags) > 0
call s:RemoveITags(b:hi_removed_tags, split(autotags, ","))
endif
" Syntax names indicating being inside a string of an attribute value.
let string_names = []
if exists("b:html_indent_string_names")
let string_names = b:html_indent_string_names
elseif exists("g:html_indent_string_names")
let string_names = g:html_indent_string_names
endif
let b:hi_insideStringNames = ['htmlString']
if len(string_names) > 0
for s in string_names
call add(b:hi_insideStringNames, s)
endfor
endif
" Syntax names indicating being inside a tag.
let tag_names = []
if exists("b:html_indent_tag_names")
let tag_names = b:html_indent_tag_names
elseif exists("g:html_indent_tag_names")
let tag_names = g:html_indent_tag_names
endif
let b:hi_insideTagNames = ['htmlTag', 'htmlScriptTag']
if len(tag_names) > 0
for s in tag_names
call add(b:hi_insideTagNames, s)
endfor
endif
let indone = {"zero": 0
\,"auto": "indent(prevnonblank(v:lnum-1))"
\,"inc": "b:hi_indent.blocktagind + shiftwidth()"}
let script1 = ''
if exists("b:html_indent_script1")
let script1 = b:html_indent_script1
elseif exists("g:html_indent_script1")
let script1 = g:html_indent_script1
endif
if len(script1) > 0
let b:hi_js1indent = get(indone, script1, indone.zero)
else
let b:hi_js1indent = 0
endif
let style1 = ''
if exists("b:html_indent_style1")
let style1 = b:html_indent_style1
elseif exists("g:html_indent_style1")
let style1 = g:html_indent_style1
endif
if len(style1) > 0
let b:hi_css1indent = get(indone, style1, indone.zero)
else
let b:hi_css1indent = 0
endif
if !exists('b:html_indent_line_limit')
if exists('g:html_indent_line_limit')
let b:html_indent_line_limit = g:html_indent_line_limit
else
let b:html_indent_line_limit = 200
endif
endif
if exists('b:html_indent_attribute')
let b:hi_attr_indent = b:html_indent_attribute
elseif exists('g:html_indent_attribute')
let b:hi_attr_indent = g:html_indent_attribute
else
let b:hi_attr_indent = 2
endif
endfunc "}}}
" Init Script Vars
"{{{
let b:hi_lasttick = 0
let b:hi_newstate = {}
let s:countonly = 0
"}}}
" Fill the s:indent_tags dict with known tags.
" The key is "tagname" or "/tagname". {{{
" The value is:
" 1 opening tag
" 2 "pre"
" 3 "script"
" 4 "style"
" 5 comment start
" 6 conditional comment start
" -1 closing tag
" -2 "/pre"
" -3 "/script"
" -4 "/style"
" -5 comment end
" -6 conditional comment end
let s:indent_tags = {}
let s:endtags = [0,0,0,0,0,0,0] " long enough for the highest index
"}}}
" Add a list of tag names for a pair of to "tags".
func s:AddITags(tags, taglist)
"{{{
for itag in a:taglist
let a:tags[itag] = 1
let a:tags['/' . itag] = -1
endfor
endfunc "}}}
" Take a list of tag name pairs that are not to be used as tag pairs.
func s:RemoveITags(tags, taglist)
"{{{
for itag in a:taglist
let a:tags[itag] = 1
let a:tags['/' . itag] = 1
endfor
endfunc "}}}
" Add a block tag, that is a tag with a different kind of indenting.
func s:AddBlockTag(tag, id, ...)
"{{{
if !(a:id >= 2 && a:id < len(s:endtags))
echoerr 'AddBlockTag ' . a:id
return
endif
let s:indent_tags[a:tag] = a:id
if a:0 == 0
let s:indent_tags['/' . a:tag] = -a:id
let s:endtags[a:id] = "" . a:tag . ">"
else
let s:indent_tags[a:1] = -a:id
let s:endtags[a:id] = a:1
endif
endfunc "}}}
" Add known tag pairs.
" Self-closing tags and tags that are sometimes {{{
" self-closing (e.g., ) are not here (when encountering
we can find
" the matching , but not the other way around).
" Known self-closing tags: " 'p', 'img', 'source', 'area', 'keygen', 'track',
" 'wbr'.
" Old HTML tags:
call s:AddITags(s:indent_tags, [
\ 'a', 'abbr', 'acronym', 'address', 'b', 'bdo', 'big',
\ 'blockquote', 'body', 'button', 'caption', 'center', 'cite', 'code',
\ 'colgroup', 'dd', 'del', 'dfn', 'dir', 'div', 'dl', 'dt', 'em', 'fieldset', 'font',
\ 'form', 'frameset', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'html',
\ 'i', 'iframe', 'ins', 'kbd', 'label', 'legend', 'li',
\ 'map', 'menu', 'noframes', 'noscript', 'object', 'ol',
\ 'optgroup', 'q', 's', 'samp', 'select', 'small', 'span', 'strong', 'sub',
\ 'sup', 'table', 'textarea', 'title', 'tt', 'u', 'ul', 'var', 'th', 'td',
\ 'tr', 'tbody', 'tfoot', 'thead'])
" New HTML5 elements:
call s:AddITags(s:indent_tags, [
\ 'article', 'aside', 'audio', 'bdi', 'canvas', 'command', 'data',
\ 'datalist', 'details', 'dialog', 'embed', 'figcaption', 'figure',
\ 'footer', 'header', 'hgroup', 'main', 'mark', 'meter', 'nav', 'output',
\ 'picture', 'progress', 'rp', 'rt', 'ruby', 'section', 'summary',
\ 'svg', 'time', 'video'])
" Tags added for web components:
call s:AddITags(s:indent_tags, [
\ 'content', 'shadow', 'template'])
"}}}
" Add Block Tags: these contain alien content
"{{{
call s:AddBlockTag('pre', 2)
call s:AddBlockTag('script', 3)
call s:AddBlockTag('style', 4)
call s:AddBlockTag('')
call s:AddBlockTag('')
"}}}
" Return non-zero when "tagname" is an opening tag, not being a block tag, for
" which there should be a closing tag. Can be used by scripts that include
" HTML indenting.
func HtmlIndent_IsOpenTag(tagname)
"{{{
if get(s:indent_tags, a:tagname) == 1
return 1
endif
return get(b:hi_tags, a:tagname) == 1
endfunc "}}}
" Get the value for "tagname", taking care of buffer-local tags.
func s:get_tag(tagname)
"{{{
let i = get(s:indent_tags, a:tagname)
if (i == 1 || i == -1) && get(b:hi_removed_tags, a:tagname) != 0
return 0
endif
if i == 0
let i = get(b:hi_tags, a:tagname)
endif
return i
endfunc "}}}
" Count the number of start and end tags in "text".
func s:CountITags(text)
"{{{
" Store the result in s:curind and s:nextrel.
let s:curind = 0 " relative indent steps for current line [unit &sw]:
let s:nextrel = 0 " relative indent steps for next line [unit &sw]:
let s:block = 0 " assume starting outside of a block
let s:countonly = 1 " don't change state
call substitute(a:text, '<\zs/\=' . s:tagname . '\>\|\|', '\=s:CheckTag(submatch(0))', 'g')
let s:countonly = 0
endfunc "}}}
" Count the number of start and end tags in text.
func s:CountTagsAndState(text)
"{{{
" Store the result in s:curind and s:nextrel. Update b:hi_newstate.block.
let s:curind = 0 " relative indent steps for current line [unit &sw]:
let s:nextrel = 0 " relative indent steps for next line [unit &sw]:
let s:block = b:hi_newstate.block
let tmp = substitute(a:text, '<\zs/\=' . s:tagname . '\>\|\|', '\=s:CheckTag(submatch(0))', 'g')
if s:block == 3
let b:hi_newstate.scripttype = s:GetScriptType(matchstr(tmp, '\C.*)
" if comment opener found,
" assume a:lnum within comment
" else
" assume usual html for a:lnum
" if a:lnum-1 has a closing comment
" look back to get indent of comment opener
" FI
" look back for a blocktag
let stopline2 = v:lnum + 1
if has_key(b:hi_indent, 'block') && b:hi_indent.block > 5
let [stopline2, stopcol2] = searchpos('', 'bpnW', stopline)
if found == 2 || found == 3
" comment opener found, assume a:lnum within comment
let state.block = (found == 3 ? 5 : 6)
let state.blocklnr = comlnum
" check preceding tags in the line:
call s:CountITags(tolower(getline(comlnum)[: comcol-2]))
if found == 2
let state.baseindent = b:hi_indent.baseindent
endif
let state.blocktagind = indent(comlnum) + (s:curind + s:nextrel) * shiftwidth()
return state
endif
" else within usual HTML
let text = tolower(getline(state.lnum))
" Check a:lnum-1 for closing comment (we need indent from the opening line).
" Not when other tags follow (might be --> inside a string).
let comcol = stridx(text, '-->')
if comcol >= 0 && match(text, '[<>]', comcol) <= 0
call cursor(state.lnum, comcol + 1)
let [comlnum, comcol] = searchpos('"
return state
endif
" Check if the previous line starts with end tag.
let swendtag = match(text, '^\s*') >= 0
" If previous line ended in a closing tag, line up with the opening tag.
if !swendtag && text =~ '' . s:tagname . '\s*>\s*$'
call cursor(state.lnum, 99999)
normal! F<
let start_lnum = HtmlIndent_FindStartTag()
if start_lnum > 0
let state.baseindent = indent(start_lnum)
if col('.') > 2
" check for tags before the matching opening tag.
let text = getline(start_lnum)
let swendtag = match(text, '^\s*') >= 0
call s:CountITags(text[: col('.') - 2])
let state.baseindent += s:nextrel * shiftwidth()
if !swendtag
let state.baseindent += s:curind * shiftwidth()
endif
endif
return state
endif
endif
" Else: no comments. Skip backwards to find the tag we're inside.
let [state.lnum, found] = HtmlIndent_FindTagStart(state.lnum)
" Check if that line starts with end tag.
let text = getline(state.lnum)
let swendtag = match(text, '^\s*') >= 0
call s:CountITags(tolower(text))
let state.baseindent = indent(state.lnum) + s:nextrel * shiftwidth()
if !swendtag
let state.baseindent += s:curind * shiftwidth()
endif
return state
endfunc "}}}
" Indent inside a
block: Keep indent as-is.
func s:Alien2()
"{{{
return -1
endfunc "}}}
" Return the indent inside a