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