├── README.md ├── indent ├── html.vim ├── html2.vim ├── javascript.vim └── typescript.vim ├── test.js └── test_script.html /README.md: -------------------------------------------------------------------------------- 1 | vim-js-indent 2 | ============= 3 | 4 | Vim indenter for standalone and embedded JavaScript and TypeScript. 5 | 6 | Installation 7 | ------------ 8 | 9 | ### vim-plug 10 | 11 | 1. Add `Plug 'jason0x43/vim-js-indent'` to your `.vimrc` 12 | 1. Restart vim 13 | 1. Run `:PlugInstall` 14 | 15 | ### Pathogen 16 | 17 | Clone `https://github.com/jason0x43/vim-js-indent.git` into your bundles 18 | directory (`~/.vim/bundle`). 19 | 20 | Configuration 21 | ------------- 22 | 23 |
24 |
js_indent_flat_switch
25 |
Boolean, default=0
26 | Set to 1 to make `case` statements align with their containing `switch`.
27 |
js_indent_logging
28 |
Boolean, default=0
29 | Set to 1 to enable logging comments (useful for debugging).
30 |
js_indent_typescript
31 |
Boolean, default=1
32 | Set to 0 to disable use of the JavaScript indenter for TypeScript buffers.
33 |
34 | 35 | License 36 | ------ 37 | Copyright © 2014-2016 Jason Cheatham. Distributed under the same terms as Vim 38 | itself. See `:help license`. 39 | -------------------------------------------------------------------------------- /indent/html.vim: -------------------------------------------------------------------------------- 1 | " Vim indent script for HTML 2 | " Header: "{{{ 3 | " Maintainer: Bram Moolenaar 4 | " Original Author: Andy Wokula 5 | " Last Change: 2015 Jun 12 6 | " 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. 11 | " 12 | " @jason0x43: This version of the indenter has been modified to 13 | " support a custom JavaScript indenter rather than cindent. 14 | " 15 | " Credits: 16 | " indent/html.vim (2006 Jun 05) from J. Zellner 17 | " indent/css.vim (2006 Dec 20) from N. Weibull 18 | " 19 | " History: 20 | " 2014 June (v1.0) overhaul (Bram) 21 | " 2012 Oct 21 (v0.9) added support for shiftwidth() 22 | " 2011 Sep 09 (v0.8) added HTML5 tags (thx to J. Zuckerman) 23 | " 2008 Apr 28 (v0.6) revised customization 24 | " 2008 Mar 09 (v0.5) fixed 'indk' issue (thx to C.J. Robinson) 25 | "}}} 26 | 27 | " Init Folklore, check user settings (2nd time ++) 28 | if exists("b:did_indent") "{{{ 29 | finish 30 | endif 31 | 32 | " @jason0x43: Let the JavaScript indenter set b:did_indent {{{ 33 | " Load the JavaScript indenter 34 | runtime! indent/javascript.vim 35 | let s:js_indent_expr = &indentexpr 36 | " }}} 37 | 38 | setlocal indentexpr=HtmlIndent() 39 | setlocal indentkeys=o,O,,<>>,{,},!^F 40 | 41 | " @jason0x43: Remove unnecessary cindent configuration 42 | 43 | " Needed for % to work when finding start/end of a tag. 44 | setlocal matchpairs+=<:> 45 | 46 | let b:undo_indent = "setlocal inde< indk< cino<" 47 | 48 | " b:hi_indent keeps state to speed up indenting consecutive lines. 49 | let b:hi_indent = {"lnum": -1} 50 | 51 | """""" Code below this is loaded only once. """"" 52 | if exists("*HtmlIndent") && !exists('g:force_reload_html') 53 | call HtmlIndent_CheckUserSettings() 54 | finish 55 | endif 56 | 57 | " shiftwidth() exists since patch 7.3.694 58 | if exists('*shiftwidth') 59 | let s:ShiftWidth = function('shiftwidth') 60 | else 61 | func! s:ShiftWidth() 62 | return &shiftwidth 63 | endfunc 64 | endif 65 | 66 | " Allow for line continuation below. 67 | let s:cpo_save = &cpo 68 | set cpo-=C 69 | "}}} 70 | 71 | " Check and process settings from b:html_indent and g:html_indent... variables. 72 | " Prefer using buffer-local settings over global settings, so that there can 73 | " be defaults for all HTML files and exceptions for specific types of HTML 74 | " files. 75 | func! HtmlIndent_CheckUserSettings() 76 | "{{{ 77 | let inctags = '' 78 | if exists("b:html_indent_inctags") 79 | let inctags = b:html_indent_inctags 80 | elseif exists("g:html_indent_inctags") 81 | let inctags = g:html_indent_inctags 82 | endif 83 | let b:hi_tags = {} 84 | if len(inctags) > 0 85 | call s:AddITags(b:hi_tags, split(inctags, ",")) 86 | endif 87 | 88 | let autotags = '' 89 | if exists("b:html_indent_autotags") 90 | let autotags = b:html_indent_autotags 91 | elseif exists("g:html_indent_autotags") 92 | let autotags = g:html_indent_autotags 93 | endif 94 | let b:hi_removed_tags = {} 95 | if len(autotags) > 0 96 | call s:RemoveITags(b:hi_removed_tags, split(autotags, ",")) 97 | endif 98 | 99 | " Syntax names indicating being inside a string of an attribute value. 100 | let string_names = [] 101 | if exists("b:html_indent_string_names") 102 | let string_names = b:html_indent_string_names 103 | elseif exists("g:html_indent_string_names") 104 | let string_names = g:html_indent_string_names 105 | endif 106 | let b:hi_insideStringNames = ['htmlString'] 107 | if len(string_names) > 0 108 | for s in string_names 109 | call add(b:hi_insideStringNames, s) 110 | endfor 111 | endif 112 | 113 | " Syntax names indicating being inside a tag. 114 | let tag_names = [] 115 | if exists("b:html_indent_tag_names") 116 | let tag_names = b:html_indent_tag_names 117 | elseif exists("g:html_indent_tag_names") 118 | let tag_names = g:html_indent_tag_names 119 | endif 120 | let b:hi_insideTagNames = ['htmlTag', 'htmlScriptTag'] 121 | if len(tag_names) > 0 122 | for s in tag_names 123 | call add(b:hi_insideTagNames, s) 124 | endfor 125 | endif 126 | 127 | let indone = {"zero": 0 128 | \,"auto": "indent(prevnonblank(v:lnum-1))" 129 | \,"inc": "b:hi_indent.blocktagind + s:ShiftWidth()"} 130 | 131 | let script1 = '' 132 | if exists("b:html_indent_script1") 133 | let script1 = b:html_indent_script1 134 | elseif exists("g:html_indent_script1") 135 | let script1 = g:html_indent_script1 136 | endif 137 | if len(script1) > 0 138 | let b:hi_js1indent = get(indone, script1, indone.zero) 139 | else 140 | let b:hi_js1indent = 0 141 | endif 142 | 143 | let style1 = '' 144 | if exists("b:html_indent_style1") 145 | let style1 = b:html_indent_style1 146 | elseif exists("g:html_indent_style1") 147 | let style1 = g:html_indent_style1 148 | endif 149 | if len(style1) > 0 150 | let b:hi_css1indent = get(indone, style1, indone.zero) 151 | else 152 | let b:hi_css1indent = 0 153 | endif 154 | 155 | if !exists('b:html_indent_line_limit') 156 | if exists('g:html_indent_line_limit') 157 | let b:html_indent_line_limit = g:html_indent_line_limit 158 | else 159 | let b:html_indent_line_limit = 200 160 | endif 161 | endif 162 | endfunc "}}} 163 | 164 | " Init Script Vars 165 | "{{{ 166 | let b:hi_lasttick = 0 167 | let b:hi_newstate = {} 168 | let s:countonly = 0 169 | "}}} 170 | 171 | " Fill the s:indent_tags dict with known tags. 172 | " The key is "tagname" or "/tagname". {{{ 173 | " The value is: 174 | " 1 opening tag 175 | " 2 "pre" 176 | " 3 "script" 177 | " 4 "style" 178 | " 5 comment start 179 | " -1 closing tag 180 | " -2 "/pre" 181 | " -3 "/script" 182 | " -4 "/style" 183 | " -5 comment end 184 | let s:indent_tags = {} 185 | let s:endtags = [0,0,0,0,0,0] " long enough for the highest index 186 | "}}} 187 | 188 | " Add a list of tag names for a pair of to "tags". 189 | func! s:AddITags(tags, taglist) 190 | "{{{ 191 | for itag in a:taglist 192 | let a:tags[itag] = 1 193 | let a:tags['/' . itag] = -1 194 | endfor 195 | endfunc "}}} 196 | 197 | " Take a list of tag name pairs that are not to be used as tag pairs. 198 | func! s:RemoveITags(tags, taglist) 199 | "{{{ 200 | for itag in a:taglist 201 | let a:tags[itag] = 1 202 | let a:tags['/' . itag] = 1 203 | endfor 204 | endfunc "}}} 205 | 206 | " Add a block tag, that is a tag with a different kind of indenting. 207 | func! s:AddBlockTag(tag, id, ...) 208 | "{{{ 209 | if !(a:id >= 2 && a:id < len(s:endtags)) 210 | echoerr 'AddBlockTag ' . a:id 211 | return 212 | endif 213 | let s:indent_tags[a:tag] = a:id 214 | if a:0 == 0 215 | let s:indent_tags['/' . a:tag] = -a:id 216 | let s:endtags[a:id] = "" 217 | else 218 | let s:indent_tags[a:1] = -a:id 219 | let s:endtags[a:id] = a:1 220 | endif 221 | endfunc "}}} 222 | 223 | " Add known tag pairs. 224 | " Self-closing tags and tags that are sometimes {{{ 225 | " self-closing (e.g.,

) are not here (when encountering

we can find 226 | " the matching

, but not the other way around). 227 | " Old HTML tags: 228 | call s:AddITags(s:indent_tags, [ 229 | \ 'a', 'abbr', 'acronym', 'address', 'b', 'bdo', 'big', 230 | \ 'blockquote', 'body', 'button', 'caption', 'center', 'cite', 'code', 231 | \ 'colgroup', 'del', 'dfn', 'dir', 'div', 'dl', 'em', 'fieldset', 'font', 232 | \ 'form', 'frameset', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'html', 233 | \ 'i', 'iframe', 'ins', 'kbd', 'label', 'legend', 'li', 234 | \ 'map', 'menu', 'noframes', 'noscript', 'object', 'ol', 235 | \ 'optgroup', 'q', 's', 'samp', 'select', 'small', 'span', 'strong', 'sub', 236 | \ 'sup', 'table', 'textarea', 'title', 'tt', 'u', 'ul', 'var', 'th', 'td', 237 | \ 'tr', 'tbody', 'tfoot', 'thead']) 238 | 239 | " Tags added 2011 Sep 09 (especially HTML5 tags): 240 | call s:AddITags(s:indent_tags, [ 241 | \ 'area', 'article', 'aside', 'audio', 'bdi', 'canvas', 242 | \ 'command', 'datalist', 'details', 'embed', 'figure', 'footer', 243 | \ 'header', 'group', 'keygen', 'mark', 'math', 'meter', 'nav', 'output', 244 | \ 'progress', 'ruby', 'section', 'svg', 'texture', 'time', 'video', 245 | \ 'wbr', 'text']) 246 | 247 | " Tags added for web components: 248 | call s:AddITags(s:indent_tags, [ 249 | \ 'content', 'shadow', 'template']) 250 | "}}} 251 | 252 | " Add Block Tags: these contain alien content 253 | "{{{ 254 | call s:AddBlockTag('pre', 2) 255 | call s:AddBlockTag('script', 3) 256 | call s:AddBlockTag('style', 4) 257 | call s:AddBlockTag('') 258 | "}}} 259 | 260 | " 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. 263 | func! 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 269 | endfunc "}}} 270 | 271 | " Get the value for "tagname", taking care of buffer-local tags. 272 | func! 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 282 | endfunc "}}} 283 | 284 | " Count the number of start and end tags in "text". 285 | func! 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 292 | call substitute(a:text, '<\zs/\=\w\+\(-\w\+\)*\>\|', '\=s:CheckTag(submatch(0))', 'g') 293 | let s:countonly = 0 294 | endfunc "}}} 295 | 296 | " Count the number of start and end tags in text. 297 | func! 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 304 | let tmp = substitute(a:text, '<\zs/\=\w\+\(-\w\+\)*\>\|', '\=s:CheckTag(submatch(0))', 'g') 305 | if s:block == 3 306 | let b:hi_newstate.scripttype = s:GetScriptType(matchstr(tmp, '\C.*\zs[^>]*')) 307 | endif 308 | let b:hi_newstate.block = s:block 309 | endfunc "}}} 310 | 311 | " Used by s:CountITags() and s:CountTagsAndState(). 312 | func! s:CheckTag(itag) 313 | "{{{ 314 | " Returns an empty string or "SCRIPT". 315 | " a:itag can be "tag" or "/tag" or "" 316 | if (s:CheckCustomTag(a:itag)) 317 | return "" 318 | endif 319 | 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 328 | else 329 | let s:nextrel -= 1 330 | endif 331 | elseif ind == 1 332 | " opening tag 333 | if s:block != 0 334 | return "" 335 | endif 336 | 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 "" 343 | endfunc "}}} 344 | 345 | " Used by s:CheckTag(). Returns an empty string or "SCRIPT". 346 | func! 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 "" 354 | endif 355 | 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 "" 371 | endfunc "}}} 372 | 373 | " Used by s:CheckTag(). 374 | func! 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 "" 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 401 | endfunc "}}} 402 | 403 | " Return the 123 | 124 | 125 | --------------------------------------------------------------------------------