]> code.delx.au - gnu-emacs-elpa/blob - packages/ada-mode/ada-mode.el
Merge commit '010e64bef8a9ec5539733ff0c2e92d075b25a271'
[gnu-emacs-elpa] / packages / ada-mode / ada-mode.el
1 ;;; ada-mode.el --- major-mode for editing Ada sources -*- lexical-binding:t -*-
2 ;;
3 ;; Copyright (C) 1994, 1995, 1997 - 2015 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.8
10 ;; package-requires: ((wisi "1.1.1") (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.8"))
172 ;; must match:
173 ;; ada-mode.texi
174 ;; README-ada-mode
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 :safe #'booleanp)
200 (make-variable-buffer-local 'ada-auto-case)
201
202 (defcustom ada-case-exception-file nil
203 "Default list of special casing exceptions dictionaries for identifiers.
204 Override with 'casing' project variable.
205
206 New exceptions may be added interactively via `ada-case-create-exception'.
207 If an exception is defined in multiple files, the first occurence is used.
208
209 The file format is one word per line, that gives the casing to be
210 used for that word in Ada source code. If the line starts with
211 the character *, then the exception will be used for partial
212 words that either start at the beginning of a word or after a _
213 character, and end either at the end of the word or at a _
214 character. Characters after the first word are ignored, and not
215 preserved when the list is written back to the file."
216 :type '(repeat (file))
217 ;; :safe #'listp ;FIXME: is '("~/.emacs" "~/.bashrc" "/etc/passwd") safe?
218 )
219
220 (defcustom ada-case-keyword 'downcase-word
221 "Buffer-local value that may override project variable `case_keyword'.
222 Global value is default for project variable `case_keyword'.
223 Function to call to adjust the case of Ada keywords."
224 :type '(choice (const downcase-word)
225 (const upcase-word))
226 ;; :safe #'functionp ; FIXME: `functionp' CANNOT be safe!
227 )
228 (make-variable-buffer-local 'ada-case-keyword)
229
230 (defcustom ada-case-identifier 'ada-mixed-case
231 "Buffer-local value that may override project variable `case_keyword'.
232 Global value is default for project variable `case_keyword'.
233 Function to call to adjust the case of Ada keywords.
234 Called with three args;
235 start - buffer pos of start of identifier
236 end - end of identifier
237 force-case - if t, treat `ada-strict-case' as t"
238 :type '(choice (const ada-mixed-case)
239 (const ada-lower-case)
240 (const ada-upper-case))
241 ;; :safe #'functionp ; FIXME: `functionp' CANNOT be safe!
242 )
243 ;; we'd like to check that there are 3 args, since the previous
244 ;; release required 2 here. But there doesn't seem to be a way to
245 ;; access the arg count, which is only available for byte-compiled
246 ;; functions
247 (make-variable-buffer-local 'ada-case-identifier)
248
249 (defcustom ada-case-strict t
250 "Buffer-local value that may override project variable `case_strict'.
251 Global value is default for project variable `case_strict'.
252 If non-nil, force Mixed_Case for identifiers.
253 Otherwise, allow UPPERCASE for identifiers."
254 :type 'boolean
255 :safe #'booleanp)
256 (make-variable-buffer-local 'ada-case-strict)
257
258 (defcustom ada-language-version 'ada2012
259 "Ada language version; one of `ada83', `ada95', `ada2005', `ada2012'.
260 Only affects the keywords to highlight, not which version the
261 indentation parser accepts."
262 :type '(choice (const ada83)
263 (const ada95)
264 (const ada2005)
265 (const ada2012))
266 :safe #'symbolp)
267 (make-variable-buffer-local 'ada-language-version)
268
269 (defcustom ada-fill-comment-prefix "-- "
270 "Comment fill prefix."
271 :type 'string)
272
273 (defcustom ada-fill-comment-postfix " --"
274 "Comment fill postfix."
275 :type 'string)
276
277 (defcustom ada-prj-file-extensions '("adp" "prj")
278 "List of Emacs Ada mode project file extensions.
279 Used when searching for a project file.
280 Any file with one of these extensions will be parsed by `ada-prj-parse-file-1'."
281 :type 'list)
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
290 ;;;;; end of user variables
291
292 (defconst ada-symbol-end
293 ;; we can't just add \> here; that might match _ in a user modified ada-mode-syntax-table
294 "\\([ \t]+\\|$\\)"
295 "Regexp to add to symbol name in `ada-which-function'.")
296
297 (defvar ada-compiler nil
298 "Default Ada compiler; can be overridden in project files.
299 Values defined by compiler packages.")
300
301 (defvar ada-xref-tool nil
302 "Default Ada cross reference tool; can be overridden in project files.
303 Values defined by cross reference packages.")
304
305 ;;;; keymap and menus
306
307 (defvar ada-ret-binding 'ada-indent-newline-indent)
308 (defvar ada-lfd-binding 'newline-and-indent)
309
310 (defun ada-case-activate-keys (map)
311 "Modify the key bindings for all the keys that should adjust casing."
312 ;; we could just put these in the keymap below, but this is easier.
313 (mapc (function
314 (lambda(key)
315 (define-key
316 map
317 (char-to-string key)
318 'ada-case-adjust-interactive)))
319 '( ?_ ?% ?& ?* ?( ?) ?- ?= ?+
320 ?| ?\; ?: ?' ?\" ?< ?, ?. ?> ?/ ?\n 32 ?\r ))
321 )
322
323 (defvar ada-mode-map
324 (let ((map (make-sparse-keymap)))
325 ;; C-c <letter> are reserved for users
326
327 ;; global-map has C-x ` 'next-error
328 (define-key map [return] 'ada-case-adjust-interactive)
329 (define-key map "\C-c`" 'ada-show-secondary-error)
330 (define-key map "\C-c;" (lambda () (error "use M-; instead"))) ; comment-dwim
331 (define-key map "\C-c<" 'ada-goto-declaration-start)
332 (define-key map "\C-c>" 'ada-goto-declaration-end)
333 (define-key map "\C-c\M-`" 'ada-fix-compiler-error)
334 (define-key map "\C-c\C-a" 'ada-align)
335 (define-key map "\C-c\C-b" 'ada-make-subprogram-body)
336 (define-key map "\C-c\C-c" 'ada-build-make)
337 (define-key map "\C-c\C-d" 'ada-goto-declaration)
338 (define-key map "\C-c\M-d" 'ada-show-declaration-parents)
339 (define-key map "\C-c\C-e" 'ada-expand)
340 (define-key map "\C-c\C-f" 'ada-show-parse-error)
341 (define-key map "\C-c\C-i" 'ada-indent-statement)
342 (define-key map "\C-c\C-m" 'ada-build-set-make)
343 (define-key map "\C-c\C-n" 'ada-next-statement-keyword)
344 (define-key map "\C-c\M-n" 'ada-next-placeholder)
345 (define-key map "\C-c\C-o" 'ada-find-other-file)
346 (define-key map "\C-c\M-o" 'ada-find-other-file-noset)
347 (define-key map "\C-c\C-p" 'ada-prev-statement-keyword)
348 (define-key map "\C-c\M-p" 'ada-prev-placeholder)
349 (define-key map "\C-c\C-q" 'ada-xref-refresh)
350 (define-key map "\C-c\C-r" 'ada-show-references)
351 (define-key map "\C-c\M-r" 'ada-build-run)
352 (define-key map "\C-c\C-s" 'ada-goto-previous-pos)
353 (define-key map "\C-c\C-v" 'ada-build-check)
354 (define-key map "\C-c\C-w" 'ada-case-adjust-at-point)
355 (define-key map "\C-c\C-x" 'ada-show-overriding)
356 (define-key map "\C-c\M-x" 'ada-show-overridden)
357 (define-key map "\C-c\C-y" 'ada-case-create-exception)
358 (define-key map "\C-c\M-y" 'ada-case-create-partial-exception)
359 (define-key map [C-down-mouse-3] 'ada-popup-menu)
360
361 (ada-case-activate-keys map)
362
363 map
364 ) "Local keymap used for Ada mode.")
365
366 (defvar ada-mode-menu (make-sparse-keymap "Ada"))
367 (easy-menu-define ada-mode-menu ada-mode-map "Menu keymap for Ada mode"
368 '("Ada"
369 ("Help"
370 ["Ada Mode" (info "ada-mode") t]
371 ["Ada Reference Manual" (info "arm2012") t]
372 ["Key bindings" describe-bindings t]
373 )
374 ["Customize" (customize-group 'ada) t]
375 ("Project files"
376 ["Find and select project ..." ada-build-prompt-select-prj-file t]
377 ["Select project ..." ada-prj-select t]
378 ["Show project" ada-prj-show t]
379 ["Show project search path" ada-prj-show-path t]
380 )
381 ("Build"
382 ["Next compilation error" next-error t]
383 ["Show secondary error" ada-show-secondary-error t]
384 ["Fix compilation error" ada-fix-compiler-error t]
385 ["Show last parse error" ada-show-parse-error t]
386 ["Check syntax" ada-build-check t]
387 ["Show main" ada-build-show-main t]
388 ["Build" ada-build-make t]
389 ["Set main and Build" ada-build-set-make t]
390 ["Run" ada-build-run t]
391 )
392 ("Navigate"
393 ["Other file" ada-find-other-file t]
394 ["Other file don't find decl" ada-find-other-file-noset t]
395 ["Find file in project" ada-find-file 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 "\\)\\>[^_]\\)")) ;; in case "_" has punctuation syntax
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 (defvar ada-prj-current-file)
989
990 (defun ada-case-create-exception (&optional word file-name partial)
991 "Define WORD as an exception for the casing system, save it in FILE-NAME.
992 If PARTIAL is non-nil, create a partial word exception. WORD
993 defaults to the active region, or the word at point. User is
994 prompted to choose a file from project variable casing if it is a
995 list."
996 (interactive)
997 (let ((casing (ada-prj-get 'casing)))
998 (setq file-name
999 (cond
1000 (file-name file-name)
1001
1002 ((< 1 (length casing))
1003 (completing-read "case exception file: " casing
1004 nil ;; predicate
1005 t ;; require-match
1006 nil ;; initial-input
1007 nil ;; hist
1008 (car casing) ;; default
1009 ))
1010 ((= 1 (length casing))
1011 (car casing))
1012
1013 (t
1014 (if ada-prj-current-file
1015 (error "No exception file specified; set `casing' in project file.")
1016 ;; IMPROVEME: could prompt, but then need to write to actual project file
1017 ;; (let ((temp
1018 ;; (read-file-name
1019 ;; "No exception file specified; adding to project. file: ")))
1020 ;; (message "remember to add %s to project file" temp)
1021 ;; (ada-prj-put 'casing temp)
1022 ;; temp)
1023 (error "No exception file specified, and no project active. See variable `ada-case-exception-file'.")))
1024 )))
1025
1026 (unless word
1027 (if (use-region-p)
1028 (setq word (buffer-substring-no-properties (region-beginning) (region-end)))
1029 (save-excursion
1030 (let ((syntax (if partial "w" "w_")))
1031 (skip-syntax-backward syntax)
1032 (setq word
1033 (buffer-substring-no-properties
1034 (point)
1035 (progn (skip-syntax-forward syntax) (point))
1036 ))))))
1037
1038 (let* ((exceptions (ada-case-read-exceptions file-name))
1039 (full-exceptions (car exceptions))
1040 (partial-exceptions (cdr exceptions)))
1041
1042 (cond
1043 ((null partial)
1044 (setq ada-case-full-exceptions (ada-case-add-exception word ada-case-full-exceptions))
1045 (setq full-exceptions (ada-case-add-exception word full-exceptions)))
1046
1047 (t
1048 (setq ada-case-partial-exceptions (ada-case-add-exception word ada-case-partial-exceptions))
1049 (setq partial-exceptions (ada-case-add-exception word partial-exceptions)))
1050 )
1051 (ada-case-save-exceptions full-exceptions partial-exceptions file-name)
1052 (message "created %s case exception '%s' in file '%s'"
1053 (if partial "partial" "full")
1054 word
1055 file-name)
1056 ))
1057
1058 (defun ada-case-create-partial-exception ()
1059 "Define active region or word at point as a partial word exception.
1060 User is prompted to choose a file from project variable casing if it is a list."
1061 (interactive)
1062 (ada-case-create-exception nil nil t))
1063
1064 (defun ada-in-numeric-literal-p ()
1065 "Return t if point is after a prefix of a numeric literal."
1066 ;; FIXME: this is actually a based numeric literal; excludes 1234
1067 (looking-back "\\([0-9]+#[0-9a-fA-F_]+\\)" (line-beginning-position)))
1068
1069 (defvar ada-keywords nil
1070 "List of Ada keywords for current `ada-language-version'.")
1071
1072 (defun ada-after-keyword-p ()
1073 "Return non-nil if point is after an element of `ada-keywords'."
1074 (let ((word (buffer-substring-no-properties
1075 (save-excursion (skip-syntax-backward "w_") (point))
1076 (point))))
1077 (member (downcase word) ada-keywords)))
1078
1079 (defun ada-lower-case (start end _force-case-strict)
1080 (downcase-region start end))
1081
1082 (defun ada-upper-case (start end _force-case-strict)
1083 (upcase-region start end))
1084
1085 (defun ada-mixed-case (start end force-case-strict)
1086 "Adjust case of region START END to Mixed_Case."
1087 (let ((done nil)
1088 next)
1089 (if (or force-case-strict ada-case-strict)
1090 (downcase-region start end))
1091 (goto-char start)
1092 (while (not done)
1093 (setq next
1094 (or
1095 (save-excursion (when (search-forward "_" end t) (point-marker)))
1096 (copy-marker (1+ end))))
1097
1098 ;; upcase first char
1099 (upcase-region (point) (1+ (point)))
1100
1101 (goto-char next)
1102 (if (< (point) end)
1103 (setq start (point))
1104 (setq done t))
1105 )))
1106
1107 (defun ada-case-adjust-identifier (&optional force-case)
1108 "Adjust case of the previous word as an identifier.
1109 Uses `ada-case-identifier', with exceptions defined in
1110 `ada-case-full-exceptions', `ada-case-partial-exceptions'."
1111 (interactive)
1112 (save-excursion
1113 (let ((end (point-marker))
1114 (start (progn (skip-syntax-backward "w_") (point)))
1115 match
1116 next
1117 (done nil))
1118
1119 (if (setq match (assoc-string (buffer-substring-no-properties start end) ada-case-full-exceptions t))
1120 ;; full word exception
1121 (progn
1122 ;; 'save-excursion' puts a marker at 'end'; if we do
1123 ;; 'delete-region' first, it moves that marker to 'start',
1124 ;; then 'insert' inserts replacement text after the
1125 ;; marker, defeating 'save-excursion'. So we do 'insert' first.
1126 (insert (car match))
1127 (delete-region (point) end))
1128
1129 ;; else apply ada-case-identifier
1130 (funcall ada-case-identifier start end force-case)
1131
1132 ;; apply partial-exceptions
1133 (goto-char start)
1134 (while (not done)
1135 (setq next
1136 (or
1137 (save-excursion (when (search-forward "_" end t) (point-marker)))
1138 (copy-marker (1+ end))))
1139
1140 (when (setq match (assoc-string (buffer-substring-no-properties start (1- next))
1141 ada-case-partial-exceptions t))
1142 ;; see comment above at 'full word exception' for why
1143 ;; we do insert first.
1144 (insert (car match))
1145 (delete-region (point) (1- next)))
1146
1147 (goto-char next)
1148 (if (< (point) end)
1149 (setq start (point))
1150 (setq done t))
1151 )))))
1152
1153 (defun ada-case-adjust (&optional typed-char in-comment)
1154 "Adjust the case of the word before point.
1155 When invoked interactively, TYPED-CHAR must be
1156 `last-command-event', and it must not have been inserted yet.
1157 If IN-COMMENT is non-nil, adjust case of words in comments and strings as code,
1158 and treat `ada-case-strict' as t in code.."
1159 (when (not (bobp))
1160 (when (save-excursion
1161 (forward-char -1); back to last character in word
1162 (and (not (bobp))
1163 (eq (char-syntax (char-after)) ?w); it can be capitalized
1164
1165 (not (and (eq typed-char ?')
1166 (eq (char-before (point)) ?'))); character literal
1167
1168 (or in-comment
1169 (not (ada-in-string-or-comment-p)))
1170 ;; we sometimes want to capitialize an Ada identifier
1171 ;; referenced in a comment, via
1172 ;; ada-case-adjust-at-point.
1173
1174 (not (ada-in-numeric-literal-p))
1175 ))
1176
1177 ;; The indentation engine may trigger a reparse on
1178 ;; non-whitespace changes, but we know we don't need to reparse
1179 ;; for this change (assuming the user has not abused case
1180 ;; exceptions!).
1181 (let ((inhibit-modification-hooks t))
1182 (cond
1183 ;; Some attributes are also keywords, but captialized as
1184 ;; attributes. So check for attribute first.
1185 ((and
1186 (not in-comment)
1187 (save-excursion
1188 (skip-syntax-backward "w_")
1189 (eq (char-before) ?')))
1190 (ada-case-adjust-identifier in-comment))
1191
1192 ((and
1193 (not in-comment)
1194 (not (eq typed-char ?_))
1195 (ada-after-keyword-p))
1196 (funcall ada-case-keyword -1))
1197
1198 (t (ada-case-adjust-identifier in-comment))
1199 ))
1200 )))
1201
1202 (defun ada-case-adjust-at-point (&optional in-comment)
1203 "Adjust case of word at point, move to end of word.
1204 With prefix arg, adjust case as code even if in comment;
1205 otherwise, capitalize words in comments."
1206 (interactive "P")
1207 (cond
1208 ((and (not in-comment)
1209 (ada-in-string-or-comment-p))
1210 (skip-syntax-backward "w_")
1211 (capitalize-word 1))
1212
1213 (t
1214 (when
1215 (and (not (eobp))
1216 ;; we use '(syntax-after (point))' here, not '(char-syntax
1217 ;; (char-after))', because the latter does not respect
1218 ;; ada-syntax-propertize.
1219 (memq (syntax-class (syntax-after (point))) '(2 3)))
1220 (skip-syntax-forward "w_"))
1221 (ada-case-adjust nil in-comment))
1222 ))
1223
1224 (defun ada-case-adjust-region (begin end)
1225 "Adjust case of all words in region BEGIN END."
1226 (interactive "r")
1227 (narrow-to-region begin end)
1228 (save-excursion
1229 (goto-char begin)
1230 (while (not (eobp))
1231 (forward-comment (point-max))
1232 (skip-syntax-forward "^w_")
1233 (skip-syntax-forward "w_")
1234 (ada-case-adjust)))
1235 (widen))
1236
1237 (defun ada-case-adjust-buffer ()
1238 "Adjust case of current buffer."
1239 (interactive)
1240 (ada-case-adjust-region (point-min) (point-max)))
1241
1242 (defun ada-case-adjust-interactive (arg)
1243 "If `ada-auto-case' is non-nil, adjust the case of the previous word, and process the character just typed.
1244 To be bound to keys that should cause auto-casing.
1245 ARG is the prefix the user entered with \\[universal-argument]."
1246 (interactive "P")
1247
1248 ;; character typed has not been inserted yet
1249 (let ((lastk last-command-event))
1250
1251 (cond
1252 ((eq lastk ?\n)
1253 (when ada-auto-case
1254 (ada-case-adjust lastk))
1255 (funcall ada-lfd-binding))
1256
1257 ((memq lastk '(?\r return))
1258 (when ada-auto-case
1259 (ada-case-adjust lastk))
1260 (funcall ada-ret-binding))
1261
1262 (t
1263 (when ada-auto-case
1264 (ada-case-adjust lastk))
1265 (self-insert-command (prefix-numeric-value arg)))
1266 )))
1267
1268 ;;;; project files
1269
1270 ;; An Emacs Ada mode project file can specify several things:
1271 ;;
1272 ;; - a compiler-specific project file
1273 ;;
1274 ;; - compiler-specific environment variables
1275 ;;
1276 ;; - other compiler-specific things (see the compiler support elisp code)
1277 ;;
1278 ;; - a list of source directories (in addition to those specified in the compiler project file)
1279 ;;
1280 ;; - a casing exception file
1281 ;;
1282 ;; All of the data used by Emacs Ada mode functions specified in a
1283 ;; project file is stored in a property list. The property list is
1284 ;; stored in an alist indexed by the project file name, so multiple
1285 ;; project files can be selected without re-parsing them (some
1286 ;; compiler project files can take a long time to parse).
1287
1288 (defvar ada-prj-alist nil
1289 "Alist holding currently parsed Emacs Ada project files. Indexed by absolute project file name.")
1290
1291 (defvar ada-prj-current-file nil
1292 "Current Emacs Ada project file.")
1293
1294 (defvar ada-prj-current-project nil
1295 "Current Emacs Ada mode project; a plist.")
1296
1297 (defun ada-prj-get (prop &optional plist)
1298 "Return value of PROP in PLIST.
1299 Optional PLIST defaults to `ada-prj-current-project'."
1300 (let ((prj (or plist ada-prj-current-project)))
1301 (if prj
1302 (plist-get prj prop)
1303
1304 ;; no project, just use default vars
1305 ;; must match code in ada-prj-default
1306 (cl-case prop
1307 (ada_compiler ada-compiler)
1308 (auto_case ada-auto-case)
1309 (case_keyword ada-case-keyword)
1310 (case_identifier ada-case-identifier)
1311 (case_strict ada-case-strict)
1312 (casing (if (listp ada-case-exception-file)
1313 ada-case-exception-file
1314 (list ada-case-exception-file)))
1315 (path_sep path-separator)
1316 (proc_env process-environment)
1317 (src_dir (list "."))
1318 (xref_tool ada-xref-tool)
1319 ))))
1320
1321 (defun ada-prj-put (prop val &optional plist)
1322 "Set value of PROP in PLIST to VAL.
1323 Optional PLIST defaults to `ada-prj-current-project'."
1324 (plist-put (or plist ada-prj-current-project) prop val))
1325
1326 (defun ada-require-project-file ()
1327 (unless ada-prj-current-file
1328 (error "no Emacs Ada project file specified")))
1329
1330 (defvar ada-prj-default-list nil
1331 ;; project file parse
1332 "List of functions to add default project variables. Called
1333 with one argument; the default project properties list. Function
1334 should add to the properties list and return it.")
1335
1336 (defvar ada-prj-default-compiler-alist nil
1337 ;; project file parse
1338 "Compiler-specific function to set default project variables.
1339 Indexed by ada-compiler. Called with one argument; the default
1340 project properties list. Function should add to the properties
1341 list and return it.")
1342
1343 (defvar ada-prj-default-xref-alist nil
1344 ;; project file parse
1345 "Xref-tool-specific function to set default project variables.
1346 Indexed by ada-xref-tool. Called with one argument; the default
1347 project properties list. Function should add to the properties
1348 list and return it.")
1349
1350 (defun ada-prj-default (&optional src-dir)
1351 "Return the default project properties list.
1352 If SRC-DIR is non-nil, use it as the default for src_dir.
1353 Include properties set via `ada-prj-default-compiler-alist',
1354 `ada-prj-default-xref-alist'."
1355
1356 (let (project func)
1357 (setq
1358 project
1359 (list
1360 ;; variable name alphabetical order
1361 'ada_compiler ada-compiler
1362 'auto_case ada-auto-case
1363 'case_keyword ada-case-keyword
1364 'case_identifier ada-case-identifier
1365 'case_strict ada-case-strict
1366 'casing (if (listp ada-case-exception-file)
1367 ada-case-exception-file
1368 (list ada-case-exception-file))
1369 'path_sep path-separator;; prj variable so users can override it for their compiler
1370 'proc_env process-environment
1371 'src_dir (list (if src-dir src-dir "."))
1372 'xref_tool ada-xref-tool
1373 ))
1374
1375 (cl-dolist (func ada-prj-default-list)
1376 (setq project (funcall func project)))
1377
1378 (setq func (cdr (assq ada-compiler ada-prj-default-compiler-alist)))
1379 (when func (setq project (funcall func project)))
1380 (setq func (cdr (assq ada-xref-tool ada-prj-default-xref-alist)))
1381 (when func (setq project (funcall func project)))
1382 project))
1383
1384 (defvar ada-prj-parser-alist
1385 (mapcar
1386 (lambda (ext) (cons ext 'ada-prj-parse-file-1))
1387 ada-prj-file-extensions)
1388 ;; project file parse
1389 "Alist of parsers for project files, indexed by file extension.
1390 Default provides the minimal Ada mode parser; compiler support
1391 code may add other parsers. Parser is called with two arguments;
1392 the project file name and the current project property
1393 list. Parser must modify or add to the property list and return it.")
1394
1395 ;; This autoloaded because it is often used in Makefiles, and thus
1396 ;; will be the first ada-mode function executed.
1397 ;;;###autoload
1398 (defun ada-parse-prj-file (prj-file)
1399 "Read Emacs Ada or compiler-specific project file PRJ-FILE, set project properties in `ada-prj-alist'."
1400 ;; Not called ada-prj-parse-file for Ada mode 4.01 compatibility
1401 ;; FIXME: use the right name, add an alias
1402 (let ((project (ada-prj-default))
1403 (parser (cdr (assoc (file-name-extension prj-file) ada-prj-parser-alist))))
1404
1405 (setq prj-file (expand-file-name prj-file))
1406
1407 (unless (file-readable-p prj-file)
1408 (error "Project file '%s' is not readable" prj-file))
1409
1410 (if parser
1411 ;; parser may reference the "current project", so bind that now.
1412 (let ((ada-prj-current-project project)
1413 (ada-prj-current-file prj-file))
1414 (setq project (funcall parser prj-file project)))
1415 (error "no project file parser defined for '%s'" prj-file))
1416
1417 ;; Store the project properties
1418 (if (assoc prj-file ada-prj-alist)
1419 (setcdr (assoc prj-file ada-prj-alist) project)
1420 (add-to-list 'ada-prj-alist (cons prj-file project)))
1421
1422 ;; return t for interactive use
1423 t))
1424
1425 (defun ada-prj-reparse-select-current ()
1426 "Reparse the current project file, re-select it.
1427 Useful when the project file has been edited."
1428 (ada-parse-prj-file ada-prj-current-file)
1429 (ada-select-prj-file ada-prj-current-file))
1430
1431 (defvar ada-prj-parse-one-compiler nil
1432 ;; project file parse
1433 "Compiler-specific function to process one Ada project property.
1434 Indexed by project variable ada_compiler.
1435 Called with three arguments; the property name, property value,
1436 and project properties list. Function should add to or modify the
1437 properties list and return it, or return nil if the name is not
1438 recognized.")
1439
1440 (defvar ada-prj-parse-one-xref nil
1441 ;; project file parse
1442 "Xref-tool-specific function to process one Ada project property.
1443 Indexed by project variable xref_tool.
1444 Called with three arguments; the property name, property value,
1445 and project properties list. Function should add to or modify the
1446 properties list and return it, or return nil if the name is not
1447 recognized.")
1448
1449 (defvar ada-prj-parse-final-compiler nil
1450 ;; project file parse
1451 "Alist of compiler-specific functions to finish processing Ada project properties.
1452 Indexed by project variable ada_compiler.
1453 Called with one argument; the project properties list. Function
1454 should add to or modify the list and return it.")
1455
1456 (defvar ada-prj-parse-final-xref nil
1457 ;; project file parse
1458 "Alist of xref-tool-specific functions to finish processing Ada project properties.
1459 Indexed by project variable xref_tool.
1460 Called with one argument; the project properties list. Function
1461 should add to or modify the list and return it.")
1462
1463 (defun ada-prj-parse-file-1 (prj-file project)
1464 "Parse the Ada mode project file PRJ-FILE, set project properties in PROJECT.
1465 Return new value of PROJECT."
1466 (let (;; fields that are lists or that otherwise require special processing
1467 casing src_dir
1468 tmp-prj
1469 (parse-one-compiler (cdr (assoc ada-compiler ada-prj-parse-one-compiler)))
1470 (parse-final-compiler (cdr (assoc ada-compiler ada-prj-parse-final-compiler)))
1471 (parse-one-xref (cdr (assoc ada-xref-tool ada-prj-parse-one-xref)))
1472 (parse-final-xref (cdr (assoc ada-xref-tool ada-prj-parse-final-xref))))
1473
1474 (with-current-buffer (find-file-noselect prj-file)
1475 (goto-char (point-min))
1476
1477 ;; process each line
1478 (while (not (eobp))
1479
1480 ;; ignore lines that don't have the format "name=value", put
1481 ;; 'name', 'value' in match-string.
1482 (when (looking-at "^\\([^=\n]+\\)=\\(.*\\)")
1483 (cond
1484 ;; variable name alphabetical order
1485
1486 ((string= (match-string 1) "ada_compiler")
1487 (let ((comp (intern (match-string 2))))
1488 (setq project (plist-put project 'ada_compiler comp))
1489 (setq parse-one-compiler (cdr (assq comp ada-prj-parse-one-compiler)))
1490 (setq parse-final-compiler (cdr (assq comp ada-prj-parse-final-compiler)))))
1491
1492 ((string= (match-string 1) "auto_case")
1493 (setq project (plist-put project 'auto_case (intern (match-string 2)))))
1494
1495 ((string= (match-string 1) "case_keyword")
1496 (setq project (plist-put project 'case_keyword (intern (match-string 2)))))
1497
1498 ((string= (match-string 1) "case_identifier")
1499 (setq project (plist-put project 'case_identifier (intern (match-string 2)))))
1500
1501 ((string= (match-string 1) "case_strict")
1502 (setq project (plist-put project 'case_strict (intern (match-string 2)))))
1503
1504 ((string= (match-string 1) "casing")
1505 (cl-pushnew (expand-file-name
1506 (substitute-in-file-name (match-string 2)))
1507 casing :test #'equal))
1508
1509 ((string= (match-string 1) "el_file")
1510 (let ((file (expand-file-name (substitute-in-file-name (match-string 2)))))
1511 (setq project (plist-put project 'el_file file))
1512 ;; eval now as well as in select, since it might affect parsing
1513 (load-file file)))
1514
1515 ((string= (match-string 1) "src_dir")
1516 (cl-pushnew (file-name-as-directory
1517 (expand-file-name (match-string 2)))
1518 src_dir :test #'equal))
1519
1520 ((string= (match-string 1) "xref_tool")
1521 (let ((xref (intern (match-string 2))))
1522 (setq project (plist-put project 'xref_tool xref))
1523 (setq parse-one-xref (cdr (assq xref ada-prj-parse-one-xref)))
1524 (setq parse-final-xref (cdr (assq xref ada-prj-parse-final-xref)))))
1525
1526 (t
1527 (if (or
1528 (and parse-one-compiler
1529 (setq tmp-prj (funcall parse-one-compiler (match-string 1) (match-string 2) project)))
1530 (and parse-one-xref
1531 (setq tmp-prj (funcall parse-one-xref (match-string 1) (match-string 2) project))))
1532
1533 (setq project tmp-prj)
1534
1535 ;; Any other field in the file is set as an environment
1536 ;; variable or a project file.
1537 (if (= ?$ (elt (match-string 1) 0))
1538 ;; process env var. We don't do expand-file-name
1539 ;; here because the application may be expecting a
1540 ;; simple string.
1541 (let ((process-environment (plist-get project 'proc_env)))
1542 (setenv (substring (match-string 1) 1)
1543 (substitute-in-file-name (match-string 2)))
1544 (setq project
1545 (plist-put project 'proc_env process-environment)))
1546
1547 ;; not recognized; assume it is a user-defined variable like "comp_opt"
1548 (setq project (plist-put project (intern (match-string 1)) (match-string 2)))
1549 )))
1550 ))
1551
1552 (forward-line 1))
1553
1554 );; done reading file
1555
1556 ;; process accumulated lists
1557 (if casing (setq project (plist-put project 'casing (reverse casing))))
1558 (if src_dir (setq project (plist-put project 'src_dir (reverse src_dir))))
1559
1560 (when parse-final-compiler
1561 ;; parse-final-compiler may reference the "current project", so
1562 ;; bind that now, to include the properties set above.
1563 (let ((ada-prj-current-project project)
1564 (ada-prj-current-file prj-file))
1565 (setq project (funcall parse-final-compiler project))))
1566
1567 (when parse-final-xref
1568 (let ((ada-prj-current-project project)
1569 (ada-prj-current-file prj-file))
1570 (setq project (funcall parse-final-xref project))))
1571
1572 project
1573 ))
1574
1575 (defvar ada-select-prj-compiler nil
1576 "Alist of functions to call for compiler specific project file selection.
1577 Indexed by project variable ada_compiler.")
1578
1579 (defvar ada-deselect-prj-compiler nil
1580 "Alist of functions to call for compiler specific project file deselection.
1581 Indexed by project variable ada_compiler.")
1582
1583 (defvar ada-select-prj-xref-tool nil
1584 "Alist of functions to call for xref-tool specific project file selection.
1585 Indexed by project variable xref_tool.")
1586
1587 (defvar ada-deselect-prj-xref-tool nil
1588 "Alist of functions to call for xref-tool specific project file deselection.
1589 Indexed by project variable xref_tool.")
1590
1591 (defun ada-select-prj-file (prj-file)
1592 "Select PRJ-FILE as the current project file."
1593 (interactive)
1594 (setq prj-file (expand-file-name prj-file))
1595
1596 (setq ada-prj-current-project (cdr (assoc prj-file ada-prj-alist)))
1597
1598 (when (null ada-prj-current-project)
1599 (setq ada-prj-current-file nil)
1600 (error "Project file '%s' was not previously parsed." prj-file))
1601
1602 (let ((func (cdr (assq (ada-prj-get 'ada_compiler) ada-deselect-prj-compiler))))
1603 (when func (funcall func)))
1604
1605 (let ((func (cdr (assq (ada-prj-get 'xref_tool) ada-deselect-prj-xref-tool))))
1606 (when func (funcall func)))
1607
1608 (setq ada-prj-current-file prj-file)
1609
1610 ;; Project file should fully specify what compilers are used,
1611 ;; including what compilation filters they need. There may be more
1612 ;; than just an Ada compiler.
1613 (setq compilation-error-regexp-alist nil)
1614 (setq compilation-filter-hook nil)
1615
1616 (when (ada-prj-get 'el_file)
1617 (load-file (ada-prj-get 'el_file)))
1618
1619 (ada-case-read-all-exceptions)
1620
1621 (setq compilation-search-path (ada-prj-get 'src_dir))
1622
1623 (let ((func (cdr (assq (ada-prj-get 'ada_compiler) ada-select-prj-compiler))))
1624 (when func (funcall func)))
1625
1626 (let ((func (cdr (assq (ada-prj-get 'xref_tool) ada-select-prj-xref-tool))))
1627 (when func (funcall func)))
1628
1629 ;; return 't', for decent display in message buffer when called interactively
1630 t)
1631
1632 (defun ada-create-select-default-prj (&optional directory)
1633 "Create a default project with src_dir set to DIRECTORY (default current directory), select it."
1634 (let* ((dir (or directory default-directory))
1635 (prj-file (expand-file-name "default_.adp" dir))
1636 (project (ada-prj-default dir)))
1637
1638 (if (assoc prj-file ada-prj-alist)
1639 (setcdr (assoc prj-file ada-prj-alist) project)
1640 (add-to-list 'ada-prj-alist (cons prj-file project)))
1641
1642 (ada-select-prj-file prj-file)
1643 ))
1644
1645 (defun ada-prj-select ()
1646 "Select the current project file from the list of currently available project files."
1647 (interactive)
1648 (ada-select-prj-file (completing-read "project: " ada-prj-alist nil t))
1649 )
1650
1651 (defun ada-prj-show ()
1652 "Show current Emacs Ada mode project file."
1653 (interactive)
1654 (message "current Emacs Ada mode project file: %s" ada-prj-current-file))
1655
1656 (defvar ada-prj-show-path nil
1657 ;; Supplied by compiler
1658 "Function to show project search path used by compiler (and possibly xref tool)."
1659 )
1660
1661 (defun ada-prj-show-path ()
1662 (interactive)
1663 (when ada-prj-show-path
1664 (funcall ada-prj-show-path)))
1665
1666 (defvar ada-show-xref-tool-buffer nil
1667 ;; Supplied by xref tool
1668 "Function to show process buffer used by xref tool."
1669 )
1670
1671 (defun ada-show-xref-tool-buffer ()
1672 (interactive)
1673 (when ada-show-xref-tool-buffer
1674 (funcall ada-show-xref-tool-buffer)))
1675
1676 ;;;; syntax properties
1677
1678 (defvar ada-mode-syntax-table
1679 (let ((table (make-syntax-table)))
1680 ;; (info "(elisp)Syntax Class Table" "*info syntax class table*")
1681 ;; make-syntax-table sets all alphanumeric to w, etc; so we only
1682 ;; have to add ada-specific things.
1683
1684 ;; string brackets. `%' is the obsolete alternative string
1685 ;; bracket (arm J.2); if we make it syntax class ", it throws
1686 ;; font-lock and indentation off the track, so we use syntax class
1687 ;; $.
1688 (modify-syntax-entry ?% "$" table)
1689 (modify-syntax-entry ?\" "\"" table)
1690
1691 ;; punctuation; operators etc
1692 (modify-syntax-entry ?# "." table); based number - ada-wisi-number-literal-p requires this syntax
1693 (modify-syntax-entry ?& "." table)
1694 (modify-syntax-entry ?* "." table)
1695 (modify-syntax-entry ?+ "." table)
1696 (modify-syntax-entry ?- ". 12" table); operator; see ada-syntax-propertize for double hyphen as comment
1697 (modify-syntax-entry ?. "." table)
1698 (modify-syntax-entry ?/ "." table)
1699 (modify-syntax-entry ?: "." table)
1700 (modify-syntax-entry ?< "." table)
1701 (modify-syntax-entry ?= "." table)
1702 (modify-syntax-entry ?> "." table)
1703 (modify-syntax-entry ?\' "." table); attribute; see ada-syntax-propertize for character literal
1704 (modify-syntax-entry ?\; "." table)
1705 (modify-syntax-entry ?\\ "." table); default is escape; not correct for Ada strings
1706 (modify-syntax-entry ?\| "." table)
1707
1708 ;; and \f and \n end a comment
1709 (modify-syntax-entry ?\f ">" table)
1710 (modify-syntax-entry ?\n ">" table)
1711
1712 (modify-syntax-entry ?_ "_" table); symbol constituents, not word.
1713
1714 (modify-syntax-entry ?\( "()" table)
1715 (modify-syntax-entry ?\) ")(" table)
1716
1717 ;; skeleton placeholder delimiters; see ada-skel.el. We use generic
1718 ;; comment delimiter class, not comment starter/comment ender, so
1719 ;; these can be distinguished from line end.
1720 (modify-syntax-entry ?{ "!" table)
1721 (modify-syntax-entry ?} "!" table)
1722
1723 table
1724 )
1725 "Syntax table to be used for editing Ada source code.")
1726
1727 (defvar ada-syntax-propertize-hook nil
1728 ;; provided by preprocessor, lumped with xref-tool
1729 "Hook run from `ada-syntax-propertize'.
1730 Called by `syntax-propertize', which is called by font-lock in
1731 `after-change-functions'. Therefore, care must be taken to avoid
1732 race conditions with the grammar parser.")
1733
1734 (defun ada-syntax-propertize (start end)
1735 "Assign `syntax-table' properties in accessible part of buffer.
1736 In particular, character constants are set to have string syntax."
1737 ;; (info "(elisp)Syntax Properties")
1738 ;;
1739 ;; called from `syntax-propertize', inside save-excursion with-silent-modifications
1740 (let ((inhibit-read-only t)
1741 (inhibit-point-motion-hooks t))
1742 (goto-char start)
1743 (save-match-data
1744 (while (re-search-forward
1745 (concat
1746 "[^a-zA-Z0-9)]\\('\\)[^'\n]\\('\\)"; 1, 2: character literal, not attribute
1747 "\\|[^a-zA-Z0-9)]\\('''\\)"; 3: character literal '''
1748 "\\|\\(--\\)"; 4: comment start
1749 )
1750 end t)
1751 ;; syntax-propertize-extend-region-functions is set to
1752 ;; syntax-propertize-wholelines by default. We assume no
1753 ;; coding standard will permit a character literal at the
1754 ;; start of a line (not preceded by whitespace).
1755 (cond
1756 ((match-beginning 1)
1757 (put-text-property
1758 (match-beginning 1) (match-end 1) 'syntax-table '(7 . ?'))
1759 (put-text-property
1760 (match-beginning 2) (match-end 2) 'syntax-table '(7 . ?')))
1761 ((match-beginning 3)
1762 (put-text-property
1763 (match-beginning 3) (1+ (match-beginning 3)) 'syntax-table '(7 . ?'))
1764 (put-text-property
1765 (1- (match-end 3)) (match-end 3) 'syntax-table '(7 . ?')))
1766 ((match-beginning 4)
1767 (put-text-property
1768 (match-beginning 4) (match-end 4) 'syntax-table '(11 . nil)))
1769 )))
1770 (run-hook-with-args 'ada-syntax-propertize-hook start end))
1771 )
1772
1773 (defun ada-in-comment-p (&optional parse-result)
1774 "Return t if inside a comment.
1775 If PARSE-RESULT is non-nil, use it instead of calling `syntax-ppss'."
1776 (nth 4 (or parse-result (syntax-ppss))))
1777
1778 (defun ada-in-string-p (&optional parse-result)
1779 "Return t if point is inside a string.
1780 If PARSE-RESULT is non-nil, use it instead of calling `syntax-ppss'."
1781 (nth 3 (or parse-result (syntax-ppss))))
1782
1783 (defun ada-in-string-or-comment-p (&optional parse-result)
1784 "Return t if inside a comment or string.
1785 If PARSE-RESULT is non-nil, use it instead of calling `syntax-ppss'."
1786 (setq parse-result (or parse-result (syntax-ppss)))
1787 (or (ada-in-string-p parse-result) (ada-in-comment-p parse-result)))
1788
1789 (defun ada-in-paren-p (&optional parse-result)
1790 "Return t if point is inside a pair of parentheses.
1791 If PARSE-RESULT is non-nil, use it instead of calling `syntax-ppss'."
1792 (> (nth 0 (or parse-result (syntax-ppss))) 0))
1793
1794 (defun ada-goto-open-paren (&optional offset parse-result)
1795 "Move point to innermost opening paren surrounding current point, plus OFFSET.
1796 Throw error if not in paren. If PARSE-RESULT is non-nil, use it
1797 instead of calling `syntax-ppss'."
1798 (goto-char (+ (or offset 0) (nth 1 (or parse-result (syntax-ppss))))))
1799
1800 ;;;; navigation within and between files
1801
1802 (defvar ada-body-suffixes '(".adb")
1803 "List of possible suffixes for Ada body files.
1804 The extensions should include a `.' if needed.")
1805
1806 (defvar ada-spec-suffixes '(".ads")
1807 "List of possible suffixes for Ada spec files.
1808 The extensions should include a `.' if needed.")
1809
1810 (defvar ada-other-file-alist
1811 '(("\\.ads$" (".adb"))
1812 ("\\.adb$" (".ads")))
1813 "Alist used by `find-file' to find the name of the other package.
1814 See `ff-other-file-alist'.")
1815
1816 (defconst ada-name-regexp
1817 "\\(\\(?:\\sw\\|[_.]\\)+\\)")
1818
1819 (defconst ada-parent-name-regexp
1820 "\\([a-zA-Z0-9_\\.]+\\)\\.[a-zA-Z0-9_]+"
1821 "Regexp for extracting the parent name from fully-qualified name.")
1822
1823 (defvar ada-file-name-from-ada-name nil
1824 ;; determined by ada-xref-tool, set by *-select-prj
1825 "Function called with one parameter ADA-NAME, which is a library
1826 unit name; it should return the filename in which ADA-NAME is
1827 found.")
1828
1829 (defun ada-file-name-from-ada-name (ada-name)
1830 "Return the filename in which ADA-NAME is found."
1831 (ada-require-project-file)
1832 (funcall ada-file-name-from-ada-name ada-name))
1833
1834 (defvar ada-ada-name-from-file-name nil
1835 ;; supplied by compiler
1836 "Function called with one parameter FILE-NAME, which is a library
1837 unit name; it should return the Ada name that should be found in FILE-NAME.")
1838
1839 (defun ada-ada-name-from-file-name (file-name)
1840 "Return the ada-name that should be found in FILE-NAME."
1841 (ada-require-project-file)
1842 (funcall ada-ada-name-from-file-name file-name))
1843
1844 (defun ada-ff-special-extract-parent ()
1845 (setq ff-function-name (match-string 1))
1846 (file-name-nondirectory
1847 (or
1848 (ff-get-file-name
1849 compilation-search-path
1850 (ada-file-name-from-ada-name ff-function-name)
1851 ada-spec-suffixes)
1852 (error "parent '%s' not found; set project file?" ff-function-name))))
1853
1854 (defun ada-ff-special-with ()
1855 (let ((package-name (match-string 1)))
1856 (setq ff-function-name (concat "^package\\s-+" package-name "\\([^_]\\|$\\)"))
1857 (file-name-nondirectory
1858 (or
1859 (ff-get-file-name
1860 compilation-search-path
1861 (ada-file-name-from-ada-name package-name)
1862 (append ada-spec-suffixes ada-body-suffixes))
1863 (error "package '%s' not found; set project file?" package-name)))
1864 ))
1865
1866 (defun ada-set-ff-special-constructs ()
1867 "Add Ada-specific pairs to `ff-special-constructs'."
1868 (set (make-local-variable 'ff-special-constructs) nil)
1869 (mapc (lambda (pair) (add-to-list 'ff-special-constructs pair))
1870 ;; Each car is a regexp; if it matches at point, the cdr is invoked.
1871 ;; Each cdr should set ff-function-name to a string or regexp
1872 ;; for ada-set-point-accordingly, and return the file name
1873 ;; (sans directory, must include suffix) to go to.
1874 (list
1875 ;; Top level child package declaration (not body), or child
1876 ;; subprogram declaration or body; go to the parent package.
1877 (cons (concat "^\\(?:private[ \t]+\\)?\\(?:package\\|procedure\\|function\\)[ \t]+"
1878 ada-parent-name-regexp "\\(?:;\\|[ \t]+\\|$\\)")
1879 'ada-ff-special-extract-parent)
1880
1881 ;; A "with" clause. Note that it may refer to a procedure body, as well as a spec
1882 (cons (concat "^\\(?:limited[ \t]+\\)?\\(?:private[ \t]+\\)?with[ \t]+" ada-name-regexp)
1883 'ada-ff-special-with)
1884 )))
1885
1886 (defvar ada-which-function nil
1887 ;; supplied by indentation engine
1888 ;;
1889 ;; This is run from ff-pre-load-hook, so ff-function-name may have
1890 ;; been set by ff-treat-special; don't reset it.
1891 "Function called with no parameters; it should return the name
1892 of the package, protected type, subprogram, or task type whose
1893 definition/declaration point is in or just after, or nil. In
1894 addition, if ff-function-name is non-nil, store in
1895 ff-function-name a regexp that will find the function in the
1896 other file.")
1897
1898 (defun ada-which-function ()
1899 "See `ada-which-function' variable."
1900 (interactive)
1901 (when ada-which-function
1902 (funcall ada-which-function)))
1903
1904 (defvar ada-on-context-clause nil
1905 ;; supplied by indentation engine
1906 "Function called with no parameters; it should return non-nil
1907 if point is on a context clause.")
1908
1909 (defun ada-on-context-clause ()
1910 "See `ada-on-context-clause' variable."
1911 (interactive)
1912 (when ada-on-context-clause
1913 (funcall ada-on-context-clause)))
1914
1915 (defvar ada-goto-subunit-name nil
1916 ;; supplied by indentation engine
1917 "Function called with no parameters; if the current buffer
1918 contains a subunit, move point to the subunit name (for
1919 `ada-goto-declaration'), return t; otherwise leave point alone,
1920 return nil.")
1921
1922 (defun ada-goto-subunit-name ()
1923 "See `ada-goto-subunit-name' variable."
1924 (interactive)
1925 (when ada-goto-subunit-name
1926 (funcall ada-goto-subunit-name)))
1927
1928 (defun ada-add-log-current-function ()
1929 "For `add-log-current-defun-function'; uses `ada-which-function'."
1930 ;; add-log-current-defun is typically called with point at the start
1931 ;; of an ediff change section, which is before the start of the
1932 ;; declaration of a new item. So go to the end of the current line
1933 ;; first, then call `ada-which-function'
1934 (save-excursion
1935 (end-of-line 1)
1936 (ada-which-function)))
1937
1938 (defun ada-set-point-accordingly ()
1939 "Move to the string specified in `ff-function-name', which may be a regexp,
1940 previously set by a file navigation command."
1941 (when ff-function-name
1942 (let ((done nil)
1943 (found nil))
1944 (goto-char (point-min))
1945 ;; We are looking for an Ada declaration, so don't stop for strings or comments
1946 ;;
1947 ;; This will still be confused by multiple references; we need
1948 ;; to use compiler cross reference info for more precision.
1949 (while (not done)
1950 (if (search-forward-regexp ff-function-name nil t)
1951 (setq found (match-beginning 0))
1952 ;; not in remainder of buffer
1953 (setq done t))
1954 (if (ada-in-string-or-comment-p)
1955 (setq found nil)
1956 (setq done t)))
1957 (when found
1958 (goto-char found)
1959 ;; different parsers find different points on the line; normalize here
1960 (back-to-indentation))
1961 (setq ff-function-name nil))))
1962
1963 (defun ada-check-current-project (file-name)
1964 "Throw error if FILE-NAME (must be absolute) is not found in
1965 the current project source directories, or if no project has been
1966 set."
1967 (when (null (car compilation-search-path))
1968 (error "no file search path defined; set project file?"))
1969
1970 ;; file-truename handles symbolic links
1971 (let* ((visited-file (file-truename file-name))
1972 (found-file (locate-file (file-name-nondirectory visited-file)
1973 compilation-search-path)))
1974 (unless found-file
1975 (error "current file not part of current project; wrong project?"))
1976
1977 (setq found-file (file-truename found-file))
1978
1979 ;; (nth 10 (file-attributes ...)) is the inode; required when hard
1980 ;; links are present.
1981 (let* ((visited-file-inode (nth 10 (file-attributes visited-file)))
1982 (found-file-inode (nth 10 (file-attributes found-file))))
1983 (unless (equal visited-file-inode found-file-inode)
1984 (error "%s (opened) and %s (found in project) are two different files"
1985 file-name found-file)))))
1986
1987 (defun ada-find-other-file-noset (other-window)
1988 "Same as `ada-find-other-file', but preserve point in the other file,
1989 don't move to corresponding declaration."
1990 (interactive "P")
1991 (ada-find-other-file other-window t))
1992
1993 (defun ada-find-other-file (other-window &optional _no-set-point)
1994 "Move to the corresponding declaration in another file.
1995
1996 - If region is active, assume it contains a package name;
1997 position point on that package declaration.
1998
1999 - If point is in the start line of a non-nested child package or
2000 subprogram declaration, position point on the corresponding
2001 parent package specification.
2002
2003 - If point is in a context clause line, position point on the
2004 first package declaration that is mentioned.
2005
2006 - If point is in a separate body, position point on the
2007 corresponding specification.
2008
2009 - If point is in a subprogram body or specification, position point
2010 on the corresponding specification or body.
2011
2012 If OTHER-WINDOW (set by interactive prefix) is non-nil, show the
2013 buffer in another window.
2014
2015 If NO-SET-POINT is nil, set point in the other file on the
2016 corresponding declaration. If non-nil, preserve existing point in
2017 the other file."
2018
2019 ;; ff-get-file, ff-find-other file first process
2020 ;; ff-special-constructs, then run the following hooks:
2021 ;;
2022 ;; ff-pre-load-hook set to ada-which-function
2023 ;; ff-file-created-hook set to ada-ff-create-body
2024 ;; ff-post-load-hook set to ada-set-point-accordingly,
2025 ;; or to a compiler-specific function that
2026 ;; uses compiler-generated cross reference
2027 ;; information
2028
2029 (interactive "P")
2030 (ada-check-current-project (buffer-file-name))
2031
2032 (cond
2033 (mark-active
2034 (setq ff-function-name (buffer-substring-no-properties (point) (mark)))
2035 (ff-get-file
2036 compilation-search-path
2037 (ada-file-name-from-ada-name ff-function-name)
2038 ada-spec-suffixes
2039 other-window)
2040 (deactivate-mark))
2041
2042 ((and (not (ada-on-context-clause))
2043 (ada-goto-subunit-name))
2044 (ada-goto-declaration other-window))
2045
2046 (t
2047 (ff-find-other-file other-window)))
2048 )
2049
2050 (defun ada-find-file (filename)
2051 ;; we assume compliation-search-path is set, either by an
2052 ;; ada-mode project, or by some other means.
2053 ;; FIXME: option to filter with ada-*-suffixes?
2054 (interactive (list (completing-read "File: "
2055 (apply-partially
2056 'locate-file-completion-table
2057 compilation-search-path nil))))
2058 (find-file (locate-file filename compilation-search-path))
2059 )
2060
2061 (defvar ada-operator-re
2062 "\\+\\|-\\|/\\|\\*\\*\\|\\*\\|=\\|&\\|abs\\|mod\\|rem\\|and\\|not\\|or\\|xor\\|<=\\|<\\|>=\\|>"
2063 "Regexp matching Ada operator_symbol.")
2064
2065 (defun ada-identifier-at-point ()
2066 "Return the identifier around point, move point to start of
2067 identifier. May be an Ada identifier or operator."
2068
2069 (when (ada-in-comment-p)
2070 (error "Inside comment"))
2071
2072 (skip-chars-backward "a-zA-Z0-9_<>=+\\-\\*/&")
2073
2074 ;; Just in front of, or inside, a string => we could have an
2075 ;; operator function declaration.
2076 (cond
2077 ((ada-in-string-p)
2078 (cond
2079
2080 ((and (= (char-before) ?\")
2081 (progn
2082 (forward-char -1)
2083 (looking-at (concat "\"\\(" ada-operator-re "\\)\""))))
2084 (concat "\"" (match-string-no-properties 1) "\""))
2085
2086 (t
2087 (error "Inside string or character constant"))
2088 ))
2089
2090 ((and (= (char-after) ?\")
2091 (looking-at (concat "\"\\(" ada-operator-re "\\)\"")))
2092 (concat "\"" (match-string-no-properties 1) "\""))
2093
2094 ((looking-at "[a-zA-Z0-9_]+\\|[+\\-*/&=<>]")
2095 (match-string-no-properties 0))
2096
2097 (t
2098 (error "No identifier around"))
2099 ))
2100
2101 ;; FIXME: use find-tag-marker-ring, ring-insert, pop-tag-mark (see xref.el)
2102 (defvar ada-goto-pos-ring '()
2103 "List of positions selected by navigation functions. Used
2104 to go back to these positions.")
2105
2106 (defconst ada-goto-pos-ring-max 16
2107 "Number of positions kept in the list `ada-goto-pos-ring'.")
2108
2109 (defun ada-goto-push-pos ()
2110 "Push current filename, position on `ada-goto-pos-ring'. See `ada-goto-previous-pos'."
2111 (setq ada-goto-pos-ring (cons (list (point) (buffer-file-name)) ada-goto-pos-ring))
2112 (if (> (length ada-goto-pos-ring) ada-goto-pos-ring-max)
2113 (setcdr (nthcdr (1- ada-goto-pos-ring-max) ada-goto-pos-ring) nil)))
2114
2115 (defun ada-goto-previous-pos ()
2116 "Go to the first position in `ada-goto-pos-ring', pop `ada-goto-pos-ring'."
2117 (interactive)
2118 (when ada-goto-pos-ring
2119 (let ((pos (pop ada-goto-pos-ring)))
2120 (find-file (cadr pos))
2121 (goto-char (car pos)))))
2122
2123 (defun ada-goto-source (file line column other-window)
2124 "Find and select FILE, at LINE and COLUMN.
2125 FILE may be absolute, or on `compilation-search-path'.
2126
2127 If OTHER-WINDOW is non-nil, show the buffer in another window."
2128 (let ((file-1
2129 (if (file-name-absolute-p file) file
2130 (ff-get-file-name compilation-search-path file))))
2131 (if file-1
2132 (setq file file-1)
2133 (error "File %s not found; installed library, or set project?" file))
2134 )
2135
2136 (ada-goto-push-pos)
2137
2138 (let ((buffer (get-file-buffer file)))
2139 (cond
2140 ((bufferp buffer)
2141 (cond
2142 ((null other-window)
2143 (switch-to-buffer buffer))
2144
2145 (t (switch-to-buffer-other-window buffer))
2146 ))
2147
2148 ((file-exists-p file)
2149 (cond
2150 ((null other-window)
2151 (find-file file))
2152
2153 (t
2154 (find-file-other-window file))
2155 ))
2156
2157 (t
2158 (error "'%s' not found" file))))
2159
2160
2161 ;; move the cursor to the correct position
2162 (push-mark nil t)
2163 (goto-char (point-min))
2164 (forward-line (1- line))
2165 (forward-char column)
2166 )
2167
2168 (defvar ada-xref-refresh-function nil
2169 ;; determined by xref_tool, set by *-select-prj-xref
2170 "Function that refreshes cross reference information cache.")
2171
2172 (defun ada-xref-refresh ()
2173 "Refresh cross reference information cache, if any."
2174 (interactive)
2175
2176 (when (null ada-xref-refresh-function)
2177 (error "no cross reference information available"))
2178
2179 (funcall ada-xref-refresh-function)
2180 )
2181
2182 (defvar ada-xref-other-function nil
2183 ;; determined by xref_tool, set by *-select-prj-xref
2184 "Function that returns cross reference information.
2185 Function is called with four arguments:
2186 - an Ada identifier or operator_symbol
2187 - filename containing the identifier (full path)
2188 - line number containing the identifier
2189 - column of the start of the identifier
2190 Returns a list '(file line column) giving the corresponding location.
2191 'file' may be absolute, or on `compilation-search-path'. If point is
2192 at the specification, the corresponding location is the body, and vice
2193 versa.")
2194
2195 (defun ada-goto-declaration (other-window)
2196 "Move to the declaration or body of the identifier around point.
2197 If at the declaration, go to the body, and vice versa.
2198
2199 If OTHER-WINDOW (set by interactive prefix) is non-nil, show the
2200 buffer in another window."
2201 (interactive "P")
2202 (ada-check-current-project (buffer-file-name))
2203
2204 (when (null ada-xref-other-function)
2205 (error "no cross reference information available"))
2206
2207 (let ((target
2208 (funcall ada-xref-other-function
2209 (ada-identifier-at-point)
2210 (buffer-file-name)
2211 (line-number-at-pos)
2212 (1+ (current-column))
2213 )))
2214
2215 (ada-goto-source (nth 0 target)
2216 (nth 1 target)
2217 (nth 2 target)
2218 other-window)
2219 ))
2220
2221 (defvar ada-xref-parent-function nil
2222 ;; determined by xref_tool, set by *-select-prj-xref
2223 "Function that returns cross reference information.
2224 Function is called with four arguments:
2225 - an Ada identifier or operator_symbol
2226 - filename containing the identifier
2227 - line number containing the identifier
2228 - column of the start of the identifier
2229 Displays a buffer in compilation-mode giving locations of the parent type declarations.")
2230
2231 (defun ada-show-declaration-parents ()
2232 "Display the locations of the parent type declarations of the type identifier around point."
2233 (interactive)
2234 (ada-check-current-project (buffer-file-name))
2235
2236 (when (null ada-xref-parent-function)
2237 (error "no cross reference information available"))
2238
2239 (funcall ada-xref-parent-function
2240 (ada-identifier-at-point)
2241 (file-name-nondirectory (buffer-file-name))
2242 (line-number-at-pos)
2243 (1+ (current-column)))
2244 )
2245
2246 (defvar ada-xref-all-function nil
2247 ;; determined by xref_tool, set by *-select-prj-xref
2248 "Function that displays cross reference information.
2249 Called with four arguments:
2250 - an Ada identifier or operator_symbol
2251 - filename containing the identifier
2252 - line number containing the identifier
2253 - column of the start of the identifier
2254 Displays a buffer in compilation-mode giving locations where the
2255 identifier is declared or referenced.")
2256
2257 (defun ada-show-references ()
2258 "Show all references of identifier at point."
2259 (interactive)
2260 (ada-check-current-project (buffer-file-name))
2261
2262 (when (null ada-xref-all-function)
2263 (error "no cross reference information available"))
2264
2265 (funcall ada-xref-all-function
2266 (ada-identifier-at-point)
2267 (file-name-nondirectory (buffer-file-name))
2268 (line-number-at-pos)
2269 (1+ (current-column)))
2270 )
2271
2272 (defvar ada-xref-overriding-function nil
2273 ;; determined by ada-xref-tool, set by *-select-prj
2274 "Function that displays cross reference information for overriding subprograms.
2275 Called with four arguments:
2276 - an Ada identifier or operator_symbol
2277 - filename containing the identifier
2278 - line number containing the identifier
2279 - column of the start of the identifier
2280 Displays a buffer in compilation-mode giving locations of the overriding declarations.")
2281
2282 (defun ada-show-overriding ()
2283 "Show all overridings of identifier at point."
2284 (interactive)
2285 (ada-check-current-project (buffer-file-name))
2286
2287 (when (null ada-xref-overriding-function)
2288 (error "no cross reference information available"))
2289
2290 (funcall ada-xref-overriding-function
2291 (ada-identifier-at-point)
2292 (file-name-nondirectory (buffer-file-name))
2293 (line-number-at-pos)
2294 (1+ (current-column)))
2295 )
2296
2297 (defvar ada-xref-overridden-function nil
2298 ;; determined by ada-xref-tool, set by *-select-prj
2299 "Function that displays cross reference information for overridden subprogram.
2300 Called with four arguments:
2301 - an Ada identifier or operator_symbol
2302 - filename containing the identifier
2303 - line number containing the identifier
2304 - column of the start of the identifier
2305 Returns a list '(file line column) giving the corresponding location.
2306 'file' may be absolute, or on `compilation-search-path'.")
2307
2308 (defun ada-show-overridden (other-window)
2309 "Show the overridden declaration of identifier at point."
2310 (interactive "P")
2311 (ada-check-current-project (buffer-file-name))
2312
2313 (when (null ada-xref-overridden-function)
2314 (error "'show overridden' not supported, or no cross reference information available"))
2315
2316 (let ((target
2317 (funcall ada-xref-overridden-function
2318 (ada-identifier-at-point)
2319 (file-name-nondirectory (buffer-file-name))
2320 (line-number-at-pos)
2321 (1+ (current-column)))))
2322
2323 (ada-goto-source (nth 0 target)
2324 (nth 1 target)
2325 (nth 2 target)
2326 other-window)
2327
2328 ))
2329
2330 ;; This is autoloaded because it may be used in ~/.emacs
2331 ;;;###autoload
2332 (defun ada-add-extensions (spec body)
2333 "Define SPEC and BODY as being valid extensions for Ada files.
2334 SPEC and BODY are two regular expressions that must match against
2335 the file name."
2336 (let* ((reg (concat (regexp-quote body) "$"))
2337 (tmp (assoc reg ada-other-file-alist)))
2338 (if tmp
2339 (setcdr tmp (list (cons spec (cadr tmp))))
2340 (add-to-list 'ada-other-file-alist (list reg (list spec)))))
2341
2342 (let* ((reg (concat (regexp-quote spec) "$"))
2343 (tmp (assoc reg ada-other-file-alist)))
2344 (if tmp
2345 (setcdr tmp (list (cons body (cadr tmp))))
2346 (add-to-list 'ada-other-file-alist (list reg (list body)))))
2347
2348 (add-to-list 'auto-mode-alist
2349 (cons (concat (regexp-quote spec) "\\'") 'ada-mode))
2350 (add-to-list 'auto-mode-alist
2351 (cons (concat (regexp-quote body) "\\'") 'ada-mode))
2352
2353 (add-to-list 'ada-spec-suffixes spec)
2354 (add-to-list 'ada-body-suffixes body)
2355
2356 (when (fboundp 'speedbar-add-supported-extension)
2357 (speedbar-add-supported-extension spec)
2358 (speedbar-add-supported-extension body))
2359 )
2360
2361 (defun ada-show-secondary-error (other-window)
2362 "Show the next secondary file reference in the compilation buffer.
2363 A secondary file reference is defined by text having text
2364 property `ada-secondary-error'. These can be set by
2365 compiler-specific compilation filters.
2366
2367 If OTHER-WINDOW (set by interactive prefix) is non-nil, show the
2368 buffer in another window."
2369 (interactive "P")
2370
2371 ;; preserving the current window works only if the frame
2372 ;; doesn't change, at least on Windows.
2373 (let ((start-buffer (current-buffer))
2374 (start-window (selected-window))
2375 pos item file)
2376 (set-buffer compilation-last-buffer)
2377 (setq pos (next-single-property-change (point) 'ada-secondary-error))
2378 (when pos
2379 (setq item (get-text-property pos 'ada-secondary-error))
2380 ;; file-relative-name handles absolute Windows paths from
2381 ;; g++. Do this in compilation buffer to get correct
2382 ;; default-directory.
2383 (setq file (file-relative-name (nth 0 item)))
2384
2385 ;; Set point in compilation buffer past this secondary error, so
2386 ;; user can easily go to the next one. For some reason, this
2387 ;; doesn't change the visible point!?
2388 (forward-line 1))
2389
2390 (set-buffer start-buffer);; for windowing history
2391 (when item
2392 (ada-goto-source
2393 file
2394 (nth 1 item); line
2395 (nth 2 item); column
2396 other-window)
2397 (select-window start-window)
2398 )
2399 ))
2400
2401 (defvar ada-goto-declaration-start nil
2402 ;; Supplied by indentation engine.
2403 ;;
2404 ;; This is run from ff-pre-load-hook, so ff-function-name may have
2405 ;; been set by ff-treat-special; don't reset it.
2406 "For `beginning-of-defun-function'. Function to move point to
2407 start of the generic, package, protected, subprogram, or task
2408 declaration point is currently in or just after. Called with no
2409 parameters.")
2410
2411 (defun ada-goto-declaration-start ()
2412 "Call `ada-goto-declaration-start'."
2413 (interactive)
2414 (when ada-goto-declaration-start
2415 (funcall ada-goto-declaration-start)))
2416
2417 (defvar ada-goto-declaration-end nil
2418 ;; supplied by indentation engine
2419 "For `end-of-defun-function'. Function to move point to end of
2420 current declaration.")
2421
2422 (defun ada-goto-declaration-end ()
2423 "See `ada-goto-declaration-end' variable."
2424 (interactive)
2425 (when ada-goto-declaration-end
2426 (funcall ada-goto-declaration-end)))
2427
2428 (defvar ada-goto-declarative-region-start nil
2429 ;; Supplied by indentation engine
2430 "Function to move point to start of the declarative region of
2431 the subprogram, package, task, or declare block point
2432 is currently in. Called with no parameters.")
2433
2434 (defun ada-goto-declarative-region-start ()
2435 "Call `ada-goto-declarative-region-start'."
2436 (when ada-goto-declarative-region-start
2437 (funcall ada-goto-declarative-region-start)))
2438
2439 (defvar ada-next-statement-keyword nil
2440 ;; Supplied by indentation engine
2441 "Function called with no parameters; it should move forward to
2442 the next keyword in the statement following the one point is
2443 in (ie from 'if' to 'then'). If not in a keyword, move forward
2444 to the next keyword in the current statement. If at the last keyword,
2445 move forward to the first keyword in the next statement or next
2446 keyword in the containing statement.")
2447
2448 (defvar ada-goto-end nil
2449 ;; Supplied by indentation engine
2450 "Function to move point to end of the declaration or statement point is in or before.
2451 Called with no parameters.")
2452
2453 (defun ada-goto-end ()
2454 "Call `ada-goto-end'."
2455 (when ada-goto-end
2456 (funcall ada-goto-end)))
2457
2458 (defun ada-next-statement-keyword ()
2459 ;; Supplied by indentation engine
2460 "See `ada-next-statement-keyword' variable."
2461 (interactive)
2462 (when ada-next-statement-keyword
2463 (unless (region-active-p)
2464 (push-mark))
2465 (funcall ada-next-statement-keyword)))
2466
2467 (defvar ada-prev-statement-keyword nil
2468 ;; Supplied by indentation engine
2469 "Function called with no parameters; it should move to the previous
2470 keyword in the statement following the one point is in (ie from
2471 'then' to 'if'). If at the first keyword, move to the previous
2472 keyword in the previous statement or containing statement.")
2473
2474 (defun ada-prev-statement-keyword ()
2475 "See `ada-prev-statement-keyword' variable."
2476 (interactive)
2477 (when ada-prev-statement-keyword
2478 (unless (region-active-p)
2479 (push-mark))
2480 (funcall ada-prev-statement-keyword)))
2481
2482 ;;;; code creation
2483
2484 (defvar ada-make-subprogram-body nil
2485 ;; Supplied by indentation engine
2486 "Function to convert subprogram specification after point into a subprogram body stub.
2487 Called with no args, point at declaration start. Leave point in
2488 subprogram body, for user to add code.")
2489
2490 (defun ada-make-subprogram-body ()
2491 "If point is in or after a subprogram specification, convert it
2492 into a subprogram body stub, by calling `ada-make-subprogram-body'."
2493 (interactive)
2494 (ada-goto-declaration-start)
2495 (if ada-make-subprogram-body
2496 (funcall ada-make-subprogram-body)
2497 (error "`ada-make-subprogram-body' not set")))
2498
2499 (defvar ada-make-package-body nil
2500 ;; Supplied by xref tool
2501 "Function to create a package body from a package spec.
2502 Called with one argument; the absolute path to the body
2503 file. Current buffer is the package spec. Should create the
2504 package body file, containing skeleton code that will compile.")
2505
2506 (defun ada-make-package-body (body-file-name)
2507 ;; no error if not set; let ada-skel do its thing.
2508 (when ada-make-package-body
2509 (funcall ada-make-package-body body-file-name)))
2510
2511 (defun ada-ff-create-body ()
2512 ;; no error if not set; let ada-skel do its thing.
2513 (when ada-make-package-body
2514 ;; ff-find-other-file calls us with point in an empty buffer for the
2515 ;; body file; ada-make-package-body expects to be in the spec. So go
2516 ;; back.
2517 (let ((body-file-name (buffer-file-name)))
2518 (ff-find-the-other-file)
2519
2520 (ada-make-package-body body-file-name)
2521 ;; FIXME (later): if 'ada-make-package-body' fails, delete the body buffer
2522 ;; so it doesn't get written to disk, and we can try again.
2523
2524 ;; back to the body, read in from the disk.
2525 (ff-find-the-other-file)
2526 (revert-buffer t t))
2527 ))
2528
2529 ;;;; fill-comment
2530
2531 (defun ada-fill-comment-paragraph (&optional justify postfix)
2532 "Fill the current comment paragraph.
2533 If JUSTIFY is non-nil, each line is justified as well.
2534 If POSTFIX and JUSTIFY are non-nil, `ada-fill-comment-postfix' is appended
2535 to each line filled and justified.
2536 The paragraph is indented on the first line."
2537 (interactive "P")
2538 (if (and (not (ada-in-comment-p))
2539 (not (looking-at "[ \t]*--")))
2540 (error "Not inside comment"))
2541
2542 (let* ((inhibit-modification-hooks t) ;; don't run parser for font-lock; comment text is exposed
2543 indent from to
2544 (opos (point-marker))
2545 ;; we bind `fill-prefix' here rather than in ada-mode because
2546 ;; setting it in ada-mode causes indent-region to use it for
2547 ;; all indentation.
2548 (fill-prefix ada-fill-comment-prefix)
2549 (fill-column (current-fill-column)))
2550
2551 ;; Find end of comment paragraph
2552 (back-to-indentation)
2553 (while (and (not (eobp)) (looking-at ".*--[ \t]*[^ \t\n]"))
2554 (forward-line 1)
2555
2556 ;; If we were at the last line in the buffer, create a dummy empty
2557 ;; line at the end of the buffer.
2558 (if (eobp)
2559 (insert "\n")
2560 (back-to-indentation)))
2561 (beginning-of-line)
2562 (setq to (point-marker))
2563 (goto-char opos)
2564
2565 ;; Find beginning of paragraph
2566 (back-to-indentation)
2567 (while (and (not (bobp)) (looking-at ".*--[ \t]*[^ \t\n]"))
2568 (forward-line -1)
2569 (back-to-indentation))
2570
2571 (unless (bobp)
2572 (forward-line 1))
2573 (beginning-of-line)
2574 (setq from (point-marker))
2575
2576 ;; Calculate the indentation we will need for the paragraph
2577 (back-to-indentation)
2578 (setq indent (current-column))
2579 ;; unindent the first line of the paragraph
2580 (delete-region from (point))
2581
2582 ;; Remove the old postfixes
2583 (goto-char from)
2584 (while (re-search-forward (concat "\\(" ada-fill-comment-postfix "\\)" "\n") to t)
2585 (delete-region (match-beginning 1) (match-end 1)))
2586
2587 (goto-char (1- to))
2588 (setq to (point-marker))
2589
2590 ;; Indent and justify the paragraph
2591 (set-left-margin from to indent)
2592 (if postfix
2593 (setq fill-column (- fill-column (length ada-fill-comment-postfix))))
2594
2595 (fill-region-as-paragraph from to justify)
2596
2597 ;; Add the postfixes if required
2598 (if postfix
2599 (save-restriction
2600 (goto-char from)
2601 (narrow-to-region from to)
2602 (while (not (eobp))
2603 (end-of-line)
2604 (insert-char ? (- fill-column (current-column)))
2605 (insert ada-fill-comment-postfix)
2606 (forward-line))
2607 ))
2608
2609 (goto-char opos)
2610
2611 ;; we disabled modification hooks, so font-lock will not run to
2612 ;; re-fontify the comment prefix; do that here.
2613 (when (memq 'jit-lock-after-change after-change-functions)
2614 (jit-lock-after-change from to 0))
2615 ))
2616
2617 ;;;; support for font-lock.el
2618
2619 ;; casing keywords defined here to keep the two lists together
2620 (defconst ada-83-keywords
2621 '("abort" "abs" "accept" "access" "all" "and" "array" "at" "begin"
2622 "body" "case" "constant" "declare" "delay" "delta" "digits" "do"
2623 "else" "elsif" "end" "entry" "exception" "exit" "for" "function"
2624 "generic" "goto" "if" "in" "is" "limited" "loop" "mod" "new"
2625 "not" "null" "of" "or" "others" "out" "package" "pragma" "private"
2626 "procedure" "raise" "range" "record" "rem" "renames" "return"
2627 "reverse" "select" "separate" "subtype" "task" "terminate" "then"
2628 "type" "use" "when" "while" "with" "xor")
2629 "List of Ada 83 keywords.")
2630
2631 (defconst ada-95-keywords
2632 '("abstract" "aliased" "protected" "requeue" "tagged" "until")
2633 "List of keywords new in Ada 95.")
2634
2635 (defconst ada-2005-keywords
2636 '("interface" "overriding" "synchronized")
2637 "List of keywords new in Ada 2005.")
2638
2639 (defconst ada-2012-keywords
2640 '("some")
2641 "List of keywords new in Ada 2012.")
2642
2643 (defun ada-font-lock-keywords ()
2644 "Return Ada mode value for `font-lock-keywords', depending on `ada-language-version'."
2645 ;; Grammar actions set `font-lock-face' property for all
2646 ;; non-keyword tokens that need it.
2647 (list
2648 (list (concat "\\<" (regexp-opt ada-keywords t) "\\>") '(0 font-lock-keyword-face))
2649 ))
2650
2651 ;;;; ada-mode
2652
2653 ;; ada-mode does not derive from prog-mode, because we need to call
2654 ;; ada-mode-post-local-vars, and prog-mode does not provide a way to
2655 ;; do that.
2656 ;;
2657 ;; autoload required by automatic mode setting
2658 ;;;###autoload
2659 (defun ada-mode ()
2660 "The major mode for editing Ada code."
2661 ;; the other ada-*.el files add to ada-mode-hook for their setup
2662
2663 (interactive)
2664 (kill-all-local-variables)
2665 (setq major-mode 'ada-mode)
2666 (setq mode-name "Ada")
2667 (use-local-map ada-mode-map)
2668 (set-syntax-table ada-mode-syntax-table)
2669 (define-abbrev-table 'ada-mode-abbrev-table ())
2670 (setq local-abbrev-table ada-mode-abbrev-table)
2671
2672 (set (make-local-variable 'syntax-propertize-function) 'ada-syntax-propertize)
2673 (set (make-local-variable 'syntax-begin-function) nil)
2674 (set (make-local-variable 'parse-sexp-ignore-comments) t)
2675 (set (make-local-variable 'parse-sexp-lookup-properties) t)
2676 (set 'case-fold-search t); Ada is case insensitive; the syntax parsing requires this setting
2677 (set (make-local-variable 'comment-start) "--")
2678 (set (make-local-variable 'comment-end) "")
2679 (set (make-local-variable 'comment-start-skip) "---*[ \t]*")
2680 (set (make-local-variable 'comment-multi-line) nil)
2681
2682 ;; we _don't_ set `fill-prefix' here because that causes
2683 ;; indent-region to use it for all indentation. See
2684 ;; ada-fill-comment-paragraph.
2685
2686 ;; AdaCore standard style (enforced by -gnaty) requires two spaces
2687 ;; after '--' in comments; this makes it easier to distinguish
2688 ;; special comments that have something else after '--'
2689 (set (make-local-variable 'comment-padding) " ")
2690
2691 (set (make-local-variable 'require-final-newline) t)
2692
2693 ;; 'font-lock-defaults' is a confusing name; it's buffer local
2694 (setq font-lock-defaults
2695 '(ada-font-lock-keywords
2696 nil t
2697 ((?\_ . "w")))); treat underscore as a word component
2698
2699 (set (make-local-variable 'ff-other-file-alist)
2700 'ada-other-file-alist)
2701 (setq ff-post-load-hook 'ada-set-point-accordingly
2702 ff-file-created-hook 'ada-ff-create-body)
2703 (add-hook 'ff-pre-load-hook 'ada-goto-push-pos)
2704 (add-hook 'ff-pre-load-hook 'ada-which-function)
2705 (setq ff-search-directories 'compilation-search-path)
2706 (when (null (car compilation-search-path))
2707 ;; find-file doesn't handle nil in search path
2708 (setq compilation-search-path (list (file-name-directory (buffer-file-name)))))
2709 (ada-set-ff-special-constructs)
2710
2711 (set (make-local-variable 'add-log-current-defun-function)
2712 'ada-add-log-current-function)
2713
2714 (when (boundp 'which-func-functions)
2715 (add-hook 'which-func-functions 'ada-which-function nil t))
2716
2717 ;; Support for align
2718 (add-to-list 'align-dq-string-modes 'ada-mode)
2719 (add-to-list 'align-open-comment-modes 'ada-mode)
2720 (set (make-local-variable 'align-region-separate) ada-align-region-separate)
2721 (set (make-local-variable 'align-indent-before-aligning) t)
2722
2723 ;; Exclude comments alone on line from alignment.
2724 (add-to-list 'align-exclude-rules-list
2725 '(ada-solo-comment
2726 (regexp . "^\\(\\s-*\\)--")
2727 (modes . '(ada-mode))))
2728 (add-to-list 'align-exclude-rules-list
2729 '(ada-solo-use
2730 (regexp . "^\\(\\s-*\\)\\<use\\>")
2731 (modes . '(ada-mode))))
2732
2733 (setq align-mode-rules-list ada-align-rules)
2734
2735 (easy-menu-add ada-mode-menu ada-mode-map)
2736
2737 (setq ada-case-strict (ada-prj-get 'case_strict))
2738
2739 (run-mode-hooks 'ada-mode-hook)
2740
2741 ;; If global-font-lock is not enabled, ada-syntax-propertize is
2742 ;; not run when the text is first loaded into the buffer. Recover
2743 ;; from that.
2744 (syntax-ppss-flush-cache (point-min))
2745 (syntax-propertize (point-max))
2746
2747 (add-hook 'hack-local-variables-hook 'ada-mode-post-local-vars nil t)
2748 )
2749
2750 (defun ada-mode-post-local-vars ()
2751 ;; These are run after ada-mode-hook and file local variables
2752 ;; because users or other ada-* files might set the relevant
2753 ;; variable inside the hook or file local variables (file local
2754 ;; variables are processed after the mode is set, and thus after
2755 ;; ada-mode is run).
2756
2757 ;; This means to fully set ada-mode interactively, user must
2758 ;; do M-x ada-mode M-; (hack-local-variables)
2759
2760 ;; fill-region-as-paragraph in ada-fill-comment-paragraph does not
2761 ;; call syntax-propertize, so set comment syntax on
2762 ;; ada-fill-comment-prefix. In post-local because user may want to
2763 ;; set it per-file.
2764 (put-text-property 0 2 'syntax-table '(11 . nil) ada-fill-comment-prefix)
2765
2766 (cl-case ada-language-version
2767 (ada83
2768 (setq ada-keywords ada-83-keywords))
2769
2770 (ada95
2771 (setq ada-keywords
2772 (append ada-83-keywords
2773 ada-95-keywords)))
2774
2775 (ada2005
2776 (setq ada-keywords
2777 (append ada-83-keywords
2778 ada-95-keywords
2779 ada-2005-keywords)))
2780 (ada2012
2781 (setq ada-keywords
2782 (append ada-83-keywords
2783 ada-95-keywords
2784 ada-2005-keywords
2785 ada-2012-keywords))))
2786
2787 (when global-font-lock-mode
2788 ;; This calls ada-font-lock-keywords, which depends on
2789 ;; ada-keywords
2790 (font-lock-refresh-defaults))
2791
2792 (when ada-goto-declaration-start
2793 (set (make-local-variable 'beginning-of-defun-function) ada-goto-declaration-start))
2794
2795 (when ada-goto-declaration-end
2796 (set (make-local-variable 'end-of-defun-function) ada-goto-declaration-end))
2797 )
2798
2799 (put 'ada-mode 'custom-mode-group 'ada)
2800
2801 (provide 'ada-mode)
2802
2803 ;;;;; Global initializations
2804
2805 (require 'ada-build)
2806
2807 (unless (featurep 'ada-indent-engine)
2808 (require 'ada-wisi))
2809
2810 (unless (featurep 'ada-xref-tool)
2811 (cl-case ada-xref-tool
2812 ((nil gnat) (require 'ada-gnat-xref))
2813 (gpr_query (require 'gpr-query))
2814 ))
2815
2816 (unless (featurep 'ada-compiler)
2817 (require 'ada-gnat-compile))
2818
2819 (unless (featurep 'ada-skeletons)
2820 (require 'ada-skel))
2821
2822 (when (featurep 'imenu)
2823 (require 'ada-imenu))
2824
2825 ;;; end of file