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