]> code.delx.au - gnu-emacs-elpa/blob - packages/ada-mode/ada-mode.el
release ada-mode 5.1.7, wisi 1.1.0; minor format changes in ada-ref-man (take 2)
[gnu-emacs-elpa] / packages / ada-mode / ada-mode.el
1 ;;; ada-mode.el --- major-mode for editing Ada sources
2 ;;
3 ;;; Copyright (C) 1994, 1995, 1997 - 2014 Free Software Foundation, Inc.
4 ;;
5 ;; Author: Stephen Leake <stephen_leake@member.fsf.org>
6 ;; Maintainer: Stephen Leake <stephen_leake@member.fsf.org>
7 ;; Keywords: languages
8 ;; ada
9 ;; Version: 5.1.7
10 ;; package-requires: ((wisi "1.1.0") (cl-lib "0.4") (emacs "24.2"))
11 ;; url: http://stephe-leake.org/emacs/ada-mode/emacs-ada-mode.html
12 ;;
13 ;; (Gnu ELPA requires single digits between dots in versions)
14 ;;
15 ;; This file is part of GNU Emacs.
16 ;;
17 ;; GNU Emacs is free software: you can redistribute it and/or modify
18 ;; it under the terms of the GNU General Public License as published by
19 ;; the Free Software Foundation, either version 3 of the License, or
20 ;; (at your option) any later version.
21 ;;
22 ;; GNU Emacs is distributed in the hope that it will be useful,
23 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
24 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 ;; GNU General Public License for more details.
26 ;;
27 ;; You should have received a copy of the GNU General Public License
28 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
29 ;;
30 ;;; Usage:
31 ;;
32 ;; Emacs should enter Ada mode automatically when you load an Ada
33 ;; file, based on the file extension. The default extensions for Ada
34 ;; files are .ads, .adb; use ada-add-extensions to add other
35 ;; extensions.
36 ;;
37 ;; By default, ada-mode is configured to take full advantage of the
38 ;; GNAT compiler. If you are using another compiler, you
39 ;; should load that compiler's ada-* file first; that will define
40 ;; ada-compiler as a feature, so ada-gnat.el will not be loaded.
41 ;;
42 ;; See the user guide (info "ada-mode"), built from ada-mode.texi.
43
44 ;;; Design:
45 ;;
46 ;; In order to support multiple compilers, we use indirect function
47 ;; calls for all operations that depend on the compiler.
48 ;;
49 ;; We also support a cross reference tool (also called xref tool) that
50 ;; is different from the compiler. For example, you can use a local
51 ;; GNAT compiler to generate and access cross-reference information,
52 ;; while using a cross-compiler for compiling the final executable.
53 ;;
54 ;; Other functions are lumped with the choice of xref tool; mapping
55 ;; Ada names to file names, creating package bodies; any tool function
56 ;; that does not create executable code.
57 ;;
58 ;; The indentation engine and skeleton tools are also called
59 ;; indirectly, to allow parallel development of new versions of these
60 ;; tools (inspired by experience with ada-smie and ada-wisi).
61 ;;
62 ;; We also support using different compilers for different projects;
63 ;; `ada-compiler' can be set in Ada mode project files. Note that
64 ;; there is only one project active at a time; the most recently
65 ;; selected one. All Ada files are assumed to belong to this project
66 ;; (which is not correct, but works well in practice; the user is
67 ;; typically only concerned about files that belong to the current
68 ;; project).
69 ;;
70 ;; There are several styles of indirect calls:
71 ;;
72 ;; - scalar global variable set during load
73 ;;
74 ;; Appropriate when the choice of implementation is fixed at load
75 ;; time; it does not depend on the current Ada project. Used for
76 ;; indentation and skeleton functions.
77 ;;
78 ;; - scalar global variable set during project select
79 ;;
80 ;; Appropriate when the choice of implementation is determined by
81 ;; the choice of compiler or xref tool, which is per-project. The
82 ;; user sets the compiler choice in the project file, but not the
83 ;; lower-level redirect choice.
84 ;;
85 ;; For example, `ada-file-name-from-ada-name' depends on the naming
86 ;; convention used by the compiler. If the project file sets
87 ;; ada_compiler to 'gnat (either directly or by default),
88 ;; ada-gnat-select-prj sets `ada-file-name-from-ada-name' to
89 ;; `ada-gnat-file-name-from-ada-name'.
90 ;;
91 ;; - scalar buffer-local variable set during project select or file open
92 ;;
93 ;; Appropriate when choice of implementation is normally
94 ;; per-project, but can be per-buffer.
95 ;;
96 ;; For example, `ada-case-strict' will normally be set by the
97 ;; project, but some files may deviate from the project standard (if
98 ;; they are generated by -fdumpspec, for example). Those files set
99 ;; `ada-case-strict' in a file local variable comment.
100 ;;
101 ;; - scalar buffer-local variable set by ada-mode or ada-mode-hook
102 ;; function
103 ;;
104 ;; Appropriate when the variable is a non-Ada mode variable, also
105 ;; used by other modes, and choice should not affect those modes.
106 ;;
107 ;; `indent-line-function', `comment-indent-function' use this style
108 ;;
109 ;; - alist global variable indexed by ada-compiler
110 ;;
111 ;; Appropriate when the choice of implementation is determined by
112 ;; the compiler, but the function is invoked during project parse,
113 ;; so we can't depend on a value set by project select.
114 ;;
115 ;; alist entries are set during load by the implementation elisp files.
116 ;;
117 ;; `ada-prj-default-compiler-alist' uses this style.
118
119 ;;; History:
120 ;;
121 ;; The first Ada mode for GNU Emacs was written by V. Broman in
122 ;; 1985. He based his work on the already existing Modula-2 mode.
123 ;; This was distributed as ada.el in versions of Emacs prior to 19.29.
124 ;;
125 ;; Lynn Slater wrote an extensive Ada mode in 1989. It consisted of
126 ;; several files with support for dired commands and other nice
127 ;; things.
128 ;;
129 ;; The probably very first Ada mode (called electric-ada.el) was
130 ;; written by Steven D. Litvintchouk and Steven M. Rosen for the
131 ;; Gosling Emacs. L. Slater based his development on ada.el and
132 ;; electric-ada.el.
133 ;;
134 ;; A complete rewrite by Rolf Ebert <ebert@inf.enst.fr> and Markus
135 ;; Heritsch <Markus.Heritsch@studbox.uni-stuttgart.de> was done at
136 ;; some point. Some ideas from the Ada mode mailing list have been
137 ;; added. Some of the functionality of L. Slater's mode has not (yet)
138 ;; been recoded in this new mode.
139 ;;
140 ;; A complete rewrite for Emacs-20 / GNAT-3.11 was done by Emmanuel
141 ;; Briot <briot@gnat.com> at Ada Core Technologies.
142 ;;
143 ;; A complete rewrite, to restructure the code more orthogonally, and
144 ;; to use wisi for the indentation engine, was done in 2012 - 2013 by
145 ;; Stephen Leake <stephen_leake@stephe-leake.org>.
146
147 ;;; Credits:
148 ;;
149 ;; Many thanks to John McCabe <john@assen.demon.co.uk> for sending so
150 ;; many patches included in this package.
151 ;; Christian Egli <Christian.Egli@hcsd.hac.com>:
152 ;; ada-imenu-generic-expression
153 ;; Many thanks also to the following persons that have contributed
154 ;; to the ada-mode
155 ;; Philippe Waroquiers (PW) <philippe@cfmu.eurocontrol.be> in particular,
156 ;; woodruff@stc.llnl.gov (John Woodruff)
157 ;; jj@ddci.dk (Jesper Joergensen)
158 ;; gse@ocsystems.com (Scott Evans)
159 ;; comar@gnat.com (Cyrille Comar)
160 ;; robin-reply@reagans.org
161 ;; and others for their valuable hints.
162
163 (require 'align)
164 (require 'cl-lib)
165 (require 'compile)
166 (require 'find-file)
167
168 (defun ada-mode-version ()
169 "Return Ada mode version."
170 (interactive)
171 (let ((version-string "5.1.7"))
172 ;; must match:
173 ;; ada-mode.texi
174 ;; README
175 ;; Version: above
176 (if (called-interactively-p 'interactive)
177 (message version-string)
178 version-string)))
179
180 ;;;;; User variables
181
182 (defvar ada-mode-hook nil
183 "List of functions to call when Ada mode is invoked.
184 This hook is executed after `ada-mode' is fully loaded, but
185 before file local variables are processed.")
186
187 (defgroup ada nil
188 "Major mode for editing Ada source code in Emacs."
189 :group 'languages)
190
191 (defcustom ada-auto-case t
192 ;; can be per-buffer
193 "Buffer-local value that may override project variable `auto_case'.
194 Global value is default for project variable `auto_case'.
195 Non-nil means automatically change case of preceding word while typing.
196 Casing of Ada keywords is done according to `ada-case-keyword',
197 identifiers are Mixed_Case."
198 :type 'boolean
199 :group 'ada
200 :safe 'booleanp)
201 (make-variable-buffer-local 'ada-auto-case)
202
203 (defcustom ada-case-exception-file nil
204 "Default list of special casing exceptions dictionaries for identifiers.
205 Override with 'casing' project variable.
206
207 New exceptions may be added interactively via `ada-case-create-exception'.
208 If an exception is defined in multiple files, the first occurence is used.
209
210 The file format is one word per line, that gives the casing to be
211 used for that word in Ada source code. If the line starts with
212 the character *, then the exception will be used for partial
213 words that either start at the beginning of a word or after a _
214 character, and end either at the end of the word or at a _
215 character. Characters after the first word are ignored, and not
216 preserved when the list is written back to the file."
217 :type '(repeat (file))
218 :group 'ada
219 :safe 'listp)
220
221 (defcustom ada-case-keyword 'downcase-word
222 "Buffer-local value that may override project variable `case_keyword'.
223 Global value is default for project variable `case_keyword'.
224 Function to call to adjust the case of Ada keywords."
225 :type '(choice (const downcase-word)
226 (const upcase-word))
227 :group 'ada
228 :safe 'functionp)
229 (make-variable-buffer-local 'ada-case-keyword)
230
231 (defcustom ada-case-identifier 'ada-mixed-case
232 "Buffer-local value that may override project variable `case_keyword'.
233 Global value is default for project variable `case_keyword'.
234 Function to call to adjust the case of Ada keywords."
235 :type '(choice (const ada-mixed-case)
236 (const downcase-region)
237 (const upcase-region))
238 :group 'ada
239 :safe 'functionp)
240 (make-variable-buffer-local 'ada-case-identifier)
241
242 (defcustom ada-case-strict t
243 "Buffer-local value that may override project variable `case_strict'.
244 Global value is default for project variable `case_strict'.
245 If non-nil, force Mixed_Case for identifiers.
246 Otherwise, allow UPPERCASE for identifiers."
247 :type 'boolean
248 :group 'ada
249 :safe 'booleanp)
250 (make-variable-buffer-local 'ada-case-strict)
251
252 (defcustom ada-language-version 'ada2012
253 "Ada language version; one of `ada83', `ada95', `ada2005', `ada2012'.
254 Only affects the keywords to highlight, not which version the
255 indentation parser accepts."
256 :type '(choice (const ada83)
257 (const ada95)
258 (const ada2005)
259 (const ada2012))
260 :group 'ada
261 :safe 'symbolp)
262 (make-variable-buffer-local 'ada-language-version)
263
264 (defcustom ada-fill-comment-prefix "-- "
265 "Comment fill prefix."
266 :type 'string
267 :group 'ada)
268 (make-variable-buffer-local 'ada-language-version)
269
270 (defcustom ada-fill-comment-postfix " --"
271 "Comment fill postfix."
272 :type 'string
273 :group 'ada)
274 (make-variable-buffer-local 'ada-language-version)
275
276 (defcustom ada-prj-file-extensions '("adp" "prj")
277 "List of Emacs Ada mode project file extensions.
278 Used when searching for a project file.
279 Any file with one of these extensions will be parsed by `ada-prj-parse-file-1'."
280 :type 'list
281 :group 'ada)
282
283 (defcustom ada-prj-file-ext-extra nil
284 "List of secondary project file extensions.
285 Used when searching for a project file that can be a primary or
286 secondary project file (referenced from a primary). The user
287 must provide a parser for a file with one of these extensions."
288 :type 'list
289 :group 'ada)
290
291 ;;;;; end of user variables
292
293 (defconst ada-symbol-end
294 ;; we can't just add \> here; that might match _ in a user modified ada-mode-syntax-table
295 "\\([ \t]+\\|$\\)"
296 "Regexp to add to symbol name in `ada-which-function'.")
297
298 (defvar ada-compiler nil
299 "Default Ada compiler; can be overridden in project files.
300 Values defined by compiler packages.")
301
302 (defvar ada-xref-tool nil
303 "Default Ada cross reference tool; can be overridden in project files.
304 Values defined by cross reference packages.")
305
306 ;;;; keymap and menus
307
308 (defvar ada-ret-binding 'ada-indent-newline-indent)
309 (defvar ada-lfd-binding 'newline-and-indent)
310
311 (defun ada-case-activate-keys (map)
312 "Modify the key bindings for all the keys that should adjust casing."
313 ;; we could just put these in the keymap below, but this is easier.
314 (mapc (function
315 (lambda(key)
316 (define-key
317 map
318 (char-to-string key)
319 'ada-case-adjust-interactive)))
320 '( ?_ ?% ?& ?* ?( ?) ?- ?= ?+
321 ?| ?\; ?: ?' ?\" ?< ?, ?. ?> ?/ ?\n 32 ?\r ))
322 )
323
324 (defvar ada-mode-map
325 (let ((map (make-sparse-keymap)))
326 ;; C-c <letter> are reserved for users
327
328 ;; global-map has C-x ` 'next-error
329 (define-key map [return] 'ada-case-adjust-interactive)
330 (define-key map "\C-c`" 'ada-show-secondary-error)
331 (define-key map "\C-c;" (lambda () (error "use M-; instead"))) ; comment-dwim
332 (define-key map "\C-c<" 'ada-goto-declaration-start)
333 (define-key map "\C-c>" 'ada-goto-declaration-end)
334 (define-key map "\C-c\M-`" 'ada-fix-compiler-error)
335 (define-key map "\C-c\C-a" 'ada-align)
336 (define-key map "\C-c\C-b" 'ada-make-subprogram-body)
337 (define-key map "\C-c\C-c" 'ada-build-make)
338 (define-key map "\C-c\C-d" 'ada-goto-declaration)
339 (define-key map "\C-c\M-d" 'ada-show-declaration-parents)
340 (define-key map "\C-c\C-e" 'ada-expand)
341 (define-key map "\C-c\C-f" 'ada-show-parse-error)
342 (define-key map "\C-c\C-i" 'ada-indent-statement)
343 (define-key map "\C-c\C-m" 'ada-build-set-make)
344 (define-key map "\C-c\C-n" 'ada-next-statement-keyword)
345 (define-key map "\C-c\M-n" 'ada-next-placeholder)
346 (define-key map "\C-c\C-o" 'ada-find-other-file)
347 (define-key map "\C-c\M-o" 'ada-find-other-file-noset)
348 (define-key map "\C-c\C-p" 'ada-prev-statement-keyword)
349 (define-key map "\C-c\M-p" 'ada-prev-placeholder)
350 (define-key map "\C-c\C-q" 'ada-xref-refresh)
351 (define-key map "\C-c\C-r" 'ada-show-references)
352 (define-key map "\C-c\M-r" 'ada-build-run)
353 (define-key map "\C-c\C-s" 'ada-goto-previous-pos)
354 (define-key map "\C-c\C-v" 'ada-build-check)
355 (define-key map "\C-c\C-w" 'ada-case-adjust-at-point)
356 (define-key map "\C-c\C-x" 'ada-show-overriding)
357 (define-key map "\C-c\M-x" 'ada-show-overridden)
358 (define-key map "\C-c\C-y" 'ada-case-create-exception)
359 (define-key map "\C-c\M-y" 'ada-case-create-partial-exception)
360 (define-key map [C-down-mouse-3] 'ada-popup-menu)
361
362 (ada-case-activate-keys map)
363
364 map
365 ) "Local keymap used for Ada mode.")
366
367 (defvar ada-mode-menu (make-sparse-keymap "Ada"))
368 (easy-menu-define ada-mode-menu ada-mode-map "Menu keymap for Ada mode"
369 '("Ada"
370 ("Help"
371 ["Ada Mode" (info "ada-mode") t]
372 ["Ada Reference Manual" (info "arm2012") t]
373 ["Key bindings" describe-bindings t]
374 )
375 ["Customize" (customize-group 'ada) t]
376 ("Project files"
377 ["Find and select project ..." ada-build-prompt-select-prj-file t]
378 ["Select project ..." ada-prj-select t]
379 ["Show project" ada-prj-show t]
380 ["Show project search path" ada-prj-show-path t]
381 )
382 ("Build"
383 ["Next compilation error" next-error t]
384 ["Show secondary error" ada-show-secondary-error t]
385 ["Fix compilation error" ada-fix-compiler-error t]
386 ["Show last parse error" ada-show-parse-error t]
387 ["Check syntax" ada-build-check t]
388 ["Show main" ada-build-show-main t]
389 ["Build" ada-build-make t]
390 ["Set main and Build" ada-build-set-make t]
391 ["Run" ada-build-run t]
392 )
393 ("Navigate"
394 ["Other file" ada-find-other-file t]
395 ["Other file don't find decl" ada-find-other-file-noset t]
396 ["Goto declaration/body" ada-goto-declaration t]
397 ["Goto next statement keyword" ada-next-statement-keyword t]
398 ["Goto declaration start" ada-goto-declaration-start t]
399 ["Goto declaration end" ada-goto-declaration-end t]
400 ["Show parent declarations" ada-show-declaration-parents t]
401 ["Show references" ada-show-references t]
402 ["Show overriding" ada-show-overriding t]
403 ["Show overridden" ada-show-overridden t]
404 ["Goto prev position" ada-goto-previous-pos t]
405 ["Next placeholder" ada-next-placeholder t]
406 ["Previous placeholder" ada-prev-placeholder t]
407 )
408 ("Edit"
409 ["Expand skeleton" ada-expand t]
410 ["Indent line or selection" indent-for-tab-command t]
411 ["Indent current statement" ada-indent-statement t]
412 ["Indent lines in file" (indent-region (point-min) (point-max)) t]
413 ["Align" ada-align t]
414 ["Comment/uncomment selection" comment-dwim t]
415 ["Fill comment paragraph" ada-fill-comment-paragraph t]
416 ["Fill comment paragraph justify" (ada-fill-comment-paragraph 'full) t]
417 ["Fill comment paragraph postfix" (ada-fill-comment-paragraph 'full t) t]
418 ["Make body for subprogram" ada-make-subprogram-body t]
419 )
420 ("Casing"
421 ["Create full exception" ada-case-create-exception t]
422 ["Create partial exception" ada-case-create-partial-exception t]
423 ["Adjust case at point" ada-case-adjust-at-point t]
424 ["Adjust case region" ada-case-adjust-region t]
425 ["Adjust case buffer" ada-case-adjust-buffer t]
426 ["Show casing files list" ada-case-show-files t]
427 )
428 ("Misc"
429 ["Show last parse error" ada-show-parse-error t]
430 ["Show xref tool buffer" ada-show-xref-tool-buffer t]
431 ["Refresh cross reference cache" ada-xref-refresh t]
432 ["Reset parser" ada-reset-parser t]
433 )))
434
435 ;; This doesn't need to be buffer-local because there can be only one
436 ;; popup menu at a time.
437 (defvar ada-context-menu-on-identifier nil)
438
439 (easy-menu-define ada-context-menu nil
440 "Context menu keymap for Ada mode"
441 '("Ada"
442 ["Make body for subprogram" ada-make-subprogram-body t]
443 ["Goto declaration/body" ada-goto-declaration :included ada-context-menu-on-identifier]
444 ["Show parent declarations" ada-show-declaration-parents :included ada-context-menu-on-identifier]
445 ["Show references" ada-show-references :included ada-context-menu-on-identifier]
446 ["Show overriding" ada-show-overriding :included ada-context-menu-on-identifier]
447 ["Show overridden" ada-show-overridden :included ada-context-menu-on-identifier]
448 ["Expand skeleton" ada-expand t]
449 ["Create full case exception" ada-case-create-exception t]
450 ["Create partial case exception" ada-case-create-partial-exception t]
451
452 ["-" nil nil]
453 ["Align" ada-align t]
454 ["Adjust case at point" ada-case-adjust-at-point (not (use-region-p))]
455 ["Adjust case region" ada-case-adjust-region (use-region-p)]
456 ["Indent current statement" ada-indent-statement t]
457 ["Goto next statement keyword" ada-next-statement-keyword t]
458 ["Goto prev statement keyword" ada-next-statement-keyword t]
459 ["Other File" ada-find-other-file t]
460 ["Other file don't find decl" ada-find-other-file-noset t]))
461
462 (defun ada-popup-menu (position)
463 "Pops up a `ada-context-menu', with `ada-context-menu-on-identifer' set appropriately.
464 POSITION is the location the mouse was clicked on.
465 Sets `ada-context-menu-last-point' to the current position before
466 displaying the menu. When a function from the menu is called,
467 point is where the mouse button was clicked."
468 (interactive "e")
469
470 (mouse-set-point last-input-event)
471
472 (setq ada-context-menu-on-identifier
473 (and (char-after)
474 (or (= (char-syntax (char-after)) ?w)
475 (= (char-after) ?_))
476 (not (ada-in-string-or-comment-p))
477 (save-excursion (skip-syntax-forward "w")
478 (not (ada-after-keyword-p)))
479 ))
480 (popup-menu ada-context-menu)
481 )
482
483 (defun ada-indent-newline-indent ()
484 "insert a newline, indent the old and new lines."
485 (interactive "*")
486 ;; point may be in the middle of a word, so insert newline first,
487 ;; then go back and indent.
488 (insert "\n")
489 (forward-char -1)
490 (funcall indent-line-function)
491 (forward-char 1)
492 (funcall indent-line-function))
493
494 (defvar ada-indent-statement nil
495 ;; indentation function
496 "Function to indent the statement/declaration point is in or after.
497 Function is called with no arguments.")
498
499 (defun ada-indent-statement ()
500 "Indent current statement."
501 (interactive)
502 (when ada-indent-statement
503 (funcall ada-indent-statement)))
504
505 (defvar ada-expand nil
506 ;; skeleton function
507 "Function to call to expand tokens (ie insert skeletons).")
508
509 (defun ada-expand ()
510 "Expand previous word into a statement skeleton."
511 (interactive)
512 (when ada-expand
513 (funcall ada-expand)))
514
515 (defvar ada-next-placeholder nil
516 ;; skeleton function
517 "Function to call to goto next placeholder.")
518
519 (defun ada-next-placeholder ()
520 "Goto next placeholder.
521 Placeholders are defined by the skeleton backend."
522 (interactive)
523 (when ada-next-placeholder
524 (funcall ada-next-placeholder)))
525
526 (defvar ada-prev-placeholder nil
527 ;; skeleton function
528 "Function to call to goto previous placeholder.")
529
530 (defun ada-prev-placeholder ()
531 "Goto previous placeholder.
532 Placeholders are defined by the skeleton backend."
533 (interactive)
534 (when ada-prev-placeholder
535 (funcall ada-prev-placeholder)))
536
537 ;;;; abbrev, align
538
539 (defvar ada-mode-abbrev-table nil
540 "Local abbrev table for Ada mode.")
541
542 (defvar ada-align-rules
543 '((ada-declaration-assign
544 (regexp . "[^:]\\(\\s-*\\)\\(:\\)[^:]")
545 (valid . (lambda () (ada-align-valid)))
546 (repeat . t)
547 (modes . '(ada-mode)))
548 (ada-associate
549 (regexp . "[^=]\\(\\s-*\\)\\(=>\\)")
550 (valid . (lambda () (ada-align-valid)))
551 (modes . '(ada-mode)))
552 (ada-comment
553 (regexp . "\\(\\s-*\\)--")
554 (valid . (lambda () (ada-align-valid)))
555 (modes . '(ada-mode)))
556 (ada-use
557 (regexp . "\\(\\s-*\\)\\<\\(use\\s-\\)")
558 (valid . (lambda () (ada-align-valid)))
559 (modes . '(ada-mode)))
560 (ada-at
561 (regexp . "\\(\\s-+\\)\\(at\\)\\>")
562 (valid . (lambda () (ada-align-valid)))
563 (modes . '(ada-mode))))
564 "Rules to use to align different lines.")
565
566 (defun ada-align-valid ()
567 "See use in `ada-align-rules'."
568 (save-excursion
569 ;; we don't put "when (match-beginning n)" here; missing a match
570 ;; is a bug in the regexp.
571 (goto-char (or (match-beginning 2) (match-beginning 1)))
572 (not (ada-in-string-or-comment-p))))
573
574 (defconst ada-align-region-separate
575 (eval-when-compile
576 (concat
577 "^\\s-*\\($\\|\\("
578 "begin\\|"
579 "declare\\|"
580 "else\\|"
581 "end\\|"
582 "exception\\|"
583 "for\\|"
584 "function\\|"
585 "generic\\|"
586 "if\\|"
587 "is\\|"
588 "procedure\\|"
589 "private\\|"
590 "record\\|"
591 "return\\|"
592 "type\\|"
593 "when"
594 "\\)\\>\\)"))
595 "See the variable `align-region-separate' for more information.")
596
597 (defun ada-align ()
598 "If region is active, apply 'align'. If not, attempt to align
599 current construct."
600 (interactive)
601 (if (use-region-p)
602 (progn
603 (align (region-beginning) (region-end))
604 (deactivate-mark))
605
606 ;; else see if we are in a construct we know how to align
607 (cond
608 ((ada-in-paramlist-p)
609 (ada-format-paramlist))
610
611 (t
612 (align-current))
613 )))
614
615 (defvar ada-in-paramlist-p nil
616 ;; Supplied by indentation engine parser
617 "Function to return t if point is inside the parameter-list of a subprogram declaration.
618 Function is called with no arguments.")
619
620 (defun ada-in-paramlist-p ()
621 "Return t if point is inside the parameter-list of a subprogram declaration."
622 (when ada-in-paramlist-p
623 (funcall ada-in-paramlist-p)))
624
625 (defun ada-format-paramlist ()
626 "Reformat the parameter list point is in."
627 (interactive)
628 (ada-goto-open-paren)
629 (funcall indent-line-function); so new list is indented properly
630
631 (let* ((begin (point))
632 (delend (progn (forward-sexp) (point))); just after matching closing paren
633 (end (progn (backward-char) (forward-comment (- (point))) (point))); end of last parameter-declaration
634 (multi-line (> end (save-excursion (goto-char begin) (line-end-position))))
635 (paramlist (ada-scan-paramlist (1+ begin) end)))
636
637 (when paramlist
638 ;; delete the original parameter-list
639 (delete-region begin delend)
640
641 ;; insert the new parameter-list
642 (goto-char begin)
643 (if multi-line
644 (ada-insert-paramlist-multi-line paramlist)
645 (ada-insert-paramlist-single-line paramlist)))
646 ))
647
648 (defvar ada-scan-paramlist nil
649 ;; Supplied by indentation engine parser
650 "Function to scan a region, return a list of subprogram parameter declarations (in inverse declaration order).
651 Function is called with two args BEGIN END (the region).
652 Each parameter declaration is represented by a list
653 '((identifier ...) aliased-p in-p out-p not-null-p access-p constant-p protected-p type default)."
654 ;; Summary of Ada syntax for a parameter specification:
655 ;; ... : [aliased] {[in] | out | in out | [null_exclusion] access [constant | protected]} ...
656 )
657
658 (defun ada-scan-paramlist (begin end)
659 (when ada-scan-paramlist
660 (funcall ada-scan-paramlist begin end)))
661
662 (defun ada-insert-paramlist-multi-line (paramlist)
663 "Insert a multi-line formatted PARAMLIST in the buffer."
664 (let ((i (length paramlist))
665 param
666 j
667 len
668 (ident-len 0)
669 (type-len 0)
670 (aliased-p nil)
671 (in-p nil)
672 (out-p nil)
673 (not-null-p nil)
674 (access-p nil)
675 ident-col
676 colon-col
677 in-col
678 out-col
679 type-col
680 default-col)
681
682 ;; accumulate info across all params
683 (while (not (zerop i))
684 (setq i (1- i))
685 (setq param (nth i paramlist))
686
687 ;; identifier list
688 (setq len 0
689 j 0)
690 (mapc (lambda (ident)
691 (setq j (1+ j))
692 (setq len (+ len (length ident))))
693 (nth 0 param))
694 (setq len (+ len (* 2 (1- j)))); space for commas
695 (setq ident-len (max ident-len len))
696
697 ;; we align the defaults after the types that have defaults, not after all types.
698 ;; "constant", "protected" are treated as part of 'type'
699 (when (nth 9 param)
700 (setq type-len
701 (max type-len
702 (+ (length (nth 8 param))
703 (if (nth 6 param) 10 0); "constant "
704 (if (nth 7 param) 10 0); protected
705 ))))
706
707 (setq aliased-p (or aliased-p (nth 1 param)))
708 (setq in-p (or in-p (nth 2 param)))
709 (setq out-p (or out-p (nth 3 param)))
710 (setq not-null-p (or not-null-p (nth 4 param)))
711 (setq access-p (or access-p (nth 5 param)))
712 )
713
714 (let ((space-before-p (save-excursion (skip-chars-backward " \t") (not (bolp))))
715 (space-after-p (save-excursion (skip-chars-forward " \t") (not (or (= (char-after) ?\;) (eolp))))))
716 (when space-before-p
717 ;; paramlist starts on same line as subprogram identifier; clean
718 ;; up whitespace. Allow for code on same line as closing paren
719 ;; ('return' or ';').
720 (skip-syntax-forward " ")
721 (delete-char (- (skip-syntax-backward " ")))
722 (if space-after-p
723 (progn
724 (insert " ")
725 (forward-char -1))
726 (insert " "))
727 ))
728
729 (insert "(")
730
731 ;; compute columns.
732 (setq ident-col (current-column))
733 (setq colon-col (+ ident-col ident-len 1))
734 (setq in-col
735 (+ colon-col (if aliased-p 10 2))); ": aliased ..."
736 (setq out-col (+ in-col (if in-p 3 0))); ": [aliased] in "
737 (setq type-col
738 (+ in-col
739 (cond
740 ;; 'not null' without access is part of the type
741 ((and not-null-p access-p) 16); ": [aliased] not null access "
742 (access-p 7); ": [aliased] access "
743 ((and in-p out-p) 7); ": [aliased] in out "
744 (in-p 3); ": [aliased] in "
745 (out-p 4); ": [aliased] out "
746 (t 0)))); ": [aliased] "
747
748 (setq default-col (+ 1 type-col type-len))
749
750 (setq i (length paramlist))
751 (while (not (zerop i))
752 (setq i (1- i))
753 (setq param (nth i paramlist))
754
755 ;; insert identifiers, space and colon
756 (mapc (lambda (ident)
757 (insert ident)
758 (insert ", "))
759 (nth 0 param))
760 (delete-char -2); last ", "
761 (indent-to colon-col)
762 (insert ": ")
763
764 (when (nth 1 param)
765 (insert "aliased "))
766
767 (indent-to in-col)
768 (when (nth 2 param)
769 (insert "in "))
770
771 (when (nth 3 param)
772 (indent-to out-col)
773 (insert "out "))
774
775 (when (and (nth 4 param) ;; not null
776 (nth 5 param)) ;; access
777 (insert "not null access"))
778
779 (when (and (not (nth 4 param)) ;; not null
780 (nth 5 param)) ;; access
781 (insert "access"))
782
783 (indent-to type-col)
784
785 (when (and (nth 4 param) ;; not null
786 (not (nth 5 param))) ;; access
787 (insert "not null "))
788
789 (when (nth 6 param)
790 (insert "constant "))
791
792 (when (nth 7 param)
793 (insert "protected "))
794
795 (insert (nth 8 param)); type
796
797 (when (nth 9 param); default
798 (indent-to default-col)
799 (insert ":= ")
800 (insert (nth 9 param)))
801
802 (if (zerop i)
803 (insert ")")
804 (insert ";")
805 (newline)
806 (indent-to ident-col))
807 )
808 ))
809
810 (defun ada-insert-paramlist-single-line (paramlist)
811 "Insert a single-line formatted PARAMLIST in the buffer."
812 (let ((i (length paramlist))
813 param)
814
815 ;; clean up whitespace
816 (skip-syntax-forward " ")
817 (delete-char (- (skip-syntax-backward " ")))
818 (insert " (")
819
820 (setq i (length paramlist))
821 (while (not (zerop i))
822 (setq i (1- i))
823 (setq param (nth i paramlist))
824
825 ;; insert identifiers, space and colon
826 (mapc (lambda (ident)
827 (insert ident)
828 (insert ", "))
829 (nth 0 param))
830 (delete-char -2); last ", "
831
832 (insert " : ")
833
834 (when (nth 1 param)
835 (insert "aliased "))
836
837 (when (nth 2 param)
838 (insert "in "))
839
840 (when (nth 3 param)
841 (insert "out "))
842
843 (when (nth 4 param)
844 (insert "not null "))
845
846 (when (nth 5 param)
847 (insert "access "))
848
849 (when (nth 6 param)
850 (insert "constant "))
851 (when (nth 7 param)
852 (insert "protected "))
853 (insert (nth 8 param)); type
854
855 (when (nth 9 param); default
856 (insert " := ")
857 (insert (nth 9 param)))
858
859 (if (zerop i)
860 (if (= (char-after) ?\;)
861 (insert ")")
862 (insert ") "))
863 (insert "; "))
864 )
865 ))
866
867 (defvar ada-reset-parser nil
868 ;; Supplied by indentation engine parser
869 "Function to reset parser, to clear confused state."
870 )
871
872 (defun ada-reset-parser ()
873 (interactive)
874 (when ada-reset-parser
875 (funcall ada-reset-parser)))
876
877 (defvar ada-show-parse-error nil
878 ;; Supplied by indentation engine parser
879 "Function to show last error reported by indentation parser."
880 )
881
882 (defun ada-show-parse-error ()
883 (interactive)
884 (when ada-show-parse-error
885 (funcall ada-show-parse-error)))
886
887 ;;;; auto-casing
888
889 (defvar ada-case-full-exceptions '()
890 "Alist of words (entities) that have special casing, built from
891 project file casing file list full word exceptions. Indexed by
892 properly cased word; value is t.")
893
894 (defvar ada-case-partial-exceptions '()
895 "Alist of partial words that have special casing, built from
896 project casing files list partial word exceptions. Indexed by
897 properly cased word; value is t.")
898
899 (defun ada-case-show-files ()
900 "Show current casing files list."
901 (interactive)
902 (if (ada-prj-get 'casing)
903 (progn
904 (pop-to-buffer (get-buffer-create "*casing files*"))
905 (erase-buffer)
906 (dolist (file (ada-prj-get 'casing))
907 (insert (format "%s\n" file))))
908 (message "no casing files")
909 ))
910
911 (defun ada-case-save-exceptions (full-exceptions partial-exceptions file-name)
912 "Save FULL-EXCEPTIONS, PARTIAL-EXCEPTIONS to the file FILE-NAME."
913 (with-temp-file (expand-file-name file-name)
914 (mapc (lambda (x) (insert (car x) "\n"))
915 (sort (copy-sequence full-exceptions)
916 (lambda(a b) (string< (car a) (car b)))))
917 (mapc (lambda (x) (insert "*" (car x) "\n"))
918 (sort (copy-sequence partial-exceptions)
919 (lambda(a b) (string< (car a) (car b)))))
920 ))
921
922 (defun ada-case-read-exceptions (file-name)
923 "Read the content of the casing exception file FILE-NAME.
924 Return (cons full-exceptions partial-exceptions)."
925 (setq file-name (expand-file-name (substitute-in-file-name file-name)))
926 (if (file-readable-p file-name)
927 (let (full-exceptions partial-exceptions word)
928 (with-temp-buffer
929 (insert-file-contents file-name)
930 (while (not (eobp))
931
932 (setq word (buffer-substring-no-properties
933 (point) (save-excursion (skip-syntax-forward "w_") (point))))
934
935 (if (char-equal (string-to-char word) ?*)
936 ;; partial word exception
937 (progn
938 (setq word (substring word 1))
939 (unless (assoc-string word partial-exceptions t)
940 (push (cons word t) partial-exceptions)))
941
942 ;; full word exception
943 (unless (assoc-string word full-exceptions t)
944 (push (cons word t) full-exceptions)))
945
946 (forward-line 1))
947 )
948 (cons full-exceptions partial-exceptions))
949
950 ;; else file not readable; might be a new project with no
951 ;; exceptions yet, so just return empty pair
952 (message "'%s' is not a readable file." file-name)
953 '(nil . nil)
954 ))
955
956 (defun ada-case-merge-exceptions (result new)
957 "Merge NEW exeptions into RESULT.
958 An item in both lists has the RESULT value."
959 (dolist (item new)
960 (unless (assoc-string (car item) result t)
961 (push item result)))
962 result)
963
964 (defun ada-case-merge-all-exceptions (exceptions)
965 "Merge EXCEPTIONS into `ada-case-full-exceptions', `ada-case-partial-exceptions'."
966 (setq ada-case-full-exceptions (ada-case-merge-exceptions ada-case-full-exceptions (car exceptions)))
967 (setq ada-case-partial-exceptions (ada-case-merge-exceptions ada-case-partial-exceptions (cdr exceptions))))
968
969 (defun ada-case-read-all-exceptions ()
970 "Read case exceptions from all files in project casing files,
971 replacing current values of `ada-case-full-exceptions', `ada-case-partial-exceptions'."
972 (interactive)
973 (setq ada-case-full-exceptions '()
974 ada-case-partial-exceptions '())
975
976 (when (ada-prj-get 'casing)
977 (dolist (file (ada-prj-get 'casing))
978 (ada-case-merge-all-exceptions (ada-case-read-exceptions file))))
979 )
980
981 (defun ada-case-add-exception (word exceptions)
982 "Add case exception WORD to EXCEPTIONS, replacing current entry, if any."
983 (if (assoc-string word exceptions t)
984 (setcar (assoc-string word exceptions t) word)
985 (push (cons word t) exceptions))
986 exceptions)
987
988 (defun ada-case-create-exception (&optional word file-name partial)
989 "Define WORD as an exception for the casing system, save it in FILE-NAME.
990 If PARTIAL is non-nil, create a partial word exception. WORD
991 defaults to the active region, or the word at point. User is
992 prompted to choose a file from project variable casing if it is a
993 list."
994 (interactive)
995 (let ((casing (ada-prj-get 'casing)))
996 (setq file-name
997 (cond
998 (file-name file-name)
999
1000 ((< 1 (length casing))
1001 (completing-read "case exception file: " casing
1002 nil ;; predicate
1003 t ;; require-match
1004 nil ;; initial-input
1005 nil ;; hist
1006 (car casing) ;; default
1007 ))
1008 ((= 1 (length casing))
1009 (car casing))
1010
1011 (t
1012 (if ada-prj-current-file
1013 (error "No exception file specified; set `casing' in project file.")
1014 ;; IMPROVEME: could prompt, but then need to write to actual project file
1015 ;; (let ((temp
1016 ;; (read-file-name
1017 ;; "No exception file specified; adding to project. file: ")))
1018 ;; (message "remember to add %s to project file" temp)
1019 ;; (ada-prj-put 'casing temp)
1020 ;; temp)
1021 (error "No exception file specified, and no project active. See variable `ada-case-exception-file'.")))
1022 )))
1023
1024 (unless word
1025 (if (use-region-p)
1026 (setq word (buffer-substring-no-properties (region-beginning) (region-end)))
1027 (save-excursion
1028 (skip-syntax-backward "w_")
1029 (setq word
1030 (buffer-substring-no-properties
1031 (point)
1032 (progn (skip-syntax-forward "w_") (point))
1033 )))))
1034
1035 (let* ((exceptions (ada-case-read-exceptions file-name))
1036 (full-exceptions (car exceptions))
1037 (partial-exceptions (cdr exceptions)))
1038
1039 (cond
1040 ((null partial)
1041 (setq ada-case-full-exceptions (ada-case-add-exception word ada-case-full-exceptions))
1042 (setq full-exceptions (ada-case-add-exception word full-exceptions)))
1043
1044 (t
1045 (setq ada-case-partial-exceptions (ada-case-add-exception word ada-case-partial-exceptions))
1046 (setq partial-exceptions (ada-case-add-exception word partial-exceptions)))
1047 )
1048 (ada-case-save-exceptions full-exceptions partial-exceptions file-name)
1049 (message "created %s case exception '%s' in file '%s'"
1050 (if partial "partial" "full")
1051 word
1052 file-name)
1053 ))
1054
1055 (defun ada-case-create-partial-exception ()
1056 "Define active region or word at point as a partial word exception.
1057 User is prompted to choose a file from project variable casing if it is a list."
1058 (interactive)
1059 (ada-case-create-exception nil nil t))
1060
1061 (defun ada-in-numeric-literal-p ()
1062 "Return t if point is after a prefix of a numeric literal."
1063 ;; FIXME: this is actually a based numeric literal; excludes 1234
1064 (looking-back "\\([0-9]+#[0-9a-fA-F_]+\\)"))
1065
1066 (defvar ada-keywords nil
1067 "List of Ada keywords for current `ada-language-version'.")
1068
1069 (defun ada-after-keyword-p ()
1070 "Return non-nil if point is after an element of `ada-keywords'."
1071 (let ((word (buffer-substring-no-properties
1072 (save-excursion (skip-syntax-backward "w_") (point))
1073 (point))))
1074 (member (downcase word) ada-keywords)))
1075
1076 (defun ada-mixed-case (start end)
1077 "Adjust case of region START END to Mixed_Case."
1078 (let ((done nil)
1079 next)
1080 (if ada-case-strict
1081 (downcase-region start end))
1082 (goto-char start)
1083 (while (not done)
1084 (setq next
1085 (or
1086 (save-excursion (when (search-forward "_" end t) (point-marker)))
1087 (copy-marker (1+ end))))
1088
1089 ;; upcase first char
1090 (upcase-region (point) (1+ (point)))
1091
1092 (goto-char next)
1093 (if (< (point) end)
1094 (setq start (point))
1095 (setq done t))
1096 )))
1097
1098 (defun ada-case-adjust-identifier ()
1099 "Adjust case of the previous word as an identifier.
1100 Uses `ada-case-identifier', with exceptions defined in
1101 `ada-case-full-exceptions', `ada-case-partial-exceptions'."
1102 (interactive)
1103 (save-excursion
1104 (let ((end (point-marker))
1105 (start (progn (skip-syntax-backward "w_") (point)))
1106 match
1107 next
1108 (done nil))
1109
1110 (if (setq match (assoc-string (buffer-substring-no-properties start end) ada-case-full-exceptions t))
1111 ;; full word exception
1112 (progn
1113 ;; 'save-excursion' puts a marker at 'end'; if we do
1114 ;; 'delete-region' first, it moves that marker to 'start',
1115 ;; then 'insert' inserts replacement text after the
1116 ;; marker, defeating 'save-excursion'. So we do 'insert' first.
1117 (insert (car match))
1118 (delete-region (point) end))
1119
1120 ;; else apply ada-case-identifier
1121 (funcall ada-case-identifier start end)
1122
1123 ;; apply partial-exceptions
1124 (goto-char start)
1125 (while (not done)
1126 (setq next
1127 (or
1128 (save-excursion (when (search-forward "_" end t) (point-marker)))
1129 (copy-marker (1+ end))))
1130
1131 (when (setq match (assoc-string (buffer-substring-no-properties start (1- next))
1132 ada-case-partial-exceptions t))
1133 ;; see comment above at 'full word exception' for why
1134 ;; we do insert first.
1135 (insert (car match))
1136 (delete-region (point) (1- next)))
1137
1138 (goto-char next)
1139 (if (< (point) end)
1140 (setq start (point))
1141 (setq done t))
1142 )))))
1143
1144 (defun ada-case-adjust (&optional typed-char in-comment)
1145 "Adjust the case of the word before point.
1146 When invoked interactively, TYPED-CHAR must be
1147 `last-command-event', and it must not have been inserted yet.
1148 If IN-COMMENT is non-nil, adjust case of words in comments and strings as code."
1149 (when (not (bobp))
1150 (when (save-excursion
1151 (forward-char -1); back to last character in word
1152 (and (not (bobp))
1153 (eq (char-syntax (char-after)) ?w); it can be capitalized
1154
1155 (not (and (eq typed-char ?')
1156 (eq (char-before (point)) ?'))); character literal
1157
1158 (or in-comment
1159 (not (ada-in-string-or-comment-p)))
1160 ;; we sometimes want to capitialize an Ada identifier
1161 ;; referenced in a comment, via
1162 ;; ada-case-adjust-at-point.
1163
1164 (not (ada-in-numeric-literal-p))
1165 ))
1166
1167 ;; The indentation engine may trigger a reparse on
1168 ;; non-whitespace changes, but we know we don't need to reparse
1169 ;; for this change (assuming the user has not abused case
1170 ;; exceptions!).
1171 (let ((inhibit-modification-hooks t))
1172 (cond
1173 ;; Some attributes are also keywords, but captialized as
1174 ;; attributes. So check for attribute first.
1175 ((and
1176 (not in-comment)
1177 (save-excursion
1178 (skip-syntax-backward "w_")
1179 (eq (char-before) ?')))
1180 (ada-case-adjust-identifier))
1181
1182 ((and
1183 (not in-comment)
1184 (not (eq typed-char ?_))
1185 (ada-after-keyword-p))
1186 (funcall ada-case-keyword -1))
1187
1188 (t (ada-case-adjust-identifier))
1189 ))
1190 )))
1191
1192 (defun ada-case-adjust-at-point (&optional in-comment)
1193 "Adjust case of word at point, move to end of word.
1194 With prefix arg, adjust case as code even if in comment;
1195 otherwise, capitalize words in comments."
1196 (interactive "P")
1197 (cond
1198 ((and (not in-comment)
1199 (ada-in-string-or-comment-p))
1200 (skip-syntax-backward "w_")
1201 (capitalize-word 1))
1202
1203 (t
1204 (when
1205 (and (not (eobp))
1206 ;; we use '(syntax-after (point))' here, not '(char-syntax
1207 ;; (char-after))', because the latter does not respect
1208 ;; ada-syntax-propertize.
1209 (memq (syntax-class (syntax-after (point))) '(2 3)))
1210 (skip-syntax-forward "w_"))
1211 (ada-case-adjust nil in-comment))
1212 ))
1213
1214 (defun ada-case-adjust-region (begin end)
1215 "Adjust case of all words in region BEGIN END."
1216 (interactive "r")
1217 (narrow-to-region begin end)
1218 (save-excursion
1219 (goto-char begin)
1220 (while (not (eobp))
1221 (forward-comment (point-max))
1222 (skip-syntax-forward "^w_")
1223 (skip-syntax-forward "w_")
1224 (ada-case-adjust)))
1225 (widen))
1226
1227 (defun ada-case-adjust-buffer ()
1228 "Adjust case of current buffer."
1229 (interactive)
1230 (ada-case-adjust-region (point-min) (point-max)))
1231
1232 (defun ada-case-adjust-interactive (arg)
1233 "If `ada-auto-case' is non-nil, adjust the case of the previous word, and process the character just typed.
1234 To be bound to keys that should cause auto-casing.
1235 ARG is the prefix the user entered with \\[universal-argument]."
1236 (interactive "P")
1237
1238 ;; character typed has not been inserted yet
1239 (let ((lastk last-command-event))
1240
1241 (cond
1242 ((eq lastk ?\n)
1243 (when ada-auto-case
1244 (ada-case-adjust lastk))
1245 (funcall ada-lfd-binding))
1246
1247 ((memq lastk '(?\r return))
1248 (when ada-auto-case
1249 (ada-case-adjust lastk))
1250 (funcall ada-ret-binding))
1251
1252 (t
1253 (when ada-auto-case
1254 (ada-case-adjust lastk))
1255 (self-insert-command (prefix-numeric-value arg)))
1256 )))
1257
1258 ;;;; project files
1259
1260 ;; An Emacs Ada mode project file can specify several things:
1261 ;;
1262 ;; - a compiler-specific project file
1263 ;;
1264 ;; - compiler-specific environment variables
1265 ;;
1266 ;; - other compiler-specific things (see the compiler support elisp code)
1267 ;;
1268 ;; - a list of source directories (in addition to those specified in the compiler project file)
1269 ;;
1270 ;; - a casing exception file
1271 ;;
1272 ;; All of the data used by Emacs Ada mode functions specified in a
1273 ;; project file is stored in a property list. The property list is
1274 ;; stored in an alist indexed by the project file name, so multiple
1275 ;; project files can be selected without re-parsing them (some
1276 ;; compiler project files can take a long time to parse).
1277
1278 (defvar ada-prj-alist nil
1279 "Alist holding currently parsed Emacs Ada project files. Indexed by absolute project file name.")
1280
1281 (defvar ada-prj-current-file nil
1282 "Current Emacs Ada project file.")
1283
1284 (defvar ada-prj-current-project nil
1285 "Current Emacs Ada mode project; a plist.")
1286
1287 (defun ada-prj-get (prop &optional plist)
1288 "Return value of PROP in PLIST.
1289 Optional PLIST defaults to `ada-prj-current-project'."
1290 (let ((prj (or plist ada-prj-current-project)))
1291 (if prj
1292 (plist-get prj prop)
1293
1294 ;; no project, just use default vars
1295 ;; must match code in ada-prj-default
1296 (cl-case prop
1297 (ada_compiler ada-compiler)
1298 (auto_case ada-auto-case)
1299 (case_keyword ada-case-keyword)
1300 (case_identifier ada-case-identifier)
1301 (case_strict ada-case-strict)
1302 (casing (if (listp ada-case-exception-file)
1303 ada-case-exception-file
1304 (list ada-case-exception-file)))
1305 (path_sep path-separator)
1306 (proc_env process-environment)
1307 (src_dir (list "."))
1308 (xref_tool ada-xref-tool)
1309 ))))
1310
1311 (defun ada-prj-put (prop val &optional plist)
1312 "Set value of PROP in PLIST to VAL.
1313 Optional PLIST defaults to `ada-prj-current-project'."
1314 (plist-put (or plist ada-prj-current-project) prop val))
1315
1316 (defun ada-require-project-file ()
1317 (unless ada-prj-current-file
1318 (error "no Emacs Ada project file specified")))
1319
1320 (defvar ada-prj-default-list nil
1321 ;; project file parse
1322 "List of functions to add default project variables. Called
1323 with one argument; the default project properties list. Function
1324 should add to the properties list and return it.")
1325
1326 (defvar ada-prj-default-compiler-alist nil
1327 ;; project file parse
1328 "Compiler-specific function to set default project variables.
1329 Indexed by ada-compiler. Called with one argument; the default
1330 project properties list. Function should add to the properties
1331 list and return it.")
1332
1333 (defvar ada-prj-default-xref-alist nil
1334 ;; project file parse
1335 "Xref-tool-specific function to set default project variables.
1336 Indexed by ada-xref-tool. Called with one argument; the default
1337 project properties list. Function should add to the properties
1338 list and return it.")
1339
1340 (defun ada-prj-default (&optional src-dir)
1341 "Return the default project properties list.
1342 If SRC-DIR is non-nil, use it as the default for src_dir.
1343 Include properties set via `ada-prj-default-compiler-alist',
1344 `ada-prj-default-xref-alist'."
1345
1346 (let (project func)
1347 (setq
1348 project
1349 (list
1350 ;; variable name alphabetical order
1351 'ada_compiler ada-compiler
1352 'auto_case ada-auto-case
1353 'case_keyword ada-case-keyword
1354 'case_identifier ada-case-identifier
1355 'case_strict ada-case-strict
1356 'casing (if (listp ada-case-exception-file)
1357 ada-case-exception-file
1358 (list ada-case-exception-file))
1359 'path_sep path-separator;; prj variable so users can override it for their compiler
1360 'proc_env process-environment
1361 'src_dir (list (if src-dir src-dir "."))
1362 'xref_tool ada-xref-tool
1363 ))
1364
1365 (cl-dolist (func ada-prj-default-list)
1366 (setq project (funcall func project)))
1367
1368 (setq func (cdr (assq ada-compiler ada-prj-default-compiler-alist)))
1369 (when func (setq project (funcall func project)))
1370 (setq func (cdr (assq ada-xref-tool ada-prj-default-xref-alist)))
1371 (when func (setq project (funcall func project)))
1372 project))
1373
1374 (defvar ada-prj-parser-alist
1375 (mapcar
1376 (lambda (ext) (cons ext 'ada-prj-parse-file-1))
1377 ada-prj-file-extensions)
1378 ;; project file parse
1379 "Alist of parsers for project files, indexed by file extension.
1380 Default provides the minimal Ada mode parser; compiler support
1381 code may add other parsers. Parser is called with two arguments;
1382 the project file name and the current project property
1383 list. Parser must modify or add to the property list and return it.")
1384
1385 ;; This autoloaded because it is often used in Makefiles, and thus
1386 ;; will be the first ada-mode function executed.
1387 ;;;###autoload
1388 (defun ada-parse-prj-file (prj-file)
1389 "Read Emacs Ada or compiler-specific project file PRJ-FILE, set project properties in `ada-prj-alist'."
1390 ;; Not called ada-prj-parse-file for Ada mode 4.01 compatibility
1391 ;; FIXME: use the right name, add an alias
1392 (let ((project (ada-prj-default))
1393 (parser (cdr (assoc (file-name-extension prj-file) ada-prj-parser-alist))))
1394
1395 (setq prj-file (expand-file-name prj-file))
1396
1397 (unless (file-readable-p prj-file)
1398 (error "Project file '%s' is not readable" prj-file))
1399
1400 (if parser
1401 ;; parser may reference the "current project", so bind that now.
1402 (let ((ada-prj-current-project project)
1403 (ada-prj-current-file prj-file))
1404 (setq project (funcall parser prj-file project)))
1405 (error "no project file parser defined for '%s'" prj-file))
1406
1407 ;; Store the project properties
1408 (if (assoc prj-file ada-prj-alist)
1409 (setcdr (assoc prj-file ada-prj-alist) project)
1410 (add-to-list 'ada-prj-alist (cons prj-file project)))
1411
1412 ;; return t for interactive use
1413 t))
1414
1415 (defun ada-prj-reparse-select-current ()
1416 "Reparse the current project file, re-select it.
1417 Useful when the project file has been edited."
1418 (ada-parse-prj-file ada-prj-current-file)
1419 (ada-select-prj-file ada-prj-current-file))
1420
1421 (defvar ada-prj-parse-one-compiler nil
1422 ;; project file parse
1423 "Compiler-specific function to process one Ada project property.
1424 Indexed by project variable ada_compiler.
1425 Called with three arguments; the property name, property value,
1426 and project properties list. Function should add to or modify the
1427 properties list and return it, or return nil if the name is not
1428 recognized.")
1429
1430 (defvar ada-prj-parse-one-xref nil
1431 ;; project file parse
1432 "Xref-tool-specific function to process one Ada project property.
1433 Indexed by project variable xref_tool.
1434 Called with three arguments; the property name, property value,
1435 and project properties list. Function should add to or modify the
1436 properties list and return it, or return nil if the name is not
1437 recognized.")
1438
1439 (defvar ada-prj-parse-final-compiler nil
1440 ;; project file parse
1441 "Alist of compiler-specific functions to finish processing Ada project properties.
1442 Indexed by project variable ada_compiler.
1443 Called with one argument; the project properties list. Function
1444 should add to or modify the list and return it.")
1445
1446 (defvar ada-prj-parse-final-xref nil
1447 ;; project file parse
1448 "Alist of xref-tool-specific functions to finish processing Ada project properties.
1449 Indexed by project variable xref_tool.
1450 Called with one argument; the project properties list. Function
1451 should add to or modify the list and return it.")
1452
1453 (defun ada-prj-parse-file-1 (prj-file project)
1454 "Parse the Ada mode project file PRJ-FILE, set project properties in PROJECT.
1455 Return new value of PROJECT."
1456 (let (;; fields that are lists or that otherwise require special processing
1457 casing src_dir
1458 tmp-prj
1459 (parse-one-compiler (cdr (assoc ada-compiler ada-prj-parse-one-compiler)))
1460 (parse-final-compiler (cdr (assoc ada-compiler ada-prj-parse-final-compiler)))
1461 (parse-one-xref (cdr (assoc ada-xref-tool ada-prj-parse-one-xref)))
1462 (parse-final-xref (cdr (assoc ada-xref-tool ada-prj-parse-final-xref))))
1463
1464 (with-current-buffer (find-file-noselect prj-file)
1465 (goto-char (point-min))
1466
1467 ;; process each line
1468 (while (not (eobp))
1469
1470 ;; ignore lines that don't have the format "name=value", put
1471 ;; 'name', 'value' in match-string.
1472 (when (looking-at "^\\([^=\n]+\\)=\\(.*\\)")
1473 (cond
1474 ;; variable name alphabetical order
1475
1476 ((string= (match-string 1) "ada_compiler")
1477 (let ((comp (intern (match-string 2))))
1478 (setq project (plist-put project 'ada_compiler comp))
1479 (setq parse-one-compiler (cdr (assq comp ada-prj-parse-one-compiler)))
1480 (setq parse-final-compiler (cdr (assq comp ada-prj-parse-final-compiler)))))
1481
1482 ((string= (match-string 1) "auto_case")
1483 (setq project (plist-put project 'auto_case (intern (match-string 2)))))
1484
1485 ((string= (match-string 1) "case_keyword")
1486 (setq project (plist-put project 'case_keyword (intern (match-string 2)))))
1487
1488 ((string= (match-string 1) "case_identifier")
1489 (setq project (plist-put project 'case_identifier (intern (match-string 2)))))
1490
1491 ((string= (match-string 1) "case_strict")
1492 (setq project (plist-put project 'case_strict (intern (match-string 2)))))
1493
1494 ((string= (match-string 1) "casing")
1495 (cl-pushnew (expand-file-name
1496 (substitute-in-file-name (match-string 2)))
1497 casing :test #'equal))
1498
1499 ((string= (match-string 1) "el_file")
1500 (let ((file (expand-file-name (substitute-in-file-name (match-string 2)))))
1501 (setq project (plist-put project 'el_file file))
1502 ;; eval now as well as in select, since it might affect parsing
1503 (load-file file)))
1504
1505 ((string= (match-string 1) "src_dir")
1506 (cl-pushnew (file-name-as-directory
1507 (expand-file-name (match-string 2)))
1508 src_dir :test #'equal))
1509
1510 ((string= (match-string 1) "xref_tool")
1511 (let ((xref (intern (match-string 2))))
1512 (setq project (plist-put project 'xref_tool xref))
1513 (setq parse-one-xref (cdr (assq xref ada-prj-parse-one-xref)))
1514 (setq parse-final-xref (cdr (assq xref ada-prj-parse-final-xref)))))
1515
1516 (t
1517 (if (or
1518 (and parse-one-compiler
1519 (setq tmp-prj (funcall parse-one-compiler (match-string 1) (match-string 2) project)))
1520 (and parse-one-xref
1521 (setq tmp-prj (funcall parse-one-xref (match-string 1) (match-string 2) project))))
1522
1523 (setq project tmp-prj)
1524
1525 ;; Any other field in the file is set as an environment
1526 ;; variable or a project file.
1527 (if (= ?$ (elt (match-string 1) 0))
1528 ;; process env var. We don't do expand-file-name
1529 ;; here because the application may be expecting a
1530 ;; simple string.
1531 (let ((process-environment (plist-get project 'proc_env)))
1532 (setenv (substring (match-string 1) 1)
1533 (substitute-in-file-name (match-string 2)))
1534 (setq project
1535 (plist-put project 'proc_env process-environment)))
1536
1537 ;; not recognized; assume it is a user-defined variable like "comp_opt"
1538 (setq project (plist-put project (intern (match-string 1)) (match-string 2)))
1539 )))
1540 ))
1541
1542 (forward-line 1))
1543
1544 );; done reading file
1545
1546 ;; process accumulated lists
1547 (if casing (setq project (plist-put project 'casing (reverse casing))))
1548 (if src_dir (setq project (plist-put project 'src_dir (reverse src_dir))))
1549
1550 (when parse-final-compiler
1551 ;; parse-final-compiler may reference the "current project", so
1552 ;; bind that now, to include the properties set above.
1553 (let ((ada-prj-current-project project)
1554 (ada-prj-current-file prj-file))
1555 (setq project (funcall parse-final-compiler project))))
1556
1557 (when parse-final-xref
1558 (let ((ada-prj-current-project project)
1559 (ada-prj-current-file prj-file))
1560 (setq project (funcall parse-final-xref project))))
1561
1562 project
1563 ))
1564
1565 (defvar ada-select-prj-compiler nil
1566 "Alist of functions to call for compiler specific project file selection.
1567 Indexed by project variable ada_compiler.")
1568
1569 (defvar ada-deselect-prj-compiler nil
1570 "Alist of functions to call for compiler specific project file deselection.
1571 Indexed by project variable ada_compiler.")
1572
1573 (defvar ada-select-prj-xref-tool nil
1574 "Alist of functions to call for xref-tool specific project file selection.
1575 Indexed by project variable xref_tool.")
1576
1577 (defvar ada-deselect-prj-xref-tool nil
1578 "Alist of functions to call for xref-tool specific project file deselection.
1579 Indexed by project variable xref_tool.")
1580
1581 (defun ada-select-prj-file (prj-file)
1582 "Select PRJ-FILE as the current project file."
1583 (interactive)
1584 (setq prj-file (expand-file-name prj-file))
1585
1586 (setq ada-prj-current-project (cdr (assoc prj-file ada-prj-alist)))
1587
1588 (when (null ada-prj-current-project)
1589 (setq ada-prj-current-file nil)
1590 (error "Project file '%s' was not previously parsed." prj-file))
1591
1592 (let ((func (cdr (assq (ada-prj-get 'ada_compiler) ada-deselect-prj-compiler))))
1593 (when func (funcall func)))
1594
1595 (let ((func (cdr (assq (ada-prj-get 'xref_tool) ada-deselect-prj-xref-tool))))
1596 (when func (funcall func)))
1597
1598 (setq ada-prj-current-file prj-file)
1599
1600 ;; Project file should fully specify what compilers are used,
1601 ;; including what compilation filters they need. There may be more
1602 ;; than just an Ada compiler.
1603 (setq compilation-error-regexp-alist nil)
1604 (setq compilation-filter-hook nil)
1605
1606 (when (ada-prj-get 'el_file)
1607 (load-file (ada-prj-get 'el_file)))
1608
1609 (ada-case-read-all-exceptions)
1610
1611 (setq compilation-search-path (ada-prj-get 'src_dir))
1612
1613 (let ((func (cdr (assq (ada-prj-get 'ada_compiler) ada-select-prj-compiler))))
1614 (when func (funcall func)))
1615
1616 (let ((func (cdr (assq (ada-prj-get 'xref_tool) ada-select-prj-xref-tool))))
1617 (when func (funcall func)))
1618
1619 ;; return 't', for decent display in message buffer when called interactively
1620 t)
1621
1622 (defun ada-create-select-default-prj (&optional directory)
1623 "Create a default project with src_dir set to DIRECTORY (default current directory), select it."
1624 (let* ((dir (or directory default-directory))
1625 (prj-file (expand-file-name "default_.adp" dir))
1626 (project (ada-prj-default dir)))
1627
1628 (if (assoc prj-file ada-prj-alist)
1629 (setcdr (assoc prj-file ada-prj-alist) project)
1630 (add-to-list 'ada-prj-alist (cons prj-file project)))
1631
1632 (ada-select-prj-file prj-file)
1633 ))
1634
1635 (defun ada-prj-select ()
1636 "Select the current project file from the list of currently available project files."
1637 (interactive)
1638 (ada-select-prj-file (completing-read "project: " ada-prj-alist nil t))
1639 )
1640
1641 (defun ada-prj-show ()
1642 "Show current Emacs Ada mode project file."
1643 (interactive)
1644 (message "current Emacs Ada mode project file: %s" ada-prj-current-file))
1645
1646 (defvar ada-prj-show-path nil
1647 ;; Supplied by compiler
1648 "Function to show project search path used by compiler (and possibly xref tool)."
1649 )
1650
1651 (defun ada-prj-show-path ()
1652 (interactive)
1653 (when ada-prj-show-path
1654 (funcall ada-prj-show-path)))
1655
1656 (defvar ada-show-xref-tool-buffer nil
1657 ;; Supplied by xref tool
1658 "Function to show process buffer used by xref tool."
1659 )
1660
1661 (defun ada-show-xref-tool-buffer ()
1662 (interactive)
1663 (when ada-show-xref-tool-buffer
1664 (funcall ada-show-xref-tool-buffer)))
1665
1666 ;;;; syntax properties
1667
1668 (defvar ada-mode-syntax-table
1669 (let ((table (make-syntax-table)))
1670 ;; (info "(elisp)Syntax Class Table" "*info syntax class table*")
1671 ;; make-syntax-table sets all alphanumeric to w, etc; so we only
1672 ;; have to add ada-specific things.
1673
1674 ;; string brackets. `%' is the obsolete alternative string
1675 ;; bracket (arm J.2); if we make it syntax class ", it throws
1676 ;; font-lock and indentation off the track, so we use syntax class
1677 ;; $.
1678 (modify-syntax-entry ?% "$" table)
1679 (modify-syntax-entry ?\" "\"" table)
1680
1681 ;; punctuation; operators etc
1682 (modify-syntax-entry ?# "." table); based number - ada-wisi-number-literal-p requires this syntax
1683 (modify-syntax-entry ?& "." table)
1684 (modify-syntax-entry ?* "." table)
1685 (modify-syntax-entry ?+ "." table)
1686 (modify-syntax-entry ?- ". 12" table); operator; see ada-syntax-propertize for double hyphen as comment
1687 (modify-syntax-entry ?. "." table)
1688 (modify-syntax-entry ?/ "." table)
1689 (modify-syntax-entry ?: "." table)
1690 (modify-syntax-entry ?< "." table)
1691 (modify-syntax-entry ?= "." table)
1692 (modify-syntax-entry ?> "." table)
1693 (modify-syntax-entry ?\' "." table); attribute; see ada-syntax-propertize for character literal
1694 (modify-syntax-entry ?\; "." table)
1695 (modify-syntax-entry ?\\ "." table); default is escape; not correct for Ada strings
1696 (modify-syntax-entry ?\| "." table)
1697
1698 ;; and \f and \n end a comment
1699 (modify-syntax-entry ?\f ">" table)
1700 (modify-syntax-entry ?\n ">" table)
1701
1702 (modify-syntax-entry ?_ "_" table); symbol constituents, not word.
1703
1704 (modify-syntax-entry ?\( "()" table)
1705 (modify-syntax-entry ?\) ")(" table)
1706
1707 ;; skeleton placeholder delimiters; see ada-skel.el. We use generic
1708 ;; comment delimiter class, not comment starter/comment ender, so
1709 ;; these can be distinguished from line end.
1710 (modify-syntax-entry ?{ "!" table)
1711 (modify-syntax-entry ?} "!" table)
1712
1713 table
1714 )
1715 "Syntax table to be used for editing Ada source code.")
1716
1717 (defvar ada-syntax-propertize-hook nil
1718 ;; provided by preprocessor, lumped with xref-tool
1719 "Hook run from `ada-syntax-propertize'.
1720 Called by `syntax-propertize', which is called by font-lock in
1721 `after-change-functions'. Therefore, care must be taken to avoid
1722 race conditions with the grammar parser.")
1723
1724 (defun ada-syntax-propertize (start end)
1725 "Assign `syntax-table' properties in accessible part of buffer.
1726 In particular, character constants are set to have string syntax."
1727 ;; (info "(elisp)Syntax Properties")
1728 ;;
1729 ;; called from `syntax-propertize', inside save-excursion with-silent-modifications
1730 (let ((inhibit-read-only t)
1731 (inhibit-point-motion-hooks t))
1732 (goto-char start)
1733 (save-match-data
1734 (while (re-search-forward
1735 (concat
1736 "[^a-zA-Z0-9)]\\('\\)[^'\n]\\('\\)"; 1, 2: character literal, not attribute
1737 "\\|[^a-zA-Z0-9)]\\('''\\)"; 3: character literal '''
1738 "\\|\\(--\\)"; 4: comment start
1739 )
1740 end t)
1741 ;; syntax-propertize-extend-region-functions is set to
1742 ;; syntax-propertize-wholelines by default. We assume no
1743 ;; coding standard will permit a character literal at the
1744 ;; start of a line (not preceded by whitespace).
1745 (cond
1746 ((match-beginning 1)
1747 (put-text-property
1748 (match-beginning 1) (match-end 1) 'syntax-table '(7 . ?'))
1749 (put-text-property
1750 (match-beginning 2) (match-end 2) 'syntax-table '(7 . ?')))
1751 ((match-beginning 3)
1752 (put-text-property
1753 (match-beginning 3) (1+ (match-beginning 3)) 'syntax-table '(7 . ?'))
1754 (put-text-property
1755 (1- (match-end 3)) (match-end 3) 'syntax-table '(7 . ?')))
1756 ((match-beginning 4)
1757 (put-text-property
1758 (match-beginning 4) (match-end 4) 'syntax-table '(11 . nil)))
1759 )))
1760 (run-hook-with-args 'ada-syntax-propertize-hook start end))
1761 )
1762
1763 (defun ada-in-comment-p (&optional parse-result)
1764 "Return t if inside a comment.
1765 If PARSE-RESULT is non-nil, use it instead of calling `syntax-ppss'."
1766 (nth 4 (or parse-result (syntax-ppss))))
1767
1768 (defun ada-in-string-p (&optional parse-result)
1769 "Return t if point is inside a string.
1770 If PARSE-RESULT is non-nil, use it instead of calling `syntax-ppss'."
1771 (nth 3 (or parse-result (syntax-ppss))))
1772
1773 (defun ada-in-string-or-comment-p (&optional parse-result)
1774 "Return t if inside a comment or string.
1775 If PARSE-RESULT is non-nil, use it instead of calling `syntax-ppss'."
1776 (setq parse-result (or parse-result (syntax-ppss)))
1777 (or (ada-in-string-p parse-result) (ada-in-comment-p parse-result)))
1778
1779 (defun ada-in-paren-p (&optional parse-result)
1780 "Return t if point is inside a pair of parentheses.
1781 If PARSE-RESULT is non-nil, use it instead of calling `syntax-ppss'."
1782 (> (nth 0 (or parse-result (syntax-ppss))) 0))
1783
1784 (defun ada-goto-open-paren (&optional offset parse-result)
1785 "Move point to innermost opening paren surrounding current point, plus OFFSET.
1786 Throw error if not in paren. If PARSE-RESULT is non-nil, use it
1787 instead of calling `syntax-ppss'."
1788 (goto-char (+ (or offset 0) (nth 1 (or parse-result (syntax-ppss))))))
1789
1790 ;;;; navigation within and between files
1791
1792 (defvar ada-body-suffixes '(".adb")
1793 "List of possible suffixes for Ada body files.
1794 The extensions should include a `.' if needed.")
1795
1796 (defvar ada-spec-suffixes '(".ads")
1797 "List of possible suffixes for Ada spec files.
1798 The extensions should include a `.' if needed.")
1799
1800 (defvar ada-other-file-alist
1801 '(("\\.ads$" (".adb"))
1802 ("\\.adb$" (".ads")))
1803 "Alist used by `find-file' to find the name of the other package.
1804 See `ff-other-file-alist'.")
1805
1806 (defconst ada-name-regexp
1807 "\\(\\(?:\\sw\\|[_.]\\)+\\)")
1808
1809 (defconst ada-parent-name-regexp
1810 "\\([a-zA-Z0-9_\\.]+\\)\\.[a-zA-Z0-9_]+"
1811 "Regexp for extracting the parent name from fully-qualified name.")
1812
1813 (defvar ada-file-name-from-ada-name nil
1814 ;; determined by ada-xref-tool, set by *-select-prj
1815 "Function called with one parameter ADA-NAME, which is a library
1816 unit name; it should return the filename in which ADA-NAME is
1817 found.")
1818
1819 (defun ada-file-name-from-ada-name (ada-name)
1820 "Return the filename in which ADA-NAME is found."
1821 (ada-require-project-file)
1822 (funcall ada-file-name-from-ada-name ada-name))
1823
1824 (defvar ada-ada-name-from-file-name nil
1825 ;; supplied by compiler
1826 "Function called with one parameter FILE-NAME, which is a library
1827 unit name; it should return the Ada name that should be found in FILE-NAME.")
1828
1829 (defun ada-ada-name-from-file-name (file-name)
1830 "Return the ada-name that should be found in FILE-NAME."
1831 (ada-require-project-file)
1832 (funcall ada-ada-name-from-file-name file-name))
1833
1834 (defun ada-ff-special-extract-parent ()
1835 (setq ff-function-name (match-string 1))
1836 (file-name-nondirectory
1837 (or
1838 (ff-get-file-name
1839 compilation-search-path
1840 (ada-file-name-from-ada-name ff-function-name)
1841 ada-spec-suffixes)
1842 (error "parent '%s' not found; set project file?" ff-function-name))))
1843
1844 (defun ada-ff-special-with ()
1845 (let ((package-name (match-string 1)))
1846 (setq ff-function-name (concat "^package\\s-+" package-name "\\([^_]\\|$\\)"))
1847 (file-name-nondirectory
1848 (or
1849 (ff-get-file-name
1850 compilation-search-path
1851 (ada-file-name-from-ada-name package-name)
1852 (append ada-spec-suffixes ada-body-suffixes))
1853 (error "package '%s' not found; set project file?" package-name)))
1854 ))
1855
1856 (defun ada-set-ff-special-constructs ()
1857 "Add Ada-specific pairs to `ff-special-constructs'."
1858 (set (make-local-variable 'ff-special-constructs) nil)
1859 (mapc (lambda (pair) (add-to-list 'ff-special-constructs pair))
1860 ;; Each car is a regexp; if it matches at point, the cdr is invoked.
1861 ;; Each cdr should set ff-function-name to a string or regexp
1862 ;; for ada-set-point-accordingly, and return the file name
1863 ;; (sans directory, must include suffix) to go to.
1864 (list
1865 ;; Top level child package declaration (not body), or child
1866 ;; subprogram declaration or body; go to the parent package.
1867 (cons (concat "^\\(?:private[ \t]+\\)?\\(?:package\\|procedure\\|function\\)[ \t]+"
1868 ada-parent-name-regexp "\\(?:;\\|[ \t]+\\|$\\)")
1869 'ada-ff-special-extract-parent)
1870
1871 ;; A "with" clause. Note that it may refer to a procedure body, as well as a spec
1872 (cons (concat "^\\(?:limited[ \t]+\\)?\\(?:private[ \t]+\\)?with[ \t]+" ada-name-regexp)
1873 'ada-ff-special-with)
1874 )))
1875
1876 (defvar ada-which-function nil
1877 ;; supplied by indentation engine
1878 ;;
1879 ;; This is run from ff-pre-load-hook, so ff-function-name may have
1880 ;; been set by ff-treat-special; don't reset it.
1881 "Function called with no parameters; it should return the name
1882 of the package, protected type, subprogram, or task type whose
1883 definition/declaration point is in or just after, or nil. In
1884 addition, if ff-function-name is non-nil, store in
1885 ff-function-name a regexp that will find the function in the
1886 other file.")
1887
1888 (defun ada-which-function ()
1889 "See `ada-which-function' variable."
1890 (interactive)
1891 (when ada-which-function
1892 (funcall ada-which-function)))
1893
1894 (defvar ada-on-context-clause nil
1895 ;; supplied by indentation engine
1896 "Function called with no parameters; it should return non-nil
1897 if point is on a context clause.")
1898
1899 (defun ada-on-context-clause ()
1900 "See `ada-on-context-clause' variable."
1901 (interactive)
1902 (when ada-on-context-clause
1903 (funcall ada-on-context-clause)))
1904
1905 (defvar ada-goto-subunit-name nil
1906 ;; supplied by indentation engine
1907 "Function called with no parameters; if the current buffer
1908 contains a subunit, move point to the subunit name (for
1909 `ada-goto-declaration'), return t; otherwise leave point alone,
1910 return nil.")
1911
1912 (defun ada-goto-subunit-name ()
1913 "See `ada-goto-subunit-name' variable."
1914 (interactive)
1915 (when ada-goto-subunit-name
1916 (funcall ada-goto-subunit-name)))
1917
1918 (defun ada-add-log-current-function ()
1919 "For `add-log-current-defun-function'; uses `ada-which-function'."
1920 ;; add-log-current-defun is typically called with point at the start
1921 ;; of an ediff change section, which is before the start of the
1922 ;; declaration of a new item. So go to the end of the current line
1923 ;; first, then call `ada-which-function'
1924 (save-excursion
1925 (end-of-line 1)
1926 (ada-which-function)))
1927
1928 (defun ada-set-point-accordingly ()
1929 "Move to the string specified in `ff-function-name', which may be a regexp,
1930 previously set by a file navigation command."
1931 (when ff-function-name
1932 (let ((done nil)
1933 (found nil))
1934 (goto-char (point-min))
1935 ;; We are looking for an Ada declaration, so don't stop for strings or comments
1936 ;;
1937 ;; This will still be confused by multiple references; we need
1938 ;; to use compiler cross reference info for more precision.
1939 (while (not done)
1940 (if (search-forward-regexp ff-function-name nil t)
1941 (setq found (match-beginning 0))
1942 ;; not in remainder of buffer
1943 (setq done t))
1944 (if (ada-in-string-or-comment-p)
1945 (setq found nil)
1946 (setq done t)))
1947 (when found
1948 (goto-char found)
1949 ;; different parsers find different points on the line; normalize here
1950 (back-to-indentation))
1951 (setq ff-function-name nil))))
1952
1953 (defun ada-check-current-project (file-name)
1954 "Throw error if FILE-NAME (must be absolute) is not found in
1955 the current project source directories, or if no project has been
1956 set."
1957 (when (null (car compilation-search-path))
1958 (error "no file search path defined; set project file?"))
1959
1960 ;; file-truename handles symbolic links
1961 (let* ((visited-file (file-truename file-name))
1962 (found-file (locate-file (file-name-nondirectory visited-file)
1963 compilation-search-path)))
1964 (unless found-file
1965 (error "current file not part of current project; wrong project?"))
1966
1967 (setq found-file (file-truename found-file))
1968
1969 ;; (nth 10 (file-attributes ...)) is the inode; required when hard
1970 ;; links are present.
1971 (let* ((visited-file-inode (nth 10 (file-attributes visited-file)))
1972 (found-file-inode (nth 10 (file-attributes found-file))))
1973 (unless (equal visited-file-inode found-file-inode)
1974 (error "%s (opened) and %s (found in project) are two different files"
1975 file-name found-file)))))
1976
1977 (defun ada-find-other-file-noset (other-window)
1978 "Same as `ada-find-other-file', but preserve point in the other file,
1979 don't move to corresponding declaration."
1980 (interactive "P")
1981 (ada-find-other-file other-window t))
1982
1983 (defun ada-find-other-file (other-window &optional no-set-point)
1984 "Move to the corresponding declaration in another file.
1985
1986 - If region is active, assume it contains a package name;
1987 position point on that package declaration.
1988
1989 - If point is in the start line of a non-nested child package or
1990 subprogram declaration, position point on the corresponding
1991 parent package specification.
1992
1993 - If point is in a context clause line, position point on the
1994 first package declaration that is mentioned.
1995
1996 - If point is in a separate body, position point on the
1997 corresponding specification.
1998
1999 - If point is in a subprogram body or specification, position point
2000 on the corresponding specification or body.
2001
2002 If OTHER-WINDOW (set by interactive prefix) is non-nil, show the
2003 buffer in another window.
2004
2005 If NO-SET-POINT is nil, set point in the other file on the
2006 corresponding declaration. If non-nil, preserve existing point in
2007 the other file."
2008
2009 ;; ff-get-file, ff-find-other file first process
2010 ;; ff-special-constructs, then run the following hooks:
2011 ;;
2012 ;; ff-pre-load-hook set to ada-which-function
2013 ;; ff-file-created-hook set to ada-ff-create-body
2014 ;; ff-post-load-hook set to ada-set-point-accordingly,
2015 ;; or to a compiler-specific function that
2016 ;; uses compiler-generated cross reference
2017 ;; information
2018
2019 (interactive "P")
2020 (ada-check-current-project (buffer-file-name))
2021
2022 (cond
2023 (mark-active
2024 (setq ff-function-name (buffer-substring-no-properties (point) (mark)))
2025 (ff-get-file
2026 compilation-search-path
2027 (ada-file-name-from-ada-name ff-function-name)
2028 ada-spec-suffixes
2029 other-window)
2030 (deactivate-mark))
2031
2032 ((and (not (ada-on-context-clause))
2033 (ada-goto-subunit-name))
2034 (ada-goto-declaration other-window))
2035
2036 (t
2037 (ff-find-other-file other-window)))
2038 )
2039
2040 (defun ada-find-file (filename)
2041 ;; we assume compliation-search-path is set, either by an
2042 ;; ada-mode project, or by some other means.
2043 ;; FIXME: option to filter with ada-*-suffixes?
2044 (interactive (list (completing-read "File: "
2045 (apply-partially
2046 'locate-file-completion-table
2047 compilation-search-path nil))))
2048 (find-file (locate-file filename compilation-search-path))
2049 )
2050
2051 (defvar ada-operator-re
2052 "\\+\\|-\\|/\\|\\*\\*\\|\\*\\|=\\|&\\|abs\\|mod\\|rem\\|and\\|not\\|or\\|xor\\|<=\\|<\\|>=\\|>"
2053 "Regexp matching Ada operator_symbol.")
2054
2055 (defun ada-identifier-at-point ()
2056 "Return the identifier around point, move point to start of
2057 identifier. May be an Ada identifier or operator."
2058
2059 (when (ada-in-comment-p)
2060 (error "Inside comment"))
2061
2062 (let (identifier)
2063
2064 (skip-chars-backward "a-zA-Z0-9_<>=+\\-\\*/&")
2065
2066 ;; Just in front of, or inside, a string => we could have an
2067 ;; operator function declaration.
2068 (cond
2069 ((ada-in-string-p)
2070 (cond
2071
2072 ((and (= (char-before) ?\")
2073 (progn
2074 (forward-char -1)
2075 (looking-at (concat "\"\\(" ada-operator-re "\\)\""))))
2076 (setq identifier (concat "\"" (match-string-no-properties 1) "\"")))
2077
2078 (t
2079 (error "Inside string or character constant"))
2080 ))
2081
2082 ((and (= (char-after) ?\")
2083 (looking-at (concat "\"\\(" ada-operator-re "\\)\"")))
2084 (setq identifier (concat "\"" (match-string-no-properties 1) "\"")))
2085
2086 ((looking-at "[a-zA-Z0-9_]+\\|[+\\-*/&=<>]")
2087 (setq identifier (match-string-no-properties 0)))
2088
2089 (t
2090 (error "No identifier around"))
2091 )))
2092
2093 (defvar ada-goto-pos-ring '()
2094 "List of positions selected by navigation functions. Used
2095 to go back to these positions.")
2096
2097 (defconst ada-goto-pos-ring-max 16
2098 "Number of positions kept in the list `ada-goto-pos-ring'.")
2099
2100 (defun ada-goto-push-pos ()
2101 "Push current filename, position on `ada-goto-pos-ring'. See `ada-goto-previous-pos'."
2102 (setq ada-goto-pos-ring (cons (list (point) (buffer-file-name)) ada-goto-pos-ring))
2103 (if (> (length ada-goto-pos-ring) ada-goto-pos-ring-max)
2104 (setcdr (nthcdr (1- ada-goto-pos-ring-max) ada-goto-pos-ring) nil)))
2105
2106 (defun ada-goto-previous-pos ()
2107 "Go to the first position in `ada-goto-pos-ring', pop `ada-goto-pos-ring'."
2108 (interactive)
2109 (when ada-goto-pos-ring
2110 (let ((pos (pop ada-goto-pos-ring)))
2111 (find-file (cadr pos))
2112 (goto-char (car pos)))))
2113
2114 (defun ada-goto-source (file line column other-window)
2115 "Find and select FILE, at LINE and COLUMN.
2116 FILE may be absolute, or on `compilation-search-path'.
2117
2118 If OTHER-WINDOW is non-nil, show the buffer in another window."
2119 (let ((file-1
2120 (if (file-name-absolute-p file) file
2121 (ff-get-file-name compilation-search-path file))))
2122 (if file-1
2123 (setq file file-1)
2124 (error "File %s not found; installed library, or set project?" file))
2125 )
2126
2127 (ada-goto-push-pos)
2128
2129 (let ((buffer (get-file-buffer file)))
2130 (cond
2131 ((bufferp buffer)
2132 (cond
2133 ((null other-window)
2134 (switch-to-buffer buffer))
2135
2136 (t (switch-to-buffer-other-window buffer))
2137 ))
2138
2139 ((file-exists-p file)
2140 (cond
2141 ((null other-window)
2142 (find-file file))
2143
2144 (t
2145 (find-file-other-window file))
2146 ))
2147
2148 (t
2149 (error "'%s' not found" file))))
2150
2151
2152 ;; move the cursor to the correct position
2153 (push-mark nil t)
2154 (goto-char (point-min))
2155 (forward-line (1- line))
2156 (forward-char column)
2157 )
2158
2159 (defvar ada-xref-refresh-function nil
2160 ;; determined by xref_tool, set by *-select-prj-xref
2161 "Function that refreshes cross reference information cache.")
2162
2163 (defun ada-xref-refresh ()
2164 "Refresh cross reference information cache, if any."
2165 (interactive)
2166
2167 (when (null ada-xref-refresh-function)
2168 (error "no cross reference information available"))
2169
2170 (funcall ada-xref-refresh-function)
2171 )
2172
2173 (defvar ada-xref-other-function nil
2174 ;; determined by xref_tool, set by *-select-prj-xref
2175 "Function that returns cross reference information.
2176 Function is called with four arguments:
2177 - an Ada identifier or operator_symbol
2178 - filename containing the identifier (full path)
2179 - line number containing the identifier
2180 - column of the start of the identifier
2181 Returns a list '(file line column) giving the corresponding location.
2182 'file' may be absolute, or on `compilation-search-path'. If point is
2183 at the specification, the corresponding location is the body, and vice
2184 versa.")
2185
2186 (defun ada-goto-declaration (other-window)
2187 "Move to the declaration or body of the identifier around point.
2188 If at the declaration, go to the body, and vice versa.
2189
2190 If OTHER-WINDOW (set by interactive prefix) is non-nil, show the
2191 buffer in another window."
2192 (interactive "P")
2193 (ada-check-current-project (buffer-file-name))
2194
2195 (when (null ada-xref-other-function)
2196 (error "no cross reference information available"))
2197
2198 (let ((target
2199 (funcall ada-xref-other-function
2200 (ada-identifier-at-point)
2201 (buffer-file-name)
2202 (line-number-at-pos)
2203 (1+ (current-column))
2204 )))
2205
2206 (ada-goto-source (nth 0 target)
2207 (nth 1 target)
2208 (nth 2 target)
2209 other-window)
2210 ))
2211
2212 (defvar ada-xref-parent-function nil
2213 ;; determined by xref_tool, set by *-select-prj-xref
2214 "Function that returns cross reference information.
2215 Function is called with four arguments:
2216 - an Ada identifier or operator_symbol
2217 - filename containing the identifier
2218 - line number containing the identifier
2219 - column of the start of the identifier
2220 Displays a buffer in compilation-mode giving locations of the parent type declarations.")
2221
2222 (defun ada-show-declaration-parents ()
2223 "Display the locations of the parent type declarations of the type identifier around point."
2224 (interactive)
2225 (ada-check-current-project (buffer-file-name))
2226
2227 (when (null ada-xref-parent-function)
2228 (error "no cross reference information available"))
2229
2230 (funcall ada-xref-parent-function
2231 (ada-identifier-at-point)
2232 (file-name-nondirectory (buffer-file-name))
2233 (line-number-at-pos)
2234 (1+ (current-column)))
2235 )
2236
2237 (defvar ada-xref-all-function nil
2238 ;; determined by xref_tool, set by *-select-prj-xref
2239 "Function that displays cross reference information.
2240 Called with four arguments:
2241 - an Ada identifier or operator_symbol
2242 - filename containing the identifier
2243 - line number containing the identifier
2244 - column of the start of the identifier
2245 Displays a buffer in compilation-mode giving locations where the
2246 identifier is declared or referenced.")
2247
2248 (defun ada-show-references ()
2249 "Show all references of identifier at point."
2250 (interactive)
2251 (ada-check-current-project (buffer-file-name))
2252
2253 (when (null ada-xref-all-function)
2254 (error "no cross reference information available"))
2255
2256 (funcall ada-xref-all-function
2257 (ada-identifier-at-point)
2258 (file-name-nondirectory (buffer-file-name))
2259 (line-number-at-pos)
2260 (1+ (current-column)))
2261 )
2262
2263 (defvar ada-xref-overriding-function nil
2264 ;; determined by ada-xref-tool, set by *-select-prj
2265 "Function that displays cross reference information for overriding subprograms.
2266 Called with four arguments:
2267 - an Ada identifier or operator_symbol
2268 - filename containing the identifier
2269 - line number containing the identifier
2270 - column of the start of the identifier
2271 Displays a buffer in compilation-mode giving locations of the overriding declarations.")
2272
2273 (defun ada-show-overriding ()
2274 "Show all overridings of identifier at point."
2275 (interactive)
2276 (ada-check-current-project (buffer-file-name))
2277
2278 (when (null ada-xref-overriding-function)
2279 (error "no cross reference information available"))
2280
2281 (funcall ada-xref-overriding-function
2282 (ada-identifier-at-point)
2283 (file-name-nondirectory (buffer-file-name))
2284 (line-number-at-pos)
2285 (1+ (current-column)))
2286 )
2287
2288 (defvar ada-xref-overridden-function nil
2289 ;; determined by ada-xref-tool, set by *-select-prj
2290 "Function that displays cross reference information for overridden subprogram.
2291 Called with four arguments:
2292 - an Ada identifier or operator_symbol
2293 - filename containing the identifier
2294 - line number containing the identifier
2295 - column of the start of the identifier
2296 Returns a list '(file line column) giving the corresponding location.
2297 'file' may be absolute, or on `compilation-search-path'.")
2298
2299 (defun ada-show-overridden (other-window)
2300 "Show the overridden declaration of identifier at point."
2301 (interactive "P")
2302 (ada-check-current-project (buffer-file-name))
2303
2304 (when (null ada-xref-overridden-function)
2305 (error "'show overridden' not supported, or no cross reference information available"))
2306
2307 (let ((target
2308 (funcall ada-xref-overridden-function
2309 (ada-identifier-at-point)
2310 (file-name-nondirectory (buffer-file-name))
2311 (line-number-at-pos)
2312 (1+ (current-column)))))
2313
2314 (ada-goto-source (nth 0 target)
2315 (nth 1 target)
2316 (nth 2 target)
2317 other-window)
2318
2319 ))
2320
2321 ;; This is autoloaded because it may be used in ~/.emacs
2322 ;;;###autoload
2323 (defun ada-add-extensions (spec body)
2324 "Define SPEC and BODY as being valid extensions for Ada files.
2325 SPEC and BODY are two regular expressions that must match against
2326 the file name."
2327 (let* ((reg (concat (regexp-quote body) "$"))
2328 (tmp (assoc reg ada-other-file-alist)))
2329 (if tmp
2330 (setcdr tmp (list (cons spec (cadr tmp))))
2331 (add-to-list 'ada-other-file-alist (list reg (list spec)))))
2332
2333 (let* ((reg (concat (regexp-quote spec) "$"))
2334 (tmp (assoc reg ada-other-file-alist)))
2335 (if tmp
2336 (setcdr tmp (list (cons body (cadr tmp))))
2337 (add-to-list 'ada-other-file-alist (list reg (list body)))))
2338
2339 (add-to-list 'auto-mode-alist
2340 (cons (concat (regexp-quote spec) "\\'") 'ada-mode))
2341 (add-to-list 'auto-mode-alist
2342 (cons (concat (regexp-quote body) "\\'") 'ada-mode))
2343
2344 (add-to-list 'ada-spec-suffixes spec)
2345 (add-to-list 'ada-body-suffixes body)
2346
2347 (when (fboundp 'speedbar-add-supported-extension)
2348 (speedbar-add-supported-extension spec)
2349 (speedbar-add-supported-extension body))
2350 )
2351
2352 (defun ada-show-secondary-error (other-window)
2353 "Show the next secondary file reference in the compilation buffer.
2354 A secondary file reference is defined by text having text
2355 property `ada-secondary-error'. These can be set by
2356 compiler-specific compilation filters.
2357
2358 If OTHER-WINDOW (set by interactive prefix) is non-nil, show the
2359 buffer in another window."
2360 (interactive "P")
2361
2362 ;; preserving the current window works only if the frame
2363 ;; doesn't change, at least on Windows.
2364 (let ((start-buffer (current-buffer))
2365 (start-window (selected-window))
2366 pos item file)
2367 (set-buffer compilation-last-buffer)
2368 (setq pos (next-single-property-change (point) 'ada-secondary-error))
2369 (when pos
2370 (setq item (get-text-property pos 'ada-secondary-error))
2371 ;; file-relative-name handles absolute Windows paths from
2372 ;; g++. Do this in compilation buffer to get correct
2373 ;; default-directory.
2374 (setq file (file-relative-name (nth 0 item)))
2375
2376 ;; Set point in compilation buffer past this secondary error, so
2377 ;; user can easily go to the next one. For some reason, this
2378 ;; doesn't change the visible point!?
2379 (forward-line 1))
2380
2381 (set-buffer start-buffer);; for windowing history
2382 (when item
2383 (ada-goto-source
2384 file
2385 (nth 1 item); line
2386 (nth 2 item); column
2387 other-window)
2388 (select-window start-window)
2389 )
2390 ))
2391
2392 (defvar ada-goto-declaration-start nil
2393 ;; Supplied by indentation engine.
2394 ;;
2395 ;; This is run from ff-pre-load-hook, so ff-function-name may have
2396 ;; been set by ff-treat-special; don't reset it.
2397 "For `beginning-of-defun-function'. Function to move point to
2398 start of the generic, package, protected, subprogram, or task
2399 declaration point is currently in or just after. Called with no
2400 parameters.")
2401
2402 (defun ada-goto-declaration-start ()
2403 "Call `ada-goto-declaration-start'."
2404 (interactive)
2405 (when ada-goto-declaration-start
2406 (funcall ada-goto-declaration-start)))
2407
2408 (defvar ada-goto-declaration-end nil
2409 ;; supplied by indentation engine
2410 "For `end-of-defun-function'. Function to move point to end of
2411 current declaration.")
2412
2413 (defun ada-goto-declaration-end ()
2414 "See `ada-goto-declaration-end' variable."
2415 (interactive)
2416 (when ada-goto-declaration-end
2417 (funcall ada-goto-declaration-end)))
2418
2419 (defvar ada-goto-declarative-region-start nil
2420 ;; Supplied by indentation engine
2421 "Function to move point to start of the declarative region of
2422 the subprogram, package, task, or declare block point
2423 is currently in. Called with no parameters.")
2424
2425 (defun ada-goto-declarative-region-start ()
2426 "Call `ada-goto-declarative-region-start'."
2427 (when ada-goto-declarative-region-start
2428 (funcall ada-goto-declarative-region-start)))
2429
2430 (defvar ada-next-statement-keyword nil
2431 ;; Supplied by indentation engine
2432 "Function called with no parameters; it should move forward to
2433 the next keyword in the statement following the one point is
2434 in (ie from 'if' to 'then'). If not in a keyword, move forward
2435 to the next keyword in the current statement. If at the last keyword,
2436 move forward to the first keyword in the next statement or next
2437 keyword in the containing statement.")
2438
2439 (defvar ada-goto-end nil
2440 ;; Supplied by indentation engine
2441 "Function to move point to end of the declaration or statement point is in or before.
2442 Called with no parameters.")
2443
2444 (defun ada-goto-end ()
2445 "Call `ada-goto-end'."
2446 (when ada-goto-end
2447 (funcall ada-goto-end)))
2448
2449 (defun ada-next-statement-keyword ()
2450 ;; Supplied by indentation engine
2451 "See `ada-next-statement-keyword' variable."
2452 (interactive)
2453 (when ada-next-statement-keyword
2454 (unless (region-active-p)
2455 (push-mark))
2456 (funcall ada-next-statement-keyword)))
2457
2458 (defvar ada-prev-statement-keyword nil
2459 ;; Supplied by indentation engine
2460 "Function called with no parameters; it should move to the previous
2461 keyword in the statement following the one point is in (ie from
2462 'then' to 'if'). If at the first keyword, move to the previous
2463 keyword in the previous statement or containing statement.")
2464
2465 (defun ada-prev-statement-keyword ()
2466 "See `ada-prev-statement-keyword' variable."
2467 (interactive)
2468 (when ada-prev-statement-keyword
2469 (unless (region-active-p)
2470 (push-mark))
2471 (funcall ada-prev-statement-keyword)))
2472
2473 ;;;; code creation
2474
2475 (defvar ada-make-subprogram-body nil
2476 ;; Supplied by indentation engine
2477 "Function to convert subprogram specification after point into a subprogram body stub.
2478 Called with no args, point at declaration start. Leave point in
2479 subprogram body, for user to add code.")
2480
2481 (defun ada-make-subprogram-body ()
2482 "If point is in or after a subprogram specification, convert it
2483 into a subprogram body stub, by calling `ada-make-subprogram-body'."
2484 (interactive)
2485 (ada-goto-declaration-start)
2486 (if ada-make-subprogram-body
2487 (funcall ada-make-subprogram-body)
2488 (error "`ada-make-subprogram-body' not set")))
2489
2490 (defvar ada-make-package-body nil
2491 ;; Supplied by xref tool
2492 "Function to create a package body from a package spec.
2493 Called with one argument; the absolute path to the body
2494 file. Current buffer is the package spec. Should create the
2495 package body file, containing skeleton code that will compile.")
2496
2497 (defun ada-make-package-body (body-file-name)
2498 ;; no error if not set; let ada-skel do its thing.
2499 (when ada-make-package-body
2500 (funcall ada-make-package-body body-file-name)))
2501
2502 (defun ada-ff-create-body ()
2503 ;; no error if not set; let ada-skel do its thing.
2504 (when ada-make-package-body
2505 ;; ff-find-other-file calls us with point in an empty buffer for the
2506 ;; body file; ada-make-package-body expects to be in the spec. So go
2507 ;; back.
2508 (let ((body-file-name (buffer-file-name)))
2509 (ff-find-the-other-file)
2510
2511 (ada-make-package-body body-file-name)
2512 ;; FIXME (later): if 'ada-make-package-body' fails, delete the body buffer
2513 ;; so it doesn't get written to disk, and we can try again.
2514
2515 ;; back to the body, read in from the disk.
2516 (ff-find-the-other-file)
2517 (revert-buffer t t))
2518 ))
2519
2520 ;;;; fill-comment
2521
2522 (defun ada-fill-comment-paragraph (&optional justify postfix)
2523 "Fill the current comment paragraph.
2524 If JUSTIFY is non-nil, each line is justified as well.
2525 If POSTFIX and JUSTIFY are non-nil, `ada-fill-comment-postfix' is appended
2526 to each line filled and justified.
2527 The paragraph is indented on the first line."
2528 (interactive "P")
2529 (if (and (not (ada-in-comment-p))
2530 (not (looking-at "[ \t]*--")))
2531 (error "Not inside comment"))
2532
2533 (let* ((inhibit-modification-hooks t) ;; don't run parser for font-lock; comment text is exposed
2534 indent from to
2535 (opos (point-marker))
2536 ;; we bind `fill-prefix' here rather than in ada-mode because
2537 ;; setting it in ada-mode causes indent-region to use it for
2538 ;; all indentation.
2539 (fill-prefix ada-fill-comment-prefix)
2540 (fill-column (current-fill-column)))
2541
2542 ;; Find end of comment paragraph
2543 (back-to-indentation)
2544 (while (and (not (eobp)) (looking-at ".*--[ \t]*[^ \t\n]"))
2545 (forward-line 1)
2546
2547 ;; If we were at the last line in the buffer, create a dummy empty
2548 ;; line at the end of the buffer.
2549 (if (eobp)
2550 (insert "\n")
2551 (back-to-indentation)))
2552 (beginning-of-line)
2553 (setq to (point-marker))
2554 (goto-char opos)
2555
2556 ;; Find beginning of paragraph
2557 (back-to-indentation)
2558 (while (and (not (bobp)) (looking-at ".*--[ \t]*[^ \t\n]"))
2559 (forward-line -1)
2560 (back-to-indentation))
2561
2562 (unless (bobp)
2563 (forward-line 1))
2564 (beginning-of-line)
2565 (setq from (point-marker))
2566
2567 ;; Calculate the indentation we will need for the paragraph
2568 (back-to-indentation)
2569 (setq indent (current-column))
2570 ;; unindent the first line of the paragraph
2571 (delete-region from (point))
2572
2573 ;; Remove the old postfixes
2574 (goto-char from)
2575 (while (re-search-forward (concat "\\(" ada-fill-comment-postfix "\\)" "\n") to t)
2576 (delete-region (match-beginning 1) (match-end 1)))
2577
2578 (goto-char (1- to))
2579 (setq to (point-marker))
2580
2581 ;; Indent and justify the paragraph
2582 (set-left-margin from to indent)
2583 (if postfix
2584 (setq fill-column (- fill-column (length ada-fill-comment-postfix))))
2585
2586 (fill-region-as-paragraph from to justify)
2587
2588 ;; Add the postfixes if required
2589 (if postfix
2590 (save-restriction
2591 (goto-char from)
2592 (narrow-to-region from to)
2593 (while (not (eobp))
2594 (end-of-line)
2595 (insert-char ? (- fill-column (current-column)))
2596 (insert ada-fill-comment-postfix)
2597 (forward-line))
2598 ))
2599
2600 (goto-char opos)
2601
2602 ;; we disabled modification hooks, so font-lock will not run to
2603 ;; re-fontify the comment prefix; do that here.
2604 (when (memq 'jit-lock-after-change after-change-functions)
2605 (jit-lock-after-change from to 0))
2606 ))
2607
2608 ;;;; support for font-lock.el
2609
2610 ;; casing keywords defined here to keep the two lists together
2611 (defconst ada-83-keywords
2612 '("abort" "abs" "accept" "access" "all" "and" "array" "at" "begin"
2613 "body" "case" "constant" "declare" "delay" "delta" "digits" "do"
2614 "else" "elsif" "end" "entry" "exception" "exit" "for" "function"
2615 "generic" "goto" "if" "in" "is" "limited" "loop" "mod" "new"
2616 "not" "null" "of" "or" "others" "out" "package" "pragma" "private"
2617 "procedure" "raise" "range" "record" "rem" "renames" "return"
2618 "reverse" "select" "separate" "subtype" "task" "terminate" "then"
2619 "type" "use" "when" "while" "with" "xor")
2620 "List of Ada 83 keywords.")
2621
2622 (defconst ada-95-keywords
2623 '("abstract" "aliased" "protected" "requeue" "tagged" "until")
2624 "List of keywords new in Ada 95.")
2625
2626 (defconst ada-2005-keywords
2627 '("interface" "overriding" "synchronized")
2628 "List of keywords new in Ada 2005.")
2629
2630 (defconst ada-2012-keywords
2631 '("some")
2632 "List of keywords new in Ada 2012.")
2633
2634 (defun ada-font-lock-keywords ()
2635 "Return Ada mode value for `font-lock-keywords', depending on `ada-language-version'."
2636 ;; Grammar actions set `font-lock-face' property for all
2637 ;; non-keyword tokens that need it.
2638 (list
2639 (list (concat "\\<" (regexp-opt ada-keywords t) "\\>") '(0 font-lock-keyword-face))
2640 ))
2641
2642 ;;;; ada-mode
2643
2644 ;; ada-mode does not derive from prog-mode, because we need to call
2645 ;; ada-mode-post-local-vars, and prog-mode does not provide a way to
2646 ;; do that.
2647 ;;
2648 ;; autoload required by automatic mode setting
2649 ;;;###autoload
2650 (defun ada-mode ()
2651 "The major mode for editing Ada code."
2652 ;; the other ada-*.el files add to ada-mode-hook for their setup
2653
2654 (interactive)
2655 (kill-all-local-variables)
2656 (setq major-mode 'ada-mode)
2657 (setq mode-name "Ada")
2658 (use-local-map ada-mode-map)
2659 (set-syntax-table ada-mode-syntax-table)
2660 (define-abbrev-table 'ada-mode-abbrev-table ())
2661 (setq local-abbrev-table ada-mode-abbrev-table)
2662
2663 (set (make-local-variable 'syntax-propertize-function) 'ada-syntax-propertize)
2664 (set (make-local-variable 'syntax-begin-function) nil)
2665 (set (make-local-variable 'parse-sexp-ignore-comments) t)
2666 (set (make-local-variable 'parse-sexp-lookup-properties) t)
2667 (set 'case-fold-search t); Ada is case insensitive; the syntax parsing requires this setting
2668 (set (make-local-variable 'comment-start) "--")
2669 (set (make-local-variable 'comment-end) "")
2670 (set (make-local-variable 'comment-start-skip) "---*[ \t]*")
2671 (set (make-local-variable 'comment-multi-line) nil)
2672
2673 ;; we _don't_ set `fill-prefix' here because that causes
2674 ;; indent-region to use it for all indentation. See
2675 ;; ada-fill-comment-paragraph.
2676
2677 ;; AdaCore standard style (enforced by -gnaty) requires two spaces
2678 ;; after '--' in comments; this makes it easier to distinguish
2679 ;; special comments that have something else after '--'
2680 (set (make-local-variable 'comment-padding) " ")
2681
2682 (set (make-local-variable 'require-final-newline) t)
2683
2684 ;; 'font-lock-defaults' is a confusing name; it's buffer local
2685 (setq font-lock-defaults
2686 '(ada-font-lock-keywords
2687 nil t
2688 ((?\_ . "w")))); treat underscore as a word component
2689
2690 (set (make-local-variable 'ff-other-file-alist)
2691 'ada-other-file-alist)
2692 (setq ff-post-load-hook 'ada-set-point-accordingly
2693 ff-file-created-hook 'ada-ff-create-body)
2694 (add-hook 'ff-pre-load-hook 'ada-goto-push-pos)
2695 (add-hook 'ff-pre-load-hook 'ada-which-function)
2696 (setq ff-search-directories 'compilation-search-path)
2697 (when (null (car compilation-search-path))
2698 ;; find-file doesn't handle nil in search path
2699 (setq compilation-search-path (list (file-name-directory (buffer-file-name)))))
2700 (ada-set-ff-special-constructs)
2701
2702 (set (make-local-variable 'add-log-current-defun-function)
2703 'ada-add-log-current-function)
2704
2705 (when (boundp 'which-func-functions)
2706 (add-hook 'which-func-functions 'ada-which-function nil t))
2707
2708 ;; Support for align
2709 (add-to-list 'align-dq-string-modes 'ada-mode)
2710 (add-to-list 'align-open-comment-modes 'ada-mode)
2711 (set (make-local-variable 'align-region-separate) ada-align-region-separate)
2712 (set (make-local-variable 'align-indent-before-aligning) t)
2713
2714 ;; Exclude comments alone on line from alignment.
2715 (add-to-list 'align-exclude-rules-list
2716 '(ada-solo-comment
2717 (regexp . "^\\(\\s-*\\)--")
2718 (modes . '(ada-mode))))
2719 (add-to-list 'align-exclude-rules-list
2720 '(ada-solo-use
2721 (regexp . "^\\(\\s-*\\)\\<use\\>")
2722 (modes . '(ada-mode))))
2723
2724 (setq align-mode-rules-list ada-align-rules)
2725
2726 (easy-menu-add ada-mode-menu ada-mode-map)
2727
2728 (setq ada-case-strict (ada-prj-get 'case_strict))
2729
2730 (run-mode-hooks 'ada-mode-hook)
2731
2732 ;; If global-font-lock is not enabled, ada-syntax-propertize is
2733 ;; not run when the text is first loaded into the buffer. Recover
2734 ;; from that.
2735 (syntax-ppss-flush-cache (point-min))
2736 (syntax-propertize (point-max))
2737
2738 (add-hook 'hack-local-variables-hook 'ada-mode-post-local-vars nil t)
2739 )
2740
2741 (defun ada-mode-post-local-vars ()
2742 ;; These are run after ada-mode-hook and file local variables
2743 ;; because users or other ada-* files might set the relevant
2744 ;; variable inside the hook or file local variables (file local
2745 ;; variables are processed after the mode is set, and thus after
2746 ;; ada-mode is run).
2747
2748 ;; This means to fully set ada-mode interactively, user must
2749 ;; do M-x ada-mode M-; (hack-local-variables)
2750
2751 ;; fill-region-as-paragraph in ada-fill-comment-paragraph does not
2752 ;; call syntax-propertize, so set comment syntax on
2753 ;; ada-fill-comment-prefix. In post-local because user may want to
2754 ;; set it per-file.
2755 (put-text-property 0 2 'syntax-table '(11 . nil) ada-fill-comment-prefix)
2756
2757 (cl-case ada-language-version
2758 (ada83
2759 (setq ada-keywords ada-83-keywords))
2760
2761 (ada95
2762 (setq ada-keywords
2763 (append ada-83-keywords
2764 ada-95-keywords)))
2765
2766 (ada2005
2767 (setq ada-keywords
2768 (append ada-83-keywords
2769 ada-95-keywords
2770 ada-2005-keywords)))
2771 (ada2012
2772 (setq ada-keywords
2773 (append ada-83-keywords
2774 ada-95-keywords
2775 ada-2005-keywords
2776 ada-2012-keywords))))
2777
2778 (when global-font-lock-mode
2779 ;; This calls ada-font-lock-keywords, which depends on
2780 ;; ada-keywords
2781 (font-lock-refresh-defaults))
2782
2783 (when ada-goto-declaration-start
2784 (set (make-local-variable 'beginning-of-defun-function) ada-goto-declaration-start))
2785
2786 (when ada-goto-declaration-end
2787 (set (make-local-variable 'end-of-defun-function) ada-goto-declaration-end))
2788 )
2789
2790 (put 'ada-mode 'custom-mode-group 'ada)
2791
2792 (provide 'ada-mode)
2793
2794 ;;;;; Global initializations
2795
2796 (require 'ada-build)
2797
2798 (unless (featurep 'ada-indent-engine)
2799 (require 'ada-wisi))
2800
2801 (unless (featurep 'ada-xref-tool)
2802 (cl-case ada-xref-tool
2803 ((nil 'gnat) (require 'ada-gnat-xref))
2804 ('gpr_query (require 'gpr-query))
2805 ))
2806
2807 (unless (featurep 'ada-compiler)
2808 (require 'ada-gnat-compile))
2809
2810 (unless (featurep 'ada-skeletons)
2811 (require 'ada-skel))
2812
2813 (when (featurep 'imenu)
2814 (require 'ada-imenu))
2815
2816 ;;; end of file