blob: 9601181bde27af9203ec16a638fa2244aba3b45d [file] [log] [blame]
Bram Moolenaar071d4272004-06-13 20:20:40 +00001" Vim indent file
2" Language: Ada
3" Maintainer: Neil Bird <neil@fnxweb.com>
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00004" Last Change: 2004 Nov 23
Bram Moolenaar071d4272004-06-13 20:20:40 +00005" 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
Bram Moolenaar293ee4d2004-12-09 21:34:53 +000028let 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\>\)'
Bram Moolenaar071d4272004-06-13 20:20:40 +000029let s:AdaComment = "\\v^(\"[^\"]*\"|'.'|[^\"']){-}\\zs\\s*--.*"
30
31
Bram Moolenaar293ee4d2004-12-09 21:34:53 +000032" Try to find indent of the block we're in
Bram Moolenaar071d4272004-06-13 20:20:40 +000033" 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
Bram Moolenaar293ee4d2004-12-09 21:34:53 +000047 return a:prev_indent
Bram Moolenaar071d4272004-06-13 20:20:40 +000048 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
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000114" Return indent of previous statement-start
Bram Moolenaar071d4272004-06-13 20:20:40 +0000115" (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)
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000172 let initind = ind
Bram Moolenaar071d4272004-06-13 20:20:40 +0000173
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*$'
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000214 " Statement end (but not 'end' ) - try to find current statement-start indent
Bram Moolenaar071d4272004-06-13 20:20:40 +0000215 let ind = s:StatementIndent( ind, lnum )
216 endif
217
218 " Check for potential argument list on next line
219 let continuation = (line =~ '[A-Za-z0-9_]\s*$')
220
221
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*('
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000228 " 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
Bram Moolenaar071d4272004-06-13 20:20:40 +0000232 elseif line =~ '^\s*\(begin\|is\)\>'
233 let ind = s:MainBlockIndent( ind, lnum, '\(procedure\|function\|declare\|package\|task\)\>', 'begin\>' )
234 elseif line =~ '^\s*record\>'
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000235 let ind = s:MainBlockIndent( ind, lnum, 'type\>\|for\>.*\<use\>', '' ) + &sw
Bram Moolenaar071d4272004-06-13 20:20:40 +0000236 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
Bram Moolenaar293ee4d2004-12-09 21:34:53 +0000258 let ind = s:MainBlockIndent( ind, lnum, '\(if\|while\|for\|loop\|accept\|begin\|record\|case\|exception\|package\)\>', '' )
Bram Moolenaar071d4272004-06-13 20:20:40 +0000259 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
264
265 return ind
266endfunction
267
268" vim: set sw=3 sts=3 :