about summary refs log tree commit diff
path: root/src/etc
diff options
context:
space:
mode:
authorChris Morgan <me@chrismorgan.info>2013-07-10 01:33:30 +1000
committerChris Morgan <me@chrismorgan.info>2013-07-10 01:33:30 +1000
commit121ae82be0c008e342bcae4e776f14671a2807b7 (patch)
treed72f4c1a7ee5ae30e7f6ce784893f7b15b1c1b5d /src/etc
parentcf65870962ebf6b9b115a8f38ca7a9acc4bbfc49 (diff)
Do indentation properly in Vim.
The cases that this improves are clearly documented inside the
committed file, so this commit message is comparatively brief.
Diffstat (limited to 'src/etc')
-rw-r--r--src/etc/vim/indent/rust.vim132
1 files changed, 129 insertions, 3 deletions
diff --git a/src/etc/vim/indent/rust.vim b/src/etc/vim/indent/rust.vim
index 8d973c9a870..55fceb96af3 100644
--- a/src/etc/vim/indent/rust.vim
+++ b/src/etc/vim/indent/rust.vim
@@ -1,11 +1,137 @@
 " Vim indent file
+" Language:         Rust
+" Author:           Chris Morgan <me@chrismorgan.info>
+" Last Change:      2013 Jul 10
 
+" Only load this indent file when no other was loaded.
 if exists("b:did_indent")
-	finish
+  finish
 endif
-
 let b:did_indent = 1
 
 setlocal cindent
 setlocal cinoptions=L0,(0,Ws,JN
-setlocal cinkeys=0{,0},!^F,o,O
+setlocal cinkeys=0{,0},!^F,o,O,0[,0]
+" Don't think cinwords will actually do anything at all... never mind
+setlocal cinwords=do,for,if,else,while,loop,impl,mod,unsafe,trait,struct,enum,fn,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},!^F,o,O,0[,0]
+
+setlocal indentexpr=GetRustIndent(v:lnum)
+
+" Only define the function once.
+if exists("*GetRustIndent")
+  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 GetRustIndent(lnum)
+
+	" Starting assumption: cindent (called at the end) will do it right
+	" normally. We just want to fix up a few cases.
+
+	if has('syntax_items')
+		if synIDattr(synID(a:lnum, 1, 1), "name") == "rustString"
+			" If the start of the line is in a string, don't change the indent
+			return -1
+		elseif synIDattr(synID(a:lnum, 1, 1), "name") =~ "\\(Comment\\|Todo\\)"
+					\ && getline(a:lnum) !~ "^\\s*/\\*"
+			" 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::
+	"
+	" match a {
+	"     b => c,
+	"         d => e,
+	"         f => g,
+	" };
+
+	" Search backwards for the previous non-empty line.
+	let prevline = s:get_line_trimmed(prevnonblank(a:lnum - 1))
+	if prevline[len(prevline) - 1] == ","
+				\ && s:get_line_trimmed(a:lnum) !~ "^\\s*[\\[\\]{}]"
+		" Oh ho! The previous line ended in a comma! I bet cindent will try to
+		" take this too far... For now, let's use the previous line's indent
+		return GetRustIndent(a:lnum - 1)
+	endif
+
+	" cindent 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)
+
+	let line = getline(a:lnum)
+	call cursor(a:lnum, 1)
+	if searchpair('{\|(', '', '}\|)', 'nbW') == 0
+		if searchpair('\[', '', '\]', 'nbW') == 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
+
+	" Fall back on cindent, which does it mostly right
+	return cindent(a:lnum)
+endfunction