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