diff options
| author | Adam Stankiewicz <sheerun@sher.pl> | 2019-03-04 10:09:33 +0100 | 
|---|---|---|
| committer | Adam Stankiewicz <sheerun@sher.pl> | 2019-03-04 10:09:33 +0100 | 
| commit | 0cd0b7f8942a42bf8cb24affb18ac5bedae5aa48 (patch) | |
| tree | 06a44efce0966a50c2fdf55ba43e629ecbf2b800 /indent | |
| parent | ec5884b1d0e58dd4727530a4f7e8a30c21b713c1 (diff) | |
| download | vim-polyglot-0cd0b7f8942a42bf8cb24affb18ac5bedae5aa48.tar.gz vim-polyglot-0cd0b7f8942a42bf8cb24affb18ac5bedae5aa48.zip | |
Add reason support, closes #266
Diffstat (limited to '')
| -rw-r--r-- | indent/reason.vim | 221 | 
1 files changed, 221 insertions, 0 deletions
| diff --git a/indent/reason.vim b/indent/reason.vim new file mode 100644 index 00000000..4901a4f5 --- /dev/null +++ b/indent/reason.vim @@ -0,0 +1,221 @@ +if exists('g:polyglot_disabled') && index(g:polyglot_disabled, 'reason') != -1 +  finish +endif + +" Vim indent file +" Language:         Reason (adapted from Rust) +" Author:           Chris Morgan <me@chrismorgan.info> (Modifications by Jordan W) +" Portions Copyright (c) 2015-present, Facebook, Inc. All rights reserved + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") +  finish +endif +let b:did_indent = 1 + +" m1 is needed to make parens line up correct: +" +"   let result = callSomething ( +"      1, +"      2 +"   ); +" +" fN (See the docs) - additional indentation *w.r.t the "prevailing indent" only applies to blocks +" *not* in nested braces!  We want it to match the blocks *inside* nested braces - and be zero, but +" the problem is that the "prevailing indent" not inside of braces is different than inside braces. +setlocal cinoptions=L0,(0,Ws,J1,j1,m1,{0,f0s +" cinkeys is ignored if indentexpr is activated so this setting is useless. See indentkeys below. +setlocal cinkeys=0{,0},0),!^F,o,O,0[,0] +" Don't think cinwords will actually do anything at all... never mind +setlocal cinwords=for,if,else,while,loop,impl,mod,unsafe,trait,struct,enum,fun,let,extern + +" Some preliminary settings +setlocal nolisp		" Make sure lisp indenting doesn't supersede us +setlocal autoindent	" indentexpr isn't much help otherwise +" Also do indentkeys, otherwise # gets shoved to column 0 :-/ +setlocal indentkeys=0{,0},0),!^F,o,O,0[,0] + + +setlocal indentexpr=GetReasonIndent(v:lnum) + +" Only define the function once. +if exists("*GetReasonIndent") +  finish +endif + +" Come here when loading the script the first time. + +function! s:get_line_trimmed(lnum) +	" Get the line and remove a trailing comment. +	" Use syntax highlighting attributes when possible. +	" NOTE: this is not accurate; /* */ or a line continuation could trick it +	let line = getline(a:lnum) +	let line_len = strlen(line) +	if has('syntax_items') +		" If the last character in the line is a comment, do a binary search for +		" the start of the comment.  synID() is slow, a linear search would take +		" too long on a long line. +		if synIDattr(synID(a:lnum, line_len, 1), "name") =~ 'Comment\|Todo' +			let min = 1 +			let max = line_len +			while min < max +				let col = (min + max) / 2 +				if synIDattr(synID(a:lnum, col, 1), "name") =~ 'Comment\|Todo' +					let max = col +				else +					let min = col + 1 +				endif +			endwhile +			let line = strpart(line, 0, min - 1) +		endif +		return substitute(line, "\s*$", "", "") +	else +		" Sorry, this is not complete, nor fully correct (e.g. string "//"). +		" Such is life. +		return substitute(line, "\s*//.*$", "", "") +	endif +endfunction + +function! s:is_string_comment(lnum, col) +	if has('syntax_items') +		for id in synstack(a:lnum, a:col) +			let synname = synIDattr(id, "name") +			if synname == "rustString" || synname =~ "^rustComment" +				return 1 +			endif +		endfor +	else +		" without syntax, let's not even try +		return 0 +	endif +endfunction + +function GetReasonIndent(lnum) + +	" Starting assumption: cindent (called at the end) will do it right +	" normally. We just want to fix up a few cases. + +	let line = getline(a:lnum) + +	if has('syntax_items') +		let synname = synIDattr(synID(a:lnum, 1, 1), "name") +		if synname == "rustString" +			" If the start of the line is in a string, don't change the indent +			return -1 +		elseif synname =~ '\(Comment\|Todo\)' +					\ && line !~ '^\s*/\*'  " not /* opening line +			if synname =~ "CommentML" " multi-line +				if line !~ '^\s*\*' && getline(a:lnum - 1) =~ '^\s*/\*' +					" This is (hopefully) the line after a /*, and it has no +					" leader, so the correct indentation is that of the +					" previous line. +					return GetReasonIndent(a:lnum - 1) +				endif +			endif +			" If it's in a comment, let cindent take care of it now. This is +			" for cases like "/*" where the next line should start " * ", not +			" "* " as the code below would otherwise cause for module scope +			" Fun fact: "  /*\n*\n*/" takes two calls to get right! +			return cindent(a:lnum) +		endif +	endif + +	" cindent gets second and subsequent match patterns/struct members wrong, +	" as it treats the comma as indicating an unfinished statement:: +	" +	" switch a { +	"   | b => c +	"     | d => e +	"     | f => g +	" }; + +	" Search backwards for the previous non-empty line. +	let prevlinenum = prevnonblank(a:lnum - 1) +	let prevline = s:get_line_trimmed(prevlinenum) +	while prevlinenum > 1 && prevline !~ '[^[:blank:]]' +		let prevlinenum = prevnonblank(prevlinenum - 1) +		let prevline = s:get_line_trimmed(prevlinenum) +	endwhile + +	" Handle where clauses nicely: subsequent values should line up nicely. +	if prevline[len(prevline) - 1] == "," +				\ && prevline =~# '^\s*where\s' +		return indent(prevlinenum) + 6 +	endif + +	if prevline =~ "\s*|.*[{(\[]$" +				" \ && s:get_line_trimmed(a:lnum) !~ '^\s*[\[\]{}]' +				" \ && prevline !~ '^\s*fun\s' +				" \ && prevline !~ '([^()]\+,$' +		" Oh ho! The previous line ended in a comma! I bet cindent will try to +		" take this too far... For now, let's normally use the previous line's +		" indent. + +		" One case where this doesn't work out is where *this* line contains +		" square or curly brackets; then we normally *do* want to be indenting +		" further. +		" +		" Another case where we don't want to is one like a function +		" definition with arguments spread over multiple lines: +		" +		" fun foo(baz: Baz, +		"        baz: Baz) // <-- cindent gets this right by itself +		" +		" Another case is similar to the previous, except calling a function +		" instead of defining it, or any conditional expression that leaves +		" an open paren: +		" +		" foo(baz, +		"     baz); +		" +		" if baz && (foo || +		"            bar) { +		" +		" There are probably other cases where we don't want to do this as +		" well. Add them as needed. +		return indent(prevlinenum) + &shiftwidth +	endif +	if prevline =~ "\s*|" +		return indent(prevlinenum) +	endif + +	if !has("patch-7.4.355") +		" cindent before 7.4.355 doesn't do the module scope well at all; e.g.:: +		" +		" static FOO : &'static [bool] = [ +		" true, +		"	 false, +		"	 false, +		"	 true, +		"	 ]; +		" +		"	 uh oh, next statement is indented further! + +		" Note that this does *not* apply the line continuation pattern properly; +		" that's too hard to do correctly for my liking at present, so I'll just +		" start with these two main cases (square brackets and not returning to +		" column zero) + +		call cursor(a:lnum, 1) +		if searchpair('{\|(', '', '}\|)', 'nbW', +					\ 's:is_string_comment(line("."), col("."))') == 0 +			if searchpair('\[', '', '\]', 'nbW', +						\ 's:is_string_comment(line("."), col("."))') == 0 +				" Global scope, should be zero +				return 0 +			else +				" At the module scope, inside square brackets only +				"if getline(a:lnum)[0] == ']' || search('\[', '', '\]', 'nW') == a:lnum +				if line =~ "^\\s*]" +					" It's the closing line, dedent it +					return 0 +				else +					return &shiftwidth +				endif +			endif +		endif +	endif + +	" Fall back on cindent, which does it mostly right +	return cindent(a:lnum) +endfunction | 
