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