1" Vim indent file 2" Language: Scala (http://scala-lang.org/) 3" Original Author: Stefan Matthias Aust 4" Modifications By: Derek Wyatt 5" URL: https://github.com/derekwyatt/vim-scala 6" Last Change: 2016 Aug 26 7 8if exists("b:did_indent") 9 finish 10endif 11let b:did_indent = 1 12 13setlocal autoindent 14setlocal indentexpr=GetScalaIndent() 15setlocal indentkeys=0{,0},0),!^F,<>>,o,O,e,=case,<CR> 16 17if exists("*GetScalaIndent") 18 finish 19endif 20let s:keepcpo= &cpo 21set cpo&vim 22 23let s:defMatcher = '\%(\%(private\|protected\)\%(\[[^\]]*\]\)\?\s\+\|abstract\s\+\|override\s\+\)*\<def\>' 24let s:funcNameMatcher = '\w\+' 25let s:typeSpecMatcher = '\%(\s*\[\_[^\]]*\]\)' 26let s:defArgMatcher = '\%((\_.\{-})\)' 27let s:returnTypeMatcher = '\%(:\s*\w\+' . s:typeSpecMatcher . '\?\)' 28let g:fullDefMatcher = '^\s*' . s:defMatcher . '\s\+' . s:funcNameMatcher . '\s*' . s:typeSpecMatcher . '\?\s*' . s:defArgMatcher . '\?\s*' . s:returnTypeMatcher . '\?\s*[={]' 29 30function! scala#ConditionalConfirm(msg) 31 if 0 32 call confirm(a:msg) 33 endif 34endfunction 35 36function! scala#GetLine(lnum) 37 let line = substitute(getline(a:lnum), '//.*$', '', '') 38 let line = substitute(line, '"\(.\|\\"\)\{-}"', '""', 'g') 39 return line 40endfunction 41 42function! scala#CountBrackets(line, openBracket, closedBracket) 43 let line = substitute(a:line, '"\(.\|\\"\)\{-}"', '', 'g') 44 let open = substitute(line, '[^' . a:openBracket . ']', '', 'g') 45 let close = substitute(line, '[^' . a:closedBracket . ']', '', 'g') 46 return strlen(open) - strlen(close) 47endfunction 48 49function! scala#CountParens(line) 50 return scala#CountBrackets(a:line, '(', ')') 51endfunction 52 53function! scala#CountCurlies(line) 54 return scala#CountBrackets(a:line, '{', '}') 55endfunction 56 57function! scala#LineEndsInIncomplete(line) 58 if a:line =~ '[.,]\s*$' 59 return 1 60 else 61 return 0 62 endif 63endfunction 64 65function! scala#LineIsAClosingXML(line) 66 if a:line =~ '^\s*</\w' 67 return 1 68 else 69 return 0 70 endif 71endfunction 72 73function! scala#LineCompletesXML(lnum, line) 74 let savedpos = getpos('.') 75 call setpos('.', [savedpos[0], a:lnum, 0, savedpos[3]]) 76 let tag = substitute(a:line, '^.*</\([^>]*\)>.*$', '\1', '') 77 let [lineNum, colnum] = searchpairpos('<' . tag . '>', '', '</' . tag . '>', 'Wbn') 78 call setpos('.', savedpos) 79 let pline = scala#GetLine(prevnonblank(lineNum - 1)) 80 if pline =~ '=\s*$' 81 return 1 82 else 83 return 0 84 endif 85endfunction 86 87function! scala#IsParentCase() 88 let savedpos = getpos('.') 89 call setpos('.', [savedpos[0], savedpos[1], 0, savedpos[3]]) 90 let [l, c] = searchpos('^\s*\%(' . s:defMatcher . '\|\%(\<case\>\)\)', 'bnW') 91 let retvalue = -1 92 if l != 0 && search('\%' . l . 'l\s*\<case\>', 'bnW') 93 let retvalue = l 94 endif 95 call setpos('.', savedpos) 96 return retvalue 97endfunction 98 99function! scala#CurlyMatcher() 100 let matchline = scala#GetLineThatMatchesBracket('{', '}') 101 if scala#CountParens(scala#GetLine(matchline)) < 0 102 let savedpos = getpos('.') 103 call setpos('.', [savedpos[0], matchline, 9999, savedpos[3]]) 104 call searchpos('{', 'Wbc') 105 call searchpos(')', 'Wb') 106 let [lnum, colnum] = searchpairpos('(', '', ')', 'Wbn') 107 call setpos('.', savedpos) 108 let line = scala#GetLine(lnum) 109 if line =~ '^\s*' . s:defMatcher 110 return lnum 111 else 112 return matchline 113 endif 114 else 115 return matchline 116 endif 117endfunction 118 119function! scala#GetLineAndColumnThatMatchesCurly() 120 return scala#GetLineAndColumnThatMatchesBracket('{', '}') 121endfunction 122 123function! scala#GetLineAndColumnThatMatchesParen() 124 return scala#GetLineAndColumnThatMatchesBracket('(', ')') 125endfunction 126 127function! scala#GetLineAndColumnThatMatchesBracket(openBracket, closedBracket) 128 let savedpos = getpos('.') 129 let curline = scala#GetLine(line('.')) 130 if curline =~ a:closedBracket . '.*' . a:openBracket . '.*' . a:closedBracket 131 call setpos('.', [savedpos[0], savedpos[1], 0, savedpos[3]]) 132 call searchpos(a:closedBracket . '\ze[^' . a:closedBracket . a:openBracket . ']*' . a:openBracket, 'W') 133 else 134 call setpos('.', [savedpos[0], savedpos[1], 9999, savedpos[3]]) 135 call searchpos(a:closedBracket, 'Wbc') 136 endif 137 let [lnum, colnum] = searchpairpos(a:openBracket, '', a:closedBracket, 'Wbn') 138 call setpos('.', savedpos) 139 return [lnum, colnum] 140endfunction 141 142function! scala#GetLineThatMatchesCurly() 143 return scala#GetLineThatMatchesBracket('{', '}') 144endfunction 145 146function! scala#GetLineThatMatchesParen() 147 return scala#GetLineThatMatchesBracket('(', ')') 148endfunction 149 150function! scala#GetLineThatMatchesBracket(openBracket, closedBracket) 151 let [lnum, colnum] = scala#GetLineAndColumnThatMatchesBracket(a:openBracket, a:closedBracket) 152 return lnum 153endfunction 154 155function! scala#NumberOfBraceGroups(line) 156 let line = substitute(a:line, '[^()]', '', 'g') 157 if strlen(line) == 0 158 return 0 159 endif 160 let line = substitute(line, '^)*', '', 'g') 161 if strlen(line) == 0 162 return 0 163 endif 164 let line = substitute(line, '^(', '', 'g') 165 if strlen(line) == 0 166 return 0 167 endif 168 let c = 1 169 let counter = 0 170 let groupCount = 0 171 while counter < strlen(line) 172 let char = strpart(line, counter, 1) 173 if char == '(' 174 let c = c + 1 175 elseif char == ')' 176 let c = c - 1 177 endif 178 if c == 0 179 let groupCount = groupCount + 1 180 endif 181 let counter = counter + 1 182 endwhile 183 return groupCount 184endfunction 185 186function! scala#MatchesIncompleteDefValr(line) 187 if a:line =~ '^\s*\%(' . s:defMatcher . '\|\<va[lr]\>\).*[=({]\s*$' 188 return 1 189 else 190 return 0 191 endif 192endfunction 193 194function! scala#LineIsCompleteIf(line) 195 if scala#CountBrackets(a:line, '{', '}') == 0 && 196 \ scala#CountBrackets(a:line, '(', ')') == 0 && 197 \ a:line =~ '^\s*\<if\>\s*([^)]*)\s*\S.*$' 198 return 1 199 else 200 return 0 201 endif 202endfunction 203 204function! scala#LineCompletesIfElse(lnum, line) 205 if a:line =~ '^\s*\%(\<if\>\|\%(}\s*\)\?\<else\>\)' 206 return 0 207 endif 208 let result = search('^\%(\s*\<if\>\s*(.*).*\n\|\s*\<if\>\s*(.*)\s*\n.*\n\)\%(\s*\<else\>\s*\<if\>\s*(.*)\s*\n.*\n\)*\%(\s*\<else\>\s*\n\|\s*\<else\>[^{]*\n\)\?\%' . a:lnum . 'l', 'Wbn') 209 if result != 0 && scala#GetLine(prevnonblank(a:lnum - 1)) !~ '{\s*$' 210 return result 211 endif 212 return 0 213endfunction 214 215function! scala#GetPrevCodeLine(lnum) 216 " This needs to skip comment lines 217 return prevnonblank(a:lnum - 1) 218endfunction 219 220function! scala#InvertBracketType(openBracket, closedBracket) 221 if a:openBracket == '(' 222 return [ '{', '}' ] 223 else 224 return [ '(', ')' ] 225 endif 226endfunction 227 228function! scala#Testhelper(lnum, line, openBracket, closedBracket, iteration) 229 let bracketCount = scala#CountBrackets(a:line, a:openBracket, a:closedBracket) 230 " There are more '}' braces than '{' on this line so it may be completing the function definition 231 if bracketCount < 0 232 let [matchedLNum, matchedColNum] = scala#GetLineAndColumnThatMatchesBracket(a:openBracket, a:closedBracket) 233 if matchedLNum == a:lnum 234 return -1 235 endif 236 let matchedLine = scala#GetLine(matchedLNum) 237 if ! scala#MatchesIncompleteDefValr(matchedLine) 238 let bracketLine = substitute(substitute(matchedLine, '\%' . matchedColNum . 'c.*$', '', ''), '[^{}()]', '', 'g') 239 if bracketLine =~ '}$' 240 return scala#Testhelper(matchedLNum, matchedLine, '{', '}', a:iteration + 1) 241 elseif bracketLine =~ ')$' 242 return scala#Testhelper(matchedLNum, matchedLine, '(', ')', a:iteration + 1) 243 else 244 let prevCodeLNum = scala#GetPrevCodeLine(matchedLNum) 245 if scala#MatchesIncompleteDefValr(scala#GetLine(prevCodeLNum)) 246 return prevCodeLNum 247 else 248 return -1 249 endif 250 endif 251 else 252 " return indent value instead 253 return matchedLNum 254 endif 255 " There's an equal number of '{' and '}' on this line so it may be a single line function definition 256 elseif bracketCount == 0 257 if a:iteration == 0 258 let otherBracketType = scala#InvertBracketType(a:openBracket, a:closedBracket) 259 return scala#Testhelper(a:lnum, a:line, otherBracketType[0], otherBracketType[1], a:iteration + 1) 260 else 261 let prevCodeLNum = scala#GetPrevCodeLine(a:lnum) 262 let prevCodeLine = scala#GetLine(prevCodeLNum) 263 if scala#MatchesIncompleteDefValr(prevCodeLine) && prevCodeLine !~ '{\s*$' 264 return prevCodeLNum 265 else 266 let possibleIfElse = scala#LineCompletesIfElse(a:lnum, a:line) 267 if possibleIfElse != 0 268 let defValrLine = prevnonblank(possibleIfElse - 1) 269 let possibleDefValr = scala#GetLine(defValrLine) 270 if scala#MatchesIncompleteDefValr(possibleDefValr) && possibleDefValr =~ '^.*=\s*$' 271 return possibleDefValr 272 else 273 return -1 274 endif 275 else 276 return -1 277 endif 278 endif 279 endif 280 else 281 return -1 282 endif 283endfunction 284 285function! scala#Test(lnum, line, openBracket, closedBracket) 286 return scala#Testhelper(a:lnum, a:line, a:openBracket, a:closedBracket, 0) 287endfunction 288 289function! scala#LineCompletesDefValr(lnum, line) 290 let bracketCount = scala#CountBrackets(a:line, '{', '}') 291 if bracketCount < 0 292 let matchedBracket = scala#GetLineThatMatchesBracket('{', '}') 293 if ! scala#MatchesIncompleteDefValr(scala#GetLine(matchedBracket)) 294 let possibleDefValr = scala#GetLine(prevnonblank(matchedBracket - 1)) 295 if matchedBracket != -1 && scala#MatchesIncompleteDefValr(possibleDefValr) 296 return 1 297 else 298 return 0 299 endif 300 else 301 return 0 302 endif 303 elseif bracketCount == 0 304 let bracketCount = scala#CountBrackets(a:line, '(', ')') 305 if bracketCount < 0 306 let matchedBracket = scala#GetLineThatMatchesBracket('(', ')') 307 if ! scala#MatchesIncompleteDefValr(scala#GetLine(matchedBracket)) 308 let possibleDefValr = scala#GetLine(prevnonblank(matchedBracket - 1)) 309 if matchedBracket != -1 && scala#MatchesIncompleteDefValr(possibleDefValr) 310 return 1 311 else 312 return 0 313 endif 314 else 315 return 0 316 endif 317 elseif bracketCount == 0 318 let possibleDefValr = scala#GetLine(prevnonblank(a:lnum - 1)) 319 if scala#MatchesIncompleteDefValr(possibleDefValr) && possibleDefValr =~ '^.*=\s*$' 320 return 1 321 else 322 let possibleIfElse = scala#LineCompletesIfElse(a:lnum, a:line) 323 if possibleIfElse != 0 324 let possibleDefValr = scala#GetLine(prevnonblank(possibleIfElse - 1)) 325 if scala#MatchesIncompleteDefValr(possibleDefValr) && possibleDefValr =~ '^.*=\s*$' 326 return 2 327 else 328 return 0 329 endif 330 else 331 return 0 332 endif 333 endif 334 else 335 return 0 336 endif 337 endif 338endfunction 339 340function! scala#SpecificLineCompletesBrackets(lnum, openBracket, closedBracket) 341 let savedpos = getpos('.') 342 call setpos('.', [savedpos[0], a:lnum, 9999, savedpos[3]]) 343 let retv = scala#LineCompletesBrackets(a:openBracket, a:closedBracket) 344 call setpos('.', savedpos) 345 346 return retv 347endfunction 348 349function! scala#LineCompletesBrackets(openBracket, closedBracket) 350 let savedpos = getpos('.') 351 let offline = 0 352 while offline == 0 353 let [lnum, colnum] = searchpos(a:closedBracket, 'Wb') 354 let [lnumA, colnumA] = searchpairpos(a:openBracket, '', a:closedBracket, 'Wbn') 355 if lnum != lnumA 356 let [lnumB, colnumB] = searchpairpos(a:openBracket, '', a:closedBracket, 'Wbnr') 357 let offline = 1 358 endif 359 endwhile 360 call setpos('.', savedpos) 361 if lnumA == lnumB && colnumA == colnumB 362 return lnumA 363 else 364 return -1 365 endif 366endfunction 367 368function! GetScalaIndent() 369 " Find a non-blank line above the current line. 370 let prevlnum = prevnonblank(v:lnum - 1) 371 372 " Hit the start of the file, use zero indent. 373 if prevlnum == 0 374 return 0 375 endif 376 377 let ind = indent(prevlnum) 378 let originalIndentValue = ind 379 let prevline = scala#GetLine(prevlnum) 380 let curlnum = v:lnum 381 let curline = scala#GetLine(curlnum) 382 if get(g:, 'scala_scaladoc_indent', 0) 383 let star_indent = 2 384 else 385 let star_indent = 1 386 end 387 388 if prevline =~ '^\s*/\*\*' 389 if prevline =~ '\*/\s*$' 390 return ind 391 else 392 return ind + star_indent 393 endif 394 endif 395 396 if curline =~ '^\s*\*' 397 return cindent(curlnum) 398 endif 399 400 " If this line starts with a { then make it indent the same as the previous line 401 if curline =~ '^\s*{' 402 call scala#ConditionalConfirm("1") 403 " Unless, of course, the previous one is a { as well 404 if prevline !~ '^\s*{' 405 call scala#ConditionalConfirm("2") 406 return indent(prevlnum) 407 endif 408 endif 409 410 " '.' continuations 411 if curline =~ '^\s*\.' 412 if prevline =~ '^\s*\.' 413 return ind 414 else 415 return ind + shiftwidth() 416 endif 417 endif 418 419 " Indent html literals 420 if prevline !~ '/>\s*$' && prevline =~ '^\s*<[a-zA-Z][^>]*>\s*$' 421 call scala#ConditionalConfirm("3") 422 return ind + shiftwidth() 423 endif 424 425 " assumes curly braces around try-block 426 if curline =~ '^\s*}\s*\<catch\>' 427 return ind - shiftwidth() 428 elseif curline =~ '^\s*\<catch\>' 429 return ind 430 endif 431 432 " Add a shiftwidth()' after lines that start a block 433 " If 'if', 'for' or 'while' end with ), this is a one-line block 434 " If 'val', 'var', 'def' end with =, this is a one-line block 435 if (prevline =~ '^\s*\<\%(\%(}\?\s*else\s\+\)\?if\|for\|while\)\>.*[)=]\s*$' && scala#NumberOfBraceGroups(prevline) <= 1) 436 \ || prevline =~ '^\s*' . s:defMatcher . '.*=\s*$' 437 \ || prevline =~ '^\s*\<va[lr]\>.*[=]\s*$' 438 \ || prevline =~ '^\s*\%(}\s*\)\?\<else\>\s*$' 439 \ || prevline =~ '=\s*$' 440 call scala#ConditionalConfirm("4") 441 let ind = ind + shiftwidth() 442 elseif prevline =~ '^\s*\<\%(}\?\s*else\s\+\)\?if\>' && curline =~ '^\s*}\?\s*\<else\>' 443 return ind 444 endif 445 446 let lineCompletedBrackets = 0 447 let bracketCount = scala#CountBrackets(prevline, '{', '}') 448 if bracketCount > 0 || prevline =~ '.*{\s*$' 449 call scala#ConditionalConfirm("5b") 450 let ind = ind + shiftwidth() 451 elseif bracketCount < 0 452 call scala#ConditionalConfirm("6b") 453 " if the closing brace actually completes the braces entirely, then we 454 " have to indent to line that started the whole thing 455 let completeLine = scala#LineCompletesBrackets('{', '}') 456 if completeLine != -1 457 call scala#ConditionalConfirm("8b") 458 let prevCompleteLine = scala#GetLine(prevnonblank(completeLine - 1)) 459 " However, what actually started this part looks like it was a function 460 " definition, so we need to indent to that line instead. This is 461 " actually pretty weak at the moment. 462 if prevCompleteLine =~ '=\s*$' 463 call scala#ConditionalConfirm("9b") 464 let ind = indent(prevnonblank(completeLine - 1)) 465 else 466 call scala#ConditionalConfirm("10b") 467 let ind = indent(completeLine) 468 endif 469 else 470 let lineCompletedBrackets = 1 471 endif 472 endif 473 474 if ind == originalIndentValue 475 let bracketCount = scala#CountBrackets(prevline, '(', ')') 476 if bracketCount > 0 || prevline =~ '.*(\s*$' 477 call scala#ConditionalConfirm("5a") 478 let ind = ind + shiftwidth() 479 elseif bracketCount < 0 480 call scala#ConditionalConfirm("6a") 481 " if the closing brace actually completes the braces entirely, then we 482 " have to indent to line that started the whole thing 483 let completeLine = scala#LineCompletesBrackets('(', ')') 484 if completeLine != -1 && prevline !~ '^.*{\s*$' 485 call scala#ConditionalConfirm("8a") 486 let prevCompleteLine = scala#GetLine(prevnonblank(completeLine - 1)) 487 " However, what actually started this part looks like it was a function 488 " definition, so we need to indent to that line instead. This is 489 " actually pretty weak at the moment. 490 if prevCompleteLine =~ '=\s*$' 491 call scala#ConditionalConfirm("9a") 492 let ind = indent(prevnonblank(completeLine - 1)) 493 else 494 call scala#ConditionalConfirm("10a") 495 let ind = indent(completeLine) 496 endif 497 else 498 " This is the only part that's different from from the '{', '}' one below 499 " Yup... some refactoring is necessary at some point. 500 let ind = ind + (bracketCount * shiftwidth()) 501 let lineCompletedBrackets = 1 502 endif 503 endif 504 endif 505 506 if curline =~ '^\s*}\?\s*\<else\>\%(\s\+\<if\>\s*(.*)\)\?\s*{\?\s*$' && 507 \ ! scala#LineIsCompleteIf(prevline) && 508 \ prevline !~ '^.*}\s*$' 509 let ind = ind - shiftwidth() 510 endif 511 512 " Subtract a shiftwidth()' on '}' or html 513 let curCurlyCount = scala#CountCurlies(curline) 514 if curCurlyCount < 0 515 call scala#ConditionalConfirm("14a") 516 let matchline = scala#CurlyMatcher() 517 return indent(matchline) 518 elseif curline =~ '^\s*</[a-zA-Z][^>]*>' 519 call scala#ConditionalConfirm("14c") 520 return ind - shiftwidth() 521 endif 522 523 let prevParenCount = scala#CountParens(prevline) 524 if prevline =~ '^\s*\<for\>.*$' && prevParenCount > 0 525 call scala#ConditionalConfirm("15") 526 let ind = indent(prevlnum) + 5 527 endif 528 529 let prevCurlyCount = scala#CountCurlies(prevline) 530 if prevCurlyCount == 0 && prevline =~ '^.*\%(=>\|⇒\)\s*$' && prevline !~ '^\s*this\s*:.*\%(=>\|⇒\)\s*$' && curline !~ '^\s*\<case\>' 531 call scala#ConditionalConfirm("16") 532 let ind = ind + shiftwidth() 533 endif 534 535 if ind == originalIndentValue && curline =~ '^\s*\<case\>' 536 call scala#ConditionalConfirm("17") 537 let parentCase = scala#IsParentCase() 538 if parentCase != -1 539 call scala#ConditionalConfirm("17a") 540 return indent(parentCase) 541 endif 542 endif 543 544 if prevline =~ '^\s*\*/' 545 \ || prevline =~ '*/\s*$' 546 call scala#ConditionalConfirm("18") 547 let ind = ind - star_indent 548 endif 549 550 if scala#LineEndsInIncomplete(prevline) 551 call scala#ConditionalConfirm("19") 552 return ind 553 endif 554 555 if scala#LineIsAClosingXML(prevline) 556 if scala#LineCompletesXML(prevlnum, prevline) 557 call scala#ConditionalConfirm("20a") 558 return ind - shiftwidth() 559 else 560 call scala#ConditionalConfirm("20b") 561 return ind 562 endif 563 endif 564 565 if ind == originalIndentValue 566 "let indentMultiplier = scala#LineCompletesDefValr(prevlnum, prevline) 567 "if indentMultiplier != 0 568 " call scala#ConditionalConfirm("19a") 569 " let ind = ind - (indentMultiplier * shiftwidth()) 570 let defValrLine = scala#Test(prevlnum, prevline, '{', '}') 571 if defValrLine != -1 572 call scala#ConditionalConfirm("21a") 573 let ind = indent(defValrLine) 574 elseif lineCompletedBrackets == 0 575 call scala#ConditionalConfirm("21b") 576 if scala#GetLine(prevnonblank(prevlnum - 1)) =~ '^.*\<else\>\s*\%(//.*\)\?$' 577 call scala#ConditionalConfirm("21c") 578 let ind = ind - shiftwidth() 579 elseif scala#LineCompletesIfElse(prevlnum, prevline) 580 call scala#ConditionalConfirm("21d") 581 let ind = ind - shiftwidth() 582 elseif scala#CountParens(curline) < 0 && curline =~ '^\s*)' && scala#GetLine(scala#GetLineThatMatchesBracket('(', ')')) =~ '.*(\s*$' 583 " Handles situations that look like this: 584 " 585 " val a = func( 586 " 10 587 " ) 588 " 589 " or 590 " 591 " val a = func( 592 " 10 593 " ).somethingHere() 594 call scala#ConditionalConfirm("21e") 595 let ind = ind - shiftwidth() 596 endif 597 endif 598 endif 599 600 call scala#ConditionalConfirm("returning " . ind) 601 602 return ind 603endfunction 604 605let &cpo = s:keepcpo 606unlet s:keepcpo 607 608" vim:set sw=2 sts=2 ts=8 et: 609" vim600:fdm=marker fdl=1 fdc=0: 610