ada.vim 11 KB

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