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