blob: 57ba53ecd4ebdd86df1b8ab8f52feadb84f1cf86 [file] [log] [blame]
Bram Moolenaarec7944a2013-06-12 21:29:15 +02001" Vim indent script for HTML
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +02002" Header: "{{{
3" Maintainer: Bram Moolenaar
4" Original Author: Andy Wokula <anwoku@yahoo.de>
Bram Moolenaar690afe12017-01-28 18:34:47 +01005" Last Change: 2017 Jan 17
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +02006" Version: 1.0
7" Description: HTML indent script with cached state for faster indenting on a
8" range of lines.
9" Supports template systems through hooks.
10" Supports Closure stylesheets.
Bram Moolenaarec7944a2013-06-12 21:29:15 +020011"
12" Credits:
13" indent/html.vim (2006 Jun 05) from J. Zellner
14" indent/css.vim (2006 Dec 20) from N. Weibull
15"
16" History:
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +020017" 2014 June (v1.0) overhaul (Bram)
Bram Moolenaar52b91d82013-06-15 21:39:51 +020018" 2012 Oct 21 (v0.9) added support for shiftwidth()
19" 2011 Sep 09 (v0.8) added HTML5 tags (thx to J. Zuckerman)
20" 2008 Apr 28 (v0.6) revised customization
21" 2008 Mar 09 (v0.5) fixed 'indk' issue (thx to C.J. Robinson)
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +020022"}}}
Bram Moolenaar071d4272004-06-13 20:20:40 +000023
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +020024" Init Folklore, check user settings (2nd time ++)
25if exists("b:did_indent") "{{{
26 finish
Bram Moolenaar071d4272004-06-13 20:20:40 +000027endif
Bram Moolenaar690afe12017-01-28 18:34:47 +010028
29" Load the Javascript indent script first, it defines GetJavascriptIndent().
30" Undo the rest.
31" Load base python indent.
32if !exists('*GetJavascriptIndent')
33 runtime! indent/javascript.vim
34endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000035let b:did_indent = 1
36
Bram Moolenaarec7944a2013-06-12 21:29:15 +020037setlocal indentexpr=HtmlIndent()
38setlocal indentkeys=o,O,<Return>,<>>,{,},!^F
Bram Moolenaar071d4272004-06-13 20:20:40 +000039
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +020040" Needed for % to work when finding start/end of a tag.
Bram Moolenaar946e27a2014-06-25 18:50:27 +020041setlocal matchpairs+=<:>
42
Bram Moolenaar690afe12017-01-28 18:34:47 +010043let b:undo_indent = "setlocal inde< indk<"
Bram Moolenaar071d4272004-06-13 20:20:40 +000044
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +020045" b:hi_indent keeps state to speed up indenting consecutive lines.
46let b:hi_indent = {"lnum": -1}
47
48"""""" Code below this is loaded only once. """""
49if exists("*HtmlIndent") && !exists('g:force_reload_html')
50 call HtmlIndent_CheckUserSettings()
51 finish
Bram Moolenaar071d4272004-06-13 20:20:40 +000052endif
53
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +020054" shiftwidth() exists since patch 7.3.694
Bram Moolenaar52b91d82013-06-15 21:39:51 +020055if exists('*shiftwidth')
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +020056 let s:ShiftWidth = function('shiftwidth')
Bram Moolenaar52b91d82013-06-15 21:39:51 +020057else
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +020058 func! s:ShiftWidth()
59 return &shiftwidth
60 endfunc
Bram Moolenaar52b91d82013-06-15 21:39:51 +020061endif
62
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +020063" Allow for line continuation below.
Bram Moolenaar91170f82006-05-05 21:15:17 +000064let s:cpo_save = &cpo
Bram Moolenaar071d4272004-06-13 20:20:40 +000065set cpo-=C
Bram Moolenaarec7944a2013-06-12 21:29:15 +020066"}}}
Bram Moolenaar071d4272004-06-13 20:20:40 +000067
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +020068" Check and process settings from b:html_indent and g:html_indent... variables.
69" Prefer using buffer-local settings over global settings, so that there can
70" be defaults for all HTML files and exceptions for specific types of HTML
71" files.
72func! HtmlIndent_CheckUserSettings()
73 "{{{
74 let inctags = ''
75 if exists("b:html_indent_inctags")
76 let inctags = b:html_indent_inctags
77 elseif exists("g:html_indent_inctags")
78 let inctags = g:html_indent_inctags
79 endif
80 let b:hi_tags = {}
81 if len(inctags) > 0
82 call s:AddITags(b:hi_tags, split(inctags, ","))
83 endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000084
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +020085 let autotags = ''
86 if exists("b:html_indent_autotags")
87 let autotags = b:html_indent_autotags
88 elseif exists("g:html_indent_autotags")
89 let autotags = g:html_indent_autotags
90 endif
91 let b:hi_removed_tags = {}
Bram Moolenaar541f92d2015-06-19 13:27:23 +020092 if len(autotags) > 0
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +020093 call s:RemoveITags(b:hi_removed_tags, split(autotags, ","))
94 endif
95
96 " Syntax names indicating being inside a string of an attribute value.
97 let string_names = []
98 if exists("b:html_indent_string_names")
99 let string_names = b:html_indent_string_names
100 elseif exists("g:html_indent_string_names")
101 let string_names = g:html_indent_string_names
102 endif
103 let b:hi_insideStringNames = ['htmlString']
104 if len(string_names) > 0
105 for s in string_names
106 call add(b:hi_insideStringNames, s)
107 endfor
108 endif
109
110 " Syntax names indicating being inside a tag.
111 let tag_names = []
112 if exists("b:html_indent_tag_names")
113 let tag_names = b:html_indent_tag_names
114 elseif exists("g:html_indent_tag_names")
115 let tag_names = g:html_indent_tag_names
116 endif
117 let b:hi_insideTagNames = ['htmlTag', 'htmlScriptTag']
118 if len(tag_names) > 0
119 for s in tag_names
120 call add(b:hi_insideTagNames, s)
121 endfor
122 endif
123
124 let indone = {"zero": 0
125 \,"auto": "indent(prevnonblank(v:lnum-1))"
126 \,"inc": "b:hi_indent.blocktagind + s:ShiftWidth()"}
127
128 let script1 = ''
129 if exists("b:html_indent_script1")
130 let script1 = b:html_indent_script1
131 elseif exists("g:html_indent_script1")
132 let script1 = g:html_indent_script1
133 endif
134 if len(script1) > 0
135 let b:hi_js1indent = get(indone, script1, indone.zero)
136 else
137 let b:hi_js1indent = 0
138 endif
139
140 let style1 = ''
141 if exists("b:html_indent_style1")
142 let style1 = b:html_indent_style1
143 elseif exists("g:html_indent_style1")
144 let style1 = g:html_indent_style1
145 endif
146 if len(style1) > 0
147 let b:hi_css1indent = get(indone, style1, indone.zero)
148 else
149 let b:hi_css1indent = 0
150 endif
151
152 if !exists('b:html_indent_line_limit')
153 if exists('g:html_indent_line_limit')
154 let b:html_indent_line_limit = g:html_indent_line_limit
155 else
156 let b:html_indent_line_limit = 200
Bram Moolenaarec7944a2013-06-12 21:29:15 +0200157 endif
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +0200158 endif
Bram Moolenaarec7944a2013-06-12 21:29:15 +0200159endfunc "}}}
Bram Moolenaar071d4272004-06-13 20:20:40 +0000160
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +0200161" Init Script Vars
162"{{{
163let b:hi_lasttick = 0
164let b:hi_newstate = {}
Bram Moolenaarec7944a2013-06-12 21:29:15 +0200165let s:countonly = 0
166 "}}}
Bram Moolenaarec7944a2013-06-12 21:29:15 +0200167
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +0200168" Fill the s:indent_tags dict with known tags.
169" The key is "tagname" or "/tagname". {{{
170" The value is:
171" 1 opening tag
172" 2 "pre"
173" 3 "script"
174" 4 "style"
175" 5 comment start
Bram Moolenaarca635012015-09-25 20:34:21 +0200176" 6 conditional comment start
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +0200177" -1 closing tag
178" -2 "/pre"
179" -3 "/script"
180" -4 "/style"
181" -5 comment end
Bram Moolenaarca635012015-09-25 20:34:21 +0200182" -6 conditional comment end
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +0200183let s:indent_tags = {}
Bram Moolenaarca635012015-09-25 20:34:21 +0200184let s:endtags = [0,0,0,0,0,0,0] " long enough for the highest index
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +0200185"}}}
186
187" Add a list of tag names for a pair of <tag> </tag> to "tags".
188func! s:AddITags(tags, taglist)
189 "{{{
190 for itag in a:taglist
191 let a:tags[itag] = 1
192 let a:tags['/' . itag] = -1
193 endfor
194endfunc "}}}
195
196" Take a list of tag name pairs that are not to be used as tag pairs.
197func! s:RemoveITags(tags, taglist)
198 "{{{
199 for itag in a:taglist
200 let a:tags[itag] = 1
201 let a:tags['/' . itag] = 1
202 endfor
203endfunc "}}}
204
205" Add a block tag, that is a tag with a different kind of indenting.
206func! s:AddBlockTag(tag, id, ...)
207 "{{{
208 if !(a:id >= 2 && a:id < len(s:endtags))
209 echoerr 'AddBlockTag ' . a:id
210 return
211 endif
212 let s:indent_tags[a:tag] = a:id
213 if a:0 == 0
214 let s:indent_tags['/' . a:tag] = -a:id
215 let s:endtags[a:id] = "</" . a:tag . ">"
216 else
217 let s:indent_tags[a:1] = -a:id
218 let s:endtags[a:id] = a:1
219 endif
220endfunc "}}}
221
222" Add known tag pairs.
223" Self-closing tags and tags that are sometimes {{{
224" self-closing (e.g., <p>) are not here (when encountering </p> we can find
225" the matching <p>, but not the other way around).
226" Old HTML tags:
227call s:AddITags(s:indent_tags, [
228 \ 'a', 'abbr', 'acronym', 'address', 'b', 'bdo', 'big',
229 \ 'blockquote', 'body', 'button', 'caption', 'center', 'cite', 'code',
230 \ 'colgroup', 'del', 'dfn', 'dir', 'div', 'dl', 'em', 'fieldset', 'font',
231 \ 'form', 'frameset', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'html',
232 \ 'i', 'iframe', 'ins', 'kbd', 'label', 'legend', 'li',
233 \ 'map', 'menu', 'noframes', 'noscript', 'object', 'ol',
Bram Moolenaarec7944a2013-06-12 21:29:15 +0200234 \ 'optgroup', 'q', 's', 'samp', 'select', 'small', 'span', 'strong', 'sub',
235 \ 'sup', 'table', 'textarea', 'title', 'tt', 'u', 'ul', 'var', 'th', 'td',
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +0200236 \ 'tr', 'tbody', 'tfoot', 'thead'])
Bram Moolenaarec7944a2013-06-12 21:29:15 +0200237
Bram Moolenaar939a1ab2016-04-10 01:31:25 +0200238" New HTML5 elements:
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +0200239call s:AddITags(s:indent_tags, [
240 \ 'area', 'article', 'aside', 'audio', 'bdi', 'canvas',
Bram Moolenaar939a1ab2016-04-10 01:31:25 +0200241 \ 'command', 'data', 'datalist', 'details', 'embed', 'figcaption',
242 \ 'figure', 'footer', 'header', 'keygen', 'mark', 'meter', 'nav', 'output',
243 \ 'progress', 'rp', 'rt', 'ruby', 'section', 'source', 'summary', 'svg',
244 \ 'time', 'track', 'video', 'wbr'])
Bram Moolenaard8b77f72015-03-05 21:21:19 +0100245
246" Tags added for web components:
247call s:AddITags(s:indent_tags, [
248 \ 'content', 'shadow', 'template'])
Bram Moolenaarec7944a2013-06-12 21:29:15 +0200249"}}}
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +0200250
251" Add Block Tags: these contain alien content
252"{{{
Bram Moolenaarec7944a2013-06-12 21:29:15 +0200253call s:AddBlockTag('pre', 2)
254call s:AddBlockTag('script', 3)
255call s:AddBlockTag('style', 4)
256call s:AddBlockTag('<!--', 5, '-->')
Bram Moolenaarca635012015-09-25 20:34:21 +0200257call s:AddBlockTag('<!--[', 6, '![endif]-->')
Bram Moolenaarec7944a2013-06-12 21:29:15 +0200258"}}}
259
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +0200260" Return non-zero when "tagname" is an opening tag, not being a block tag, for
261" which there should be a closing tag. Can be used by scripts that include
262" HTML indenting.
263func! HtmlIndent_IsOpenTag(tagname)
264 "{{{
265 if get(s:indent_tags, a:tagname) == 1
266 return 1
267 endif
268 return get(b:hi_tags, a:tagname) == 1
269endfunc "}}}
Bram Moolenaarec7944a2013-06-12 21:29:15 +0200270
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +0200271" Get the value for "tagname", taking care of buffer-local tags.
272func! s:get_tag(tagname)
273 "{{{
274 let i = get(s:indent_tags, a:tagname)
275 if (i == 1 || i == -1) && get(b:hi_removed_tags, a:tagname) != 0
276 return 0
277 endif
278 if i == 0
279 let i = get(b:hi_tags, a:tagname)
280 endif
281 return i
282endfunc "}}}
Bram Moolenaarec7944a2013-06-12 21:29:15 +0200283
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +0200284" Count the number of start and end tags in "text".
285func! s:CountITags(text)
286 "{{{
287 " Store the result in s:curind and s:nextrel.
288 let s:curind = 0 " relative indent steps for current line [unit &sw]:
289 let s:nextrel = 0 " relative indent steps for next line [unit &sw]:
290 let s:block = 0 " assume starting outside of a block
291 let s:countonly = 1 " don't change state
Bram Moolenaarca635012015-09-25 20:34:21 +0200292 call substitute(a:text, '<\zs/\=\w\+\(-\w\+\)*\>\|<!--\[\|\[endif\]-->\|<!--\|-->', '\=s:CheckTag(submatch(0))', 'g')
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +0200293 let s:countonly = 0
294endfunc "}}}
295
296" Count the number of start and end tags in text.
297func! s:CountTagsAndState(text)
298 "{{{
299 " Store the result in s:curind and s:nextrel. Update b:hi_newstate.block.
300 let s:curind = 0 " relative indent steps for current line [unit &sw]:
301 let s:nextrel = 0 " relative indent steps for next line [unit &sw]:
302
303 let s:block = b:hi_newstate.block
Bram Moolenaarca635012015-09-25 20:34:21 +0200304 let tmp = substitute(a:text, '<\zs/\=\w\+\(-\w\+\)*\>\|<!--\[\|\[endif\]-->\|<!--\|-->', '\=s:CheckTag(submatch(0))', 'g')
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +0200305 if s:block == 3
306 let b:hi_newstate.scripttype = s:GetScriptType(matchstr(tmp, '\C.*<SCRIPT\>\zs[^>]*'))
307 endif
308 let b:hi_newstate.block = s:block
309endfunc "}}}
310
311" Used by s:CountITags() and s:CountTagsAndState().
312func! s:CheckTag(itag)
313 "{{{
314 " Returns an empty string or "SCRIPT".
315 " a:itag can be "tag" or "/tag" or "<!--" or "-->"
Bram Moolenaard8b77f72015-03-05 21:21:19 +0100316 if (s:CheckCustomTag(a:itag))
317 return ""
318 endif
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +0200319 let ind = s:get_tag(a:itag)
320 if ind == -1
321 " closing tag
322 if s:block != 0
323 " ignore itag within a block
324 return ""
325 endif
326 if s:nextrel == 0
327 let s:curind -= 1
Bram Moolenaarec7944a2013-06-12 21:29:15 +0200328 else
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +0200329 let s:nextrel -= 1
Bram Moolenaarec7944a2013-06-12 21:29:15 +0200330 endif
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +0200331 elseif ind == 1
332 " opening tag
333 if s:block != 0
334 return ""
Bram Moolenaarec7944a2013-06-12 21:29:15 +0200335 endif
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +0200336 let s:nextrel += 1
337 elseif ind != 0
338 " block-tag (opening or closing)
339 return s:CheckBlockTag(a:itag, ind)
340 " else ind==0 (other tag found): keep indent
341 endif
342 return ""
Bram Moolenaarec7944a2013-06-12 21:29:15 +0200343endfunc "}}}
344
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +0200345" Used by s:CheckTag(). Returns an empty string or "SCRIPT".
346func! s:CheckBlockTag(blocktag, ind)
347 "{{{
348 if a:ind > 0
349 " a block starts here
350 if s:block != 0
351 " already in a block (nesting) - ignore
352 " especially ignore comments after other blocktags
353 return ""
Bram Moolenaarec7944a2013-06-12 21:29:15 +0200354 endif
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +0200355 let s:block = a:ind " block type
356 if s:countonly
357 return ""
358 endif
359 let b:hi_newstate.blocklnr = v:lnum
360 " save allover indent for the endtag
361 let b:hi_newstate.blocktagind = b:hi_indent.baseindent + (s:nextrel + s:curind) * s:ShiftWidth()
362 if a:ind == 3
363 return "SCRIPT" " all except this must be lowercase
364 " line is to be checked again for the type attribute
365 endif
366 else
367 let s:block = 0
368 " we get here if starting and closing a block-tag on the same line
369 endif
370 return ""
371endfunc "}}}
Bram Moolenaarec7944a2013-06-12 21:29:15 +0200372
Bram Moolenaard8b77f72015-03-05 21:21:19 +0100373" Used by s:CheckTag().
374func! s:CheckCustomTag(ctag)
375 "{{{
376 " Returns 1 if ctag is the tag for a custom element, 0 otherwise.
377 " a:ctag can be "tag" or "/tag" or "<!--" or "-->"
378 let pattern = '\%\(\w\+-\)\+\w\+'
379 if match(a:ctag, pattern) == -1
380 return 0
381 endif
382 if matchstr(a:ctag, '\/\ze.\+') == "/"
383 " closing tag
384 if s:block != 0
385 " ignore ctag within a block
386 return 1
387 endif
388 if s:nextrel == 0
389 let s:curind -= 1
390 else
391 let s:nextrel -= 1
392 endif
393 else
394 " opening tag
395 if s:block != 0
396 return 1
397 endif
398 let s:nextrel += 1
399 endif
400 return 1
401endfunc "}}}
402
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +0200403" Return the <script> type: either "javascript" or ""
404func! s:GetScriptType(str)
405 "{{{
406 if a:str == "" || a:str =~ "java"
407 return "javascript"
408 else
409 return ""
410 endif
411endfunc "}}}
Bram Moolenaarec7944a2013-06-12 21:29:15 +0200412
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +0200413" Look back in the file, starting at a:lnum - 1, to compute a state for the
414" start of line a:lnum. Return the new state.
415func! s:FreshState(lnum)
416 "{{{
417 " A state is to know ALL relevant details about the
418 " lines 1..a:lnum-1, initial calculating (here!) can be slow, but updating is
419 " fast (incremental).
420 " TODO: this should be split up in detecting the block type and computing the
421 " indent for the block type, so that when we do not know the indent we do
422 " not need to clear the whole state and re-detect the block type again.
423 " State:
424 " lnum last indented line == prevnonblank(a:lnum - 1)
425 " block = 0 a:lnum located within special tag: 0:none, 2:<pre>,
Bram Moolenaarca635012015-09-25 20:34:21 +0200426 " 3:<script>, 4:<style>, 5:<!--, 6:<!--[
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +0200427 " baseindent use this indent for line a:lnum as a start - kind of
428 " autoindent (if block==0)
429 " scripttype = '' type attribute of a script tag (if block==3)
430 " blocktagind indent for current opening (get) and closing (set)
431 " blocktag (if block!=0)
432 " blocklnr lnum of starting blocktag (if block!=0)
433 " inattr line {lnum} starts with attributes of a tag
434 let state = {}
435 let state.lnum = prevnonblank(a:lnum - 1)
436 let state.scripttype = ""
437 let state.blocktagind = -1
438 let state.block = 0
439 let state.baseindent = 0
440 let state.blocklnr = 0
441 let state.inattr = 0
442
443 if state.lnum == 0
444 return state
445 endif
446
447 " Heuristic:
448 " remember startline state.lnum
449 " look back for <pre, </pre, <script, </script, <style, </style tags
450 " remember stopline
451 " if opening tag found,
452 " assume a:lnum within block
453 " else
454 " look back in result range (stopline, startline) for comment
455 " \ delimiters (<!--, -->)
456 " if comment opener found,
457 " assume a:lnum within comment
458 " else
459 " assume usual html for a:lnum
460 " if a:lnum-1 has a closing comment
461 " look back to get indent of comment opener
462 " FI
463
464 " look back for a blocktag
Bram Moolenaarca635012015-09-25 20:34:21 +0200465 let stopline2 = v:lnum + 1
466 if has_key(b:hi_indent, 'block') && b:hi_indent.block > 5
467 let [stopline2, stopcol2] = searchpos('<!--', 'bnW')
468 endif
469 let [stopline, stopcol] = searchpos('\c<\zs\/\=\%(pre\>\|script\>\|style\>\)', "bnW")
470 if stopline > 0 && stopline < stopline2
471 " ugly ... why isn't there searchstr()
Bram Moolenaarec7944a2013-06-12 21:29:15 +0200472 let tagline = tolower(getline(stopline))
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +0200473 let blocktag = matchstr(tagline, '\/\=\%(pre\>\|script\>\|style\>\)', stopcol - 1)
474 if blocktag[0] != "/"
475 " opening tag found, assume a:lnum within block
476 let state.block = s:indent_tags[blocktag]
477 if state.block == 3
478 let state.scripttype = s:GetScriptType(matchstr(tagline, '\>[^>]*', stopcol))
479 endif
480 let state.blocklnr = stopline
481 " check preceding tags in the line:
482 call s:CountITags(tagline[: stopcol-2])
483 let state.blocktagind = indent(stopline) + (s:curind + s:nextrel) * s:ShiftWidth()
484 return state
Bram Moolenaarec7944a2013-06-12 21:29:15 +0200485 elseif stopline == state.lnum
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +0200486 " handle special case: previous line (= state.lnum) contains a
487 " closing blocktag which is preceded by line-noise;
488 " blocktag == "/..."
489 let swendtag = match(tagline, '^\s*</') >= 0
490 if !swendtag
Bram Moolenaarca635012015-09-25 20:34:21 +0200491 let [bline, bcol] = searchpos('<'.blocktag[1:].'\>', "bnW")
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +0200492 call s:CountITags(tolower(getline(bline)[: bcol-2]))
493 let state.baseindent = indent(bline) + (s:curind + s:nextrel) * s:ShiftWidth()
494 return state
495 endif
Bram Moolenaarec7944a2013-06-12 21:29:15 +0200496 endif
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +0200497 endif
Bram Moolenaarca635012015-09-25 20:34:21 +0200498 if stopline > stopline2
499 let stopline = stopline2
500 let stopcol = stopcol2
501 endif
Bram Moolenaarec7944a2013-06-12 21:29:15 +0200502
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +0200503 " else look back for comment
Bram Moolenaarca635012015-09-25 20:34:21 +0200504 let [comlnum, comcol, found] = searchpos('\(<!--\[\)\|\(<!--\)\|-->', 'bpnW', stopline)
505 if found == 2 || found == 3
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +0200506 " comment opener found, assume a:lnum within comment
Bram Moolenaarca635012015-09-25 20:34:21 +0200507 let state.block = (found == 3 ? 5 : 6)
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +0200508 let state.blocklnr = comlnum
509 " check preceding tags in the line:
510 call s:CountITags(tolower(getline(comlnum)[: comcol-2]))
Bram Moolenaarca635012015-09-25 20:34:21 +0200511 if found == 2
512 let state.baseindent = b:hi_indent.baseindent
513 endif
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +0200514 let state.blocktagind = indent(comlnum) + (s:curind + s:nextrel) * s:ShiftWidth()
Bram Moolenaarec7944a2013-06-12 21:29:15 +0200515 return state
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +0200516 endif
Bram Moolenaarec7944a2013-06-12 21:29:15 +0200517
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +0200518 " else within usual HTML
519 let text = tolower(getline(state.lnum))
520
521 " Check a:lnum-1 for closing comment (we need indent from the opening line).
522 " Not when other tags follow (might be --> inside a string).
523 let comcol = stridx(text, '-->')
524 if comcol >= 0 && match(text, '[<>]', comcol) <= 0
525 call cursor(state.lnum, comcol + 1)
526 let [comlnum, comcol] = searchpos('<!--', 'bW')
527 if comlnum == state.lnum
528 let text = text[: comcol-2]
Bram Moolenaarec7944a2013-06-12 21:29:15 +0200529 else
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +0200530 let text = tolower(getline(comlnum)[: comcol-2])
Bram Moolenaar071d4272004-06-13 20:20:40 +0000531 endif
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +0200532 call s:CountITags(text)
533 let state.baseindent = indent(comlnum) + (s:curind + s:nextrel) * s:ShiftWidth()
534 " TODO check tags that follow "-->"
535 return state
536 endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000537
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +0200538 " Check if the previous line starts with end tag.
539 let swendtag = match(text, '^\s*</') >= 0
540
541 " If previous line ended in a closing tag, line up with the opening tag.
542 if !swendtag && text =~ '</\w\+\s*>\s*$'
543 call cursor(state.lnum, 99999)
Bram Moolenaar7b61a542014-08-23 15:31:19 +0200544 normal! F<
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +0200545 let start_lnum = HtmlIndent_FindStartTag()
546 if start_lnum > 0
547 let state.baseindent = indent(start_lnum)
548 if col('.') > 2
549 " check for tags before the matching opening tag.
550 let text = getline(start_lnum)
551 let swendtag = match(text, '^\s*</') >= 0
552 call s:CountITags(text[: col('.') - 2])
553 let state.baseindent += s:nextrel * s:ShiftWidth()
554 if !swendtag
555 let state.baseindent += s:curind * s:ShiftWidth()
556 endif
557 endif
558 return state
Bram Moolenaarec7944a2013-06-12 21:29:15 +0200559 endif
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +0200560 endif
561
562 " Else: no comments. Skip backwards to find the tag we're inside.
563 let [state.lnum, found] = HtmlIndent_FindTagStart(state.lnum)
564 " Check if that line starts with end tag.
565 let text = getline(state.lnum)
566 let swendtag = match(text, '^\s*</') >= 0
567 call s:CountITags(tolower(text))
568 let state.baseindent = indent(state.lnum) + s:nextrel * s:ShiftWidth()
569 if !swendtag
570 let state.baseindent += s:curind * s:ShiftWidth()
571 endif
572 return state
Bram Moolenaarec7944a2013-06-12 21:29:15 +0200573endfunc "}}}
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +0200574
575" Indent inside a <pre> block: Keep indent as-is.
576func! s:Alien2()
577 "{{{
578 return -1
579endfunc "}}}
580
581" Return the indent inside a <script> block for javascript.
582func! s:Alien3()
583 "{{{
584 let lnum = prevnonblank(v:lnum - 1)
585 while lnum > 1 && getline(lnum) =~ '^\s*/[/*]'
586 " Skip over comments to avoid that cindent() aligns with the <script> tag
587 let lnum = prevnonblank(lnum - 1)
588 endwhile
589 if lnum == b:hi_indent.blocklnr
590 " indent for the first line after <script>
591 return eval(b:hi_js1indent)
592 endif
593 if b:hi_indent.scripttype == "javascript"
Bram Moolenaar690afe12017-01-28 18:34:47 +0100594 return GetJavascriptIndent()
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +0200595 else
596 return -1
597 endif
598endfunc "}}}
599
600" Return the indent inside a <style> block.
601func! s:Alien4()
602 "{{{
603 if prevnonblank(v:lnum-1) == b:hi_indent.blocklnr
604 " indent for first content line
605 return eval(b:hi_css1indent)
606 endif
607 return s:CSSIndent()
608endfunc "}}}
609
610" Indending inside a <style> block. Returns the indent.
611func! s:CSSIndent()
612 "{{{
613 " This handles standard CSS and also Closure stylesheets where special lines
614 " start with @.
615 " When the line starts with '*' or the previous line starts with "/*"
616 " and does not end in "*/", use C indenting to format the comment.
617 " Adopted $VIMRUNTIME/indent/css.vim
618 let curtext = getline(v:lnum)
619 if curtext =~ '^\s*[*]'
620 \ || (v:lnum > 1 && getline(v:lnum - 1) =~ '\s*/\*'
621 \ && getline(v:lnum - 1) !~ '\*/\s*$')
622 return cindent(v:lnum)
623 endif
624
625 let min_lnum = b:hi_indent.blocklnr
626 let prev_lnum = s:CssPrevNonComment(v:lnum - 1, min_lnum)
627 let [prev_lnum, found] = HtmlIndent_FindTagStart(prev_lnum)
628 if prev_lnum <= min_lnum
629 " Just below the <style> tag, indent for first content line after comments.
630 return eval(b:hi_css1indent)
631 endif
632
633 " If the current line starts with "}" align with it's match.
634 if curtext =~ '^\s*}'
635 call cursor(v:lnum, 1)
636 try
637 normal! %
638 " Found the matching "{", align with it after skipping unfinished lines.
639 let align_lnum = s:CssFirstUnfinished(line('.'), min_lnum)
640 return indent(align_lnum)
641 catch
642 " can't find it, try something else, but it's most likely going to be
643 " wrong
644 endtry
645 endif
646
647 " add indent after {
648 let brace_counts = HtmlIndent_CountBraces(prev_lnum)
649 let extra = brace_counts.c_open * s:ShiftWidth()
650
651 let prev_text = getline(prev_lnum)
652 let below_end_brace = prev_text =~ '}\s*$'
653
654 " Search back to align with the first line that's unfinished.
655 let align_lnum = s:CssFirstUnfinished(prev_lnum, min_lnum)
656
657 " Handle continuation lines if aligning with previous line and not after a
658 " "}".
659 if extra == 0 && align_lnum == prev_lnum && !below_end_brace
660 let prev_hasfield = prev_text =~ '^\s*[a-zA-Z0-9-]\+:'
661 let prev_special = prev_text =~ '^\s*\(/\*\|@\)'
662 if curtext =~ '^\s*\(/\*\|@\)'
663 " if the current line is not a comment or starts with @ (used by template
664 " systems) reduce indent if previous line is a continuation line
665 if !prev_hasfield && !prev_special
666 let extra = -s:ShiftWidth()
667 endif
668 else
669 let cur_hasfield = curtext =~ '^\s*[a-zA-Z0-9-]\+:'
670 let prev_unfinished = s:CssUnfinished(prev_text)
671 if !cur_hasfield && (prev_hasfield || prev_unfinished)
672 " Continuation line has extra indent if the previous line was not a
673 " continuation line.
674 let extra = s:ShiftWidth()
675 " Align with @if
676 if prev_text =~ '^\s*@if '
677 let extra = 4
678 endif
679 elseif cur_hasfield && !prev_hasfield && !prev_special
680 " less indent below a continuation line
681 let extra = -s:ShiftWidth()
682 endif
683 endif
684 endif
685
686 if below_end_brace
687 " find matching {, if that line starts with @ it's not the start of a rule
688 " but something else from a template system
689 call cursor(prev_lnum, 1)
690 call search('}\s*$')
691 try
692 normal! %
693 " Found the matching "{", align with it.
694 let align_lnum = s:CssFirstUnfinished(line('.'), min_lnum)
695 let special = getline(align_lnum) =~ '^\s*@'
696 catch
697 let special = 0
698 endtry
699 if special
700 " do not reduce indent below @{ ... }
701 if extra < 0
702 let extra += s:ShiftWidth()
703 endif
704 else
705 let extra -= (brace_counts.c_close - (prev_text =~ '^\s*}')) * s:ShiftWidth()
706 endif
707 endif
708
709 " if no extra indent yet...
710 if extra == 0
711 if brace_counts.p_open > brace_counts.p_close
712 " previous line has more ( than ): add a shiftwidth
713 let extra = s:ShiftWidth()
714 elseif brace_counts.p_open < brace_counts.p_close
715 " previous line has more ) than (: subtract a shiftwidth
716 let extra = -s:ShiftWidth()
717 endif
718 endif
719
720 return indent(align_lnum) + extra
721endfunc "}}}
722
723" Inside <style>: Whether a line is unfinished.
724func! s:CssUnfinished(text)
725 "{{{
726 return a:text =~ '\s\(||\|&&\|:\)\s*$'
727endfunc "}}}
728
729" Search back for the first unfinished line above "lnum".
730func! s:CssFirstUnfinished(lnum, min_lnum)
731 "{{{
732 let align_lnum = a:lnum
733 while align_lnum > a:min_lnum && s:CssUnfinished(getline(align_lnum - 1))
734 let align_lnum -= 1
735 endwhile
736 return align_lnum
737endfunc "}}}
738
739" Find the non-empty line at or before "lnum" that is not a comment.
740func! s:CssPrevNonComment(lnum, stopline)
741 "{{{
742 " caller starts from a line a:lnum + 1 that is not a comment
743 let lnum = prevnonblank(a:lnum)
744 while 1
Bram Moolenaarec7944a2013-06-12 21:29:15 +0200745 let ccol = match(getline(lnum), '\*/')
746 if ccol < 0
Bram Moolenaar3e496b02016-09-25 22:11:48 +0200747 " No comment end thus it's something else.
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +0200748 return lnum
Bram Moolenaarec7944a2013-06-12 21:29:15 +0200749 endif
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +0200750 call cursor(lnum, ccol + 1)
751 " Search back for the /* that starts the comment
Bram Moolenaarec7944a2013-06-12 21:29:15 +0200752 let lnum = search('/\*', 'bW', a:stopline)
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +0200753 if indent(".") == virtcol(".") - 1
754 " The found /* is at the start of the line. Now go back to the line
755 " above it and again check if it is a comment.
756 let lnum = prevnonblank(lnum - 1)
Bram Moolenaarec7944a2013-06-12 21:29:15 +0200757 else
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +0200758 " /* is after something else, thus it's not a comment line.
759 return lnum
Bram Moolenaarec7944a2013-06-12 21:29:15 +0200760 endif
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +0200761 endwhile
Bram Moolenaarec7944a2013-06-12 21:29:15 +0200762endfunc "}}}
763
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +0200764" Check the number of {} and () in line "lnum". Return a dict with the counts.
765func! HtmlIndent_CountBraces(lnum)
766 "{{{
767 let brs = substitute(getline(a:lnum), '[''"].\{-}[''"]\|/\*.\{-}\*/\|/\*.*$\|[^{}()]', '', 'g')
768 let c_open = 0
769 let c_close = 0
770 let p_open = 0
771 let p_close = 0
772 for brace in split(brs, '\zs')
773 if brace == "{"
774 let c_open += 1
775 elseif brace == "}"
776 if c_open > 0
777 let c_open -= 1
778 else
779 let c_close += 1
780 endif
781 elseif brace == '('
782 let p_open += 1
783 elseif brace == ')'
784 if p_open > 0
785 let p_open -= 1
786 else
787 let p_close += 1
788 endif
789 endif
790 endfor
791 return {'c_open': c_open,
792 \ 'c_close': c_close,
793 \ 'p_open': p_open,
794 \ 'p_close': p_close}
795endfunc "}}}
796
797" Return the indent for a comment: <!-- -->
798func! s:Alien5()
799 "{{{
800 let curtext = getline(v:lnum)
801 if curtext =~ '^\s*\zs-->'
802 " current line starts with end of comment, line up with comment start.
803 call cursor(v:lnum, 0)
804 let lnum = search('<!--', 'b')
805 if lnum > 0
806 " TODO: what if <!-- is not at the start of the line?
807 return indent(lnum)
808 endif
809
810 " Strange, can't find it.
Bram Moolenaarec7944a2013-06-12 21:29:15 +0200811 return -1
Bram Moolenaar946e27a2014-06-25 18:50:27 +0200812 endif
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +0200813
814 let prevlnum = prevnonblank(v:lnum - 1)
815 let prevtext = getline(prevlnum)
816 let idx = match(prevtext, '^\s*\zs<!--')
817 if idx >= 0
818 " just below comment start, add a shiftwidth
819 return idx + s:ShiftWidth()
820 endif
821
822 " Some files add 4 spaces just below a TODO line. It's difficult to detect
823 " the end of the TODO, so let's not do that.
824
825 " Align with the previous non-blank line.
826 return indent(prevlnum)
Bram Moolenaar946e27a2014-06-25 18:50:27 +0200827endfunc "}}}
828
Bram Moolenaarca635012015-09-25 20:34:21 +0200829" Return the indent for conditional comment: <!--[ ![endif]-->
830func! s:Alien6()
831 "{{{
832 let curtext = getline(v:lnum)
833 if curtext =~ '\s*\zs<!\[endif\]-->'
834 " current line starts with end of comment, line up with comment start.
835 let lnum = search('<!--', 'bn')
836 if lnum > 0
837 return indent(lnum)
838 endif
839 endif
840 return b:hi_indent.baseindent + s:ShiftWidth()
841endfunc "}}}
842
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +0200843" When the "lnum" line ends in ">" find the line containing the matching "<".
844func! HtmlIndent_FindTagStart(lnum)
845 "{{{
846 " Avoids using the indent of a continuation line.
847 " Moves the cursor.
848 " Return two values:
849 " - the matching line number or "lnum".
850 " - a flag indicating whether we found the end of a tag.
851 " This method is global so that HTML-like indenters can use it.
852 " To avoid matching " > " or " < " inside a string require that the opening
853 " "<" is followed by a word character and the closing ">" comes after a
854 " non-white character.
855 let idx = match(getline(a:lnum), '\S>\s*$')
856 if idx > 0
857 call cursor(a:lnum, idx)
858 let lnum = searchpair('<\w', '' , '\S>', 'bW', '', max([a:lnum - b:html_indent_line_limit, 0]))
859 if lnum > 0
860 return [lnum, 1]
Bram Moolenaar071d4272004-06-13 20:20:40 +0000861 endif
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +0200862 endif
863 return [a:lnum, 0]
Bram Moolenaarec7944a2013-06-12 21:29:15 +0200864endfunc "}}}
Bram Moolenaar071d4272004-06-13 20:20:40 +0000865
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +0200866" Find the unclosed start tag from the current cursor position.
867func! HtmlIndent_FindStartTag()
868 "{{{
869 " The cursor must be on or before a closing tag.
870 " If found, positions the cursor at the match and returns the line number.
871 " Otherwise returns 0.
872 let tagname = matchstr(getline('.')[col('.') - 1:], '</\zs\w\+\ze')
873 let start_lnum = searchpair('<' . tagname . '\>', '', '</' . tagname . '\>', 'bW')
874 if start_lnum > 0
875 return start_lnum
876 endif
877 return 0
878endfunc "}}}
Bram Moolenaar071d4272004-06-13 20:20:40 +0000879
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +0200880" Moves the cursor from a "<" to the matching ">".
881func! HtmlIndent_FindTagEnd()
882 "{{{
883 " Call this with the cursor on the "<" of a start tag.
884 " This will move the cursor to the ">" of the matching end tag or, when it's
885 " a self-closing tag, to the matching ">".
886 " Limited to look up to b:html_indent_line_limit lines away.
887 let text = getline('.')
888 let tagname = matchstr(text, '\w\+\|!--', col('.'))
889 if tagname == '!--'
890 call search('--\zs>')
891 elseif s:get_tag('/' . tagname) != 0
892 " tag with a closing tag, find matching "</tag>"
893 call searchpair('<' . tagname, '', '</' . tagname . '\zs>', 'W', '', line('.') + b:html_indent_line_limit)
894 else
895 " self-closing tag, find the ">"
896 call search('\S\zs>')
897 endif
898endfunc "}}}
Bram Moolenaarec7944a2013-06-12 21:29:15 +0200899
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +0200900" Indenting inside a start tag. Return the correct indent or -1 if unknown.
901func! s:InsideTag(foundHtmlString)
902 "{{{
903 if a:foundHtmlString
904 " Inside an attribute string.
905 " Align with the previous line or use an external function.
906 let lnum = v:lnum - 1
907 if lnum > 1
908 if exists('b:html_indent_tag_string_func')
909 return b:html_indent_tag_string_func(lnum)
910 endif
911 return indent(lnum)
912 endif
913 endif
914
915 " Should be another attribute: " attr="val". Align with the previous
916 " attribute start.
917 let lnum = v:lnum
918 while lnum > 1
919 let lnum -= 1
920 let text = getline(lnum)
921 " Find a match with one of these, align with "attr":
922 " attr=
923 " <tag attr=
924 " text<tag attr=
925 " <tag>text</tag>text<tag attr=
926 " For long lines search for the first match, finding the last match
927 " gets very slow.
928 if len(text) < 300
929 let idx = match(text, '.*\s\zs[_a-zA-Z0-9-]\+="')
930 else
931 let idx = match(text, '\s\zs[_a-zA-Z0-9-]\+="')
932 endif
933 if idx > 0
934 " Found the attribute. TODO: assumes spaces, no Tabs.
935 return idx
936 endif
937 endwhile
938 return -1
939endfunc "}}}
940
941" THE MAIN INDENT FUNCTION. Return the amount of indent for v:lnum.
942func! HtmlIndent()
943 "{{{
Bram Moolenaar9da7ff72015-01-14 12:52:36 +0100944 if prevnonblank(v:lnum - 1) < 1
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +0200945 " First non-blank line has no indent.
946 return 0
947 endif
948
949 let curtext = tolower(getline(v:lnum))
950 let indentunit = s:ShiftWidth()
951
952 let b:hi_newstate = {}
953 let b:hi_newstate.lnum = v:lnum
954
955 " When syntax HL is enabled, detect we are inside a tag. Indenting inside
956 " a tag works very differently. Do not do this when the line starts with
957 " "<", it gets the "htmlTag" ID but we are not inside a tag then.
958 if curtext !~ '^\s*<'
Bram Moolenaar7b61a542014-08-23 15:31:19 +0200959 normal! ^
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +0200960 let stack = synstack(v:lnum, col('.')) " assumes there are no tabs
961 let foundHtmlString = 0
962 for synid in reverse(stack)
963 let name = synIDattr(synid, "name")
964 if index(b:hi_insideStringNames, name) >= 0
965 let foundHtmlString = 1
966 elseif index(b:hi_insideTagNames, name) >= 0
967 " Yes, we are inside a tag.
968 let indent = s:InsideTag(foundHtmlString)
969 if indent >= 0
970 " Do not keep the state. TODO: could keep the block type.
971 let b:hi_indent.lnum = 0
972 return indent
973 endif
974 endif
975 endfor
976 endif
977
978 " does the line start with a closing tag?
979 let swendtag = match(curtext, '^\s*</') >= 0
980
981 if prevnonblank(v:lnum - 1) == b:hi_indent.lnum && b:hi_lasttick == b:changedtick - 1
982 " use state (continue from previous line)
983 else
984 " start over (know nothing)
985 let b:hi_indent = s:FreshState(v:lnum)
986 endif
987
988 if b:hi_indent.block >= 2
989 " within block
990 let endtag = s:endtags[b:hi_indent.block]
991 let blockend = stridx(curtext, endtag)
992 if blockend >= 0
993 " block ends here
994 let b:hi_newstate.block = 0
995 " calc indent for REST OF LINE (may start more blocks):
996 call s:CountTagsAndState(strpart(curtext, blockend + strlen(endtag)))
997 if swendtag && b:hi_indent.block != 5
998 let indent = b:hi_indent.blocktagind + s:curind * indentunit
999 let b:hi_newstate.baseindent = indent + s:nextrel * indentunit
1000 else
1001 let indent = s:Alien{b:hi_indent.block}()
1002 let b:hi_newstate.baseindent = b:hi_indent.blocktagind + s:nextrel * indentunit
1003 endif
1004 else
1005 " block continues
1006 " indent this line with alien method
1007 let indent = s:Alien{b:hi_indent.block}()
1008 endif
1009 else
1010 " not within a block - within usual html
1011 let b:hi_newstate.block = b:hi_indent.block
1012 if swendtag
1013 " The current line starts with an end tag, align with its start tag.
1014 call cursor(v:lnum, 1)
1015 let start_lnum = HtmlIndent_FindStartTag()
1016 if start_lnum > 0
1017 " check for the line starting with something inside a tag:
1018 " <sometag <- align here
1019 " attr=val><open> not here
1020 let text = getline(start_lnum)
1021 let angle = matchstr(text, '[<>]')
1022 if angle == '>'
1023 call cursor(start_lnum, 1)
1024 normal! f>%
1025 let start_lnum = line('.')
1026 let text = getline(start_lnum)
1027 endif
1028
1029 let indent = indent(start_lnum)
1030 if col('.') > 2
1031 let swendtag = match(text, '^\s*</') >= 0
1032 call s:CountITags(text[: col('.') - 2])
1033 let indent += s:nextrel * s:ShiftWidth()
1034 if !swendtag
1035 let indent += s:curind * s:ShiftWidth()
1036 endif
1037 endif
1038 else
1039 " not sure what to do
1040 let indent = b:hi_indent.baseindent
1041 endif
1042 let b:hi_newstate.baseindent = indent
1043 else
1044 call s:CountTagsAndState(curtext)
1045 let indent = b:hi_indent.baseindent
1046 let b:hi_newstate.baseindent = indent + (s:curind + s:nextrel) * indentunit
1047 endif
1048 endif
1049
1050 let b:hi_lasttick = b:changedtick
1051 call extend(b:hi_indent, b:hi_newstate, "force")
1052 return indent
1053endfunc "}}}
1054
1055" Check user settings when loading this script the first time.
Bram Moolenaarec7944a2013-06-12 21:29:15 +02001056call HtmlIndent_CheckUserSettings()
Bram Moolenaar071d4272004-06-13 20:20:40 +00001057
Bram Moolenaar91170f82006-05-05 21:15:17 +00001058let &cpo = s:cpo_save
1059unlet s:cpo_save
1060
Bram Moolenaar8bb1c3e2014-07-04 16:43:17 +02001061" vim: fdm=marker ts=8 sw=2 tw=78