xref: /vim-8.2.3635/runtime/indent/ada.vim (revision 00a927d6)
1"------------------------------------------------------------------------------
2"  Description: Vim Ada indent file
3"     Language: Ada (2005)
4"	   $Id: ada.vim 887 2008-07-08 14:29:01Z krischik $
5"    Copyright: Copyright (C) 2006 Martin Krischik
6"   Maintainer: Martin Krischik <[email protected]>
7"		Neil Bird <[email protected]>
8"		Ned Okie <[email protected]>
9"      $Author: krischik $
10"	 $Date: 2008-07-08 16:29:01 +0200 (Di, 08 Jul 2008) $
11"      Version: 4.6
12"    $Revision: 887 $
13"     $HeadURL: https://gnuada.svn.sourceforge.net/svnroot/gnuada/trunk/tools/vim/indent/ada.vim $
14"      History: 24.05.2006 MK Unified Headers
15"		16.07.2006 MK Ada-Mode as vim-ball
16"		15.10.2006 MK Bram's suggestion for runtime integration
17"		05.11.2006 MK Bram suggested to save on spaces
18"		19.09.2007 NO g: missing before ada#Comment
19"    Help Page: ft-vim-indent
20"------------------------------------------------------------------------------
21" ToDo:
22"  Verify handling of multi-line exprs. and recovery upon the final ';'.
23"  Correctly find comments given '"' and "" ==> " syntax.
24"  Combine the two large block-indent functions into one?
25"------------------------------------------------------------------------------
26
27" Only load this indent file when no other was loaded.
28if exists("b:did_indent") || version < 700
29   finish
30endif
31
32let b:did_indent = 45
33
34setlocal indentexpr=GetAdaIndent()
35setlocal indentkeys-=0{,0}
36setlocal indentkeys+=0=~then,0=~end,0=~elsif,0=~when,0=~exception,0=~begin,0=~is,0=~record
37
38" Only define the functions once.
39if exists("*GetAdaIndent")
40   finish
41endif
42
43if exists("g:ada_with_gnat_project_files")
44   let s:AdaBlockStart = '^\s*\(if\>\|while\>\|else\>\|elsif\>\|loop\>\|for\>.*\<\(loop\|use\)\>\|declare\>\|begin\>\|type\>.*\<is\>[^;]*$\|\(type\>.*\)\=\<record\>\|procedure\>\|function\>\|accept\>\|do\>\|task\>\|package\>\|project\>\|then\>\|when\>\|is\>\)'
45else
46   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\>\)'
47endif
48
49" Section: s:MainBlockIndent {{{1
50"
51" Try to find indent of the block we're in
52" prev_indent = the previous line's indent
53" prev_lnum   = previous line (to start looking on)
54" blockstart  = expr. that indicates a possible start of this block
55" stop_at     = if non-null, if a matching line is found, gives up!
56" No recursive previous block analysis: simply look for a valid line
57" with a lesser or equal indent than we currently (on prev_lnum) have.
58" This shouldn't work as well as it appears to with lines that are currently
59" nowhere near the correct indent (e.g., start of line)!
60" Seems to work OK as it 'starts' with the indent of the /previous/ line.
61function s:MainBlockIndent (prev_indent, prev_lnum, blockstart, stop_at)
62   let lnum = a:prev_lnum
63   let line = substitute( getline(lnum), g:ada#Comment, '', '' )
64   while lnum > 1
65      if a:stop_at != ''  &&  line =~ '^\s*' . a:stop_at  &&  indent(lnum) < a:prev_indent
66	 return a:prev_indent
67      elseif line =~ '^\s*' . a:blockstart
68	 let ind = indent(lnum)
69	 if ind < a:prev_indent
70	    return ind
71	 endif
72      endif
73
74      let lnum = prevnonblank(lnum - 1)
75      " Get previous non-blank/non-comment-only line
76      while 1
77	 let line = substitute( getline(lnum), g:ada#Comment, '', '' )
78	 if line !~ '^\s*$' && line !~ '^\s*#'
79	    break
80	 endif
81	 let lnum = prevnonblank(lnum - 1)
82	 if lnum <= 0
83	    return a:prev_indent
84	 endif
85      endwhile
86   endwhile
87   " Fallback - just move back one
88   return a:prev_indent - &sw
89endfunction MainBlockIndent
90
91" Section: s:EndBlockIndent {{{1
92"
93" Try to find indent of the block we're in (and about to complete),
94" including handling of nested blocks. Works on the 'end' of a block.
95" prev_indent = the previous line's indent
96" prev_lnum   = previous line (to start looking on)
97" blockstart  = expr. that indicates a possible start of this block
98" blockend    = expr. that indicates a possible end of this block
99function s:EndBlockIndent( prev_indent, prev_lnum, blockstart, blockend )
100   let lnum = a:prev_lnum
101   let line = getline(lnum)
102   let ends = 0
103   while lnum > 1
104      if getline(lnum) =~ '^\s*' . a:blockstart
105	 let ind = indent(lnum)
106	 if ends <= 0
107	    if ind < a:prev_indent
108	       return ind
109	    endif
110	 else
111	    let ends = ends - 1
112	 endif
113      elseif getline(lnum) =~ '^\s*' . a:blockend
114	 let ends = ends + 1
115      endif
116
117      let lnum = prevnonblank(lnum - 1)
118      " Get previous non-blank/non-comment-only line
119      while 1
120	 let line = getline(lnum)
121	 let line = substitute( line, g:ada#Comment, '', '' )
122	 if line !~ '^\s*$'
123	    break
124	 endif
125	 let lnum = prevnonblank(lnum - 1)
126	 if lnum <= 0
127	    return a:prev_indent
128	 endif
129      endwhile
130   endwhile
131   " Fallback - just move back one
132   return a:prev_indent - &sw
133endfunction EndBlockIndent
134
135" Section: s:StatementIndent {{{1
136"
137" Return indent of previous statement-start
138" (after we've indented due to multi-line statements).
139" This time, we start searching on the line *before* the one given (which is
140" the end of a statement - we want the previous beginning).
141function s:StatementIndent( current_indent, prev_lnum )
142   let lnum  = a:prev_lnum
143   while lnum > 0
144      let prev_lnum = lnum
145      let lnum = prevnonblank(lnum - 1)
146      " Get previous non-blank/non-comment-only line
147      while 1
148	 let line = substitute( getline(lnum), g:ada#Comment, '', '' )
149
150	 if line !~ '^\s*$' && line !~ '^\s*#'
151	    break
152	 endif
153	 let lnum = prevnonblank(lnum - 1)
154	 if lnum <= 0
155	    return a:current_indent
156	 endif
157      endwhile
158      " Leave indent alone if our ';' line is part of a ';'-delineated
159      " aggregate (e.g., procedure args.) or first line after a block start.
160      if line =~ s:AdaBlockStart || line =~ '(\s*$'
161	 return a:current_indent
162      endif
163      if line !~ '[.=(]\s*$'
164	 let ind = indent(prev_lnum)
165	 if ind < a:current_indent
166	    return ind
167	 endif
168      endif
169   endwhile
170   " Fallback - just use current one
171   return a:current_indent
172endfunction StatementIndent
173
174
175" Section: GetAdaIndent {{{1
176"
177" Find correct indent of a new line based upon what went before
178"
179function GetAdaIndent()
180   " Find a non-blank line above the current line.
181   let lnum = prevnonblank(v:lnum - 1)
182   let ind = indent(lnum)
183   let package_line = 0
184
185   " Get previous non-blank/non-comment-only/non-cpp line
186   while 1
187      let line = substitute( getline(lnum), g:ada#Comment, '', '' )
188      if line !~ '^\s*$' && line !~ '^\s*#'
189	 break
190      endif
191      let lnum = prevnonblank(lnum - 1)
192      if lnum <= 0
193	 return ind
194      endif
195   endwhile
196
197   " Get default indent (from prev. line)
198   let ind = indent(lnum)
199   let initind = ind
200
201   " Now check what's on the previous line
202   if line =~ s:AdaBlockStart  ||  line =~ '(\s*$'
203      " Check for false matches to AdaBlockStart
204      let false_match = 0
205      if line =~ '^\s*\(procedure\|function\|package\)\>.*\<is\s*new\>'
206	 " Generic instantiation
207	 let false_match = 1
208      elseif line =~ ')\s*;\s*$'  ||  line =~ '^\([^(]*([^)]*)\)*[^(]*;\s*$'
209	 " forward declaration
210	 let false_match = 1
211      endif
212      " Move indent in
213      if ! false_match
214	 let ind = ind + &sw
215      endif
216   elseif line =~ '^\s*\(case\|exception\)\>'
217      " Move indent in twice (next 'when' will move back)
218      let ind = ind + 2 * &sw
219   elseif line =~ '^\s*end\s*record\>'
220      " Move indent back to tallying 'type' preceeding the 'record'.
221      " Allow indent to be equal to 'end record's.
222      let ind = s:MainBlockIndent( ind+&sw, lnum, 'type\>', '' )
223   elseif line =~ '\(^\s*new\>.*\)\@<!)\s*[;,]\s*$'
224      " Revert to indent of line that started this parenthesis pair
225      exe lnum
226      exe 'normal! $F)%'
227      if getline('.') =~ '^\s*('
228	 " Dire layout - use previous indent (could check for g:ada#Comment here)
229	 let ind = indent( prevnonblank( line('.')-1 ) )
230      else
231	 let ind = indent('.')
232      endif
233      exe v:lnum
234   elseif line =~ '[.=(]\s*$'
235      " A statement continuation - move in one
236      let ind = ind + &sw
237   elseif line =~ '^\s*new\>'
238      " Multiple line generic instantiation ('package blah is\nnew thingy')
239      let ind = s:StatementIndent( ind - &sw, lnum )
240   elseif line =~ ';\s*$'
241      " Statement end (but not 'end' ) - try to find current statement-start indent
242      let ind = s:StatementIndent( ind, lnum )
243   endif
244
245   " Check for potential argument list on next line
246   let continuation = (line =~ '[A-Za-z0-9_]\s*$')
247
248
249   " Check current line; search for simplistic matching start-of-block
250   let line = getline(v:lnum)
251   if line =~ '^\s*#'
252      " Start of line for ada-pp
253      let ind = 0
254   elseif continuation && line =~ '^\s*('
255      " Don't do this if we've already indented due to the previous line
256      if ind == initind
257	 let ind = ind + &sw
258      endif
259   elseif line =~ '^\s*\(begin\|is\)\>'
260      let ind = s:MainBlockIndent( ind, lnum, '\(procedure\|function\|declare\|package\|task\)\>', 'begin\>' )
261   elseif line =~ '^\s*record\>'
262      let ind = s:MainBlockIndent( ind, lnum, 'type\>\|for\>.*\<use\>', '' ) + &sw
263   elseif line =~ '^\s*\(else\|elsif\)\>'
264      let ind = s:MainBlockIndent( ind, lnum, 'if\>', '' )
265   elseif line =~ '^\s*when\>'
266      " Align 'when' one /in/ from matching block start
267      let ind = s:MainBlockIndent( ind, lnum, '\(case\|exception\)\>', '' ) + &sw
268   elseif line =~ '^\s*end\>\s*\<if\>'
269      " End of if statements
270      let ind = s:EndBlockIndent( ind, lnum, 'if\>', 'end\>\s*\<if\>' )
271   elseif line =~ '^\s*end\>\s*\<loop\>'
272      " End of loops
273      let ind = s:EndBlockIndent( ind, lnum, '\(\(while\|for\)\>.*\)\?\<loop\>', 'end\>\s*\<loop\>' )
274   elseif line =~ '^\s*end\>\s*\<record\>'
275      " End of records
276      let ind = s:EndBlockIndent( ind, lnum, '\(type\>.*\)\=\<record\>', 'end\>\s*\<record\>' )
277   elseif line =~ '^\s*end\>\s*\<procedure\>'
278      " End of procedures
279      let ind = s:EndBlockIndent( ind, lnum, 'procedure\>.*\<is\>', 'end\>\s*\<procedure\>' )
280   elseif line =~ '^\s*end\>\s*\<case\>'
281      " End of case statement
282      let ind = s:EndBlockIndent( ind, lnum, 'case\>.*\<is\>', 'end\>\s*\<case\>' )
283   elseif line =~ '^\s*end\>'
284      " General case for end
285      let ind = s:MainBlockIndent( ind, lnum, '\(if\|while\|for\|loop\|accept\|begin\|record\|case\|exception\|package\)\>', '' )
286   elseif line =~ '^\s*exception\>'
287      let ind = s:MainBlockIndent( ind, lnum, 'begin\>', '' )
288   elseif line =~ '^\s*then\>'
289      let ind = s:MainBlockIndent( ind, lnum, 'if\>', '' )
290   endif
291
292   return ind
293endfunction GetAdaIndent
294
295finish " 1}}}
296
297"------------------------------------------------------------------------------
298"   Copyright (C) 2006	Martin Krischik
299"
300"   Vim is Charityware - see ":help license" or uganda.txt for licence details.
301"------------------------------------------------------------------------------
302" vim: textwidth=78 wrap tabstop=8 shiftwidth=3 softtabstop=3 noexpandtab
303" vim: foldmethod=marker
304