Patch 7.0.084
[MacVim.git] / runtime / indent / ada.vim
blobf36ff9abbcabd33dd97ba59208e8beb432124343
1 " Vim indent file
2 " Language:     Ada
3 " Maintainer:   Neil Bird <neil@fnxweb.com>
4 " Last Change:  2006 Apr 30
5 " Version:      $Id: ada.vim,v 1.3 2006/04/30 18:31:36 vimboss Exp $
6 " Look for the latest version at http://vim.sourceforge.net/
8 " ToDo:
9 "  Verify handling of multi-line exprs. and recovery upon the final ';'.
10 "  Correctly find comments given '"' and "" ==> " syntax.
11 "  Combine the two large block-indent functions into one?
13 " Only load this indent file when no other was loaded.
14 if exists("b:did_indent")
15    finish
16 endif
17 let b:did_indent = 1
19 setlocal indentexpr=GetAdaIndent()
20 setlocal indentkeys-=0{,0}
21 setlocal indentkeys+=0=~then,0=~end,0=~elsif,0=~when,0=~exception,0=~begin,0=~is,0=~record
23 " Only define the functions once.
24 if exists("*GetAdaIndent")
25    finish
26 endif
28 let s:AdaBlockStart = '^\s*\(if\>\|while\>\|else\>\|elsif\>\|loop\>\|for\>.*\<\(loop\|use\)\>\|declare\>\|begin\>\|type\>.*\<is\>[^;]*$\|\(type\>.*\)\=\<record\>\|procedure\>\|function\>\|accept\>\|do\>\|task\>\|package\>\|then\>\|when\>\|is\>\)'
29 let s:AdaComment = "\\v^(\"[^\"]*\"|'.'|[^\"']){-}\\zs\\s*--.*"
32 " Try to find indent of the block we're in
33 " prev_indent = the previous line's indent
34 " prev_lnum   = previous line (to start looking on)
35 " blockstart  = expr. that indicates a possible start of this block
36 " stop_at     = if non-null, if a matching line is found, gives up!
37 " No recursive previous block analysis: simply look for a valid line
38 " with a lesser or equal indent than we currently (on prev_lnum) have.
39 " This shouldn't work as well as it appears to with lines that are currently
40 " nowhere near the correct indent (e.g., start of line)!
41 " Seems to work OK as it 'starts' with the indent of the /previous/ line.
42 function s:MainBlockIndent( prev_indent, prev_lnum, blockstart, stop_at )
43    let lnum = a:prev_lnum
44    let line = substitute( getline(lnum), s:AdaComment, '', '' )
45    while lnum > 1
46       if a:stop_at != ''  &&  line =~ '^\s*' . a:stop_at  &&  indent(lnum) < a:prev_indent
47          return a:prev_indent
48       elseif line =~ '^\s*' . a:blockstart
49          let ind = indent(lnum)
50          if ind < a:prev_indent
51             return ind
52          endif
53       endif
55       let lnum = prevnonblank(lnum - 1)
56       " Get previous non-blank/non-comment-only line
57       while 1
58          let line = substitute( getline(lnum), s:AdaComment, '', '' )
59          if line !~ '^\s*$' && line !~ '^\s*#'
60             break
61          endif
62          let lnum = prevnonblank(lnum - 1)
63          if lnum <= 0
64             return a:prev_indent
65          endif
66       endwhile
67    endwhile
68    " Fallback - just move back one
69    return a:prev_indent - &sw
70 endfunction
72 " Try to find indent of the block we're in (and about to complete),
73 " including handling of nested blocks. Works on the 'end' of a block.
74 " prev_indent = the previous line's indent
75 " prev_lnum   = previous line (to start looking on)
76 " blockstart  = expr. that indicates a possible start of this block
77 " blockend    = expr. that indicates a possible end of this block
78 function s:EndBlockIndent( prev_indent, prev_lnum, blockstart, blockend )
79    let lnum = a:prev_lnum
80    let line = getline(lnum)
81    let ends = 0
82    while lnum > 1
83       if getline(lnum) =~ '^\s*' . a:blockstart
84          let ind = indent(lnum)
85          if ends <= 0
86             if ind < a:prev_indent
87                return ind
88             endif
89          else
90             let ends = ends - 1
91          endif
92       elseif getline(lnum) =~ '^\s*' . a:blockend
93          let ends = ends + 1
94       endif
96       let lnum = prevnonblank(lnum - 1)
97       " Get previous non-blank/non-comment-only line
98       while 1
99          let line = getline(lnum)
100          let line = substitute( line, s:AdaComment, '', '' )
101          if line !~ '^\s*$'
102             break
103          endif
104          let lnum = prevnonblank(lnum - 1)
105          if lnum <= 0
106             return a:prev_indent
107          endif
108       endwhile
109    endwhile
110    " Fallback - just move back one
111    return a:prev_indent - &sw
112 endfunction
114 " Return indent of previous statement-start
115 " (after we've indented due to multi-line statements).
116 " This time, we start searching on the line *before* the one given (which is
117 " the end of a statement - we want the previous beginning).
118 function s:StatementIndent( current_indent, prev_lnum )
119    let lnum  = a:prev_lnum
120    while lnum > 0
121       let prev_lnum = lnum
122       let lnum = prevnonblank(lnum - 1)
123       " Get previous non-blank/non-comment-only line
124       while 1
125          let line = substitute( getline(lnum), s:AdaComment, '', '' )
126          if line !~ '^\s*$' && line !~ '^\s*#'
127             break
128          endif
129          let lnum = prevnonblank(lnum - 1)
130          if lnum <= 0
131             return a:current_indent
132          endif
133       endwhile
134       " Leave indent alone if our ';' line is part of a ';'-delineated
135       " aggregate (e.g., procedure args.) or first line after a block start.
136       if line =~ s:AdaBlockStart || line =~ '(\s*$'
137          return a:current_indent
138       endif
139       if line !~ '[.=(]\s*$'
140          let ind = indent(prev_lnum)
141          if ind < a:current_indent
142             return ind
143          endif
144       endif
145    endwhile
146    " Fallback - just use current one
147    return a:current_indent
148 endfunction
151 " Find correct indent of a new line based upon what went before
152 function GetAdaIndent()
153    " Find a non-blank line above the current line.
154    let lnum = prevnonblank(v:lnum - 1)
155    let ind = indent(lnum)
156    let package_line = 0
158    " Get previous non-blank/non-comment-only/non-cpp line
159    while 1
160       let line = substitute( getline(lnum), s:AdaComment, '', '' )
161       if line !~ '^\s*$' && line !~ '^\s*#'
162          break
163       endif
164       let lnum = prevnonblank(lnum - 1)
165       if lnum <= 0
166          return ind
167       endif
168    endwhile
170    " Get default indent (from prev. line)
171    let ind = indent(lnum)
172    let initind = ind
174    " Now check what's on the previous line
175    if line =~ s:AdaBlockStart  ||  line =~ '(\s*$'
176       " Check for false matches to AdaBlockStart
177       let false_match = 0
178       if line =~ '^\s*\(procedure\|function\|package\)\>.*\<is\s*new\>'
179          " Generic instantiation
180          let false_match = 1
181       elseif line =~ ')\s*;\s*$'  ||  line =~ '^\([^(]*([^)]*)\)*[^(]*;\s*$'
182          " forward declaration
183          let false_match = 1
184       endif
185       " Move indent in
186       if ! false_match
187          let ind = ind + &sw
188       endif
189    elseif line =~ '^\s*\(case\|exception\)\>'
190       " Move indent in twice (next 'when' will move back)
191       let ind = ind + 2 * &sw
192    elseif line =~ '^\s*end\s*record\>'
193       " Move indent back to tallying 'type' preceeding the 'record'.
194       " Allow indent to be equal to 'end record's.
195       let ind = s:MainBlockIndent( ind+&sw, lnum, 'type\>', '' )
196    elseif line =~ '\(^\s*new\>.*\)\@<!)\s*[;,]\s*$'
197       " Revert to indent of line that started this parenthesis pair
198       exe lnum
199       exe 'normal! $F)%'
200       if getline('.') =~ '^\s*('
201          " Dire layout - use previous indent (could check for AdaComment here)
202          let ind = indent( prevnonblank( line('.')-1 ) )
203       else
204          let ind = indent('.')
205       endif
206       exe v:lnum
207    elseif line =~ '[.=(]\s*$'
208       " A statement continuation - move in one
209       let ind = ind + &sw
210    elseif line =~ '^\s*new\>'
211       " Multiple line generic instantiation ('package blah is\nnew thingy')
212       let ind = s:StatementIndent( ind - &sw, lnum )
213    elseif line =~ ';\s*$'
214       " Statement end (but not 'end' ) - try to find current statement-start indent
215       let ind = s:StatementIndent( ind, lnum )
216    endif
218    " Check for potential argument list on next line
219    let continuation = (line =~ '[A-Za-z0-9_]\s*$')
222    " Check current line; search for simplistic matching start-of-block
223    let line = getline(v:lnum)
224    if line =~ '^\s*#'
225       " Start of line for ada-pp
226       let ind = 0
227    elseif continuation && line =~ '^\s*('
228       " Don't do this if we've already indented due to the previous line
229       if ind == initind
230          let ind = ind + &sw
231       endif
232    elseif line =~ '^\s*\(begin\|is\)\>'
233       let ind = s:MainBlockIndent( ind, lnum, '\(procedure\|function\|declare\|package\|task\)\>', 'begin\>' )
234    elseif line =~ '^\s*record\>'
235       let ind = s:MainBlockIndent( ind, lnum, 'type\>\|for\>.*\<use\>', '' ) + &sw
236    elseif line =~ '^\s*\(else\|elsif\)\>'
237       let ind = s:MainBlockIndent( ind, lnum, 'if\>', '' )
238    elseif line =~ '^\s*when\>'
239       " Align 'when' one /in/ from matching block start
240       let ind = s:MainBlockIndent( ind, lnum, '\(case\|exception\)\>', '' ) + &sw
241    elseif line =~ '^\s*end\>\s*\<if\>'
242       " End of if statements
243       let ind = s:EndBlockIndent( ind, lnum, 'if\>', 'end\>\s*\<if\>' )
244    elseif line =~ '^\s*end\>\s*\<loop\>'
245       " End of loops
246       let ind = s:EndBlockIndent( ind, lnum, '\(\(while\|for\)\>.*\)\?\<loop\>', 'end\>\s*\<loop\>' )
247    elseif line =~ '^\s*end\>\s*\<record\>'
248       " End of records
249       let ind = s:EndBlockIndent( ind, lnum, '\(type\>.*\)\=\<record\>', 'end\>\s*\<record\>' )
250    elseif line =~ '^\s*end\>\s*\<procedure\>'
251       " End of procedures
252       let ind = s:EndBlockIndent( ind, lnum, 'procedure\>.*\<is\>', 'end\>\s*\<procedure\>' )
253    elseif line =~ '^\s*end\>\s*\<case\>'
254       " End of case statement
255       let ind = s:EndBlockIndent( ind, lnum, 'case\>.*\<is\>', 'end\>\s*\<case\>' )
256    elseif line =~ '^\s*end\>'
257       " General case for end
258       let ind = s:MainBlockIndent( ind, lnum, '\(if\|while\|for\|loop\|accept\|begin\|record\|case\|exception\|package\)\>', '' )
259    elseif line =~ '^\s*exception\>'
260       let ind = s:MainBlockIndent( ind, lnum, 'begin\>', '' )
261    elseif line =~ '^\s*then\>'
262       let ind = s:MainBlockIndent( ind, lnum, 'if\>', '' )
263    endif
265    return ind
266 endfunction
268 " vim: set sw=3 sts=3 :