xref: /vim-8.2.3635/runtime/indent/html.vim (revision 2346a637)
1ec7944aaSBram Moolenaar" Vim indent script for HTML
28bb1c3e5SBram Moolenaar" Maintainer:	Bram Moolenaar
38bb1c3e5SBram Moolenaar" Original Author: Andy Wokula <[email protected]>
4*2346a637SBram Moolenaar" Last Change:	2021 Jun 13
57ff78465SBram Moolenaar" Version:	1.0 "{{{
68bb1c3e5SBram Moolenaar" Description:	HTML indent script with cached state for faster indenting on a
7ec7944aaSBram Moolenaar"		range of lines.
88bb1c3e5SBram Moolenaar"		Supports template systems through hooks.
98bb1c3e5SBram Moolenaar"		Supports Closure stylesheets.
10ec7944aaSBram Moolenaar"
11ec7944aaSBram Moolenaar" Credits:
12ec7944aaSBram Moolenaar"	indent/html.vim (2006 Jun 05) from J. Zellner
13ec7944aaSBram Moolenaar"	indent/css.vim (2006 Dec 20) from N. Weibull
14ec7944aaSBram Moolenaar"
15ec7944aaSBram Moolenaar" History:
168bb1c3e5SBram Moolenaar" 2014 June	(v1.0) overhaul (Bram)
1752b91d80SBram Moolenaar" 2012 Oct 21	(v0.9) added support for shiftwidth()
1852b91d80SBram Moolenaar" 2011 Sep 09	(v0.8) added HTML5 tags (thx to J. Zuckerman)
1952b91d80SBram Moolenaar" 2008 Apr 28	(v0.6) revised customization
2052b91d80SBram Moolenaar" 2008 Mar 09	(v0.5) fixed 'indk' issue (thx to C.J. Robinson)
21ec7944aaSBram Moolenaar"}}}
22071d4279SBram Moolenaar
238bb1c3e5SBram Moolenaar" Init Folklore, check user settings (2nd time ++)
248bb1c3e5SBram Moolenaarif exists("b:did_indent") "{{{
25071d4279SBram Moolenaar  finish
26071d4279SBram Moolenaarendif
27690afe1fSBram Moolenaar
28690afe1fSBram Moolenaar" Load the Javascript indent script first, it defines GetJavascriptIndent().
29690afe1fSBram Moolenaar" Undo the rest.
30690afe1fSBram Moolenaar" Load base python indent.
31690afe1fSBram Moolenaarif !exists('*GetJavascriptIndent')
32690afe1fSBram Moolenaar  runtime! indent/javascript.vim
33690afe1fSBram Moolenaarendif
34071d4279SBram Moolenaarlet b:did_indent = 1
35071d4279SBram Moolenaar
36ec7944aaSBram Moolenaarsetlocal indentexpr=HtmlIndent()
37ec7944aaSBram Moolenaarsetlocal indentkeys=o,O,<Return>,<>>,{,},!^F
38071d4279SBram Moolenaar
398bb1c3e5SBram Moolenaar" Needed for % to work when finding start/end of a tag.
40946e27abSBram Moolenaarsetlocal matchpairs+=<:>
41946e27abSBram Moolenaar
42690afe1fSBram Moolenaarlet b:undo_indent = "setlocal inde< indk<"
43071d4279SBram Moolenaar
448bb1c3e5SBram Moolenaar" b:hi_indent keeps state to speed up indenting consecutive lines.
458bb1c3e5SBram Moolenaarlet b:hi_indent = {"lnum": -1}
468bb1c3e5SBram Moolenaar
478bb1c3e5SBram Moolenaar"""""" Code below this is loaded only once. """""
488bb1c3e5SBram Moolenaarif exists("*HtmlIndent") && !exists('g:force_reload_html')
49ec7944aaSBram Moolenaar  call HtmlIndent_CheckUserSettings()
50ec7944aaSBram Moolenaar  finish
51071d4279SBram Moolenaarendif
52071d4279SBram Moolenaar
538bb1c3e5SBram Moolenaar" Allow for line continuation below.
5491170f8aSBram Moolenaarlet s:cpo_save = &cpo
55071d4279SBram Moolenaarset cpo-=C
56ec7944aaSBram Moolenaar"}}}
57071d4279SBram Moolenaar
58b5b75624SBram Moolenaar" Pattern to match the name of a tag, including custom elements.
59b5b75624SBram Moolenaarlet s:tagname = '\w\+\(-\w\+\)*'
60b5b75624SBram Moolenaar
618bb1c3e5SBram Moolenaar" Check and process settings from b:html_indent and g:html_indent... variables.
628bb1c3e5SBram Moolenaar" Prefer using buffer-local settings over global settings, so that there can
638bb1c3e5SBram Moolenaar" be defaults for all HTML files and exceptions for specific types of HTML
648bb1c3e5SBram Moolenaar" files.
65*2346a637SBram Moolenaarfunc HtmlIndent_CheckUserSettings()
668bb1c3e5SBram Moolenaar  "{{{
678bb1c3e5SBram Moolenaar  let inctags = ''
688bb1c3e5SBram Moolenaar  if exists("b:html_indent_inctags")
698bb1c3e5SBram Moolenaar    let inctags = b:html_indent_inctags
708bb1c3e5SBram Moolenaar  elseif exists("g:html_indent_inctags")
718bb1c3e5SBram Moolenaar    let inctags = g:html_indent_inctags
72071d4279SBram Moolenaar  endif
738bb1c3e5SBram Moolenaar  let b:hi_tags = {}
748bb1c3e5SBram Moolenaar  if len(inctags) > 0
758bb1c3e5SBram Moolenaar    call s:AddITags(b:hi_tags, split(inctags, ","))
768bb1c3e5SBram Moolenaar  endif
778bb1c3e5SBram Moolenaar
788bb1c3e5SBram Moolenaar  let autotags = ''
798bb1c3e5SBram Moolenaar  if exists("b:html_indent_autotags")
808bb1c3e5SBram Moolenaar    let autotags = b:html_indent_autotags
818bb1c3e5SBram Moolenaar  elseif exists("g:html_indent_autotags")
828bb1c3e5SBram Moolenaar    let autotags = g:html_indent_autotags
838bb1c3e5SBram Moolenaar  endif
848bb1c3e5SBram Moolenaar  let b:hi_removed_tags = {}
85541f92d6SBram Moolenaar  if len(autotags) > 0
868bb1c3e5SBram Moolenaar    call s:RemoveITags(b:hi_removed_tags, split(autotags, ","))
878bb1c3e5SBram Moolenaar  endif
888bb1c3e5SBram Moolenaar
898bb1c3e5SBram Moolenaar  " Syntax names indicating being inside a string of an attribute value.
908bb1c3e5SBram Moolenaar  let string_names = []
918bb1c3e5SBram Moolenaar  if exists("b:html_indent_string_names")
928bb1c3e5SBram Moolenaar    let string_names = b:html_indent_string_names
938bb1c3e5SBram Moolenaar  elseif exists("g:html_indent_string_names")
948bb1c3e5SBram Moolenaar    let string_names = g:html_indent_string_names
958bb1c3e5SBram Moolenaar  endif
968bb1c3e5SBram Moolenaar  let b:hi_insideStringNames = ['htmlString']
978bb1c3e5SBram Moolenaar  if len(string_names) > 0
988bb1c3e5SBram Moolenaar    for s in string_names
998bb1c3e5SBram Moolenaar      call add(b:hi_insideStringNames, s)
1008bb1c3e5SBram Moolenaar    endfor
1018bb1c3e5SBram Moolenaar  endif
1028bb1c3e5SBram Moolenaar
1038bb1c3e5SBram Moolenaar  " Syntax names indicating being inside a tag.
1048bb1c3e5SBram Moolenaar  let tag_names = []
1058bb1c3e5SBram Moolenaar  if exists("b:html_indent_tag_names")
1068bb1c3e5SBram Moolenaar    let tag_names = b:html_indent_tag_names
1078bb1c3e5SBram Moolenaar  elseif exists("g:html_indent_tag_names")
1088bb1c3e5SBram Moolenaar    let tag_names = g:html_indent_tag_names
1098bb1c3e5SBram Moolenaar  endif
1108bb1c3e5SBram Moolenaar  let b:hi_insideTagNames = ['htmlTag', 'htmlScriptTag']
1118bb1c3e5SBram Moolenaar  if len(tag_names) > 0
1128bb1c3e5SBram Moolenaar    for s in tag_names
1138bb1c3e5SBram Moolenaar      call add(b:hi_insideTagNames, s)
1148bb1c3e5SBram Moolenaar    endfor
115071d4279SBram Moolenaar  endif
116071d4279SBram Moolenaar
117ec7944aaSBram Moolenaar  let indone = {"zero": 0
118ec7944aaSBram Moolenaar              \,"auto": "indent(prevnonblank(v:lnum-1))"
1193ec574f2SBram Moolenaar              \,"inc": "b:hi_indent.blocktagind + shiftwidth()"}
1208bb1c3e5SBram Moolenaar
1218bb1c3e5SBram Moolenaar  let script1 = ''
1228bb1c3e5SBram Moolenaar  if exists("b:html_indent_script1")
1238bb1c3e5SBram Moolenaar    let script1 = b:html_indent_script1
1248bb1c3e5SBram Moolenaar  elseif exists("g:html_indent_script1")
1258bb1c3e5SBram Moolenaar    let script1 = g:html_indent_script1
12691170f8aSBram Moolenaar  endif
1278bb1c3e5SBram Moolenaar  if len(script1) > 0
1288bb1c3e5SBram Moolenaar    let b:hi_js1indent = get(indone, script1, indone.zero)
1298bb1c3e5SBram Moolenaar  else
1308bb1c3e5SBram Moolenaar    let b:hi_js1indent = 0
1318bb1c3e5SBram Moolenaar  endif
1328bb1c3e5SBram Moolenaar
1338bb1c3e5SBram Moolenaar  let style1 = ''
1348bb1c3e5SBram Moolenaar  if exists("b:html_indent_style1")
1358bb1c3e5SBram Moolenaar    let style1 = b:html_indent_style1
1368bb1c3e5SBram Moolenaar  elseif exists("g:html_indent_style1")
1378bb1c3e5SBram Moolenaar    let style1 = g:html_indent_style1
1388bb1c3e5SBram Moolenaar  endif
1398bb1c3e5SBram Moolenaar  if len(style1) > 0
1408bb1c3e5SBram Moolenaar    let b:hi_css1indent = get(indone, style1, indone.zero)
1418bb1c3e5SBram Moolenaar  else
1428bb1c3e5SBram Moolenaar    let b:hi_css1indent = 0
1438bb1c3e5SBram Moolenaar  endif
1448bb1c3e5SBram Moolenaar
1458bb1c3e5SBram Moolenaar  if !exists('b:html_indent_line_limit')
1468bb1c3e5SBram Moolenaar    if exists('g:html_indent_line_limit')
1478bb1c3e5SBram Moolenaar      let b:html_indent_line_limit = g:html_indent_line_limit
1488bb1c3e5SBram Moolenaar    else
1498bb1c3e5SBram Moolenaar      let b:html_indent_line_limit = 200
1508bb1c3e5SBram Moolenaar    endif
151ec7944aaSBram Moolenaar  endif
152ec7944aaSBram Moolenaarendfunc "}}}
153ec7944aaSBram Moolenaar
1548bb1c3e5SBram Moolenaar" Init Script Vars
1558bb1c3e5SBram Moolenaar"{{{
1568bb1c3e5SBram Moolenaarlet b:hi_lasttick = 0
1578bb1c3e5SBram Moolenaarlet b:hi_newstate = {}
158ec7944aaSBram Moolenaarlet s:countonly = 0
159ec7944aaSBram Moolenaar "}}}
1608bb1c3e5SBram Moolenaar
1618bb1c3e5SBram Moolenaar" Fill the s:indent_tags dict with known tags.
1628bb1c3e5SBram Moolenaar" The key is "tagname" or "/tagname".  {{{
1638bb1c3e5SBram Moolenaar" The value is:
1648bb1c3e5SBram Moolenaar" 1   opening tag
1658bb1c3e5SBram Moolenaar" 2   "pre"
1668bb1c3e5SBram Moolenaar" 3   "script"
1678bb1c3e5SBram Moolenaar" 4   "style"
1688bb1c3e5SBram Moolenaar" 5   comment start
169ca63501fSBram Moolenaar" 6   conditional comment start
1708bb1c3e5SBram Moolenaar" -1  closing tag
1718bb1c3e5SBram Moolenaar" -2  "/pre"
1728bb1c3e5SBram Moolenaar" -3  "/script"
1738bb1c3e5SBram Moolenaar" -4  "/style"
1748bb1c3e5SBram Moolenaar" -5  comment end
175ca63501fSBram Moolenaar" -6  conditional comment end
1768bb1c3e5SBram Moolenaarlet s:indent_tags = {}
177ca63501fSBram Moolenaarlet s:endtags = [0,0,0,0,0,0,0]   " long enough for the highest index
1788bb1c3e5SBram Moolenaar"}}}
1798bb1c3e5SBram Moolenaar
1808bb1c3e5SBram Moolenaar" Add a list of tag names for a pair of <tag> </tag> to "tags".
181*2346a637SBram Moolenaarfunc s:AddITags(tags, taglist)
1828bb1c3e5SBram Moolenaar  "{{{
183ec7944aaSBram Moolenaar  for itag in a:taglist
1848bb1c3e5SBram Moolenaar    let a:tags[itag] = 1
1858bb1c3e5SBram Moolenaar    let a:tags['/' . itag] = -1
186ec7944aaSBram Moolenaar  endfor
187ec7944aaSBram Moolenaarendfunc "}}}
1888bb1c3e5SBram Moolenaar
1898bb1c3e5SBram Moolenaar" Take a list of tag name pairs that are not to be used as tag pairs.
190*2346a637SBram Moolenaarfunc s:RemoveITags(tags, taglist)
1918bb1c3e5SBram Moolenaar  "{{{
1928bb1c3e5SBram Moolenaar  for itag in a:taglist
1938bb1c3e5SBram Moolenaar    let a:tags[itag] = 1
1948bb1c3e5SBram Moolenaar    let a:tags['/' . itag] = 1
1958bb1c3e5SBram Moolenaar  endfor
1968bb1c3e5SBram Moolenaarendfunc "}}}
1978bb1c3e5SBram Moolenaar
1988bb1c3e5SBram Moolenaar" Add a block tag, that is a tag with a different kind of indenting.
199*2346a637SBram Moolenaarfunc s:AddBlockTag(tag, id, ...)
2008bb1c3e5SBram Moolenaar  "{{{
2018bb1c3e5SBram Moolenaar  if !(a:id >= 2 && a:id < len(s:endtags))
2028bb1c3e5SBram Moolenaar    echoerr 'AddBlockTag ' . a:id
203ec7944aaSBram Moolenaar    return
204ec7944aaSBram Moolenaar  endif
205ec7944aaSBram Moolenaar  let s:indent_tags[a:tag] = a:id
206ec7944aaSBram Moolenaar  if a:0 == 0
207ec7944aaSBram Moolenaar    let s:indent_tags['/' . a:tag] = -a:id
2088bb1c3e5SBram Moolenaar    let s:endtags[a:id] = "</" . a:tag . ">"
209ec7944aaSBram Moolenaar  else
210ec7944aaSBram Moolenaar    let s:indent_tags[a:1] = -a:id
2118bb1c3e5SBram Moolenaar    let s:endtags[a:id] = a:1
212ec7944aaSBram Moolenaar  endif
213ec7944aaSBram Moolenaarendfunc "}}}
214ec7944aaSBram Moolenaar
2158bb1c3e5SBram Moolenaar" Add known tag pairs.
2168bb1c3e5SBram Moolenaar" Self-closing tags and tags that are sometimes {{{
2178bb1c3e5SBram Moolenaar" self-closing (e.g., <p>) are not here (when encountering </p> we can find
218d47d5223SBram Moolenaar" the matching <p>, but not the other way around).
219d47d5223SBram Moolenaar" Known self-closing tags: " 'p', 'img', 'source', 'area', 'keygen', 'track',
220d47d5223SBram Moolenaar" 'wbr'.
2218bb1c3e5SBram Moolenaar" Old HTML tags:
2228bb1c3e5SBram Moolenaarcall s:AddITags(s:indent_tags, [
2238bb1c3e5SBram Moolenaar    \ 'a', 'abbr', 'acronym', 'address', 'b', 'bdo', 'big',
2248bb1c3e5SBram Moolenaar    \ 'blockquote', 'body', 'button', 'caption', 'center', 'cite', 'code',
22573fef330SBram Moolenaar    \ 'colgroup', 'dd', 'del', 'dfn', 'dir', 'div', 'dl', 'dt', 'em', 'fieldset', 'font',
2268bb1c3e5SBram Moolenaar    \ 'form', 'frameset', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'html',
2278bb1c3e5SBram Moolenaar    \ 'i', 'iframe', 'ins', 'kbd', 'label', 'legend', 'li',
2288bb1c3e5SBram Moolenaar    \ 'map', 'menu', 'noframes', 'noscript', 'object', 'ol',
229ec7944aaSBram Moolenaar    \ 'optgroup', 'q', 's', 'samp', 'select', 'small', 'span', 'strong', 'sub',
230ec7944aaSBram Moolenaar    \ 'sup', 'table', 'textarea', 'title', 'tt', 'u', 'ul', 'var', 'th', 'td',
2318bb1c3e5SBram Moolenaar    \ 'tr', 'tbody', 'tfoot', 'thead'])
232ec7944aaSBram Moolenaar
233939a1abeSBram Moolenaar" New HTML5 elements:
2348bb1c3e5SBram Moolenaarcall s:AddITags(s:indent_tags, [
235d47d5223SBram Moolenaar    \ 'article', 'aside', 'audio', 'bdi', 'canvas', 'command', 'data',
236d47d5223SBram Moolenaar    \ 'datalist', 'details', 'dialog', 'embed', 'figcaption', 'figure',
237d47d5223SBram Moolenaar    \ 'footer', 'header', 'hgroup', 'main', 'mark', 'meter', 'nav', 'output',
238d47d5223SBram Moolenaar    \ 'picture', 'progress', 'rp', 'rt', 'ruby', 'section', 'summary',
239d47d5223SBram Moolenaar    \ 'svg', 'time', 'video'])
240d8b77f7dSBram Moolenaar
241d8b77f7dSBram Moolenaar" Tags added for web components:
242d8b77f7dSBram Moolenaarcall s:AddITags(s:indent_tags, [
243d8b77f7dSBram Moolenaar    \ 'content', 'shadow', 'template'])
244ec7944aaSBram Moolenaar"}}}
2458bb1c3e5SBram Moolenaar
2468bb1c3e5SBram Moolenaar" Add Block Tags: these contain alien content
2478bb1c3e5SBram Moolenaar"{{{
248ec7944aaSBram Moolenaarcall s:AddBlockTag('pre', 2)
249ec7944aaSBram Moolenaarcall s:AddBlockTag('script', 3)
250ec7944aaSBram Moolenaarcall s:AddBlockTag('style', 4)
251ec7944aaSBram Moolenaarcall s:AddBlockTag('<!--', 5, '-->')
252ca63501fSBram Moolenaarcall s:AddBlockTag('<!--[', 6, '![endif]-->')
253ec7944aaSBram Moolenaar"}}}
254ec7944aaSBram Moolenaar
2558bb1c3e5SBram Moolenaar" Return non-zero when "tagname" is an opening tag, not being a block tag, for
2568bb1c3e5SBram Moolenaar" which there should be a closing tag.  Can be used by scripts that include
2578bb1c3e5SBram Moolenaar" HTML indenting.
258*2346a637SBram Moolenaarfunc HtmlIndent_IsOpenTag(tagname)
2598bb1c3e5SBram Moolenaar  "{{{
2608bb1c3e5SBram Moolenaar  if get(s:indent_tags, a:tagname) == 1
2618bb1c3e5SBram Moolenaar    return 1
262ec7944aaSBram Moolenaar  endif
2638bb1c3e5SBram Moolenaar  return get(b:hi_tags, a:tagname) == 1
2648bb1c3e5SBram Moolenaarendfunc "}}}
2658bb1c3e5SBram Moolenaar
2668bb1c3e5SBram Moolenaar" Get the value for "tagname", taking care of buffer-local tags.
267*2346a637SBram Moolenaarfunc s:get_tag(tagname)
2688bb1c3e5SBram Moolenaar  "{{{
2698bb1c3e5SBram Moolenaar  let i = get(s:indent_tags, a:tagname)
2708bb1c3e5SBram Moolenaar  if (i == 1 || i == -1) && get(b:hi_removed_tags, a:tagname) != 0
2718bb1c3e5SBram Moolenaar    return 0
2728bb1c3e5SBram Moolenaar  endif
2738bb1c3e5SBram Moolenaar  if i == 0
2748bb1c3e5SBram Moolenaar    let i = get(b:hi_tags, a:tagname)
2758bb1c3e5SBram Moolenaar  endif
2768bb1c3e5SBram Moolenaar  return i
2778bb1c3e5SBram Moolenaarendfunc "}}}
2788bb1c3e5SBram Moolenaar
2798bb1c3e5SBram Moolenaar" Count the number of start and end tags in "text".
280*2346a637SBram Moolenaarfunc s:CountITags(text)
2818bb1c3e5SBram Moolenaar  "{{{
2828bb1c3e5SBram Moolenaar  " Store the result in s:curind and s:nextrel.
2838bb1c3e5SBram Moolenaar  let s:curind = 0  " relative indent steps for current line [unit &sw]:
2848bb1c3e5SBram Moolenaar  let s:nextrel = 0  " relative indent steps for next line [unit &sw]:
285ec7944aaSBram Moolenaar  let s:block = 0		" assume starting outside of a block
286ec7944aaSBram Moolenaar  let s:countonly = 1	" don't change state
287b5b75624SBram Moolenaar  call substitute(a:text, '<\zs/\=' . s:tagname . '\>\|<!--\[\|\[endif\]-->\|<!--\|-->', '\=s:CheckTag(submatch(0))', 'g')
288ec7944aaSBram Moolenaar  let s:countonly = 0
289ec7944aaSBram Moolenaarendfunc "}}}
2908bb1c3e5SBram Moolenaar
2918bb1c3e5SBram Moolenaar" Count the number of start and end tags in text.
292*2346a637SBram Moolenaarfunc s:CountTagsAndState(text)
2938bb1c3e5SBram Moolenaar  "{{{
2948bb1c3e5SBram Moolenaar  " Store the result in s:curind and s:nextrel.  Update b:hi_newstate.block.
2958bb1c3e5SBram Moolenaar  let s:curind = 0  " relative indent steps for current line [unit &sw]:
2968bb1c3e5SBram Moolenaar  let s:nextrel = 0  " relative indent steps for next line [unit &sw]:
2978bb1c3e5SBram Moolenaar
2988bb1c3e5SBram Moolenaar  let s:block = b:hi_newstate.block
299b5b75624SBram Moolenaar  let tmp = substitute(a:text, '<\zs/\=' . s:tagname . '\>\|<!--\[\|\[endif\]-->\|<!--\|-->', '\=s:CheckTag(submatch(0))', 'g')
3008bb1c3e5SBram Moolenaar  if s:block == 3
3018bb1c3e5SBram Moolenaar    let b:hi_newstate.scripttype = s:GetScriptType(matchstr(tmp, '\C.*<SCRIPT\>\zs[^>]*'))
3028bb1c3e5SBram Moolenaar  endif
3038bb1c3e5SBram Moolenaar  let b:hi_newstate.block = s:block
3048bb1c3e5SBram Moolenaarendfunc "}}}
3058bb1c3e5SBram Moolenaar
3068bb1c3e5SBram Moolenaar" Used by s:CountITags() and s:CountTagsAndState().
307*2346a637SBram Moolenaarfunc s:CheckTag(itag)
3088bb1c3e5SBram Moolenaar  "{{{
3098bb1c3e5SBram Moolenaar  " Returns an empty string or "SCRIPT".
3108bb1c3e5SBram Moolenaar  " a:itag can be "tag" or "/tag" or "<!--" or "-->"
311d8b77f7dSBram Moolenaar  if (s:CheckCustomTag(a:itag))
312d8b77f7dSBram Moolenaar    return ""
313d8b77f7dSBram Moolenaar  endif
3148bb1c3e5SBram Moolenaar  let ind = s:get_tag(a:itag)
315ec7944aaSBram Moolenaar  if ind == -1
316ec7944aaSBram Moolenaar    " closing tag
317ec7944aaSBram Moolenaar    if s:block != 0
318ec7944aaSBram Moolenaar      " ignore itag within a block
3198bb1c3e5SBram Moolenaar      return ""
320ec7944aaSBram Moolenaar    endif
321ec7944aaSBram Moolenaar    if s:nextrel == 0
322ec7944aaSBram Moolenaar      let s:curind -= 1
323ec7944aaSBram Moolenaar    else
324ec7944aaSBram Moolenaar      let s:nextrel -= 1
325ec7944aaSBram Moolenaar    endif
326ec7944aaSBram Moolenaar  elseif ind == 1
327ec7944aaSBram Moolenaar    " opening tag
328ec7944aaSBram Moolenaar    if s:block != 0
3298bb1c3e5SBram Moolenaar      return ""
330ec7944aaSBram Moolenaar    endif
331ec7944aaSBram Moolenaar    let s:nextrel += 1
332ec7944aaSBram Moolenaar  elseif ind != 0
333ec7944aaSBram Moolenaar    " block-tag (opening or closing)
3348bb1c3e5SBram Moolenaar    return s:CheckBlockTag(a:itag, ind)
335ec7944aaSBram Moolenaar  " else ind==0 (other tag found): keep indent
3368bb1c3e5SBram Moolenaar  endif
3378bb1c3e5SBram Moolenaar  return ""
338ec7944aaSBram Moolenaarendfunc "}}}
3398bb1c3e5SBram Moolenaar
3408bb1c3e5SBram Moolenaar" Used by s:CheckTag(). Returns an empty string or "SCRIPT".
341*2346a637SBram Moolenaarfunc s:CheckBlockTag(blocktag, ind)
3428bb1c3e5SBram Moolenaar  "{{{
343ec7944aaSBram Moolenaar  if a:ind > 0
344ec7944aaSBram Moolenaar    " a block starts here
345ec7944aaSBram Moolenaar    if s:block != 0
346ec7944aaSBram Moolenaar      " already in a block (nesting) - ignore
347ec7944aaSBram Moolenaar      " especially ignore comments after other blocktags
3488bb1c3e5SBram Moolenaar      return ""
349ec7944aaSBram Moolenaar    endif
350ec7944aaSBram Moolenaar    let s:block = a:ind		" block type
351ec7944aaSBram Moolenaar    if s:countonly
3528bb1c3e5SBram Moolenaar      return ""
353ec7944aaSBram Moolenaar    endif
3548bb1c3e5SBram Moolenaar    let b:hi_newstate.blocklnr = v:lnum
355ec7944aaSBram Moolenaar    " save allover indent for the endtag
3563ec574f2SBram Moolenaar    let b:hi_newstate.blocktagind = b:hi_indent.baseindent + (s:nextrel + s:curind) * shiftwidth()
357ec7944aaSBram Moolenaar    if a:ind == 3
358ec7944aaSBram Moolenaar      return "SCRIPT"    " all except this must be lowercase
359ec7944aaSBram Moolenaar      " line is to be checked again for the type attribute
360ec7944aaSBram Moolenaar    endif
361ec7944aaSBram Moolenaar  else
362ec7944aaSBram Moolenaar    let s:block = 0
3638bb1c3e5SBram Moolenaar    " we get here if starting and closing a block-tag on the same line
364ec7944aaSBram Moolenaar  endif
3658bb1c3e5SBram Moolenaar  return ""
366ec7944aaSBram Moolenaarendfunc "}}}
3678bb1c3e5SBram Moolenaar
368d8b77f7dSBram Moolenaar" Used by s:CheckTag().
369*2346a637SBram Moolenaarfunc s:CheckCustomTag(ctag)
370d8b77f7dSBram Moolenaar  "{{{
371d8b77f7dSBram Moolenaar  " Returns 1 if ctag is the tag for a custom element, 0 otherwise.
372d8b77f7dSBram Moolenaar  " a:ctag can be "tag" or "/tag" or "<!--" or "-->"
373d8b77f7dSBram Moolenaar  let pattern = '\%\(\w\+-\)\+\w\+'
374d8b77f7dSBram Moolenaar  if match(a:ctag, pattern) == -1
375d8b77f7dSBram Moolenaar    return 0
376d8b77f7dSBram Moolenaar  endif
377d8b77f7dSBram Moolenaar  if matchstr(a:ctag, '\/\ze.\+') == "/"
378d8b77f7dSBram Moolenaar    " closing tag
379d8b77f7dSBram Moolenaar    if s:block != 0
380d8b77f7dSBram Moolenaar      " ignore ctag within a block
381d8b77f7dSBram Moolenaar      return 1
382d8b77f7dSBram Moolenaar    endif
383d8b77f7dSBram Moolenaar    if s:nextrel == 0
384d8b77f7dSBram Moolenaar      let s:curind -= 1
385d8b77f7dSBram Moolenaar    else
386d8b77f7dSBram Moolenaar      let s:nextrel -= 1
387d8b77f7dSBram Moolenaar    endif
388d8b77f7dSBram Moolenaar  else
389d8b77f7dSBram Moolenaar    " opening tag
390d8b77f7dSBram Moolenaar    if s:block != 0
391d8b77f7dSBram Moolenaar      return 1
392d8b77f7dSBram Moolenaar    endif
393d8b77f7dSBram Moolenaar    let s:nextrel += 1
394d8b77f7dSBram Moolenaar  endif
395d8b77f7dSBram Moolenaar  return 1
396d8b77f7dSBram Moolenaarendfunc "}}}
397d8b77f7dSBram Moolenaar
3988bb1c3e5SBram Moolenaar" Return the <script> type: either "javascript" or ""
399*2346a637SBram Moolenaarfunc s:GetScriptType(str)
4008bb1c3e5SBram Moolenaar  "{{{
401ec7944aaSBram Moolenaar  if a:str == "" || a:str =~ "java"
402ec7944aaSBram Moolenaar    return "javascript"
403ec7944aaSBram Moolenaar  else
404ec7944aaSBram Moolenaar    return ""
405ec7944aaSBram Moolenaar  endif
406ec7944aaSBram Moolenaarendfunc "}}}
407ec7944aaSBram Moolenaar
4088bb1c3e5SBram Moolenaar" Look back in the file, starting at a:lnum - 1, to compute a state for the
4098bb1c3e5SBram Moolenaar" start of line a:lnum.  Return the new state.
410*2346a637SBram Moolenaarfunc s:FreshState(lnum)
4118bb1c3e5SBram Moolenaar  "{{{
4128bb1c3e5SBram Moolenaar  " A state is to know ALL relevant details about the
4138bb1c3e5SBram Moolenaar  " lines 1..a:lnum-1, initial calculating (here!) can be slow, but updating is
414ec7944aaSBram Moolenaar  " fast (incremental).
4158bb1c3e5SBram Moolenaar  " TODO: this should be split up in detecting the block type and computing the
4168bb1c3e5SBram Moolenaar  " indent for the block type, so that when we do not know the indent we do
4178bb1c3e5SBram Moolenaar  " not need to clear the whole state and re-detect the block type again.
418ec7944aaSBram Moolenaar  " State:
419ec7944aaSBram Moolenaar  "	lnum		last indented line == prevnonblank(a:lnum - 1)
420ec7944aaSBram Moolenaar  "	block = 0	a:lnum located within special tag: 0:none, 2:<pre>,
421ca63501fSBram Moolenaar  "			3:<script>, 4:<style>, 5:<!--, 6:<!--[
422ec7944aaSBram Moolenaar  "	baseindent	use this indent for line a:lnum as a start - kind of
423ec7944aaSBram Moolenaar  "			autoindent (if block==0)
424ec7944aaSBram Moolenaar  "	scripttype = ''	type attribute of a script tag (if block==3)
425ec7944aaSBram Moolenaar  "	blocktagind	indent for current opening (get) and closing (set)
426ec7944aaSBram Moolenaar  "			blocktag (if block!=0)
427ec7944aaSBram Moolenaar  "	blocklnr	lnum of starting blocktag (if block!=0)
428ec7944aaSBram Moolenaar  "	inattr		line {lnum} starts with attributes of a tag
429ec7944aaSBram Moolenaar  let state = {}
430ec7944aaSBram Moolenaar  let state.lnum = prevnonblank(a:lnum - 1)
431ec7944aaSBram Moolenaar  let state.scripttype = ""
432ec7944aaSBram Moolenaar  let state.blocktagind = -1
433ec7944aaSBram Moolenaar  let state.block = 0
434ec7944aaSBram Moolenaar  let state.baseindent = 0
435ec7944aaSBram Moolenaar  let state.blocklnr = 0
436ec7944aaSBram Moolenaar  let state.inattr = 0
437ec7944aaSBram Moolenaar
438ec7944aaSBram Moolenaar  if state.lnum == 0
439ec7944aaSBram Moolenaar    return state
440ec7944aaSBram Moolenaar  endif
441ec7944aaSBram Moolenaar
442ec7944aaSBram Moolenaar  " Heuristic:
443ec7944aaSBram Moolenaar  " remember startline state.lnum
444ec7944aaSBram Moolenaar  " look back for <pre, </pre, <script, </script, <style, </style tags
445ec7944aaSBram Moolenaar  " remember stopline
446ec7944aaSBram Moolenaar  " if opening tag found,
447ec7944aaSBram Moolenaar  "	assume a:lnum within block
448ec7944aaSBram Moolenaar  " else
449ec7944aaSBram Moolenaar  "	look back in result range (stopline, startline) for comment
450ec7944aaSBram Moolenaar  "	    \ delimiters (<!--, -->)
451ec7944aaSBram Moolenaar  "	if comment opener found,
452ec7944aaSBram Moolenaar  "	    assume a:lnum within comment
453ec7944aaSBram Moolenaar  "	else
454ec7944aaSBram Moolenaar  "	    assume usual html for a:lnum
455ec7944aaSBram Moolenaar  "	    if a:lnum-1 has a closing comment
456ec7944aaSBram Moolenaar  "		look back to get indent of comment opener
457ec7944aaSBram Moolenaar  " FI
458ec7944aaSBram Moolenaar
4598bb1c3e5SBram Moolenaar  " look back for a blocktag
460ca63501fSBram Moolenaar  let stopline2 = v:lnum + 1
461ca63501fSBram Moolenaar  if has_key(b:hi_indent, 'block') && b:hi_indent.block > 5
462ca63501fSBram Moolenaar    let [stopline2, stopcol2] = searchpos('<!--', 'bnW')
463ca63501fSBram Moolenaar  endif
464ca63501fSBram Moolenaar  let [stopline, stopcol] = searchpos('\c<\zs\/\=\%(pre\>\|script\>\|style\>\)', "bnW")
465ca63501fSBram Moolenaar  if stopline > 0 && stopline < stopline2
466ca63501fSBram Moolenaar    " ugly ... why isn't there searchstr()
467ec7944aaSBram Moolenaar    let tagline = tolower(getline(stopline))
468ec7944aaSBram Moolenaar    let blocktag = matchstr(tagline, '\/\=\%(pre\>\|script\>\|style\>\)', stopcol - 1)
4698bb1c3e5SBram Moolenaar    if blocktag[0] != "/"
470ec7944aaSBram Moolenaar      " opening tag found, assume a:lnum within block
471ec7944aaSBram Moolenaar      let state.block = s:indent_tags[blocktag]
472ec7944aaSBram Moolenaar      if state.block == 3
473ec7944aaSBram Moolenaar        let state.scripttype = s:GetScriptType(matchstr(tagline, '\>[^>]*', stopcol))
474ec7944aaSBram Moolenaar      endif
475ec7944aaSBram Moolenaar      let state.blocklnr = stopline
476ec7944aaSBram Moolenaar      " check preceding tags in the line:
4778bb1c3e5SBram Moolenaar      call s:CountITags(tagline[: stopcol-2])
4783ec574f2SBram Moolenaar      let state.blocktagind = indent(stopline) + (s:curind + s:nextrel) * shiftwidth()
479ec7944aaSBram Moolenaar      return state
480ec7944aaSBram Moolenaar    elseif stopline == state.lnum
481ec7944aaSBram Moolenaar      " handle special case: previous line (= state.lnum) contains a
482ec7944aaSBram Moolenaar      " closing blocktag which is preceded by line-noise;
483ec7944aaSBram Moolenaar      " blocktag == "/..."
484ec7944aaSBram Moolenaar      let swendtag = match(tagline, '^\s*</') >= 0
485ec7944aaSBram Moolenaar      if !swendtag
486ca63501fSBram Moolenaar        let [bline, bcol] = searchpos('<'.blocktag[1:].'\>', "bnW")
4878bb1c3e5SBram Moolenaar        call s:CountITags(tolower(getline(bline)[: bcol-2]))
4883ec574f2SBram Moolenaar        let state.baseindent = indent(bline) + (s:curind + s:nextrel) * shiftwidth()
489ec7944aaSBram Moolenaar        return state
490ec7944aaSBram Moolenaar      endif
491ec7944aaSBram Moolenaar    endif
4928bb1c3e5SBram Moolenaar  endif
493ca63501fSBram Moolenaar  if stopline > stopline2
494ca63501fSBram Moolenaar    let stopline = stopline2
495ca63501fSBram Moolenaar    let stopcol = stopcol2
496ca63501fSBram Moolenaar  endif
497ec7944aaSBram Moolenaar
498ec7944aaSBram Moolenaar  " else look back for comment
499ca63501fSBram Moolenaar  let [comlnum, comcol, found] = searchpos('\(<!--\[\)\|\(<!--\)\|-->', 'bpnW', stopline)
500ca63501fSBram Moolenaar  if found == 2 || found == 3
501ec7944aaSBram Moolenaar    " comment opener found, assume a:lnum within comment
502ca63501fSBram Moolenaar    let state.block = (found == 3 ? 5 : 6)
5038bb1c3e5SBram Moolenaar    let state.blocklnr = comlnum
504ec7944aaSBram Moolenaar    " check preceding tags in the line:
5058bb1c3e5SBram Moolenaar    call s:CountITags(tolower(getline(comlnum)[: comcol-2]))
506ca63501fSBram Moolenaar    if found == 2
507ca63501fSBram Moolenaar      let state.baseindent = b:hi_indent.baseindent
508ca63501fSBram Moolenaar    endif
5093ec574f2SBram Moolenaar    let state.blocktagind = indent(comlnum) + (s:curind + s:nextrel) * shiftwidth()
510ec7944aaSBram Moolenaar    return state
511ec7944aaSBram Moolenaar  endif
512ec7944aaSBram Moolenaar
5138bb1c3e5SBram Moolenaar  " else within usual HTML
5148bb1c3e5SBram Moolenaar  let text = tolower(getline(state.lnum))
515946e27abSBram Moolenaar
5168bb1c3e5SBram Moolenaar  " Check a:lnum-1 for closing comment (we need indent from the opening line).
5178bb1c3e5SBram Moolenaar  " Not when other tags follow (might be --> inside a string).
5188bb1c3e5SBram Moolenaar  let comcol = stridx(text, '-->')
5198bb1c3e5SBram Moolenaar  if comcol >= 0 && match(text, '[<>]', comcol) <= 0
520ec7944aaSBram Moolenaar    call cursor(state.lnum, comcol + 1)
5218bb1c3e5SBram Moolenaar    let [comlnum, comcol] = searchpos('<!--', 'bW')
5228bb1c3e5SBram Moolenaar    if comlnum == state.lnum
5238bb1c3e5SBram Moolenaar      let text = text[: comcol-2]
524ec7944aaSBram Moolenaar    else
5258bb1c3e5SBram Moolenaar      let text = tolower(getline(comlnum)[: comcol-2])
526ec7944aaSBram Moolenaar    endif
5278bb1c3e5SBram Moolenaar    call s:CountITags(text)
5283ec574f2SBram Moolenaar    let state.baseindent = indent(comlnum) + (s:curind + s:nextrel) * shiftwidth()
529ec7944aaSBram Moolenaar    " TODO check tags that follow "-->"
5308bb1c3e5SBram Moolenaar    return state
531ec7944aaSBram Moolenaar  endif
532ec7944aaSBram Moolenaar
533946e27abSBram Moolenaar  " Check if the previous line starts with end tag.
5348bb1c3e5SBram Moolenaar  let swendtag = match(text, '^\s*</') >= 0
535946e27abSBram Moolenaar
536946e27abSBram Moolenaar  " If previous line ended in a closing tag, line up with the opening tag.
537b5b75624SBram Moolenaar  if !swendtag && text =~ '</' . s:tagname . '\s*>\s*$'
538946e27abSBram Moolenaar    call cursor(state.lnum, 99999)
5397b61a546SBram Moolenaar    normal! F<
5408bb1c3e5SBram Moolenaar    let start_lnum = HtmlIndent_FindStartTag()
5418bb1c3e5SBram Moolenaar    if start_lnum > 0
5428bb1c3e5SBram Moolenaar      let state.baseindent = indent(start_lnum)
5438bb1c3e5SBram Moolenaar      if col('.') > 2
5448bb1c3e5SBram Moolenaar        " check for tags before the matching opening tag.
5458bb1c3e5SBram Moolenaar        let text = getline(start_lnum)
5468bb1c3e5SBram Moolenaar        let swendtag = match(text, '^\s*</') >= 0
5478bb1c3e5SBram Moolenaar        call s:CountITags(text[: col('.') - 2])
5483ec574f2SBram Moolenaar        let state.baseindent += s:nextrel * shiftwidth()
5498bb1c3e5SBram Moolenaar        if !swendtag
5503ec574f2SBram Moolenaar          let state.baseindent += s:curind * shiftwidth()
5518bb1c3e5SBram Moolenaar        endif
5528bb1c3e5SBram Moolenaar      endif
553946e27abSBram Moolenaar      return state
554946e27abSBram Moolenaar    endif
555946e27abSBram Moolenaar  endif
556946e27abSBram Moolenaar
5578bb1c3e5SBram Moolenaar  " Else: no comments. Skip backwards to find the tag we're inside.
5588bb1c3e5SBram Moolenaar  let [state.lnum, found] = HtmlIndent_FindTagStart(state.lnum)
5598bb1c3e5SBram Moolenaar  " Check if that line starts with end tag.
5608bb1c3e5SBram Moolenaar  let text = getline(state.lnum)
5618bb1c3e5SBram Moolenaar  let swendtag = match(text, '^\s*</') >= 0
5628bb1c3e5SBram Moolenaar  call s:CountITags(tolower(text))
5633ec574f2SBram Moolenaar  let state.baseindent = indent(state.lnum) + s:nextrel * shiftwidth()
564ec7944aaSBram Moolenaar  if !swendtag
5653ec574f2SBram Moolenaar    let state.baseindent += s:curind * shiftwidth()
566ec7944aaSBram Moolenaar  endif
567ec7944aaSBram Moolenaar  return state
568ec7944aaSBram Moolenaarendfunc "}}}
569ec7944aaSBram Moolenaar
5708bb1c3e5SBram Moolenaar" Indent inside a <pre> block: Keep indent as-is.
571*2346a637SBram Moolenaarfunc s:Alien2()
5728bb1c3e5SBram Moolenaar  "{{{
573ec7944aaSBram Moolenaar  return -1
574ec7944aaSBram Moolenaarendfunc "}}}
5758bb1c3e5SBram Moolenaar
5768bb1c3e5SBram Moolenaar" Return the indent inside a <script> block for javascript.
577*2346a637SBram Moolenaarfunc s:Alien3()
5788bb1c3e5SBram Moolenaar  "{{{
5798bb1c3e5SBram Moolenaar  let lnum = prevnonblank(v:lnum - 1)
5808bb1c3e5SBram Moolenaar  while lnum > 1 && getline(lnum) =~ '^\s*/[/*]'
5818bb1c3e5SBram Moolenaar    " Skip over comments to avoid that cindent() aligns with the <script> tag
5828bb1c3e5SBram Moolenaar    let lnum = prevnonblank(lnum - 1)
5838bb1c3e5SBram Moolenaar  endwhile
584*2346a637SBram Moolenaar  if lnum < b:hi_indent.blocklnr
585*2346a637SBram Moolenaar    " indent for <script> itself
586*2346a637SBram Moolenaar    return b:hi_indent.blocktagind
587*2346a637SBram Moolenaar  endif
5888bb1c3e5SBram Moolenaar  if lnum == b:hi_indent.blocklnr
589ec7944aaSBram Moolenaar    " indent for the first line after <script>
5908bb1c3e5SBram Moolenaar    return eval(b:hi_js1indent)
591ec7944aaSBram Moolenaar  endif
5928bb1c3e5SBram Moolenaar  if b:hi_indent.scripttype == "javascript"
593*2346a637SBram Moolenaar    " indent for further lines
5947ff78465SBram Moolenaar    return eval(b:hi_js1indent) + GetJavascriptIndent()
595ec7944aaSBram Moolenaar  else
596071d4279SBram Moolenaar    return -1
597071d4279SBram Moolenaar  endif
598ec7944aaSBram Moolenaarendfunc "}}}
5998bb1c3e5SBram Moolenaar
6008bb1c3e5SBram Moolenaar" Return the indent inside a <style> block.
601*2346a637SBram Moolenaarfunc s:Alien4()
6028bb1c3e5SBram Moolenaar  "{{{
6038bb1c3e5SBram Moolenaar  if prevnonblank(v:lnum-1) == b:hi_indent.blocklnr
604ec7944aaSBram Moolenaar    " indent for first content line
6058bb1c3e5SBram Moolenaar    return eval(b:hi_css1indent)
60691170f8aSBram Moolenaar  endif
607ec7944aaSBram Moolenaar  return s:CSSIndent()
6088bb1c3e5SBram Moolenaarendfunc "}}}
609ec7944aaSBram Moolenaar
6108bb1c3e5SBram Moolenaar" Indending inside a <style> block.  Returns the indent.
611*2346a637SBram Moolenaarfunc s:CSSIndent()
6128bb1c3e5SBram Moolenaar  "{{{
6138bb1c3e5SBram Moolenaar  " This handles standard CSS and also Closure stylesheets where special lines
6148bb1c3e5SBram Moolenaar  " start with @.
6158bb1c3e5SBram Moolenaar  " When the line starts with '*' or the previous line starts with "/*"
6168bb1c3e5SBram Moolenaar  " and does not end in "*/", use C indenting to format the comment.
6178bb1c3e5SBram Moolenaar  " Adopted $VIMRUNTIME/indent/css.vim
6188bb1c3e5SBram Moolenaar  let curtext = getline(v:lnum)
6198bb1c3e5SBram Moolenaar  if curtext =~ '^\s*[*]'
6208bb1c3e5SBram Moolenaar        \ || (v:lnum > 1 && getline(v:lnum - 1) =~ '\s*/\*'
6218bb1c3e5SBram Moolenaar        \     && getline(v:lnum - 1) !~ '\*/\s*$')
622ec7944aaSBram Moolenaar    return cindent(v:lnum)
623071d4279SBram Moolenaar  endif
6248bb1c3e5SBram Moolenaar
6258bb1c3e5SBram Moolenaar  let min_lnum = b:hi_indent.blocklnr
6268bb1c3e5SBram Moolenaar  let prev_lnum = s:CssPrevNonComment(v:lnum - 1, min_lnum)
6278bb1c3e5SBram Moolenaar  let [prev_lnum, found] = HtmlIndent_FindTagStart(prev_lnum)
6288bb1c3e5SBram Moolenaar  if prev_lnum <= min_lnum
6298bb1c3e5SBram Moolenaar    " Just below the <style> tag, indent for first content line after comments.
6308bb1c3e5SBram Moolenaar    return eval(b:hi_css1indent)
631ec7944aaSBram Moolenaar  endif
6328bb1c3e5SBram Moolenaar
633ba3ff539SBram Moolenaar  " If the current line starts with "}" align with its match.
6348bb1c3e5SBram Moolenaar  if curtext =~ '^\s*}'
6358bb1c3e5SBram Moolenaar    call cursor(v:lnum, 1)
6368bb1c3e5SBram Moolenaar    try
6378bb1c3e5SBram Moolenaar      normal! %
6388bb1c3e5SBram Moolenaar      " Found the matching "{", align with it after skipping unfinished lines.
6398bb1c3e5SBram Moolenaar      let align_lnum = s:CssFirstUnfinished(line('.'), min_lnum)
6408bb1c3e5SBram Moolenaar      return indent(align_lnum)
6418bb1c3e5SBram Moolenaar    catch
6428bb1c3e5SBram Moolenaar      " can't find it, try something else, but it's most likely going to be
6438bb1c3e5SBram Moolenaar      " wrong
6448bb1c3e5SBram Moolenaar    endtry
645ec7944aaSBram Moolenaar  endif
6468bb1c3e5SBram Moolenaar
6478bb1c3e5SBram Moolenaar  " add indent after {
6488bb1c3e5SBram Moolenaar  let brace_counts = HtmlIndent_CountBraces(prev_lnum)
6493ec574f2SBram Moolenaar  let extra = brace_counts.c_open * shiftwidth()
6508bb1c3e5SBram Moolenaar
6518bb1c3e5SBram Moolenaar  let prev_text = getline(prev_lnum)
6528bb1c3e5SBram Moolenaar  let below_end_brace = prev_text =~ '}\s*$'
6538bb1c3e5SBram Moolenaar
6548bb1c3e5SBram Moolenaar  " Search back to align with the first line that's unfinished.
6558bb1c3e5SBram Moolenaar  let align_lnum = s:CssFirstUnfinished(prev_lnum, min_lnum)
6568bb1c3e5SBram Moolenaar
6578bb1c3e5SBram Moolenaar  " Handle continuation lines if aligning with previous line and not after a
6588bb1c3e5SBram Moolenaar  " "}".
6598bb1c3e5SBram Moolenaar  if extra == 0 && align_lnum == prev_lnum && !below_end_brace
6608bb1c3e5SBram Moolenaar    let prev_hasfield = prev_text =~ '^\s*[a-zA-Z0-9-]\+:'
6618bb1c3e5SBram Moolenaar    let prev_special = prev_text =~ '^\s*\(/\*\|@\)'
6628bb1c3e5SBram Moolenaar    if curtext =~ '^\s*\(/\*\|@\)'
6638bb1c3e5SBram Moolenaar      " if the current line is not a comment or starts with @ (used by template
6648bb1c3e5SBram Moolenaar      " systems) reduce indent if previous line is a continuation line
6658bb1c3e5SBram Moolenaar      if !prev_hasfield && !prev_special
6663ec574f2SBram Moolenaar        let extra = -shiftwidth()
6678bb1c3e5SBram Moolenaar      endif
6688bb1c3e5SBram Moolenaar    else
6698bb1c3e5SBram Moolenaar      let cur_hasfield = curtext =~ '^\s*[a-zA-Z0-9-]\+:'
6708bb1c3e5SBram Moolenaar      let prev_unfinished = s:CssUnfinished(prev_text)
671fc65cabbSBram Moolenaar      if prev_unfinished
6728bb1c3e5SBram Moolenaar        " Continuation line has extra indent if the previous line was not a
6738bb1c3e5SBram Moolenaar        " continuation line.
6743ec574f2SBram Moolenaar        let extra = shiftwidth()
6758bb1c3e5SBram Moolenaar        " Align with @if
6768bb1c3e5SBram Moolenaar        if prev_text =~ '^\s*@if '
6778bb1c3e5SBram Moolenaar          let extra = 4
6788bb1c3e5SBram Moolenaar        endif
6798bb1c3e5SBram Moolenaar      elseif cur_hasfield && !prev_hasfield && !prev_special
6808bb1c3e5SBram Moolenaar        " less indent below a continuation line
6813ec574f2SBram Moolenaar        let extra = -shiftwidth()
6828bb1c3e5SBram Moolenaar      endif
6838bb1c3e5SBram Moolenaar    endif
6848bb1c3e5SBram Moolenaar  endif
6858bb1c3e5SBram Moolenaar
6868bb1c3e5SBram Moolenaar  if below_end_brace
6878bb1c3e5SBram Moolenaar    " find matching {, if that line starts with @ it's not the start of a rule
6888bb1c3e5SBram Moolenaar    " but something else from a template system
6898bb1c3e5SBram Moolenaar    call cursor(prev_lnum, 1)
6908bb1c3e5SBram Moolenaar    call search('}\s*$')
6918bb1c3e5SBram Moolenaar    try
6928bb1c3e5SBram Moolenaar      normal! %
6938bb1c3e5SBram Moolenaar      " Found the matching "{", align with it.
6948bb1c3e5SBram Moolenaar      let align_lnum = s:CssFirstUnfinished(line('.'), min_lnum)
6958bb1c3e5SBram Moolenaar      let special = getline(align_lnum) =~ '^\s*@'
6968bb1c3e5SBram Moolenaar    catch
6978bb1c3e5SBram Moolenaar      let special = 0
6988bb1c3e5SBram Moolenaar    endtry
6998bb1c3e5SBram Moolenaar    if special
7008bb1c3e5SBram Moolenaar      " do not reduce indent below @{ ... }
7018bb1c3e5SBram Moolenaar      if extra < 0
7023ec574f2SBram Moolenaar        let extra += shiftwidth()
7038bb1c3e5SBram Moolenaar      endif
7048bb1c3e5SBram Moolenaar    else
7053ec574f2SBram Moolenaar      let extra -= (brace_counts.c_close - (prev_text =~ '^\s*}')) * shiftwidth()
7068bb1c3e5SBram Moolenaar    endif
7078bb1c3e5SBram Moolenaar  endif
7088bb1c3e5SBram Moolenaar
7098bb1c3e5SBram Moolenaar  " if no extra indent yet...
7108bb1c3e5SBram Moolenaar  if extra == 0
7118bb1c3e5SBram Moolenaar    if brace_counts.p_open > brace_counts.p_close
7128bb1c3e5SBram Moolenaar      " previous line has more ( than ): add a shiftwidth
7133ec574f2SBram Moolenaar      let extra = shiftwidth()
7148bb1c3e5SBram Moolenaar    elseif brace_counts.p_open < brace_counts.p_close
7158bb1c3e5SBram Moolenaar      " previous line has more ) than (: subtract a shiftwidth
7163ec574f2SBram Moolenaar      let extra = -shiftwidth()
7178bb1c3e5SBram Moolenaar    endif
7188bb1c3e5SBram Moolenaar  endif
7198bb1c3e5SBram Moolenaar
7208bb1c3e5SBram Moolenaar  return indent(align_lnum) + extra
721ec7944aaSBram Moolenaarendfunc "}}}
7228bb1c3e5SBram Moolenaar
7238bb1c3e5SBram Moolenaar" Inside <style>: Whether a line is unfinished.
724fc65cabbSBram Moolenaar" 	tag:
725fc65cabbSBram Moolenaar" 	tag: blah
726fc65cabbSBram Moolenaar" 	tag: blah &&
727fc65cabbSBram Moolenaar" 	tag: blah ||
728*2346a637SBram Moolenaarfunc s:CssUnfinished(text)
7298bb1c3e5SBram Moolenaar  "{{{
730fc65cabbSBram Moolenaar  return a:text =~ '\(||\|&&\|:\|\k\)\s*$'
7318bb1c3e5SBram Moolenaarendfunc "}}}
7328bb1c3e5SBram Moolenaar
7338bb1c3e5SBram Moolenaar" Search back for the first unfinished line above "lnum".
734*2346a637SBram Moolenaarfunc s:CssFirstUnfinished(lnum, min_lnum)
7358bb1c3e5SBram Moolenaar  "{{{
7368bb1c3e5SBram Moolenaar  let align_lnum = a:lnum
7378bb1c3e5SBram Moolenaar  while align_lnum > a:min_lnum && s:CssUnfinished(getline(align_lnum - 1))
7388bb1c3e5SBram Moolenaar    let align_lnum -= 1
7398bb1c3e5SBram Moolenaar  endwhile
7408bb1c3e5SBram Moolenaar  return align_lnum
7418bb1c3e5SBram Moolenaarendfunc "}}}
7428bb1c3e5SBram Moolenaar
7438bb1c3e5SBram Moolenaar" Find the non-empty line at or before "lnum" that is not a comment.
744*2346a637SBram Moolenaarfunc s:CssPrevNonComment(lnum, stopline)
7458bb1c3e5SBram Moolenaar  "{{{
7468bb1c3e5SBram Moolenaar  " caller starts from a line a:lnum + 1 that is not a comment
747ec7944aaSBram Moolenaar  let lnum = prevnonblank(a:lnum)
7488bb1c3e5SBram Moolenaar  while 1
749ec7944aaSBram Moolenaar    let ccol = match(getline(lnum), '\*/')
750ec7944aaSBram Moolenaar    if ccol < 0
7513e496b0eSBram Moolenaar      " No comment end thus it's something else.
752ec7944aaSBram Moolenaar      return lnum
753ec7944aaSBram Moolenaar    endif
754ec7944aaSBram Moolenaar    call cursor(lnum, ccol + 1)
7558bb1c3e5SBram Moolenaar    " Search back for the /* that starts the comment
756ec7944aaSBram Moolenaar    let lnum = search('/\*', 'bW', a:stopline)
757ec7944aaSBram Moolenaar    if indent(".") == virtcol(".") - 1
7588bb1c3e5SBram Moolenaar      " The  found /* is at the start of the line. Now go back to the line
7598bb1c3e5SBram Moolenaar      " above it and again check if it is a comment.
7608bb1c3e5SBram Moolenaar      let lnum = prevnonblank(lnum - 1)
761ec7944aaSBram Moolenaar    else
7628bb1c3e5SBram Moolenaar      " /* is after something else, thus it's not a comment line.
763ec7944aaSBram Moolenaar      return lnum
764ec7944aaSBram Moolenaar    endif
7658bb1c3e5SBram Moolenaar  endwhile
766ec7944aaSBram Moolenaarendfunc "}}}
7678bb1c3e5SBram Moolenaar
7688bb1c3e5SBram Moolenaar" Check the number of {} and () in line "lnum". Return a dict with the counts.
769*2346a637SBram Moolenaarfunc HtmlIndent_CountBraces(lnum)
7708bb1c3e5SBram Moolenaar  "{{{
7718bb1c3e5SBram Moolenaar  let brs = substitute(getline(a:lnum), '[''"].\{-}[''"]\|/\*.\{-}\*/\|/\*.*$\|[^{}()]', '', 'g')
7728bb1c3e5SBram Moolenaar  let c_open = 0
7738bb1c3e5SBram Moolenaar  let c_close = 0
7748bb1c3e5SBram Moolenaar  let p_open = 0
7758bb1c3e5SBram Moolenaar  let p_close = 0
776ec7944aaSBram Moolenaar  for brace in split(brs, '\zs')
777ec7944aaSBram Moolenaar    if brace == "{"
7788bb1c3e5SBram Moolenaar      let c_open += 1
779ec7944aaSBram Moolenaar    elseif brace == "}"
7808bb1c3e5SBram Moolenaar      if c_open > 0
7818bb1c3e5SBram Moolenaar        let c_open -= 1
782ec7944aaSBram Moolenaar      else
7838bb1c3e5SBram Moolenaar        let c_close += 1
7848bb1c3e5SBram Moolenaar      endif
7858bb1c3e5SBram Moolenaar    elseif brace == '('
7868bb1c3e5SBram Moolenaar      let p_open += 1
7878bb1c3e5SBram Moolenaar    elseif brace == ')'
7888bb1c3e5SBram Moolenaar      if p_open > 0
7898bb1c3e5SBram Moolenaar        let p_open -= 1
7908bb1c3e5SBram Moolenaar      else
7918bb1c3e5SBram Moolenaar        let p_close += 1
792ec7944aaSBram Moolenaar      endif
793ec7944aaSBram Moolenaar    endif
794ec7944aaSBram Moolenaar  endfor
7958bb1c3e5SBram Moolenaar  return {'c_open': c_open,
7968bb1c3e5SBram Moolenaar        \ 'c_close': c_close,
7978bb1c3e5SBram Moolenaar        \ 'p_open': p_open,
7988bb1c3e5SBram Moolenaar        \ 'p_close': p_close}
799ec7944aaSBram Moolenaarendfunc "}}}
800ec7944aaSBram Moolenaar
8018bb1c3e5SBram Moolenaar" Return the indent for a comment: <!-- -->
802*2346a637SBram Moolenaarfunc s:Alien5()
8038bb1c3e5SBram Moolenaar  "{{{
8048bb1c3e5SBram Moolenaar  let curtext = getline(v:lnum)
8058bb1c3e5SBram Moolenaar  if curtext =~ '^\s*\zs-->'
8068bb1c3e5SBram Moolenaar    " current line starts with end of comment, line up with comment start.
8078bb1c3e5SBram Moolenaar    call cursor(v:lnum, 0)
8088bb1c3e5SBram Moolenaar    let lnum = search('<!--', 'b')
8098bb1c3e5SBram Moolenaar    if lnum > 0
8108bb1c3e5SBram Moolenaar      " TODO: what if <!-- is not at the start of the line?
8118bb1c3e5SBram Moolenaar      return indent(lnum)
8128bb1c3e5SBram Moolenaar    endif
8138bb1c3e5SBram Moolenaar
8148bb1c3e5SBram Moolenaar    " Strange, can't find it.
8158bb1c3e5SBram Moolenaar    return -1
8168bb1c3e5SBram Moolenaar  endif
8178bb1c3e5SBram Moolenaar
8188bb1c3e5SBram Moolenaar  let prevlnum = prevnonblank(v:lnum - 1)
8198bb1c3e5SBram Moolenaar  let prevtext = getline(prevlnum)
8208bb1c3e5SBram Moolenaar  let idx = match(prevtext, '^\s*\zs<!--')
8218bb1c3e5SBram Moolenaar  if idx >= 0
8228bb1c3e5SBram Moolenaar    " just below comment start, add a shiftwidth
8234072ba57SBram Moolenaar    return indent(prevlnum) + shiftwidth()
8248bb1c3e5SBram Moolenaar  endif
8258bb1c3e5SBram Moolenaar
8268bb1c3e5SBram Moolenaar  " Some files add 4 spaces just below a TODO line.  It's difficult to detect
8278bb1c3e5SBram Moolenaar  " the end of the TODO, so let's not do that.
8288bb1c3e5SBram Moolenaar
8298bb1c3e5SBram Moolenaar  " Align with the previous non-blank line.
8308bb1c3e5SBram Moolenaar  return indent(prevlnum)
8318bb1c3e5SBram Moolenaarendfunc "}}}
8328bb1c3e5SBram Moolenaar
833ca63501fSBram Moolenaar" Return the indent for conditional comment: <!--[ ![endif]-->
834*2346a637SBram Moolenaarfunc s:Alien6()
835ca63501fSBram Moolenaar  "{{{
836ca63501fSBram Moolenaar  let curtext = getline(v:lnum)
837ca63501fSBram Moolenaar  if curtext =~ '\s*\zs<!\[endif\]-->'
838ca63501fSBram Moolenaar    " current line starts with end of comment, line up with comment start.
839ca63501fSBram Moolenaar    let lnum = search('<!--', 'bn')
840ca63501fSBram Moolenaar    if lnum > 0
841ca63501fSBram Moolenaar      return indent(lnum)
842ca63501fSBram Moolenaar    endif
843ca63501fSBram Moolenaar  endif
8443ec574f2SBram Moolenaar  return b:hi_indent.baseindent + shiftwidth()
845ca63501fSBram Moolenaarendfunc "}}}
846ca63501fSBram Moolenaar
8478bb1c3e5SBram Moolenaar" When the "lnum" line ends in ">" find the line containing the matching "<".
848*2346a637SBram Moolenaarfunc HtmlIndent_FindTagStart(lnum)
8498bb1c3e5SBram Moolenaar  "{{{
8508bb1c3e5SBram Moolenaar  " Avoids using the indent of a continuation line.
8518bb1c3e5SBram Moolenaar  " Moves the cursor.
8528bb1c3e5SBram Moolenaar  " Return two values:
8538bb1c3e5SBram Moolenaar  " - the matching line number or "lnum".
8548bb1c3e5SBram Moolenaar  " - a flag indicating whether we found the end of a tag.
8558bb1c3e5SBram Moolenaar  " This method is global so that HTML-like indenters can use it.
8568bb1c3e5SBram Moolenaar  " To avoid matching " > " or " < " inside a string require that the opening
8578bb1c3e5SBram Moolenaar  " "<" is followed by a word character and the closing ">" comes after a
8588bb1c3e5SBram Moolenaar  " non-white character.
8598bb1c3e5SBram Moolenaar  let idx = match(getline(a:lnum), '\S>\s*$')
8608bb1c3e5SBram Moolenaar  if idx > 0
8618bb1c3e5SBram Moolenaar    call cursor(a:lnum, idx)
8628bb1c3e5SBram Moolenaar    let lnum = searchpair('<\w', '' , '\S>', 'bW', '', max([a:lnum - b:html_indent_line_limit, 0]))
8638bb1c3e5SBram Moolenaar    if lnum > 0
8648bb1c3e5SBram Moolenaar      return [lnum, 1]
8658bb1c3e5SBram Moolenaar    endif
8668bb1c3e5SBram Moolenaar  endif
8678bb1c3e5SBram Moolenaar  return [a:lnum, 0]
8688bb1c3e5SBram Moolenaarendfunc "}}}
8698bb1c3e5SBram Moolenaar
8708bb1c3e5SBram Moolenaar" Find the unclosed start tag from the current cursor position.
871*2346a637SBram Moolenaarfunc HtmlIndent_FindStartTag()
8728bb1c3e5SBram Moolenaar  "{{{
8738bb1c3e5SBram Moolenaar  " The cursor must be on or before a closing tag.
8748bb1c3e5SBram Moolenaar  " If found, positions the cursor at the match and returns the line number.
8758bb1c3e5SBram Moolenaar  " Otherwise returns 0.
876b5b75624SBram Moolenaar  let tagname = matchstr(getline('.')[col('.') - 1:], '</\zs' . s:tagname . '\ze')
8778bb1c3e5SBram Moolenaar  let start_lnum = searchpair('<' . tagname . '\>', '', '</' . tagname . '\>', 'bW')
8788bb1c3e5SBram Moolenaar  if start_lnum > 0
8798bb1c3e5SBram Moolenaar    return start_lnum
8808bb1c3e5SBram Moolenaar  endif
8818bb1c3e5SBram Moolenaar  return 0
8828bb1c3e5SBram Moolenaarendfunc "}}}
8838bb1c3e5SBram Moolenaar
8848bb1c3e5SBram Moolenaar" Moves the cursor from a "<" to the matching ">".
885*2346a637SBram Moolenaarfunc HtmlIndent_FindTagEnd()
8868bb1c3e5SBram Moolenaar  "{{{
8878bb1c3e5SBram Moolenaar  " Call this with the cursor on the "<" of a start tag.
8888bb1c3e5SBram Moolenaar  " This will move the cursor to the ">" of the matching end tag or, when it's
8898bb1c3e5SBram Moolenaar  " a self-closing tag, to the matching ">".
8908bb1c3e5SBram Moolenaar  " Limited to look up to b:html_indent_line_limit lines away.
8918bb1c3e5SBram Moolenaar  let text = getline('.')
892b5b75624SBram Moolenaar  let tagname = matchstr(text, s:tagname . '\|!--', col('.'))
8938bb1c3e5SBram Moolenaar  if tagname == '!--'
8948bb1c3e5SBram Moolenaar    call search('--\zs>')
8958bb1c3e5SBram Moolenaar  elseif s:get_tag('/' . tagname) != 0
8968bb1c3e5SBram Moolenaar    " tag with a closing tag, find matching "</tag>"
8978bb1c3e5SBram Moolenaar    call searchpair('<' . tagname, '', '</' . tagname . '\zs>', 'W', '', line('.') + b:html_indent_line_limit)
8988bb1c3e5SBram Moolenaar  else
8998bb1c3e5SBram Moolenaar    " self-closing tag, find the ">"
9008bb1c3e5SBram Moolenaar    call search('\S\zs>')
9018bb1c3e5SBram Moolenaar  endif
9028bb1c3e5SBram Moolenaarendfunc "}}}
9038bb1c3e5SBram Moolenaar
9048bb1c3e5SBram Moolenaar" Indenting inside a start tag. Return the correct indent or -1 if unknown.
905*2346a637SBram Moolenaarfunc s:InsideTag(foundHtmlString)
9068bb1c3e5SBram Moolenaar  "{{{
9078bb1c3e5SBram Moolenaar  if a:foundHtmlString
9088bb1c3e5SBram Moolenaar    " Inside an attribute string.
90963b74a83SBram Moolenaar    " Align with the opening quote or use an external function.
9108bb1c3e5SBram Moolenaar    let lnum = v:lnum - 1
9118bb1c3e5SBram Moolenaar    if lnum > 1
9128bb1c3e5SBram Moolenaar      if exists('b:html_indent_tag_string_func')
9138bb1c3e5SBram Moolenaar        return b:html_indent_tag_string_func(lnum)
9148bb1c3e5SBram Moolenaar      endif
91563b74a83SBram Moolenaar      " If there is a double quote in the previous line, indent with the
91663b74a83SBram Moolenaar      " character after it.
91763b74a83SBram Moolenaar      if getline(lnum) =~ '"'
91863b74a83SBram Moolenaar	call cursor(lnum, 0)
91963b74a83SBram Moolenaar	normal f"
92063b74a83SBram Moolenaar	return virtcol('.')
92163b74a83SBram Moolenaar      endif
9228bb1c3e5SBram Moolenaar      return indent(lnum)
9238bb1c3e5SBram Moolenaar    endif
9248bb1c3e5SBram Moolenaar  endif
9258bb1c3e5SBram Moolenaar
9268bb1c3e5SBram Moolenaar  " Should be another attribute: " attr="val".  Align with the previous
9278bb1c3e5SBram Moolenaar  " attribute start.
9288bb1c3e5SBram Moolenaar  let lnum = v:lnum
9298bb1c3e5SBram Moolenaar  while lnum > 1
9308bb1c3e5SBram Moolenaar    let lnum -= 1
9318bb1c3e5SBram Moolenaar    let text = getline(lnum)
9328bb1c3e5SBram Moolenaar    " Find a match with one of these, align with "attr":
9338bb1c3e5SBram Moolenaar    "       attr=
9348bb1c3e5SBram Moolenaar    "  <tag attr=
9358bb1c3e5SBram Moolenaar    "  text<tag attr=
9368bb1c3e5SBram Moolenaar    "  <tag>text</tag>text<tag attr=
9378bb1c3e5SBram Moolenaar    " For long lines search for the first match, finding the last match
9388bb1c3e5SBram Moolenaar    " gets very slow.
9398bb1c3e5SBram Moolenaar    if len(text) < 300
9408bb1c3e5SBram Moolenaar      let idx = match(text, '.*\s\zs[_a-zA-Z0-9-]\+="')
9418bb1c3e5SBram Moolenaar    else
9428bb1c3e5SBram Moolenaar      let idx = match(text, '\s\zs[_a-zA-Z0-9-]\+="')
9438bb1c3e5SBram Moolenaar    endif
944b5b75624SBram Moolenaar    if idx == -1
945b5b75624SBram Moolenaar      " try <tag attr
946b5b75624SBram Moolenaar      let idx = match(text, '<' . s:tagname . '\s\+\zs\w')
947b5b75624SBram Moolenaar    endif
948b5b75624SBram Moolenaar    if idx == -1
949942db23cSBram Moolenaar      " after just "<tag" indent two levels more
950b5b75624SBram Moolenaar      let idx = match(text, '<' . s:tagname . '$')
951b5b75624SBram Moolenaar      if idx >= 0
952942db23cSBram Moolenaar	call cursor(lnum, idx + 1)
953942db23cSBram Moolenaar	return virtcol('.') - 1 + shiftwidth() * 2
954b5b75624SBram Moolenaar      endif
955b5b75624SBram Moolenaar    endif
9568bb1c3e5SBram Moolenaar    if idx > 0
957b5b75624SBram Moolenaar      " Found the attribute to align with.
958b5b75624SBram Moolenaar      call cursor(lnum, idx)
959b5b75624SBram Moolenaar      return virtcol('.')
9608bb1c3e5SBram Moolenaar    endif
9618bb1c3e5SBram Moolenaar  endwhile
962ec7944aaSBram Moolenaar  return -1
963ec7944aaSBram Moolenaarendfunc "}}}
964ec7944aaSBram Moolenaar
9658bb1c3e5SBram Moolenaar" THE MAIN INDENT FUNCTION. Return the amount of indent for v:lnum.
966*2346a637SBram Moolenaarfunc HtmlIndent()
9678bb1c3e5SBram Moolenaar  "{{{
9689da7ff70SBram Moolenaar  if prevnonblank(v:lnum - 1) < 1
9698bb1c3e5SBram Moolenaar    " First non-blank line has no indent.
9708bb1c3e5SBram Moolenaar    return 0
971946e27abSBram Moolenaar  endif
972946e27abSBram Moolenaar
9738bb1c3e5SBram Moolenaar  let curtext = tolower(getline(v:lnum))
9743ec574f2SBram Moolenaar  let indentunit = shiftwidth()
975ec7944aaSBram Moolenaar
9768bb1c3e5SBram Moolenaar  let b:hi_newstate = {}
9778bb1c3e5SBram Moolenaar  let b:hi_newstate.lnum = v:lnum
9788bb1c3e5SBram Moolenaar
9798bb1c3e5SBram Moolenaar  " When syntax HL is enabled, detect we are inside a tag.  Indenting inside
9808bb1c3e5SBram Moolenaar  " a tag works very differently. Do not do this when the line starts with
9818bb1c3e5SBram Moolenaar  " "<", it gets the "htmlTag" ID but we are not inside a tag then.
9828bb1c3e5SBram Moolenaar  if curtext !~ '^\s*<'
9837b61a546SBram Moolenaar    normal! ^
9848bb1c3e5SBram Moolenaar    let stack = synstack(v:lnum, col('.'))  " assumes there are no tabs
9858bb1c3e5SBram Moolenaar    let foundHtmlString = 0
9868bb1c3e5SBram Moolenaar    for synid in reverse(stack)
9878bb1c3e5SBram Moolenaar      let name = synIDattr(synid, "name")
9888bb1c3e5SBram Moolenaar      if index(b:hi_insideStringNames, name) >= 0
9898bb1c3e5SBram Moolenaar        let foundHtmlString = 1
9908bb1c3e5SBram Moolenaar      elseif index(b:hi_insideTagNames, name) >= 0
9918bb1c3e5SBram Moolenaar        " Yes, we are inside a tag.
9928bb1c3e5SBram Moolenaar        let indent = s:InsideTag(foundHtmlString)
9938bb1c3e5SBram Moolenaar        if indent >= 0
9948bb1c3e5SBram Moolenaar          " Do not keep the state. TODO: could keep the block type.
9958bb1c3e5SBram Moolenaar          let b:hi_indent.lnum = 0
9968bb1c3e5SBram Moolenaar          return indent
9978bb1c3e5SBram Moolenaar        endif
9988bb1c3e5SBram Moolenaar      endif
9998bb1c3e5SBram Moolenaar    endfor
10008bb1c3e5SBram Moolenaar  endif
1001ec7944aaSBram Moolenaar
100252b91d80SBram Moolenaar  " does the line start with a closing tag?
10038bb1c3e5SBram Moolenaar  let swendtag = match(curtext, '^\s*</') >= 0
1004ec7944aaSBram Moolenaar
10058bb1c3e5SBram Moolenaar  if prevnonblank(v:lnum - 1) == b:hi_indent.lnum && b:hi_lasttick == b:changedtick - 1
1006ec7944aaSBram Moolenaar    " use state (continue from previous line)
1007ec7944aaSBram Moolenaar  else
1008ec7944aaSBram Moolenaar    " start over (know nothing)
10098bb1c3e5SBram Moolenaar    let b:hi_indent = s:FreshState(v:lnum)
1010071d4279SBram Moolenaar  endif
1011071d4279SBram Moolenaar
10128bb1c3e5SBram Moolenaar  if b:hi_indent.block >= 2
1013ec7944aaSBram Moolenaar    " within block
10148bb1c3e5SBram Moolenaar    let endtag = s:endtags[b:hi_indent.block]
10158bb1c3e5SBram Moolenaar    let blockend = stridx(curtext, endtag)
1016ec7944aaSBram Moolenaar    if blockend >= 0
1017ec7944aaSBram Moolenaar      " block ends here
10188bb1c3e5SBram Moolenaar      let b:hi_newstate.block = 0
1019ec7944aaSBram Moolenaar      " calc indent for REST OF LINE (may start more blocks):
10208bb1c3e5SBram Moolenaar      call s:CountTagsAndState(strpart(curtext, blockend + strlen(endtag)))
10218bb1c3e5SBram Moolenaar      if swendtag && b:hi_indent.block != 5
10228bb1c3e5SBram Moolenaar        let indent = b:hi_indent.blocktagind + s:curind * indentunit
10238bb1c3e5SBram Moolenaar        let b:hi_newstate.baseindent = indent + s:nextrel * indentunit
1024ec7944aaSBram Moolenaar      else
10258bb1c3e5SBram Moolenaar        let indent = s:Alien{b:hi_indent.block}()
10268bb1c3e5SBram Moolenaar        let b:hi_newstate.baseindent = b:hi_indent.blocktagind + s:nextrel * indentunit
102791170f8aSBram Moolenaar      endif
1028ec7944aaSBram Moolenaar    else
1029ec7944aaSBram Moolenaar      " block continues
1030ec7944aaSBram Moolenaar      " indent this line with alien method
10318bb1c3e5SBram Moolenaar      let indent = s:Alien{b:hi_indent.block}()
1032071d4279SBram Moolenaar    endif
1033ec7944aaSBram Moolenaar  else
1034ec7944aaSBram Moolenaar    " not within a block - within usual html
10358bb1c3e5SBram Moolenaar    let b:hi_newstate.block = b:hi_indent.block
1036ec7944aaSBram Moolenaar    if swendtag
10378bb1c3e5SBram Moolenaar      " The current line starts with an end tag, align with its start tag.
10388bb1c3e5SBram Moolenaar      call cursor(v:lnum, 1)
10398bb1c3e5SBram Moolenaar      let start_lnum = HtmlIndent_FindStartTag()
10408bb1c3e5SBram Moolenaar      if start_lnum > 0
10418bb1c3e5SBram Moolenaar        " check for the line starting with something inside a tag:
10428bb1c3e5SBram Moolenaar        " <sometag               <- align here
10438bb1c3e5SBram Moolenaar        "    attr=val><open>     not here
10448bb1c3e5SBram Moolenaar        let text = getline(start_lnum)
10458bb1c3e5SBram Moolenaar        let angle = matchstr(text, '[<>]')
10468bb1c3e5SBram Moolenaar        if angle == '>'
10478bb1c3e5SBram Moolenaar          call cursor(start_lnum, 1)
10488bb1c3e5SBram Moolenaar          normal! f>%
10498bb1c3e5SBram Moolenaar          let start_lnum = line('.')
10508bb1c3e5SBram Moolenaar          let text = getline(start_lnum)
1051ec7944aaSBram Moolenaar        endif
1052071d4279SBram Moolenaar
10538bb1c3e5SBram Moolenaar        let indent = indent(start_lnum)
10548bb1c3e5SBram Moolenaar        if col('.') > 2
10558bb1c3e5SBram Moolenaar          let swendtag = match(text, '^\s*</') >= 0
10568bb1c3e5SBram Moolenaar          call s:CountITags(text[: col('.') - 2])
10573ec574f2SBram Moolenaar          let indent += s:nextrel * shiftwidth()
10588bb1c3e5SBram Moolenaar          if !swendtag
10593ec574f2SBram Moolenaar            let indent += s:curind * shiftwidth()
10608bb1c3e5SBram Moolenaar          endif
10618bb1c3e5SBram Moolenaar        endif
10628bb1c3e5SBram Moolenaar      else
10638bb1c3e5SBram Moolenaar        " not sure what to do
10648bb1c3e5SBram Moolenaar        let indent = b:hi_indent.baseindent
10658bb1c3e5SBram Moolenaar      endif
10668bb1c3e5SBram Moolenaar      let b:hi_newstate.baseindent = indent
10678bb1c3e5SBram Moolenaar    else
10688bb1c3e5SBram Moolenaar      call s:CountTagsAndState(curtext)
10698bb1c3e5SBram Moolenaar      let indent = b:hi_indent.baseindent
10708bb1c3e5SBram Moolenaar      let b:hi_newstate.baseindent = indent + (s:curind + s:nextrel) * indentunit
10718bb1c3e5SBram Moolenaar    endif
10728bb1c3e5SBram Moolenaar  endif
10738bb1c3e5SBram Moolenaar
10748bb1c3e5SBram Moolenaar  let b:hi_lasttick = b:changedtick
10758bb1c3e5SBram Moolenaar  call extend(b:hi_indent, b:hi_newstate, "force")
10768bb1c3e5SBram Moolenaar  return indent
1077ec7944aaSBram Moolenaarendfunc "}}}
1078071d4279SBram Moolenaar
10798bb1c3e5SBram Moolenaar" Check user settings when loading this script the first time.
1080ec7944aaSBram Moolenaarcall HtmlIndent_CheckUserSettings()
1081071d4279SBram Moolenaar
108291170f8aSBram Moolenaarlet &cpo = s:cpo_save
108391170f8aSBram Moolenaarunlet s:cpo_save
108491170f8aSBram Moolenaar
10858bb1c3e5SBram Moolenaar" vim: fdm=marker ts=8 sw=2 tw=78
1086