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