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 <krischik@users.sourceforge.net> 7" Neil Bird <neil@fnxweb.com> 8" Ned Okie <nokie@radford.edu> 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 42let s:keepcpo= &cpo 43set cpo&vim 44 45if exists("g:ada_with_gnat_project_files") 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\>\|project\>\|then\>\|when\>\|is\>\)' 47else 48 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\>\)' 49endif 50 51" Section: s:MainBlockIndent {{{1 52" 53" Try to find indent of the block we're in 54" prev_indent = the previous line's indent 55" prev_lnum = previous line (to start looking on) 56" blockstart = expr. that indicates a possible start of this block 57" stop_at = if non-null, if a matching line is found, gives up! 58" No recursive previous block analysis: simply look for a valid line 59" with a lesser or equal indent than we currently (on prev_lnum) have. 60" This shouldn't work as well as it appears to with lines that are currently 61" nowhere near the correct indent (e.g., start of line)! 62" Seems to work OK as it 'starts' with the indent of the /previous/ line. 63function s:MainBlockIndent (prev_indent, prev_lnum, blockstart, stop_at) 64 let lnum = a:prev_lnum 65 let line = substitute( getline(lnum), g:ada#Comment, '', '' ) 66 while lnum > 1 67 if a:stop_at != '' && line =~ '^\s*' . a:stop_at && indent(lnum) < a:prev_indent 68 return a:prev_indent 69 elseif line =~ '^\s*' . a:blockstart 70 let ind = indent(lnum) 71 if ind < a:prev_indent 72 return ind 73 endif 74 endif 75 76 let lnum = prevnonblank(lnum - 1) 77 " Get previous non-blank/non-comment-only line 78 while 1 79 let line = substitute( getline(lnum), g:ada#Comment, '', '' ) 80 if line !~ '^\s*$' && line !~ '^\s*#' 81 break 82 endif 83 let lnum = prevnonblank(lnum - 1) 84 if lnum <= 0 85 return a:prev_indent 86 endif 87 endwhile 88 endwhile 89 " Fallback - just move back one 90 return a:prev_indent - shiftwidth() 91endfunction MainBlockIndent 92 93" Section: s:EndBlockIndent {{{1 94" 95" Try to find indent of the block we're in (and about to complete), 96" including handling of nested blocks. Works on the 'end' of a block. 97" prev_indent = the previous line's indent 98" prev_lnum = previous line (to start looking on) 99" blockstart = expr. that indicates a possible start of this block 100" blockend = expr. that indicates a possible end of this block 101function s:EndBlockIndent( prev_indent, prev_lnum, blockstart, blockend ) 102 let lnum = a:prev_lnum 103 let line = getline(lnum) 104 let ends = 0 105 while lnum > 1 106 if getline(lnum) =~ '^\s*' . a:blockstart 107 let ind = indent(lnum) 108 if ends <= 0 109 if ind < a:prev_indent 110 return ind 111 endif 112 else 113 let ends = ends - 1 114 endif 115 elseif getline(lnum) =~ '^\s*' . a:blockend 116 let ends = ends + 1 117 endif 118 119 let lnum = prevnonblank(lnum - 1) 120 " Get previous non-blank/non-comment-only line 121 while 1 122 let line = getline(lnum) 123 let line = substitute( line, g:ada#Comment, '', '' ) 124 if line !~ '^\s*$' 125 break 126 endif 127 let lnum = prevnonblank(lnum - 1) 128 if lnum <= 0 129 return a:prev_indent 130 endif 131 endwhile 132 endwhile 133 " Fallback - just move back one 134 return a:prev_indent - shiftwidth() 135endfunction EndBlockIndent 136 137" Section: s:StatementIndent {{{1 138" 139" Return indent of previous statement-start 140" (after we've indented due to multi-line statements). 141" This time, we start searching on the line *before* the one given (which is 142" the end of a statement - we want the previous beginning). 143function s:StatementIndent( current_indent, prev_lnum ) 144 let lnum = a:prev_lnum 145 while lnum > 0 146 let prev_lnum = lnum 147 let lnum = prevnonblank(lnum - 1) 148 " Get previous non-blank/non-comment-only line 149 while 1 150 let line = substitute( getline(lnum), g:ada#Comment, '', '' ) 151 152 if line !~ '^\s*$' && line !~ '^\s*#' 153 break 154 endif 155 let lnum = prevnonblank(lnum - 1) 156 if lnum <= 0 157 return a:current_indent 158 endif 159 endwhile 160 " Leave indent alone if our ';' line is part of a ';'-delineated 161 " aggregate (e.g., procedure args.) or first line after a block start. 162 if line =~ s:AdaBlockStart || line =~ '(\s*$' 163 return a:current_indent 164 endif 165 if line !~ '[.=(]\s*$' 166 let ind = indent(prev_lnum) 167 if ind < a:current_indent 168 return ind 169 endif 170 endif 171 endwhile 172 " Fallback - just use current one 173 return a:current_indent 174endfunction StatementIndent 175 176 177" Section: GetAdaIndent {{{1 178" 179" Find correct indent of a new line based upon what went before 180" 181function GetAdaIndent() 182 " Find a non-blank line above the current line. 183 let lnum = prevnonblank(v:lnum - 1) 184 let ind = indent(lnum) 185 let package_line = 0 186 187 " Get previous non-blank/non-comment-only/non-cpp line 188 while 1 189 let line = substitute( getline(lnum), g:ada#Comment, '', '' ) 190 if line !~ '^\s*$' && line !~ '^\s*#' 191 break 192 endif 193 let lnum = prevnonblank(lnum - 1) 194 if lnum <= 0 195 return ind 196 endif 197 endwhile 198 199 " Get default indent (from prev. line) 200 let ind = indent(lnum) 201 let initind = ind 202 203 " Now check what's on the previous line 204 if line =~ s:AdaBlockStart || line =~ '(\s*$' 205 " Check for false matches to AdaBlockStart 206 let false_match = 0 207 if line =~ '^\s*\(procedure\|function\|package\)\>.*\<is\s*new\>' 208 " Generic instantiation 209 let false_match = 1 210 elseif line =~ ')\s*;\s*$' || line =~ '^\([^(]*([^)]*)\)*[^(]*;\s*$' 211 " forward declaration 212 let false_match = 1 213 endif 214 " Move indent in 215 if ! false_match 216 let ind = ind + shiftwidth() 217 endif 218 elseif line =~ '^\s*\(case\|exception\)\>' 219 " Move indent in twice (next 'when' will move back) 220 let ind = ind + 2 * shiftwidth() 221 elseif line =~ '^\s*end\s*record\>' 222 " Move indent back to tallying 'type' preceding the 'record'. 223 " Allow indent to be equal to 'end record's. 224 let ind = s:MainBlockIndent( ind+shiftwidth(), lnum, 'type\>', '' ) 225 elseif line =~ '\(^\s*new\>.*\)\@<!)\s*[;,]\s*$' 226 " Revert to indent of line that started this parenthesis pair 227 exe lnum 228 exe 'normal! $F)%' 229 if getline('.') =~ '^\s*(' 230 " Dire layout - use previous indent (could check for g:ada#Comment here) 231 let ind = indent( prevnonblank( line('.')-1 ) ) 232 else 233 let ind = indent('.') 234 endif 235 exe v:lnum 236 elseif line =~ '[.=(]\s*$' 237 " A statement continuation - move in one 238 let ind = ind + shiftwidth() 239 elseif line =~ '^\s*new\>' 240 " Multiple line generic instantiation ('package blah is\nnew thingy') 241 let ind = s:StatementIndent( ind - shiftwidth(), lnum ) 242 elseif line =~ ';\s*$' 243 " Statement end (but not 'end' ) - try to find current statement-start indent 244 let ind = s:StatementIndent( ind, lnum ) 245 endif 246 247 " Check for potential argument list on next line 248 let continuation = (line =~ '[A-Za-z0-9_]\s*$') 249 250 251 " Check current line; search for simplistic matching start-of-block 252 let line = getline(v:lnum) 253 if line =~ '^\s*#' 254 " Start of line for ada-pp 255 let ind = 0 256 elseif continuation && line =~ '^\s*(' 257 " Don't do this if we've already indented due to the previous line 258 if ind == initind 259 let ind = ind + shiftwidth() 260 endif 261 elseif line =~ '^\s*\(begin\|is\)\>' 262 let ind = s:MainBlockIndent( ind, lnum, '\(procedure\|function\|declare\|package\|task\)\>', 'begin\>' ) 263 elseif line =~ '^\s*record\>' 264 let ind = s:MainBlockIndent( ind, lnum, 'type\>\|for\>.*\<use\>', '' ) + shiftwidth() 265 elseif line =~ '^\s*\(else\|elsif\)\>' 266 let ind = s:MainBlockIndent( ind, lnum, 'if\>', '' ) 267 elseif line =~ '^\s*when\>' 268 " Align 'when' one /in/ from matching block start 269 let ind = s:MainBlockIndent( ind, lnum, '\(case\|exception\)\>', '' ) + shiftwidth() 270 elseif line =~ '^\s*end\>\s*\<if\>' 271 " End of if statements 272 let ind = s:EndBlockIndent( ind, lnum, 'if\>', 'end\>\s*\<if\>' ) 273 elseif line =~ '^\s*end\>\s*\<loop\>' 274 " End of loops 275 let ind = s:EndBlockIndent( ind, lnum, '\(\(while\|for\)\>.*\)\?\<loop\>', 'end\>\s*\<loop\>' ) 276 elseif line =~ '^\s*end\>\s*\<record\>' 277 " End of records 278 let ind = s:EndBlockIndent( ind, lnum, '\(type\>.*\)\=\<record\>', 'end\>\s*\<record\>' ) 279 elseif line =~ '^\s*end\>\s*\<procedure\>' 280 " End of procedures 281 let ind = s:EndBlockIndent( ind, lnum, 'procedure\>.*\<is\>', 'end\>\s*\<procedure\>' ) 282 elseif line =~ '^\s*end\>\s*\<case\>' 283 " End of case statement 284 let ind = s:EndBlockIndent( ind, lnum, 'case\>.*\<is\>', 'end\>\s*\<case\>' ) 285 elseif line =~ '^\s*end\>' 286 " General case for end 287 let ind = s:MainBlockIndent( ind, lnum, '\(if\|while\|for\|loop\|accept\|begin\|record\|case\|exception\|package\)\>', '' ) 288 elseif line =~ '^\s*exception\>' 289 let ind = s:MainBlockIndent( ind, lnum, 'begin\>', '' ) 290 elseif line =~ '^\s*then\>' 291 let ind = s:MainBlockIndent( ind, lnum, 'if\>', '' ) 292 endif 293 294 return ind 295endfunction GetAdaIndent 296 297let &cpo = s:keepcpo 298unlet s:keepcpo 299 300finish " 1}}} 301 302"------------------------------------------------------------------------------ 303" Copyright (C) 2006 Martin Krischik 304" 305" Vim is Charityware - see ":help license" or uganda.txt for licence details. 306"------------------------------------------------------------------------------ 307" vim: textwidth=78 wrap tabstop=8 shiftwidth=3 softtabstop=3 noexpandtab 308" vim: foldmethod=marker 309