xref: /vim-8.2.3635/runtime/indent/rust.vim (revision 3ec574f2)
13c2881dcSBram Moolenaar" Vim indent file
23c2881dcSBram Moolenaar" Language:         Rust
33c2881dcSBram Moolenaar" Author:           Chris Morgan <[email protected]>
4*3ec574f2SBram Moolenaar" Last Change:      2017 Jun 13
53c2881dcSBram Moolenaar" For bugs, patches and license go to https://github.com/rust-lang/rust.vim
63c2881dcSBram Moolenaar
73c2881dcSBram Moolenaar" Only load this indent file when no other was loaded.
83c2881dcSBram Moolenaarif exists("b:did_indent")
93c2881dcSBram Moolenaar	finish
103c2881dcSBram Moolenaarendif
113c2881dcSBram Moolenaarlet b:did_indent = 1
123c2881dcSBram Moolenaar
133c2881dcSBram Moolenaarsetlocal cindent
143c2881dcSBram Moolenaarsetlocal cinoptions=L0,(0,Ws,J1,j1
153c2881dcSBram Moolenaarsetlocal cinkeys=0{,0},!^F,o,O,0[,0]
163c2881dcSBram Moolenaar" Don't think cinwords will actually do anything at all... never mind
173c2881dcSBram Moolenaarsetlocal cinwords=for,if,else,while,loop,impl,mod,unsafe,trait,struct,enum,fn,extern
183c2881dcSBram Moolenaar
193c2881dcSBram Moolenaar" Some preliminary settings
203c2881dcSBram Moolenaarsetlocal nolisp		" Make sure lisp indenting doesn't supersede us
213c2881dcSBram Moolenaarsetlocal autoindent	" indentexpr isn't much help otherwise
223c2881dcSBram Moolenaar" Also do indentkeys, otherwise # gets shoved to column 0 :-/
233c2881dcSBram Moolenaarsetlocal indentkeys=0{,0},!^F,o,O,0[,0]
243c2881dcSBram Moolenaar
253c2881dcSBram Moolenaarsetlocal indentexpr=GetRustIndent(v:lnum)
263c2881dcSBram Moolenaar
273c2881dcSBram Moolenaar" Only define the function once.
283c2881dcSBram Moolenaarif exists("*GetRustIndent")
293c2881dcSBram Moolenaar	finish
303c2881dcSBram Moolenaarendif
313c2881dcSBram Moolenaar
323c2881dcSBram Moolenaarlet s:save_cpo = &cpo
333c2881dcSBram Moolenaarset cpo&vim
343c2881dcSBram Moolenaar
353c2881dcSBram Moolenaar" Come here when loading the script the first time.
363c2881dcSBram Moolenaar
373c2881dcSBram Moolenaarfunction! s:get_line_trimmed(lnum)
383c2881dcSBram Moolenaar	" Get the line and remove a trailing comment.
393c2881dcSBram Moolenaar	" Use syntax highlighting attributes when possible.
403c2881dcSBram Moolenaar	" NOTE: this is not accurate; /* */ or a line continuation could trick it
413c2881dcSBram Moolenaar	let line = getline(a:lnum)
423c2881dcSBram Moolenaar	let line_len = strlen(line)
433c2881dcSBram Moolenaar	if has('syntax_items')
443c2881dcSBram Moolenaar		" If the last character in the line is a comment, do a binary search for
453c2881dcSBram Moolenaar		" the start of the comment.  synID() is slow, a linear search would take
463c2881dcSBram Moolenaar		" too long on a long line.
473c2881dcSBram Moolenaar		if synIDattr(synID(a:lnum, line_len, 1), "name") =~ 'Comment\|Todo'
483c2881dcSBram Moolenaar			let min = 1
493c2881dcSBram Moolenaar			let max = line_len
503c2881dcSBram Moolenaar			while min < max
513c2881dcSBram Moolenaar				let col = (min + max) / 2
523c2881dcSBram Moolenaar				if synIDattr(synID(a:lnum, col, 1), "name") =~ 'Comment\|Todo'
533c2881dcSBram Moolenaar					let max = col
543c2881dcSBram Moolenaar				else
553c2881dcSBram Moolenaar					let min = col + 1
563c2881dcSBram Moolenaar				endif
573c2881dcSBram Moolenaar			endwhile
583c2881dcSBram Moolenaar			let line = strpart(line, 0, min - 1)
593c2881dcSBram Moolenaar		endif
603c2881dcSBram Moolenaar		return substitute(line, "\s*$", "", "")
613c2881dcSBram Moolenaar	else
623c2881dcSBram Moolenaar		" Sorry, this is not complete, nor fully correct (e.g. string "//").
633c2881dcSBram Moolenaar		" Such is life.
643c2881dcSBram Moolenaar		return substitute(line, "\s*//.*$", "", "")
653c2881dcSBram Moolenaar	endif
663c2881dcSBram Moolenaarendfunction
673c2881dcSBram Moolenaar
683c2881dcSBram Moolenaarfunction! s:is_string_comment(lnum, col)
693c2881dcSBram Moolenaar	if has('syntax_items')
703c2881dcSBram Moolenaar		for id in synstack(a:lnum, a:col)
713c2881dcSBram Moolenaar			let synname = synIDattr(id, "name")
723c2881dcSBram Moolenaar			if synname == "rustString" || synname =~ "^rustComment"
733c2881dcSBram Moolenaar				return 1
743c2881dcSBram Moolenaar			endif
753c2881dcSBram Moolenaar		endfor
763c2881dcSBram Moolenaar	else
773c2881dcSBram Moolenaar		" without syntax, let's not even try
783c2881dcSBram Moolenaar		return 0
793c2881dcSBram Moolenaar	endif
803c2881dcSBram Moolenaarendfunction
813c2881dcSBram Moolenaar
823c2881dcSBram Moolenaarfunction GetRustIndent(lnum)
833c2881dcSBram Moolenaar
843c2881dcSBram Moolenaar	" Starting assumption: cindent (called at the end) will do it right
853c2881dcSBram Moolenaar	" normally. We just want to fix up a few cases.
863c2881dcSBram Moolenaar
873c2881dcSBram Moolenaar	let line = getline(a:lnum)
883c2881dcSBram Moolenaar
893c2881dcSBram Moolenaar	if has('syntax_items')
903c2881dcSBram Moolenaar		let synname = synIDattr(synID(a:lnum, 1, 1), "name")
913c2881dcSBram Moolenaar		if synname == "rustString"
923c2881dcSBram Moolenaar			" If the start of the line is in a string, don't change the indent
933c2881dcSBram Moolenaar			return -1
943c2881dcSBram Moolenaar		elseif synname =~ '\(Comment\|Todo\)'
953c2881dcSBram Moolenaar					\ && line !~ '^\s*/\*'  " not /* opening line
963c2881dcSBram Moolenaar			if synname =~ "CommentML" " multi-line
973c2881dcSBram Moolenaar				if line !~ '^\s*\*' && getline(a:lnum - 1) =~ '^\s*/\*'
983c2881dcSBram Moolenaar					" This is (hopefully) the line after a /*, and it has no
993c2881dcSBram Moolenaar					" leader, so the correct indentation is that of the
1003c2881dcSBram Moolenaar					" previous line.
1013c2881dcSBram Moolenaar					return GetRustIndent(a:lnum - 1)
1023c2881dcSBram Moolenaar				endif
1033c2881dcSBram Moolenaar			endif
1043c2881dcSBram Moolenaar			" If it's in a comment, let cindent take care of it now. This is
1053c2881dcSBram Moolenaar			" for cases like "/*" where the next line should start " * ", not
1063c2881dcSBram Moolenaar			" "* " as the code below would otherwise cause for module scope
1073c2881dcSBram Moolenaar			" Fun fact: "  /*\n*\n*/" takes two calls to get right!
1083c2881dcSBram Moolenaar			return cindent(a:lnum)
1093c2881dcSBram Moolenaar		endif
1103c2881dcSBram Moolenaar	endif
1113c2881dcSBram Moolenaar
1123c2881dcSBram Moolenaar	" cindent gets second and subsequent match patterns/struct members wrong,
1133c2881dcSBram Moolenaar	" as it treats the comma as indicating an unfinished statement::
1143c2881dcSBram Moolenaar	"
1153c2881dcSBram Moolenaar	" match a {
1163c2881dcSBram Moolenaar	"     b => c,
1173c2881dcSBram Moolenaar	"         d => e,
1183c2881dcSBram Moolenaar	"         f => g,
1193c2881dcSBram Moolenaar	" };
1203c2881dcSBram Moolenaar
1213c2881dcSBram Moolenaar	" Search backwards for the previous non-empty line.
1223c2881dcSBram Moolenaar	let prevlinenum = prevnonblank(a:lnum - 1)
1233c2881dcSBram Moolenaar	let prevline = s:get_line_trimmed(prevlinenum)
1243c2881dcSBram Moolenaar	while prevlinenum > 1 && prevline !~ '[^[:blank:]]'
1253c2881dcSBram Moolenaar		let prevlinenum = prevnonblank(prevlinenum - 1)
1263c2881dcSBram Moolenaar		let prevline = s:get_line_trimmed(prevlinenum)
1273c2881dcSBram Moolenaar	endwhile
1283c2881dcSBram Moolenaar
1293c2881dcSBram Moolenaar	" Handle where clauses nicely: subsequent values should line up nicely.
1303c2881dcSBram Moolenaar	if prevline[len(prevline) - 1] == ","
1313c2881dcSBram Moolenaar				\ && prevline =~# '^\s*where\s'
1323c2881dcSBram Moolenaar		return indent(prevlinenum) + 6
1333c2881dcSBram Moolenaar	endif
1343c2881dcSBram Moolenaar
1353c2881dcSBram Moolenaar	if prevline[len(prevline) - 1] == ","
1363c2881dcSBram Moolenaar				\ && s:get_line_trimmed(a:lnum) !~ '^\s*[\[\]{}]'
1373c2881dcSBram Moolenaar				\ && prevline !~ '^\s*fn\s'
1383c2881dcSBram Moolenaar				\ && prevline !~ '([^()]\+,$'
1393c2881dcSBram Moolenaar				\ && s:get_line_trimmed(a:lnum) !~ '^\s*\S\+\s*=>'
1403c2881dcSBram Moolenaar		" Oh ho! The previous line ended in a comma! I bet cindent will try to
1413c2881dcSBram Moolenaar		" take this too far... For now, let's normally use the previous line's
1423c2881dcSBram Moolenaar		" indent.
1433c2881dcSBram Moolenaar
1443c2881dcSBram Moolenaar		" One case where this doesn't work out is where *this* line contains
1453c2881dcSBram Moolenaar		" square or curly brackets; then we normally *do* want to be indenting
1463c2881dcSBram Moolenaar		" further.
1473c2881dcSBram Moolenaar		"
1483c2881dcSBram Moolenaar		" Another case where we don't want to is one like a function
1493c2881dcSBram Moolenaar		" definition with arguments spread over multiple lines:
1503c2881dcSBram Moolenaar		"
1513c2881dcSBram Moolenaar		" fn foo(baz: Baz,
1523c2881dcSBram Moolenaar		"        baz: Baz) // <-- cindent gets this right by itself
1533c2881dcSBram Moolenaar		"
1543c2881dcSBram Moolenaar		" Another case is similar to the previous, except calling a function
1553c2881dcSBram Moolenaar		" instead of defining it, or any conditional expression that leaves
1563c2881dcSBram Moolenaar		" an open paren:
1573c2881dcSBram Moolenaar		"
1583c2881dcSBram Moolenaar		" foo(baz,
1593c2881dcSBram Moolenaar		"     baz);
1603c2881dcSBram Moolenaar		"
1613c2881dcSBram Moolenaar		" if baz && (foo ||
1623c2881dcSBram Moolenaar		"            bar) {
1633c2881dcSBram Moolenaar		"
1643c2881dcSBram Moolenaar		" Another case is when the current line is a new match arm.
1653c2881dcSBram Moolenaar		"
1663c2881dcSBram Moolenaar		" There are probably other cases where we don't want to do this as
1673c2881dcSBram Moolenaar		" well. Add them as needed.
1683c2881dcSBram Moolenaar		return indent(prevlinenum)
1693c2881dcSBram Moolenaar	endif
1703c2881dcSBram Moolenaar
1713c2881dcSBram Moolenaar	if !has("patch-7.4.355")
1723c2881dcSBram Moolenaar		" cindent before 7.4.355 doesn't do the module scope well at all; e.g.::
1733c2881dcSBram Moolenaar		"
1743c2881dcSBram Moolenaar		" static FOO : &'static [bool] = [
1753c2881dcSBram Moolenaar		" true,
1763c2881dcSBram Moolenaar		"	 false,
1773c2881dcSBram Moolenaar		"	 false,
1783c2881dcSBram Moolenaar		"	 true,
1793c2881dcSBram Moolenaar		"	 ];
1803c2881dcSBram Moolenaar		"
1813c2881dcSBram Moolenaar		"	 uh oh, next statement is indented further!
1823c2881dcSBram Moolenaar
1833c2881dcSBram Moolenaar		" Note that this does *not* apply the line continuation pattern properly;
1843c2881dcSBram Moolenaar		" that's too hard to do correctly for my liking at present, so I'll just
1853c2881dcSBram Moolenaar		" start with these two main cases (square brackets and not returning to
1863c2881dcSBram Moolenaar		" column zero)
1873c2881dcSBram Moolenaar
1883c2881dcSBram Moolenaar		call cursor(a:lnum, 1)
1893c2881dcSBram Moolenaar		if searchpair('{\|(', '', '}\|)', 'nbW',
1903c2881dcSBram Moolenaar					\ 's:is_string_comment(line("."), col("."))') == 0
1913c2881dcSBram Moolenaar			if searchpair('\[', '', '\]', 'nbW',
1923c2881dcSBram Moolenaar						\ 's:is_string_comment(line("."), col("."))') == 0
1933c2881dcSBram Moolenaar				" Global scope, should be zero
1943c2881dcSBram Moolenaar				return 0
1953c2881dcSBram Moolenaar			else
1963c2881dcSBram Moolenaar				" At the module scope, inside square brackets only
1973c2881dcSBram Moolenaar				"if getline(a:lnum)[0] == ']' || search('\[', '', '\]', 'nW') == a:lnum
1983c2881dcSBram Moolenaar				if line =~ "^\\s*]"
1993c2881dcSBram Moolenaar					" It's the closing line, dedent it
2003c2881dcSBram Moolenaar					return 0
2013c2881dcSBram Moolenaar				else
202*3ec574f2SBram Moolenaar					return shiftwidth()
2033c2881dcSBram Moolenaar				endif
2043c2881dcSBram Moolenaar			endif
2053c2881dcSBram Moolenaar		endif
2063c2881dcSBram Moolenaar	endif
2073c2881dcSBram Moolenaar
2083c2881dcSBram Moolenaar	" Fall back on cindent, which does it mostly right
2093c2881dcSBram Moolenaar	return cindent(a:lnum)
2103c2881dcSBram Moolenaarendfunction
2113c2881dcSBram Moolenaar
2123c2881dcSBram Moolenaarlet &cpo = s:save_cpo
2133c2881dcSBram Moolenaarunlet s:save_cpo
214