diff options
author | Adam Stankiewicz <sheerun@sher.pl> | 2021-04-14 12:11:13 +0200 |
---|---|---|
committer | Adam Stankiewicz <sheerun@sher.pl> | 2021-04-14 12:11:26 +0200 |
commit | 067e07e137d26ca6bc3f8496fe7bd569500d7fa4 (patch) | |
tree | 55e340bc5698fd426894568a0052db5ebe5e09b4 | |
parent | 5de7022bcb4c7a155952dbeb666f912db3bde2af (diff) | |
download | vim-polyglot-067e07e137d26ca6bc3f8496fe7bd569500d7fa4.tar.gz vim-polyglot-067e07e137d26ca6bc3f8496fe7bd569500d7fa4.zip |
Change svelte provider, fixes #700
-rw-r--r-- | README.md | 2 | ||||
-rw-r--r-- | autoload/svelte.vim | 71 | ||||
-rw-r--r-- | ftplugin/svelte.vim | 50 | ||||
-rw-r--r-- | ftplugin/svelte/fold.vim | 131 | ||||
-rw-r--r-- | indent/svelte.vim | 373 | ||||
-rw-r--r-- | packages.yaml | 2 | ||||
-rw-r--r-- | syntax/svelte-html.vim | 127 | ||||
-rw-r--r-- | syntax/svelte.vim | 341 |
8 files changed, 850 insertions, 247 deletions
@@ -176,7 +176,7 @@ On top of all language packs from [vim repository](https://github.com/vim/vim/tr - [smt2](https://github.com/bohlender/vim-smt2) (SMT syntax highlighting for smt2 and smt files) - [solidity](https://github.com/TovarishFin/vim-solidity) (Solidity syntax highlighting for sol files) - [stylus](https://github.com/wavded/vim-stylus) (Stylus syntax highlighting for styl and stylus files) -- [svelte](https://github.com/evanleck/vim-svelte/tree/main) (Svelte syntax highlighting for svelte files) +- [svelte](https://github.com/leafOfTree/vim-svelte-plugin) (Svelte syntax highlighting for svelte files) - [svg-indent](https://github.com/jasonshell/vim-svg-indent) - [svg](https://github.com/vim-scripts/svg.vim) (SVG syntax highlighting for svg files) - [swift](https://github.com/keith/swift.vim) (Swift syntax highlighting for swift files) diff --git a/autoload/svelte.vim b/autoload/svelte.vim new file mode 100644 index 00000000..60d31469 --- /dev/null +++ b/autoload/svelte.vim @@ -0,0 +1,71 @@ +if polyglot#init#is_disabled(expand('<sfile>:p'), 'svelte', 'autoload/svelte.vim') + finish +endif + +let s:name = 'vim-svelte-plugin' +let s:debug = exists("g:vim_svelte_plugin_debug") + \ && g:vim_svelte_plugin_debug == 1 + +function! svelte#GetConfig(name, default) + let name = 'g:vim_svelte_plugin_'.a:name + return exists(name) ? eval(name) : a:default +endfunction + +" Since svelte#Log and svelte#GetConfig are always called +" in syntax and indent files, +" this file will be sourced when opening the first svelte file +if exists('##CursorMoved') && exists('*OnChangeSvelteSubtype') + augroup vim_svelte_plugin + autocmd! + autocmd CursorMoved,CursorMovedI,WinEnter *.svelte + \ call s:CheckSubtype() + augroup END + + let s:subtype = '' + function! s:CheckSubtype() + let subtype = GetSvelteSubtype() + + if s:subtype != subtype + call OnChangeSvelteSubtype(subtype) + let s:subtype = subtype + endif + endfunction +endif + +function! s:SynsEOL(lnum) + let lnum = prevnonblank(a:lnum) + let cnum = strlen(getline(lnum)) + return map(synstack(lnum, cnum), 'synIDattr(v:val, "name")') +endfunction + +function! GetSvelteSubtype() + let lnum = line('.') + let cursyns = s:SynsEOL(lnum) + let syn = !empty(cursyns) ? get(cursyns, 0, '') : '' + + let subtype = matchstr(syn, '\w\+\zeSvelte') + if subtype =~ 'css\w\+' + " For cssScss, cssLess, ... + let subtype = subtype[3:] + endif + let subtype = tolower(subtype) + return subtype +endfunction + +function! GetSvelteTag(...) + let lnum = a:0 > 0 ? a:1 : line('.') + let cursyns = s:SynsEOL(lnum) + let syn = get(cursyns, 0, '') + + if syn =~ 'SvelteTemplate' + let tag = 'template' + elseif syn =~ 'SvelteScript' + let tag = 'script' + elseif syn =~ 'SvelteStyle' + let tag = 'style' + else + let tag = '' + endif + + return tag +endfunction diff --git a/ftplugin/svelte.vim b/ftplugin/svelte.vim index cf32ef5b..051e085d 100644 --- a/ftplugin/svelte.vim +++ b/ftplugin/svelte.vim @@ -2,46 +2,24 @@ if polyglot#init#is_disabled(expand('<sfile>:p'), 'svelte', 'ftplugin/svelte.vim finish endif -" Vim filetype plugin -" Language: Svelte 3 (HTML/JavaScript) -" Author: Evan Lecklider <evan@lecklider.com> -" Maintainer: Evan Lecklide <evan@lecklider.com> -" URL: https://github.com/evanleck/vim-svelte -if (exists('b:did_ftplugin')) - finish -endif +if exists("b:did_ftplugin") | finish | endif let b:did_ftplugin = 1 -" Matchit support -if exists('loaded_matchit') && !exists('b:match_words') - let b:match_ignorecase = 0 +if !has('nvim') + setlocal matchpairs+=<:> +endif - " In order: - " - " 1. Svelte control flow keywords. - " 2. Parens. - " 3-5. HTML tags pulled from Vim itself. - " - " https://github.com/vim/vim/blob/5259275347667a90fb88d8ea74331f88ad68edfc/runtime/ftplugin/html.vim#L29-L35 - let b:match_words = - \ '#\%(if\|await\|each\)\>:\:\%(else\|catch\|then\)\>:\/\%(if\|await\|each\)\>,' . - \ '{:},' . +if exists("loaded_matchit") + let b:match_ignorecase = 1 + let b:match_words = '<:>,' . \ '<\@<=[ou]l\>[^>]*\%(>\|$\):<\@<=li\>:<\@<=/[ou]l>,' . \ '<\@<=dl\>[^>]*\%(>\|$\):<\@<=d[td]\>:<\@<=/dl>,' . - \ '<\@<=\([^/][^ \t>]*\)[^>]*\%(>\|$\):<\@<=/\1>' + \ '<\@<=\([^/][^ \t>]*\)[^>]*\%(>\|$\):<\@<=/\1>,' . + \ '{#\(if\|each\)[^}]*}:{\:else[^}]*}:{\/\(if\|each\)},' . + \ '{#await[^}]*}:{\:then[^}]*}:{\/await},' endif -" ALE fixing and linting. -if exists('g:loaded_ale') - if !exists('b:ale_fixers') - let b:ale_fixers = ['eslint', 'prettier', 'prettier_standard'] - endif - - if !exists('b:ale_linter_aliases') - let b:ale_linter_aliases = ['css', 'javascript'] - endif - - if !exists('b:ale_linters') - let b:ale_linters = ['stylelint', 'eslint'] - endif -endif +" Indent correctly with template string for vim-javascript/builtin +" indentexpr +let b:syng_str = '^\%(.*template\)\@!.*string\|special' +let b:syng_strcom = '^\%(.*template\)\@!.*string\|comment\|regex\|special\|doc' diff --git a/ftplugin/svelte/fold.vim b/ftplugin/svelte/fold.vim new file mode 100644 index 00000000..2732660a --- /dev/null +++ b/ftplugin/svelte/fold.vim @@ -0,0 +1,131 @@ +if polyglot#init#is_disabled(expand('<sfile>:p'), 'svelte', 'ftplugin/svelte/fold.vim') + finish +endif + +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +" +" Config {{{ +" +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +let s:use_foldexpr = exists("g:vim_svelte_plugin_use_foldexpr") + \ && g:vim_svelte_plugin_use_foldexpr == 1 +"}}} + +if !s:use_foldexpr | finish | endif + +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +" +" Settings {{{ +" +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +setlocal foldmethod=expr +setlocal foldexpr=GetSvelteFold(v:lnum) +"}}} + +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +" +" Variables {{{ +" +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +let s:empty_line = '\v^\s*$' +let s:block_end = '\v^\s*}|]|\)' +let s:svelte_tag_start = '\v^\<\w+' +let s:svelte_tag_end = '\v^\<\/\w+' +let s:svelte_internal_blocks = '\v:(else|then|catch)' +"}}} + +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +" +" Functions {{{ +" +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +" see :h fold-expr +" value meaning +" 0 the line is not in a fold +" 1, 2, .. the line is in a fold with this level +" -1 the fold level is undefined, use the fold level of a +" line before or after this line, whichever is the +" lowest. +" "=" use fold level from the previous line +" "a1", "a2", .. add one, two, .. to the fold level of the previous +" line, use the result for the current line +" "s1", "s2", .. subtract one, two, .. from the fold level of the +" previous line, use the result for the next line +" ">1", ">2", .. a fold with this level starts at this line +" "<1", "<2", .. a fold with this level ends at this line +function! GetSvelteFold(lnum) + let this_line = getline(a:lnum) + let next_line = getline(a:lnum + 1) + + " Handle empty lines + if this_line =~ s:empty_line + return -1 + endif + + " Handle start/end tags + if this_line =~ s:svelte_tag_start + return '>1' + endif + if this_line =~ s:svelte_tag_end + " If return '<1', fold will get incorrect with prev line + return 1 + endif + + " Fold by indent + if a:lnum > 1 + let prev_indent = s:IndentLevel(a:lnum - 1) + else + let prev_indent = 0 + endif + let this_indent = s:IndentLevel(a:lnum) + let next_indent = s:IndentLevel(s:NextNonBlankLine(a:lnum)) + + " Fold separately on blocks + if this_line =~ s:svelte_internal_blocks + return '>'.next_indent + endif + + if GetSvelteTag(a:lnum) == 'script' + " Handle closing '}' + if this_line =~ '\v^\s*},?\s*$' + return '<'.prev_indent + endif + + " --this + " ----next + if this_indent < next_indent + return '>'.next_indent + endif + + " ----this + " --next + if this_indent >= next_indent + return this_indent + endif + else + " Template or style + return this_indent + endif +endfunction + +function! s:IndentLevel(lnum) + " Add 1 to indentLevel, so start/end tags can fold properly + return indent(a:lnum) / &shiftwidth + 1 +endfunction + +function! s:NextNonBlankLine(lnum) + let next_line = a:lnum + 1 + let last_line = line('$') + + while next_line <= last_line + if getline(next_line) =~ '\v\S' + return next_line + endif + + let next_line += 1 + endwhile + + return 0 +endfunction +"}}} +" vim: fdm=marker 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 diff --git a/packages.yaml b/packages.yaml index d244ef49..62fda539 100644 --- a/packages.yaml +++ b/packages.yaml @@ -1574,7 +1574,7 @@ filetypes: - stylus --- name: svelte -remote: evanleck/vim-svelte@main +remote: leafOfTree/vim-svelte-plugin filetypes: - name: svelte linguist: Svelte diff --git a/syntax/svelte-html.vim b/syntax/svelte-html.vim new file mode 100644 index 00000000..c0b243b1 --- /dev/null +++ b/syntax/svelte-html.vim @@ -0,0 +1,127 @@ +if polyglot#init#is_disabled(expand('<sfile>:p'), 'svelte', 'syntax/svelte-html.vim') + finish +endif + +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +" +" Config {{{ +" +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +let s:highlight_svelte_attr = svelte#GetConfig('highlight_svelte_attr', 0) +")}}} + +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +" +" Syntax highlight {{{ +" +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +syntax match svelteComponentName containedin=htmlTagN '\v\C<[a-zA-Z0-9]+(\.[A-Z][a-zA-Z0-9]+)*>' + +syntax match svelteComponentName containedin=htmlTagN '\v\C<[a-z0-9]+(-[a-z0-9]+)+>' + +syntax match svelteComponentName containedin=htmlTagN '\vsvelte:\w*' + +" Syntax for vim-svelte-theme +syntax match htmlAttr '\v(\S|\<)@<![^\/\<\>[:blank:]]+' containedin=htmlTag + \ contains=htmlString,svelteValue,htmlArg +syntax match htmlAttrEqual '\v\=' containedin=htmlAttr + +syntax match svelteAttr + \ '\v(\S)@<!(on|bind|use|in|out|transition|animate|class):[^\=\>[:blank:]]+(\=\"[^"]*\"|\=\{[^}]*\})?' + \ containedin=htmlTag + \ contains=svelteKey,svelteValue + +syntax match svelteKey contained '\v(on|bind|use|in|out|transition|animate|class):[^\=\>[:blank:]]+' +syntax match svelteValue contained '\v\{[^}]*\}' + +syntax region svelteExpression + \ containedin=htmlH.*,htmlItalic + \ matchgroup=svelteBrace + \ transparent + \ start="{" + \ end="}\(}\)\@!" + +syntax region svelteExpression + \ containedin=htmlSvelteTemplate,svelteValue,htmlString,htmlValue,htmlArg,htmlTag + \ contains=@simpleJavascriptExpression,svelteAtTags + \ matchgroup=svelteBrace + \ transparent + \ start="{" + \ end="}\(}\)\@!" + +syntax region svelteExpression + \ containedin=htmlTag + \ contains=@simpleJavascriptExpression,svelteAtTags,svelteShortProp + \ matchgroup=svelteBrace + \ transparent + \ start="{" + \ end="}\(}\)\@!" + +syntax match svelteAtTags '\v\@(html|debug)' +syntax match svelteShortProp '\v<\w+>' + +syntax region svelteBlockBody + \ containedin=htmlSvelteTemplate,htmlLink + \ contains=@simpleJavascriptExpression,svelteBlockKeyword + \ matchgroup=svelteBrace + \ start="{:" + \ end="}" + +syntax region svelteBlockStart + \ containedin=htmlSvelteTemplate,htmlLink + \ contains=@simpleJavascriptExpression,svelteBlockKeyword + \ matchgroup=svelteBrace + \ start="{#" + \ end="}" + +syntax region svelteBlockEnd + \ containedin=htmlSvelteTemplate,htmlLink + \ contains=@simpleJavascriptExpression,svelteBlockKeyword + \ matchgroup=svelteBrace + \ start="{\/" + \ end="}" + +syntax keyword svelteBlockKeyword if else each await then catch as + +syntax cluster simpleJavascriptExpression + \ contains=javaScriptStringS,javaScriptStringD,javaScriptTemplateString,javascriptNumber,javaScriptOperator + +" Redefine JavaScript syntax +syntax region javaScriptStringS + \ start=+'+ skip=+\\\\\|\\'+ end=+'\|$+ contained +syntax region javaScriptStringD + \ start=+"+ skip=+\\\\\|\\"+ end=+"\|$+ contained +syntax region javaScriptTemplateString + \ start=+`+ skip=+\\`+ end=+`+ contained + \ contains=javaScriptTemplateExpression +syntax region javaScriptTemplateExpression + \ matchgroup=Type + \ start=+${+ end=+}+ keepend contained + +syntax match javaScriptNumber '\v<-?\d+L?>|0[xX][0-9a-fA-F]+>' contained +syntax match javaScriptOperator '[-!|&+<>=%*~^]' contained +syntax match javaScriptOperator '\v(*)@<!/(/|*)@!' contained +syntax keyword javaScriptOperator delete instanceof typeof void new in of contained + +highlight default link svelteAttr htmlTag +if s:highlight_svelte_attr + highlight default link svelteKey Type + highlight default link svelteValue None +else + highlight default link svelteKey htmlArg + highlight default link svelteValue String +endif + +highlight default link svelteBrace Type +highlight default link svelteBlockKeyword Statement +highlight default link svelteComponentName htmlTagName +highlight default link javaScriptTemplateString String +highlight default link javaScriptStringS String +highlight default link javaScriptStringD String +highlight default link javaScriptNumber Constant +highlight default link javaScriptOperator Operator +highlight default link svelteAttr htmlTag +highlight default link svelteAttrEqual htmlTag +highlight default link svelteShortProp htmlValue +"}}} +" vim: fdm=marker diff --git a/syntax/svelte.vim b/syntax/svelte.vim index d872826c..f67c8caf 100644 --- a/syntax/svelte.vim +++ b/syntax/svelte.vim @@ -2,123 +2,278 @@ if polyglot#init#is_disabled(expand('<sfile>:p'), 'svelte', 'syntax/svelte.vim') finish endif +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" " Vim syntax file -" Language: Svelte 3 (HTML/JavaScript) -" Author: Evan Lecklider <evan@lecklider.com> -" Maintainer: Evan Lecklide <evan@lecklider.com> -" Depends: pangloss/vim-javascript -" URL: https://github.com/evanleck/vim-svelte " -" Like vim-jsx, this depends on the pangloss/vim-javascript syntax package (and -" is tested against it exclusively). If you're using vim-polyglot, then you're -" all set. - -if exists("b:current_syntax") +" Language: Svelte +" Maintainer: leaf <leafvocation@gmail.com> +" +" CREDITS: Inspired by mxw/vim-jsx. +" +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +if exists("b:current_syntax") && b:current_syntax == 'svelte' finish endif -" Read HTML to begin with. -runtime! syntax/html.vim -unlet! b:current_syntax +" For advanced users, this variable can be used to avoid overload +let b:current_loading_main_syntax = 'svelte' +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +" +" Config {{{ +" +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +let s:load_full_syntax = svelte#GetConfig('load_full_syntax', 0) +let s:use_pug = svelte#GetConfig('use_pug', 0) +let s:use_less = svelte#GetConfig('use_less', 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) +"}}} -" Expand HTML tag names to include mixed case, periods, and colons. -syntax match htmlTagName contained "\<[a-zA-Z:\.]*\>" +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +" +" Functions {{{ +" +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +function! s:LoadSyntax(group, type) + if s:load_full_syntax + call s:LoadFullSyntax(a:group, a:type) + else + call s:LoadDefaultSyntax(a:group, a:type) + endif +endfunction -" Special attributes that include some kind of binding e.g. "on:click", -" "bind:something", etc. -syntax match svelteKeyword "\<[a-z]\+:[a-zA-Z|]\+=" contained containedin=htmlTag +function! s:LoadDefaultSyntax(group, type) + unlet! b:current_syntax + let syntaxPaths = ['$VIMRUNTIME', '$VIM/vimfiles', '$HOME/.vim'] + for path in syntaxPaths + let file = expand(path).'/syntax/'.a:type.'.vim' + if filereadable(file) + execute 'syntax include '.a:group.' '.file + endif + endfor +endfunction -" The "slot" attribute has special meaning. -syntax keyword svelteKeyword slot contained containedin=htmlTag +function! s:LoadFullSyntax(group, type) + call s:SetCurrentSyntax(a:type) + exec 'syntax include '.a:group.' syntax/'.a:type.'.vim' +endfunction -" According to vim-jsx, you can let jsBlock take care of ending the region. -" https://github.com/mxw/vim-jsx/blob/master/after/syntax/jsx.vim -syntax region svelteExpression start="{" end="" contains=jsBlock,javascriptBlock containedin=htmlString,htmlTag,htmlArg,htmlValue,htmlH1,htmlH2,htmlH3,htmlH4,htmlH5,htmlH6,htmlHead,htmlTitle,htmlBoldItalicUnderline,htmlUnderlineBold,htmlUnderlineItalicBold,htmlUnderlineBoldItalic,htmlItalicUnderline,htmlItalicBold,htmlItalicBoldUnderline,htmlItalicUnderlineBold,htmlLink,htmlLeadingSpace,htmlBold,htmlBoldUnderline,htmlBoldItalic,htmlBoldUnderlineItalic,htmlUnderline,htmlUnderlineItalic,htmlItalic,htmlStrike,javaScript +" Settings to avoid syntax overload +function! s:SetCurrentSyntax(type) + if a:type == 'coffee' + syntax cluster coffeeJS contains=@htmlJavaScript -" Block conditionals. -syntax match svelteConditional "#if" contained containedin=jsBlock,javascriptBlock -syntax match svelteConditional "/if" contained containedin=jsBlock,javascriptBlock -syntax match svelteConditional ":else if" contained containedin=jsBlock,javascriptBlock -syntax match svelteConditional ":else" contained containedin=jsBlock,javascriptBlock + " Avoid overload of `javascript.vim` + let b:current_syntax = 'svelte' + else + unlet! b:current_syntax + endif +endfunction +"}}} -" Block keywords. -syntax match svelteKeyword "#await" contained containedin=jsBlock,javascriptBlock -syntax match svelteKeyword "/await" contained containedin=jsBlock,javascriptBlock -syntax match svelteKeyword ":catch" contained containedin=jsBlock,javascriptBlock -syntax match svelteKeyword ":then" contained containedin=jsBlock,javascriptBlock +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +" +" Load main syntax {{{ +" +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +" Load syntax/html.vim to syntax group, which loads full JavaScript and CSS +" syntax. It defines group htmlJavaScript and htmlCss. +call s:LoadSyntax('@HTMLSyntax', 'html') -" Inline keywords. -syntax match svelteKeyword "@html" contained containedin=jsBlock,javascriptBlock -syntax match svelteKeyword "@debug" contained containedin=jsBlock,javascriptBlock +" Load svelte-html syntax +syntax include syntax/svelte-html.vim -" Repeat functions. -syntax match svelteRepeat "#each" contained containedin=jsBlock,javascriptBlock -syntax match svelteRepeat "/each" contained containedin=jsBlock,javascriptBlock +" Avoid overload +if !hlexists('cssTagName') + call s:LoadSyntax('@htmlCss', 'css') +endif -highlight def link svelteConditional Conditional -highlight def link svelteKeyword Keyword -highlight def link svelteRepeat Repeat +" Avoid overload +if !hlexists('javaScriptComment') + call s:LoadSyntax('@htmlJavaScript', 'javascript') +endif +"}}} -" Preprocessed languages that aren't supported out of the box by Svelte require -" additional syntax files to be pulled in and can slow Vim down a bit. For that -" reason, preprocessed languages must be enabled manually. Note that some may -" require additional plugins that contain the actual syntax definitions. +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" " -" Heavily cribbed from https://github.com/posva/vim-vue and largely completed by -" @davidroeca (thank you!). - -" A syntax should be registered if there's a valid syntax definition known to -" Vim and it is enabled for the Svelte plugin. -function! s:enabled(language) - " Check whether a syntax file for {language} exists - let s:syntax_name = get(a:language, 'as', a:language.name) - if empty(globpath(&runtimepath, 'syntax/' . s:syntax_name . '.vim')) - return 0 - endif +" Load pre-processors syntax {{{ +" +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +" If pug is enabled, load vim-pug syntax +if s:use_pug + call s:LoadFullSyntax('@PugSyntax', 'pug') +endif + +" If less is enabled, load less syntax +if s:use_less + call s:LoadSyntax('@LessSyntax', 'less') + runtime! after/syntax/less.vim +endif - " If g:svelte_preprocessors is set, check for it there, otherwise return 0. - if exists('g:svelte_preprocessors') && type(g:svelte_preprocessors) == v:t_list - return index(g:svelte_preprocessors, a:language.name) != -1 +" If sass is enabled, load sass syntax +if s:use_sass + call s:LoadSyntax('@SassSyntax', 'sass') + runtime! after/syntax/sass.vim +endif + +" If CoffeeScript is enabled, load the syntax. Keep name consistent with +" vim-coffee-script/after/html.vim +if s:use_coffee + call s:LoadFullSyntax('@htmlCoffeeScript', 'coffee') +endif + +" If TypeScript is enabled, load the syntax. +if s:use_typescript + call s:LoadFullSyntax('@TypeScript', 'typescript') +endif +"}}} + +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +" +" Syntax highlight {{{ +" +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +" All start with html/javascript/css for emmet-vim in-file type detection +" Normal tag template +syntax region htmlSvelteTemplate fold + \ start=+<[-:a-zA-Z0-9]\+[^>]*>$+ + \ end=+^</[-:a-zA-Z0-9]\+>+ + \ keepend contains=@HTMLSyntax +" Start tag across multiple lines or Empty tag across multiple lines +syntax region htmlSvelteTemplate fold + \ start=+<[-:a-zA-Z0-9]\+[^>]*$+ + \ end=+^\(<\/[-:a-zA-Z0-9]\+>\)\|^\([^<]*\/>\)+ + \ keepend contains=@HTMLSyntax +" Tag in one line +syntax match htmlSvelteTemplate fold + \ +<[-:a-zA-Z0-9]\+[^>]*>.*</[-:a-zA-Z0-9]\+>+ + \ contains=@HTMLSyntax +" Empty tag in one line +syntax match htmlSvelteTemplate fold + \ +<[-:a-zA-Z0-9]\+[^>]*/>+ + \ contains=@HTMLSyntax +" @html,@debug tag in one line +syntax match htmlSvelteTemplate fold + \ +{@\(html\|debug\)[^}]*}+ + \ contains=@HTMLSyntax +" Control blocks like {#if ...}, {#each ...} +syntax region htmlSvelteTemplate fold + \ start=+{#[-a-zA-Z0-9]\+[^}]*}+ + \ end=+^{/[-a-zA-Z0-9]\+}+ + \ keepend contains=@HTMLSyntax + +syntax region javascriptSvelteScript fold + \ start=+<script[^>]*>+ + \ end=+</script>+ + \ keepend + \ contains=@htmlJavaScript,jsImport,jsExport,svelteTag,svelteKeyword + +syntax region cssSvelteStyle fold + \ start=+<style[^>]*>+ + \ end=+</style>+ + \ keepend contains=@htmlCss,svelteTag + +" Preprocessors syntax +syntax region pugSvelteTemplate fold + \ start=+<template[^>]*lang="pug"[^>]*>+ + \ end=+</template>+ + \ keepend contains=@PugSyntax,svelteTag + +syntax region coffeeSvelteScript fold + \ start=+<script[^>]*lang="coffee"[^>]*>+ + \ end=+</script>+ + \ keepend contains=@htmlCoffeeScript,jsImport,jsExport,svelteTag + +syntax region typescriptSvelteScript fold + \ start=+<script[^>]*lang="\(ts\|typescript\)"[^>]*>+ + \ end=+</script>+ + \ keepend contains=@TypeScript,svelteTag + +syntax region cssLessSvelteStyle fold + \ start=+<style[^>]*lang="less"[^>]*>+ + \ end=+</style>+ + \ keepend contains=@LessSyntax,svelteTag +syntax region cssSassSvelteStyle fold + \ start=+<style[^>]*lang="sass"[^>]*>+ + \ end=+</style>+ + \ keepend contains=@SassSyntax,svelteTag +syntax region cssScssSvelteStyle fold + \ start=+<style[^>]*lang="scss"[^>]*>+ + \ end=+</style>+ + \ keepend contains=@SassSyntax,svelteTag + +syntax region svelteTag + \ start=+^<[^/]+ end=+>+ skip=+></+ + \ contained contains=htmlTagN,htmlString,htmlArg fold +syntax region svelteTag + \ start=+^</+ end=+>+ + \ contained contains=htmlTagN,htmlString,htmlArg +syntax keyword svelteKeyword $ contained + +highlight default link svelteTag htmlTag +highlight default link svelteKeyword Keyword +highlight default link cssUnitDecorators2 Number +highlight default link cssKeyFrameProp2 Constant +"}}} + +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +" +" Syntax patch {{{ +" +""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" +" Patch 7.4.1142 +if has("patch-7.4-1142") + if has("win32") + syn iskeyword @,48-57,_,128-167,224-235,$ else - return 0 + syn iskeyword @,48-57,_,192-255,$ endif -endfunction +endif -" Default tag definitions. -let s:languages = [ - \ { 'name': 'less', 'tag': 'style' }, - \ { 'name': 'scss', 'tag': 'style' }, - \ { 'name': 'sass', 'tag': 'style' }, - \ { 'name': 'stylus', 'tag': 'style' }, - \ { 'name': 'typescript', 'tag': 'script' }, - \ ] - -" Add global tag definitions to our defaults. -if exists('g:svelte_preprocessor_tags') && type(g:svelte_preprocessor_tags) == v:t_list - let s:languages += g:svelte_preprocessor_tags +" Style +" Redefine (less|sass)Definition to highlight <style> correctly and +" enable emmet-vim css type. +if s:use_less + silent! syntax clear lessDefinition + syntax region cssLessDefinition matchgroup=cssBraces + \ contains=@LessSyntax,cssLessDefinition + \ contained containedin=cssLessSvelteStyle + \ start="{" end="}" +endif +if s:use_sass + silent! syntax clear sassDefinition + syntax region cssSassDefinition matchgroup=cssBraces + \ contains=@SassSyntax,cssSassDefinition + \ contained containedin=cssScssSvelteStyle,cssSassSvelteStyle + \ start="{" end="}" endif -for s:language in s:languages - let s:attr = '\(lang\|type\)=\("\|''\)[^\2]*' . s:language.name . '[^\2]*\2' - let s:start = '<' . s:language.tag . '\>\_[^>]*' . s:attr . '\_[^>]*>' +" Avoid css syntax interference +silent! syntax clear cssUnitDecorators +" Have to use a different name +syntax match cssUnitDecorators2 + \ /\(#\|-\|+\|%\|mm\|cm\|in\|pt\|pc\|em\|ex\|px\|ch\|rem\|vh\|vw\|vmin\|vmax\|dpi\|dppx\|dpcm\|Hz\|kHz\|s\|ms\|deg\|grad\|rad\)\ze\(;\|$\)/ + \ contained + \ containedin=cssAttrRegion,sassCssAttribute,lessCssAttribute - if s:enabled(s:language) - execute 'syntax include @' . s:language.name . ' syntax/' . get(s:language, 'as', s:language.name) . '.vim' - unlet! b:current_syntax +silent! syntax clear cssKeyFrameProp +syn match cssKeyFrameProp2 /\d*%\|from\|to/ + \ contained nextgroup=cssDefinition + \ containedin=cssAttrRegion,sassCssAttribute,lessCssAttribute - execute 'syntax region svelte_' . s:language.name - \ 'keepend' - \ 'start=/' . s:start . '/' - \ 'end="</' . s:language.tag . '>"me=s-1' - \ 'contains=@' . s:language.name . ',svelteSurroundingTag' - \ 'fold' - endif -endfor +" HTML +" Clear htmlHead that may cause highlighting out of bounds +silent! syntax clear htmlHead -syntax region svelteSurroundingTag contained start=+<\(script\|style\|template\)+ end=+>+ fold contains=htmlTagN,htmlString,htmlArg,htmlValue,htmlTagError,htmlEvent +" JavaScript +" Number with minus +syntax match javaScriptNumber '\v<-?\d+L?>|0[xX][0-9a-fA-F]+>' + \ containedin=@javascriptSvelteScript display -" Cybernetically enhanced web apps. -let b:current_syntax = "svelte" +" html5 data-* +syntax match htmlArg '\v<data(-[.a-z0-9]+)+>' containedin=@HTMLSyntax +"}}} -" Sync from start because of the wacky nesting. -syntax sync fromstart +let b:current_syntax = 'svelte' +" vim: fdm=marker |