diff options
Diffstat (limited to 'indent')
-rw-r--r-- | indent/svelte.vim | 373 |
1 files changed, 257 insertions, 116 deletions
diff --git a/indent/svelte.vim b/indent/svelte.vim index cb01c424..047d1479 100644 --- a/indent/svelte.vim +++ b/indent/svelte.vim @@ -2,160 +2,301 @@ if polyglot#init#is_disabled(expand('<sfile>:p'), 'svelte', 'indent/svelte.vim') finish endif +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" " Vim indent file -" Language: Svelte 3 (HTML/JavaScript) -" Author: Evan Lecklider <evan@lecklider.com> -" Maintainer: Evan Lecklide <evan@lecklider.com> -" URL: https://github.com/evanleck/vim-svelte - +" +" Language: Svelte +" Maintainer: leafOfTree <leafvocation@gmail.com> +" +" CREDITS: Inspired by mxw/vim-jsx. +" +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" if exists("b:did_indent") finish endif -if !exists('g:svelte_indent_script') - let g:svelte_indent_script = 1 -endif +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +" +" Variables {{{ +" +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +let s:name = 'vim-svelte-plugin' +" Let <template> handled by HTML +let s:svelte_tag_start = '\v^\<(script|style)' +let s:svelte_tag_end = '\v^\<\/(script|style)' +let s:template_tag = '\v^\s*\<\/?template' +" https://developer.mozilla.org/en-US/docs/Glossary/Empty_element +let s:empty_tagname = '(area|base|br|col|embed|hr|input|img|keygen|link|meta|param|source|track|wbr)' +let s:empty_tag = '\v\C\<'.s:empty_tagname.'[^/]*\>' +let s:empty_tag_start = '\v\<'.s:empty_tagname.'[^\>]*$' +let s:empty_tag_end = '\v^\s*[^\<\>\/]*\>\s*' +let s:tag_end = '\v^\s*\/?\>\s*' +let s:oneline_block = '^\s*{#.*{/.*}\s*$' +"}}} + +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +" +" Config {{{ +" +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +let s:use_pug = svelte#GetConfig('use_pug', 0) +let s:use_sass = svelte#GetConfig('use_sass', 0) +let s:use_coffee = svelte#GetConfig('use_coffee', 0) +let s:use_typescript = svelte#GetConfig('use_typescript', 0) +let s:has_init_indent = svelte#GetConfig('has_init_indent', 1) +let s:debug = svelte#GetConfig('debug', 0) +"}}} + +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +" +" Load indent method {{{ +" +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +" Save shiftwidth +let s:sw = &sw + +" Use lib/indent/ files for compatibility +unlet! b:did_indent +runtime lib/indent/xml.vim -if !exists('g:svelte_indent_style') - let g:svelte_indent_style = 1 -endif +unlet! b:did_indent +runtime lib/indent/css.vim -" Try to mirror Svelte's indent settings so the HTML indenting scripts match. -if g:svelte_indent_script - let b:html_indent_script1 = "inc" -else - let b:html_indent_script1 = "zero" +" Use normal indent files +unlet! b:did_indent +runtime! indent/javascript.vim +let b:javascript_indentexpr = &indentexpr + +if s:use_pug + unlet! b:did_indent + let s:save_formatoptions = &formatoptions + runtime! indent/pug.vim + let &formatoptions = s:save_formatoptions endif -if g:svelte_indent_style - let b:html_indent_style1 = "inc" -else - let b:html_indent_style1 = "zero" +if s:use_sass + unlet! b:did_indent + runtime! indent/sass.vim endif -runtime! indent/html.vim -unlet! b:did_indent +if s:use_coffee + unlet! b:did_indent + runtime! indent/coffee.vim +endif -let s:html_indent = &l:indentexpr -let b:did_indent = 1 +if s:use_typescript + unlet! b:did_indent + runtime! indent/typescript.vim +endif +" Recover shiftwidth +let &sw = s:sw +"}}} + +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +" +" Settings {{{ +" +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +" JavaScript indentkeys +setlocal indentkeys=0{,0},0),0],0\,,!^F,o,O,e,:,=:else +" XML indentkeys +setlocal indentkeys+=*<Return>,<>>,<<>,/ setlocal indentexpr=GetSvelteIndent() -setlocal indentkeys=o,O,*<Return>,<>>,{,},0),0],!^F,;,=:else,=:then,=:catch,=/if,=/each,=/await - -" Only define the function once. -if exists('*GetSvelteIndent') - finish -endif +"}}} +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +" +" Functions {{{ +" +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" function! GetSvelteIndent() - let current_line_number = v:lnum - - if current_line_number == 0 - return 0 - endif - - let current_line = getline(current_line_number) + let prevlnum = prevnonblank(v:lnum-1) + let prevline = getline(prevlnum) + let prevsyns = s:SynsSOL(prevlnum) + + let curline = getline(v:lnum) + let cursyns = s:SynsSOL(v:lnum) + let cursyn = get(cursyns, 0, '') + + if s:SynHTML(cursyn) + call s:Log('syntax: html') + let ind = XmlIndentGet(v:lnum, 0) + if prevline =~? s:empty_tag + call s:Log('previous line is empty tag') + let ind = ind - &sw + endif - " Opening script and style tags should be all the way outdented. - if current_line =~ '^\s*</\?\(script\|style\)' - return 0 - endif + if s:IsBlockStart(prevsyns) && prevline !~ s:oneline_block + call s:Log('increase block indent') + let ind = ind + &sw + endif - let previous_line_number = prevnonblank(current_line_number - 1) - let previous_line = getline(previous_line_number) - let previous_line_indent = indent(previous_line_number) + if s:IsBlockEnd(cursyns, curline) + call s:Log('decrease block indent') + let ind = ind - &sw + endif - " The inside of scripts an styles should be indented unless disabled. - if previous_line =~ '^\s*<script' - if g:svelte_indent_script - return previous_line_indent + shiftwidth() + " Align '/>' and '>' with '<' for multiline tags. + if curline =~? s:tag_end + let ind = ind - &sw + endif + " Then correct the indentation of any element following '/>' or '>'. + if prevline =~? s:tag_end + let ind = ind + &sw + + "Decrease indent if prevlines are a multiline empty tag + let [start, end] = s:PrevMultilineEmptyTag(v:lnum) + if end == prevlnum + call s:Log('previous line is a multiline empty tag') + let ind = indent(v:lnum - 1) + endif + endif + elseif s:SynPug(cursyn) + call s:Log('syntax: pug') + let ind = GetPugIndent() + elseif s:SynCoffee(cursyn) + call s:Log('syntax: coffee') + let ind = GetCoffeeIndent(v:lnum) + elseif s:SynTypeScript(cursyn) + call s:Log('syntax: typescript') + let ind = GetTypescriptIndent() + elseif s:SynSASS(cursyn) + call s:Log('syntax: sass') + let ind = GetSassIndent() + elseif s:SynStyle(cursyn) + call s:Log('syntax: style') + let ind = GetCSSIndent() + else + call s:Log('syntax: javascript') + if len(b:javascript_indentexpr) + let ind = eval(b:javascript_indentexpr) else - return previous_line_indent + let ind = cindent(v:lnum) endif endif - if previous_line =~ '^\s*<style' - if g:svelte_indent_style - return previous_line_indent + shiftwidth() - else - return previous_line_indent + if curline =~? s:svelte_tag_start || curline =~? s:svelte_tag_end + \|| prevline =~? s:svelte_tag_end + \|| (curline =~ s:template_tag && s:SynPug(cursyn)) + call s:Log('current line is svelte tag or previous line is svelte tag end') + call s:Log('... or current line is pug template tag') + let ind = 0 + elseif s:has_init_indent + if s:SynSvelteScriptOrStyle(cursyn) && ind < 1 + call s:Log('add initial indent') + let ind = &sw endif + elseif prevline =~? s:svelte_tag_start + call s:Log('previous line is svelte tag start') + let ind = 0 endif - execute "let indent = " . s:html_indent + call s:Log('indent: '.ind) + return ind +endfunction - " For some reason, the HTML CSS indentation keeps indenting the next line over - " and over after each style declaration. - if searchpair('<style>', '', '</style>', 'bW') && previous_line =~ ';$' && current_line !~ '}' - return previous_line_indent - endif +function! s:IsBlockStart(prevsyns) + let prevsyn_second = get(a:prevsyns, 1, '') + " Some HTML tags add an extra syntax layer + let prevsyn_third = get(a:prevsyns, 2, '') + return s:SynBlockBody(prevsyn_second) + \ || s:SynBlockStart(prevsyn_second) + \ || s:SynBlockBody(prevsyn_third) + \ || s:SynBlockStart(prevsyn_third) +endfunction - " "/await" or ":catch" or ":then" - if current_line =~ '^\s*{\s*\/await' || current_line =~ '^\s*{\s*:\(catch\|then\)' - let await_start = searchpair('{\s*#await\>', '', '{\s*\/await\>', 'bW') +function! s:IsBlockEnd(cursyns, curline) + let cursyn_second = get(a:cursyns, 1, '') + " Some HTML tags add an extra syntax layer + let cursyn_third = get(a:cursyns, 2, '') + return a:curline !~ '^\s*$' + \ && (s:SynBlockBody(cursyn_second) + \ || s:SynBlockEnd(cursyn_second) + \ || s:SynBlockBody(cursyn_third) + \ || s:SynBlockEnd(cursyn_third)) +endfunction - if await_start - return indent(await_start) - endif - endif +function! s:SynsEOL(lnum) + let lnum = prevnonblank(a:lnum) + let col = strlen(getline(lnum)) + return map(synstack(lnum, col), 'synIDattr(v:val, "name")') +endfunction - " "/each" - if current_line =~ '^\s*{\s*\/each' - let each_start = searchpair('{\s*#each\>', '', '{\s*\/each\>', 'bW') +function! s:SynsSOL(lnum) + let lnum = prevnonblank(a:lnum) + let col = match(getline(lnum), '\S') + 1 + return map(synstack(lnum, col), 'synIDattr(v:val, "name")') +endfunction - if each_start - return indent(each_start) - endif - endif +function! s:SynHTML(syn) + return a:syn ==? 'htmlSvelteTemplate' +endfunction - " "/if" - if current_line =~ '^\s*{\s*\/if' - let if_start = searchpair('{\s*#if\>', '', '{\s*\/if\>', 'bW') +function! s:SynBlockBody(syn) + return a:syn ==? 'svelteBlockBody' +endfunction - if if_start - return indent(if_start) - endif - endif +function! s:SynBlockStart(syn) + return a:syn ==? 'svelteBlockStart' +endfunction - " ":else" is tricky because it can match an opening "#each" _or_ an opening - " "#if", so we try to be smart and look for the closest of the two. - if current_line =~ '^\s*{\s*:else' - let if_start = searchpair('{\s*#if\>', '', '{\s*\/if\>', 'bW') +function! s:SynBlockEnd(syn) + return a:syn ==? 'svelteBlockEnd' +endfunction - " If it's an "else if" then we know to look for an "#if" - if current_line =~ '^\s*{\s*:else if' && if_start - return indent(if_start) - else - " The greater line number will be closer to the cursor position because - " we're searching backward. - return indent(max([if_start, searchpair('{\s*#each\>', '', '{\s*\/each\>', 'bW')])) - endif - endif +function! s:SynPug(syn) + return a:syn ==? 'pugSvelteTemplate' +endfunction - " "#if" or "#each" - if previous_line =~ '^\s*{\s*#\(if\|each\|await\)' - return previous_line_indent + shiftwidth() - endif +function! s:SynCoffee(syn) + return a:syn ==? 'coffeeSvelteScript' +endfunction - " ":else" or ":then" - if previous_line =~ '^\s*{\s*:\(else\|catch\|then\)' - return previous_line_indent + shiftwidth() - endif +function! s:SynTypeScript(syn) + return a:syn ==? 'typescriptSvelteScript' +endfunction + +function! s:SynSASS(syn) + return a:syn ==? 'cssSassSvelteStyle' +endfunction + +function! s:SynStyle(syn) + return a:syn =~? 'SvelteStyle' +endfunction + +function! s:SynSvelteScriptOrStyle(syn) + return a:syn =~? '\v(SvelteStyle)|(SvelteScript)' +endfunction - " Custom element juggling for abnormal self-closing tags (<Widget />), - " capitalized component tags (<Widget></Widget>), and custom svelte tags - " (<svelte:head></svelte:head>). - if synID(previous_line_number, match(previous_line, '\S') + 1, 0) == hlID('htmlTag') - \ && synID(current_line_number, match(current_line, '\S') + 1, 0) != hlID('htmlEndTag') - let indents_match = indent == previous_line_indent - let previous_closes = previous_line =~ '/>$' - - if indents_match && !previous_closes && previous_line =~ '<\(\u\|\l\+:\l\+\)' - return previous_line_indent + shiftwidth() - elseif !indents_match && previous_closes - return previous_line_indent +function! s:PrevMultilineEmptyTag(lnum) + let lnum = a:lnum - 1 + let lnums = [0, 0] + while lnum > 0 + let line = getline(lnum) + if line =~? s:empty_tag_end + let lnums[1] = lnum endif - endif - return indent + if line =~? s:tag_start + if line =~? s:empty_tag_start + let lnums[0] = lnum + return lnums + else + return [0, 0] + endif + endif + + let lnum = lnum - 1 + endwhile endfunction + +function! s:Log(msg) + if s:debug + echom '['.s:name.']['.v:lnum.'] '.a:msg + endif +endfunction +"}}} + +let b:did_indent = 1 +" vim: fdm=marker |