Add bug number.
[bpt/emacs.git] / lisp / progmodes / ada-mode.el
1 ;;; ada-mode.el --- major-mode for editing Ada sources
2
3 ;; Copyright (C) 1994, 1995, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
4 ;; 2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
5
6 ;; Author: Rolf Ebert <ebert@inf.enst.fr>
7 ;; Markus Heritsch <Markus.Heritsch@studbox.uni-stuttgart.de>
8 ;; Emmanuel Briot <briot@gnat.com>
9 ;; Maintainer: Stephen Leake <stephen_leake@member.fsf.org>
10 ;; Keywords: languages ada
11
12 ;; This file is part of GNU Emacs.
13
14 ;; GNU Emacs is free software: you can redistribute it and/or modify
15 ;; it under the terms of the GNU General Public License as published by
16 ;; the Free Software Foundation, either version 3 of the License, or
17 ;; (at your option) any later version.
18
19 ;; GNU Emacs is distributed in the hope that it will be useful,
20 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
21 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 ;; GNU General Public License for more details.
23
24 ;; You should have received a copy of the GNU General Public License
25 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
26
27 ;;; Commentary:
28 ;; This mode is a major mode for editing Ada code. This is a major
29 ;; rewrite of the file packaged with Emacs-20. The Ada mode is
30 ;; composed of four Lisp files: ada-mode.el, ada-xref.el, ada-prj.el
31 ;; and ada-stmt.el. Only this file (ada-mode.el) is completely
32 ;; independent from the GNU Ada compiler GNAT, distributed by Ada
33 ;; Core Technologies. All the other files rely heavily on features
34 ;; provided only by GNAT.
35 ;;
36 ;; Note: this mode will not work with Emacs 19. If you are on a VMS
37 ;; system, where the latest version of Emacs is 19.28, you will need
38 ;; another file, called ada-vms.el, that provides some required
39 ;; functions.
40
41 ;;; Usage:
42 ;; Emacs should enter Ada mode automatically when you load an Ada file.
43 ;; By default, the valid extensions for Ada files are .ads, .adb or .ada
44 ;; If the ada-mode does not start automatically, then simply type the
45 ;; following command :
46 ;; M-x ada-mode
47 ;;
48 ;; By default, ada-mode is configured to take full advantage of the GNAT
49 ;; compiler (the menus will include the cross-referencing features,...).
50 ;; If you are using another compiler, you might want to set the following
51 ;; variable in your .emacs (Note: do not set this in the ada-mode-hook, it
52 ;; won't work) :
53 ;; (setq ada-which-compiler 'generic)
54 ;;
55 ;; This mode requires find-file.el to be present on your system.
56
57 ;;; History:
58 ;; The first Ada mode for GNU Emacs was written by V. Broman in
59 ;; 1985. He based his work on the already existing Modula-2 mode.
60 ;; This was distributed as ada.el in versions of Emacs prior to 19.29.
61 ;;
62 ;; Lynn Slater wrote an extensive Ada mode in 1989. It consisted of
63 ;; several files with support for dired commands and other nice
64 ;; things. It is currently available from the PAL
65 ;; (wuarchive.wustl.edu:/languages/ada) as ada-mode-1.06a.tar.Z.
66 ;;
67 ;; The probably very first Ada mode (called electric-ada.el) was
68 ;; written by Steven D. Litvintchouk and Steven M. Rosen for the
69 ;; Gosling Emacs. L. Slater based his development on ada.el and
70 ;; electric-ada.el.
71 ;;
72 ;; A complete rewrite by M. Heritsch and R. Ebert has been done.
73 ;; Some ideas from the Ada mode mailing list have been
74 ;; added. Some of the functionality of L. Slater's mode has not
75 ;; (yet) been recoded in this new mode. Perhaps you prefer sticking
76 ;; to his version.
77 ;;
78 ;; A complete rewrite for Emacs-20 / GNAT-3.11 has been done by Ada Core
79 ;; Technologies.
80
81 ;;; Credits:
82 ;; Many thanks to John McCabe <john@assen.demon.co.uk> for sending so
83 ;; many patches included in this package.
84 ;; Christian Egli <Christian.Egli@hcsd.hac.com>:
85 ;; ada-imenu-generic-expression
86 ;; Many thanks also to the following persons that have contributed
87 ;; to the ada-mode
88 ;; Philippe Waroquiers (PW) <philippe@cfmu.eurocontrol.be> in particular,
89 ;; woodruff@stc.llnl.gov (John Woodruff)
90 ;; jj@ddci.dk (Jesper Joergensen)
91 ;; gse@ocsystems.com (Scott Evans)
92 ;; comar@gnat.com (Cyrille Comar)
93 ;; stephen.leake@gsfc.nasa.gov (Stephen Leake)
94 ;; robin-reply@reagans.org
95 ;; and others for their valuable hints.
96
97 ;;; Code:
98 ;; Note: Every function in this package is compiler-independent.
99 ;; The names start with ada-
100 ;; The variables that the user can edit can all be modified through
101 ;; the customize mode. They are sorted in alphabetical order in this
102 ;; file.
103
104 ;; Supported packages.
105 ;; This package supports a number of other Emacs modes. These other modes
106 ;; should be loaded before the ada-mode, which will then setup some variables
107 ;; to improve the support for Ada code.
108 ;; Here is the list of these modes:
109 ;; `which-function-mode': Display the name of the subprogram the cursor is
110 ;; in in the mode line.
111 ;; `outline-mode': Provides the capability to collapse or expand the code
112 ;; for specific language constructs, for instance if you want to hide the
113 ;; code corresponding to a subprogram
114 ;; `align': This mode is now provided with Emacs 21, but can also be
115 ;; installed manually for older versions of Emacs. It provides the
116 ;; capability to automatically realign the selected region (for instance
117 ;; all ':=', ':' and '--' will be aligned on top of each other.
118 ;; `imenu': Provides a menu with the list of entities defined in the current
119 ;; buffer, and an easy way to jump to any of them
120 ;; `speedbar': Provides a separate file browser, and the capability for each
121 ;; file to see the list of entities defined in it and to jump to them
122 ;; easily
123 ;; `abbrev-mode': Provides the capability to define abbreviations, which
124 ;; are automatically expanded when you type them. See the Emacs manual.
125
126 (require 'find-file nil t)
127 (require 'align nil t)
128 (require 'which-func nil t)
129 (require 'compile nil t)
130
131 (defvar ispell-check-comments)
132 (defvar skeleton-further-elements)
133
134 (defun ada-mode-version ()
135 "Return Ada mode version."
136 (interactive)
137 (let ((version-string "4.00"))
138 (if (called-interactively-p 'interactive)
139 (message version-string)
140 version-string)))
141
142 (defvar ada-mode-hook nil
143 "*List of functions to call when Ada mode is invoked.
144 This hook is automatically executed after the `ada-mode' is
145 fully loaded.
146 This is a good place to add Ada environment specific bindings.")
147
148 (defgroup ada nil
149 "Major mode for editing and compiling Ada source in Emacs."
150 :link '(custom-group-link :tag "Font Lock Faces group" font-lock-faces)
151 :group 'languages)
152
153 (defcustom ada-auto-case t
154 "*Non-nil means automatically change case of preceding word while typing.
155 Casing is done according to `ada-case-keyword', `ada-case-identifier'
156 and `ada-case-attribute'."
157 :type 'boolean :group 'ada)
158
159 (defcustom ada-broken-decl-indent 0
160 "*Number of columns to indent a broken declaration.
161
162 An example is :
163 declare
164 A,
165 >>>>>B : Integer;"
166 :type 'integer :group 'ada)
167
168 (defcustom ada-broken-indent 2
169 "*Number of columns to indent the continuation of a broken line.
170
171 An example is :
172 My_Var : My_Type := (Field1 =>
173 >>>>>>>>>Value);"
174 :type 'integer :group 'ada)
175
176 (defcustom ada-continuation-indent ada-broken-indent
177 "*Number of columns to indent the continuation of broken lines in parenthesis.
178
179 An example is :
180 Func (Param1,
181 >>>>>Param2);"
182 :type 'integer :group 'ada)
183
184 (defcustom ada-case-attribute 'ada-capitalize-word
185 "*Function to call to adjust the case of Ada attributes.
186 It may be `downcase-word', `upcase-word', `ada-loose-case-word',
187 `ada-capitalize-word' or `ada-no-auto-case'."
188 :type '(choice (const downcase-word)
189 (const upcase-word)
190 (const ada-capitalize-word)
191 (const ada-loose-case-word)
192 (const ada-no-auto-case))
193 :group 'ada)
194
195 (defcustom ada-case-exception-file
196 (list (convert-standard-filename' "~/.emacs_case_exceptions"))
197 "*List of special casing exceptions dictionaries for identifiers.
198 The first file is the one where new exceptions will be saved by Emacs
199 when you call `ada-create-case-exception'.
200
201 These files should contain one word per line, that gives the casing
202 to be used for that word in Ada files. If the line starts with the
203 character *, then the exception will be used for substrings that either
204 start at the beginning of a word or after a _ character, and end either
205 at the end of the word or at a _ character. Each line can be terminated
206 by a comment."
207 :type '(repeat (file))
208 :group 'ada)
209
210 (defcustom ada-case-keyword 'downcase-word
211 "*Function to call to adjust the case of an Ada keywords.
212 It may be `downcase-word', `upcase-word', `ada-loose-case-word' or
213 `ada-capitalize-word'."
214 :type '(choice (const downcase-word)
215 (const upcase-word)
216 (const ada-capitalize-word)
217 (const ada-loose-case-word)
218 (const ada-no-auto-case))
219 :group 'ada)
220
221 (defcustom ada-case-identifier 'ada-loose-case-word
222 "*Function to call to adjust the case of an Ada identifier.
223 It may be `downcase-word', `upcase-word', `ada-loose-case-word' or
224 `ada-capitalize-word'."
225 :type '(choice (const downcase-word)
226 (const upcase-word)
227 (const ada-capitalize-word)
228 (const ada-loose-case-word)
229 (const ada-no-auto-case))
230 :group 'ada)
231
232 (defcustom ada-clean-buffer-before-saving t
233 "*Non-nil means remove trailing spaces and untabify the buffer before saving."
234 :type 'boolean :group 'ada)
235 (make-obsolete-variable 'ada-clean-buffer-before-saving
236 "use the `write-file-functions' hook."
237 "23.2")
238
239
240 (defcustom ada-indent 3
241 "*Size of Ada indentation.
242
243 An example is :
244 procedure Foo is
245 begin
246 >>>>>>>>>>null;"
247 :type 'integer :group 'ada)
248
249 (defcustom ada-indent-after-return t
250 "*Non-nil means automatically indent after RET or LFD."
251 :type 'boolean :group 'ada)
252
253 (defcustom ada-indent-align-comments t
254 "*Non-nil means align comments on previous line comments, if any.
255 If nil, indentation is calculated as usual.
256 Note that indentation is calculated only if `ada-indent-comment-as-code' is t.
257
258 For instance:
259 A := 1; -- A multi-line comment
260 -- aligned if ada-indent-align-comments is t"
261 :type 'boolean :group 'ada)
262
263 (defcustom ada-indent-comment-as-code t
264 "*Non-nil means indent comment lines as code.
265 A nil value means do not auto-indent comments."
266 :type 'boolean :group 'ada)
267
268 (defcustom ada-indent-handle-comment-special nil
269 "*Non-nil if comment lines should be handled specially inside parenthesis.
270 By default, if the line that contains the open parenthesis has some
271 text following it, then the following lines will be indented in the
272 same column as this text. This will not be true if the first line is
273 a comment and `ada-indent-handle-comment-special' is t.
274
275 type A is
276 ( Value_1, -- common behavior, when not a comment
277 Value_2);
278
279 type A is
280 ( -- `ada-indent-handle-comment-special' is nil
281 Value_1,
282 Value_2);
283
284 type A is
285 ( -- `ada-indent-handle-comment-special' is non-nil
286 Value_1,
287 Value_2);"
288 :type 'boolean :group 'ada)
289
290 (defcustom ada-indent-is-separate t
291 "*Non-nil means indent 'is separate' or 'is abstract' if on a single line."
292 :type 'boolean :group 'ada)
293
294 (defcustom ada-indent-record-rel-type 3
295 "*Indentation for 'record' relative to 'type' or 'use'.
296
297 An example is:
298 type A is
299 >>>>>>>>>>>record"
300 :type 'integer :group 'ada)
301
302 (defcustom ada-indent-renames ada-broken-indent
303 "*Indentation for renames relative to the matching function statement.
304 If `ada-indent-return' is null or negative, the indentation is done relative to
305 the open parenthesis (if there is no parenthesis, `ada-broken-indent' is used).
306
307 An example is:
308 function A (B : Integer)
309 return C;
310 >>>renames Foo;"
311 :type 'integer :group 'ada)
312
313 (defcustom ada-indent-return 0
314 "*Indentation for 'return' relative to the matching 'function' statement.
315 If `ada-indent-return' is null or negative, the indentation is done relative to
316 the open parenthesis (if there is no parenthesis, `ada-broken-indent' is used).
317
318 An example is:
319 function A (B : Integer)
320 >>>>>return C;"
321 :type 'integer :group 'ada)
322
323 (defcustom ada-indent-to-open-paren t
324 "*Non-nil means indent according to the innermost open parenthesis."
325 :type 'boolean :group 'ada)
326
327 (defcustom ada-fill-comment-prefix "-- "
328 "*Text inserted in the first columns when filling a comment paragraph.
329 Note: if you modify this variable, you will have to invoke `ada-mode'
330 again to take account of the new value."
331 :type 'string :group 'ada)
332
333 (defcustom ada-fill-comment-postfix " --"
334 "*Text inserted at the end of each line when filling a comment paragraph.
335 Used by `ada-fill-comment-paragraph-postfix'."
336 :type 'string :group 'ada)
337
338 (defcustom ada-label-indent -4
339 "*Number of columns to indent a label.
340
341 An example is:
342 procedure Foo is
343 begin
344 >>>>Label:
345
346 This is also used for <<..>> labels"
347 :type 'integer :group 'ada)
348
349 (defcustom ada-language-version 'ada95
350 "*Ada language version; one of `ada83', `ada95', `ada2005'."
351 :type '(choice (const ada83) (const ada95) (const ada2005)) :group 'ada)
352
353 (defcustom ada-move-to-declaration nil
354 "*Non-nil means `ada-move-to-start' moves to the subprogram declaration, not to 'begin'."
355 :type 'boolean :group 'ada)
356
357 (defcustom ada-popup-key '[down-mouse-3]
358 "*Key used for binding the contextual menu.
359 If nil, no contextual menu is available."
360 :type '(restricted-sexp :match-alternatives (stringp vectorp))
361 :group 'ada)
362
363 (defcustom ada-search-directories
364 (append '(".")
365 (split-string (or (getenv "ADA_INCLUDE_PATH") "") ":")
366 '("/usr/adainclude" "/usr/local/adainclude"
367 "/opt/gnu/adainclude"))
368 "*Default list of directories to search for Ada files.
369 See the description for the `ff-search-directories' variable. This variable
370 is the initial value of `ada-search-directories-internal'."
371 :type '(repeat (choice :tag "Directory"
372 (const :tag "default" nil)
373 (directory :format "%v")))
374 :group 'ada)
375
376 (defvar ada-search-directories-internal ada-search-directories
377 "Internal version of `ada-search-directories'.
378 Its value is the concatenation of the search path as read in the project file
379 and the standard runtime location, and the value of the user-defined
380 `ada-search-directories'.")
381
382 (defcustom ada-stmt-end-indent 0
383 "*Number of columns to indent the end of a statement on a separate line.
384
385 An example is:
386 if A = B
387 >>>>then"
388 :type 'integer :group 'ada)
389
390 (defcustom ada-tab-policy 'indent-auto
391 "*Control the behavior of the TAB key.
392 Must be one of :
393 `indent-rigidly' : always adds `ada-indent' blanks at the beginning of the line.
394 `indent-auto' : use indentation functions in this file.
395 `always-tab' : do `indent-relative'."
396 :type '(choice (const indent-auto)
397 (const indent-rigidly)
398 (const always-tab))
399 :group 'ada)
400
401 (defcustom ada-use-indent ada-broken-indent
402 "*Indentation for the lines in a 'use' statement.
403
404 An example is:
405 use Ada.Text_IO,
406 >>>>Ada.Numerics;"
407 :type 'integer :group 'ada)
408
409 (defcustom ada-when-indent 3
410 "*Indentation for 'when' relative to 'exception' or 'case'.
411
412 An example is:
413 case A is
414 >>>>when B =>"
415 :type 'integer :group 'ada)
416
417 (defcustom ada-with-indent ada-broken-indent
418 "*Indentation for the lines in a 'with' statement.
419
420 An example is:
421 with Ada.Text_IO,
422 >>>>Ada.Numerics;"
423 :type 'integer :group 'ada)
424
425 (defcustom ada-which-compiler 'gnat
426 "*Name of the compiler to use.
427 This will determine what features are made available through the Ada mode.
428 The possible choices are:
429 `gnat': Use Ada Core Technologies' GNAT compiler. Add some cross-referencing
430 features.
431 `generic': Use a generic compiler."
432 :type '(choice (const gnat)
433 (const generic))
434 :group 'ada)
435
436
437 ;;; ---- end of user configurable variables
438 \f
439
440 (defvar ada-body-suffixes '(".adb")
441 "List of possible suffixes for Ada body files.
442 The extensions should include a `.' if needed.")
443
444 (defvar ada-spec-suffixes '(".ads")
445 "List of possible suffixes for Ada spec files.
446 The extensions should include a `.' if needed.")
447
448 (defvar ada-mode-menu (make-sparse-keymap "Ada")
449 "Menu for Ada mode.")
450
451 (defvar ada-mode-map (make-sparse-keymap)
452 "Local keymap used for Ada mode.")
453
454 (defvar ada-mode-extra-map (make-sparse-keymap)
455 "Keymap used for non-standard keybindings.")
456
457 ;; default is C-c C-q because it's free in ada-mode-map
458 (defvar ada-mode-extra-prefix "\C-c\C-q"
459 "Prefix key to access `ada-mode-extra-map' functions.")
460
461 (defvar ada-mode-abbrev-table nil
462 "Local abbrev table for Ada mode.")
463
464 (defvar ada-mode-syntax-table nil
465 "Syntax table to be used for editing Ada source code.")
466
467 (defvar ada-mode-symbol-syntax-table nil
468 "Syntax table for Ada, where `_' is a word constituent.")
469
470 (eval-when-compile
471 ;; These values are used in eval-when-compile expressions.
472 (defconst ada-83-string-keywords
473 '("abort" "abs" "accept" "access" "all" "and" "array" "at" "begin"
474 "body" "case" "constant" "declare" "delay" "delta" "digits" "do"
475 "else" "elsif" "end" "entry" "exception" "exit" "for" "function"
476 "generic" "goto" "if" "in" "is" "limited" "loop" "mod" "new"
477 "not" "null" "of" "or" "others" "out" "package" "pragma" "private"
478 "procedure" "raise" "range" "record" "rem" "renames" "return"
479 "reverse" "select" "separate" "subtype" "task" "terminate" "then"
480 "type" "use" "when" "while" "with" "xor")
481 "List of Ada 83 keywords.
482 Used to define `ada-*-keywords'.")
483
484 (defconst ada-95-string-keywords
485 '("abstract" "aliased" "protected" "requeue" "tagged" "until")
486 "List of keywords new in Ada 95.
487 Used to define `ada-*-keywords'.")
488
489 (defconst ada-2005-string-keywords
490 '("interface" "overriding" "synchronized")
491 "List of keywords new in Ada 2005.
492 Used to define `ada-*-keywords.'"))
493
494 (defvar ada-ret-binding nil
495 "Variable to save key binding of RET when casing is activated.")
496
497 (defvar ada-case-exception '()
498 "Alist of words (entities) that have special casing.")
499
500 (defvar ada-case-exception-substring '()
501 "Alist of substrings (entities) that have special casing.
502 The substrings are detected for word constituant when the word
503 is not itself in `ada-case-exception', and only for substrings that
504 either are at the beginning or end of the word, or start after '_'.")
505
506 (defvar ada-lfd-binding nil
507 "Variable to save key binding of LFD when casing is activated.")
508
509 (defvar ada-other-file-alist nil
510 "Variable used by `find-file' to find the name of the other package.
511 See `ff-other-file-alist'.")
512
513 (defvar ada-align-list
514 '(("[^:]\\(\\s-*\\):[^:]" 1 t)
515 ("[^=]\\(\\s-+\\)=[^=]" 1 t)
516 ("\\(\\s-*\\)use\\s-" 1)
517 ("\\(\\s-*\\)--" 1))
518 "Ada support for align.el <= 2.2.
519 This variable provides regular expressions on which to align different lines.
520 See `align-mode-alist' for more information.")
521
522 (defvar ada-align-modes
523 '((ada-declaration
524 (regexp . "[^:]\\(\\s-*\\):[^:]")
525 (valid . (lambda() (not (ada-in-comment-p))))
526 (modes . '(ada-mode)))
527 (ada-assignment
528 (regexp . "[^=]\\(\\s-+\\)=[^=]")
529 (valid . (lambda() (not (ada-in-comment-p))))
530 (modes . '(ada-mode)))
531 (ada-comment
532 (regexp . "\\(\\s-*\\)--")
533 (modes . '(ada-mode)))
534 (ada-use
535 (regexp . "\\(\\s-*\\)use\\s-")
536 (valid . (lambda() (not (ada-in-comment-p))))
537 (modes . '(ada-mode)))
538 )
539 "Ada support for align.el >= 2.8.
540 This variable defines several rules to use to align different lines.")
541
542 (defconst ada-align-region-separate
543 (eval-when-compile
544 (concat
545 "^\\s-*\\($\\|\\("
546 "begin\\|"
547 "declare\\|"
548 "else\\|"
549 "end\\|"
550 "exception\\|"
551 "for\\|"
552 "function\\|"
553 "generic\\|"
554 "if\\|"
555 "is\\|"
556 "procedure\\|"
557 "record\\|"
558 "return\\|"
559 "type\\|"
560 "when"
561 "\\)\\>\\)"))
562 "See the variable `align-region-separate' for more information.")
563
564 ;;; ---- Below are the regexp used in this package for parsing
565
566 (defconst ada-83-keywords
567 (eval-when-compile
568 (concat "\\<" (regexp-opt ada-83-string-keywords t) "\\>"))
569 "Regular expression matching Ada83 keywords.")
570
571 (defconst ada-95-keywords
572 (eval-when-compile
573 (concat "\\<" (regexp-opt
574 (append
575 ada-95-string-keywords
576 ada-83-string-keywords) t) "\\>"))
577 "Regular expression matching Ada95 keywords.")
578
579 (defconst ada-2005-keywords
580 (eval-when-compile
581 (concat "\\<" (regexp-opt
582 (append
583 ada-2005-string-keywords
584 ada-83-string-keywords
585 ada-95-string-keywords) t) "\\>"))
586 "Regular expression matching Ada2005 keywords.")
587
588 (defvar ada-keywords ada-2005-keywords
589 "Regular expression matching Ada keywords.")
590 ;; FIXME: make this customizable
591
592 (defconst ada-ident-re
593 "[[:alpha:]]\\(?:[_[:alnum:]]\\)*"
594 ;; [:alnum:] matches any multibyte word constituent, as well as
595 ;; Latin-1 letters and numbers. This allows __ and trailing _;
596 ;; someone (emacs bug#1919) proposed [^\W_] to fix that, but \W does
597 ;; _not_ mean "not word constituent" inside a character alternative.
598 "Regexp matching an Ada identifier.")
599
600 (defconst ada-goto-label-re
601 (concat "<<" ada-ident-re ">>")
602 "Regexp matching a goto label.")
603
604 (defconst ada-block-label-re
605 (concat ada-ident-re "[ \t\n]*:[^=]")
606 "Regexp matching a block label.
607 Note that this also matches a variable declaration.")
608
609 (defconst ada-label-re
610 (concat "\\(?:" ada-block-label-re "\\)\\|\\(?:" ada-goto-label-re "\\)")
611 "Regexp matching a goto or block label.")
612
613 ;; "with" needs to be included in the regexp, to match generic subprogram parameters
614 ;; Similarly, we put '[not] overriding' on the same line with 'procedure' etc.
615 (defvar ada-procedure-start-regexp
616 (concat
617 "^[ \t]*\\(with[ \t]+\\)?\\(\\(not[ \t]+\\)?overriding[ \t]+\\)?\\(procedure\\|function\\|task\\)[ \t\n]+"
618
619 ;; subprogram name: operator ("[+/=*]")
620 "\\("
621 "\\(\"[^\"]+\"\\)"
622
623 ;; subprogram name: name
624 "\\|"
625 "\\(\\(\\sw\\|[_.]\\)+\\)"
626 "\\)")
627 "Regexp matching Ada subprogram start.
628 The actual start is at (match-beginning 4). The name is in (match-string 5).")
629
630 (defconst ada-name-regexp
631 "\\([a-zA-Z][a-zA-Z0-9_.']*[a-zA-Z0-9]\\)"
632 "Regexp matching a fully qualified name (including attribute).")
633
634 (defconst ada-package-start-regexp
635 (concat "^[ \t]*\\(private[ \t]+\\)?\\(package\\)[ \t\n]+\\(body[ \t]*\\)?" ada-name-regexp)
636 "Regexp matching start of package.
637 The package name is in (match-string 4).")
638
639 (defconst ada-compile-goto-error-file-linenr-re
640 "\\([-_.a-zA-Z0-9]+\\):\\([0-9]+\\)\\(:\\([0-9]+\\)\\)?"
641 "Regexp matching filename:linenr[:column].")
642
643
644 ;;; ---- regexps for indentation functions
645
646 (defvar ada-block-start-re
647 (eval-when-compile
648 (concat "\\<\\(" (regexp-opt '("begin" "declare" "else"
649 "exception" "generic" "loop" "or"
650 "private" "select" ))
651 "\\|\\(\\(limited\\|abstract\\|tagged\\)[ \t\n]+\\)*record\\)\\>"))
652 "Regexp for keywords starting Ada blocks.")
653
654 (defvar ada-end-stmt-re
655 (eval-when-compile
656 (concat "\\("
657 ";" "\\|"
658 "=>[ \t]*$" "\\|"
659 "=>[ \t]*--.*$" "\\|"
660 "^[ \t]*separate[ \t]*(\\(\\sw\\|[_.]\\)+)" "\\|"
661 "\\<" (regexp-opt '("begin" "declare" "is" "do" "else" "generic"
662 "loop" "private" "record" "select"
663 "then abort" "then") t) "\\>" "\\|"
664 "^[ \t]*" (regexp-opt '("function" "package" "procedure")
665 t) "\\>\\(\\sw\\|[ \t_.]\\)+\\<is\\>" "\\|"
666 "^[ \t]*exception\\>"
667 "\\)") )
668 "Regexp of possible ends for a non-broken statement.
669 A new statement starts after these.")
670
671 (defvar ada-matching-start-re
672 (eval-when-compile
673 (concat "\\<"
674 (regexp-opt
675 '("end" "loop" "select" "begin" "case" "do" "declare"
676 "if" "task" "package" "procedure" "function" "record" "protected") t)
677 "\\>"))
678 "Regexp used in `ada-goto-matching-start'.")
679
680 (defvar ada-matching-decl-start-re
681 (eval-when-compile
682 (concat "\\<"
683 (regexp-opt
684 '("is" "separate" "end" "declare" "if" "new" "begin" "generic" "when") t)
685 "\\>"))
686 "Regexp used in `ada-goto-matching-decl-start'.")
687
688 (defvar ada-loop-start-re
689 "\\<\\(for\\|while\\|loop\\)\\>"
690 "Regexp for the start of a loop.")
691
692 (defvar ada-subprog-start-re
693 (eval-when-compile
694 (concat "\\<" (regexp-opt '("accept" "entry" "function" "overriding" "package" "procedure"
695 "protected" "task") t) "\\>"))
696 "Regexp for the start of a subprogram.")
697
698 (defvar ada-contextual-menu-on-identifier nil
699 "Set to true when the right mouse button was clicked on an identifier.")
700
701 (defvar ada-contextual-menu-last-point nil
702 "Position of point just before displaying the menu.
703 This is a list (point buffer).
704 Since `ada-popup-menu' moves the point where the user clicked, the region
705 is modified. Therefore no command from the menu knows what the user selected
706 before displaying the contextual menu.
707 To get the original region, restore the point to this position before
708 calling `region-end' and `region-beginning'.
709 Modify this variable if you want to restore the point to another position.")
710
711 (easy-menu-define ada-contextual-menu nil
712 "Menu to use when the user presses the right mouse button.
713 The variable `ada-contextual-menu-on-identifier' will be set to t before
714 displaying the menu if point was on an identifier."
715 '("Ada"
716 ["Goto Declaration/Body" ada-point-and-xref
717 :included ada-contextual-menu-on-identifier]
718 ["Goto Body" ada-point-and-xref-body
719 :included ada-contextual-menu-on-identifier]
720 ["Goto Previous Reference" ada-xref-goto-previous-reference]
721 ["List References" ada-find-references
722 :included ada-contextual-menu-on-identifier]
723 ["List Local References" ada-find-local-references
724 :included ada-contextual-menu-on-identifier]
725 ["-" nil nil]
726 ["Other File" ff-find-other-file]
727 ["Goto Parent Unit" ada-goto-parent]))
728
729 \f
730 ;;------------------------------------------------------------------
731 ;; Support for imenu (see imenu.el)
732 ;;------------------------------------------------------------------
733
734 (defconst ada-imenu-comment-re "\\([ \t]*--.*\\)?")
735
736 (defconst ada-imenu-subprogram-menu-re
737 (concat "^[ \t]*\\(overriding[ \t]*\\)?\\(procedure\\|function\\)[ \t\n]+"
738 "\\(\\(\\sw\\|_\\)+\\)[ \t\n]*\\([ \t\n]\\|([^)]+)"
739 ada-imenu-comment-re
740 "\\)[ \t\n]*"
741 "\\(return[ \t\n]+\\(\\sw\\|[_.]\\)+[ \t\n]*\\)?is[ \t\n]"))
742
743 (defvar ada-imenu-generic-expression
744 (list
745 (list nil ada-imenu-subprogram-menu-re 3)
746 (list "*Specs*"
747 (concat
748 "^[ \t]*\\(procedure\\|function\\)[ \t\n]+\\(\\(\\sw\\|_\\)+\\)"
749 "\\("
750 "\\(" ada-imenu-comment-re "[ \t\n]+\\|[ \t\n]*([^)]+)"
751 ada-imenu-comment-re "\\)";; parameter list or simple space
752 "\\([ \t\n]*return[ \t\n]+\\(\\sw\\|[_.]\\)+[ \t\n]*\\)?"
753 "\\)?;") 2)
754 '("*Tasks*" "^[ \t]*task[ \t]+\\(type[ \t]+\\)?\\(\\(body[ \t]+\\)?\\(\\sw\\|_\\)+\\)" 2)
755 '("*Type Defs*" "^[ \t]*\\(sub\\)?type[ \t]+\\(\\(\\sw\\|_\\)+\\)" 2)
756 '("*Protected*"
757 "^[ \t]*protected[ \t]+\\(type[ \t]+\\)?\\(\\(body[ \t]+\\)?\\(\\sw\\|_\\)+\\)" 2)
758 '("*Packages*" "^[ \t]*package[ \t]+\\(\\(body[ \t]+\\)?\\(\\sw\\|[_.]\\)+\\)" 1))
759 "Imenu generic expression for Ada mode.
760 See `imenu-generic-expression'. This variable will create several submenus for
761 each type of entity that can be found in an Ada file.")
762
763 \f
764 ;;------------------------------------------------------------
765 ;; Support for compile.el
766 ;;------------------------------------------------------------
767
768 (defun ada-compile-mouse-goto-error ()
769 "Mouse interface for `ada-compile-goto-error'."
770 (interactive)
771 (mouse-set-point last-input-event)
772 (ada-compile-goto-error (point))
773 )
774
775 (defun ada-compile-goto-error (pos)
776 "Replace `compile-goto-error' from compile.el.
777 If POS is on a file and line location, go to this position. It adds
778 to compile.el the capacity to go to a reference in an error message.
779 For instance, on these lines:
780 foo.adb:61:11: [...] in call to size declared at foo.ads:11
781 foo.adb:61:11: [...] in call to local declared at line 20
782 the 4 file locations can be clicked on and jumped to."
783 (interactive "d")
784 (goto-char pos)
785
786 (skip-chars-backward "-a-zA-Z0-9_:./\\")
787 (cond
788 ;; special case: looking at a filename:line not at the beginning of a line
789 ;; or a simple line reference "at line ..."
790 ((and (not (bolp))
791 (or (looking-at ada-compile-goto-error-file-linenr-re)
792 (and
793 (save-excursion
794 (beginning-of-line)
795 (looking-at ada-compile-goto-error-file-linenr-re))
796 (save-excursion
797 (if (looking-at "\\([0-9]+\\)") (backward-word 1))
798 (looking-at "line \\([0-9]+\\)"))))
799 )
800 (let ((line (if (match-beginning 2) (match-string 2) (match-string 1)))
801 (file (if (match-beginning 2) (match-string 1)
802 (save-excursion (beginning-of-line)
803 (looking-at ada-compile-goto-error-file-linenr-re)
804 (match-string 1))))
805 (error-pos (point-marker))
806 source)
807
808 ;; set source marker
809 (save-excursion
810 (compilation-find-file (point-marker) (match-string 1) "./")
811 (set-buffer file)
812
813 (when (stringp line)
814 (goto-char (point-min))
815 (forward-line (1- (string-to-number line))))
816
817 (setq source (point-marker)))
818
819 (compilation-goto-locus error-pos source nil)
820
821 ))
822
823 ;; otherwise, default behavior
824 (t
825 (compile-goto-error))
826 )
827 (recenter))
828
829 \f
830 ;;-------------------------------------------------------------------------
831 ;; Grammar related function
832 ;; The functions below work with the syntax class of the characters in an Ada
833 ;; buffer. Two syntax tables are created, depending on whether we want '_'
834 ;; to be considered as part of a word or not.
835 ;; Some characters may have multiple meanings depending on the context:
836 ;; - ' is either the beginning of a constant character or an attribute
837 ;; - # is either part of a based litteral or a gnatprep statement.
838 ;; - " starts a string, but not if inside a constant character.
839 ;; - ( and ) should be ignored if inside a constant character.
840 ;; Thus their syntax property is changed automatically, and we can still use
841 ;; the standard Emacs functions for sexp (see `ada-in-string-p')
842 ;;
843 ;; On Emacs, this is done through the `syntax-table' text property. The
844 ;; corresponding action is applied automatically each time the buffer
845 ;; changes. If `font-lock-mode' is enabled (the default) the action is
846 ;; set up by `font-lock-syntactic-keywords'. Otherwise, we do it
847 ;; manually in `ada-after-change-function'. The proper method is
848 ;; installed by `ada-handle-syntax-table-properties'.
849 ;;
850 ;; on XEmacs, the `syntax-table' property does not exist and we have to use a
851 ;; slow advice to `parse-partial-sexp' to do the same thing.
852 ;; When executing parse-partial-sexp, we simply modify the strings before and
853 ;; after, so that the special constants '"', '(' and ')' do not interact
854 ;; with parse-partial-sexp.
855 ;; Note: this code is slow and needs to be rewritten as soon as something
856 ;; better is available on XEmacs.
857 ;;-------------------------------------------------------------------------
858
859 (defun ada-create-syntax-table ()
860 "Create the two syntax tables use in the Ada mode.
861 The standard table declares `_' as a symbol constituent, the second one
862 declares it as a word constituent."
863 (interactive)
864 (setq ada-mode-syntax-table (make-syntax-table))
865
866 ;; define string brackets (`%' is alternative string bracket, but
867 ;; almost never used as such and throws font-lock and indentation
868 ;; off the track.)
869 (modify-syntax-entry ?% "$" ada-mode-syntax-table)
870 (modify-syntax-entry ?\" "\"" ada-mode-syntax-table)
871
872 (modify-syntax-entry ?: "." ada-mode-syntax-table)
873 (modify-syntax-entry ?\; "." ada-mode-syntax-table)
874 (modify-syntax-entry ?& "." ada-mode-syntax-table)
875 (modify-syntax-entry ?\| "." ada-mode-syntax-table)
876 (modify-syntax-entry ?+ "." ada-mode-syntax-table)
877 (modify-syntax-entry ?* "." ada-mode-syntax-table)
878 (modify-syntax-entry ?/ "." ada-mode-syntax-table)
879 (modify-syntax-entry ?= "." ada-mode-syntax-table)
880 (modify-syntax-entry ?< "." ada-mode-syntax-table)
881 (modify-syntax-entry ?> "." ada-mode-syntax-table)
882 (modify-syntax-entry ?$ "." ada-mode-syntax-table)
883 (modify-syntax-entry ?\[ "." ada-mode-syntax-table)
884 (modify-syntax-entry ?\] "." ada-mode-syntax-table)
885 (modify-syntax-entry ?\{ "." ada-mode-syntax-table)
886 (modify-syntax-entry ?\} "." ada-mode-syntax-table)
887 (modify-syntax-entry ?. "." ada-mode-syntax-table)
888 (modify-syntax-entry ?\\ "." ada-mode-syntax-table)
889 (modify-syntax-entry ?\' "." ada-mode-syntax-table)
890
891 ;; a single hyphen is punctuation, but a double hyphen starts a comment
892 (modify-syntax-entry ?- ". 12" ada-mode-syntax-table)
893
894 ;; See the comment above on grammar related function for the special
895 ;; setup for '#'.
896 (if (featurep 'xemacs)
897 (modify-syntax-entry ?# "<" ada-mode-syntax-table)
898 (modify-syntax-entry ?# "$" ada-mode-syntax-table))
899
900 ;; and \f and \n end a comment
901 (modify-syntax-entry ?\f "> " ada-mode-syntax-table)
902 (modify-syntax-entry ?\n "> " ada-mode-syntax-table)
903
904 ;; define what belongs in Ada symbols
905 (modify-syntax-entry ?_ "_" ada-mode-syntax-table)
906
907 ;; define parentheses to match
908 (modify-syntax-entry ?\( "()" ada-mode-syntax-table)
909 (modify-syntax-entry ?\) ")(" ada-mode-syntax-table)
910
911 (setq ada-mode-symbol-syntax-table (copy-syntax-table ada-mode-syntax-table))
912 (modify-syntax-entry ?_ "w" ada-mode-symbol-syntax-table)
913 )
914
915 ;; Support of special characters in XEmacs (see the comments at the beginning
916 ;; of the section on Grammar related functions).
917
918 (if (featurep 'xemacs)
919 (defadvice parse-partial-sexp (around parse-partial-sexp-protect-constants)
920 "Handles special character constants and gnatprep statements."
921 (let (change)
922 (if (< to from)
923 (let ((tmp from))
924 (setq from to to tmp)))
925 (save-excursion
926 (goto-char from)
927 (while (re-search-forward "'\\([(\")#]\\)'" to t)
928 (setq change (cons (list (match-beginning 1)
929 1
930 (match-string 1))
931 change))
932 (replace-match "'A'"))
933 (goto-char from)
934 (while (re-search-forward "\\(#[0-9a-fA-F]*#\\)" to t)
935 (setq change (cons (list (match-beginning 1)
936 (length (match-string 1))
937 (match-string 1))
938 change))
939 (replace-match (make-string (length (match-string 1)) ?@))))
940 ad-do-it
941 (save-excursion
942 (while change
943 (goto-char (caar change))
944 (delete-char (cadar change))
945 (insert (caddar change))
946 (setq change (cdr change)))))))
947
948 (defun ada-set-syntax-table-properties ()
949 "Assign `syntax-table' properties in accessible part of buffer.
950 In particular, character constants are said to be strings, #...#
951 are treated as numbers instead of gnatprep comments."
952 (let ((modified (buffer-modified-p))
953 (buffer-undo-list t)
954 (inhibit-read-only t)
955 (inhibit-point-motion-hooks t)
956 (inhibit-modification-hooks t))
957 (remove-text-properties (point-min) (point-max) '(syntax-table nil))
958 (goto-char (point-min))
959 (while (re-search-forward
960 ;; The following regexp was adapted from
961 ;; `ada-font-lock-syntactic-keywords'.
962 "^[ \t]*\\(#\\(?:if\\|else\\|elsif\\|end\\)\\)\\|[^a-zA-Z0-9)]\\('\\)[^'\n]\\('\\)"
963 nil t)
964 (if (match-beginning 1)
965 (put-text-property
966 (match-beginning 1) (match-end 1) 'syntax-table '(11 . ?\n))
967 (put-text-property
968 (match-beginning 2) (match-end 2) 'syntax-table '(7 . ?'))
969 (put-text-property
970 (match-beginning 3) (match-end 3) 'syntax-table '(7 . ?'))))
971 (unless modified
972 (restore-buffer-modified-p nil))))
973
974 (defun ada-after-change-function (beg end old-len)
975 "Called when the region between BEG and END was changed in the buffer.
976 OLD-LEN indicates what the length of the replaced text was."
977 (save-excursion
978 (save-restriction
979 (let ((from (progn (goto-char beg) (line-beginning-position)))
980 (to (progn (goto-char end) (line-end-position))))
981 (narrow-to-region from to)
982 (save-match-data
983 (ada-set-syntax-table-properties))))))
984
985 (defun ada-initialize-syntax-table-properties ()
986 "Assign `syntax-table' properties in current buffer."
987 (save-excursion
988 (save-restriction
989 (widen)
990 (save-match-data
991 (ada-set-syntax-table-properties))))
992 (add-hook 'after-change-functions 'ada-after-change-function nil t))
993
994 (defun ada-handle-syntax-table-properties ()
995 "Handle `syntax-table' properties."
996 (if font-lock-mode
997 ;; `font-lock-mode' will take care of `syntax-table' properties.
998 (remove-hook 'after-change-functions 'ada-after-change-function t)
999 ;; Take care of `syntax-table' properties manually.
1000 (ada-initialize-syntax-table-properties)))
1001
1002 ;;------------------------------------------------------------------
1003 ;; Testing the grammatical context
1004 ;;------------------------------------------------------------------
1005
1006 (defsubst ada-in-comment-p (&optional parse-result)
1007 "Return t if inside a comment.
1008 If PARSE-RESULT is non-nil, use it instead of calling `parse-partial-sexp'."
1009 (nth 4 (or parse-result
1010 (parse-partial-sexp
1011 (line-beginning-position) (point)))))
1012
1013 (defsubst ada-in-string-p (&optional parse-result)
1014 "Return t if point is inside a string.
1015 If PARSE-RESULT is non-nil, use it instead of calling `parse-partial-sexp'."
1016 (nth 3 (or parse-result
1017 (parse-partial-sexp
1018 (line-beginning-position) (point)))))
1019
1020 (defsubst ada-in-string-or-comment-p (&optional parse-result)
1021 "Return t if inside a comment or string.
1022 If PARSE-RESULT is non-nil, use it instead of calling `parse-partial-sexp'."
1023 (setq parse-result (or parse-result
1024 (parse-partial-sexp
1025 (line-beginning-position) (point))))
1026 (or (ada-in-string-p parse-result) (ada-in-comment-p parse-result)))
1027
1028
1029 ;;------------------------------------------------------------------
1030 ;; Contextual menus
1031 ;; The Ada mode comes with contextual menus, bound by default to the right
1032 ;; mouse button.
1033 ;; Add items to this menu by modifying `ada-contextual-menu'. Note that the
1034 ;; variable `ada-contextual-menu-on-identifier' is set automatically to t
1035 ;; if the mouse button was pressed on an identifier.
1036 ;;------------------------------------------------------------------
1037
1038 (defun ada-call-from-contextual-menu (function)
1039 "Execute FUNCTION when called from the contextual menu.
1040 It forces Emacs to change the cursor position."
1041 (interactive)
1042 (funcall function)
1043 (setq ada-contextual-menu-last-point
1044 (list (point) (current-buffer))))
1045
1046 (defun ada-popup-menu (position)
1047 "Pops up a contextual menu, depending on where the user clicked.
1048 POSITION is the location the mouse was clicked on.
1049 Sets `ada-contextual-menu-last-point' to the current position before
1050 displaying the menu. When a function from the menu is called, the
1051 point is where the mouse button was clicked."
1052 (interactive "e")
1053
1054 ;; declare this as a local variable, so that the function called
1055 ;; in the contextual menu does not hide the region in
1056 ;; transient-mark-mode.
1057 (let ((deactivate-mark nil))
1058 (setq ada-contextual-menu-last-point
1059 (list (point) (current-buffer)))
1060 (mouse-set-point last-input-event)
1061
1062 (setq ada-contextual-menu-on-identifier
1063 (and (char-after)
1064 (or (= (char-syntax (char-after)) ?w)
1065 (= (char-after) ?_))
1066 (not (ada-in-string-or-comment-p))
1067 (save-excursion (skip-syntax-forward "w")
1068 (not (ada-after-keyword-p)))
1069 ))
1070 (if (fboundp 'popup-menu)
1071 (funcall (symbol-function 'popup-menu) ada-contextual-menu)
1072 (let (choice)
1073 (setq choice (x-popup-menu position ada-contextual-menu))
1074 (if choice
1075 (funcall (lookup-key ada-contextual-menu (vector (car choice)))))))
1076
1077 (set-buffer (cadr ada-contextual-menu-last-point))
1078 (goto-char (car ada-contextual-menu-last-point))
1079 ))
1080
1081
1082 ;;------------------------------------------------------------------
1083 ;; Misc functions
1084 ;;------------------------------------------------------------------
1085
1086 ;;;###autoload
1087 (defun ada-add-extensions (spec body)
1088 "Define SPEC and BODY as being valid extensions for Ada files.
1089 Going from body to spec with `ff-find-other-file' used these
1090 extensions.
1091 SPEC and BODY are two regular expressions that must match against
1092 the file name."
1093 (let* ((reg (concat (regexp-quote body) "$"))
1094 (tmp (assoc reg ada-other-file-alist)))
1095 (if tmp
1096 (setcdr tmp (list (cons spec (cadr tmp))))
1097 (add-to-list 'ada-other-file-alist (list reg (list spec)))))
1098
1099 (let* ((reg (concat (regexp-quote spec) "$"))
1100 (tmp (assoc reg ada-other-file-alist)))
1101 (if tmp
1102 (setcdr tmp (list (cons body (cadr tmp))))
1103 (add-to-list 'ada-other-file-alist (list reg (list body)))))
1104
1105 (add-to-list 'auto-mode-alist
1106 (cons (concat (regexp-quote spec) "\\'") 'ada-mode))
1107 (add-to-list 'auto-mode-alist
1108 (cons (concat (regexp-quote body) "\\'") 'ada-mode))
1109
1110 (add-to-list 'ada-spec-suffixes spec)
1111 (add-to-list 'ada-body-suffixes body)
1112
1113 ;; Support for speedbar (Specifies that we want to see these files in
1114 ;; speedbar)
1115 (if (fboundp 'speedbar-add-supported-extension)
1116 (progn
1117 (funcall (symbol-function 'speedbar-add-supported-extension)
1118 spec)
1119 (funcall (symbol-function 'speedbar-add-supported-extension)
1120 body)))
1121 )
1122
1123
1124 ;;;###autoload
1125 (defun ada-mode ()
1126 "Ada mode is the major mode for editing Ada code."
1127
1128 (interactive)
1129 (kill-all-local-variables)
1130
1131 (set-syntax-table ada-mode-syntax-table)
1132
1133 (set (make-local-variable 'require-final-newline) mode-require-final-newline)
1134
1135 ;; Set the paragraph delimiters so that one can select a whole block
1136 ;; simply with M-h
1137 (set (make-local-variable 'paragraph-start) "[ \t\n\f]*$")
1138 (set (make-local-variable 'paragraph-separate) "[ \t\n\f]*$")
1139
1140 ;; comment end must be set because it may hold a wrong value if
1141 ;; this buffer had been in another mode before. RE
1142 (set (make-local-variable 'comment-end) "")
1143
1144 ;; used by autofill and indent-new-comment-line
1145 (set (make-local-variable 'comment-start-skip) "---*[ \t]*")
1146
1147 ;; used by autofill to break a comment line and continue it on another line.
1148 ;; The reason we need this one is that the default behavior does not work
1149 ;; correctly with the definition of paragraph-start above when the comment
1150 ;; is right after a multi-line subprogram declaration (the comments are
1151 ;; aligned under the latest parameter, not under the declaration start).
1152 (set (make-local-variable 'comment-line-break-function)
1153 (lambda (&optional soft) (let ((fill-prefix nil))
1154 (indent-new-comment-line soft))))
1155
1156 (set (make-local-variable 'indent-line-function)
1157 'ada-indent-current-function)
1158
1159 (set (make-local-variable 'comment-column) 40)
1160
1161 ;; Emacs 20.3 defines a comment-padding to insert spaces between
1162 ;; the comment and the text. We do not want any, this is already
1163 ;; included in comment-start
1164 (unless (featurep 'xemacs)
1165 (set (make-local-variable 'parse-sexp-ignore-comments) t)
1166 (set (make-local-variable 'comment-padding) 0)
1167 (set (make-local-variable 'parse-sexp-lookup-properties) t))
1168
1169 (set 'case-fold-search t)
1170 (if (boundp 'imenu-case-fold-search)
1171 (set 'imenu-case-fold-search t))
1172
1173 (set (make-local-variable 'fill-paragraph-function)
1174 'ada-fill-comment-paragraph)
1175
1176 ;; Support for compile.el
1177 ;; We just substitute our own functions to go to the error.
1178 (add-hook 'compilation-mode-hook
1179 (lambda()
1180 ;; FIXME: This has global impact! -stef
1181 (define-key compilation-minor-mode-map [mouse-2]
1182 'ada-compile-mouse-goto-error)
1183 (define-key compilation-minor-mode-map "\C-c\C-c"
1184 'ada-compile-goto-error)
1185 (define-key compilation-minor-mode-map "\C-m"
1186 'ada-compile-goto-error)))
1187
1188 ;; font-lock support :
1189
1190 (set (make-local-variable 'font-lock-defaults)
1191 '(ada-font-lock-keywords
1192 nil t
1193 ((?\_ . "w") (?# . "."))
1194 beginning-of-line
1195 (font-lock-syntactic-keywords . ada-font-lock-syntactic-keywords)))
1196
1197 ;; Set up support for find-file.el.
1198 (set (make-local-variable 'ff-other-file-alist)
1199 'ada-other-file-alist)
1200 (set (make-local-variable 'ff-search-directories)
1201 'ada-search-directories-internal)
1202 (setq ff-post-load-hook 'ada-set-point-accordingly
1203 ff-file-created-hook 'ada-make-body)
1204 (add-hook 'ff-pre-load-hook 'ada-which-function-are-we-in)
1205
1206 (make-local-variable 'ff-special-constructs)
1207 (mapc (lambda (pair) (add-to-list 'ff-special-constructs pair))
1208 (list
1209 ;; Top level child package declaration; go to the parent package.
1210 (cons (eval-when-compile
1211 (concat "^\\(private[ \t]\\)?[ \t]*package[ \t]+"
1212 "\\(body[ \t]+\\)?"
1213 "\\(\\(\\sw\\|[_.]\\)+\\)\\.\\(\\sw\\|_\\)+[ \t\n]+is"))
1214 (lambda ()
1215 (ff-get-file
1216 ada-search-directories-internal
1217 (ada-make-filename-from-adaname (match-string 3))
1218 ada-spec-suffixes)))
1219
1220 ;; A "separate" clause.
1221 (cons "^separate[ \t\n]*(\\(\\(\\sw\\|[_.]\\)+\\))"
1222 (lambda ()
1223 (ff-get-file
1224 ada-search-directories-internal
1225 (ada-make-filename-from-adaname (match-string 1))
1226 ada-spec-suffixes)))
1227
1228 ;; A "with" clause.
1229 (cons "^with[ \t]+\\([a-zA-Z0-9_\\.]+\\)"
1230 (lambda ()
1231 (ff-get-file
1232 ada-search-directories-internal
1233 (ada-make-filename-from-adaname (match-string 1))
1234 ada-spec-suffixes)))
1235 ))
1236
1237 ;; Support for outline-minor-mode
1238 (set (make-local-variable 'outline-regexp)
1239 "\\([ \t]*\\(procedure\\|function\\|package\\|if\\|while\\|for\\|declare\\|case\\|end\\|begin\\|loop\\)\\|--\\)")
1240 (set (make-local-variable 'outline-level) 'ada-outline-level)
1241
1242 ;; Support for imenu : We want a sorted index
1243 (setq imenu-generic-expression ada-imenu-generic-expression)
1244
1245 (setq imenu-sort-function 'imenu--sort-by-name)
1246
1247 ;; Support for ispell : Check only comments
1248 (set (make-local-variable 'ispell-check-comments) 'exclusive)
1249
1250 ;; Support for align
1251 (add-to-list 'align-dq-string-modes 'ada-mode)
1252 (add-to-list 'align-open-comment-modes 'ada-mode)
1253 (set (make-local-variable 'align-region-separate) ada-align-region-separate)
1254
1255 ;; Exclude comments alone on line from alignment.
1256 (add-to-list 'align-exclude-rules-list
1257 '(ada-solo-comment
1258 (regexp . "^\\(\\s-*\\)--")
1259 (modes . '(ada-mode))))
1260 (add-to-list 'align-exclude-rules-list
1261 '(ada-solo-use
1262 (regexp . "^\\(\\s-*\\)\\<use\\>")
1263 (modes . '(ada-mode))))
1264
1265 (setq ada-align-modes nil)
1266
1267 (add-to-list 'ada-align-modes
1268 '(ada-declaration-assign
1269 (regexp . "[^:]\\(\\s-*\\):[^:]")
1270 (valid . (lambda() (not (ada-in-comment-p))))
1271 (repeat . t)
1272 (modes . '(ada-mode))))
1273 (add-to-list 'ada-align-modes
1274 '(ada-associate
1275 (regexp . "[^=]\\(\\s-*\\)=>")
1276 (valid . (lambda() (not (ada-in-comment-p))))
1277 (modes . '(ada-mode))))
1278 (add-to-list 'ada-align-modes
1279 '(ada-comment
1280 (regexp . "\\(\\s-*\\)--")
1281 (modes . '(ada-mode))))
1282 (add-to-list 'ada-align-modes
1283 '(ada-use
1284 (regexp . "\\(\\s-*\\)\\<use\\s-")
1285 (valid . (lambda() (not (ada-in-comment-p))))
1286 (modes . '(ada-mode))))
1287 (add-to-list 'ada-align-modes
1288 '(ada-at
1289 (regexp . "\\(\\s-+\\)at\\>")
1290 (modes . '(ada-mode))))
1291
1292 (setq align-mode-rules-list ada-align-modes)
1293
1294 ;; Set up the contextual menu
1295 (if ada-popup-key
1296 (define-key ada-mode-map ada-popup-key 'ada-popup-menu))
1297
1298 ;; Support for Abbreviations (the user still need to "M-x abbrev-mode"
1299 (define-abbrev-table 'ada-mode-abbrev-table ())
1300 (setq local-abbrev-table ada-mode-abbrev-table)
1301
1302 ;; Support for which-function mode
1303 (make-local-variable 'which-func-functions)
1304 (setq which-func-functions '(ada-which-function))
1305
1306 ;; Support for indent-new-comment-line (Especially for XEmacs)
1307 (set (make-local-variable 'comment-multi-line) nil)
1308
1309 ;; Support for add-log
1310 (set (make-local-variable 'add-log-current-defun-function) 'ada-which-function)
1311
1312 (setq major-mode 'ada-mode
1313 mode-name "Ada")
1314
1315 (use-local-map ada-mode-map)
1316
1317 (easy-menu-add ada-mode-menu ada-mode-map)
1318
1319 (set-syntax-table ada-mode-syntax-table)
1320
1321 (set (make-local-variable 'skeleton-further-elements)
1322 '((< '(backward-delete-char-untabify
1323 (min ada-indent (current-column))))))
1324 (add-hook 'skeleton-end-hook 'ada-adjust-case-skeleton nil t)
1325
1326 (run-mode-hooks 'ada-mode-hook)
1327
1328 ;; To be run after the hook, in case the user modified
1329 ;; ada-fill-comment-prefix
1330 (make-local-variable 'comment-start)
1331 (if ada-fill-comment-prefix
1332 (set 'comment-start ada-fill-comment-prefix)
1333 (set 'comment-start "-- "))
1334
1335 ;; Run this after the hook to give the users a chance to activate
1336 ;; font-lock-mode
1337
1338 (unless (featurep 'xemacs)
1339 (ada-initialize-syntax-table-properties)
1340 (add-hook 'font-lock-mode-hook 'ada-handle-syntax-table-properties nil t))
1341
1342 ;; the following has to be done after running the ada-mode-hook
1343 ;; because users might want to set the values of these variable
1344 ;; inside the hook
1345
1346 (cond ((eq ada-language-version 'ada83)
1347 (setq ada-keywords ada-83-keywords))
1348 ((eq ada-language-version 'ada95)
1349 (setq ada-keywords ada-95-keywords))
1350 ((eq ada-language-version 'ada2005)
1351 (setq ada-keywords ada-2005-keywords)))
1352
1353 (if ada-auto-case
1354 (ada-activate-keys-for-case)))
1355
1356 (defun ada-adjust-case-skeleton ()
1357 "Adjust the case of the text inserted by a skeleton."
1358 (save-excursion
1359 (let ((aa-end (point)))
1360 (ada-adjust-case-region
1361 (progn (goto-char (symbol-value 'beg)) (forward-word -1) (point))
1362 (goto-char aa-end)))))
1363
1364 (defun ada-region-selected ()
1365 "Should we operate on an active region?"
1366 (if (fboundp 'use-region-p)
1367 (use-region-p)
1368 (region-active-p)))
1369 \f
1370 ;;-----------------------------------------------------------------
1371 ;; auto-casing
1372 ;; Since Ada is case-insensitive, the Ada mode provides an extensive set of
1373 ;; functions to auto-case identifiers, keywords, ...
1374 ;; The basic rules for autocasing are defined through the variables
1375 ;; `ada-case-attribute', `ada-case-keyword' and `ada-case-identifier'. These
1376 ;; are references to the functions that will do the actual casing.
1377 ;;
1378 ;; However, in most cases, the user will want to define some exceptions to
1379 ;; these casing rules. This is done through a list of files, that contain
1380 ;; one word per line. These files are stored in `ada-case-exception-file'.
1381 ;; For backward compatibility, this variable can also be a string.
1382 ;;-----------------------------------------------------------------
1383
1384 (defun ada-save-exceptions-to-file (file-name)
1385 "Save the casing exception lists to the file FILE-NAME.
1386 Casing exception lists are `ada-case-exception' and `ada-case-exception-substring'."
1387 (find-file (expand-file-name file-name))
1388 (erase-buffer)
1389 (mapc (lambda (x) (insert (car x) "\n"))
1390 (sort (copy-sequence ada-case-exception)
1391 (lambda(a b) (string< (car a) (car b)))))
1392 (mapc (lambda (x) (insert "*" (car x) "\n"))
1393 (sort (copy-sequence ada-case-exception-substring)
1394 (lambda(a b) (string< (car a) (car b)))))
1395 (save-buffer)
1396 (kill-buffer nil)
1397 )
1398
1399 (defun ada-create-case-exception (&optional word)
1400 "Define WORD as an exception for the casing system.
1401 If WORD is not given, then the current word in the buffer is used instead.
1402 The new word is added to the first file in `ada-case-exception-file'.
1403 The standard casing rules will no longer apply to this word."
1404 (interactive)
1405 (let ((previous-syntax-table (syntax-table))
1406 file-name
1407 )
1408
1409 (cond ((stringp ada-case-exception-file)
1410 (setq file-name ada-case-exception-file))
1411 ((listp ada-case-exception-file)
1412 (setq file-name (car ada-case-exception-file)))
1413 (t
1414 (error (concat "No exception file specified. "
1415 "See variable ada-case-exception-file"))))
1416
1417 (set-syntax-table ada-mode-symbol-syntax-table)
1418 (unless word
1419 (save-excursion
1420 (skip-syntax-backward "w")
1421 (setq word (buffer-substring-no-properties
1422 (point) (save-excursion (forward-word 1) (point))))))
1423 (set-syntax-table previous-syntax-table)
1424
1425 ;; Reread the exceptions file, in case it was modified by some other,
1426 (ada-case-read-exceptions-from-file file-name)
1427
1428 ;; If the word is already in the list, even with a different casing
1429 ;; we simply want to replace it.
1430 (if (and (not (equal ada-case-exception '()))
1431 (assoc-string word ada-case-exception t))
1432 (setcar (assoc-string word ada-case-exception t) word)
1433 (add-to-list 'ada-case-exception (cons word t))
1434 )
1435
1436 (ada-save-exceptions-to-file file-name)
1437 ))
1438
1439 (defun ada-create-case-exception-substring (&optional word)
1440 "Define the substring WORD as an exception for the casing system.
1441 If WORD is not given, then the current word in the buffer is used instead,
1442 or the selected region if any is active.
1443 The new word is added to the first file in `ada-case-exception-file'.
1444 When auto-casing a word, this substring will be special-cased, unless the
1445 word itself has a special casing."
1446 (interactive)
1447 (let ((file-name
1448 (cond ((stringp ada-case-exception-file)
1449 ada-case-exception-file)
1450 ((listp ada-case-exception-file)
1451 (car ada-case-exception-file))
1452 (t
1453 (error (concat "No exception file specified. "
1454 "See variable ada-case-exception-file"))))))
1455
1456 ;; Find the substring to define as an exception. Order is: the parameter,
1457 ;; if any, or the selected region, or the word under the cursor
1458 (cond
1459 (word nil)
1460
1461 ((ada-region-selected)
1462 (setq word (buffer-substring-no-properties
1463 (region-beginning) (region-end))))
1464
1465 (t
1466 (let ((underscore-syntax (char-syntax ?_)))
1467 (unwind-protect
1468 (progn
1469 (modify-syntax-entry ?_ "." (syntax-table))
1470 (save-excursion
1471 (skip-syntax-backward "w")
1472 (set 'word (buffer-substring-no-properties
1473 (point)
1474 (save-excursion (forward-word 1) (point))))))
1475 (modify-syntax-entry ?_ (make-string 1 underscore-syntax)
1476 (syntax-table))))))
1477
1478 ;; Reread the exceptions file, in case it was modified by some other,
1479 (ada-case-read-exceptions-from-file file-name)
1480
1481 ;; If the word is already in the list, even with a different casing
1482 ;; we simply want to replace it.
1483 (if (and (not (equal ada-case-exception-substring '()))
1484 (assoc-string word ada-case-exception-substring t))
1485 (setcar (assoc-string word ada-case-exception-substring t) word)
1486 (add-to-list 'ada-case-exception-substring (cons word t))
1487 )
1488
1489 (ada-save-exceptions-to-file file-name)
1490
1491 (message "%s" (concat "Defining " word " as a casing exception"))))
1492
1493 (defun ada-case-read-exceptions-from-file (file-name)
1494 "Read the content of the casing exception file FILE-NAME."
1495 (if (file-readable-p (expand-file-name file-name))
1496 (let ((buffer (current-buffer)))
1497 (find-file (expand-file-name file-name))
1498 (set-syntax-table ada-mode-symbol-syntax-table)
1499 (widen)
1500 (goto-char (point-min))
1501 (while (not (eobp))
1502
1503 ;; If the item is already in the list, even with an other casing,
1504 ;; do not add it again. This way, the user can easily decide which
1505 ;; priority should be applied to each casing exception
1506 (let ((word (buffer-substring-no-properties
1507 (point) (save-excursion (forward-word 1) (point)))))
1508
1509 ;; Handling a substring ?
1510 (if (char-equal (string-to-char word) ?*)
1511 (progn
1512 (setq word (substring word 1))
1513 (unless (assoc-string word ada-case-exception-substring t)
1514 (add-to-list 'ada-case-exception-substring (cons word t))))
1515 (unless (assoc-string word ada-case-exception t)
1516 (add-to-list 'ada-case-exception (cons word t)))))
1517
1518 (forward-line 1))
1519 (kill-buffer nil)
1520 (set-buffer buffer)))
1521 )
1522
1523 (defun ada-case-read-exceptions ()
1524 "Read all the casing exception files from `ada-case-exception-file'."
1525 (interactive)
1526
1527 ;; Reinitialize the casing exception list
1528 (setq ada-case-exception '()
1529 ada-case-exception-substring '())
1530
1531 (cond ((stringp ada-case-exception-file)
1532 (ada-case-read-exceptions-from-file ada-case-exception-file))
1533
1534 ((listp ada-case-exception-file)
1535 (mapcar 'ada-case-read-exceptions-from-file
1536 ada-case-exception-file))))
1537
1538 (defun ada-adjust-case-substring ()
1539 "Adjust case of substrings in the previous word."
1540 (interactive)
1541 (let ((substrings ada-case-exception-substring)
1542 (max (point))
1543 (case-fold-search t)
1544 (underscore-syntax (char-syntax ?_))
1545 re)
1546
1547 (save-excursion
1548 (forward-word -1)
1549
1550 (unwind-protect
1551 (progn
1552 (modify-syntax-entry ?_ "." (syntax-table))
1553
1554 (while substrings
1555 (setq re (concat "\\b" (regexp-quote (caar substrings)) "\\b"))
1556
1557 (save-excursion
1558 (while (re-search-forward re max t)
1559 (replace-match (caar substrings) t)))
1560 (setq substrings (cdr substrings))
1561 )
1562 )
1563 (modify-syntax-entry ?_ (make-string 1 underscore-syntax) (syntax-table)))
1564 )))
1565
1566 (defun ada-adjust-case-identifier ()
1567 "Adjust case of the previous identifier.
1568 The auto-casing is done according to the value of `ada-case-identifier'
1569 and the exceptions defined in `ada-case-exception-file'."
1570 (interactive)
1571 (if (or (equal ada-case-exception '())
1572 (equal (char-after) ?_))
1573 (progn
1574 (funcall ada-case-identifier -1)
1575 (ada-adjust-case-substring))
1576
1577 (progn
1578 (let ((end (point))
1579 (start (save-excursion (skip-syntax-backward "w")
1580 (point)))
1581 match)
1582 ;; If we have an exception, replace the word by the correct casing
1583 (if (setq match (assoc-string (buffer-substring start end)
1584 ada-case-exception t))
1585
1586 (progn
1587 (delete-region start end)
1588 (insert (car match)))
1589
1590 ;; Else simply re-case the word
1591 (funcall ada-case-identifier -1)
1592 (ada-adjust-case-substring))))))
1593
1594 (defun ada-after-keyword-p ()
1595 "Return t if cursor is after a keyword that is not an attribute."
1596 (save-excursion
1597 (forward-word -1)
1598 (and (not (and (char-before)
1599 (or (= (char-before) ?_)
1600 (= (char-before) ?'))));; unless we have a _ or '
1601 (looking-at (concat ada-keywords "[^_]")))))
1602
1603 (defun ada-adjust-case (&optional force-identifier)
1604 "Adjust the case of the word before the character just typed.
1605 If FORCE-IDENTIFIER is non-nil then also adjust keyword as identifier."
1606 (if (not (bobp))
1607 (progn
1608 (forward-char -1)
1609 (if (and (not (bobp))
1610 ;; or if at the end of a character constant
1611 (not (and (eq (following-char) ?')
1612 (eq (char-before (1- (point))) ?')))
1613 ;; or if the previous character was not part of a word
1614 (eq (char-syntax (char-before)) ?w)
1615 ;; if in a string or a comment
1616 (not (ada-in-string-or-comment-p))
1617 )
1618 (if (save-excursion
1619 (forward-word -1)
1620 (or (= (point) (point-min))
1621 (backward-char 1))
1622 (= (following-char) ?'))
1623 (funcall ada-case-attribute -1)
1624 (if (and
1625 (not force-identifier) ; (MH)
1626 (ada-after-keyword-p))
1627 (funcall ada-case-keyword -1)
1628 (ada-adjust-case-identifier))))
1629 (forward-char 1)
1630 ))
1631 )
1632
1633 (defun ada-adjust-case-interactive (arg)
1634 "Adjust the case of the previous word, and process the character just typed.
1635 ARG is the prefix the user entered with \\[universal-argument]."
1636 (interactive "P")
1637
1638 (if ada-auto-case
1639 (let ((lastk last-command-event)
1640 (previous-syntax-table (syntax-table)))
1641
1642 (unwind-protect
1643 (progn
1644 (set-syntax-table ada-mode-symbol-syntax-table)
1645 (cond ((or (eq lastk ?\n)
1646 (eq lastk ?\r))
1647 ;; horrible kludge
1648 (insert " ")
1649 (ada-adjust-case)
1650 ;; horrible dekludge
1651 (delete-backward-char 1)
1652 ;; some special keys and their bindings
1653 (cond
1654 ((eq lastk ?\n)
1655 (funcall ada-lfd-binding))
1656 ((eq lastk ?\r)
1657 (funcall ada-ret-binding))))
1658 ((eq lastk ?\C-i) (ada-tab))
1659 ;; Else just insert the character
1660 ((self-insert-command (prefix-numeric-value arg))))
1661 ;; if there is a keyword in front of the underscore
1662 ;; then it should be part of an identifier (MH)
1663 (if (eq lastk ?_)
1664 (ada-adjust-case t)
1665 (ada-adjust-case))
1666 )
1667 ;; Restore the syntax table
1668 (set-syntax-table previous-syntax-table))
1669 )
1670
1671 ;; Else, no auto-casing
1672 (cond
1673 ((eq last-command-event ?\n)
1674 (funcall ada-lfd-binding))
1675 ((eq last-command-event ?\r)
1676 (funcall ada-ret-binding))
1677 (t
1678 (self-insert-command (prefix-numeric-value arg))))
1679 ))
1680
1681 (defun ada-activate-keys-for-case ()
1682 "Modify the key bindings for all the keys that should readjust the casing."
1683 (interactive)
1684 ;; Save original key-bindings to allow swapping ret/lfd
1685 ;; when casing is activated.
1686 ;; The 'or ...' is there to be sure that the value will not
1687 ;; be changed again when Ada mode is called more than once
1688 (or ada-ret-binding (setq ada-ret-binding (key-binding "\C-M")))
1689 (or ada-lfd-binding (setq ada-lfd-binding (key-binding "\C-j")))
1690
1691 ;; Call case modifying function after certain keys.
1692 (mapcar (function (lambda(key) (define-key
1693 ada-mode-map
1694 (char-to-string key)
1695 'ada-adjust-case-interactive)))
1696 '( ?` ?_ ?# ?% ?& ?* ?( ?) ?- ?= ?+
1697 ?| ?\; ?: ?' ?\" ?< ?, ?. ?> ?/ ?\n 32 ?\r )))
1698
1699 (defun ada-loose-case-word (&optional arg)
1700 "Upcase first letter and letters following `_' in the following word.
1701 No other letter is modified.
1702 ARG is ignored, and is there for compatibility with `capitalize-word' only."
1703 (interactive)
1704 (save-excursion
1705 (let ((end (save-excursion (skip-syntax-forward "w") (point)))
1706 (first t))
1707 (skip-syntax-backward "w")
1708 (while (and (or first (search-forward "_" end t))
1709 (< (point) end))
1710 (and first
1711 (setq first nil))
1712 (insert-char (upcase (following-char)) 1)
1713 (delete-char 1)))))
1714
1715 (defun ada-no-auto-case (&optional arg)
1716 "Do nothing. ARG is ignored.
1717 This function can be used for the auto-casing variables in Ada mode, to
1718 adapt to unusal auto-casing schemes. Since it does nothing, you can for
1719 instance use it for `ada-case-identifier' if you don't want any special
1720 auto-casing for identifiers, whereas keywords have to be lower-cased.
1721 See also `ada-auto-case' to disable auto casing altogether."
1722 nil)
1723
1724 (defun ada-capitalize-word (&optional arg)
1725 "Upcase first letter and letters following '_', lower case other letters.
1726 ARG is ignored, and is there for compatibility with `capitalize-word' only."
1727 (interactive)
1728 (let ((end (save-excursion (skip-syntax-forward "w") (point)))
1729 (begin (save-excursion (skip-syntax-backward "w") (point))))
1730 (modify-syntax-entry ?_ "_")
1731 (capitalize-region begin end)
1732 (modify-syntax-entry ?_ "w")))
1733
1734 (defun ada-adjust-case-region (from to)
1735 "Adjust the case of all words in the region between FROM and TO.
1736 Attention: This function might take very long for big regions!"
1737 (interactive "*r")
1738 (let ((begin nil)
1739 (end nil)
1740 (keywordp nil)
1741 (attribp nil)
1742 (previous-syntax-table (syntax-table)))
1743 (message "Adjusting case ...")
1744 (unwind-protect
1745 (save-excursion
1746 (set-syntax-table ada-mode-symbol-syntax-table)
1747 (goto-char to)
1748 ;;
1749 ;; loop: look for all identifiers, keywords, and attributes
1750 ;;
1751 (while (re-search-backward "\\<\\(\\sw+\\)\\>" from t)
1752 (setq end (match-end 1))
1753 (setq attribp
1754 (and (> (point) from)
1755 (save-excursion
1756 (forward-char -1)
1757 (setq attribp (looking-at "'.[^']")))))
1758 (or
1759 ;; do nothing if it is a string or comment
1760 (ada-in-string-or-comment-p)
1761 (progn
1762 ;;
1763 ;; get the identifier or keyword or attribute
1764 ;;
1765 (setq begin (point))
1766 (setq keywordp (looking-at ada-keywords))
1767 (goto-char end)
1768 ;;
1769 ;; casing according to user-option
1770 ;;
1771 (if attribp
1772 (funcall ada-case-attribute -1)
1773 (if keywordp
1774 (funcall ada-case-keyword -1)
1775 (ada-adjust-case-identifier)))
1776 (goto-char begin))))
1777 (message "Adjusting case ... Done"))
1778 (set-syntax-table previous-syntax-table))))
1779
1780 (defun ada-adjust-case-buffer ()
1781 "Adjust the case of all words in the whole buffer.
1782 ATTENTION: This function might take very long for big buffers!"
1783 (interactive "*")
1784 (ada-adjust-case-region (point-min) (point-max)))
1785
1786 \f
1787 ;;--------------------------------------------------------------
1788 ;; Format Parameter Lists
1789 ;; Some special algorithms are provided to indent the parameter lists in
1790 ;; subprogram declarations. This is done in two steps:
1791 ;; - First parses the parameter list. The returned list has the following
1792 ;; format:
1793 ;; ( (<Param_Name> in? out? access? <Type_Name> <Default_Expression>)
1794 ;; ... )
1795 ;; This is done in `ada-scan-paramlist'.
1796 ;; - Delete and recreate the parameter list in function
1797 ;; `ada-insert-paramlist'.
1798 ;; Both steps are called from `ada-format-paramlist'.
1799 ;; Note: Comments inside the parameter list are lost.
1800 ;; The syntax has to be correct, or the reformating will fail.
1801 ;;--------------------------------------------------------------
1802
1803 (defun ada-format-paramlist ()
1804 "Reformat the parameter list point is in."
1805 (interactive)
1806 (let ((begin nil)
1807 (end nil)
1808 (delend nil)
1809 (paramlist nil)
1810 (previous-syntax-table (syntax-table)))
1811 (unwind-protect
1812 (progn
1813 (set-syntax-table ada-mode-symbol-syntax-table)
1814
1815 ;; check if really inside parameter list
1816 (or (ada-in-paramlist-p)
1817 (error "Not in parameter list"))
1818
1819 ;; find start of current parameter-list
1820 (ada-search-ignore-string-comment
1821 (concat ada-subprog-start-re "\\|\\<body\\>" ) t nil)
1822 (down-list 1)
1823 (backward-char 1)
1824 (setq begin (point))
1825
1826 ;; find end of parameter-list
1827 (forward-sexp 1)
1828 (setq delend (point))
1829 (delete-char -1)
1830 (insert "\n")
1831
1832 ;; find end of last parameter-declaration
1833 (forward-comment -1000)
1834 (setq end (point))
1835
1836 ;; build a list of all elements of the parameter-list
1837 (setq paramlist (ada-scan-paramlist (1+ begin) end))
1838
1839 ;; delete the original parameter-list
1840 (delete-region begin delend)
1841
1842 ;; insert the new parameter-list
1843 (goto-char begin)
1844 (ada-insert-paramlist paramlist))
1845
1846 ;; restore syntax-table
1847 (set-syntax-table previous-syntax-table)
1848 )))
1849
1850 (defun ada-scan-paramlist (begin end)
1851 "Scan the parameter list found in between BEGIN and END.
1852 Return the equivalent internal parameter list."
1853 (let ((paramlist (list))
1854 (param (list))
1855 (notend t)
1856 (apos nil)
1857 (epos nil)
1858 (semipos nil)
1859 (match-cons nil))
1860
1861 (goto-char begin)
1862
1863 ;; loop until end of last parameter
1864 (while notend
1865
1866 ;; find first character of parameter-declaration
1867 (ada-goto-next-non-ws)
1868 (setq apos (point))
1869
1870 ;; find last character of parameter-declaration
1871 (if (setq match-cons
1872 (ada-search-ignore-string-comment "[ \t\n]*;" nil end t))
1873 (progn
1874 (setq epos (car match-cons))
1875 (setq semipos (cdr match-cons)))
1876 (setq epos end))
1877
1878 ;; read name(s) of parameter(s)
1879 (goto-char apos)
1880 (looking-at "\\(\\(\\sw\\|[_, \t\n]\\)*\\(\\sw\\|_\\)\\)[ \t\n]*:[^=]")
1881
1882 (setq param (list (match-string 1)))
1883 (ada-search-ignore-string-comment ":" nil epos t 'search-forward)
1884
1885 ;; look for 'in'
1886 (setq apos (point))
1887 (setq param
1888 (append param
1889 (list
1890 (consp
1891 (ada-search-ignore-string-comment
1892 "in" nil epos t 'word-search-forward)))))
1893
1894 ;; look for 'out'
1895 (goto-char apos)
1896 (setq param
1897 (append param
1898 (list
1899 (consp
1900 (ada-search-ignore-string-comment
1901 "out" nil epos t 'word-search-forward)))))
1902
1903 ;; look for 'access'
1904 (goto-char apos)
1905 (setq param
1906 (append param
1907 (list
1908 (consp
1909 (ada-search-ignore-string-comment
1910 "access" nil epos t 'word-search-forward)))))
1911
1912 ;; skip 'in'/'out'/'access'
1913 (goto-char apos)
1914 (ada-goto-next-non-ws)
1915 (while (looking-at "\\<\\(in\\|out\\|access\\)\\>")
1916 (forward-word 1)
1917 (ada-goto-next-non-ws))
1918
1919 ;; read type of parameter
1920 ;; We accept spaces in the name, since some software like Rose
1921 ;; generates something like: "A : B 'Class"
1922 (looking-at "\\<\\(\\sw\\|[_.' \t]\\)+\\>")
1923 (setq param
1924 (append param
1925 (list (match-string 0))))
1926
1927 ;; read default-expression, if there is one
1928 (goto-char (setq apos (match-end 0)))
1929 (setq param
1930 (append param
1931 (list
1932 (if (setq match-cons
1933 (ada-search-ignore-string-comment
1934 ":=" nil epos t 'search-forward))
1935 (buffer-substring (car match-cons) epos)
1936 nil))))
1937
1938 ;; add this parameter-declaration to the list
1939 (setq paramlist (append paramlist (list param)))
1940
1941 ;; check if it was the last parameter
1942 (if (eq epos end)
1943 (setq notend nil)
1944 (goto-char semipos))
1945 )
1946 (reverse paramlist)))
1947
1948 (defun ada-insert-paramlist (paramlist)
1949 "Insert a formatted PARAMLIST in the buffer."
1950 (let ((i (length paramlist))
1951 (parlen 0)
1952 (typlen 0)
1953 (inp nil)
1954 (outp nil)
1955 (accessp nil)
1956 (column nil)
1957 (firstcol nil))
1958
1959 ;; loop until last parameter
1960 (while (not (zerop i))
1961 (setq i (1- i))
1962
1963 ;; get max length of parameter-name
1964 (setq parlen (max parlen (length (nth 0 (nth i paramlist)))))
1965
1966 ;; get max length of type-name
1967 (setq typlen (max typlen (length (nth 4 (nth i paramlist)))))
1968
1969 ;; is there any 'in' ?
1970 (setq inp (or inp (nth 1 (nth i paramlist))))
1971
1972 ;; is there any 'out' ?
1973 (setq outp (or outp (nth 2 (nth i paramlist))))
1974
1975 ;; is there any 'access' ?
1976 (setq accessp (or accessp (nth 3 (nth i paramlist))))
1977 )
1978
1979 ;; does paramlist already start on a separate line ?
1980 (if (save-excursion
1981 (re-search-backward "^.\\|[^ \t]" nil t)
1982 (looking-at "^."))
1983 ;; yes => re-indent it
1984 (progn
1985 (ada-indent-current)
1986 (save-excursion
1987 (if (looking-at "\\(is\\|return\\)")
1988 (replace-match " \\1"))))
1989
1990 ;; no => insert it where we are after removing any whitespace
1991 (fixup-whitespace)
1992 (save-excursion
1993 (cond
1994 ((looking-at "[ \t]*\\(\n\\|;\\)")
1995 (replace-match "\\1"))
1996 ((looking-at "[ \t]*\\(is\\|return\\)")
1997 (replace-match " \\1"))))
1998 (insert " "))
1999
2000 (insert "(")
2001 (ada-indent-current)
2002
2003 (setq firstcol (current-column))
2004 (setq i (length paramlist))
2005
2006 ;; loop until last parameter
2007 (while (not (zerop i))
2008 (setq i (1- i))
2009 (setq column firstcol)
2010
2011 ;; insert parameter-name, space and colon
2012 (insert (nth 0 (nth i paramlist)))
2013 (indent-to (+ column parlen 1))
2014 (insert ": ")
2015 (setq column (current-column))
2016
2017 ;; insert 'in' or space
2018 (if (nth 1 (nth i paramlist))
2019 (insert "in ")
2020 (if (and
2021 (or inp
2022 accessp)
2023 (not (nth 3 (nth i paramlist))))
2024 (insert " ")))
2025
2026 ;; insert 'out' or space
2027 (if (nth 2 (nth i paramlist))
2028 (insert "out ")
2029 (if (and
2030 (or outp
2031 accessp)
2032 (not (nth 3 (nth i paramlist))))
2033 (insert " ")))
2034
2035 ;; insert 'access'
2036 (if (nth 3 (nth i paramlist))
2037 (insert "access "))
2038
2039 (setq column (current-column))
2040
2041 ;; insert type-name and, if necessary, space and default-expression
2042 (insert (nth 4 (nth i paramlist)))
2043 (if (nth 5 (nth i paramlist))
2044 (progn
2045 (indent-to (+ column typlen 1))
2046 (insert (nth 5 (nth i paramlist)))))
2047
2048 ;; check if it was the last parameter
2049 (if (zerop i)
2050 (insert ")")
2051 ;; no => insert ';' and newline and indent
2052 (insert ";")
2053 (newline)
2054 (indent-to firstcol))
2055 )
2056
2057 ;; if anything follows, except semicolon, newline, is or return
2058 ;; put it in a new line and indent it
2059 (unless (looking-at "[ \t]*\\(;\\|\n\\|is\\|return\\)")
2060 (ada-indent-newline-indent))
2061 ))
2062
2063
2064 \f
2065 ;;;----------------------------------------------------------------
2066 ;; Indentation Engine
2067 ;; All indentations are indicated as a two-element string:
2068 ;; - position of reference in the buffer
2069 ;; - offset to indent from this position (can also be a symbol or a list
2070 ;; that are evaluated)
2071 ;; Thus the total indentation for a line is the column number of the reference
2072 ;; position plus whatever value the evaluation of the second element provides.
2073 ;; This mechanism is used so that the Ada mode can "explain" how the
2074 ;; indentation was calculated, by showing which variables were used.
2075 ;;
2076 ;; The indentation itself is done in only one pass: first we try to guess in
2077 ;; what context we are by looking at the following keyword or punctuation
2078 ;; sign. If nothing remarkable is found, just try to guess the indentation
2079 ;; based on previous lines.
2080 ;;
2081 ;; The relevant functions for indentation are:
2082 ;; - `ada-indent-region': Re-indent a region of text
2083 ;; - `ada-justified-indent-current': Re-indent the current line and shows the
2084 ;; calculation that were done
2085 ;; - `ada-indent-current': Re-indent the current line
2086 ;; - `ada-get-current-indent': Calculate the indentation for the current line,
2087 ;; based on the context (see above).
2088 ;; - `ada-get-indent-*': Calculate the indentation in a specific context.
2089 ;; For efficiency, these functions do not check they are in the correct
2090 ;; context.
2091 ;;;----------------------------------------------------------------
2092
2093 (defun ada-indent-region (beg end)
2094 "Indent the region between BEG end END."
2095 (interactive "*r")
2096 (goto-char beg)
2097 (let ((block-done 0)
2098 (lines-remaining (count-lines beg end))
2099 (msg (format "%%4d out of %4d lines remaining ..."
2100 (count-lines beg end)))
2101 (endmark (copy-marker end)))
2102 ;; catch errors while indenting
2103 (while (< (point) endmark)
2104 (if (> block-done 39)
2105 (progn
2106 (setq lines-remaining (- lines-remaining block-done)
2107 block-done 0)
2108 (message msg lines-remaining)))
2109 (if (= (char-after) ?\n) nil
2110 (ada-indent-current))
2111 (forward-line 1)
2112 (setq block-done (1+ block-done)))
2113 (message "Indenting ... done")))
2114
2115 (defun ada-indent-newline-indent ()
2116 "Indent the current line, insert a newline and then indent the new line."
2117 (interactive "*")
2118 (ada-indent-current)
2119 (newline)
2120 (ada-indent-current))
2121
2122 (defun ada-indent-newline-indent-conditional ()
2123 "Insert a newline and indent it.
2124 The original line is re-indented if `ada-indent-after-return' is non-nil."
2125 (interactive "*")
2126 ;; If at end of buffer (entering brand new code), some indentation
2127 ;; fails. For example, a block label requires whitespace following
2128 ;; the : to be recognized. So we do the newline first, then
2129 ;; go back and indent the original line.
2130 (newline)
2131 (if ada-indent-after-return
2132 (progn
2133 (forward-char -1)
2134 (ada-indent-current)
2135 (forward-char 1)))
2136 (ada-indent-current))
2137
2138 (defun ada-justified-indent-current ()
2139 "Indent the current line and explain how the calculation was done."
2140 (interactive)
2141
2142 (let ((cur-indent (ada-indent-current)))
2143
2144 (let ((line (save-excursion
2145 (goto-char (car cur-indent))
2146 (count-lines 1 (point)))))
2147
2148 (if (equal (cdr cur-indent) '(0))
2149 (message (concat "same indentation as line " (number-to-string line)))
2150 (message "%s" (mapconcat (lambda(x)
2151 (cond
2152 ((symbolp x)
2153 (symbol-name x))
2154 ((numberp x)
2155 (number-to-string x))
2156 ((listp x)
2157 (concat "- " (symbol-name (cadr x))))
2158 ))
2159 (cdr cur-indent)
2160 " + "))))
2161 (save-excursion
2162 (goto-char (car cur-indent))
2163 (sit-for 1))))
2164
2165 (defun ada-batch-reformat ()
2166 "Re-indent and re-case all the files found on the command line.
2167 This function should be used from the command line, with a
2168 command like:
2169 emacs -batch -l ada-mode -f ada-batch-reformat file1 file2 ..."
2170
2171 (while command-line-args-left
2172 (let ((source (car command-line-args-left)))
2173 (message "Formating %s" source)
2174 (find-file source)
2175 (ada-indent-region (point-min) (point-max))
2176 (ada-adjust-case-buffer)
2177 (write-file source))
2178 (setq command-line-args-left (cdr command-line-args-left)))
2179 (message "Done")
2180 (kill-emacs 0))
2181
2182 (defsubst ada-goto-previous-word ()
2183 "Move point to the beginning of the previous word of Ada code.
2184 Return the new position of point or nil if not found."
2185 (ada-goto-next-word t))
2186
2187 (defun ada-indent-current ()
2188 "Indent current line as Ada code.
2189 Return the calculation that was done, including the reference point
2190 and the offset."
2191 (interactive)
2192 (let ((previous-syntax-table (syntax-table))
2193 (orgpoint (point-marker))
2194 cur-indent tmp-indent
2195 prev-indent)
2196
2197 (unwind-protect
2198 (progn
2199 (set-syntax-table ada-mode-symbol-syntax-table)
2200
2201 ;; This need to be done here so that the advice is not always
2202 ;; activated (this might interact badly with other modes)
2203 (if (featurep 'xemacs)
2204 (ad-activate 'parse-partial-sexp t))
2205
2206 (save-excursion
2207 (setq cur-indent
2208
2209 ;; Not First line in the buffer ?
2210 (if (save-excursion (zerop (forward-line -1)))
2211 (progn
2212 (back-to-indentation)
2213 (ada-get-current-indent))
2214
2215 ;; first line in the buffer
2216 (list (point-min) 0))))
2217
2218 ;; Evaluate the list to get the column to indent to
2219 ;; prev-indent contains the column to indent to
2220 (if cur-indent
2221 (setq prev-indent (save-excursion (goto-char (car cur-indent))
2222 (current-column))
2223 tmp-indent (cdr cur-indent))
2224 (setq prev-indent 0 tmp-indent '()))
2225
2226 (while (not (null tmp-indent))
2227 (cond
2228 ((numberp (car tmp-indent))
2229 (setq prev-indent (+ prev-indent (car tmp-indent))))
2230 (t
2231 (setq prev-indent (+ prev-indent (eval (car tmp-indent)))))
2232 )
2233 (setq tmp-indent (cdr tmp-indent)))
2234
2235 ;; only re-indent if indentation is different then the current
2236 (if (= (save-excursion (back-to-indentation) (current-column)) prev-indent)
2237 nil
2238 (beginning-of-line)
2239 (delete-horizontal-space)
2240 (indent-to prev-indent))
2241 ;;
2242 ;; restore position of point
2243 ;;
2244 (goto-char orgpoint)
2245 (if (< (current-column) (current-indentation))
2246 (back-to-indentation)))
2247
2248 ;; restore syntax-table
2249 (set-syntax-table previous-syntax-table)
2250 (if (featurep 'xemacs)
2251 (ad-deactivate 'parse-partial-sexp))
2252 )
2253
2254 cur-indent
2255 ))
2256
2257 (defun ada-get-current-indent ()
2258 "Return the indentation to use for the current line."
2259 (let (column
2260 pos
2261 match-cons
2262 result
2263 (orgpoint (save-excursion
2264 (beginning-of-line)
2265 (forward-comment -10000)
2266 (forward-line 1)
2267 (point))))
2268
2269 (setq result
2270 (cond
2271
2272 ;;-----------------------------
2273 ;; in open parenthesis, but not in parameter-list
2274 ;;-----------------------------
2275
2276 ((and ada-indent-to-open-paren
2277 (not (ada-in-paramlist-p))
2278 (setq column (ada-in-open-paren-p)))
2279
2280 ;; check if we have something like this (Table_Component_Type =>
2281 ;; Source_File_Record)
2282 (save-excursion
2283
2284 ;; Align the closing parenthesis on the opening one
2285 (if (= (following-char) ?\))
2286 (save-excursion
2287 (goto-char column)
2288 (skip-chars-backward " \t")
2289 (list (1- (point)) 0))
2290
2291 (if (and (skip-chars-backward " \t")
2292 (= (char-before) ?\n)
2293 (not (forward-comment -10000))
2294 (= (char-before) ?>))
2295 ;; ??? Could use a different variable
2296 (list column 'ada-broken-indent)
2297
2298 ;; We want all continuation lines to be indented the same
2299 ;; (ada-broken-line from the opening parenthesis. However, in
2300 ;; parameter list, each new parameter should be indented at the
2301 ;; column as the opening parenthesis.
2302
2303 ;; A special case to handle nested boolean expressions, as in
2304 ;; ((B
2305 ;; and then C) -- indented by ada-broken-indent
2306 ;; or else D) -- indenting this line.
2307 ;; ??? This is really a hack, we should have a proper way to go to
2308 ;; ??? the beginning of the statement
2309
2310 (if (= (char-before) ?\))
2311 (backward-sexp))
2312
2313 (if (memq (char-before) '(?, ?\; ?\( ?\)))
2314 (list column 0)
2315 (list column 'ada-continuation-indent)
2316 )))))
2317
2318 ;;---------------------------
2319 ;; at end of buffer
2320 ;;---------------------------
2321
2322 ((not (char-after))
2323 (ada-indent-on-previous-lines nil orgpoint orgpoint))
2324
2325 ;;---------------------------
2326 ;; starting with e
2327 ;;---------------------------
2328
2329 ((= (downcase (char-after)) ?e)
2330 (cond
2331
2332 ;; ------- end ------
2333
2334 ((looking-at "end\\>")
2335 (let ((label 0)
2336 limit)
2337 (save-excursion
2338 (ada-goto-matching-start 1)
2339
2340 ;;
2341 ;; found 'loop' => skip back to 'while' or 'for'
2342 ;; if 'loop' is not on a separate line
2343 ;; Stop the search for 'while' and 'for' when a ';' is encountered.
2344 ;;
2345 (if (save-excursion
2346 (beginning-of-line)
2347 (looking-at ".+\\<loop\\>"))
2348 (progn
2349 (save-excursion
2350 (setq limit (car (ada-search-ignore-string-comment ";" t))))
2351 (if (save-excursion
2352 (and
2353 (setq match-cons
2354 (ada-search-ignore-string-comment ada-loop-start-re t limit))
2355 (not (looking-at "\\<loop\\>"))))
2356 (progn
2357 (goto-char (car match-cons))
2358 (save-excursion
2359 (back-to-indentation)
2360 (if (looking-at ada-block-label-re)
2361 (setq label (- ada-label-indent))))))))
2362
2363 ;; found 'record' =>
2364 ;; if the keyword is found at the beginning of a line (or just
2365 ;; after limited, we indent on it, otherwise we indent on the
2366 ;; beginning of the type declaration)
2367 ;; type A is (B : Integer;
2368 ;; C : Integer) is record
2369 ;; end record; -- This is badly indented otherwise
2370 (if (looking-at "record")
2371 (if (save-excursion
2372 (beginning-of-line)
2373 (looking-at "^[ \t]*\\(record\\|limited record\\)"))
2374 (list (save-excursion (back-to-indentation) (point)) 0)
2375 (list (save-excursion
2376 (car (ada-search-ignore-string-comment "\\<type\\>" t)))
2377 0))
2378
2379 ;; Else keep the same indentation as the beginning statement
2380 (list (+ (save-excursion (back-to-indentation) (point)) label) 0)))))
2381
2382 ;; ------ exception ----
2383
2384 ((looking-at "exception\\>")
2385 (save-excursion
2386 (ada-goto-matching-start 1)
2387 (list (save-excursion (back-to-indentation) (point)) 0)))
2388
2389 ;; else
2390
2391 ((looking-at "else\\>")
2392 (if (save-excursion (ada-goto-previous-word)
2393 (looking-at "\\<or\\>"))
2394 (ada-indent-on-previous-lines nil orgpoint orgpoint)
2395 (save-excursion
2396 (ada-goto-matching-start 1 nil t)
2397 (list (progn (back-to-indentation) (point)) 0))))
2398
2399 ;; elsif
2400
2401 ((looking-at "elsif\\>")
2402 (save-excursion
2403 (ada-goto-matching-start 1 nil t)
2404 (list (progn (back-to-indentation) (point)) 0)))
2405
2406 ))
2407
2408 ;;---------------------------
2409 ;; starting with w (when)
2410 ;;---------------------------
2411
2412 ((and (= (downcase (char-after)) ?w)
2413 (looking-at "when\\>"))
2414 (save-excursion
2415 (ada-goto-matching-start 1)
2416 (list (save-excursion (back-to-indentation) (point))
2417 'ada-when-indent)))
2418
2419 ;;---------------------------
2420 ;; starting with t (then)
2421 ;;---------------------------
2422
2423 ((and (= (downcase (char-after)) ?t)
2424 (looking-at "then\\>"))
2425 (if (save-excursion (ada-goto-previous-word)
2426 (looking-at "and\\>"))
2427 (ada-indent-on-previous-lines nil orgpoint orgpoint)
2428 (save-excursion
2429 ;; Select has been added for the statement: "select ... then abort"
2430 (ada-search-ignore-string-comment
2431 "\\<\\(elsif\\|if\\|select\\)\\>" t nil)
2432 (list (progn (back-to-indentation) (point))
2433 'ada-stmt-end-indent))))
2434
2435 ;;---------------------------
2436 ;; starting with l (loop)
2437 ;;---------------------------
2438
2439 ((and (= (downcase (char-after)) ?l)
2440 (looking-at "loop\\>"))
2441 (setq pos (point))
2442 (save-excursion
2443 (goto-char (match-end 0))
2444 (ada-goto-stmt-start)
2445 (if (looking-at "\\<\\(loop\\|if\\)\\>")
2446 (ada-indent-on-previous-lines nil orgpoint orgpoint)
2447 (unless (looking-at ada-loop-start-re)
2448 (ada-search-ignore-string-comment ada-loop-start-re
2449 nil pos))
2450 (if (looking-at "\\<loop\\>")
2451 (ada-indent-on-previous-lines nil orgpoint orgpoint)
2452 (list (progn (back-to-indentation) (point)) 'ada-stmt-end-indent)))))
2453
2454 ;;----------------------------
2455 ;; starting with l (limited) or r (record)
2456 ;;----------------------------
2457
2458 ((or (and (= (downcase (char-after)) ?l)
2459 (looking-at "limited\\>"))
2460 (and (= (downcase (char-after)) ?r)
2461 (looking-at "record\\>")))
2462
2463 (save-excursion
2464 (ada-search-ignore-string-comment
2465 "\\<\\(type\\|use\\)\\>" t nil)
2466 (if (looking-at "\\<use\\>")
2467 (ada-search-ignore-string-comment "for" t nil nil
2468 'word-search-backward))
2469 (list (progn (back-to-indentation) (point))
2470 'ada-indent-record-rel-type)))
2471
2472 ;;---------------------------
2473 ;; starting with b (begin)
2474 ;;---------------------------
2475
2476 ((and (= (downcase (char-after)) ?b)
2477 (looking-at "begin\\>"))
2478 (save-excursion
2479 (if (ada-goto-matching-decl-start t)
2480 (list (progn (back-to-indentation) (point)) 0)
2481 (ada-indent-on-previous-lines nil orgpoint orgpoint))))
2482
2483 ;;---------------------------
2484 ;; starting with i (is)
2485 ;;---------------------------
2486
2487 ((and (= (downcase (char-after)) ?i)
2488 (looking-at "is\\>"))
2489
2490 (if (and ada-indent-is-separate
2491 (save-excursion
2492 (goto-char (match-end 0))
2493 (ada-goto-next-non-ws (save-excursion (end-of-line)
2494 (point)))
2495 (looking-at "\\<abstract\\>\\|\\<separate\\>")))
2496 (save-excursion
2497 (ada-goto-stmt-start)
2498 (list (progn (back-to-indentation) (point)) 'ada-indent))
2499 (save-excursion
2500 (ada-goto-stmt-start)
2501 (if (looking-at "\\<overriding\\|package\\|procedure\\|function\\>")
2502 (list (progn (back-to-indentation) (point)) 0)
2503 (list (progn (back-to-indentation) (point)) 'ada-indent)))))
2504
2505 ;;---------------------------
2506 ;; starting with r (return, renames)
2507 ;;---------------------------
2508
2509 ((and (= (downcase (char-after)) ?r)
2510 (looking-at "re\\(turn\\|names\\)\\>"))
2511
2512 (save-excursion
2513 (let ((var 'ada-indent-return))
2514 ;; If looking at a renames, skip the 'return' statement too
2515 (if (looking-at "renames")
2516 (let (pos)
2517 (save-excursion
2518 (set 'pos (ada-search-ignore-string-comment ";\\|return\\>" t)))
2519 (if (and pos
2520 (= (downcase (char-after (car pos))) ?r))
2521 (goto-char (car pos)))
2522 (set 'var 'ada-indent-renames)))
2523
2524 (forward-comment -1000)
2525 (if (= (char-before) ?\))
2526 (forward-sexp -1)
2527 (forward-word -1))
2528
2529 ;; If there is a parameter list, and we have a function declaration
2530 ;; or a access to subprogram declaration
2531 (let ((num-back 1))
2532 (if (and (= (following-char) ?\()
2533 (save-excursion
2534 (or (progn
2535 (backward-word 1)
2536 (looking-at "\\(function\\|procedure\\)\\>"))
2537 (progn
2538 (backward-word 1)
2539 (set 'num-back 2)
2540 (looking-at "\\(function\\|procedure\\)\\>")))))
2541
2542 ;; The indentation depends of the value of ada-indent-return
2543 (if (<= (eval var) 0)
2544 (list (point) (list '- var))
2545 (list (progn (backward-word num-back) (point))
2546 var))
2547
2548 ;; Else there is no parameter list, but we have a function
2549 ;; Only do something special if the user want to indent
2550 ;; relative to the "function" keyword
2551 (if (and (> (eval var) 0)
2552 (save-excursion (forward-word -1)
2553 (looking-at "function\\>")))
2554 (list (progn (forward-word -1) (point)) var)
2555
2556 ;; Else...
2557 (ada-indent-on-previous-lines nil orgpoint orgpoint)))))))
2558
2559 ;;--------------------------------
2560 ;; starting with 'o' or 'p'
2561 ;; 'or' as statement-start
2562 ;; 'private' as statement-start
2563 ;;--------------------------------
2564
2565 ((and (or (= (downcase (char-after)) ?o)
2566 (= (downcase (char-after)) ?p))
2567 (or (ada-looking-at-semi-or)
2568 (ada-looking-at-semi-private)))
2569 (save-excursion
2570 ;; ??? Wasn't this done already in ada-looking-at-semi-or ?
2571 (ada-goto-matching-start 1)
2572 (list (progn (back-to-indentation) (point)) 0)))
2573
2574 ;;--------------------------------
2575 ;; starting with 'd' (do)
2576 ;;--------------------------------
2577
2578 ((and (= (downcase (char-after)) ?d)
2579 (looking-at "do\\>"))
2580 (save-excursion
2581 (ada-goto-stmt-start)
2582 (list (progn (back-to-indentation) (point)) 'ada-stmt-end-indent)))
2583
2584 ;;--------------------------------
2585 ;; starting with '-' (comment)
2586 ;;--------------------------------
2587
2588 ((= (char-after) ?-)
2589 (if ada-indent-comment-as-code
2590
2591 ;; Indent comments on previous line comments if required
2592 ;; We must use a search-forward (even if the code is more complex),
2593 ;; since we want to find the beginning of the comment.
2594 (let (pos)
2595
2596 (if (and ada-indent-align-comments
2597 (save-excursion
2598 (forward-line -1)
2599 (beginning-of-line)
2600 (while (and (not pos)
2601 (search-forward "--"
2602 (save-excursion
2603 (end-of-line) (point))
2604 t))
2605 (unless (ada-in-string-p)
2606 (setq pos (point))))
2607 pos))
2608 (list (- pos 2) 0)
2609
2610 ;; Else always on previous line
2611 (ada-indent-on-previous-lines nil orgpoint orgpoint)))
2612
2613 ;; Else same indentation as the previous line
2614 (list (save-excursion (back-to-indentation) (point)) 0)))
2615
2616 ;;--------------------------------
2617 ;; starting with '#' (preprocessor line)
2618 ;;--------------------------------
2619
2620 ((and (= (char-after) ?#)
2621 (equal ada-which-compiler 'gnat)
2622 (looking-at "#[ \t]*\\(if\\|els\\(e\\|if\\)\\|end[ \t]*if\\)"))
2623 (list (save-excursion (beginning-of-line) (point)) 0))
2624
2625 ;;--------------------------------
2626 ;; starting with ')' (end of a parameter list)
2627 ;;--------------------------------
2628
2629 ((and (not (eobp)) (= (char-after) ?\)))
2630 (save-excursion
2631 (forward-char 1)
2632 (backward-sexp 1)
2633 (list (point) 0)))
2634
2635 ;;---------------------------------
2636 ;; new/abstract/separate
2637 ;;---------------------------------
2638
2639 ((looking-at "\\(new\\|abstract\\|separate\\)\\>")
2640 (ada-indent-on-previous-lines nil orgpoint orgpoint))
2641
2642 ;;---------------------------------
2643 ;; package/function/procedure
2644 ;;---------------------------------
2645
2646 ((and (or (= (downcase (char-after)) ?p) (= (downcase (char-after)) ?f))
2647 (looking-at "\\<\\(package\\|function\\|procedure\\)\\>"))
2648 (save-excursion
2649 ;; Go up until we find either a generic section, or the end of the
2650 ;; previous subprogram/package, or 'overriding' for this function/procedure
2651 (let (found)
2652 (while (and (not found)
2653 (ada-search-ignore-string-comment
2654 "\\<\\(generic\\|end\\|begin\\|overriding\\|package\\|procedure\\|function\\)\\>" t))
2655
2656 ;; avoid "with procedure"... in generic parts
2657 (save-excursion
2658 (forward-word -1)
2659 (setq found (not (looking-at "with"))))))
2660
2661 (cond
2662 ((looking-at "\\<generic\\|overriding\\>")
2663 (list (progn (back-to-indentation) (point)) 0))
2664
2665 (t
2666 (ada-indent-on-previous-lines nil orgpoint orgpoint)))))
2667
2668 ;;---------------------------------
2669 ;; label
2670 ;;---------------------------------
2671
2672 ((looking-at ada-label-re)
2673 (if (ada-in-decl-p)
2674 ;; ada-block-label-re matches variable declarations
2675 (ada-indent-on-previous-lines nil orgpoint orgpoint)
2676 (append (ada-indent-on-previous-lines nil orgpoint orgpoint)
2677 '(ada-label-indent))))
2678
2679 ))
2680
2681 ;;---------------------------------
2682 ;; Other syntaxes
2683 ;;---------------------------------
2684 (or result (ada-indent-on-previous-lines nil orgpoint orgpoint))))
2685
2686 (defun ada-indent-on-previous-lines (&optional nomove orgpoint initial-pos)
2687 "Calculate the indentation for the new line after ORGPOINT.
2688 The result list is based on the previous lines in the buffer.
2689 If NOMOVE is nil, moves point to the beginning of the current statement.
2690 if INITIAL-POS is non-nil, moves point to INITIAL-POS before calculation."
2691 (if initial-pos
2692 (goto-char initial-pos))
2693 (let ((oldpoint (point)))
2694
2695 ;; Is inside a parameter-list ?
2696 (if (ada-in-paramlist-p)
2697 (ada-get-indent-paramlist)
2698
2699 ;; Move to beginning of current statement. If already at a
2700 ;; statement start, move to beginning of enclosing statement.
2701 (unless nomove
2702 (ada-goto-stmt-start t))
2703
2704 ;; no beginning found => don't change indentation
2705 (if (and (eq oldpoint (point))
2706 (not nomove))
2707 (ada-get-indent-nochange)
2708
2709 (cond
2710 ;;
2711 ((and
2712 ada-indent-to-open-paren
2713 (ada-in-open-paren-p))
2714 (ada-get-indent-open-paren))
2715 ;;
2716 ((looking-at "end\\>")
2717 (ada-get-indent-end orgpoint))
2718 ;;
2719 ((looking-at ada-loop-start-re)
2720 (ada-get-indent-loop orgpoint))
2721 ;;
2722 ((looking-at ada-subprog-start-re)
2723 (ada-get-indent-subprog orgpoint))
2724 ;;
2725 ((looking-at ada-block-start-re)
2726 (ada-get-indent-block-start orgpoint))
2727 ;;
2728 ((looking-at ada-block-label-re) ; also variable declaration
2729 (ada-get-indent-block-label orgpoint))
2730 ;;
2731 ((looking-at ada-goto-label-re)
2732 (ada-get-indent-goto-label orgpoint))
2733 ;;
2734 ((looking-at "\\(sub\\)?type\\>")
2735 (ada-get-indent-type orgpoint))
2736 ;;
2737 ;; "then" has to be included in the case of "select...then abort"
2738 ;; statements, since (goto-stmt-start) at the beginning of
2739 ;; the current function would leave the cursor on that position
2740 ((looking-at "\\(\\(els\\)?if\\>\\)\\|then abort\\\>")
2741 (ada-get-indent-if orgpoint))
2742 ;;
2743 ((looking-at "case\\>")
2744 (ada-get-indent-case orgpoint))
2745 ;;
2746 ((looking-at "when\\>")
2747 (ada-get-indent-when orgpoint))
2748 ;;
2749 ((looking-at "separate\\>")
2750 (ada-get-indent-nochange))
2751 ;;
2752 ((looking-at "with\\>\\|use\\>")
2753 ;; Are we still in that statement, or are we in fact looking at
2754 ;; the previous one ?
2755 (if (save-excursion (search-forward ";" oldpoint t))
2756 (list (progn (back-to-indentation) (point)) 0)
2757 (list (point) (if (looking-at "with")
2758 'ada-with-indent
2759 'ada-use-indent))))
2760 ;;
2761 (t
2762 (ada-get-indent-noindent orgpoint)))))
2763 ))
2764
2765 (defun ada-get-indent-open-paren ()
2766 "Calculate the indentation when point is behind an unclosed parenthesis."
2767 (list (ada-in-open-paren-p) 0))
2768
2769 (defun ada-get-indent-nochange ()
2770 "Return the current indentation of the previous line."
2771 (save-excursion
2772 (forward-line -1)
2773 (back-to-indentation)
2774 (list (point) 0)))
2775
2776 (defun ada-get-indent-paramlist ()
2777 "Calculate the indentation when point is inside a parameter list."
2778 (save-excursion
2779 (ada-search-ignore-string-comment "[^ \t\n]" t nil t)
2780 (cond
2781 ;; in front of the first parameter
2782 ((= (char-after) ?\()
2783 (goto-char (match-end 0))
2784 (list (point) 0))
2785
2786 ;; in front of another parameter
2787 ((= (char-after) ?\;)
2788 (goto-char (cdr (ada-search-ignore-string-comment "(\\|;" t nil t)))
2789 (ada-goto-next-non-ws)
2790 (list (point) 0))
2791
2792 ;; After an affectation (default parameter value in subprogram
2793 ;; declaration)
2794 ((and (= (following-char) ?=) (= (preceding-char) ?:))
2795 (back-to-indentation)
2796 (list (point) 'ada-broken-indent))
2797
2798 ;; inside a parameter declaration
2799 (t
2800 (goto-char (cdr (ada-search-ignore-string-comment "(\\|;" t nil t)))
2801 (ada-goto-next-non-ws)
2802 (list (point) 'ada-broken-indent)))))
2803
2804 (defun ada-get-indent-end (orgpoint)
2805 "Calculate the indentation when point is just before an end statement.
2806 ORGPOINT is the limit position used in the calculation."
2807 (let ((defun-name nil)
2808 (indent nil))
2809
2810 ;; is the line already terminated by ';' ?
2811 (if (save-excursion
2812 (ada-search-ignore-string-comment ";" nil orgpoint nil
2813 'search-forward))
2814
2815 ;; yes, look what's following 'end'
2816 (progn
2817 (forward-word 1)
2818 (ada-goto-next-non-ws)
2819 (cond
2820 ;;
2821 ;; loop/select/if/case/return
2822 ;;
2823 ((looking-at "\\<\\(loop\\|select\\|if\\|case\\|return\\)\\>")
2824 (save-excursion (ada-check-matching-start (match-string 0)))
2825 (list (save-excursion (back-to-indentation) (point)) 0))
2826
2827 ;;
2828 ;; record
2829 ;;
2830 ((looking-at "\\<record\\>")
2831 (save-excursion
2832 (ada-check-matching-start (match-string 0))
2833 ;; we are now looking at the matching "record" statement
2834 (forward-word 1)
2835 (ada-goto-stmt-start)
2836 ;; now on the matching type declaration, or use clause
2837 (unless (looking-at "\\(for\\|type\\)\\>")
2838 (ada-search-ignore-string-comment "\\<type\\>" t))
2839 (list (progn (back-to-indentation) (point)) 0)))
2840 ;;
2841 ;; a named block end
2842 ;;
2843 ((looking-at ada-ident-re)
2844 (setq defun-name (match-string 0))
2845 (save-excursion
2846 (ada-goto-matching-start 0)
2847 (ada-check-defun-name defun-name))
2848 (list (progn (back-to-indentation) (point)) 0))
2849 ;;
2850 ;; a block-end without name
2851 ;;
2852 ((= (char-after) ?\;)
2853 (save-excursion
2854 (ada-goto-matching-start 0)
2855 (if (looking-at "\\<begin\\>")
2856 (progn
2857 (setq indent (list (point) 0))
2858 (if (ada-goto-matching-decl-start t)
2859 (list (progn (back-to-indentation) (point)) 0)
2860 indent))
2861 (list (progn (back-to-indentation) (point)) 0)
2862 )))
2863 ;;
2864 ;; anything else - should maybe signal an error ?
2865 ;;
2866 (t
2867 (list (save-excursion (back-to-indentation) (point))
2868 'ada-broken-indent))))
2869
2870 (list (save-excursion (back-to-indentation) (point))
2871 'ada-broken-indent))))
2872
2873 (defun ada-get-indent-case (orgpoint)
2874 "Calculate the indentation when point is just before a case statement.
2875 ORGPOINT is the limit position used in the calculation."
2876 (let ((match-cons nil)
2877 (opos (point)))
2878 (cond
2879 ;;
2880 ;; case..is..when..=>
2881 ;;
2882 ((save-excursion
2883 (setq match-cons (and
2884 ;; the `=>' must be after the keyword `is'.
2885 (ada-search-ignore-string-comment
2886 "is" nil orgpoint nil 'word-search-forward)
2887 (ada-search-ignore-string-comment
2888 "[ \t\n]+=>" nil orgpoint))))
2889 (save-excursion
2890 (goto-char (car match-cons))
2891 (unless (ada-search-ignore-string-comment "when" t opos)
2892 (error "Missing 'when' between 'case' and '=>'"))
2893 (list (save-excursion (back-to-indentation) (point)) 'ada-indent)))
2894 ;;
2895 ;; case..is..when
2896 ;;
2897 ((save-excursion
2898 (setq match-cons (ada-search-ignore-string-comment
2899 "when" nil orgpoint nil 'word-search-forward)))
2900 (goto-char (cdr match-cons))
2901 (list (save-excursion (back-to-indentation) (point)) 'ada-broken-indent))
2902 ;;
2903 ;; case..is
2904 ;;
2905 ((save-excursion
2906 (setq match-cons (ada-search-ignore-string-comment
2907 "is" nil orgpoint nil 'word-search-forward)))
2908 (list (save-excursion (back-to-indentation) (point)) 'ada-when-indent))
2909 ;;
2910 ;; incomplete case
2911 ;;
2912 (t
2913 (list (save-excursion (back-to-indentation) (point))
2914 'ada-broken-indent)))))
2915
2916 (defun ada-get-indent-when (orgpoint)
2917 "Calculate the indentation when point is just before a when statement.
2918 ORGPOINT is the limit position used in the calculation."
2919 (let ((cur-indent (save-excursion (back-to-indentation) (point))))
2920 (if (ada-search-ignore-string-comment "[ \t\n]*=>" nil orgpoint)
2921 (list cur-indent 'ada-indent)
2922 (list cur-indent 'ada-broken-indent))))
2923
2924 (defun ada-get-indent-if (orgpoint)
2925 "Calculate the indentation when point is just before an if statement.
2926 ORGPOINT is the limit position used in the calculation."
2927 (let ((cur-indent (save-excursion (back-to-indentation) (point)))
2928 (match-cons nil))
2929 ;;
2930 ;; Move to the correct then (ignore all "and then")
2931 ;;
2932 (while (and (setq match-cons (ada-search-ignore-string-comment
2933 "\\<\\(then\\|and[ \t]*then\\)\\>"
2934 nil orgpoint))
2935 (= (downcase (char-after (car match-cons))) ?a)))
2936 ;; If "then" was found (we are looking at it)
2937 (if match-cons
2938 (progn
2939 ;;
2940 ;; 'then' first in separate line ?
2941 ;; => indent according to 'then',
2942 ;; => else indent according to 'if'
2943 ;;
2944 (if (save-excursion
2945 (back-to-indentation)
2946 (looking-at "\\<then\\>"))
2947 (setq cur-indent (save-excursion (back-to-indentation) (point))))
2948 ;; skip 'then'
2949 (forward-word 1)
2950 (list cur-indent 'ada-indent))
2951
2952 (list cur-indent 'ada-broken-indent))))
2953
2954 (defun ada-get-indent-block-start (orgpoint)
2955 "Calculate the indentation when point is at the start of a block.
2956 ORGPOINT is the limit position used in the calculation."
2957 (let ((pos nil))
2958 (cond
2959 ((save-excursion
2960 (forward-word 1)
2961 (setq pos (ada-goto-next-non-ws orgpoint)))
2962 (goto-char pos)
2963 (save-excursion
2964 (ada-indent-on-previous-lines t orgpoint)))
2965
2966 ;; Special case for record types, for instance for:
2967 ;; type A is (B : Integer;
2968 ;; C : Integer) is record
2969 ;; null; -- This is badly indented otherwise
2970 ((looking-at "record")
2971
2972 ;; If record is at the beginning of the line, indent from there
2973 (if (save-excursion
2974 (beginning-of-line)
2975 (looking-at "^[ \t]*\\(record\\|limited record\\)"))
2976 (list (save-excursion (back-to-indentation) (point)) 'ada-indent)
2977
2978 ;; else indent relative to the type command
2979 (list (save-excursion
2980 (car (ada-search-ignore-string-comment "\\<type\\>" t)))
2981 'ada-indent)))
2982
2983 ;; Special case for label:
2984 ((looking-at ada-block-label-re)
2985 (list (- (save-excursion (back-to-indentation) (point)) ada-label-indent) 'ada-indent))
2986
2987 ;; nothing follows the block-start
2988 (t
2989 (list (save-excursion (back-to-indentation) (point)) 'ada-indent)))))
2990
2991 (defun ada-get-indent-subprog (orgpoint)
2992 "Calculate the indentation when point is just before a subprogram.
2993 ORGPOINT is the limit position used in the calculation."
2994 (let ((match-cons nil)
2995 (cur-indent (save-excursion (back-to-indentation) (point)))
2996 (foundis nil))
2997 ;;
2998 ;; is there an 'is' in front of point ?
2999 ;;
3000 (if (save-excursion
3001 (setq match-cons
3002 (ada-search-ignore-string-comment
3003 "\\<\\(is\\|do\\)\\>" nil orgpoint)))
3004 ;;
3005 ;; yes, then skip to its end
3006 ;;
3007 (progn
3008 (setq foundis t)
3009 (goto-char (cdr match-cons)))
3010 ;;
3011 ;; no, then goto next non-ws, if there is one in front of point
3012 ;;
3013 (progn
3014 (unless (ada-goto-next-non-ws orgpoint)
3015 (goto-char orgpoint))))
3016
3017 (cond
3018 ;;
3019 ;; nothing follows 'is'
3020 ;;
3021 ((and
3022 foundis
3023 (save-excursion
3024 (not (ada-search-ignore-string-comment
3025 "[^ \t\n]" nil orgpoint t))))
3026 (list cur-indent 'ada-indent))
3027 ;;
3028 ;; is abstract/separate/new ...
3029 ;;
3030 ((and
3031 foundis
3032 (save-excursion
3033 (setq match-cons
3034 (ada-search-ignore-string-comment
3035 "\\<\\(separate\\|new\\|abstract\\)\\>"
3036 nil orgpoint))))
3037 (goto-char (car match-cons))
3038 (ada-search-ignore-string-comment ada-subprog-start-re t)
3039 (ada-get-indent-noindent orgpoint))
3040 ;;
3041 ;; something follows 'is'
3042 ;;
3043 ((and
3044 foundis
3045 (save-excursion (setq match-cons (ada-goto-next-non-ws orgpoint)))
3046 (goto-char match-cons)
3047 (ada-indent-on-previous-lines t orgpoint)))
3048 ;;
3049 ;; no 'is' but ';'
3050 ;;
3051 ((save-excursion
3052 (ada-search-ignore-string-comment ";" nil orgpoint nil 'search-forward))
3053 (list cur-indent 0))
3054 ;;
3055 ;; no 'is' or ';'
3056 ;;
3057 (t
3058 (list cur-indent 'ada-broken-indent)))))
3059
3060 (defun ada-get-indent-noindent (orgpoint)
3061 "Calculate the indentation when point is just before a 'noindent stmt'.
3062 ORGPOINT is the limit position used in the calculation."
3063 (let ((label 0))
3064 (save-excursion
3065 (beginning-of-line)
3066
3067 (cond
3068
3069 ;; This one is called when indenting a line preceded by a multi-line
3070 ;; subprogram declaration (in that case, we are at this point inside
3071 ;; the parameter declaration list)
3072 ((ada-in-paramlist-p)
3073 (ada-previous-procedure)
3074 (list (save-excursion (back-to-indentation) (point)) 0))
3075
3076 ;; This one is called when indenting the second line of a multi-line
3077 ;; declaration section, in a declare block or a record declaration
3078 ((looking-at "[ \t]*\\(\\sw\\|_\\)*[ \t]*,[ \t]*$")
3079 (list (save-excursion (back-to-indentation) (point))
3080 'ada-broken-decl-indent))
3081
3082 ;; This one is called in every other case when indenting a line at the
3083 ;; top level
3084 (t
3085 (if (looking-at (concat "[ \t]*" ada-block-label-re))
3086 (setq label (- ada-label-indent))
3087
3088 (let (p)
3089
3090 ;; "with private" or "null record" cases
3091 (if (or (save-excursion
3092 (and (ada-search-ignore-string-comment "\\<private\\>" nil orgpoint)
3093 (setq p (point))
3094 (save-excursion (forward-char -7);; skip back "private"
3095 (ada-goto-previous-word)
3096 (looking-at "with"))))
3097 (save-excursion
3098 (and (ada-search-ignore-string-comment "\\<record\\>" nil orgpoint)
3099 (setq p (point))
3100 (save-excursion (forward-char -6);; skip back "record"
3101 (ada-goto-previous-word)
3102 (looking-at "null")))))
3103 (progn
3104 (goto-char p)
3105 (re-search-backward "\\<\\(type\\|subtype\\)\\>" nil t)
3106 (list (save-excursion (back-to-indentation) (point)) 0)))))
3107 (if (save-excursion
3108 (ada-search-ignore-string-comment ";" nil orgpoint nil
3109 'search-forward))
3110 (list (+ (save-excursion (back-to-indentation) (point)) label) 0)
3111 (list (+ (save-excursion (back-to-indentation) (point)) label)
3112 'ada-broken-indent)))))))
3113
3114 (defun ada-get-indent-block-label (orgpoint)
3115 "Calculate the indentation when before a label or variable declaration.
3116 ORGPOINT is the limit position used in the calculation."
3117 (let ((match-cons nil)
3118 (cur-indent (save-excursion (back-to-indentation) (point))))
3119 (ada-search-ignore-string-comment ":" nil)
3120 (cond
3121 ;; loop label
3122 ((save-excursion
3123 (setq match-cons (ada-search-ignore-string-comment
3124 ada-loop-start-re nil orgpoint)))
3125 (goto-char (car match-cons))
3126 (ada-get-indent-loop orgpoint))
3127
3128 ;; declare label
3129 ((save-excursion
3130 (setq match-cons (ada-search-ignore-string-comment
3131 "\\<declare\\|begin\\>" nil orgpoint)))
3132 (goto-char (car match-cons))
3133 (list (save-excursion (back-to-indentation) (point)) 'ada-indent))
3134
3135 ;; variable declaration
3136 ((ada-in-decl-p)
3137 (if (save-excursion
3138 (ada-search-ignore-string-comment ";" nil orgpoint))
3139 (list cur-indent 0)
3140 (list cur-indent 'ada-broken-indent)))
3141
3142 ;; nothing follows colon
3143 (t
3144 (list cur-indent '(- ada-label-indent))))))
3145
3146 (defun ada-get-indent-goto-label (orgpoint)
3147 "Calculate the indentation when at a goto label."
3148 (search-forward ">>")
3149 (ada-goto-next-non-ws)
3150 (if (>= (point) orgpoint)
3151 ;; labeled statement is the one we need to indent
3152 (list (- (point) ada-label-indent))
3153 ;; else indentation is indent for labeled statement
3154 (ada-indent-on-previous-lines t orgpoint)))
3155
3156 (defun ada-get-indent-loop (orgpoint)
3157 "Calculate the indentation when just before a loop or a for ... use.
3158 ORGPOINT is the limit position used in the calculation."
3159 (let ((match-cons nil)
3160 (pos (point))
3161
3162 ;; If looking at a named block, skip the label
3163 (label (save-excursion
3164 (back-to-indentation)
3165 (if (looking-at ada-block-label-re)
3166 (- ada-label-indent)
3167 0))))
3168
3169 (cond
3170
3171 ;;
3172 ;; statement complete
3173 ;;
3174 ((save-excursion
3175 (ada-search-ignore-string-comment ";" nil orgpoint nil
3176 'search-forward))
3177 (list (+ (save-excursion (back-to-indentation) (point)) label) 0))
3178 ;;
3179 ;; simple loop
3180 ;;
3181 ((looking-at "loop\\>")
3182 (setq pos (ada-get-indent-block-start orgpoint))
3183 (if (equal label 0)
3184 pos
3185 (list (+ (car pos) label) (cadr pos))))
3186
3187 ;;
3188 ;; 'for'- loop (or also a for ... use statement)
3189 ;;
3190 ((looking-at "for\\>")
3191 (cond
3192 ;;
3193 ;; for ... use
3194 ;;
3195 ((save-excursion
3196 (and
3197 (goto-char (match-end 0))
3198 (ada-goto-next-non-ws orgpoint)
3199 (forward-word 1)
3200 (if (= (char-after) ?') (forward-word 1) t)
3201 (ada-goto-next-non-ws orgpoint)
3202 (looking-at "\\<use\\>")
3203 ;;
3204 ;; check if there is a 'record' before point
3205 ;;
3206 (progn
3207 (setq match-cons (ada-search-ignore-string-comment
3208 "record" nil orgpoint nil 'word-search-forward))
3209 t)))
3210 (if match-cons
3211 (progn
3212 (goto-char (car match-cons))
3213 (list (save-excursion (back-to-indentation) (point)) 'ada-indent))
3214 (list (save-excursion (back-to-indentation) (point)) 'ada-broken-indent))
3215 )
3216
3217 ;;
3218 ;; for..loop
3219 ;;
3220 ((save-excursion
3221 (setq match-cons (ada-search-ignore-string-comment
3222 "loop" nil orgpoint nil 'word-search-forward)))
3223 (goto-char (car match-cons))
3224 ;;
3225 ;; indent according to 'loop', if it's first in the line;
3226 ;; otherwise to 'for'
3227 ;;
3228 (unless (save-excursion
3229 (back-to-indentation)
3230 (looking-at "\\<loop\\>"))
3231 (goto-char pos))
3232 (list (+ (save-excursion (back-to-indentation) (point)) label)
3233 'ada-indent))
3234 ;;
3235 ;; for-statement is broken
3236 ;;
3237 (t
3238 (list (+ (save-excursion (back-to-indentation) (point)) label)
3239 'ada-broken-indent))))
3240
3241 ;;
3242 ;; 'while'-loop
3243 ;;
3244 ((looking-at "while\\>")
3245 ;;
3246 ;; while..loop ?
3247 ;;
3248 (if (save-excursion
3249 (setq match-cons (ada-search-ignore-string-comment
3250 "loop" nil orgpoint nil 'word-search-forward)))
3251
3252 (progn
3253 (goto-char (car match-cons))
3254 ;;
3255 ;; indent according to 'loop', if it's first in the line;
3256 ;; otherwise to 'while'.
3257 ;;
3258 (unless (save-excursion
3259 (back-to-indentation)
3260 (looking-at "\\<loop\\>"))
3261 (goto-char pos))
3262 (list (+ (save-excursion (back-to-indentation) (point)) label)
3263 'ada-indent))
3264
3265 (list (+ (save-excursion (back-to-indentation) (point)) label)
3266 'ada-broken-indent))))))
3267
3268 (defun ada-get-indent-type (orgpoint)
3269 "Calculate the indentation when before a type statement.
3270 ORGPOINT is the limit position used in the calculation."
3271 (let ((match-dat nil))
3272 (cond
3273 ;;
3274 ;; complete record declaration
3275 ;;
3276 ((save-excursion
3277 (and
3278 (setq match-dat (ada-search-ignore-string-comment
3279 "end" nil orgpoint nil 'word-search-forward))
3280 (ada-goto-next-non-ws)
3281 (looking-at "\\<record\\>")
3282 (forward-word 1)
3283 (ada-goto-next-non-ws)
3284 (= (char-after) ?\;)))
3285 (goto-char (car match-dat))
3286 (list (save-excursion (back-to-indentation) (point)) 0))
3287 ;;
3288 ;; record type
3289 ;;
3290 ((save-excursion
3291 (setq match-dat (ada-search-ignore-string-comment
3292 "record" nil orgpoint nil 'word-search-forward)))
3293 (goto-char (car match-dat))
3294 (list (save-excursion (back-to-indentation) (point)) 'ada-indent))
3295 ;;
3296 ;; complete type declaration
3297 ;;
3298 ((save-excursion
3299 (ada-search-ignore-string-comment ";" nil orgpoint nil
3300 'search-forward))
3301 (list (save-excursion (back-to-indentation) (point)) 0))
3302 ;;
3303 ;; "type ... is", but not "type ... is ...", which is broken
3304 ;;
3305 ((save-excursion
3306 (and
3307 (ada-search-ignore-string-comment "is" nil orgpoint nil
3308 'word-search-forward)
3309 (not (ada-goto-next-non-ws orgpoint))))
3310 (list (save-excursion (back-to-indentation) (point)) 'ada-broken-indent))
3311 ;;
3312 ;; broken statement
3313 ;;
3314 (t
3315 (list (save-excursion (back-to-indentation) (point))
3316 'ada-broken-indent)))))
3317
3318 \f
3319 ;; -----------------------------------------------------------
3320 ;; -- searching and matching
3321 ;; -----------------------------------------------------------
3322
3323 (defun ada-goto-stmt-start (&optional ignore-goto-label)
3324 "Move point to the beginning of the statement that point is in or after.
3325 Return the new position of point.
3326 As a special case, if we are looking at a closing parenthesis, skip to the
3327 open parenthesis."
3328 (let ((match-dat nil)
3329 (orgpoint (point)))
3330
3331 (setq match-dat (ada-search-prev-end-stmt))
3332 (if match-dat
3333
3334 ;;
3335 ;; found a previous end-statement => check if anything follows
3336 ;;
3337 (unless (looking-at "declare")
3338 (progn
3339 (unless (save-excursion
3340 (goto-char (cdr match-dat))
3341 (ada-goto-next-non-ws orgpoint ignore-goto-label))
3342 ;;
3343 ;; nothing follows => it's the end-statement directly in
3344 ;; front of point => search again
3345 ;;
3346 (setq match-dat (ada-search-prev-end-stmt)))
3347 ;;
3348 ;; if found the correct end-statement => goto next non-ws
3349 ;;
3350 (if match-dat
3351 (goto-char (cdr match-dat)))
3352 (ada-goto-next-non-ws)
3353 ))
3354
3355 ;;
3356 ;; no previous end-statement => we are at the beginning of the
3357 ;; accessible part of the buffer
3358 ;;
3359 (progn
3360 (goto-char (point-min))
3361 ;;
3362 ;; skip to the very first statement, if there is one
3363 ;;
3364 (unless (ada-goto-next-non-ws orgpoint)
3365 (goto-char orgpoint))))
3366 (point)))
3367
3368
3369 (defun ada-search-prev-end-stmt ()
3370 "Move point to previous end statement.
3371 Return a cons cell whose car is the beginning and whose cdr
3372 is the end of the match."
3373 (let ((match-dat nil)
3374 (found nil))
3375
3376 ;; search until found or beginning-of-buffer
3377 (while
3378 (and
3379 (not found)
3380 (setq match-dat (ada-search-ignore-string-comment
3381 ada-end-stmt-re t)))
3382
3383 (goto-char (car match-dat))
3384 (unless (ada-in-open-paren-p)
3385 (cond
3386
3387 ((and (looking-at
3388 "\\<\\(record\\|loop\\|select\\|else\\|then\\)\\>")
3389 (save-excursion
3390 (ada-goto-previous-word)
3391 (looking-at "\\<\\(end\\|or\\|and\\)\\>[ \t]*[^;]")))
3392 (forward-word -1))
3393
3394 ((looking-at "is")
3395 (setq found
3396 (and (save-excursion (ada-goto-previous-word)
3397 (ada-goto-previous-word)
3398 (not (looking-at "subtype")))
3399
3400 (save-excursion (goto-char (cdr match-dat))
3401 (ada-goto-next-non-ws)
3402 ;; words that can go after an 'is'
3403 (not (looking-at
3404 (eval-when-compile
3405 (concat "\\<"
3406 (regexp-opt
3407 '("separate" "access" "array"
3408 "private" "abstract" "new") t)
3409 "\\>\\|("))))))))
3410
3411 ((looking-at "private")
3412 (save-excursion
3413 (backward-word 1)
3414 (setq found (not (looking-at "is")))))
3415
3416 (t
3417 (setq found t))
3418 )))
3419
3420 (if found
3421 match-dat
3422 nil)))
3423
3424
3425 (defun ada-goto-next-non-ws (&optional limit skip-goto-label)
3426 "Skip to next non-whitespace character.
3427 Skips spaces, newlines and comments, and possibly goto labels.
3428 Return `point' if moved, nil if not.
3429 Stop the search at LIMIT.
3430 Do not call this function from within a string."
3431 (unless limit
3432 (setq limit (point-max)))
3433 (while (and (<= (point) limit)
3434 (or (progn (forward-comment 10000)
3435 (if (and (not (eobp))
3436 (save-excursion (forward-char 1)
3437 (ada-in-string-p)))
3438 (progn (forward-sexp 1) t)))
3439 (and skip-goto-label
3440 (looking-at ada-goto-label-re)
3441 (progn
3442 (goto-char (match-end 0))
3443 t)))))
3444 (if (< (point) limit)
3445 (point)
3446 nil)
3447 )
3448
3449
3450 (defun ada-goto-stmt-end (&optional limit)
3451 "Move point to the end of the statement that point is in or before.
3452 Return the new position of point or nil if not found.
3453 Stop the search at LIMIT."
3454 (if (ada-search-ignore-string-comment ada-end-stmt-re nil limit)
3455 (point)
3456 nil))
3457
3458
3459 (defun ada-goto-next-word (&optional backward)
3460 "Move point to the beginning of the next word of Ada code.
3461 If BACKWARD is non-nil, jump to the beginning of the previous word.
3462 Return the new position of point or nil if not found."
3463 (let ((match-cons nil)
3464 (orgpoint (point))
3465 (old-syntax (char-to-string (char-syntax ?_))))
3466 (modify-syntax-entry ?_ "w")
3467 (unless backward
3468 (skip-syntax-forward "w"))
3469 (if (setq match-cons
3470 (ada-search-ignore-string-comment "\\w" backward nil t))
3471 ;;
3472 ;; move to the beginning of the word found
3473 ;;
3474 (progn
3475 (goto-char (car match-cons))
3476 (skip-syntax-backward "w")
3477 (point))
3478 ;;
3479 ;; if not found, restore old position of point
3480 ;;
3481 (goto-char orgpoint)
3482 'nil)
3483 (modify-syntax-entry ?_ old-syntax))
3484 )
3485
3486
3487 (defun ada-check-matching-start (keyword)
3488 "Signal an error if matching block start is not KEYWORD.
3489 Moves point to the matching block start."
3490 (ada-goto-matching-start 0)
3491 (unless (looking-at (concat "\\<" keyword "\\>"))
3492 (error "Matching start is not '%s'" keyword)))
3493
3494
3495 (defun ada-check-defun-name (defun-name)
3496 "Check if the name of the matching defun really is DEFUN-NAME.
3497 Assumes point to be already positioned by `ada-goto-matching-start'.
3498 Moves point to the beginning of the declaration."
3499
3500 ;; named block without a `declare'; ada-goto-matching-start leaves
3501 ;; point at start of 'begin' for a block.
3502 (if (save-excursion
3503 (ada-goto-previous-word)
3504 (looking-at (concat "\\<" defun-name "\\> *:")))
3505 t ; do nothing
3506 ;; else
3507 ;;
3508 ;; 'accept' or 'package' ?
3509 ;;
3510 (unless (looking-at ada-subprog-start-re)
3511 (ada-goto-matching-decl-start))
3512 ;;
3513 ;; 'begin' of 'procedure'/'function'/'task' or 'declare'
3514 ;;
3515 (save-excursion
3516 ;;
3517 ;; a named 'declare'-block ? => jump to the label
3518 ;;
3519 (if (looking-at "\\<declare\\>")
3520 (progn
3521 (forward-comment -1)
3522 (backward-word 1))
3523 ;;
3524 ;; no, => 'procedure'/'function'/'task'/'protected'
3525 ;;
3526 (progn
3527 (forward-word 2)
3528 (backward-word 1)
3529 ;;
3530 ;; skip 'body' 'type'
3531 ;;
3532 (if (looking-at "\\<\\(body\\|type\\)\\>")
3533 (forward-word 1))
3534 (forward-sexp 1)
3535 (backward-sexp 1)))
3536 ;;
3537 ;; should be looking-at the correct name
3538 ;;
3539 (unless (looking-at (concat "\\<" defun-name "\\>"))
3540 (error "Matching defun has different name: %s"
3541 (buffer-substring (point)
3542 (progn (forward-sexp 1) (point))))))))
3543
3544 (defun ada-goto-matching-decl-start (&optional noerror recursive)
3545 "Move point to the matching declaration start of the current 'begin'.
3546 If NOERROR is non-nil, it only returns nil if no match was found."
3547 (let ((nest-count 1)
3548
3549 ;; first should be set to t if we should stop at the first
3550 ;; "begin" we encounter.
3551 (first (not recursive))
3552 (count-generic nil)
3553 (stop-at-when nil)
3554 )
3555
3556 ;; Ignore "when" most of the time, except if we are looking at the
3557 ;; beginning of a block (structure: case .. is
3558 ;; when ... =>
3559 ;; begin ...
3560 ;; exception ... )
3561 (if (looking-at "begin")
3562 (setq stop-at-when t))
3563
3564 (if (or
3565 (looking-at "\\<\\(package\\|procedure\\|function\\)\\>")
3566 (save-excursion
3567 (ada-search-ignore-string-comment
3568 "\\<\\(package\\|procedure\\|function\\|generic\\)\\>" t)
3569 (looking-at "generic")))
3570 (setq count-generic t))
3571
3572 ;; search backward for interesting keywords
3573 (while (and
3574 (not (zerop nest-count))
3575 (ada-search-ignore-string-comment ada-matching-decl-start-re t))
3576 ;;
3577 ;; calculate nest-depth
3578 ;;
3579 (cond
3580 ;;
3581 ((looking-at "end")
3582 (ada-goto-matching-start 1 noerror)
3583
3584 ;; In some case, two begin..end block can follow each other closely,
3585 ;; which we have to detect, as in
3586 ;; procedure P is
3587 ;; procedure Q is
3588 ;; begin
3589 ;; end;
3590 ;; begin -- here we should go to procedure, not begin
3591 ;; end
3592
3593 (if (looking-at "begin")
3594 (let ((loop-again t))
3595 (save-excursion
3596 (while loop-again
3597 ;; If begin was just there as the beginning of a block
3598 ;; (with no declare) then do nothing, otherwise just
3599 ;; register that we have to find the statement that
3600 ;; required the begin
3601
3602 (ada-search-ignore-string-comment
3603 "\\<\\(declare\\|begin\\|end\\|procedure\\|function\\|task\\|package\\)\\>"
3604 t)
3605
3606 (if (looking-at "end")
3607 (ada-goto-matching-start 1 noerror t)
3608 ;; (ada-goto-matching-decl-start noerror t)
3609
3610 (setq loop-again nil)
3611 (unless (looking-at "begin")
3612 (setq nest-count (1+ nest-count))))
3613 ))
3614 )))
3615 ;;
3616 ((looking-at "generic")
3617 (if count-generic
3618 (progn
3619 (setq first nil)
3620 (setq nest-count (1- nest-count)))))
3621 ;;
3622 ((looking-at "if")
3623 (save-excursion
3624 (forward-word -1)
3625 (unless (looking-at "\\<end[ \t\n]*if\\>")
3626 (progn
3627 (setq nest-count (1- nest-count))
3628 (setq first nil)))))
3629
3630 ;;
3631 ((looking-at "declare\\|generic")
3632 (setq nest-count (1- nest-count))
3633 (setq first t))
3634 ;;
3635 ((looking-at "is")
3636 ;; check if it is only a type definition, but not a protected
3637 ;; type definition, which should be handled like a procedure.
3638 (if (or (looking-at "is[ \t]+<>")
3639 (save-excursion
3640 (forward-comment -10000)
3641 (forward-char -1)
3642
3643 ;; Detect if we have a closing parenthesis (Could be
3644 ;; either the end of subprogram parameters or (<>)
3645 ;; in a type definition
3646 (if (= (char-after) ?\))
3647 (progn
3648 (forward-char 1)
3649 (backward-sexp 1)
3650 (forward-comment -10000)
3651 ))
3652 (skip-chars-backward "a-zA-Z0-9_.'")
3653 (ada-goto-previous-word)
3654 (and
3655 (looking-at "\\<\\(sub\\)?type\\|case\\>")
3656 (save-match-data
3657 (ada-goto-previous-word)
3658 (not (looking-at "\\<protected\\>"))))
3659 )) ; end of `or'
3660 (goto-char (match-beginning 0))
3661 (progn
3662 (setq nest-count (1- nest-count))
3663 (setq first nil))))
3664
3665 ;;
3666 ((looking-at "new")
3667 (if (save-excursion
3668 (ada-goto-previous-word)
3669 (looking-at "is"))
3670 (goto-char (match-beginning 0))))
3671 ;;
3672 ((and first
3673 (looking-at "begin"))
3674 (setq nest-count 0))
3675 ;;
3676 ((looking-at "when")
3677 (save-excursion
3678 (forward-word -1)
3679 (unless (looking-at "\\<exit[ \t\n]*when\\>")
3680 (progn
3681 (if stop-at-when
3682 (setq nest-count (1- nest-count)))
3683 ))))
3684 ;;
3685 ((looking-at "begin")
3686 (setq first nil))
3687 ;;
3688 (t
3689 (setq nest-count (1+ nest-count))
3690 (setq first nil)))
3691
3692 );; end of loop
3693
3694 ;; check if declaration-start is really found
3695 (if (and
3696 (zerop nest-count)
3697 (if (looking-at "is")
3698 (ada-search-ignore-string-comment ada-subprog-start-re t)
3699 (looking-at "declare\\|generic")))
3700 t
3701 (if noerror nil
3702 (error "No matching proc/func/task/declare/package/protected")))
3703 ))
3704
3705 (defun ada-goto-matching-start (&optional nest-level noerror gotothen)
3706 "Move point to the beginning of a block-start.
3707 Which block depends on the value of NEST-LEVEL, which defaults to zero.
3708 If NOERROR is non-nil, it only returns nil if no matching start was found.
3709 If GOTOTHEN is non-nil, point moves to the 'then' following 'if'."
3710 (let ((nest-count (if nest-level nest-level 0))
3711 (found nil)
3712
3713 (last-was-begin '())
3714 ;; List all keywords encountered while traversing
3715 ;; something like '("end" "end" "begin")
3716 ;; This is removed from the list when "package", "procedure",...
3717 ;; are seen. The goal is to find whether a package has an elaboration
3718 ;; part
3719
3720 (pos nil))
3721
3722 ;; search backward for interesting keywords
3723 (while (and
3724 (not found)
3725 (ada-search-ignore-string-comment ada-matching-start-re t))
3726
3727 (unless (and (looking-at "\\<record\\>")
3728 (save-excursion
3729 (forward-word -1)
3730 (looking-at "\\<null\\>")))
3731 (progn
3732 ;; calculate nest-depth
3733 (cond
3734 ;; found block end => increase nest depth
3735 ((looking-at "end")
3736 (push nil last-was-begin)
3737 (setq nest-count (1+ nest-count)))
3738
3739 ;; found loop/select/record/case/if => check if it starts or
3740 ;; ends a block
3741 ((looking-at "loop\\|select\\|record\\|case\\|if")
3742 (setq pos (point))
3743 (save-excursion
3744 ;; check if keyword follows 'end'
3745 (ada-goto-previous-word)
3746 (if (looking-at "\\<end\\>[ \t]*[^;]")
3747 (progn
3748 ;; it ends a block => increase nest depth
3749 (setq nest-count (1+ nest-count)
3750 pos (point))
3751 (push nil last-was-begin))
3752
3753 ;; it starts a block => decrease nest depth
3754 (setq nest-count (1- nest-count))
3755
3756 ;; Some nested "begin .. end" blocks with no "declare"?
3757 ;; => remove those entries
3758 (while (car last-was-begin)
3759 (setq last-was-begin (cdr (cdr last-was-begin))))
3760
3761 (setq last-was-begin (cdr last-was-begin))
3762 ))
3763 (goto-char pos)
3764 )
3765
3766 ;; found package start => check if it really is a block
3767 ((looking-at "package")
3768 (save-excursion
3769 ;; ignore if this is just a renames statement
3770 (let ((current (point))
3771 (pos (ada-search-ignore-string-comment
3772 "\\<\\(is\\|renames\\|;\\)\\>" nil)))
3773 (if pos
3774 (goto-char (car pos))
3775 (error (concat
3776 "No matching 'is' or 'renames' for 'package' at"
3777 " line "
3778 (number-to-string (count-lines 1 (1+ current)))))))
3779 (unless (looking-at "renames")
3780 (progn
3781 (forward-word 1)
3782 (ada-goto-next-non-ws)
3783 ;; ignore it if it is only a declaration with 'new'
3784 ;; We could have package Foo is new ....
3785 ;; or package Foo is separate;
3786 ;; or package Foo is begin null; end Foo
3787 ;; for elaboration code (elaboration)
3788 (if (and (not (looking-at "\\<\\(new\\|separate\\|begin\\)\\>"))
3789 (not (car last-was-begin)))
3790 (setq nest-count (1- nest-count))))))
3791
3792 (setq last-was-begin (cdr last-was-begin))
3793 )
3794 ;; found task start => check if it has a body
3795 ((looking-at "task")
3796 (save-excursion
3797 (forward-word 1)
3798 (ada-goto-next-non-ws)
3799 (cond
3800 ((looking-at "\\<body\\>"))
3801 ((looking-at "\\<type\\>")
3802 ;; In that case, do nothing if there is a "is"
3803 (forward-word 2);; skip "type"
3804 (ada-goto-next-non-ws);; skip type name
3805
3806 ;; Do nothing if we are simply looking at a simple
3807 ;; "task type name;" statement with no block
3808 (unless (looking-at ";")
3809 (progn
3810 ;; Skip the parameters
3811 (if (looking-at "(")
3812 (ada-search-ignore-string-comment ")" nil))
3813 (let ((tmp (ada-search-ignore-string-comment
3814 "\\<\\(is\\|;\\)\\>" nil)))
3815 (if tmp
3816 (progn
3817 (goto-char (car tmp))
3818 (if (looking-at "is")
3819 (setq nest-count (1- nest-count)))))))))
3820 (t
3821 ;; Check if that task declaration had a block attached to
3822 ;; it (i.e do nothing if we have just "task name;")
3823 (unless (progn (forward-word 1)
3824 (looking-at "[ \t]*;"))
3825 (setq nest-count (1- nest-count))))))
3826 (setq last-was-begin (cdr last-was-begin))
3827 )
3828
3829 ((looking-at "declare")
3830 ;; remove entry for begin and end (include nested begin..end
3831 ;; groups)
3832 (setq last-was-begin (cdr last-was-begin))
3833 (let ((count 1))
3834 (while (and (> count 0))
3835 (if (equal (car last-was-begin) t)
3836 (setq count (1+ count))
3837 (setq count (1- count)))
3838 (setq last-was-begin (cdr last-was-begin))
3839 )))
3840
3841 ((looking-at "protected")
3842 ;; Ignore if this is just a declaration
3843 (save-excursion
3844 (let ((pos (ada-search-ignore-string-comment
3845 "\\(\\<is\\>\\|\\<renames\\>\\|;\\)" nil)))
3846 (if pos
3847 (goto-char (car pos)))
3848 (if (looking-at "is")
3849 ;; remove entry for end
3850 (setq last-was-begin (cdr last-was-begin)))))
3851 (setq nest-count (1- nest-count)))
3852
3853 ((or (looking-at "procedure")
3854 (looking-at "function"))
3855 ;; Ignore if this is just a declaration
3856 (save-excursion
3857 (let ((pos (ada-search-ignore-string-comment
3858 "\\(\\<is\\>\\|\\<renames\\>\\|)[ \t]*;\\)" nil)))
3859 (if pos
3860 (goto-char (car pos)))
3861 (if (looking-at "is")
3862 ;; remove entry for begin and end
3863 (setq last-was-begin (cdr (cdr last-was-begin))))))
3864 )
3865
3866 ;; all the other block starts
3867 (t
3868 (push (looking-at "begin") last-was-begin)
3869 (setq nest-count (1- nest-count)))
3870
3871 )
3872
3873 ;; match is found, if nest-depth is zero
3874 (setq found (zerop nest-count))))) ; end of loop
3875
3876 (if (bobp)
3877 (point)
3878 (if found
3879 ;;
3880 ;; match found => is there anything else to do ?
3881 ;;
3882 (progn
3883 (cond
3884 ;;
3885 ;; found 'if' => skip to 'then', if it's on a separate line
3886 ;; and GOTOTHEN is non-nil
3887 ;;
3888 ((and
3889 gotothen
3890 (looking-at "if")
3891 (save-excursion
3892 (ada-search-ignore-string-comment "then" nil nil nil
3893 'word-search-forward)
3894 (back-to-indentation)
3895 (looking-at "\\<then\\>")))
3896 (goto-char (match-beginning 0)))
3897
3898 ;;
3899 ;; found 'do' => skip back to 'accept' or 'return'
3900 ;;
3901 ((looking-at "do")
3902 (unless (ada-search-ignore-string-comment
3903 "\\<accept\\|return\\>" t)
3904 (error "Missing 'accept' or 'return' in front of 'do'"))))
3905 (point))
3906
3907 (if noerror
3908 nil
3909 (error "No matching start"))))))
3910
3911
3912 (defun ada-goto-matching-end (&optional nest-level noerror)
3913 "Move point to the end of a block.
3914 Which block depends on the value of NEST-LEVEL, which defaults to zero.
3915 If NOERROR is non-nil, it only returns nil if no matching start found."
3916 (let ((nest-count (or nest-level 0))
3917 (regex (eval-when-compile
3918 (concat "\\<"
3919 (regexp-opt '("end" "loop" "select" "begin" "case"
3920 "if" "task" "package" "record" "do"
3921 "procedure" "function") t)
3922 "\\>")))
3923 found
3924 pos
3925
3926 ;; First is used for subprograms: they are generally handled
3927 ;; recursively, but of course we do not want to do that the
3928 ;; first time (see comment below about subprograms)
3929 (first (not (looking-at "declare"))))
3930
3931 ;; If we are already looking at one of the keywords, this shouldn't count
3932 ;; in the nesting loop below, so we just make sure we don't count it.
3933 ;; "declare" is a special case because we need to look after the "begin"
3934 ;; keyword
3935 (if (looking-at "\\<if\\|loop\\|case\\|begin\\>")
3936 (forward-char 1))
3937
3938 ;;
3939 ;; search forward for interesting keywords
3940 ;;
3941 (while (and
3942 (not found)
3943 (ada-search-ignore-string-comment regex nil))
3944
3945 ;;
3946 ;; calculate nest-depth
3947 ;;
3948 (backward-word 1)
3949 (cond
3950 ;; procedures and functions need to be processed recursively, in
3951 ;; case they are defined in a declare/begin block, as in:
3952 ;; declare -- NL 0 (nested level)
3953 ;; A : Boolean;
3954 ;; procedure B (C : D) is
3955 ;; begin -- NL 1
3956 ;; null;
3957 ;; end B; -- NL 0, and we would exit
3958 ;; begin
3959 ;; end; -- we should exit here
3960 ;; processing them recursively avoids the need for any special
3961 ;; handling.
3962 ;; Nothing should be done if we have only the specs or a
3963 ;; generic instantion.
3964
3965 ((and (looking-at "\\<procedure\\|function\\>"))
3966 (if first
3967 (forward-word 1)
3968
3969 (setq pos (point))
3970 (ada-search-ignore-string-comment "is\\|;")
3971 (if (= (char-before) ?s)
3972 (progn
3973 (ada-goto-next-non-ws)
3974 (unless (looking-at "\\<new\\>")
3975 (progn
3976 (goto-char pos)
3977 (ada-goto-matching-end 0 t)))))))
3978
3979 ;; found block end => decrease nest depth
3980 ((looking-at "\\<end\\>")
3981 (setq nest-count (1- nest-count)
3982 found (<= nest-count 0))
3983 ;; skip the following keyword
3984 (if (progn
3985 (skip-chars-forward "end")
3986 (ada-goto-next-non-ws)
3987 (looking-at "\\<\\(loop\\|select\\|record\\|case\\|if\\)\\>"))
3988 (forward-word 1)))
3989
3990 ;; found package start => check if it really starts a block, and is not
3991 ;; in fact a generic instantiation for instance
3992 ((looking-at "\\<package\\>")
3993 (ada-search-ignore-string-comment "is" nil nil nil
3994 'word-search-forward)
3995 (ada-goto-next-non-ws)
3996 ;; ignore and skip it if it is only a 'new' package
3997 (if (looking-at "\\<new\\>")
3998 (goto-char (match-end 0))
3999 (setq nest-count (1+ nest-count)
4000 found (<= nest-count 0))))
4001
4002 ;; all the other block starts
4003 (t
4004 (if (not first)
4005 (setq nest-count (1+ nest-count)))
4006 (setq found (<= nest-count 0))
4007 (forward-word 1))) ; end of 'cond'
4008
4009 (setq first nil))
4010
4011 (if found
4012 t
4013 (if noerror
4014 nil
4015 (error "No matching end")))
4016 ))
4017
4018
4019 (defun ada-search-ignore-string-comment
4020 (search-re &optional backward limit paramlists search-func)
4021 "Regexp-search for SEARCH-RE, ignoring comments, strings.
4022 Returns a cons cell of begin and end of match data or nil, if not found.
4023 If BACKWARD is non-nil, search backward; search forward otherwise.
4024 The search stops at pos LIMIT.
4025 If PARAMLISTS is nil, ignore parameter lists.
4026 The search is done using SEARCH-FUNC. SEARCH-FUNC can be optimized
4027 in case we are searching for a constant string.
4028 Point is moved at the beginning of the SEARCH-RE."
4029 (let (found
4030 begin
4031 end
4032 parse-result
4033 (previous-syntax-table (syntax-table)))
4034
4035 ;; FIXME: need to pass BACKWARD to search-func!
4036 (unless search-func
4037 (setq search-func (if backward 're-search-backward 're-search-forward)))
4038
4039 ;;
4040 ;; search until found or end-of-buffer
4041 ;; We have to test that we do not look further than limit
4042 ;;
4043 (set-syntax-table ada-mode-symbol-syntax-table)
4044 (while (and (not found)
4045 (or (not limit)
4046 (or (and backward (<= limit (point)))
4047 (>= limit (point))))
4048 (funcall search-func search-re limit 1))
4049 (setq begin (match-beginning 0))
4050 (setq end (match-end 0))
4051
4052 (setq parse-result (parse-partial-sexp
4053 (save-excursion (beginning-of-line) (point))
4054 (point)))
4055
4056 (cond
4057 ;;
4058 ;; If inside a string, skip it (and the following comments)
4059 ;;
4060 ((ada-in-string-p parse-result)
4061 (if (featurep 'xemacs)
4062 (search-backward "\"" nil t)
4063 (goto-char (nth 8 parse-result)))
4064 (unless backward (forward-sexp 1)))
4065 ;;
4066 ;; If inside a comment, skip it (and the following comments)
4067 ;; There is a special code for comments at the end of the file
4068 ;;
4069 ((ada-in-comment-p parse-result)
4070 (if (featurep 'xemacs)
4071 (progn
4072 (forward-line 1)
4073 (beginning-of-line)
4074 (forward-comment -1))
4075 (goto-char (nth 8 parse-result)))
4076 (unless backward
4077 ;; at the end of the file, it is not possible to skip a comment
4078 ;; so we just go at the end of the line
4079 (if (forward-comment 1)
4080 (progn
4081 (forward-comment 1000)
4082 (beginning-of-line))
4083 (end-of-line))))
4084 ;;
4085 ;; directly in front of a comment => skip it, if searching forward
4086 ;;
4087 ((and (= (char-after begin) ?-) (= (char-after (1+ begin)) ?-))
4088 (unless backward (progn (forward-char -1) (forward-comment 1000))))
4089
4090 ;;
4091 ;; found a parameter-list but should ignore it => skip it
4092 ;;
4093 ((and (not paramlists) (ada-in-paramlist-p))
4094 (if backward
4095 (search-backward "(" nil t)
4096 (search-forward ")" nil t)))
4097 ;;
4098 ;; found what we were looking for
4099 ;;
4100 (t
4101 (setq found t)))) ; end of loop
4102
4103 (set-syntax-table previous-syntax-table)
4104
4105 (if found
4106 (cons begin end)
4107 nil)))
4108
4109 ;; -------------------------------------------------------
4110 ;; -- Testing the position of the cursor
4111 ;; -------------------------------------------------------
4112
4113 (defun ada-in-decl-p ()
4114 "Return t if point is inside a declarative part.
4115 Assumes point to be at the end of a statement."
4116 (or (ada-in-paramlist-p)
4117 (save-excursion
4118 (ada-goto-matching-decl-start t))))
4119
4120
4121 (defun ada-looking-at-semi-or ()
4122 "Return t if looking at an 'or' following a semicolon."
4123 (save-excursion
4124 (and (looking-at "\\<or\\>")
4125 (progn
4126 (forward-word 1)
4127 (ada-goto-stmt-start)
4128 (looking-at "\\<or\\>")))))
4129
4130
4131 (defun ada-looking-at-semi-private ()
4132 "Return t if looking at the start of a private section in a package.
4133 Return nil if the private is part of the package name, as in
4134 'private package A is...' (this can only happen at top level)."
4135 (save-excursion
4136 (and (looking-at "\\<private\\>")
4137 (not (looking-at "\\<private[ \t]*\\(package\\|generic\\)"))
4138
4139 ;; Make sure this is the start of a private section (ie after
4140 ;; a semicolon or just after the package declaration, but not
4141 ;; after a 'type ... is private' or 'is new ... with private'
4142 ;;
4143 ;; Note that a 'private' statement at the beginning of the buffer
4144 ;; does not indicate a private section, since this is instead a
4145 ;; 'private procedure ...'
4146 (progn (forward-comment -1000)
4147 (and (not (bobp))
4148 (or (= (char-before) ?\;)
4149 (and (forward-word -3)
4150 (looking-at "\\<package\\>"))))))))
4151
4152
4153 (defun ada-in-paramlist-p ()
4154 "Return t if point is inside a parameter-list."
4155 (save-excursion
4156 (and
4157 (ada-search-ignore-string-comment "(\\|)" t nil t)
4158 ;; inside parentheses ?
4159 (= (char-after) ?\()
4160
4161 ;; We could be looking at two things here:
4162 ;; operator definition: function "." (
4163 ;; subprogram definition: procedure .... (
4164 ;; Let's skip back over the first one
4165 (progn
4166 (skip-chars-backward " \t\n")
4167 (if (= (char-before) ?\")
4168 (backward-char 3)
4169 (backward-word 1))
4170 t)
4171
4172 ;; and now over the second one
4173 (backward-word 1)
4174
4175 ;; We should ignore the case when the reserved keyword is in a
4176 ;; comment (for instance, when we have:
4177 ;; -- .... package
4178 ;; Test (A)
4179 ;; we should return nil
4180
4181 (not (ada-in-string-or-comment-p))
4182
4183 ;; right keyword two words before parenthesis ?
4184 ;; Type is in this list because of discriminants
4185 (looking-at (eval-when-compile
4186 (concat "\\<\\("
4187 "procedure\\|function\\|body\\|"
4188 "task\\|entry\\|accept\\|"
4189 "access[ \t]+procedure\\|"
4190 "access[ \t]+function\\|"
4191 "pragma\\|"
4192 "type\\)\\>"))))))
4193
4194 (defun ada-search-ignore-complex-boolean (regexp backwardp)
4195 "Search for REGEXP, ignoring comments, strings, 'and then', 'or else'.
4196 If BACKWARDP is non-nil, search backward; search forward otherwise."
4197 (let (result)
4198 (while (and (setq result (ada-search-ignore-string-comment regexp backwardp))
4199 (save-excursion (forward-word -1)
4200 (looking-at "and then\\|or else"))))
4201 result))
4202
4203 (defun ada-in-open-paren-p ()
4204 "Non-nil if in an open parenthesis.
4205 Return value is the position of the first non-ws behind the last unclosed
4206 parenthesis, or nil."
4207 (save-excursion
4208 (let ((parse (parse-partial-sexp
4209 (point)
4210 (or (car (ada-search-ignore-complex-boolean
4211 "\\<\\(;\\|is\\|then\\|loop\\|begin\\|else\\)\\>"
4212 t))
4213 (point-min)))))
4214
4215 (if (nth 1 parse)
4216 (progn
4217 (goto-char (1+ (nth 1 parse)))
4218
4219 ;; Skip blanks, if they are not followed by a comment
4220 ;; See:
4221 ;; type A is ( Value_0,
4222 ;; Value_1);
4223 ;; type B is ( -- comment
4224 ;; Value_2);
4225
4226 (if (or (not ada-indent-handle-comment-special)
4227 (not (looking-at "[ \t]+--")))
4228 (skip-chars-forward " \t"))
4229
4230 (point))))))
4231
4232 \f
4233 ;; -----------------------------------------------------------
4234 ;; -- Behavior Of TAB Key
4235 ;; -----------------------------------------------------------
4236
4237 (defun ada-tab ()
4238 "Do indenting or tabbing according to `ada-tab-policy'.
4239 In Transient Mark mode, if the mark is active, operate on the contents
4240 of the region. Otherwise, operate only on the current line."
4241 (interactive)
4242 (cond ((eq ada-tab-policy 'indent-rigidly) (ada-tab-hard))
4243 ((eq ada-tab-policy 'indent-auto)
4244 (if (ada-region-selected)
4245 (ada-indent-region (region-beginning) (region-end))
4246 (ada-indent-current)))
4247 ((eq ada-tab-policy 'always-tab) (error "Not implemented"))
4248 ))
4249
4250 (defun ada-untab (arg)
4251 "Delete leading indenting according to `ada-tab-policy'."
4252 ;; FIXME: ARG is ignored
4253 (interactive "P")
4254 (cond ((eq ada-tab-policy 'indent-rigidly) (ada-untab-hard))
4255 ((eq ada-tab-policy 'indent-auto) (error "Not implemented"))
4256 ((eq ada-tab-policy 'always-tab) (error "Not implemented"))
4257 ))
4258
4259 (defun ada-indent-current-function ()
4260 "Ada mode version of the `indent-line-function'."
4261 (interactive "*")
4262 (let ((starting-point (point-marker)))
4263 (beginning-of-line)
4264 (ada-tab)
4265 (if (< (point) starting-point)
4266 (goto-char starting-point))
4267 (set-marker starting-point nil)
4268 ))
4269
4270 (defun ada-tab-hard ()
4271 "Indent current line to next tab stop."
4272 (interactive)
4273 (save-excursion
4274 (beginning-of-line)
4275 (insert-char ? ada-indent))
4276 (if (save-excursion (= (point) (progn (beginning-of-line) (point))))
4277 (forward-char ada-indent)))
4278
4279 (defun ada-untab-hard ()
4280 "Indent current line to previous tab stop."
4281 (interactive)
4282 (let ((bol (save-excursion (progn (beginning-of-line) (point))))
4283 (eol (save-excursion (progn (end-of-line) (point)))))
4284 (indent-rigidly bol eol (- 0 ada-indent))))
4285
4286
4287 \f
4288 ;; ------------------------------------------------------------
4289 ;; -- Miscellaneous
4290 ;; ------------------------------------------------------------
4291
4292 ;; Not needed any more for Emacs 21.2, but still needed for backward
4293 ;; compatibility
4294 (defun ada-remove-trailing-spaces ()
4295 "Remove trailing spaces in the whole buffer."
4296 (interactive)
4297 (save-match-data
4298 (save-excursion
4299 (save-restriction
4300 (widen)
4301 (goto-char (point-min))
4302 (while (re-search-forward "[ \t]+$" (point-max) t)
4303 (replace-match "" nil nil))))))
4304
4305 (defun ada-gnat-style ()
4306 "Clean up comments, `(' and `,' for GNAT style checking switch."
4307 (interactive)
4308 (save-excursion
4309
4310 ;; The \n is required, or the line after an empty comment line is
4311 ;; simply ignored.
4312 (goto-char (point-min))
4313 (while (re-search-forward "--[ \t]*\\([^-\n]\\)" nil t)
4314 (replace-match "-- \\1")
4315 (forward-line 1)
4316 (beginning-of-line))
4317
4318 (goto-char (point-min))
4319 (while (re-search-forward "\\>(" nil t)
4320 (if (not (ada-in-string-or-comment-p))
4321 (replace-match " (")))
4322 (goto-char (point-min))
4323 (while (re-search-forward ";--" nil t)
4324 (forward-char -1)
4325 (if (not (ada-in-string-or-comment-p))
4326 (replace-match "; --")))
4327 (goto-char (point-min))
4328 (while (re-search-forward "([ \t]+" nil t)
4329 (if (not (ada-in-string-or-comment-p))
4330 (replace-match "(")))
4331 (goto-char (point-min))
4332 (while (re-search-forward ")[ \t]+)" nil t)
4333 (if (not (ada-in-string-or-comment-p))
4334 (replace-match "))")))
4335 (goto-char (point-min))
4336 (while (re-search-forward "\\>:" nil t)
4337 (if (not (ada-in-string-or-comment-p))
4338 (replace-match " :")))
4339
4340 ;; Make sure there is a space after a ','.
4341 ;; Always go back to the beginning of the match, since otherwise
4342 ;; a statement like ('F','D','E') is incorrectly modified.
4343 (goto-char (point-min))
4344 (while (re-search-forward ",[ \t]*\\(.\\)" nil t)
4345 (if (not (save-excursion
4346 (goto-char (match-beginning 0))
4347 (ada-in-string-or-comment-p)))
4348 (replace-match ", \\1")))
4349
4350 ;; Operators should be surrounded by spaces.
4351 (goto-char (point-min))
4352 (while (re-search-forward
4353 "[ \t]*\\(/=\\|\\*\\*\\|:=\\|\\.\\.\\|[-:+*/]\\)[ \t]*"
4354 nil t)
4355 (goto-char (match-beginning 1))
4356 (if (or (looking-at "--")
4357 (ada-in-string-or-comment-p))
4358 (progn
4359 (forward-line 1)
4360 (beginning-of-line))
4361 (cond
4362 ((string= (match-string 1) "/=")
4363 (replace-match " /= "))
4364 ((string= (match-string 1) "..")
4365 (replace-match " .. "))
4366 ((string= (match-string 1) "**")
4367 (replace-match " ** "))
4368 ((string= (match-string 1) ":=")
4369 (replace-match " := "))
4370 (t
4371 (replace-match " \\1 ")))
4372 (forward-char 1)))
4373 ))
4374
4375
4376 \f
4377 ;; -------------------------------------------------------------
4378 ;; -- Moving To Procedures/Packages/Statements
4379 ;; -------------------------------------------------------------
4380
4381 (defun ada-move-to-start ()
4382 "Move point to the matching start of the current Ada structure."
4383 (interactive)
4384 (let ((pos (point))
4385 (previous-syntax-table (syntax-table)))
4386 (unwind-protect
4387 (progn
4388 (set-syntax-table ada-mode-symbol-syntax-table)
4389
4390 (save-excursion
4391 ;;
4392 ;; do nothing if in string or comment or not on 'end ...;'
4393 ;; or if an error occurs during processing
4394 ;;
4395 (or
4396 (ada-in-string-or-comment-p)
4397 (and (progn
4398 (or (looking-at "[ \t]*\\<end\\>")
4399 (backward-word 1))
4400 (or (looking-at "[ \t]*\\<end\\>")
4401 (backward-word 1))
4402 (or (looking-at "[ \t]*\\<end\\>")
4403 (error "Not on end ...;")))
4404 (ada-goto-matching-start 1)
4405 (setq pos (point))
4406
4407 ;;
4408 ;; on 'begin' => go on, according to user option
4409 ;;
4410 ada-move-to-declaration
4411 (looking-at "\\<begin\\>")
4412 (ada-goto-matching-decl-start)
4413 (setq pos (point))))
4414
4415 ) ; end of save-excursion
4416
4417 ;; now really move to the found position
4418 (goto-char pos))
4419
4420 ;; restore syntax-table
4421 (set-syntax-table previous-syntax-table))))
4422
4423 (defun ada-move-to-end ()
4424 "Move point to the matching end of the block around point.
4425 Moves to 'begin' if in a declarative part."
4426 (interactive)
4427 (let ((pos (point))
4428 decl-start
4429 (previous-syntax-table (syntax-table)))
4430 (unwind-protect
4431 (progn
4432 (set-syntax-table ada-mode-symbol-syntax-table)
4433
4434 (save-excursion
4435
4436 (cond
4437 ;; Go to the beginning of the current word, and check if we are
4438 ;; directly on 'begin'
4439 ((save-excursion
4440 (skip-syntax-backward "w")
4441 (looking-at "\\<begin\\>"))
4442 (ada-goto-matching-end 1)
4443 )
4444
4445 ;; on first line of subprogram body
4446 ;; Do nothing for specs or generic instantion, since these are
4447 ;; handled as the general case (find the enclosing block)
4448 ;; We also need to make sure that we ignore nested subprograms
4449 ((save-excursion
4450 (and (skip-syntax-backward "w")
4451 (looking-at "\\<function\\>\\|\\<procedure\\>" )
4452 (ada-search-ignore-string-comment "is\\|;")
4453 (not (= (char-before) ?\;))
4454 ))
4455 (skip-syntax-backward "w")
4456 (ada-goto-matching-end 0 t))
4457
4458 ;; on first line of task declaration
4459 ((save-excursion
4460 (and (ada-goto-stmt-start)
4461 (looking-at "\\<task\\>" )
4462 (forward-word 1)
4463 (ada-goto-next-non-ws)
4464 (looking-at "\\<body\\>")))
4465 (ada-search-ignore-string-comment "begin" nil nil nil
4466 'word-search-forward))
4467 ;; accept block start
4468 ((save-excursion
4469 (and (ada-goto-stmt-start)
4470 (looking-at "\\<accept\\>" )))
4471 (ada-goto-matching-end 0))
4472 ;; package start
4473 ((save-excursion
4474 (setq decl-start (and (ada-goto-matching-decl-start t) (point)))
4475 (and decl-start (looking-at "\\<package\\>")))
4476 (ada-goto-matching-end 1))
4477
4478 ;; On a "declare" keyword
4479 ((save-excursion
4480 (skip-syntax-backward "w")
4481 (looking-at "\\<declare\\>"))
4482 (ada-goto-matching-end 0 t))
4483
4484 ;; inside a 'begin' ... 'end' block
4485 (decl-start
4486 (goto-char decl-start)
4487 (ada-goto-matching-end 0 t))
4488
4489 ;; (hopefully ;-) everything else
4490 (t
4491 (ada-goto-matching-end 1)))
4492 (setq pos (point))
4493 )
4494
4495 ;; now really move to the position found
4496 (goto-char pos))
4497
4498 ;; restore syntax-table
4499 (set-syntax-table previous-syntax-table))))
4500
4501 (defun ada-next-procedure ()
4502 "Move point to next procedure."
4503 (interactive)
4504 (end-of-line)
4505 (if (re-search-forward ada-procedure-start-regexp nil t)
4506 (goto-char (match-beginning 4))
4507 (error "No more functions/procedures/tasks")))
4508
4509 (defun ada-previous-procedure ()
4510 "Move point to previous procedure."
4511 (interactive)
4512 (beginning-of-line)
4513 (if (re-search-backward ada-procedure-start-regexp nil t)
4514 (goto-char (match-beginning 4))
4515 (error "No more functions/procedures/tasks")))
4516
4517 (defun ada-next-package ()
4518 "Move point to next package."
4519 (interactive)
4520 (end-of-line)
4521 (if (re-search-forward ada-package-start-regexp nil t)
4522 (goto-char (match-beginning 1))
4523 (error "No more packages")))
4524
4525 (defun ada-previous-package ()
4526 "Move point to previous package."
4527 (interactive)
4528 (beginning-of-line)
4529 (if (re-search-backward ada-package-start-regexp nil t)
4530 (goto-char (match-beginning 1))
4531 (error "No more packages")))
4532
4533 \f
4534 ;; ------------------------------------------------------------
4535 ;; -- Define keymap and menus for Ada
4536 ;; -------------------------------------------------------------
4537
4538 (defun ada-create-keymap ()
4539 "Create the keymap associated with the Ada mode."
4540
4541 ;; All non-standard keys go into ada-mode-extra-map
4542 (define-key ada-mode-map ada-mode-extra-prefix ada-mode-extra-map)
4543
4544 ;; Indentation and Formatting
4545 (define-key ada-mode-map "\C-j" 'ada-indent-newline-indent-conditional)
4546 (define-key ada-mode-map "\C-m" 'ada-indent-newline-indent-conditional)
4547 (define-key ada-mode-map "\t" 'ada-tab)
4548 (define-key ada-mode-map "\C-c\t" 'ada-justified-indent-current)
4549 (define-key ada-mode-map "\C-c\C-l" 'ada-indent-region)
4550 (define-key ada-mode-map [(shift tab)] 'ada-untab)
4551 (define-key ada-mode-map "\C-c\C-f" 'ada-format-paramlist)
4552 ;; We don't want to make meta-characters case-specific.
4553
4554 ;; Movement
4555 (define-key ada-mode-map "\M-\C-e" 'ada-next-procedure)
4556 (define-key ada-mode-map "\M-\C-a" 'ada-previous-procedure)
4557 (define-key ada-mode-map "\C-c\C-a" 'ada-move-to-start)
4558 (define-key ada-mode-map "\C-c\C-e" 'ada-move-to-end)
4559
4560 ;; Compilation
4561 (unless (lookup-key ada-mode-map "\C-c\C-c")
4562 (define-key ada-mode-map "\C-c\C-c" 'compile))
4563
4564 ;; Casing
4565 (define-key ada-mode-map "\C-c\C-b" 'ada-adjust-case-buffer)
4566 (define-key ada-mode-map "\C-c\C-t" 'ada-case-read-exceptions)
4567 (define-key ada-mode-map "\C-c\C-y" 'ada-create-case-exception)
4568 (define-key ada-mode-map "\C-c\C-\M-y" 'ada-create-case-exception-substring)
4569
4570 ;; On XEmacs, you can easily specify whether DEL should deletes
4571 ;; one character forward or one character backward. Take this into
4572 ;; account
4573 (define-key ada-mode-map
4574 (if (boundp 'delete-key-deletes-forward) [backspace] "\177")
4575 'backward-delete-char-untabify)
4576
4577 ;; Make body
4578 (define-key ada-mode-map "\C-c\C-n" 'ada-make-subprogram-body)
4579
4580 ;; Use predefined function of Emacs19 for comments (RE)
4581 (define-key ada-mode-map "\C-c;" 'comment-region)
4582 (define-key ada-mode-map "\C-c:" 'ada-uncomment-region)
4583
4584 ;; The following keys are bound to functions defined in ada-xref.el or
4585 ;; ada-prj,el., However, RMS rightly thinks that the code should be shared,
4586 ;; and activated only if the right compiler is used
4587
4588 (define-key ada-mode-map (if (featurep 'xemacs) '(shift button3) [S-mouse-3])
4589 'ada-point-and-xref)
4590 (define-key ada-mode-map [(control tab)] 'ada-complete-identifier)
4591
4592 (define-key ada-mode-extra-map "o" 'ff-find-other-file)
4593 (define-key ada-mode-map "\C-c5\C-d" 'ada-goto-declaration-other-frame)
4594 (define-key ada-mode-map "\C-c\C-d" 'ada-goto-declaration)
4595 (define-key ada-mode-map "\C-c\C-s" 'ada-xref-goto-previous-reference)
4596 (define-key ada-mode-map "\C-c\C-c" 'ada-compile-application)
4597 (define-key ada-mode-extra-map "c" 'ada-change-prj)
4598 (define-key ada-mode-extra-map "d" 'ada-set-default-project-file)
4599 (define-key ada-mode-extra-map "g" 'ada-gdb-application)
4600 (define-key ada-mode-map "\C-c\C-m" 'ada-set-main-compile-application)
4601 (define-key ada-mode-extra-map "r" 'ada-run-application)
4602 (define-key ada-mode-map "\C-c\C-o" 'ada-goto-parent)
4603 (define-key ada-mode-map "\C-c\C-r" 'ada-find-references)
4604 (define-key ada-mode-extra-map "l" 'ada-find-local-references)
4605 (define-key ada-mode-map "\C-c\C-v" 'ada-check-current)
4606 (define-key ada-mode-extra-map "f" 'ada-find-file)
4607
4608 (define-key ada-mode-extra-map "u" 'ada-prj-edit)
4609
4610 (define-key ada-mode-map "\C-xnd" 'ada-narrow-to-defun); override narrow-to-defun
4611
4612 ;; The templates, defined in ada-stmt.el
4613
4614 (let ((map (make-sparse-keymap)))
4615 (define-key map "h" 'ada-header)
4616 (define-key map "\C-a" 'ada-array)
4617 (define-key map "b" 'ada-exception-block)
4618 (define-key map "d" 'ada-declare-block)
4619 (define-key map "c" 'ada-case)
4620 (define-key map "\C-e" 'ada-elsif)
4621 (define-key map "e" 'ada-else)
4622 (define-key map "\C-k" 'ada-package-spec)
4623 (define-key map "k" 'ada-package-body)
4624 (define-key map "\C-p" 'ada-procedure-spec)
4625 (define-key map "p" 'ada-subprogram-body)
4626 (define-key map "\C-f" 'ada-function-spec)
4627 (define-key map "f" 'ada-for-loop)
4628 (define-key map "i" 'ada-if)
4629 (define-key map "l" 'ada-loop)
4630 (define-key map "\C-r" 'ada-record)
4631 (define-key map "\C-s" 'ada-subtype)
4632 (define-key map "S" 'ada-tabsize)
4633 (define-key map "\C-t" 'ada-task-spec)
4634 (define-key map "t" 'ada-task-body)
4635 (define-key map "\C-y" 'ada-type)
4636 (define-key map "\C-v" 'ada-private)
4637 (define-key map "u" 'ada-use)
4638 (define-key map "\C-u" 'ada-with)
4639 (define-key map "\C-w" 'ada-when)
4640 (define-key map "w" 'ada-while-loop)
4641 (define-key map "\C-x" 'ada-exception)
4642 (define-key map "x" 'ada-exit)
4643 (define-key ada-mode-extra-map "t" map))
4644 )
4645
4646
4647 (defun ada-create-menu ()
4648 "Create the Ada menu as shown in the menu bar."
4649 (let ((m '("Ada"
4650 ("Help"
4651 ["Ada Mode" (info "ada-mode") t]
4652 ["GNAT User's Guide" (info "gnat_ugn")
4653 (eq ada-which-compiler 'gnat)]
4654 ["GNAT Reference Manual" (info "gnat_rm")
4655 (eq ada-which-compiler 'gnat)]
4656 ["Gcc Documentation" (info "gcc")
4657 (eq ada-which-compiler 'gnat)]
4658 ["Gdb Documentation" (info "gdb")
4659 (eq ada-which-compiler 'gnat)]
4660 ["Ada95 Reference Manual" (info "arm95") t])
4661 ("Options" :included (eq major-mode 'ada-mode)
4662 ["Auto Casing" (setq ada-auto-case (not ada-auto-case))
4663 :style toggle :selected ada-auto-case]
4664 ["Auto Indent After Return"
4665 (setq ada-indent-after-return (not ada-indent-after-return))
4666 :style toggle :selected ada-indent-after-return]
4667 ["Automatically Recompile For Cross-references"
4668 (setq ada-xref-create-ali (not ada-xref-create-ali))
4669 :style toggle :selected ada-xref-create-ali
4670 :included (eq ada-which-compiler 'gnat)]
4671 ["Confirm Commands"
4672 (setq ada-xref-confirm-compile (not ada-xref-confirm-compile))
4673 :style toggle :selected ada-xref-confirm-compile
4674 :included (eq ada-which-compiler 'gnat)]
4675 ["Show Cross-references In Other Buffer"
4676 (setq ada-xref-other-buffer (not ada-xref-other-buffer))
4677 :style toggle :selected ada-xref-other-buffer
4678 :included (eq ada-which-compiler 'gnat)]
4679 ["Tight Integration With GNU Visual Debugger"
4680 (setq ada-tight-gvd-integration (not ada-tight-gvd-integration))
4681 :style toggle :selected ada-tight-gvd-integration
4682 :included (string-match "gvd" ada-prj-default-debugger)])
4683 ["Customize" (customize-group 'ada)
4684 :included (fboundp 'customize-group)]
4685 ["Check file" ada-check-current t]
4686 ["Compile file" ada-compile-current t]
4687 ["Set main and Build" ada-set-main-compile-application t]
4688 ["Show main" ada-show-current-main t]
4689 ["Build" ada-compile-application t]
4690 ["Run" ada-run-application t]
4691 ["Debug" ada-gdb-application (eq ada-which-compiler 'gnat)]
4692 ["------" nil nil]
4693 ("Project"
4694 ["Show project" ada-show-current-project t]
4695 ["Load..." ada-set-default-project-file t]
4696 ["New..." ada-prj-new t]
4697 ["Edit..." ada-prj-edit t])
4698 ("Goto" :included (eq major-mode 'ada-mode)
4699 ["Goto Declaration/Body" ada-goto-declaration
4700 (eq ada-which-compiler 'gnat)]
4701 ["Goto Body" ada-goto-body
4702 (eq ada-which-compiler 'gnat)]
4703 ["Goto Declaration Other Frame"
4704 ada-goto-declaration-other-frame
4705 (eq ada-which-compiler 'gnat)]
4706 ["Goto Previous Reference" ada-xref-goto-previous-reference
4707 (eq ada-which-compiler 'gnat)]
4708 ["List Local References" ada-find-local-references
4709 (eq ada-which-compiler 'gnat)]
4710 ["List References" ada-find-references
4711 (eq ada-which-compiler 'gnat)]
4712 ["Goto Reference To Any Entity" ada-find-any-references
4713 (eq ada-which-compiler 'gnat)]
4714 ["Goto Parent Unit" ada-goto-parent
4715 (eq ada-which-compiler 'gnat)]
4716 ["--" nil nil]
4717 ["Next compilation error" next-error t]
4718 ["Previous Package" ada-previous-package t]
4719 ["Next Package" ada-next-package t]
4720 ["Previous Procedure" ada-previous-procedure t]
4721 ["Next Procedure" ada-next-procedure t]
4722 ["Goto Start Of Statement" ada-move-to-start t]
4723 ["Goto End Of Statement" ada-move-to-end t]
4724 ["-" nil nil]
4725 ["Other File" ff-find-other-file t]
4726 ["Other File Other Window" ada-ff-other-window t])
4727 ("Edit" :included (eq major-mode 'ada-mode)
4728 ["Search File On Source Path" ada-find-file t]
4729 ["------" nil nil]
4730 ["Complete Identifier" ada-complete-identifier t]
4731 ["-----" nil nil]
4732 ["Indent Line" ada-indent-current-function t]
4733 ["Justify Current Indentation" ada-justified-indent-current t]
4734 ["Indent Lines in Selection" ada-indent-region t]
4735 ["Indent Lines in File"
4736 (ada-indent-region (point-min) (point-max)) t]
4737 ["Format Parameter List" ada-format-paramlist t]
4738 ["-" nil nil]
4739 ["Comment Selection" comment-region t]
4740 ["Uncomment Selection" ada-uncomment-region t]
4741 ["--" nil nil]
4742 ["Fill Comment Paragraph" fill-paragraph t]
4743 ["Fill Comment Paragraph Justify"
4744 ada-fill-comment-paragraph-justify t]
4745 ["Fill Comment Paragraph Postfix"
4746 ada-fill-comment-paragraph-postfix t]
4747 ["---" nil nil]
4748 ["Adjust Case Selection" ada-adjust-case-region t]
4749 ["Adjust Case in File" ada-adjust-case-buffer t]
4750 ["Create Case Exception" ada-create-case-exception t]
4751 ["Create Case Exception Substring"
4752 ada-create-case-exception-substring t]
4753 ["Reload Case Exceptions" ada-case-read-exceptions t]
4754 ["----" nil nil]
4755 ["Make body for subprogram" ada-make-subprogram-body t]
4756 ["-----" nil nil]
4757 ["Narrow to subprogram" ada-narrow-to-defun t])
4758 ("Templates"
4759 :included (eq major-mode 'ada-mode)
4760 ["Header" ada-header t]
4761 ["-" nil nil]
4762 ["Package Body" ada-package-body t]
4763 ["Package Spec" ada-package-spec t]
4764 ["Function Spec" ada-function-spec t]
4765 ["Procedure Spec" ada-procedure-spec t]
4766 ["Proc/func Body" ada-subprogram-body t]
4767 ["Task Body" ada-task-body t]
4768 ["Task Spec" ada-task-spec t]
4769 ["Declare Block" ada-declare-block t]
4770 ["Exception Block" ada-exception-block t]
4771 ["--" nil nil]
4772 ["Entry" ada-entry t]
4773 ["Entry family" ada-entry-family t]
4774 ["Select" ada-select t]
4775 ["Accept" ada-accept t]
4776 ["Or accept" ada-or-accep t]
4777 ["Or delay" ada-or-delay t]
4778 ["Or terminate" ada-or-terminate t]
4779 ["---" nil nil]
4780 ["Type" ada-type t]
4781 ["Private" ada-private t]
4782 ["Subtype" ada-subtype t]
4783 ["Record" ada-record t]
4784 ["Array" ada-array t]
4785 ["----" nil nil]
4786 ["If" ada-if t]
4787 ["Else" ada-else t]
4788 ["Elsif" ada-elsif t]
4789 ["Case" ada-case t]
4790 ["-----" nil nil]
4791 ["While Loop" ada-while-loop t]
4792 ["For Loop" ada-for-loop t]
4793 ["Loop" ada-loop t]
4794 ["------" nil nil]
4795 ["Exception" ada-exception t]
4796 ["Exit" ada-exit t]
4797 ["When" ada-when t])
4798 )))
4799
4800 (easy-menu-define ada-mode-menu ada-mode-map "Menu keymap for Ada mode" m)
4801 (if (featurep 'xemacs)
4802 (progn
4803 (define-key ada-mode-map [menu-bar] ada-mode-menu)
4804 (set 'mode-popup-menu (cons "Ada mode" ada-mode-menu))))))
4805
4806 \f
4807 ;; -------------------------------------------------------
4808 ;; Commenting/Uncommenting code
4809 ;; The following two calls are provided to enhance the standard
4810 ;; comment-region function, which only allows uncommenting if the
4811 ;; comment is at the beginning of a line. If the line have been re-indented,
4812 ;; we are unable to use comment-region, which makes no sense.
4813 ;;
4814 ;; In addition, we provide an interface to the standard comment handling
4815 ;; function for justifying the comments.
4816 ;; -------------------------------------------------------
4817
4818 (defadvice comment-region (before ada-uncomment-anywhere disable)
4819 (if (and (consp arg) ;; a prefix with \C-u is of the form '(4), whereas
4820 ;; \C-u 2 sets arg to '2' (fixed by S.Leake)
4821 (derived-mode-p 'ada-mode))
4822 (save-excursion
4823 (let ((cs (concat "^[ \t]*" (regexp-quote comment-start))))
4824 (goto-char beg)
4825 (while (re-search-forward cs end t)
4826 (replace-match comment-start))
4827 ))))
4828
4829 (defun ada-uncomment-region (beg end &optional arg)
4830 "Uncomment region BEG .. END.
4831 ARG gives number of comment characters."
4832 (interactive "r\nP")
4833
4834 ;; This advice is not needed anymore with Emacs21. However, for older
4835 ;; versions, as well as for XEmacs, we still need to enable it.
4836 (if (or (<= emacs-major-version 20) (featurep 'xemacs))
4837 (progn
4838 (ad-activate 'comment-region)
4839 (comment-region beg end (- (or arg 2)))
4840 (ad-deactivate 'comment-region))
4841 (comment-region beg end (list (- (or arg 2))))
4842 (ada-indent-region beg end)))
4843
4844 (defun ada-fill-comment-paragraph-justify ()
4845 "Fill current comment paragraph and justify each line as well."
4846 (interactive)
4847 (ada-fill-comment-paragraph 'full))
4848
4849 (defun ada-fill-comment-paragraph-postfix ()
4850 "Fill current comment paragraph and justify each line as well.
4851 Adds `ada-fill-comment-postfix' at the end of each line."
4852 (interactive)
4853 (ada-fill-comment-paragraph 'full t))
4854
4855 (defun ada-fill-comment-paragraph (&optional justify postfix)
4856 "Fill the current comment paragraph.
4857 If JUSTIFY is non-nil, each line is justified as well.
4858 If POSTFIX and JUSTIFY are non-nil, `ada-fill-comment-postfix' is appended
4859 to each line filled and justified.
4860 The paragraph is indented on the first line."
4861 (interactive "P")
4862
4863 ;; check if inside comment or just in front a comment
4864 (if (and (not (ada-in-comment-p))
4865 (not (looking-at "[ \t]*--")))
4866 (error "Not inside comment"))
4867
4868 (let* (indent from to
4869 (opos (point-marker))
4870
4871 ;; Sets this variable to nil, otherwise it prevents
4872 ;; fill-region-as-paragraph to work on Emacs <= 20.2
4873 (parse-sexp-lookup-properties nil)
4874
4875 fill-prefix
4876 (fill-column (current-fill-column)))
4877
4878 ;; Find end of paragraph
4879 (back-to-indentation)
4880 (while (and (not (eobp)) (looking-at ".*--[ \t]*[^ \t\n]"))
4881 (forward-line 1)
4882
4883 ;; If we were at the last line in the buffer, create a dummy empty
4884 ;; line at the end of the buffer.
4885 (if (eobp)
4886 (insert "\n")
4887 (back-to-indentation)))
4888 (beginning-of-line)
4889 (setq to (point-marker))
4890 (goto-char opos)
4891
4892 ;; Find beginning of paragraph
4893 (back-to-indentation)
4894 (while (and (not (bobp)) (looking-at ".*--[ \t]*[^ \t\n]"))
4895 (forward-line -1)
4896 (back-to-indentation))
4897
4898 ;; We want one line above the first one, unless we are at the beginning
4899 ;; of the buffer
4900 (unless (bobp)
4901 (forward-line 1))
4902 (beginning-of-line)
4903 (setq from (point-marker))
4904
4905 ;; Calculate the indentation we will need for the paragraph
4906 (back-to-indentation)
4907 (setq indent (current-column))
4908 ;; unindent the first line of the paragraph
4909 (delete-region from (point))
4910
4911 ;; Remove the old postfixes
4912 (goto-char from)
4913 (while (re-search-forward "--\n" to t)
4914 (replace-match "\n"))
4915
4916 (goto-char (1- to))
4917 (setq to (point-marker))
4918
4919 ;; Indent and justify the paragraph
4920 (setq fill-prefix ada-fill-comment-prefix)
4921 (set-left-margin from to indent)
4922 (if postfix
4923 (setq fill-column (- fill-column (length ada-fill-comment-postfix))))
4924
4925 (fill-region-as-paragraph from to justify)
4926
4927 ;; Add the postfixes if required
4928 (if postfix
4929 (save-restriction
4930 (goto-char from)
4931 (narrow-to-region from to)
4932 (while (not (eobp))
4933 (end-of-line)
4934 (insert-char ? (- fill-column (current-column)))
4935 (insert ada-fill-comment-postfix)
4936 (forward-line))
4937 ))
4938
4939 ;; In Emacs <= 20.2 and XEmacs <=20.4, there is a bug, and a newline is
4940 ;; inserted at the end. Delete it
4941 (if (or (featurep 'xemacs)
4942 (<= emacs-major-version 19)
4943 (and (= emacs-major-version 20)
4944 (<= emacs-minor-version 2)))
4945 (progn
4946 (goto-char to)
4947 (end-of-line)
4948 (delete-char 1)))
4949
4950 (goto-char opos)))
4951
4952
4953 ;; ---------------------------------------------------
4954 ;; support for find-file.el
4955 ;; These functions are used by find-file to guess the file names from
4956 ;; unit names, and to find the other file (spec or body) from the current
4957 ;; file (body or spec).
4958 ;; It is also used to find in which function we are, so as to put the
4959 ;; cursor at the correct position.
4960 ;; Standard Ada does not force any relation between unit names and file names,
4961 ;; so some of these functions can only be a good approximation. However, they
4962 ;; are also overridden in `ada-xref'.el when we know that the user is using
4963 ;; GNAT.
4964 ;; ---------------------------------------------------
4965
4966 ;; Overridden when we work with GNAT, to use gnatkrunch
4967 (defun ada-make-filename-from-adaname (adaname)
4968 "Determine the filename in which ADANAME is found.
4969 This matches the GNAT default naming convention, except for
4970 pre-defined units."
4971 (while (string-match "\\." adaname)
4972 (setq adaname (replace-match "-" t t adaname)))
4973 (downcase adaname)
4974 )
4975
4976 (defun ada-other-file-name ()
4977 "Return the name of the other file.
4978 The name returned is the body if `current-buffer' is the spec,
4979 or the spec otherwise."
4980
4981 (let ((is-spec nil)
4982 (is-body nil)
4983 (suffixes ada-spec-suffixes)
4984 (name (buffer-file-name)))
4985
4986 ;; Guess whether we have a spec or a body, and get the basename of the
4987 ;; file. Since the extension may not start with '.', we can not use
4988 ;; file-name-extension
4989 (while (and (not is-spec)
4990 suffixes)
4991 (if (string-match (concat "\\(.*\\)" (car suffixes) "$") name)
4992 (setq is-spec t
4993 name (match-string 1 name)))
4994 (setq suffixes (cdr suffixes)))
4995
4996 (if (not is-spec)
4997 (progn
4998 (setq suffixes ada-body-suffixes)
4999 (while (and (not is-body)
5000 suffixes)
5001 (if (string-match (concat "\\(.*\\)" (car suffixes) "$") name)
5002 (setq is-body t
5003 name (match-string 1 name)))
5004 (setq suffixes (cdr suffixes)))))
5005
5006 ;; If this wasn't in either list, return name itself
5007 (if (not (or is-spec is-body))
5008 name
5009
5010 ;; Else find the other possible names
5011 (if is-spec
5012 (setq suffixes ada-body-suffixes)
5013 (setq suffixes ada-spec-suffixes))
5014 (setq is-spec name)
5015
5016 (while suffixes
5017
5018 ;; If we are using project file, search for the other file in all
5019 ;; the possible src directories.
5020
5021 (if (fboundp 'ada-find-src-file-in-dir)
5022 (let ((other
5023 (ada-find-src-file-in-dir
5024 (file-name-nondirectory (concat name (car suffixes))))))
5025 (if other
5026 (set 'is-spec other)))
5027
5028 ;; Else search in the current directory
5029 (if (file-exists-p (concat name (car suffixes)))
5030 (setq is-spec (concat name (car suffixes)))))
5031 (setq suffixes (cdr suffixes)))
5032
5033 is-spec)))
5034
5035 (defun ada-which-function-are-we-in ()
5036 "Return the name of the function whose definition/declaration point is in.
5037 Used in `ff-pre-load-hook'."
5038 (setq ff-function-name nil)
5039 (save-excursion
5040 (end-of-line);; make sure we get the complete name
5041 (or (if (re-search-backward ada-procedure-start-regexp nil t)
5042 (setq ff-function-name (match-string 5)))
5043 (if (re-search-backward ada-package-start-regexp nil t)
5044 (setq ff-function-name (match-string 4))))
5045 ))
5046
5047
5048 (defvar ada-last-which-function-line -1
5049 "Last line on which `ada-which-function' was called.")
5050 (defvar ada-last-which-function-subprog 0
5051 "Last subprogram name returned by `ada-which-function'.")
5052 (make-variable-buffer-local 'ada-last-which-function-subprog)
5053 (make-variable-buffer-local 'ada-last-which-function-line)
5054
5055
5056 (defun ada-which-function ()
5057 "Return the name of the function whose body the point is in.
5058 This function works even in the case of nested subprograms, whereas the
5059 standard Emacs function `which-function' does not.
5060 Since the search can be long, the results are cached."
5061
5062 (let ((line (count-lines 1 (point)))
5063 (pos (point))
5064 end-pos
5065 func-name indent
5066 found)
5067
5068 ;; If this is the same line as before, simply return the same result
5069 (if (= line ada-last-which-function-line)
5070 ada-last-which-function-subprog
5071
5072 (save-excursion
5073 ;; In case the current line is also the beginning of the body
5074 (end-of-line)
5075
5076 ;; Are we looking at "function Foo\n (paramlist)"
5077 (skip-chars-forward " \t\n(")
5078
5079 (condition-case nil
5080 (up-list 1)
5081 (error nil))
5082
5083 (skip-chars-forward " \t\n")
5084 (if (looking-at "return")
5085 (progn
5086 (forward-word 1)
5087 (skip-chars-forward " \t\n")
5088 (skip-chars-forward "a-zA-Z0-9_'")))
5089
5090 ;; Can't simply do forward-word, in case the "is" is not on the
5091 ;; same line as the closing parenthesis
5092 (skip-chars-forward "is \t\n")
5093
5094 ;; No look for the closest subprogram body that has not ended yet.
5095 ;; Not that we expect all the bodies to be finished by "end <name>",
5096 ;; or a simple "end;" indented in the same column as the start of
5097 ;; the subprogram. The goal is to be as efficient as possible.
5098
5099 (while (and (not found)
5100 (re-search-backward ada-imenu-subprogram-menu-re nil t))
5101
5102 ;; Get the function name, but not the properties, or this changes
5103 ;; the face in the modeline on Emacs 21
5104 (setq func-name (match-string-no-properties 3))
5105 (if (and (not (ada-in-comment-p))
5106 (not (save-excursion
5107 (goto-char (match-end 0))
5108 (looking-at "[ \t\n]*new"))))
5109 (save-excursion
5110 (back-to-indentation)
5111 (setq indent (current-column))
5112 (if (ada-search-ignore-string-comment
5113 (concat "end[ \t]+" func-name "[ \t]*;\\|^"
5114 (make-string indent ? ) "end;"))
5115 (setq end-pos (point))
5116 (setq end-pos (point-max)))
5117 (if (>= end-pos pos)
5118 (setq found func-name))))
5119 )
5120 (setq ada-last-which-function-line line
5121 ada-last-which-function-subprog found)
5122 found))))
5123
5124 (defun ada-ff-other-window ()
5125 "Find other file in other window using `ff-find-other-file'."
5126 (interactive)
5127 (and (fboundp 'ff-find-other-file)
5128 (ff-find-other-file t)))
5129
5130 (defun ada-set-point-accordingly ()
5131 "Move to the function declaration that was set by `ff-which-function-are-we-in'."
5132 (if ff-function-name
5133 (progn
5134 (goto-char (point-min))
5135 (unless (ada-search-ignore-string-comment
5136 (concat ff-function-name "\\b") nil)
5137 (goto-char (point-min))))))
5138
5139 (defun ada-get-body-name (&optional spec-name)
5140 "Return the file name for the body of SPEC-NAME.
5141 If SPEC-NAME is nil, return the body for the current package.
5142 Return nil if no body was found."
5143 (interactive)
5144
5145 (unless spec-name (setq spec-name (buffer-file-name)))
5146
5147 ;; Remove the spec extension. We can not simply remove the file extension,
5148 ;; but we need to take into account the specific non-GNAT extensions that the
5149 ;; user might have specified.
5150
5151 (let ((suffixes ada-spec-suffixes)
5152 end)
5153 (while suffixes
5154 (setq end (- (length spec-name) (length (car suffixes))))
5155 (if (string-equal (car suffixes) (substring spec-name end))
5156 (setq spec-name (substring spec-name 0 end)))
5157 (setq suffixes (cdr suffixes))))
5158
5159 ;; If find-file.el was available, use its functions
5160 (if (fboundp 'ff-get-file-name)
5161 (ff-get-file-name ada-search-directories-internal
5162 (ada-make-filename-from-adaname
5163 (file-name-nondirectory
5164 (file-name-sans-extension spec-name)))
5165 ada-body-suffixes)
5166 ;; Else emulate it very simply
5167 (concat (ada-make-filename-from-adaname
5168 (file-name-nondirectory
5169 (file-name-sans-extension spec-name)))
5170 ".adb")))
5171
5172 \f
5173 ;; ---------------------------------------------------
5174 ;; support for font-lock.el
5175 ;; Strings are a real pain in Ada because a single quote character is
5176 ;; overloaded as a string quote and type/instance delimiter. By default, a
5177 ;; single quote is given punctuation syntax in `ada-mode-syntax-table'.
5178 ;; So, for Font Lock mode purposes, we mark single quotes as having string
5179 ;; syntax when the gods that created Ada determine them to be.
5180 ;;
5181 ;; This only works in Emacs. See the comments before the grammar functions
5182 ;; at the beginning of this file for how this is done with XEmacs.
5183 ;; ----------------------------------------------------
5184
5185 (defconst ada-font-lock-syntactic-keywords
5186 ;; Mark single quotes as having string quote syntax in 'c' instances.
5187 ;; We used to explicitly avoid ''' as a special case for fear the buffer
5188 ;; be highlighted as a string, but it seems this fear is unfounded.
5189 ;;
5190 ;; This sets the properties of the characters, so that ada-in-string-p
5191 ;; correctly handles '"' too...
5192 '(("[^a-zA-Z0-9)]\\('\\)[^\n]\\('\\)" (1 (7 . ?')) (2 (7 . ?')))
5193 ("^[ \t]*\\(#\\(if\\|else\\|elsif\\|end\\)\\)" (1 (11 . ?\n)))))
5194
5195 (defvar ada-font-lock-keywords
5196 (eval-when-compile
5197 (list
5198 ;;
5199 ;; handle "type T is access function return S;"
5200 (list "\\<\\(function[ \t]+return\\)\\>" '(1 font-lock-keyword-face) )
5201
5202 ;; preprocessor line
5203 (list "^[ \t]*\\(#.*\n\\)" '(1 font-lock-type-face t))
5204
5205 ;;
5206 ;; accept, entry, function, package (body), protected (body|type),
5207 ;; pragma, procedure, task (body) plus name.
5208 (list (concat
5209 "\\<\\("
5210 "accept\\|"
5211 "entry\\|"
5212 "function\\|"
5213 "package[ \t]+body\\|"
5214 "package\\|"
5215 "pragma\\|"
5216 "procedure\\|"
5217 "protected[ \t]+body\\|"
5218 "protected[ \t]+type\\|"
5219 "protected\\|"
5220 "task[ \t]+body\\|"
5221 "task[ \t]+type\\|"
5222 "task"
5223 "\\)\\>[ \t]*"
5224 "\\(\\sw+\\(\\.\\sw*\\)*\\)?")
5225 '(1 font-lock-keyword-face) '(2 font-lock-function-name-face nil t))
5226 ;;
5227 ;; Optional keywords followed by a type name.
5228 (list (concat ; ":[ \t]*"
5229 "\\<\\(access[ \t]+all\\|access[ \t]+constant\\|access\\|constant\\|in[ \t]+reverse\\|\\|in[ \t]+out\\|in\\|out\\)\\>"
5230 "[ \t]*"
5231 "\\(\\sw+\\(\\.\\sw*\\)*\\)?")
5232 '(1 font-lock-keyword-face nil t) '(2 font-lock-type-face nil t))
5233
5234 ;;
5235 ;; Main keywords, except those treated specially below.
5236 (concat "\\<"
5237 (regexp-opt
5238 '("abort" "abs" "abstract" "accept" "access" "aliased" "all"
5239 "and" "array" "at" "begin" "case" "declare" "delay" "delta"
5240 "digits" "do" "else" "elsif" "entry" "exception" "exit" "for"
5241 "generic" "if" "in" "interface" "is" "limited" "loop" "mod" "not"
5242 "null" "or" "others" "overriding" "private" "protected" "raise"
5243 "range" "record" "rem" "renames" "requeue" "return" "reverse"
5244 "select" "separate" "synchronized" "tagged" "task" "terminate"
5245 "then" "until" "when" "while" "with" "xor") t)
5246 "\\>")
5247 ;;
5248 ;; Anything following end and not already fontified is a body name.
5249 '("\\<\\(end\\)\\>\\([ \t]+\\)?\\(\\(\\sw\\|[_.]\\)+\\)?"
5250 (1 font-lock-keyword-face) (3 font-lock-function-name-face nil t))
5251 ;;
5252 ;; Keywords followed by a type or function name.
5253 (list (concat "\\<\\("
5254 "new\\|of\\|subtype\\|type"
5255 "\\)\\>[ \t]*\\(\\sw+\\(\\.\\sw*\\)*\\)?[ \t]*\\((\\)?")
5256 '(1 font-lock-keyword-face)
5257 '(2 (if (match-beginning 4)
5258 font-lock-function-name-face
5259 font-lock-type-face) nil t))
5260 ;;
5261 ;; Keywords followed by a (comma separated list of) reference.
5262 ;; Note that font-lock only works on single lines, thus we can not
5263 ;; correctly highlight a with_clause that spans multiple lines.
5264 (list (concat "\\<\\(goto\\|raise\\|use\\|with\\)"
5265 "[ \t]+\\([a-zA-Z0-9_., \t]+\\)\\W")
5266 '(1 font-lock-keyword-face) '(2 font-lock-reference-face nil t))
5267
5268 ;;
5269 ;; Goto tags.
5270 '("<<\\(\\sw+\\)>>" 1 font-lock-reference-face)
5271
5272 ;; Highlight based-numbers (R. Reagan <robin-reply@reagans.org>)
5273 (list "\\([0-9]+#[0-9a-fA-F_]+#\\)" '(1 font-lock-constant-face t))
5274
5275 ;; Ada unnamed numerical constants
5276 (list "\\W\\([-+]?[0-9._]+\\)\\>" '(1 font-lock-constant-face))
5277
5278 ))
5279 "Default expressions to highlight in Ada mode.")
5280
5281
5282 ;; ---------------------------------------------------------
5283 ;; Support for outline.el
5284 ;; ---------------------------------------------------------
5285
5286 (defun ada-outline-level ()
5287 "This is so that `current-column' DTRT in otherwise-hidden text."
5288 ;; patch from Dave Love <fx@gnu.org>
5289 (let (buffer-invisibility-spec)
5290 (save-excursion
5291 (back-to-indentation)
5292 (current-column))))
5293
5294 ;; ---------------------------------------------------------
5295 ;; Support for narrow-to-region
5296 ;; ---------------------------------------------------------
5297
5298 (defun ada-narrow-to-defun (&optional arg)
5299 "Make text outside current subprogram invisible.
5300 The subprogram visible is the one that contains or follow point.
5301 Optional ARG is ignored.
5302 Use \\[widen] to go back to the full visibility for the buffer."
5303
5304 (interactive)
5305 (save-excursion
5306 (let (end)
5307 (widen)
5308 (forward-line 1)
5309 (ada-previous-procedure)
5310
5311 (save-excursion
5312 (beginning-of-line)
5313 (setq end (point)))
5314
5315 (ada-move-to-end)
5316 (end-of-line)
5317 (narrow-to-region end (point))
5318 (message
5319 "Use M-x widen to get back to full visibility in the buffer"))))
5320
5321 ;; ---------------------------------------------------------
5322 ;; Automatic generation of code
5323 ;; The Ada mode has a set of function to automatically generate a subprogram
5324 ;; or package body from its spec.
5325 ;; These function only use a primary and basic algorithm, this could use a
5326 ;; lot of improvement.
5327 ;; When the user is using GNAT, we rather use gnatstub to generate an accurate
5328 ;; body.
5329 ;; ----------------------------------------------------------
5330
5331 (defun ada-gen-treat-proc (match)
5332 "Make dummy body of a procedure/function specification.
5333 MATCH is a cons cell containing the start and end locations of the last search
5334 for `ada-procedure-start-regexp'."
5335 (goto-char (car match))
5336 (let (func-found procname functype)
5337 (cond
5338 ((or (looking-at "^[ \t]*procedure")
5339 (setq func-found (looking-at "^[ \t]*function")))
5340 ;; treat it as a proc/func
5341 (forward-word 2)
5342 (forward-word -1)
5343 (setq procname (buffer-substring (point) (cdr match))) ; store proc name
5344
5345 ;; goto end of procname
5346 (goto-char (cdr match))
5347
5348 ;; skip over parameterlist
5349 (unless (looking-at "[ \t\n]*\\(;\\|return\\)")
5350 (forward-sexp))
5351
5352 ;; if function, skip over 'return' and result type.
5353 (if func-found
5354 (progn
5355 (forward-word 1)
5356 (skip-chars-forward " \t\n")
5357 (setq functype (buffer-substring (point)
5358 (progn
5359 (skip-chars-forward
5360 "a-zA-Z0-9_\.")
5361 (point))))))
5362 ;; look for next non WS
5363 (cond
5364 ((looking-at "[ \t]*;")
5365 (delete-region (match-beginning 0) (match-end 0));; delete the ';'
5366 (ada-indent-newline-indent)
5367 (insert "is")
5368 (ada-indent-newline-indent)
5369 (if func-found
5370 (progn
5371 (insert "Result : " functype ";")
5372 (ada-indent-newline-indent)))
5373 (insert "begin")
5374 (ada-indent-newline-indent)
5375 (if func-found
5376 (insert "return Result;")
5377 (insert "null;"))
5378 (ada-indent-newline-indent)
5379 (insert "end " procname ";")
5380 (ada-indent-newline-indent)
5381 )
5382
5383 ((looking-at "[ \t\n]*is")
5384 ;; do nothing
5385 )
5386
5387 ((looking-at "[ \t\n]*rename")
5388 ;; do nothing
5389 )
5390
5391 (t
5392 (message "unknown syntax"))))
5393 (t
5394 (if (looking-at "^[ \t]*task")
5395 (progn
5396 (message "Task conversion is not yet implemented")
5397 (forward-word 2)
5398 (if (looking-at "[ \t]*;")
5399 (forward-line)
5400 (ada-move-to-end))
5401 ))))))
5402
5403 (defun ada-make-body ()
5404 "Create an Ada package body in the current buffer.
5405 The spec must be the previously visited buffer.
5406 This function typically is to be hooked into `ff-file-created-hook'."
5407 (delete-region (point-min) (point-max))
5408 (insert-buffer-substring (car (cdr (buffer-list))))
5409 (goto-char (point-min))
5410 (ada-mode)
5411
5412 (let (found ada-procedure-or-package-start-regexp)
5413 (if (setq found
5414 (ada-search-ignore-string-comment ada-package-start-regexp nil))
5415 (progn (goto-char (cdr found))
5416 (insert " body")
5417 )
5418 (error "No package"))
5419
5420 (setq ada-procedure-or-package-start-regexp
5421 (concat ada-procedure-start-regexp
5422 "\\|"
5423 ada-package-start-regexp))
5424
5425 (while (setq found
5426 (ada-search-ignore-string-comment
5427 ada-procedure-or-package-start-regexp nil))
5428 (progn
5429 (goto-char (car found))
5430 (if (looking-at ada-package-start-regexp)
5431 (progn (goto-char (cdr found))
5432 (insert " body"))
5433 (ada-gen-treat-proc found))))))
5434
5435
5436 (defun ada-make-subprogram-body ()
5437 "Create a dummy subprogram body in package body file from spec surrounding point."
5438 (interactive)
5439 (let* ((found (re-search-backward ada-procedure-start-regexp nil t))
5440 (spec (match-beginning 0))
5441 body-file)
5442 (if found
5443 (progn
5444 (goto-char spec)
5445 (if (and (re-search-forward "(\\|;" nil t)
5446 (= (char-before) ?\())
5447 (progn
5448 (ada-search-ignore-string-comment ")" nil)
5449 (ada-search-ignore-string-comment ";" nil)))
5450 (setq spec (buffer-substring spec (point)))
5451
5452 ;; If find-file.el was available, use its functions
5453 (setq body-file (ada-get-body-name))
5454 (if body-file
5455 (find-file body-file)
5456 (error "No body found for the package. Create it first"))
5457
5458 (save-restriction
5459 (widen)
5460 (goto-char (point-max))
5461 (forward-comment -10000)
5462 (re-search-backward "\\<end\\>" nil t)
5463 ;; Move to the beginning of the elaboration part, if any
5464 (re-search-backward "^begin" nil t)
5465 (newline)
5466 (forward-char -1)
5467 (insert spec)
5468 (re-search-backward ada-procedure-start-regexp nil t)
5469 (ada-gen-treat-proc (cons (match-beginning 0) (match-end 0)))
5470 ))
5471 (error "Not in subprogram spec"))))
5472
5473 ;; --------------------------------------------------------
5474 ;; Global initializations
5475 ;; --------------------------------------------------------
5476
5477 ;; Create the keymap once and for all. If we do that in ada-mode,
5478 ;; the keys changed in the user's .emacs have to be modified
5479 ;; every time
5480 (ada-create-keymap)
5481 (ada-create-menu)
5482
5483 ;; Create the syntax tables, but do not activate them
5484 (ada-create-syntax-table)
5485
5486 ;; Add the default extensions (and set up speedbar)
5487 (ada-add-extensions ".ads" ".adb")
5488 ;; This two files are generated by GNAT when running with -gnatD
5489 (if (equal ada-which-compiler 'gnat)
5490 (ada-add-extensions ".ads.dg" ".adb.dg"))
5491
5492 ;; Read the special cases for exceptions
5493 (ada-case-read-exceptions)
5494
5495 ;; Setup auto-loading of the other Ada mode files.
5496 (autoload 'ada-change-prj "ada-xref" nil t)
5497 (autoload 'ada-check-current "ada-xref" nil t)
5498 (autoload 'ada-compile-application "ada-xref" nil t)
5499 (autoload 'ada-compile-current "ada-xref" nil t)
5500 (autoload 'ada-complete-identifier "ada-xref" nil t)
5501 (autoload 'ada-find-file "ada-xref" nil t)
5502 (autoload 'ada-find-any-references "ada-xref" nil t)
5503 (autoload 'ada-find-src-file-in-dir "ada-xref" nil t)
5504 (autoload 'ada-find-local-references "ada-xref" nil t)
5505 (autoload 'ada-find-references "ada-xref" nil t)
5506 (autoload 'ada-gdb-application "ada-xref" nil t)
5507 (autoload 'ada-goto-declaration "ada-xref" nil t)
5508 (autoload 'ada-goto-declaration-other-frame "ada-xref" nil t)
5509 (autoload 'ada-goto-parent "ada-xref" nil t)
5510 (autoload 'ada-make-body-gnatstub "ada-xref" nil t)
5511 (autoload 'ada-point-and-xref "ada-xref" nil t)
5512 (autoload 'ada-reread-prj-file "ada-xref" nil t)
5513 (autoload 'ada-run-application "ada-xref" nil t)
5514 (autoload 'ada-set-default-project-file "ada-xref" nil t)
5515 (autoload 'ada-xref-goto-previous-reference "ada-xref" nil t)
5516 (autoload 'ada-set-main-compile-application "ada-xref" nil t)
5517 (autoload 'ada-show-current-main "ada-xref" nil t)
5518
5519 (autoload 'ada-customize "ada-prj" nil t)
5520 (autoload 'ada-prj-edit "ada-prj" nil t)
5521 (autoload 'ada-prj-new "ada-prj" nil t)
5522 (autoload 'ada-prj-save "ada-prj" nil t)
5523
5524 (autoload 'ada-array "ada-stmt" nil t)
5525 (autoload 'ada-case "ada-stmt" nil t)
5526 (autoload 'ada-declare-block "ada-stmt" nil t)
5527 (autoload 'ada-else "ada-stmt" nil t)
5528 (autoload 'ada-elsif "ada-stmt" nil t)
5529 (autoload 'ada-exception "ada-stmt" nil t)
5530 (autoload 'ada-exception-block "ada-stmt" nil t)
5531 (autoload 'ada-exit "ada-stmt" nil t)
5532 (autoload 'ada-for-loop "ada-stmt" nil t)
5533 (autoload 'ada-function-spec "ada-stmt" nil t)
5534 (autoload 'ada-header "ada-stmt" nil t)
5535 (autoload 'ada-if "ada-stmt" nil t)
5536 (autoload 'ada-loop "ada-stmt" nil t)
5537 (autoload 'ada-package-body "ada-stmt" nil t)
5538 (autoload 'ada-package-spec "ada-stmt" nil t)
5539 (autoload 'ada-private "ada-stmt" nil t)
5540 (autoload 'ada-procedure-spec "ada-stmt" nil t)
5541 (autoload 'ada-record "ada-stmt" nil t)
5542 (autoload 'ada-subprogram-body "ada-stmt" nil t)
5543 (autoload 'ada-subtype "ada-stmt" nil t)
5544 (autoload 'ada-tabsize "ada-stmt" nil t)
5545 (autoload 'ada-task-body "ada-stmt" nil t)
5546 (autoload 'ada-task-spec "ada-stmt" nil t)
5547 (autoload 'ada-type "ada-stmt" nil t)
5548 (autoload 'ada-use "ada-stmt" nil t)
5549 (autoload 'ada-when "ada-stmt" nil t)
5550 (autoload 'ada-while-loop "ada-stmt" nil t)
5551 (autoload 'ada-with "ada-stmt" nil t)
5552
5553 ;;; provide ourselves
5554 (provide 'ada-mode)
5555
5556 ;; arch-tag: 1b7d45ec-1698-43b5-8d4a-e479ea023270
5557 ;;; ada-mode.el ends here