summaryrefslogtreecommitdiffstats
path: root/indent/svelte.vim
diff options
context:
space:
mode:
authorAdam Stankiewicz <sheerun@sher.pl>2021-04-14 12:11:13 +0200
committerAdam Stankiewicz <sheerun@sher.pl>2021-04-14 12:11:26 +0200
commit067e07e137d26ca6bc3f8496fe7bd569500d7fa4 (patch)
tree55e340bc5698fd426894568a0052db5ebe5e09b4 /indent/svelte.vim
parent5de7022bcb4c7a155952dbeb666f912db3bde2af (diff)
downloadvim-polyglot-067e07e137d26ca6bc3f8496fe7bd569500d7fa4.tar.gz
vim-polyglot-067e07e137d26ca6bc3f8496fe7bd569500d7fa4.zip
Change svelte provider, fixes #700
Diffstat (limited to 'indent/svelte.vim')
-rw-r--r--indent/svelte.vim373
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