public
Created

Homecooked Vim indent script for HTML

  • Download Gist
html.vim
VimL
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
if exists("b:did_indent")
finish
endif
let b:did_indent = 1
 
setlocal indentexpr=HtmlIndentGet(v:lnum)
setlocal indentkeys=o,O,*<Return>,<>>,<<>,/,{,}
 
if exists('*HtmlIndentGet') | finish | endif
"
" Loads an external indent file and returns the indentexpression
fun! s:LoadExternalIndent(type)
unlet! b:did_indent
exe "runtime! indent/" . a:type . ".vim"
let indexp = &l:indentexpr
setlocal indentexpr=HtmlIndentGet(v:lnum)
return indexp
endfun
 
" Tags with forbidden end tags
let s:forbidden_end_tag_regex = '\<\(area\|base\|basefont\|br\|col\|frame\|hr\|img\|input\|isindex\|link\|meta\|param\)\>'
" Tags with optional end tags
" let s:optional_end_tag_regex = '\<\(body\|colgroup\|dd\|dt\|head\|html\|li\|option\|p\|tbody\|td\|tfoot\|th\|thead\|tr\)\>'
" Tags with special end tags (that do not count as indent contributions)
let s:special_end_tag_regex = '/\(pre\|script\|style\)\>'
 
" Checks if the character on the given line and column is an HTML bracket
fun! s:IsHtmlBracket(line, col)
" The syntax name of a bracket is htmlTag or htmlEndTag
return synIDattr(synID(a:line, a:col, 0), 'name') =~ 'html\(End\)\?Tag'
endfun
 
" Counts the number of tags contributing to the indent
fun! s:CountContributingTags(lnum, count_start)
if a:count_start
" Match start tags
let pat = '<\a\+'
else
" Match end tags
let pat = '</\a\+'
end
 
let line = getline(a:lnum)
" Number of tags contributing to the indent
let nvalid = 0
" The current tag to be processed
let curhit = 1
let hit = matchstr(line, pat)
 
while hit != ""
if s:IsHtmlBracket(a:lnum, match(line, pat, 0, curhit) + 1)
if a:count_start
"if hit =~? s:optional_end_tag_regex
" " Check if the tag has an end tag
" if searchpairpos(hit . '\a\@!', '', '</' . strpart(hit, 1) . '\a\@!', 'nW') != [0, 0]
" let nvalid += 1
" endif
if hit !~? s:forbidden_end_tag_regex
" Do not count tags without end tags
let nvalid += 1
endif
elseif hit !~? s:special_end_tag_regex
" Filter special end tags
let nvalid += 1
end
endif
 
let curhit += 1
let hit = matchstr(line, pat, 0, curhit)
endwhile
 
return nvalid
endfun
 
" Returns the balance of HTML brackets on the given line
" The balance is negative if there are more closing brackets than opening,
" and positive if there are more opening brackets
fun! s:GetBracketBalance(lnum)
let balance = 0
let line = getline(a:lnum)
 
" Match all brackets
let idx = match(line, '[<>]')
 
while idx != -1
if s:IsHtmlBracket(a:lnum, idx + 1)
if line[idx] == '<'
let balance += 1
elseif line[idx] == '>'
let balance -= 1
endif
endif
 
let idx = match(line, '[<>]', idx + 1)
endwhile
 
return balance
endfun
 
" Returns the indent for aligning attributes when a tag on the given line is
" unclosed
fun! s:GetAttributeIndent(lnum)
let indent = 0
let line = getline(a:lnum)
 
" Find the tag start (the last opening html bracket)
let idx = strridx(line, '<')
while !s:IsHtmlBracket(a:lnum, idx + 1)
let idx = strridx(line, '<', idx - 1)
endwhile
 
" tag start length of tag name space after tag
return idx + strlen(matchstr(line, '<\a\+', idx)) + 1
endfun
 
" Returns the line that starts a tag that closes on the given line
fun! s:GetTagStartLine(lnum)
let line = getline(a:lnum)
 
" Find the tag end (the first closing html bracket)
let idx = stridx(line, '>')
while !s:IsHtmlBracket(a:lnum, idx + 1)
let idx = stridx(line, '>', idx + 1)
endwhile
 
" Store the current position for later restoring
let cline = line('.')
let ccol = col('.')
 
" Move inside the unclosed tag
call cursor(a:lnum, 1)
" Search for the start
let [start_lnum, start_col] = searchpairpos('<', '', '>', 'nWb',
\ 'synIDattr(synID(line("."), col("."), 0), "name") !~? "html\\(End\\)\\?Tag"')
 
call cursor(cline, ccol)
return start_lnum
endfun
 
fun! HtmlIndentGet(lnum)
" No indent in pre tags
if getline(a:lnum) =~ '\c</pre>'
\ || 0 < searchpair('\c<pre>', '', '\c</pre>', 'nWb')
\ || 0 < searchpair('\c<pre>', '', '\c</pre>', 'nW')
return -1
endif
 
" Javascript indentation
if synIDattr(synID(a:lnum, 1, 1), 'name') =~ 'java.*' &&
\ synIDattr(synID(a:lnum, strlen(getline(a:lnum)), 1), 'name')
\ =~ 'java.*'
if !exists('b:javascript_indentexpr')
let b:javascript_indentexpr = <SID>LoadExternalIndent("javascript")
endif
exe "let ind = " . b:javascript_indentexpr
return ind
endif
 
" CSS indentation
if synIDattr(synID(a:lnum, 1, 1), 'name') =~ 'css.*' &&
\ synIDattr(synID(a:lnum, strlen(getline(a:lnum)), 1), 'name')
\ =~ 'css.*'
if !exists('b:css_indentexpr')
let b:css_indentexpr = <SID>LoadExternalIndent("css")
endif
exe "let ind = " . b:css_indentexpr
return ind
endif
 
let plnum = prevnonblank(a:lnum - 1)
" First line
if plnum == 0 | return 0 | endif
 
let bb = s:GetBracketBalance(plnum)
 
" Tags on the current line can only remove indent
let ccnt = s:CountContributingTags(a:lnum, 0)
let ccnt -= s:CountContributingTags(a:lnum, 1)
if ccnt < 0 | let ccnt = 0 | endif
 
if bb > 0
" The line above starts an unclosed tag, so the indent should align the
" attributes
return s:GetAttributeIndent(plnum) - (&sw * ccnt)
else
let pscnt = 0
let baseindent = indent(plnum)
 
" Tags on the line above can only add indent
let pcnt = s:CountContributingTags(plnum, 1)
let pcnt -= s:CountContributingTags(plnum, 0)
if pcnt < 0 | let pcnt = 0 | endif
 
if bb < 0
" The line above closes a previously unclosed
let pslnum = s:GetTagStartLine(plnum)
 
" Tags on lines above can only add indent
let pscnt = s:CountContributingTags(pslnum, 1)
let pscnt -= s:CountContributingTags(pslnum, 0)
if pscnt < 0 | let pscnt = 0 | endif
 
let baseindent = indent(pslnum)
endif
 
return baseindent + (&sw * ( pscnt + pcnt - ccnt ))
endif
 
return -1
endfun

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.