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