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