blob: 72e2e80e5547a46c7e0e474140c61d0701670ef9 [file] [log] [blame]
Bram Moolenaar071d4272004-06-13 20:20:40 +00001" Vim indent file
2" Language: Ada
3" Maintainer: Neil Bird <neil@fnxweb.com>
4" Last Change: 2003 May 20
5" Version: $Id$
6" Look for the latest version at http://vim.sourceforge.net/
7"
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?
12
13" Only load this indent file when no other was loaded.
14if exists("b:did_indent")
15 finish
16endif
17let b:did_indent = 1
18
19setlocal indentexpr=GetAdaIndent()
20setlocal indentkeys-=0{,0}
21setlocal indentkeys+=0=~then,0=~end,0=~elsif,0=~when,0=~exception,0=~begin,0=~is,0=~record
22
23" Only define the functions once.
24if exists("*GetAdaIndent")
25 finish
26endif
27
28let s:AdaBlockStart = '^\s*\(if\>\|while\>\|else\>\|elsif\>\|loop\>\|for\>.*\<loop\>\|declare\>\|begin\>\|type\>.*\<is\s*$\|\(type\>.*\)\=\<record\>\|procedure\>\|function\>\|accept\>\|do\>\|task\>\|package\>\|then\>\|when\>\|is\>\)'
29let s:AdaComment = "\\v^(\"[^\"]*\"|'.'|[^\"']){-}\\zs\\s*--.*"
30
31
32" Try to find indent of the block we're in (and about to complete)
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.
42function 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 -1
48 elseif line =~ '^\s*' . a:blockstart
49 let ind = indent(lnum)
50 if ind < a:prev_indent
51 return ind
52 endif
53 endif
54
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
70endfunction
71
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
78function 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
95
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
112endfunction
113
114" As per MainBlockIndent, but 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).
118function 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
148endfunction
149
150
151" Find correct indent of a new line based upon what went before
152function 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
157
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
169
170 " Get default indent (from prev. line)
171 let ind = indent(lnum)
172
173 " Now check what's on the previous line
174 if line =~ s:AdaBlockStart || line =~ '(\s*$'
175 " Check for false matches to AdaBlockStart
176 let false_match = 0
177 if line =~ '^\s*\(procedure\|function\|package\)\>.*\<is\s*new\>'
178 " Generic instantiation
179 let false_match = 1
180 elseif line =~ ')\s*;\s*$' || line =~ '^\([^(]*([^)]*)\)*[^(]*;\s*$'
181 " forward declaration
182 let false_match = 1
183 endif
184 " Move indent in
185 if ! false_match
186 let ind = ind + &sw
187 endif
188 elseif line =~ '^\s*\(case\|exception\)\>'
189 " Move indent in twice (next 'when' will move back)
190 let ind = ind + 2 * &sw
191 elseif line =~ '^\s*end\s*record\>'
192 " Move indent back to tallying 'type' preceeding the 'record'.
193 " Allow indent to be equal to 'end record's.
194 let ind = s:MainBlockIndent( ind+&sw, lnum, 'type\>', '' )
195 elseif line =~ '\(^\s*new\>.*\)\@<!)\s*[;,]\s*$'
196 " Revert to indent of line that started this parenthesis pair
197 exe lnum
198 exe 'normal! $F)%'
199 if getline('.') =~ '^\s*('
200 " Dire layout - use previous indent (could check for AdaComment here)
201 let ind = indent( prevnonblank( line('.')-1 ) )
202 else
203 let ind = indent('.')
204 endif
205 exe v:lnum
206 elseif line =~ '[.=(]\s*$'
207 " A statement continuation - move in one
208 let ind = ind + &sw
209 elseif line =~ '^\s*new\>'
210 " Multiple line generic instantiation ('package blah is\nnew thingy')
211 let ind = s:StatementIndent( ind - &sw, lnum )
212 elseif line =~ ';\s*$'
213 " Statement end - try to find current statement-start indent
214 let ind = s:StatementIndent( ind, lnum )
215 endif
216
217 " Check for potential argument list on next line
218 let continuation = (line =~ '[A-Za-z0-9_]\s*$')
219
220
221 " Check current line; search for simplistic matching start-of-block
222 let line = getline(v:lnum)
223 if line =~ '^\s*#'
224 " Start of line for ada-pp
225 let ind = 0
226 elseif continuation && line =~ '^\s*('
227 let ind = ind + &sw
228 elseif line =~ '^\s*\(begin\|is\)\>'
229 let ind = s:MainBlockIndent( ind, lnum, '\(procedure\|function\|declare\|package\|task\)\>', 'begin\>' )
230 elseif line =~ '^\s*record\>'
231 let ind = s:MainBlockIndent( ind, lnum, 'type\>', '' ) + &sw
232 elseif line =~ '^\s*\(else\|elsif\)\>'
233 let ind = s:MainBlockIndent( ind, lnum, 'if\>', '' )
234 elseif line =~ '^\s*when\>'
235 " Align 'when' one /in/ from matching block start
236 let ind = s:MainBlockIndent( ind, lnum, '\(case\|exception\)\>', '' ) + &sw
237 elseif line =~ '^\s*end\>\s*\<if\>'
238 " End of if statements
239 let ind = s:EndBlockIndent( ind, lnum, 'if\>', 'end\>\s*\<if\>' )
240 elseif line =~ '^\s*end\>\s*\<loop\>'
241 " End of loops
242 let ind = s:EndBlockIndent( ind, lnum, '\(\(while\|for\)\>.*\)\?\<loop\>', 'end\>\s*\<loop\>' )
243 elseif line =~ '^\s*end\>\s*\<record\>'
244 " End of records
245 let ind = s:EndBlockIndent( ind, lnum, '\(type\>.*\)\=\<record\>', 'end\>\s*\<record\>' )
246 elseif line =~ '^\s*end\>\s*\<procedure\>'
247 " End of procedures
248 let ind = s:EndBlockIndent( ind, lnum, 'procedure\>.*\<is\>', 'end\>\s*\<procedure\>' )
249 elseif line =~ '^\s*end\>\s*\<case\>'
250 " End of case statement
251 let ind = s:EndBlockIndent( ind, lnum, 'case\>.*\<is\>', 'end\>\s*\<case\>' )
252 elseif line =~ '^\s*end\>'
253 " General case for end
254 let ind = s:MainBlockIndent( ind, lnum, '\(if\|while\|for\|loop\|accept\|begin\|record\|case\|exception\)\>', '' )
255 elseif line =~ '^\s*exception\>'
256 let ind = s:MainBlockIndent( ind, lnum, 'begin\>', '' )
257 elseif line =~ '^\s*then\>'
258 let ind = s:MainBlockIndent( ind, lnum, 'if\>', '' )
259 endif
260
261 return ind
262endfunction
263
264" vim: set sw=3 sts=3 :