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