Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
falcon.vim455 linesDownload Raw Back to indent
1" Vim indent file2" Language: Falcon3" Maintainer: Steven Oliver <oliver.steven@gmail.com>4" Website: https://steveno@github.com/steveno/falconpl-vim.git5" Credits: This is, to a great extent, a copy n' paste of ruby.vim.6"		2022 April: b:undo_indent added by Doug Kearns7 8" 1. Setup {{{19" ============10 11" Only load this indent file when no other was loaded.12if exists("b:did_indent")13    finish14endif15let b:did_indent = 116 17setlocal nosmartindent18 19" Setup indent function and when to use it20setlocal indentexpr=FalconGetIndent(v:lnum)21setlocal indentkeys=0{,0},0),0],!^F,o,O,e22setlocal indentkeys+==~case,=~catch,=~default,=~elif,=~else,=~end,=~\"23 24let b:undo_indent = "setl inde< indk< si<"25 26" Define the appropriate indent function but only once27if exists("*FalconGetIndent")28    finish29endif30 31let s:cpo_save = &cpo32set cpo&vim33 34" 2. Variables {{{135" ============36 37" Regex of syntax group names that are strings AND comments38let s:syng_strcom = '\<falcon\%(String\|StringEscape\|Comment\)\>'39 40" Regex of syntax group names that are strings41let s:syng_string = '\<falcon\%(String\|StringEscape\)\>'42 43" Regex that defines blocks.44"45" Note that there's a slight problem with this regex and s:continuation_regex.46" Code like this will be matched by both:47"48"   method_call do |(a, b)|49"50" The reason is that the pipe matches a hanging "|" operator.51"52let s:block_regex =53      \ '\%(\<do:\@!\>\|%\@<!{\)\s*\%(|\s*(*\s*\%([*@&]\=\h\w*,\=\s*\)\%(,\s*(*\s*[*@&]\=\h\w*\s*)*\s*\)*|\)\=\s*\%(#.*\)\=$'54 55let s:block_continuation_regex = '^\s*[^])}\t ].*'.s:block_regex56 57" Regex that defines continuation lines.58" TODO: this needs to deal with if ...: and so on59let s:continuation_regex =60      \ '\%(%\@<![({[\\.,:*/%+]\|\<and\|\<or\|\%(<%\)\@<![=-]\|\W[|&?]\|||\|&&\)\s*\%(#.*\)\=$'61 62" Regex that defines bracket continuations63let s:bracket_continuation_regex = '%\@<!\%([({[]\)\s*\%(#.*\)\=$'64 65" Regex that defines continuation lines, not including (, {, or [.66let s:non_bracket_continuation_regex = '\%([\\.,:*/%+]\|\<and\|\<or\|\%(<%\)\@<![=-]\|\W[|&?]\|||\|&&\)\s*\%(#.*\)\=$'67 68" Keywords to indent on69let s:falcon_indent_keywords = '^\s*\(case\|catch\|class\|enum\|default\|elif\|else' .70    \ '\|for\|function\|if.*"[^"]*:.*"\|if \(\(:\)\@!.\)*$\|loop\|object\|select' .71    \ '\|switch\|try\|while\|\w*\s*=\s*\w*([$\)'72 73" Keywords to deindent on74let s:falcon_deindent_keywords = '^\s*\(case\|catch\|default\|elif\|else\|end\)'75 76" 3. Functions {{{177" ============78 79" Check if the character at lnum:col is inside a string, comment, or is ascii.80function s:IsInStringOrComment(lnum, col)81    return synIDattr(synID(a:lnum, a:col, 1), 'name') =~ s:syng_strcom82endfunction83 84" Check if the character at lnum:col is inside a string.85function s:IsInString(lnum, col)86    return synIDattr(synID(a:lnum, a:col, 1), 'name') =~ s:syng_string87endfunction88 89" Check if the character at lnum:col is inside a string delimiter90function s:IsInStringDelimiter(lnum, col)91    return synIDattr(synID(a:lnum, a:col, 1), 'name') == 'falconStringDelimiter'92endfunction93 94" Find line above 'lnum' that isn't empty, in a comment, or in a string.95function s:PrevNonBlankNonString(lnum)96    let in_block = 097    let lnum = prevnonblank(a:lnum)98    while lnum > 099	" Go in and out of blocks comments as necessary.100	" If the line isn't empty (with opt. comment) or in a string, end search.101	let line = getline(lnum)102	if line =~ '^=begin'103	    if in_block104		let in_block = 0105	    else106		break107	    endif108	elseif !in_block && line =~ '^=end'109	    let in_block = 1110	elseif !in_block && line !~ '^\s*#.*$' && !(s:IsInStringOrComment(lnum, 1)111		    \ && s:IsInStringOrComment(lnum, strlen(line)))112	    break113	endif114	let lnum = prevnonblank(lnum - 1)115    endwhile116    return lnum117endfunction118 119" Find line above 'lnum' that started the continuation 'lnum' may be part of.120function s:GetMSL(lnum)121    " Start on the line we're at and use its indent.122    let msl = a:lnum123    let msl_body = getline(msl)124    let lnum = s:PrevNonBlankNonString(a:lnum - 1)125    while lnum > 0126	" If we have a continuation line, or we're in a string, use line as MSL.127	" Otherwise, terminate search as we have found our MSL already.128	let line = getline(lnum)129	130	if s:Match(line, s:non_bracket_continuation_regex) &&131          	\ s:Match(msl, s:non_bracket_continuation_regex)132	    " If the current line is a non-bracket continuation and so is the133	    " previous one, keep its indent and continue looking for an MSL.134	    "    135	    " Example:136	    "   method_call one,137	    "       two,138	    "           three139	    "           140	    let msl = lnum141	elseif s:Match(lnum, s:non_bracket_continuation_regex) &&142		    \ (s:Match(msl, s:bracket_continuation_regex) || s:Match(msl, s:block_continuation_regex))143	    " If the current line is a bracket continuation or a block-starter, but144	    " the previous is a non-bracket one, respect the previous' indentation,145	    " and stop here.146	    " 147	    " Example:148	    "   method_call one,149	    "       two {150	    "           three151	    "152	    return lnum153	elseif s:Match(lnum, s:bracket_continuation_regex) &&154		    \ (s:Match(msl, s:bracket_continuation_regex) || s:Match(msl, s:block_continuation_regex))155	    " If both lines are bracket continuations (the current may also be a156	    " block-starter), use the current one's and stop here157	    "158	    " Example:159	    "   method_call(160	    "       other_method_call(161	    "             foo162	    return msl163	elseif s:Match(lnum, s:block_regex) &&164		    \ !s:Match(msl, s:continuation_regex) &&165		    \ !s:Match(msl, s:block_continuation_regex)166	    " If the previous line is a block-starter and the current one is167	    " mostly ordinary, use the current one as the MSL.168	    " 169	    " Example:170	    "   method_call do171	    "       something172	    "           something_else173	    return msl174	else175	    let col = match(line, s:continuation_regex) + 1176	    if (col > 0 && !s:IsInStringOrComment(lnum, col))177			\ || s:IsInString(lnum, strlen(line))178		let msl = lnum179	    else180		break181	    endif182	endif183	184	let msl_body = getline(msl)185	let lnum = s:PrevNonBlankNonString(lnum - 1)186    endwhile187    return msl188endfunction189 190" Check if line 'lnum' has more opening brackets than closing ones.191function s:ExtraBrackets(lnum)192    let opening = {'parentheses': [], 'braces': [], 'brackets': []}193    let closing = {'parentheses': [], 'braces': [], 'brackets': []}194 195    let line = getline(a:lnum)196    let pos  = match(line, '[][(){}]', 0)197 198    " Save any encountered opening brackets, and remove them once a matching199    " closing one has been found. If a closing bracket shows up that doesn't200    " close anything, save it for later.201    while pos != -1202	if !s:IsInStringOrComment(a:lnum, pos + 1)203	    if line[pos] == '('204		call add(opening.parentheses, {'type': '(', 'pos': pos})205	    elseif line[pos] == ')'206		if empty(opening.parentheses)207		    call add(closing.parentheses, {'type': ')', 'pos': pos})208		else209		    let opening.parentheses = opening.parentheses[0:-2]210		endif211	    elseif line[pos] == '{'212		call add(opening.braces, {'type': '{', 'pos': pos})213	    elseif line[pos] == '}'214		if empty(opening.braces)215		    call add(closing.braces, {'type': '}', 'pos': pos})216		else217		    let opening.braces = opening.braces[0:-2]218		endif219	    elseif line[pos] == '['220		call add(opening.brackets, {'type': '[', 'pos': pos})221	    elseif line[pos] == ']'222		if empty(opening.brackets)223		    call add(closing.brackets, {'type': ']', 'pos': pos})224		else225		    let opening.brackets = opening.brackets[0:-2]226		endif227	    endif228	endif229	230	let pos = match(line, '[][(){}]', pos + 1)231    endwhile232 233    " Find the rightmost brackets, since they're the ones that are important in234    " both opening and closing cases235    let rightmost_opening = {'type': '(', 'pos': -1}236    let rightmost_closing = {'type': ')', 'pos': -1}237 238    for opening in opening.parentheses + opening.braces + opening.brackets239	if opening.pos > rightmost_opening.pos240	    let rightmost_opening = opening241	endif242    endfor243 244    for closing in closing.parentheses + closing.braces + closing.brackets245	if closing.pos > rightmost_closing.pos246	    let rightmost_closing = closing247	endif248    endfor249 250    return [rightmost_opening, rightmost_closing]251endfunction252 253function s:Match(lnum, regex)254    let col = match(getline(a:lnum), '\C'.a:regex) + 1255    return col > 0 && !s:IsInStringOrComment(a:lnum, col) ? col : 0256endfunction257 258function s:MatchLast(lnum, regex)259    let line = getline(a:lnum)260    let col = match(line, '.*\zs' . a:regex)261    while col != -1 && s:IsInStringOrComment(a:lnum, col)262	let line = strpart(line, 0, col)263	let col = match(line, '.*' . a:regex)264    endwhile265    return col + 1266endfunction267 268" 4. FalconGetIndent Routine {{{1269" ============270 271function FalconGetIndent(...)272    " For the current line, use the first argument if given, else v:lnum273    let clnum = a:0 ? a:1 : v:lnum274 275    " Use zero indent at the top of the file276    if clnum == 0277        return 0278    endif279 280    let line = getline(clnum)281    let ind = -1282 283    " If we got a closing bracket on an empty line, find its match and indent284    " according to it.  For parentheses we indent to its column - 1, for the285    " others we indent to the containing line's MSL's level.  Return -1 if fail.286    let col = matchend(line, '^\s*[]})]')287    if col > 0 && !s:IsInStringOrComment(clnum, col)288	call cursor(clnum, col)289	let bs = strpart('(){}[]', stridx(')}]', line[col - 1]) * 2, 2)290	if searchpair(escape(bs[0], '\['), '', bs[1], 'bW', s:skip_expr) > 0291	    if line[col-1]==')' && col('.') != col('$') - 1292		let ind = virtcol('.') - 1293	    else294		let ind = indent(s:GetMSL(line('.')))295	    endif296	endif297	return ind298    endif299 300    " If we have a deindenting keyword, find its match and indent to its level.301    " TODO: this is messy302    if s:Match(clnum, s:falcon_deindent_keywords)303	call cursor(clnum, 1)304	if searchpair(s:end_start_regex, s:end_middle_regex, s:end_end_regex, 'bW',305		    \ s:end_skip_expr) > 0306	    let msl  = s:GetMSL(line('.'))307	    let line = getline(line('.'))308 309	    if strpart(line, 0, col('.') - 1) =~ '=\s*$' &&310			\ strpart(line, col('.') - 1, 2) !~ 'do'311		let ind = virtcol('.') - 1312	    elseif getline(msl) =~ '=\s*\(#.*\)\=$'313		let ind = indent(line('.'))314	    else315		let ind = indent(msl)316	    endif317	endif318	return ind319    endif320 321    " If we are in a multi-line string or line-comment, don't do anything to it.322    if s:IsInString(clnum, matchend(line, '^\s*') + 1)323	return indent('.')324    endif325 326    " Find a non-blank, non-multi-line string line above the current line.327    let lnum = s:PrevNonBlankNonString(clnum - 1)328 329    " If the line is empty and inside a string, use the previous line.330    if line =~ '^\s*$' && lnum != prevnonblank(clnum - 1)331	return indent(prevnonblank(clnum))332    endif333 334    " At the start of the file use zero indent.335    if lnum == 0336	return 0337    endif338 339    " Set up variables for the previous line.340    let line = getline(lnum)341    let ind = indent(lnum)342 343    " If the previous line ended with a block opening, add a level of indent.344    if s:Match(lnum, s:block_regex)345	return indent(s:GetMSL(lnum)) + shiftwidth()346    endif347 348    " If it contained hanging closing brackets, find the rightmost one, find its349    " match and indent according to that.350    if line =~ '[[({]' || line =~ '[])}]\s*\%(#.*\)\=$'351	let [opening, closing] = s:ExtraBrackets(lnum)352 353	if opening.pos != -1354	    if opening.type == '(' && searchpair('(', '', ')', 'bW', s:skip_expr) > 0355		if col('.') + 1 == col('$')356		    return ind + shiftwidth()357		else358		    return virtcol('.')359		endif360	    else361		let nonspace = matchend(line, '\S', opening.pos + 1) - 1362		return nonspace > 0 ? nonspace : ind + shiftwidth()363	    endif364	elseif closing.pos != -1365	    call cursor(lnum, closing.pos + 1)366	    normal! %367 368	    if s:Match(line('.'), s:falcon_indent_keywords)369		return indent('.') + shiftwidth()370	    else371		return indent('.')372	    endif373	else374	    call cursor(clnum, 0)  " FIXME: column was vcol375	end376    endif377 378    " If the previous line ended with an "end", match that "end"s beginning's379    " indent.380    let col = s:Match(lnum, '\%(^\|[^.:@$]\)\<end\>\s*\%(#.*\)\=$')381    if col > 0382	call cursor(lnum, col)383	if searchpair(s:end_start_regex, '', s:end_end_regex, 'bW',384		    \ s:end_skip_expr) > 0385	    let n = line('.')386	    let ind = indent('.')387	    let msl = s:GetMSL(n)388	    if msl != n389		let ind = indent(msl)390	    end391	    return ind392	endif393    end394 395    let col = s:Match(lnum, s:falcon_indent_keywords)396    if col > 0397	call cursor(lnum, col)398	let ind = virtcol('.') - 1 + shiftwidth()399	" TODO: make this better (we need to count them) (or, if a searchpair400	" fails, we know that something is lacking an end and thus we indent a401	" level402	if s:Match(lnum, s:end_end_regex)403	    let ind = indent('.')404	endif405	return ind406    endif407 408    " Set up variables to use and search for MSL to the previous line.409    let p_lnum = lnum410    let lnum = s:GetMSL(lnum)411 412    " If the previous line wasn't a MSL and is continuation return its indent.413    " TODO: the || s:IsInString() thing worries me a bit.414    if p_lnum != lnum415	if s:Match(p_lnum, s:non_bracket_continuation_regex) || s:IsInString(p_lnum,strlen(line))416	    return ind417	endif418    endif419 420    " Set up more variables, now that we know we wasn't continuation bound.421    let line = getline(lnum)422    let msl_ind = indent(lnum)423 424    " If the MSL line had an indenting keyword in it, add a level of indent.425    " TODO: this does not take into account contrived things such as426    " module Foo; class Bar; end427    if s:Match(lnum, s:falcon_indent_keywords)428	let ind = msl_ind + shiftwidth()429	if s:Match(lnum, s:end_end_regex)430	    let ind = ind - shiftwidth()431	endif432	return ind433    endif434 435    " If the previous line ended with [*+/.,-=], but wasn't a block ending or a436    " closing bracket, indent one extra level.437    if s:Match(lnum, s:non_bracket_continuation_regex) && !s:Match(lnum, '^\s*\([\])}]\|end\)')438	if lnum == p_lnum439	    let ind = msl_ind + shiftwidth()440	else441	    let ind = msl_ind442	endif443	return ind444    endif445 446  return ind447endfunction448 449" }}}1450 451let &cpo = s:cpo_save452unlet s:cpo_save453 454" vim: set sw=4 sts=4 et tw=80 :455