diff options
Diffstat (limited to 'autoload')
-rw-r--r-- | autoload/crystal/indent.vim | 967 | ||||
-rw-r--r-- | autoload/crystal_lang.vim | 6 | ||||
-rw-r--r-- | autoload/dart.vim | 14 | ||||
-rw-r--r-- | autoload/vital/_crystal/ColorEcho.vim | 5 | ||||
-rw-r--r-- | autoload/vital/_crystal/Data/List.vim | 6 | ||||
-rw-r--r-- | autoload/vital/_crystal/Data/String.vim | 5 | ||||
-rw-r--r-- | autoload/vital/_crystal/Process.vim | 6 | ||||
-rw-r--r-- | autoload/vital/_crystal/Web/JSON.vim | 5 |
8 files changed, 831 insertions, 183 deletions
diff --git a/autoload/crystal/indent.vim b/autoload/crystal/indent.vim index d5a02de9..96d32990 100644 --- a/autoload/crystal/indent.vim +++ b/autoload/crystal/indent.vim @@ -3,162 +3,853 @@ if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'crystal') == -1 " Variables {{{1 " ========= -" Regex of syntax group names that are strings or characters. +" Syntax group names that are strings. let g:crystal#indent#syng_string = - \ '\<crystal\%(String\|Interpolation\|NoInterpolation\|StringEscape\|CharLiteral\|ASCIICode\)\>' + \ '\<crystal\%(String\|Interpolation\|NoInterpolation\|StringEscape\)\>' lockvar g:crystal#indent#syng_string -" Regex of syntax group names that are strings, characters, symbols, -" regexps, or comments. +" Syntax group names that are strings/symbols/regexes or comments. let g:crystal#indent#syng_strcom = - \ g:crystal#indent#syng_string.'\|' . - \ '\<crystal\%(Regexp\|RegexpEscape\|Symbol\|Comment\)\>' + \ g:crystal#indent#syng_string . + \ '\|' . + \ '\<crystal\%(CharLiteral\|Comment\|Regexp\|RegexpCharClass\|RegexpEscape\|Symbol\|ASCIICode\)\>' lockvar g:crystal#indent#syng_strcom +" Syntax group names that are string/regex/symbol delimiters. +let g:crystal#indent#syng_delim = + \ '\<crystal\%(StringDelimiter\|RegexpDelimiter\|SymbolDelimiter\|InterpolationDelim\)\>' +lockvar g:crystal#indent#syng_delim + +" Syntax group that represents all of the above combined. +let g:crystal#indent#syng_strcomdelim = + \ g:crystal#indent#syng_strcom . + \ '\|' . + \ g:crystal#indent#syng_delim +lockvar g:crystal#indent#syng_strcomdelim + +" Regex for the start of a line +let g:crystal#indent#sol = '\%(\_^\|;\)\s*\zs' +lockvar g:crystal#indent#sol + +" Regex for the end of a line +let g:crystal#indent#eol = '\ze\s*\%(#.*\)\=\%(\_$\|;\)' +lockvar g:crystal#indent#eol + " Expression used to check whether we should skip a match with searchpair(). let g:crystal#indent#skip_expr = - \ 'synIDattr(synID(line("."), col("."), 1), "name") =~# "'.g:crystal#indent#syng_strcom.'"' + \ 'crystal#indent#IsInStringOrComment(line("."), col("."))' lockvar g:crystal#indent#skip_expr -" Regex for the start of a line: -" start of line + whitespace + optional opening macro delimiter -let g:crystal#indent#sol = '^\s*\zs\%(\\\={%\s*\)\=' -lockvar g:crystal#indent#sol +" Regex that defines a link attribute +let g:crystal#indent#link_attribute_regex = + \ g:crystal#indent#sol.'@\[.*]' +lockvar g:crystal#indent#link_attribute_regex -" Regex for the end of a line: -" whitespace + optional closing macro delimiter + whitespace + -" optional comment + end of line -let g:crystal#indent#eol = '\s*\%(%}\)\=\ze\s*\%(#.*\)\=$' -lockvar g:crystal#indent#eol +" Regex that defines a type declaration +let g:crystal#indent#type_declaration_regex = + \ g:crystal#indent#sol . + \ '\%(\<\%(private\|protected\)\s\+\)\=' . + \ '\%(\<\%(getter\|setter\|property\)?\=\s\+\)\=' . + \ '@\=\h\k*\s\+:\s\+\S.*' +lockvar g:crystal#indent#type_declaration_regex + +" Regex for operator symbols: +" , : / + * - = ~ < & ^ \ +" | that is not part of a block opening +" % that is not part of a macro delimiter +" ! that is not part of a method name +" ? that is not part of a method name +" > that is not part of a -> +" +" Additionally, all symbols must not be part of a global variable name, +" like $~. +let g:crystal#indent#operator_regex = + \ '\$\@1<!' . + \ '\%(' . + \ '[.,:/+*\-=~<&^\\]' . + \ '\|' . + \ '\%(\%(\<do\>\|%\@1<!{\)\s*|[^|]*\)\@<!|' . + \ '\|' . + \ '{\@1<!%' . + \ '\|' . + \ '\%(\k\|]\)\@1<!\!' . + \ '\|' . + \ '\%(\k\|]\)\@1<!?' . + \ '\|' . + \ '-\@1<!>' . + \ '\)' +lockvar g:crystal#indent#operator_regex " Regex that defines blocks. let g:crystal#indent#block_regex = - \ '\%(\<do\>\|%\@1<!{\)\s*\%(|[^|]*|\)\='.g:crystal#indent#eol + \ '\%(' . + \ '\%('.g:crystal#indent#operator_regex.'\s*\)\@<!{\@1<!{' . + \ '\|' . + \ '\<do\>' . + \ '\)' . + \ '\s*\%(|[^|]*|\)\=' . + \ g:crystal#indent#eol lockvar g:crystal#indent#block_regex +" Regex that defines the beginning of a hanging expression. +let g:crystal#indent#hanging_assignment_regex = + \ '\%('.g:crystal#indent#operator_regex.'\s*\)\@<=' . + \ '\.\@1<!\<\%(if\||unless\|case\|begin\)\>' +lockvar g:crystal#indent#hanging_assignment_regex + " Regex that defines the start-match for the 'end' keyword. -" NOTE: This *should* properly match the 'do' only at the end of the -" line let g:crystal#indent#end_start_regex = + \ '\%(' . \ g:crystal#indent#sol . \ '\%(' . \ '\%(\<\%(private\|protected\)\s\+\)\=' . - \ '\%(\<\%(abstract\s\+\)\=\%(class\|struct\)\>\|\<\%(def\|module\|macro\|lib\|enum\)\>\)' . + \ '\%(\<\%(abstract\s\+\)\=\%(class\|struct\)\>\|\<\%(def\|module\|macro\|lib\|enum\|annotation\)\>\)' . \ '\|' . - \ '\<\%(if\|unless\|while\|until\|case\|begin\|for\|union\)\>' . + \ '\<\%(if\|unless\|while\|until\|case\|begin\|union\)\>' . \ '\)' . \ '\|' . - \ g:crystal#indent#block_regex + \ g:crystal#indent#hanging_assignment_regex . + \ '\|' . + \ g:crystal#indent#block_regex . + \ '\)' lockvar g:crystal#indent#end_start_regex " Regex that defines the middle-match for the 'end' keyword. let g:crystal#indent#end_middle_regex = \ g:crystal#indent#sol . - \ '\<\%(else\|elsif\|rescue\|ensure\|when\)\>' + \ '\<\%(else\|elsif\|when\|rescue\|ensure\)\>' lockvar g:crystal#indent#end_middle_regex " Regex that defines the end-match for the 'end' keyword. let g:crystal#indent#end_end_regex = - \ g:crystal#indent#sol . - \ '\<end\>' + \ g:crystal#indent#sol.'\%(\<end\>\|%\@1<!}\@1<!}}\@!\)' lockvar g:crystal#indent#end_end_regex -" Regex used for words that add a level of indent. +" Regex used for words that, at the start of a line, add a level of indent. let g:crystal#indent#crystal_indent_keywords = \ g:crystal#indent#end_start_regex . \ '\|' . \ g:crystal#indent#end_middle_regex lockvar g:crystal#indent#crystal_indent_keywords -" Regex used for words that remove a level of indent. +" Regex used for words that, at the start of a line, remove a level of indent. let g:crystal#indent#crystal_deindent_keywords = \ g:crystal#indent#end_middle_regex . \ '\|' . \ g:crystal#indent#end_end_regex lockvar g:crystal#indent#crystal_deindent_keywords -" Regex that defines a type declaration -let g:crystal#indent#crystal_type_declaration = - \ '@\=\h\k*\s\+:\s\+\S.*' -lockvar g:crystal#indent#crystal_type_declaration +" Regex that defines hanging expressions for macro control tags. +let g:crystal#indent#macro_hanging_assignment_regex = + \ '\%('.g:crystal#indent#operator_regex.'\s*\)\@<=' . + \ '\\\=\zs{%\s*\%(if\|unless\|begin\)\>.*%}' +lockvar g:crystal#indent#macro_hanging_assignment_regex -" Regex that defines continuation lines, not including (, {, or [. -let g:crystal#indent#non_bracket_continuation_regex = +" Regex that defines the start-match for the 'end' keyword in macro +" control tags. +let g:crystal#indent#macro_end_start_regex = \ '\%(' . - \ '[\\.,:/%+\-=~<>&^]' . + \ g:crystal#indent#sol . + \ '\%(' . + \ '\\\=\zs{%\s*\%(if\|unless\|for\|begin\)\>.*%}' . \ '\|' . - \ '\%(\%(\<do\>\|%\@1<!{\)\s*|[^|]*\)\@<!|' . + \ '\\\=\zs{%.*\<do\s*%}' . + \ '\)' . \ '\|' . - \ '\%(]\|\w\)\@1<!?' . + \ g:crystal#indent#macro_hanging_assignment_regex . + \ '\)' +lockvar g:crystal#indent#macro_end_start_regex + +" Regex that defines the middle-match for the 'end' keyword in macro +" control tags. +let g:crystal#indent#macro_end_middle_regex = + \ g:crystal#indent#sol.'\\\=\zs{%\s*\%(else\|elsif\)\>.*%}' +lockvar g:crystal#indent#macro_end_middle_regex + +" Regex that defines the end-match for the 'end' keyword in macro +" control tags. +let g:crystal#indent#macro_end_end_regex = + \ g:crystal#indent#sol.'\\\=\zs{%\s*end\s*%}' +lockvar g:crystal#indent#macro_end_end_regex + +" Regex used for words that, at the start of a line, add a level of +" indent after macro control tags. +let g:crystal#indent#crystal_macro_indent_keywords = + \ g:crystal#indent#macro_end_start_regex . \ '\|' . - \ '\<\%(if\|unless\)\>' . + \ g:crystal#indent#macro_end_middle_regex +lockvar g:crystal#indent#crystal_macro_indent_keywords + +" Regex used for words that, at the start of a line, remove a level of +" indent after macro control tags. +let g:crystal#indent#crystal_macro_deindent_keywords = + \ g:crystal#indent#macro_end_middle_regex . \ '\|' . - \ '\%('.g:crystal#indent#crystal_type_declaration.'\h\k*\)\@<!\*' . - \ '\)' . - \ g:crystal#indent#eol -lockvar g:crystal#indent#non_bracket_continuation_regex + \ g:crystal#indent#macro_end_end_regex +lockvar g:crystal#indent#crystal_macro_deindent_keywords " Regex that defines bracket continuations -let g:crystal#indent#bracket_continuation_regex = '%\@1<!\%([({[]\)\s*\%(#.*\)\=$' +let g:crystal#indent#bracket_continuation_regex = + \ '%\@1<![({[]'.g:crystal#indent#eol lockvar g:crystal#indent#bracket_continuation_regex +" Regex that defines continuation lines, not including (, {, or [. +let g:crystal#indent#non_bracket_continuation_regex = + \ '\%(' . + \ g:crystal#indent#operator_regex . + \ '\|' . + \ '\<\%(if\|unless\|then\)\>' . + \ '\)' . + \ g:crystal#indent#eol +lockvar g:crystal#indent#non_bracket_continuation_regex + " Regex that defines continuation lines. let g:crystal#indent#continuation_regex = - \ g:crystal#indent#non_bracket_continuation_regex . + \ g:crystal#indent#bracket_continuation_regex . \ '\|' . - \ g:crystal#indent#bracket_continuation_regex + \ g:crystal#indent#non_bracket_continuation_regex lockvar g:crystal#indent#continuation_regex +" Regex that defines dot continuations +let g:crystal#indent#dot_continuation_regex = '\.'.g:crystal#indent#eol +lockvar g:crystal#indent#dot_continuation_regex + " Regex that defines end of bracket continuation followed by another continuation let g:crystal#indent#bracket_switch_continuation_regex = - \ '^\([^(]\+\zs).\+\)\+'.g:crystal#indent#continuation_regex + \ '^\%([^(]\+\zs).\+\)\+\%('.g:crystal#indent#continuation_regex.'\)' lockvar g:crystal#indent#bracket_switch_continuation_regex -" Regex that defines continuable keywords -let g:crystal#indent#continuable_regex = - \ '\%(^\s*\|[=,*/%+\-|;{]\|<<\|>>\|:\s\)\s*\zs' . - \ '\<\%(if\|for\|while\|until\|unless\):\@!\>' -lockvar g:crystal#indent#continuable_regex - -" Regex that defines the first part of a splat pattern -let g:crystal#indent#splat_regex = '[[,(]\s*\*\s*\%(#.*\)\=$' -lockvar g:crystal#indent#splat_regex - -let g:crystal#indent#block_continuation_regex = '^\s*[^])}\t ].*'.g:crystal#indent#block_regex +let g:crystal#indent#block_continuation_regex = + \ '^\s*[^])}\t ].*'.g:crystal#indent#block_regex lockvar g:crystal#indent#block_continuation_regex " Regex that describes a leading operator (only a method call's dot for now) -let g:crystal#indent#leading_operator_regex = '^\s*[.]' +let g:crystal#indent#leading_operator_regex = g:crystal#indent#sol.'\.' lockvar g:crystal#indent#leading_operator_regex +" Indent callbacks for the current line +let g:crystal#indent#curr_line_callbacks = [ + \ 'crystal#indent#MultilineString', + \ 'crystal#indent#ClosingBracketOnEmptyLine', + \ 'crystal#indent#DeindentingMacroTag', + \ 'crystal#indent#DeindentingKeyword', + \ 'crystal#indent#LeadingOperator' + \ ] +lockvar g:crystal#indent#curr_line_callbacks + +" Indent callbacks for the previous line +let g:crystal#indent#prev_line_callbacks = [ + \ 'crystal#indent#StartOfFile', + \ 'crystal#indent#AfterTypeDeclaration', + \ 'crystal#indent#AfterLinkAttribute', + \ 'crystal#indent#ContinuedLine', + \ 'crystal#indent#AfterBlockOpening', + \ 'crystal#indent#AfterUnbalancedBracket', + \ 'crystal#indent#AfterLeadingOperator', + \ 'crystal#indent#AfterEndMacroTag', + \ 'crystal#indent#AfterEndKeyword', + \ 'crystal#indent#AfterIndentMacroTag', + \ 'crystal#indent#AfterIndentKeyword' + \ ] +lockvar g:crystal#indent#prev_line_callbacks + +" Indent callbacks for the MSL +let g:crystal#indent#msl_callbacks = [ + \ 'crystal#indent#PreviousNotMSL', + \ 'crystal#indent#IndentingKeywordInMSL', + \ 'crystal#indent#ContinuedHangingOperator' + \ ] +lockvar g:crystal#indent#msl_callbacks + +" Indenting Logic Callbacks {{{1 +" ========================= + +function! crystal#indent#ClosingBracketOnEmptyLine(cline_info) abort + let info = a:cline_info + + " If we got a closing bracket on an empty line, find its match and indent + " according to it. For parentheses we indent to its column - 1, for the + " others we indent to the containing line's MSL's level. Return -1 if fail. + + let idx = match(info.cline, g:crystal#indent#sol.'[]})]') + + if idx >= 0 + let closing_bracket = info.cline[idx] + + if closing_bracket ==# ')' + let opening_bracket = '(' + elseif closing_bracket ==# ']' + let opening_bracket = '\[' + elseif closing_bracket ==# '}' + let opening_bracket = '{' + endif + + call searchpair( + \ opening_bracket, + \ '', + \ closing_bracket, + \ 'bW', + \ g:crystal#indent#skip_expr) + + if line('.') == info.clnum + return indent('.') + endif + + if g:crystal_indent_block_style ==# 'do' && + \ getline('.') =~# g:crystal#indent#block_regex + return col('.') - 1 + else + return indent(crystal#indent#GetMSL(line('.'))) + endif + endif + + return -1 +endfunction + +function! crystal#indent#DeindentingKeyword(cline_info) abort + let info = a:cline_info + + " If we have a deindenting keyword, find its match and indent to its level. + let idx = match(info.cline, g:crystal#indent#crystal_deindent_keywords) + + if idx >= 0 + call cursor(0, idx + 1) + + call searchpair( + \ g:crystal#indent#end_start_regex, + \ g:crystal#indent#end_middle_regex, + \ g:crystal#indent#end_end_regex, + \ 'bW', + \ g:crystal#indent#skip_expr) + + let lnum = line('.') + + " If the search did not change the current line, then either 1) the + " code is malformed or 2) the indenting keyword is on the same line + " as this one: in either case, do nothing and exit the indent + " expression. + if lnum == info.clnum + return indent('.') + endif + + " Count the number of both opening and closing macro control tags + " between this line and the starting line: if the number of + " opening tags is greater than the number of closing tags, then we + " must be inside of a macro block, so indent accordingly. + let diff = crystal#indent#RelativeMacroDepth(lnum, info.clnum) + + if diff > 0 + return indent(lnum) + info.sw * (diff + 1) + elseif diff < 0 + return indent(lnum) + info.sw * (diff - 1) + endif + + " If none of the above special cases apply, proceed normally. + let line = getline(lnum) + + if g:crystal_indent_block_style ==# 'do' && + \ line =~# g:crystal#indent#block_regex + return col('.') - 1 + elseif g:crystal_indent_assignment_style ==# 'hanging' && + \ line =~# g:crystal#indent#hanging_assignment_regex + return col('.') - 1 + else + return indent(crystal#indent#GetMSL(lnum)) + endif + endif + + return -1 +endfunction + +function! crystal#indent#DeindentingMacroTag(cline_info) abort + let info = a:cline_info + + " If we have a deindenting tag, find its match and indent to its level. + let idx = match(info.cline, g:crystal#indent#crystal_macro_deindent_keywords) + + if idx >= 0 + call cursor(0, idx + 1) + + call searchpair( + \ g:crystal#indent#macro_end_start_regex, + \ g:crystal#indent#macro_end_middle_regex, + \ g:crystal#indent#macro_end_end_regex, + \ 'bW', + \ g:crystal#indent#skip_expr) + + " If this tag was preceded by a \, we need to keep searching until + " we find a tag that also has a \. + if info.cline[idx - 1] ==# '\' + while getline('.')[col('.') - 2] !=# '\' + call searchpair( + \ g:crystal#indent#macro_end_start_regex, + \ g:crystal#indent#macro_end_middle_regex, + \ g:crystal#indent#macro_end_end_regex, + \ 'bW', + \ g:crystal#indent#skip_expr) + endwhile + + " Position the cursor on the \ for later + call cursor(0, col('.') - 1) + endif + + " If the search did not change the current line, then either 1) the + " code is malformed or 2) the indenting tag is on the same line as + " this one: in either case, do nothing and exit the indent + " expression. + if line('.') == info.clnum + return indent('.') + endif + + if g:crystal_indent_assignment_style ==# 'hanging' && + \ getline('.') =~# g:crystal#indent#macro_hanging_assignment_regex + return col('.') - 1 + else + return indent('.') + endif + endif + + return -1 +endfunction + +function! crystal#indent#MultilineString(cline_info) abort + let info = a:cline_info + + " If we are in a multi-line string, don't do anything to it. + if crystal#indent#IsInString(info.clnum, 1) + return indent('.') + endif + + return -1 +endfunction + +function! crystal#indent#LeadingOperator(cline_info) abort + let info = a:cline_info + + " If the current line starts with a leading operator, add a level of indent. + if info.cline =~# g:crystal#indent#leading_operator_regex + return indent(crystal#indent#GetMSL(info.clnum)) + info.sw + endif + + return -1 +endfunction + +function! crystal#indent#EmptyInsideString(pline_info) abort + let info = a:pline_info + + " If the line is empty and inside a string (the previous line is a string, + " too), use the previous line's indent + + let plnum = prevnonblank(info.clnum - 1) + let pline = getline(plnum) + + if info.cline =~# '^\s*$' + \ && crystal#indent#IsInString(plnum, 1) + \ && crystal#indent#IsInString(plnum, strlen(pline)) + return indent(plnum) + endif + + return -1 +endfunction + +function! crystal#indent#StartOfFile(pline_info) abort + let info = a:pline_info + + " At the start of the file use zero indent. + if info.plnum == 0 + return 0 + endif + + return -1 +endfunction + +function! crystal#indent#AfterTypeDeclaration(pline_info) abort + let info = a:pline_info + + " Short circuit if the previous line was a type declaration; this + " allows us to skip checking for type declarations before * and + " ? later on, which will save a lot of time. + + if info.pline =~# g:crystal#indent#type_declaration_regex + if info.pline =~# ','.g:crystal#indent#eol + return indent(info.plnum) + else + let idx = match(info.pline, g:crystal#indent#block_regex) + + if idx >= 0 + if g:crystal_indent_block_style ==# 'do' + return idx + info.sw + else + return indent(info.plnum) + endif + endif + + return indent(crystal#indent#GetMSL(info.plnum)) + endif + endif + + return -1 +endfunction + +function! crystal#indent#AfterLinkAttribute(pline_info) abort + let info = a:pline_info + + " Short circuit if the previous line was a link attribute. + + if info.pline =~# g:crystal#indent#link_attribute_regex + return indent(info.plnum) + endif + + return -1 +endfunction + +" Example: +" +" if foo || bar || +" baz || bing +" puts "foo" +" end +" +function! crystal#indent#ContinuedLine(pline_info) abort + let info = a:pline_info + + let idx = match(info.pline, g:crystal#indent#end_start_regex) + + if idx >= 0 && info.pline =~# g:crystal#indent#non_bracket_continuation_regex + if info.pline =~# g:crystal#indent#hanging_assignment_regex + if g:crystal_indent_assignment_style ==# 'hanging' + " hanging indent + let ind = idx + else + " align with variable + let ind = indent(info.plnum) + endif + else + let ind = indent(crystal#indent#GetMSL(info.plnum)) + endif + + return ind + info.sw * 2 + endif + + return -1 +endfunction + +function! crystal#indent#AfterBlockOpening(pline_info) abort + let info = a:pline_info + + " If the previous line ended with a block opening, add a level of indent. + let idx = match(info.pline, g:crystal#indent#block_regex) + + if idx >= 0 + if g:crystal_indent_block_style ==# 'do' + " don't align to the msl, align to the "do" + let ind = idx + info.sw + else + let plnum_msl = crystal#indent#GetMSL(info.plnum) + + if getline(plnum_msl) =~# '='.g:crystal#indent#eol + " in the case of assignment to the msl, align to the starting line, + " not to the msl + let ind = indent(info.plnum) + info.sw + else + let ind = indent(plnum_msl) + info.sw + endif + endif + + return ind + endif + + return -1 +endfunction + +function! crystal#indent#AfterLeadingOperator(pline_info) abort + let info = a:pline_info + + " If the previous line started with a leading operator, use its MSL's level + " of indent + if info.pline =~# g:crystal#indent#leading_operator_regex + return indent(crystal#indent#GetMSL(info.plnum)) + endif + + return -1 +endfunction + +function! crystal#indent#AfterUnbalancedBracket(pline_info) abort + let info = a:pline_info + + " If the previous line contained unclosed opening brackets and we are still + " in them, find the rightmost one and add indent depending on the bracket + " type. + " + " If it contained hanging closing brackets, find the rightmost one, find its + " match and indent according to that. + + if info.pline =~# '[[({]\|[])}]'.g:crystal#indent#eol + let [opening, closing] = crystal#indent#ExtraBrackets(info.plnum) + + if opening.pos != -1 + if strpart(info.pline, opening.pos + 1) =~# '^'.g:crystal#indent#eol + return indent(crystal#indent#GetMSL(info.plnum)) + info.sw + else + return opening.pos + 1 + endif + elseif closing.pos != -1 + call cursor(info.plnum, closing.pos + 1) + + if closing.type ==# ')' + let target = '(' + elseif closing.type ==# ']' + let target = '\[' + elseif closing.type ==# '}' + let target = '{' + endif + + call searchpair(target, '', closing.type, 'bW', g:crystal#indent#skip_expr) + + return indent(crystal#indent#GetMSL(line('.'))) + end + endif + + return -1 +endfunction + +function! crystal#indent#AfterEndKeyword(pline_info) abort + let info = a:pline_info + + let idx = match(info.pline, g:crystal#indent#end_end_regex) + + if idx >= 0 + if g:crystal_indent_assignment_style ==# 'variable' && + \ g:crystal_indent_block_style ==# 'expression' + " Simply align with the "end" + return idx + endif + + " Return the indent of the nearest indenting line + call cursor(info.plnum, idx + 1) + + let lnum = searchpair( + \ g:crystal#indent#end_start_regex, + \ '', + \ g:crystal#indent#end_end_regex, + \ 'bW', + \ g:crystal#indent#skip_expr) + + return indent(crystal#indent#GetMSL(lnum)) + endif + + return -1 +endfunction + +function! crystal#indent#AfterEndMacroTag(pline_info) abort + let info = a:pline_info + + " If the previous line ended with an "end" macro tag, match the indent + " of that tag's corresponding opening tag. + let idx = match(info.pline, g:crystal#indent#macro_end_end_regex) + + if idx >= 0 + call cursor(info.plnum, idx + 1) + + if g:crystal_indent_assignment_style ==# 'hanging' + let lnum = searchpair( + \ g:crystal#indent#macro_end_start_regex, + \ '', + \ g:crystal#indent#macro_end_end_regex, + \ 'bW', + \ g:crystal#indent#skip_expr) + else + let lnum = searchpair( + \ g:crystal#indent#macro_end_start_regex, + \ g:crystal#indent#macro_end_middle_regex, + \ g:crystal#indent#macro_end_end_regex, + \ 'bW', + \ g:crystal#indent#skip_expr) + endif + + return indent(lnum) + end + + return -1 +endfunction + +function! crystal#indent#AfterIndentKeyword(pline_info) abort + let info = a:pline_info + + let idx = match(info.pline, g:crystal#indent#crystal_indent_keywords) + + if idx >= 0 + " If there is an "end" after the indenting keyword on the same line, + " do nothing. + let idx2 = match(info.pline, g:crystal#indent#end_end_regex) + + if idx2 > idx + return indent('.') + endif + + if g:crystal_indent_assignment_style ==# 'hanging' && + \ info.pline =~# g:crystal#indent#hanging_assignment_regex + return idx + info.sw + else + return indent(info.plnum) + info.sw + endif + endif + + return -1 +endfunction + +function! crystal#indent#AfterIndentMacroTag(pline_info) abort + let info = a:pline_info + + let idx = match(info.pline, g:crystal#indent#crystal_macro_indent_keywords) + + if idx >= 0 + if g:crystal_indent_assignment_style ==# 'hanging' && + \ info.pline =~# g:crystal#indent#macro_hanging_assignment_regex + " If the indenting tag was preceded by a \, we must shift over an + " additional space. + let shift = info.pline[idx - 1] ==# '\' + return idx + info.sw - shift + else + return indent(info.plnum) + info.sw + endif + endif + + return -1 +endfunction + +function! crystal#indent#PreviousNotMSL(msl_info) abort + let info = a:msl_info + + if info.plnum != info.plnum_msl + if info.pline =~# g:crystal#indent#bracket_switch_continuation_regex + return indent(info.plnum) - 1 + elseif info.pline =~# g:crystal#indent#non_bracket_continuation_regex + return indent(info.plnum) + endif + endif + + return -1 +endfunction + +function! crystal#indent#IndentingKeywordInMSL(msl_info) abort + let info = a:msl_info + + " If the MSL line had an indenting keyword in it, add a level of indent. + let idx = match(info.pline_msl, g:crystal#indent#crystal_indent_keywords) + + if idx >= 0 + let ind = indent(info.plnum_msl) + info.sw + + if info.pline_msl =~# g:crystal#indent#end_end_regex + let ind = ind - info.sw + elseif info.pline =~# g:crystal#indent#hanging_assignment_regex + if g:crystal_indent_assignment_style ==# 'hanging' + " hanging indent + let ind = idx + info.sw + else + " align with variable + let ind = indent(info.plnum_msl) + info.sw + endif + endif + + return ind + endif + + return -1 +endfunction + +function! crystal#indent#ContinuedHangingOperator(msl_info) abort + let info = a:msl_info + + " If the previous line ended with an operator but wasn't a block + " ending or a closing bracket, indent one extra level. + if crystal#indent#Match(info.plnum_msl, g:crystal#indent#non_bracket_continuation_regex) && + \ info.pline_msl !~# g:crystal#indent#sol.'\%([\])}]\|\<end\>\)' + if info.plnum_msl == info.plnum + let ind = indent(info.plnum_msl) + info.sw + else + let ind = indent(info.plnum_msl) + endif + + return ind + endif + + return -1 +endfunction + " Auxiliary Functions {{{1 " =================== -" Check if the character at lnum:col is inside a string, comment, or is ascii. +" Check if the character at lnum:col is inside a string. +function! crystal#indent#IsInString(lnum, col) abort + return synIDattr(synID(a:lnum, a:col, 1), 'name') =~# g:crystal#indent#syng_string +endfunction + +" Check if the character at lnum:col is inside a string delimiter. +function! crystal#indent#IsInStringDelimiter(lnum, col) abort + return synIDattr(synID(a:lnum, a:col, 1), 'name') =~# g:crystal#indent#syng_delim +endfunction + +" Check if the character at lnum:col is inside a string, comment, regexp, etc. function! crystal#indent#IsInStringOrComment(lnum, col) abort return synIDattr(synID(a:lnum, a:col, 1), 'name') =~# g:crystal#indent#syng_strcom endfunction -" Check if the character at lnum:col is inside a string or character. -function! crystal#indent#IsInString(lnum, col) abort - return synIDattr(synID(a:lnum, a:col, 1), 'name') =~# g:crystal#indent#syng_string +" Check if the character lnum:col is inside a string, comment, regexp, +" delimiter, etc. +function! crystal#indent#IsInStringOrCommentOrDelimiter(lnum, col) abort + return synIDattr(synID(a:lnum, a:col, 1), 'name') =~# g:crystal#indent#syng_strcomdelim endfunction -" Check if the character at lnum:col is inside a string or regexp -" delimiter -function! crystal#indent#IsInStringDelimiter(lnum, col) abort - return synIDattr(synID(a:lnum, a:col, 1), 'name') =~# '\<crystal\%(StringDelimiter\|RegexpDelimiter\)\>' +function! crystal#indent#IsAssignment(str, pos) abort + return strpart(a:str, 0, a:pos - 1) =~# '=\s*$' +endfunction + +function! crystal#indent#IsLineComment(lnum) abort + return getline(a:lnum) =~# g:crystal#indent#sol.'#' +endfunction + +" Determine the relative macro block depth of one line versus another. +" 'a' and 'b' are the line numbers for said lines. +" +" For example: +" +" A return value of 2 would indicate that line A is inside two macro +" blocks relative to line B. +" +" A return value of -2 would indicate that line B is inside two macro +" blocks relative to line A. +" +" A return value of 0 would indicate that line A and line B are either +" in the same macro block or at the same relative macro block depth. +" +" NOTE: It is assumed that a < b - 2; otherwise, the return value will +" always be 0. +function! crystal#indent#RelativeMacroDepth(a, b) abort + let diff = 0 + + for i in range(a:a + 1, a:b - 1) + if crystal#indent#Match(i, g:crystal#indent#macro_end_start_regex) + let diff += 1 + elseif crystal#indent#Match(i, g:crystal#indent#macro_end_end_regex) + let diff -= 1 + endif + endfor + + return diff endfunction -" Find line above 'lnum' that isn't empty, in a comment, or in a string. -function! crystal#indent#PrevNonBlankNonString(lnum) abort +" Wrapper for prevnonblank() that skips lines that are line comments or +" inside of multiline strings. +function! crystal#indent#PrevNonBlank(lnum) abort let lnum = prevnonblank(a:lnum) while lnum > 0 let line = getline(lnum) - let start = match(line, '\S') + let start = match(line, '\S') + 1 - if !crystal#indent#IsInStringOrComment(lnum, start + 1) + if !crystal#indent#IsInStringOrComment(lnum, start) break endif @@ -171,95 +862,104 @@ endfunction " Find line above 'lnum' that started the continuation 'lnum' may be part of. function! crystal#indent#GetMSL(lnum) abort " Start on the line we're at and use its indent. - let msl = a:lnum - let msl_body = getline(msl) - let lnum = crystal#indent#PrevNonBlankNonString(a:lnum - 1) + let mslnum = a:lnum + let lnum = crystal#indent#PrevNonBlank(a:lnum - 1) while lnum > 0 " If we have a continuation line, or we're in a string, use line as MSL. " Otherwise, terminate search as we have found our MSL already. + let msl = getline(mslnum) let line = getline(lnum) - if crystal#indent#Match(msl, g:crystal#indent#leading_operator_regex) + if msl =~# g:crystal#indent#leading_operator_regex " If the current line starts with a leading operator, keep its indent " and keep looking for an MSL. - let msl = lnum - elseif crystal#indent#Match(lnum, g:crystal#indent#splat_regex) - " If the above line looks like the "*" of a splat, use the current one's - " indentation. + let mslnum = lnum + elseif line =~# g:crystal#indent#type_declaration_regex && + \ line !~# ','.g:crystal#indent#eol && + \ line !~# g:crystal#indent#block_regex + " If the previous line is a type declaration that doesn't end with + " a comman or a block opening, it is the MSL. " " Example: - " Hash[* - " method_call do - " something + " record ColorRGB, + " red : UInt8, + " green : UInt8, + " blue : UInt8 do + " def fore(io : IO) : Nil + " io << "38;2;" + " io << red << ";" + " io << green << ";" + " io << blue + " end + " end " - return msl - elseif crystal#indent#Match(lnum, g:crystal#indent#non_bracket_continuation_regex) && - \ crystal#indent#Match(msl, g:crystal#indent#non_bracket_continuation_regex) + return mslnum + elseif line =~# g:crystal#indent#non_bracket_continuation_regex && + \ msl =~# g:crystal#indent#non_bracket_continuation_regex " If the current line is a non-bracket continuation and so is the - " previous one, keep its indent and continue looking for an MSL. + " previous one, keep its indent and continue looking for an mslnum. " " Example: " method_call one, " two, " three " - let msl = lnum - elseif crystal#indent#Match(lnum, g:crystal#indent#non_bracket_continuation_regex) && + let mslnum = lnum + elseif line =~# g:crystal#indent#non_bracket_continuation_regex && \ ( - \ crystal#indent#Match(msl, g:crystal#indent#bracket_continuation_regex) || - \ crystal#indent#Match(msl, g:crystal#indent#block_continuation_regex) + \ msl =~# g:crystal#indent#bracket_continuation_regex || + \ msl =~# g:crystal#indent#block_continuation_regex \ ) " If the current line is a bracket continuation or a block-starter, but - " the previous is a non-bracket one, respect the previous' indentation, - " and stop here. + " the previous is a non-bracket one, keep looking for an mslnum. " " Example: " method_call one, " two { " three " - return lnum - elseif crystal#indent#Match(lnum, g:crystal#indent#bracket_continuation_regex) && + " method_call one, + " two, + " three { + " four + " + let mslnum = lnum + elseif line =~# g:crystal#indent#bracket_continuation_regex && \ ( - \ crystal#indent#Match(msl, g:crystal#indent#bracket_continuation_regex) || - \ crystal#indent#Match(msl, g:crystal#indent#block_continuation_regex) + \ msl =~# g:crystal#indent#bracket_continuation_regex || + \ msl =~# g:crystal#indent#block_continuation_regex \ ) " If both lines are bracket continuations (the current may also be a - " block-starter), use the current one's and stop here + " block-starter), use the current one's and stop here. " " Example: " method_call( " other_method_call( " foo - return msl - elseif crystal#indent#Match(lnum, g:crystal#indent#block_regex) && - \ !crystal#indent#Match(msl, g:crystal#indent#continuation_regex) && - \ !crystal#indent#Match(msl, g:crystal#indent#block_continuation_regex) + " + return mslnum + elseif line =~# g:crystal#indent#block_regex && + \ msl !~# g:crystal#indent#continuation_regex " If the previous line is a block-starter and the current one is - " mostly ordinary, use the current one as the MSL. + " mostly ordinary, use the current one as the mslnum. " " Example: " method_call do " something " something_else - return msl + " + return mslnum + elseif line =~# g:crystal#indent#continuation_regex + let mslnum = lnum else - let col = match(line, g:crystal#indent#continuation_regex) + 1 - - if (col > 0 && !crystal#indent#IsInStringOrComment(lnum, col)) - \ || crystal#indent#IsInString(lnum, strlen(line)) - let msl = lnum - else - break - endif + break endif - let msl_body = getline(msl) - let lnum = crystal#indent#PrevNonBlankNonString(lnum - 1) + let lnum = crystal#indent#PrevNonBlank(lnum - 1) endwhile - return msl + return mslnum endfunction " Check if line 'lnum' has more opening brackets than closing ones. @@ -268,7 +968,7 @@ function! crystal#indent#ExtraBrackets(lnum) abort let closing = {'parentheses': [], 'braces': [], 'brackets': []} let line = getline(a:lnum) - let pos = match(line, '[][(){}]', 0) + let pos = match(line, '[][(){}]') " Save any encountered opening brackets, and remove them once a matching " closing one has been found. If a closing bracket shows up that doesn't @@ -326,42 +1026,19 @@ function! crystal#indent#ExtraBrackets(lnum) abort endfunction function! crystal#indent#Match(lnum, regex) abort - let regex = '\C'.a:regex - - let line = getline(a:lnum) - let col = match(line, regex) + 1 - - while col && - \ crystal#indent#IsInStringOrComment(a:lnum, col) || - \ crystal#indent#IsInStringDelimiter(a:lnum, col) - let col = match(line, regex, col) + 1 - endwhile - - return col -endfunction + let line = getline(a:lnum) + let offset = match(line, '\C'.a:regex) + let col = offset + 1 -" Locates the containing class/module/struct/enum/lib's definition line, -" ignoring nested classes along the way. -function! crystal#indent#FindContainingClass() abort - let saved_position = getcurpos() - - while searchpair( - \ g:crystal#indent#end_start_regex, - \ g:crystal#indent#end_middle_regex, - \ g:crystal#indent#end_end_regex, - \ 'bWz', - \ g:crystal#indent#skip_expr) > 0 - if expand('<cword>') =~# '\<\%(class\|module\|struct\|enum\|lib\)\>' - let found_lnum = line('.') - call setpos('.', saved_position) - return found_lnum - endif + while col && crystal#indent#IsInStringOrCommentOrDelimiter(a:lnum, col) + let offset = match(line, '\C'.a:regex, offset + 1) + let col = offset + 1 endwhile - call setpos('.', saved_position) - return 0 + return col ? col : 0 endfunction -" vim: sw=2 sts=2 et: +" }}}1 +" vim:sw=2 sts=2 ts=8 fdm=marker et: endif diff --git a/autoload/crystal_lang.vim b/autoload/crystal_lang.vim index a697a943..b0e63ea2 100644 --- a/autoload/crystal_lang.vim +++ b/autoload/crystal_lang.vim @@ -1,8 +1,5 @@ if !exists('g:polyglot_disabled') || index(g:polyglot_disabled, 'crystal') == -1 -let s:save_cpo = &cpo -set cpo&vim - let s:V = vital#crystal#new() let s:P = s:V.import('Process') let s:C = s:V.import('ColorEcho') @@ -365,9 +362,6 @@ function! crystal_lang#expand(file, pos, ...) abort return crystal_lang#tool('expand', a:file, a:pos, get(a:, 1, '')) endfunction -let &cpo = s:save_cpo -unlet s:save_cpo - " vim: sw=2 sts=2 et: endif diff --git a/autoload/dart.vim b/autoload/dart.vim index 367e54e4..52d76c02 100644 --- a/autoload/dart.vim +++ b/autoload/dart.vim @@ -27,12 +27,16 @@ function! s:clearQfList(reason) abort endif endfunction -function! dart#fmt(q_args) abort - let cmd = s:FindDartFmt() - if type(cmd) != type('') | return | endif +function! dart#fmt(...) abort + let l:dartfmt = s:FindDartFmt() + if type(l:dartfmt) != type('') | return | endif let buffer_content = getline(1, '$') - let args = '--stdin-name '.expand('%').' '.a:q_args - let lines = systemlist(printf('%s %s', cmd, args), join(buffer_content, "\n")) + let l:cmd = [l:dartfmt, '--stdin-name', shellescape(expand('%'))] + if exists('g:dartfmt_options') + call extend(l:cmd, g:dartfmt_options) + endif + call extend(l:cmd, a:000) + let lines = systemlist(join(l:cmd), join(buffer_content, "\n")) " TODO(https://github.com/dart-lang/sdk/issues/38507) - Remove once the " tool no longer emits this line on SDK upgrades. if lines[-1] ==# 'Isolate creation failed' diff --git a/autoload/vital/_crystal/ColorEcho.vim b/autoload/vital/_crystal/ColorEcho.vim index af9b4374..f93c5215 100644 --- a/autoload/vital/_crystal/ColorEcho.vim +++ b/autoload/vital/_crystal/ColorEcho.vim @@ -10,8 +10,6 @@ execute join(['function! vital#_crystal#ColorEcho#import() abort', printf("retur delfunction s:_SID " ___vital___ scriptencoding utf-8 -let s:save_cpo = &cpo -set cpo&vim function! s:_is_available() abort if has('gui_running') @@ -185,7 +183,4 @@ function! s:echo(str) abort call echorizer.echo() endfunction -let &cpo = s:save_cpo -unlet s:save_cpo - endif diff --git a/autoload/vital/_crystal/Data/List.vim b/autoload/vital/_crystal/Data/List.vim index c84e1ffa..18a7a947 100644 --- a/autoload/vital/_crystal/Data/List.vim +++ b/autoload/vital/_crystal/Data/List.vim @@ -11,9 +11,6 @@ delfunction s:_SID " ___vital___ " Utilities for list. -let s:save_cpo = &cpo -set cpo&vim - function! s:pop(list) abort return remove(a:list, -1) endfunction @@ -460,9 +457,6 @@ function! s:combinations(list, r) abort return result endfunction -let &cpo = s:save_cpo -unlet s:save_cpo - " vim:set et ts=2 sts=2 sw=2 tw=0: endif diff --git a/autoload/vital/_crystal/Data/String.vim b/autoload/vital/_crystal/Data/String.vim index fe37c6a5..d5124081 100644 --- a/autoload/vital/_crystal/Data/String.vim +++ b/autoload/vital/_crystal/Data/String.vim @@ -11,9 +11,6 @@ delfunction s:_SID " ___vital___ " Utilities for string. -let s:save_cpo = &cpo -set cpo&vim - function! s:_vital_loaded(V) abort let s:V = a:V let s:L = s:V.import('Data.List') @@ -624,8 +621,6 @@ function! s:split_posix_text(text, ...) abort return split(text, newline, 1) endfunction -let &cpo = s:save_cpo -unlet s:save_cpo " vim:set et ts=2 sts=2 sw=2 tw=0: endif diff --git a/autoload/vital/_crystal/Process.vim b/autoload/vital/_crystal/Process.vim index 9a159932..ecbfc0d4 100644 --- a/autoload/vital/_crystal/Process.vim +++ b/autoload/vital/_crystal/Process.vim @@ -15,9 +15,6 @@ delfunction s:_SID " FIXME: This module name should be Vital.System ? " But the name has been already taken. -let s:save_cpo = &cpo -set cpo&vim - " FIXME: Unfortunately, can't use s:_vital_loaded() for this purpose. " Because these variables are used when this script file is loaded. @@ -169,9 +166,6 @@ else endif -let &cpo = s:save_cpo -unlet s:save_cpo - " vim:set et ts=2 sts=2 sw=2 tw=0: endif diff --git a/autoload/vital/_crystal/Web/JSON.vim b/autoload/vital/_crystal/Web/JSON.vim index e9567fef..c6892b4e 100644 --- a/autoload/vital/_crystal/Web/JSON.vim +++ b/autoload/vital/_crystal/Web/JSON.vim @@ -9,8 +9,6 @@ endfunction execute join(['function! vital#_crystal#Web#JSON#import() abort', printf("return map({'decode': '', '_vital_depends': '', '_vital_created': '', 'encode': '', '_vital_loaded': ''}, \"vital#_crystal#function('<SNR>%s_' . v:key)\")", s:_SID()), 'endfunction'], "\n") delfunction s:_SID " ___vital___ -let s:save_cpo = &cpo -set cpo&vim function! s:_true() abort return 1 @@ -174,9 +172,6 @@ function! s:_encode_dict(val, settings) abort endfunction " @vimlint(EVL102, 0, l:ns) -let &cpo = s:save_cpo -unlet s:save_cpo - " vim:set et ts=2 sts=2 sw=2 tw=0: endif |