diff --git a/src/etc/vim/ftplugin/rust.vim b/src/etc/vim/ftplugin/rust.vim new file mode 100644 index 00000000000..f329dd6ce02 --- /dev/null +++ b/src/etc/vim/ftplugin/rust.vim @@ -0,0 +1,25 @@ +" Vim syntax file +" Language: Rust +" Maintainer: Chris Morgan +" Last Change: 2013 Jul 6 + +if exists("b:did_ftplugin") + finish +endif +let b:did_ftplugin = 1 + +setlocal comments=s1:/*,mb:*,ex:*/,:///,://!,:// +setlocal commentstring=//%s +setlocal formatoptions-=t formatoptions+=croqnlj + +" This includeexpr isn't perfect, but it's a good start +setlocal includeexpr=substitute(v:fname,'::','/','g') + +" NOT adding .rc as it's being phased out (0.7) +setlocal suffixesadd=.rs + +if exists("g:ftplugin_rust_source_path") + let &l:path=g:ftplugin_rust_source_path . ',' . &l:path +endif + +let b:undo_ftplugin = "setlocal formatoptions< comments< commentstring< includeexpr< suffixesadd<" 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 +" 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 diff --git a/src/etc/vim/syntax/rust.vim b/src/etc/vim/syntax/rust.vim index f40bed8640d..dfcdb11e768 100644 --- a/src/etc/vim/syntax/rust.vim +++ b/src/etc/vim/syntax/rust.vim @@ -2,7 +2,8 @@ " Language: Rust " Maintainer: Patrick Walton " Maintainer: Ben Blum -" Last Change: 2013 Jun 14 +" Maintainer: Chris Morgan +" Last Change: 2013 Jul 10 if version < 600 syntax clear @@ -13,8 +14,8 @@ endif syn keyword rustConditional match if else syn keyword rustOperator as -syn match rustAssert "\" @@ -116,13 +117,18 @@ syn match rustFloat display "\<[0-9][0-9_]*\.[0-9_]\+\%([eE][+-]\=[0-9 syn match rustLifetime display "\'\%([^[:cntrl:][:space:][:punct:][:digit:]]\|_\)\%([^[:cntrl:][:punct:][:space:]]\|_\)*" syn match rustCharacter "'\([^'\\]\|\\\(['nrt\\\"]\|x\x\{2}\|u\x\{4}\|U\x\{8}\)\)'" -syn region rustCommentDoc start="/\*[\*!]" end="\*/" -syn region rustCommentDoc start="//[/!]" skip="\\$" end="$" keepend -syn match rustComment "/\*\*/" -syn region rustComment start="/\*\([^\*!]\|$\)" end="\*/" contains=rustTodo -syn region rustComment start="//\([^/!]\|$\)" skip="\\$" end="$" contains=rustTodo keepend +syn region rustComment start="/\*" end="\*/" contains=rustTodo +syn region rustComment start="//" skip="\\$" end="$" contains=rustTodo keepend +syn region rustCommentDoc start="/\*\%(!\|\*/\@!\)" end="\*/" contains=rustTodo +syn region rustCommentDoc start="//[/!]" skip="\\$" end="$" contains=rustTodo keepend -syn keyword rustTodo contained TODO FIXME XXX NB +syn keyword rustTodo contained TODO FIXME XXX NB NOTE + +" Trivial folding rules to begin with. +" TODO: use the AST to make really good folding +syn region rustFoldBraces start="{" end="}" transparent fold +" If you wish to enable this, setlocal foldmethod=syntax +" It's not enabled by default as it would drive some people mad. hi def link rustHexNumber rustNumber hi def link rustBinNumber rustNumber @@ -142,10 +148,13 @@ hi def link rustKeyword Keyword hi def link rustConditional Conditional hi def link rustIdentifier Identifier hi def link rustModPath Include +hi def link rustModPathSep Delimiter hi def link rustFuncName Function hi def link rustFuncCall Function hi def link rustCommentDoc SpecialComment hi def link rustComment Comment +hi def link rustAssert PreCondit +hi def link rustFail PreCondit hi def link rustMacro Macro hi def link rustType Type hi def link rustTodo Todo @@ -160,7 +169,6 @@ hi def link rustLifetime Special " hi rustAssert ctermfg=yellow " hi rustFail ctermfg=red " hi rustMacro ctermfg=magenta -" hi rustModPathSep ctermfg=grey syn sync minlines=200 syn sync maxlines=500