1 ;; @(#) ada-xref.el --- use Gnat for lookup and completion in Ada mode
3 ;; Copyright (C) 1994, 1995--1998, 1999 Free Software Foundation, Inc.
5 ;; Author: Markus Heritsch <Markus.Heritsch@studbox.uni-stuttgart.de>
6 ;; Rolf Ebert <ebert@inf.enst.fr>
7 ;; Emmanuel Briot <briot@gnat.com>
8 ;; Maintainer: Emmanuel Briot <briot@gnat.com>
9 ;; Ada Core Technologies's version: $Revision: 1.75 $
10 ;; Keywords: languages ada xref
12 ;; This file is not part of GNU Emacs.
14 ;; This program is free software; you can redistribute it and/or modify
15 ;; it under the terms of the GNU General Public License as published by
16 ;; the Free Software Foundation; either version 2, or (at your option)
19 ;; This program is distributed in the hope that it will be useful,
20 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
21 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 ;; GNU General Public License for more details.
24 ;; You should have received a copy of the GNU General Public License
25 ;; along with GNU Emacs; see the file COPYING. If not, write to
26 ;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
29 ;;; This Package provides a set of functions to use the output of the
30 ;;; cross reference capabilities of the GNAT Ada compiler
31 ;;; for lookup and completion in Ada mode.
33 ;;; The functions provided are the following ones :
34 ;;; - `ada-complete-identifier': completes the current identifier as much as
35 ;;; possible, depending of the known identifier in the unit
36 ;;; - `ada-point-and-xref': moves the mouse pointer and shows the declaration
37 ;;; of the selected identifier (either in the same buffer or in another
39 ;;; - `ada-goto-declaration': shows the declaration of the selected
40 ;;; identifier (the one under the cursor), either in the same buffer or in
42 ;;; - `ada-goto-declaration-other-frame': same as previous, but opens a new
43 ;; frame to show the declaration
44 ;;; - `ada-compile-application': recompile your whole application, provided
45 ;;; that a project file exists in your directory
46 ;;; - `ada-run-application': run your application directly from Emacs
47 ;;; - `ada-reread-prj-file': force Emacs to read your project file again.
48 ;;; Otherwise, this file is only read the first time Emacs needs some
49 ;;; informations, which are then kept in memory
50 ;;; - `ada-change-prj': change the prj file associated with a buffer
51 ;;; - `ada-change-default-prj': change the default project file used for
54 ;;; If a file *.`adp' exists in the ada-file directory, then it is
55 ;;; read for configuration informations. It is read only the first
56 ;;; time a cross-reference is asked for, and is not read later.
58 ;;; You need Emacs >= 20.2 to run this package
60 ;; ----- Requirements -----------------------------------------------------
65 ;; ----- Dynamic byte compilation -----------------------------------------
66 (defvar byte-compile-dynamic nil
)
67 (make-local-variable 'byte-compile-dynamic
)
68 (setq byte-compile-dynamic t
)
70 ;; ------ Use variables
71 (defcustom ada-xref-other-buffer t
72 "*If nil, always display the cross-references in the same buffer.
73 Otherwise create either a new buffer or a new frame."
74 :type
'boolean
:group
'ada
)
76 (defcustom ada-xref-create-ali t
77 "*If non-nil, run gcc whenever the cross-references are not up-to-date.
78 If nil, the cross-reference mode will never run gcc."
79 :type
'boolean
:group
'ada
)
81 (defcustom ada-xref-confirm-compile nil
82 "*If non-nil, always ask for user confirmation before compiling or running
84 :type
'boolean
:group
'ada
)
86 (defcustom ada-krunch-args
"0"
87 "*Maximum number of characters for filenames created by gnatkr.
88 Set to 0, if you don't use crunched filenames. This should be a string."
89 :type
'string
:group
'ada
)
91 (defcustom ada-prj-default-comp-cmd
92 "${cross_prefix}gcc -c -g -gnatq ${comp_opt} -I${src_dir}"
93 "*Default command to be used to compile a single file.
94 Emacs will add the filename at the end of this command.
95 This is the same syntax as in the project file."
96 :type
'string
:group
'ada
)
98 (defcustom ada-prj-default-make-cmd
99 (concat "${cross_prefix}gnatmake ${main} -aI${src_dir} -aO${obj_dir} "
100 "-g -gnatq -cargs ${comp_opt} "
101 "-bargs ${bind_opt} -largs ${link_opt}")
102 "*Default command to be used to compile the application.
103 This is the same syntax as in the project file."
104 :type
'string
:group
'ada
)
106 (defcustom ada-prj-default-project-file
""
107 "*Name of the project file to use for every Ada file.
108 Emacs will not try to use the standard algorithm to find the project file if
109 this string is not empty."
110 :type
'(file :must-match t
) :group
'ada
)
112 (defcustom ada-gnatstub-opts
"-q -I${src_dir}"
113 "*List of the options to pass to gnatsub to generate the body of a package.
114 This has the same syntax as in the project file (with variable substitution)."
115 :type
'string
:group
'ada
)
117 (defcustom ada-always-ask-project nil
118 "*If nil, use default values when no project file was found.
119 Otherwise, ask the user for the name of the project file to use.")
121 ;; ------- Nothing to be modified by the user below this
122 (defvar ada-last-prj-file
""
123 "Name of the last project file entered by the user.")
125 (defvar ada-check-switch
" -gnats "
126 "Switch added to the command line to check the current file.")
128 (defvar ada-project-file-extension
".adp"
129 "The extension used for project files.")
131 (defconst is-windows
(memq system-type
(quote (windows-nt)))
132 "True if we are running on windows NT or windows 95.")
134 (defvar ada-xref-pos-ring
'()
135 "List of positions selected by the cross-references functions.
136 Used to go back to these positions.")
138 (defconst ada-xref-pos-ring-max
16
139 "Number of positions kept in the list ada-xref-pos-ring.")
141 (defvar ada-operator-re
142 "\\+\\|-\\|/\\|\\*\\|=\\|mod\\|and\\|not\\|or\\|xor\\|<=\\|<\\|>=\\|>"
143 "Regexp to match for operators.")
145 (defvar ada-xref-default-prj-file nil
146 "Name of the default prj file, per directory.
147 Every directory is potentially associated with a default project file.
148 If it is nil, then the first prj file loaded will be the default for this
151 ;; These variables will be overwritted by buffer-local variables
152 (defvar ada-prj-prj-file nil
153 "Name of the project file for the current ada buffer.")
154 (defvar ada-prj-src-dir nil
155 "List of directories to look into for ada sources.")
156 (defvar ada-prj-obj-dir nil
157 "List of directories to look into for object and .ali files.")
158 (defvar ada-prj-comp-opt nil
159 "Switches to use on the command line for the default compile command.")
160 (defvar ada-prj-bind-opt nil
161 "Switches to use on the command line for the default bind command.")
162 (defvar ada-prj-link-opt nil
163 "Switches to use on the command line for the default link command.")
164 (defvar ada-prj-comp-cmd nil
165 "Command to use to compile the current file only.")
166 (defvar ada-prj-make-cmd nil
167 "Command to use to compile the whole current application.")
168 (defvar ada-prj-run-cmd nil
169 "Command to use to run the current application.")
170 (defvar ada-prj-debug-cmd nil
171 "Command to use to run the debugger.")
172 (defvar ada-prj-main nil
173 "Name of the main programm of the current application.")
174 (defvar ada-prj-remote-machine nil
175 "Name of the machine to log on before a compilation.")
176 (defvar ada-prj-cross-prefix nil
177 "Prefix to be added to the gnatmake, gcc, ... commands when
178 using a cross-compilation environment.
179 A '-' is automatically added at the end if not already present.
180 For instance, the compiler is called `ada-prj-cross-prefix'gnatmake.")
182 ;; ----- Keybindings ------------------------------------------------------
184 (defun ada-add-keymap ()
185 "Add new key bindings when using `ada-xrel.el'."
189 (define-key ada-mode-map
'(shift button3
) 'ada-point-and-xref
)
190 (define-key ada-mode-map
'(control tab
) 'ada-complete-identifier
))
191 (define-key ada-mode-map
[C-tab
] 'ada-complete-identifier
)
192 (define-key ada-mode-map
[S-mouse-3
] 'ada-point-and-xref
))
194 (define-key ada-mode-map
"\C-co" 'ff-find-other-file
)
195 (define-key ada-mode-map
"\C-c5\C-d" 'ada-goto-declaration-other-frame
)
196 (define-key ada-mode-map
"\C-c\C-d" 'ada-goto-declaration
)
197 (define-key ada-mode-map
"\C-c\C-s" 'ada-xref-goto-previous-reference
)
198 (define-key ada-mode-map
"\C-c\C-x" 'ada-reread-prj-file
)
199 (define-key ada-mode-map
[f10] 'next-error)
200 (define-key ada-mode-map "\C-c\C-c" 'ada-compile-application)
201 (define-key ada-mode-map "\C-cb" 'ada-buffer-list)
202 (define-key ada-mode-map "\C-cc" 'ada-change-prj)
203 (define-key ada-mode-map "\C-cd" 'ada-change-default-prj)
204 (define-key ada-mode-map "\C-cg" 'ada-gdb-application)
205 (define-key ada-mode-map "\C-cr" 'ada-run-application)
206 (define-key ada-mode-map "\C-c\C-o" 'ada-goto-parent)
207 (define-key ada-mode-map "\C-c\C-r" 'ada-find-references)
208 (define-key ada-mode-map "\C-c\C-v" 'ada-check-current)
211 ;; ----- Menus --------------------------------------------------------------
212 (defun ada-add-ada-menu ()
213 "Add some items to the standard Ada mode menu."
218 (add-menu-button '("Ada") ["Check file" ada-check-current t] "Goto")
219 (add-menu-button '("Ada") ["Compile file" ada-compile-current t]
221 (add-menu-button '("Ada") ["Build" ada-compile-application t] "Goto")
222 (add-menu-button '("Ada") ["Run" ada-run-application t] "Goto")
223 (add-menu-button '("Ada") ["Debug" ada-gdb-application t] "Goto")
224 (add-menu-button '("Ada") ["--" nil t] "Goto")
225 (add-submenu '("Ada") '("Project"
226 ["Associate" ada-change-prj t]
227 ["Set Default" ada-set-default-project-file t]
228 ["List" ada-buffer-list t])
230 (add-menu-button '("Ada" "Goto") ["Goto Parent Unit" ada-goto-parent t]
231 "Next compilation error")
232 (add-menu-button '("Ada" "Goto") ["Goto References to any entity"
233 ada-find-any-references t]
234 "Next compilation error")
235 (add-menu-button '("Ada" "Goto") ["List References"
236 ada-find-references t]
237 "Next compilation error")
238 (add-menu-button '("Ada" "Goto") ["Goto Declaration Other Frame"
239 ada-goto-declaration-other-frame t]
240 "Next compilation error")
241 (add-menu-button '("Ada" "Goto") ["Goto Declaration/Body"
242 ada-goto-declaration t]
243 "Next compilation error")
244 (add-menu-button '("Ada" "Goto") ["Goto Previous Reference"
245 ada-xref-goto-previous-reference t]
246 "Next compilation error")
247 (add-menu-button '("Ada" "Goto") ["--" nil t]
248 "Next compilation error")
249 (add-menu-button '("Ada" "Edit") ["Complete Identifier"
250 ada-complete-identifier t]
252 (add-menu-button '("Ada" "Edit") ["--------" nil t]
254 (add-menu-button '("Ada" "Help") ["Gnat User Guide" (info "gnat_ug")])
255 (add-menu-button '("Ada" "Help") ["Gnat Reference Manual"
257 (add-menu-button '("Ada" "Help") ["Gcc Documentation" (info "gcc")])
258 (add-menu-button '("Ada" "Help") ["Gdb Documentation" (info "gdb")])
262 (define-key-after (lookup-key ada-mode-map [menu-bar Ada]) [Check]
263 '("Check file" . ada-check-current) 'Customize)
264 (define-key-after (lookup-key ada-mode-map [menu-bar Ada]) [Compile]
265 '("Compile file" . ada-compile-current) 'Check)
266 (define-key-after (lookup-key ada-mode-map [menu-bar Ada]) [Build]
267 '("Build" . ada-compile-application) 'Compile)
268 (define-key-after (lookup-key ada-mode-map [menu-bar Ada]) [Run]
269 '("Run" . ada-run-application) 'Build)
270 (define-key-after (lookup-key ada-mode-map [menu-bar Ada]) [Debug]
271 '("Debug" . ada-gdb-application) 'Run)
272 (define-key-after (lookup-key ada-mode-map [menu-bar Ada]) [rem]
273 '("--" . nil) 'Debug)
274 (define-key-after (lookup-key ada-mode-map [menu-bar Ada]) [Project]
275 (cons "Project" (easy-menu-create-menu
277 '(["Associate" ada-change-prj t]
278 ["Set Default" ada-set-default-project-file t]
279 ["List" ada-buffer-list t])))
282 (let ((help-submenu (lookup-key ada-mode-map [menu-bar Ada Help]))
283 (goto-submenu (lookup-key ada-mode-map [menu-bar Ada Goto]))
284 (edit-submenu (lookup-key ada-mode-map [menu-bar Ada Edit])))
286 (define-key help-submenu [Gnat_ug]
287 '("Gnat User Guide" . (lambda() (interactive) (info "gnat_ug"))))
288 (define-key help-submenu [Gnat_rm]
289 '("Gnat Reference Manual" . (lambda() (interactive) (info "gnat_rm"))))
290 (define-key help-submenu [Gcc]
291 '("Gcc Documentation" . (lambda() (interactive) (info "gcc"))))
292 (define-key help-submenu [gdb]
293 '("Ada Aware Gdb Documentation" .
294 (lambda() (interactive) (info "gdb"))))
295 (define-key goto-submenu [rem] '("----" . nil))
296 (define-key goto-submenu [Parent]
297 '("Goto Parent Unit" . ada-goto-parent))
298 (define-key goto-submenu [References-any]
299 '("Goto References to any entity" . ada-find-any-references))
300 (define-key goto-submenu [References]
301 '("List References" . ada-find-references))
302 (define-key goto-submenu [Prev]
303 '("Goto Previous Reference" . ada-xref-goto-previous-reference))
304 (define-key goto-submenu [Decl-other]
305 '("Goto Declaration Other Frame" . ada-goto-declaration-other-frame))
306 (define-key goto-submenu [Decl]
307 '("Goto Declaration/Body" . ada-goto-declaration))
309 (define-key edit-submenu [rem] '("----" . nil))
310 (define-key edit-submenu [Complete] '("Complete Identifier"
311 . ada-complete-identifier))
315 ;; ----- Utilities -------------------------------------------------
317 (defun ada-require-project-file ()
318 "If no project file is assigned to this buffer, load one."
319 (if (not ( my-local-variable-if-set-p 'ada-prj-src-dir (current-buffer)))
320 (ada-parse-prj-file (ada-prj-find-prj-file))))
322 (defun my-local-variable-if-set-p (variable &optional buffer)
323 "Returns t if VARIABLE is local in BUFFER and is non-nil."
324 (and (local-variable-p variable buffer)
327 (symbol-value variable))))
329 (defun ada-xref-push-pos (filename position)
330 "Push (FILENAME, POSITION) on the position ring for cross-references."
331 (setq ada-xref-pos-ring (cons (list position filename) ada-xref-pos-ring))
332 (if (> (length ada-xref-pos-ring) ada-xref-pos-ring-max)
333 (setcdr (nthcdr (1- ada-xref-pos-ring-max) ada-xref-pos-ring) nil)))
335 (defun ada-xref-goto-previous-reference ()
336 "Go to the previous cross-reference we were on."
338 (if ada-xref-pos-ring
339 (let ((pos (car ada-xref-pos-ring)))
340 (setq ada-xref-pos-ring (cdr ada-xref-pos-ring))
341 (find-file (car (cdr pos)))
342 (goto-char (car pos)))))
344 (defun ada-convert-file-name (name)
345 "Converts from NAME to a name that can be used by the compilation commands.
346 This is overriden on VMS to convert from VMS filenames to Unix filenames."
349 (defun ada-set-default-project-file (name)
350 "Set the file whose name is NAME as the default project file."
351 (interactive "fProject file:")
352 (set 'ada-prj-default-project-file name)
353 (ada-reread-prj-file t)
356 (defun ada-replace-substring (cmd-string search-for replace-with)
357 "Replace all instances of SEARCH-FOR with REPLACE-WITH in CMD-STRING."
358 (while (string-match search-for cmd-string)
359 (setq cmd-string (replace-match replace-with t t cmd-string)))
362 (defun ada-treat-cmd-string (cmd-string)
363 "Replace meta-sequences like ${...} in CMD-STRING with the appropriate value.
364 The current buffer must be the one where all local variable are defined (that
366 (if ( my-local-variable-if-set-p 'ada-prj-src-dir (current-buffer))
367 (if (string-match "\\(-[^-\$I]*I\\)\${src_dir}" cmd-string)
369 (let ((str-def (substring cmd-string (match-beginning 1)
372 (ada-replace-substring cmd-string
373 "\\(-[^-\$I]*I\\)\${src_dir}"
375 (lambda (x) (concat str-def x))
376 ada-prj-src-dir " ")))))))
377 (if ( my-local-variable-if-set-p 'ada-prj-obj-dir (current-buffer))
378 (if (string-match "\\(-[^-\$O]*O\\)\${obj_dir}" cmd-string)
380 (let ((str-def (substring cmd-string (match-beginning 1)
383 (ada-replace-substring cmd-string
384 "\\(-[^-\$O]*O\\)\${obj_dir}"
386 (lambda (x) (concat str-def x))
389 (if ( my-local-variable-if-set-p 'ada-prj-remote-machine (current-buffer))
391 (ada-replace-substring cmd-string "\${remote_machine}"
392 ada-prj-remote-machine)))
393 (if ( my-local-variable-if-set-p 'ada-prj-comp-opt (current-buffer))
395 (ada-replace-substring cmd-string "\${comp_opt}"
397 (if ( my-local-variable-if-set-p 'ada-prj-bind-opt (current-buffer))
399 (ada-replace-substring cmd-string "\${bind_opt}"
401 (if ( my-local-variable-if-set-p 'ada-prj-link-opt (current-buffer))
403 (ada-replace-substring cmd-string "\${link_opt}"
405 (if ( my-local-variable-if-set-p 'ada-prj-main (current-buffer))
407 (ada-replace-substring cmd-string "\${main}"
409 (if ( my-local-variable-if-set-p 'ada-prj-cross-prefix (current-buffer))
411 (ada-replace-substring cmd-string "\${cross_prefix}"
412 ada-prj-cross-prefix)))
416 (defun ada-prj-find-prj-file (&optional no-user-question)
417 "Find the prj file associated with the current buffer.
418 The rules are the following ones :
419 - If the buffer is already associated with a prj file, use this one
420 - else if there's a default prj file for the same directory use it
421 - else if a prj file with the same filename exists, use it
422 - else if there's only one prj file in the directory, use it
423 - else if there are more than one prj file, ask the user
424 - else if there is no prj file and NO-USER-QUESTION is nil, ask the user
425 for the project file to use."
426 (let* ((current-file (buffer-file-name))
427 (first-choice (concat
428 (file-name-sans-extension current-file)
429 ada-project-file-extension))
430 (dir (file-name-directory current-file))
432 ;; on Emacs 20.2, directory-files does not work if
433 ;; parse-sexp-lookup-properties is set
434 (parse-sexp-lookup-properties nil)
435 (prj-files (directory-files
437 (concat ".*" (regexp-quote ada-project-file-extension) "$")))
439 (default (assoc dir ada-xref-default-prj-file))
444 ((my-local-variable-if-set-p 'ada-prj-prj-file (current-buffer))
447 (default ;; directory default project file
450 ;; global default project file
451 ((and ada-prj-default-project-file
452 (not (string= ada-prj-default-project-file "")))
453 ada-prj-default-project-file)
455 ((file-exists-p first-choice)
458 ((= (length prj-files) 1)
461 ((> (length prj-files) 1)
462 ;; more than one possible prj file => ask the user
463 (with-output-to-temp-buffer "*choice list*"
464 (princ "There are more than one possible project file. Which one should\n")
465 (princ "I use ?\n\n")
466 (princ " no. file name \n")
467 (princ " --- ------------------------\n")
469 (while (<= counter (length prj-files))
470 (princ (format " %2d) %s\n"
472 (nth (1- counter) prj-files)))
473 (setq counter (1+ counter))
476 ) ; end of with-output-to ...
480 (not (integerp choice))
482 (> choice (length prj-files)))
483 (setq choice (string-to-int
484 (read-from-minibuffer "Enter No. of your choice: "
486 (nth (1- choice) prj-files))
488 ((= (length prj-files) 0)
489 ;; no project file found. Ask the user about it (the default value
490 ;; is the last one the user entered.
491 (if (or no-user-question (not ada-always-ask-project))
493 (setq ada-last-prj-file
494 (read-file-name "project file:" nil ada-last-prj-file))
495 (if (string= ada-last-prj-file "") nil ada-last-prj-file))
500 (defun ada-parse-prj-file (prj-file)
501 "Reads and parses the project file PRJ-FILE.
502 Does nothing if PRJ-FILE was not found.
503 The current buffer should be the ada-file buffer"
505 (let ((tmp-src-dir nil)
515 (tmp-remote-machine nil)
516 (tmp-cross-prefix nil)
517 (tmp-cd-cmd (if prj-file
518 (concat "cd " (file-name-directory prj-file) " && ")
519 (concat "cd " (file-name-directory (buffer-file-name (current-buffer))) " && ")))
520 (ada-buffer (current-buffer))
522 ;; tries to find a project file in the current directory
527 ;; first look for the src_dir lines
529 (goto-char (point-min))
531 (re-search-forward "^src_dir=\\(.*\\)" nil t)
533 (setq tmp-src-dir (cons
534 (file-name-as-directory
538 ;; then for the obj_dir lines
539 (goto-char (point-min))
540 (while (re-search-forward "^obj_dir=\\(.*\\)" nil t)
541 (setq tmp-obj-dir (cons
542 (file-name-as-directory
547 ;; then for the options lines
548 (goto-char (point-min))
549 (if (re-search-forward "^comp_opt=\\(.*\\)" nil t)
550 (setq tmp-comp-opt (match-string 1)))
551 (goto-char (point-min))
552 (if (re-search-forward "^bind_opt=\\(.*\\)" nil t)
553 (setq tmp-bind-opt (match-string 1)))
554 (goto-char (point-min))
555 (if (re-search-forward "^link_opt=\\(.*\\)" nil t)
556 (setq tmp-link-opt (match-string 1)))
557 (goto-char (point-min))
558 (if (re-search-forward "^main=\\(.*\\)" nil t)
559 (setq tmp-main (match-string 1)))
560 (goto-char (point-min))
561 (if (re-search-forward "^comp_cmd=\\(.*\\)" nil t)
562 (setq tmp-comp-cmd (match-string 1)))
563 (goto-char (point-min))
564 (if (re-search-forward "^remote_machine=\\(.*\\)" nil t)
565 (setq tmp-remote-machine (match-string 1)))
566 (goto-char (point-min))
567 (if (re-search-forward "^cross_prefix=\\(.*\\)" nil t)
568 (setq tmp-cross-prefix (match-string 1)))
569 (goto-char (point-min))
570 (if (re-search-forward "^make_cmd=\\(.*\\)" nil t)
571 (setq tmp-make-cmd (match-string 1)))
572 (goto-char (point-min))
573 (if (re-search-forward "^run_cmd=\\(.*\\)" nil t)
574 (setq tmp-run-cmd (match-string 1)))
575 (goto-char (point-min))
576 (if (re-search-forward "^debug_cmd=\\(.*\\)" nil t)
577 (setq tmp-debug-cmd (match-string 1)))
579 ;; kills the project file buffer, and go back to the ada buffer
581 (set-buffer ada-buffer)
584 ;; creates local variables (with default values if needed)
585 (set (make-local-variable 'ada-prj-prj-file) prj-file)
587 (set (make-local-variable 'ada-prj-src-dir)
588 (if tmp-src-dir (reverse tmp-src-dir) '("./")))
590 (set (make-local-variable 'ada-prj-obj-dir)
591 (if tmp-obj-dir (reverse tmp-obj-dir) '("./")))
593 (set (make-local-variable 'ada-prj-comp-opt)
594 (if tmp-comp-opt tmp-comp-opt ""))
596 (set (make-local-variable 'ada-prj-bind-opt)
597 (if tmp-bind-opt tmp-bind-opt ""))
599 (set (make-local-variable 'ada-prj-link-opt)
600 (if tmp-link-opt tmp-link-opt ""))
602 (set (make-local-variable 'ada-prj-cross-prefix)
604 (if (or (string= tmp-cross-prefix "")
605 (= (aref tmp-cross-prefix (1- (length tmp-cross-prefix))) ?-))
607 (concat tmp-cross-prefix "-"))
610 (set (make-local-variable 'ada-prj-main)
611 (if tmp-main tmp-main
612 (substring (buffer-file-name) 0 -4)))
614 (set (make-local-variable 'ada-prj-remote-machine)
615 (ada-treat-cmd-string
616 (if tmp-remote-machine tmp-remote-machine "")))
618 (set (make-local-variable 'ada-prj-comp-cmd)
619 (ada-treat-cmd-string
620 (if tmp-comp-cmd tmp-comp-cmd
621 (concat tmp-cd-cmd ada-prj-default-comp-cmd))))
623 (set (make-local-variable 'ada-prj-make-cmd)
624 (ada-treat-cmd-string
625 (if tmp-make-cmd tmp-make-cmd
626 (concat tmp-cd-cmd ada-prj-default-make-cmd))))
628 (set (make-local-variable 'ada-prj-run-cmd)
629 (ada-treat-cmd-string
630 (if tmp-run-cmd tmp-run-cmd
631 (if is-windows "${main}.exe" "${main}"))))
633 (set (make-local-variable 'ada-prj-debug-cmd)
634 (ada-treat-cmd-string
635 (if tmp-debug-cmd tmp-debug-cmd
637 "${cross_prefix}gdb ${main}.exe"
638 "${cross_prefix}gdb ${main}"))))
640 ;; Add each directory in src_dir to the default prj list
643 (if (not (assoc (expand-file-name x)
644 ada-xref-default-prj-file))
645 (setq ada-xref-default-prj-file
646 (cons (cons (expand-file-name x)
648 ada-xref-default-prj-file))))
651 ;; Add the directories to the search path for ff-find-other-file
652 ;; Do not add the '/' or '\' at the end
653 (set (make-local-variable 'ff-search-directories)
654 (append (mapcar 'directory-file-name ada-prj-src-dir)
655 ada-search-directories))
657 ;; Sets up the compilation-search-path so that Emacs is able to
658 ;; go to the source of the errors in a compilation buffer
659 (setq compilation-search-path ada-prj-src-dir)
664 (defun ada-find-references (&optional pos)
665 "Find all references to the entity under POS.
666 Calls gnatfind to find the references."
670 (ada-require-project-file)
672 (let* ((identlist (ada-read-identifier pos))
673 (alifile (ada-get-ali-file-name (ada-file-of identlist))))
675 (set-buffer (get-file-buffer (ada-file-of identlist)))
677 ;; if the file is more recent than the executable
678 (if (or (buffer-modified-p (current-buffer))
679 (file-newer-than-file-p (ada-file-of identlist) alifile))
680 (ada-find-any-references (ada-name-of identlist)
681 (ada-file-of identlist)
683 (ada-find-any-references (ada-name-of identlist)
684 (ada-file-of identlist)
685 (ada-line-of identlist)
686 (ada-column-of identlist))))
689 (defun ada-find-any-references (entity &optional file line column)
690 "Search for references to any entity whose name is ENTITY.
691 ENTITY was first found the location given by FILE, LINE and COLUMN."
692 (interactive "sEntity name: ")
693 (ada-require-project-file)
695 (let* ((command (concat "gnatfind -rf " entity
696 (if file (concat ":" (file-name-nondirectory file)))
697 (if line (concat ":" line))
698 (if column (concat ":" column)))))
700 ;; If a project file is defined, use it
701 (if (my-local-variable-if-set-p 'ada-prj-prj-file (current-buffer))
702 (setq command (concat command " -p" ada-prj-prj-file)))
704 (compile-internal command "No more references" "gnatfind")
706 ;; Hide the "Compilation" menu
708 (set-buffer "*gnatfind*")
709 (local-unset-key [menu-bar compilation-menu]))
713 (defun ada-buffer-list ()
714 "Display a buffer with all the Ada buffers and their associated project."
717 (set-buffer (get-buffer-create "*Buffer List*"))
718 (setq buffer-read-only nil)
720 (setq standard-output (current-buffer))
721 (princ "The following line is a list showing the associations between
722 directories and project file. It has the format : ((directory_1 . project_file1)
723 (directory2 . project_file2)...)\n\n")
724 (princ ada-xref-default-prj-file)
726 Buffer Mode Project file
727 ------ ---- ------------
729 (let ((bl (buffer-list)))
731 (let* ((buffer (car bl))
732 (buffer-name (buffer-name buffer))
733 this-buffer-mode-name
734 this-buffer-project-file)
737 (setq this-buffer-mode-name
738 (if (eq buffer standard-output)
739 "Buffer Menu" mode-name))
740 (if (string= this-buffer-mode-name
742 (setq this-buffer-project-file
743 (if ( my-local-variable-if-set-p 'ada-prj-prj-file
745 (expand-file-name ada-prj-prj-file)
747 (if (string= this-buffer-mode-name
750 (princ (format "%-19s " buffer-name))
751 (princ (format "%-6s " this-buffer-mode-name))
752 (princ this-buffer-project-file)
759 ) ;; end save-excursion
760 (display-buffer "*Buffer List*")
764 (defun ada-change-prj (filename)
765 "Set FILENAME to be the project file for current buffer."
766 (interactive "fproject file:")
768 ;; make sure we are using an Ada file
769 (if (not (string= mode-name "Ada"))
770 (error "You must be in ada-mode to use this function"))
772 ;; create the local variable if necessay
773 (if (not ( my-local-variable-if-set-p 'ada-prj-prj-file (current-buffer)))
774 (make-local-variable 'ada-prj-prj-file))
776 ;; ask the user for the new file name
777 (setq ada-prj-prj-file filename)
779 ;; force Emacs to reread the prj file next-time
780 (ada-reread-prj-file)
783 (defun ada-change-default-prj (filename)
784 "Set FILENAME to be the default project file for the current directory."
785 (interactive "ffile name:")
786 (let ((dir (file-name-directory (buffer-file-name)))
787 (prj (expand-file-name filename)))
789 ;; Associate the directory with a project file
790 (if (assoc dir ada-xref-default-prj-file)
791 (setcdr (assoc dir ada-xref-default-prj-file) prj)
792 (add-to-list 'ada-xref-default-prj-file (list dir prj)))
794 ;; Reparse the project file
795 (ada-parse-prj-file ada-prj-default-project-file)))
798 ;; ----- Identlist manipulation -------------------------------------------
799 ;; An identlist is a vector that is used internally to reference an identifier
800 ;; To facilitate its use, we provide the following macros
802 (defmacro ada-make-identlist () (make-vector 8 nil))
803 (defmacro ada-name-of (identlist) (list 'aref identlist 0))
804 (defmacro ada-line-of (identlist) (list 'aref identlist 1))
805 (defmacro ada-column-of (identlist) (list 'aref identlist 2))
806 (defmacro ada-file-of (identlist) (list 'aref identlist 3))
807 (defmacro ada-ali-index-of (identlist) (list 'aref identlist 4))
808 (defmacro ada-declare-file-of (identlist) (list 'aref identlist 5))
809 (defmacro ada-references-of (identlist) (list 'aref identlist 6))
810 (defmacro ada-on-declaration (identlist) (list 'aref identlist 7))
812 (defmacro ada-set-name (identlist name) (list 'aset identlist 0 name))
813 (defmacro ada-set-line (identlist line) (list 'aset identlist 1 line))
814 (defmacro ada-set-column (identlist col) (list 'aset identlist 2 col))
815 (defmacro ada-set-file (identlist file) (list 'aset identlist 3 file))
816 (defmacro ada-set-ali-index (identlist index) (list 'aset identlist 4 index))
817 (defmacro ada-set-declare-file (identlist file) (list 'aset identlist 5 file))
818 (defmacro ada-set-references (identlist ref) (list 'aset identlist 6 ref))
819 (defmacro ada-set-on-declaration (ident value) (list 'aset ident 7 value))
821 (defsubst ada-get-ali-buffer (file)
822 "Reads the ali file into a new buffer, and returns this buffer's name"
823 (find-file-noselect (ada-get-ali-file-name file)))
827 ;; ----- Identifier Completion --------------------------------------------
828 (defun ada-complete-identifier (pos)
829 "Tries to complete the identifier around POS.
830 The feature is only available if the files where not compiled using the -gnatx
833 (ada-require-project-file)
835 ;; Initialize function-local variablesand jump to the .ali buffer
836 ;; Note that for regexp search is case insensitive too
837 (let* ((curbuf (current-buffer))
838 (identlist (ada-read-identifier pos))
839 (sofar (concat "^[0-9]+[a-zA-Z][0-9]+[ *]\\("
840 (regexp-quote (ada-name-of identlist))
846 ;; we are already in the .ali buffer
847 (goto-char (point-max))
849 ;; build an alist of possible completions
850 (while (re-search-backward sofar nil t)
851 (setq symalist (cons (cons (match-string 1) nil) symalist)))
853 (setq completed (try-completion "" symalist))
858 ;; deletes the incomplete identifier in the buffer
860 (looking-at "[a-zA-Z0-9_]+")
862 ;; inserts the completed symbol
866 ;; ----- Cross-referencing ----------------------------------------
868 (defun ada-point-and-xref ()
869 "Calls `mouse-set-point' and then `ada-goto-declaration'."
871 (mouse-set-point last-input-event)
872 (ada-goto-declaration (point)))
874 (defun ada-goto-declaration (pos)
875 "Display the declaration of the identifier around POS.
876 The declaration is shown in another buffer if `ada-xref-other-buffer' is
879 (ada-require-project-file)
881 (ada-xref-push-pos (buffer-file-name) pos)
882 (ada-find-in-ali (ada-read-identifier pos)))
884 (defun ada-goto-declaration-other-frame (pos)
885 "Display the declaration of the identifier around POS.
886 The declation is shown in another frame if `ada-xref-other-buffer' is non-nil."
888 (ada-require-project-file)
890 (ada-xref-push-pos (buffer-file-name) pos)
891 (ada-find-in-ali (ada-read-identifier pos) t))
893 (defun ada-compile (command)
894 "Start COMMAND on the machine specified in the project file."
895 (if (and (my-local-variable-if-set-p 'ada-prj-remote-machine (current-buffer))
896 (not (string= ada-prj-remote-machine "")))
898 (concat "rsh " ada-prj-remote-machine " '"
902 (defun ada-compile-application ()
903 "Compiles the application, using the command found in the project file."
905 (ada-require-project-file)
907 ;; prompt for command to execute
909 (if ada-xref-confirm-compile
910 (read-from-minibuffer "enter command to compile: "
915 (defun ada-compile-current ()
916 "Recompile the current file."
918 (ada-require-project-file)
920 ;; prompt for command to execute
922 (if ada-xref-confirm-compile
923 (read-from-minibuffer "enter command to compile: "
925 ada-prj-comp-cmd " " (ada-convert-file-name (buffer-file-name))))
926 (concat ada-prj-comp-cmd " " (ada-convert-file-name (buffer-file-name)))))
929 (defun ada-check-current ()
930 "Recompile the current file."
932 (ada-require-project-file)
934 ;; prompt for command to execute
935 (let ((command (concat ada-prj-comp-cmd ada-check-switch
936 (ada-convert-file-name (buffer-file-name)))))
938 (if ada-xref-confirm-compile
939 (read-from-minibuffer "enter command to compile: " command)
943 (defun ada-run-application ()
944 "Run the application."
946 (ada-require-project-file)
948 (if (and (my-local-variable-if-set-p 'ada-prj-cross-prefix (current-buffer))
949 (not (string= ada-prj-cross-prefix "")))
950 (error "This feature is not supported yet for cross-compilation environments"))
952 (let ((command ada-prj-run-cmd)
953 (buffer (current-buffer)))
954 ;; Search the command name if necessary
955 (if (not (my-local-variable-if-set-p 'ada-prj-run-cmd (current-buffer)))
956 (setq command (file-name-sans-extension (buffer-name)))
959 ;; Ask for the arguments to the command
961 (read-from-minibuffer "Enter command to execute: "
966 (set-buffer (get-buffer-create "*run*"))
967 (goto-char (point-max))
968 (insert "\nRunning " command "\n\n")
970 (comint-arguments command 0 0)
972 (comint-arguments command 1 nil))
974 (display-buffer "*run*")
976 ;; change to buffer *run* for interactive programs
978 (switch-to-buffer "*run*")
983 (defun ada-gdb-application ()
984 "Start the debugger on the application."
988 (let ((buffer (current-buffer))
990 (ada-require-project-file)
992 (if (and (my-local-variable-if-set-p 'ada-prj-cross-prefix buffer)
993 (not (string= ada-prj-cross-prefix "")))
994 (error "This feature is not supported yet for cross-compilation environments"))
996 ;; If the command to use was given in the project file
997 (if (my-local-variable-if-set-p 'ada-prj-debug-cmd buffer)
998 (gdb ada-prj-debug-cmd)
999 ;; Else the user will have to enter the command himself
1003 (set 'gdb-buffer (current-buffer))
1005 ;; Switch back to the source buffer
1006 ;; and Activate the debug part in the contextual menu
1007 (switch-to-buffer buffer)
1009 (if (functionp 'gud-make-debug-menu)
1010 (gud-make-debug-menu))
1012 ;; Warning: on Emacs >= 20.3.8, same-window-regexps includes gud-*,
1013 ;; so the following call to display buffer will select the
1014 ;; buffer instead of displaying it in another window
1015 ;; This is why the second argument to display-buffer is 't'
1016 (display-buffer gdb-buffer t)
1020 (defun ada-reread-prj-file (&optional for-all-buffer)
1021 "Forces Emacs to read the project file again.
1022 Otherwise, this file is only read once, and never read again
1023 If FOR-ALL-BUFFER is non-nil, or the function was called with \C-u prefix,
1024 then do this for every opened buffer."
1028 ;; do this for every buffer
1032 ;; if we have the ada-mode and there is a real file
1033 ;; associated with the buffer
1034 (if (and (string= mode-name "Ada")
1037 (kill-local-variable 'ada-prj-src-dir)
1038 (kill-local-variable 'ada-prj-obj-dir)
1039 (ada-parse-prj-file (ada-prj-find-prj-file))))
1043 ;; else do this just for the current buffer
1044 (kill-local-variable 'ada-prj-src-dir)
1045 (kill-local-variable 'ada-prj-obj-dir)
1046 (ada-parse-prj-file (ada-prj-find-prj-file)))
1049 ;; ------ Private routines
1051 (defun ada-xref-current (file &optional ali-file-name)
1052 "Update the cross-references for FILE.
1053 This in fact recompiles FILE to create ALI-FILE-NAME."
1055 (if (and ali-file-name
1056 (get-file-buffer ali-file-name))
1057 (kill-buffer (get-file-buffer ali-file-name)))
1058 ;; prompt for command to execute
1059 (setq compile-command (concat ada-prj-comp-cmd
1063 (if ada-xref-confirm-compile
1064 (read-from-minibuffer "enter command to execute gcc: "
1069 (defun ada-first-non-nil (list)
1070 "Returns the first non-nil element of the LIST"
1073 ((car list) (car list))
1074 (t (ada-first-non-nil (cdr list)))
1078 (defun ada-find-ali-file-in-dir (file)
1079 "Search for FILE in obj_dir.
1080 The current buffer must be the Ada file."
1083 (if (file-exists-p (concat (file-name-directory x)
1085 (concat (file-name-directory x) file)
1090 (defun ada-get-ali-file-name (file)
1091 "Create the ali file name for the ada-file FILE.
1092 The file is searched for in every directory shown in the obj_dir lines of
1095 ;; This function has to handle the special case of non-standard
1096 ;; file names (i.e. not .adb or .ads)
1097 ;; The trick is the following:
1098 ;; 1- replace the extension of the current file with .ali,
1099 ;; and look for this file
1100 ;; 2- If this file is found:
1101 ;; grep the "^U" lines, and make sure we are not reading the
1102 ;; .ali file for a spec file. If we are, go to step 3.
1103 ;; 3- If the file is not found or step 2 failed:
1104 ;; find the name of the "other file", ie the body, and look
1105 ;; for its associated .ali file by subtituing the extension
1108 (set-buffer (get-file-buffer file))
1109 (let ((short-ali-file-name
1110 (concat (file-name-sans-extension (file-name-nondirectory file))
1114 ;; we take the first possible completion
1115 (setq ali-file-name (ada-find-ali-file-in-dir short-ali-file-name))
1117 ;; If we have found the .ali file, but the source file was a spec
1118 ;; with a non-standard name, search the .ali file for the body if any,
1119 ;; since the xref information is more complete in that one
1120 (unless ali-file-name
1121 (if (not (string= (file-name-extension file) ".ads"))
1123 (specs ada-spec-suffixes)
1126 (if (string-match (concat (regexp-quote (car specs)) "$")
1129 (set 'specs (cdr specs)))
1133 (ada-find-ali-file-in-dir
1134 (concat (file-name-sans-extension
1135 (file-name-nondirectory
1136 (ada-other-file-name)))
1139 (set 'ali-file-name body-ali))))
1141 ;; else we did not find the .ali file
1142 ;; Second chance: in case the files do not have standard names (such
1143 ;; as for instance file_s.ada and file_b.ada), try to go to the
1144 ;; other file and look for its ali file
1145 (setq short-ali-file-name
1146 (concat (file-name-sans-extension
1147 (file-name-nondirectory (ada-other-file-name)))
1149 (setq ali-file-name (ada-find-ali-file-in-dir short-ali-file-name))
1151 ;; If still not found, try to recompile the file
1152 (if (not ali-file-name)
1154 ;; recompile only if the user asked for this
1155 (if ada-xref-create-ali
1156 (ada-xref-current file ali-file-name))
1157 (error "Ali file not found. Recompile your file")))
1160 ;; same if the .ali file is too old and we must recompile it
1161 (if (and (file-newer-than-file-p file ali-file-name)
1162 ada-xref-create-ali)
1163 (ada-xref-current file ali-file-name))
1165 ;; else returns the correct absolute file name
1166 (expand-file-name ali-file-name))
1169 (defun ada-get-ada-file-name (file original-file)
1170 "Create the complete file name (+directory) for FILE.
1171 The original file (where the user was) is ORIGINAL-FILE. Search in project
1172 file for possible paths."
1175 (set-buffer (get-file-buffer original-file))
1176 ;; we choose the first possible completion and we
1177 ;; return the absolute file name
1179 (ada-first-non-nil (mapcar (lambda (x)
1180 (if (file-exists-p (concat (file-name-directory x)
1181 (file-name-nondirectory file)))
1182 (concat (file-name-directory x)
1183 (file-name-nondirectory file))
1188 (expand-file-name filename)
1190 (file-name-nondirectory file)
1191 " not found in src_dir. Please check your project file")))
1195 (defun ada-find-file-number-in-ali (file)
1196 "Returns the file number for FILE in the associated ali file."
1197 (set-buffer (ada-get-ali-buffer file))
1198 (goto-char (point-min))
1200 (let ((begin (re-search-forward "^D")))
1202 (re-search-forward (concat "^D " (file-name-nondirectory file)))
1203 (count-lines begin (point))))
1205 (defun ada-read-identifier (pos)
1206 "Returns the identlist around POS and switch to the .ali buffer."
1208 ;; If there's a compilation in progress, it's probably because the
1209 ;; .ali file didn't exist. So we should wait...
1210 (if compilation-in-progress
1212 (message "Compilation in progress. Try again when it is finished")
1213 (set 'quit-flag t)))
1215 ;; If at end of buffer (e.g the buffer is empty), error
1216 (if (>= (point) (point-max))
1217 (error "No identifier on point"))
1219 ;; goto first character of the identifier/operator (skip backward < and >
1220 ;; since they are part of multiple character operators
1222 (skip-chars-backward "a-zA-Z0-9_<>")
1224 ;; check if it really is an identifier
1225 (if (ada-in-comment-p)
1226 (error "Inside comment"))
1228 (let (identifier identlist)
1229 ;; Just in front of a string => we could have an operator declaration,
1230 ;; as in "+", "-", ..
1231 (if (= (char-after) ?\")
1234 ;; if looking at an operator
1235 (if (looking-at ada-operator-re)
1237 (if (and (= (char-before) ?\")
1238 (= (char-after (+ (length (match-string 0)) (point))) ?\"))
1240 (set 'identifier (concat "\"" (match-string 0) "\"")))
1242 (if (ada-in-string-p)
1243 (error "Inside string or character constant"))
1244 (if (looking-at (concat ada-keywords "[^a-zA-Z_]"))
1245 (error "No cross-reference available for reserved keyword"))
1246 (if (looking-at "[a-zA-Z0-9_]+")
1247 (set 'identifier (match-string 0))
1248 (error "No identifier around")))
1250 ;; Build the identlist
1251 (set 'identlist (ada-make-identlist))
1252 (ada-set-name identlist (downcase identifier))
1253 (ada-set-line identlist
1254 (number-to-string (count-lines (point-min) (point))))
1255 (ada-set-column identlist
1256 (number-to-string (1+ (current-column))))
1257 (ada-set-file identlist (buffer-file-name))
1261 (defun ada-get-all-references (identlist)
1262 "Completes and returns the IDENTLIST with the information extracted
1263 from the ali file (definition file and places where it is referenced)."
1265 (let ((ali-buffer (ada-get-ali-buffer (ada-file-of identlist)))
1267 (set-buffer ali-buffer)
1268 (goto-char (point-min))
1269 (ada-set-on-declaration identlist nil)
1271 ;; First attempt: we might already be on the declaration of the identifier
1272 ;; We want to look for the declaration only in a definite interval (after
1273 ;; the "^X ..." line for the current file, and before the next "^X" line
1275 (if (re-search-forward
1276 (concat "^X [0-9]+ " (file-name-nondirectory (ada-file-of identlist)))
1278 (let ((bound (save-excursion (re-search-forward "^X " nil t))))
1279 (set 'declaration-found
1281 (concat "^" (ada-line-of identlist)
1282 "." (ada-column-of identlist)
1283 "[ *]" (regexp-quote (ada-name-of identlist))
1284 " \\(.*\\)$") bound t))
1285 (if declaration-found
1286 (ada-set-on-declaration identlist t))
1289 ;; If declaration is still nil, then we were not on a declaration, and
1290 ;; have to fall back on other algorithms
1292 (unless declaration-found
1294 ;; Since we alread know the number of the file, search for a direct
1296 (goto-char (point-min))
1297 (set 'declaration-found t)
1300 (number-to-string (ada-find-file-number-in-ali
1301 (ada-file-of identlist))))
1302 (unless (re-search-forward (concat (ada-ali-index-of identlist)
1303 "|\\([0-9]+.[0-9]+ \\)*"
1304 (ada-line-of identlist)
1306 (ada-column-of identlist))
1309 ;; if we did not find it, it may be because the first reference
1310 ;; is not required to have a 'unit_number|' item included.
1311 ;; Or maybe we are already on the declaration...
1312 (unless (re-search-forward (concat "^\\([a-zA-Z0-9_.\"]+[ *]\\)*"
1313 (ada-line-of identlist)
1315 (ada-column-of identlist))
1318 ;; If still not found, then either the declaration is unknown
1319 ;; or the source file has been modified since the ali file was
1321 (set 'declaration-found nil)
1325 ;; Last check to be completly sure we have found the correct line (the
1326 ;; ali might not be up to date for instance)
1327 (if declaration-found
1330 ;; while we have a continuation line, go up one line
1331 (while (looking-at "^\\.")
1333 (unless (looking-at (concat "[0-9]+.[0-9]+[ *]"
1334 (ada-name-of identlist) " "))
1335 (set 'declaration-found nil))))
1337 ;; Still no success ! The ali file must be too old, and we need to
1338 ;; use a basic algorithm based on guesses. Note that this only happens
1339 ;; if the user does not want us to automatically recompile files
1341 (unless declaration-found
1342 (unless (ada-xref-find-in-modified-ali identlist)
1343 ;; no more idea to find the declaration. Give up
1345 (kill-buffer ali-buffer)
1346 (error (concat "No declaration of " (ada-name-of identlist)
1352 ;; Now that we have found a suitable line in the .ali file, get the
1353 ;; information available
1355 (if declaration-found
1356 (let ((current-line (buffer-substring
1357 (point) (save-excursion (end-of-line) (point)))))
1361 (while (looking-at "^\\.\\(.*\\)")
1362 (set 'current-line (concat current-line (match-string 1)))
1366 (if (re-search-backward "^X [0-9]+ \\([a-zA-Z0-9_.-]+\\)" nil t)
1367 (ada-set-declare-file
1369 (ada-get-ada-file-name (match-string 1)
1370 (ada-file-of identlist))))
1372 (ada-set-references identlist current-line)
1376 (defun ada-xref-find-in-modified-ali (identlist)
1377 "Find the matching position for IDENTLIST in the current ali buffer.
1378 This function is only called when the file was not up-to-date, so we need
1379 to make some guesses.
1380 This function is disabled for operators, and only works for identifiers."
1382 (unless (= (string-to-char (ada-name-of identlist)) ?\")
1384 (let ((declist '()) ;;; ( (line_in_ali_file line_in_ada) ( ... ))
1385 (my-regexp (concat "[ *]"
1386 (regexp-quote (ada-name-of identlist)) " "))
1393 (goto-char (point-max))
1394 (while (re-search-backward my-regexp nil t)
1396 (set 'line-ali (count-lines (point-min) (point)))
1398 ;; have a look at the line and column numbers
1399 (if (looking-at "^\\([0-9]+\\).\\([0-9]+\\)[ *]")
1401 (setq line-ada (match-string 1))
1402 (setq col-ada (match-string 2)))
1403 (setq line-ada "--")
1406 ;; construct a list with the file names and the positions within
1407 (if (re-search-backward "^X [0-9]+ \\([a-zA-Z0-9._-]+\\)" nil t)
1409 'declist (list line-ali (match-string 1) line-ada col-ada))
1414 ;; how many possible declarations have we found ?
1415 (setq len (length declist))
1419 (kill-buffer (current-buffer))
1420 (error (concat "No declaration of "
1421 (ada-name-of identlist)
1422 " recorded in .ali file")))
1424 ;; one => should be the right one
1426 (goto-line (caar declist)))
1428 ;; more than one => display choice list
1430 (with-output-to-temp-buffer "*choice list*"
1432 (princ "Identifier is overloaded and Xref information is not up to date.\n")
1433 (princ "Possible declarations are:\n\n")
1434 (princ " no. in file at line col\n")
1435 (princ " --- --------------------- ---- ----\n")
1437 (while (<= counter len)
1438 (princ (format " %2d) %-21s %4s %4s\n"
1440 (ada-get-ada-file-name
1441 (nth 1 (nth (1- counter) declist))
1442 (ada-file-of identlist))
1443 (nth 2 (nth (1- counter) declist))
1444 (nth 3 (nth (1- counter) declist))
1446 (setq counter (1+ counter))
1449 ) ; end of with-output-to ...
1453 (not (integerp choice))
1456 (setq choice (string-to-int
1457 (read-from-minibuffer "Enter No. of your choice: "))))
1458 (goto-line (car (nth (1- choice) declist)))
1462 (defun ada-find-in-ali (identlist &optional other-frame)
1463 "Look in the .ali file for the definition of the identifier in IDENTLIST.
1464 If OTHER-FRAME is non nil, and `ada-xref-other-buffer' is non nil,
1465 opens a new window to show the declaration."
1467 (ada-get-all-references identlist)
1468 (let ((ali-line (ada-references-of identlist))
1471 ;; If we were on a declaration, go to the body
1472 (if (ada-on-declaration identlist)
1473 (if (string-match "\\([0-9]+\\)[bc]\\([0-9]+\\)" ali-line)
1475 (setq line (match-string 1 ali-line)
1476 col (match-string 2 ali-line))
1477 ;; it there was a file number in the same line
1478 (if (string-match "\\([0-9]+\\)|\\([^|bc]+\\)?[bc]" ali-line)
1479 (let ((file-number (match-string 1 ali-line)))
1480 (goto-char (point-min))
1481 (re-search-forward "^D \\([a-zA-Z0-9_.-]+\\)" nil t
1482 (string-to-number file-number))
1483 (set 'file (match-string 1))
1485 ;; Else get the nearest file
1486 (re-search-backward "^X [0-9]+ \\([a-zA-Z0-9_.-]+\\)" nil t)
1487 (set 'file (match-string 1))
1490 (error "No body found"))
1492 ;; Else we were not on the declaration, find the place for it
1493 (string-match "\\([0-9]+\\)[a-zA-Z+]\\([0-9]+\\)[ *]" ali-line)
1494 (setq line (match-string 1 ali-line)
1495 col (match-string 2 ali-line)
1496 file (ada-declare-file-of identlist))
1499 ;; Now go to the buffer
1500 (ada-xref-change-buffer
1501 (ada-get-ada-file-name file (ada-file-of identlist))
1502 (string-to-number line)
1503 (1- (string-to-number col))
1508 (defun ada-xref-change-buffer
1509 (file line column identlist &optional other-frame)
1510 "Select and display FILE, at LINE and COLUMN. The new file is
1511 associated with the same project file as the one for IDENTLIST.
1512 If we do not end on the same identifier as IDENTLIST, find the closest
1513 match. Kills the .ali buffer at the end.
1514 If OTHER-FRAME is non-nil, creates a new frame to show the file."
1518 (ali-buffer (current-buffer)))
1520 ;; get the current project file for the source ada file
1522 (set-buffer (get-file-buffer (ada-file-of identlist)))
1523 (set 'prj-file ada-prj-prj-file))
1525 ;; Select and display the destination buffer
1526 (if ada-xref-other-buffer
1528 (find-file-other-frame file)
1529 (set 'declaration-buffer (find-file-noselect file))
1530 (set-buffer declaration-buffer)
1531 (switch-to-buffer-other-window declaration-buffer)
1536 ;; If the new buffer is not already associated with a project file, do it
1537 (unless (my-local-variable-if-set-p 'ada-prj-prj-file (current-buffer))
1539 (make-local-variable 'ada-prj-prj-file)
1540 (set 'ada-prj-prj-file prj-file)))
1542 ;; move the cursor to the correct position
1545 (move-to-column column)
1547 ;; If we are not on the identifier, the ali file was not up-to-date.
1548 ;; Try to find the nearest position where the identifier is found,
1549 ;; this is probably the right one.
1550 (unless (looking-at (ada-name-of identlist))
1551 (ada-xref-search-nearest (ada-name-of identlist)))
1553 (kill-buffer ali-buffer)))
1556 (defun ada-xref-search-nearest (name)
1557 "Searches for NAME nearest to the position recorded in the Xref file.
1558 It returns the position of the declaration in the buffer or nil if not found."
1559 (let ((orgpos (point))
1563 (goto-char (point-max))
1565 ;; loop - look for all declarations of name in this file
1566 (while (search-backward name nil t)
1568 ;; check if it really is a complete Ada identifier
1570 (not (save-excursion
1571 (goto-char (match-end 0))
1573 (not (ada-in-string-or-comment-p))
1575 ;; variable declaration ?
1577 (skip-chars-forward "a-zA-Z_0-9" )
1578 (ada-goto-next-non-ws)
1579 (looking-at ":[^=]"))
1580 ;; procedure, function, task or package declaration ?
1582 (ada-goto-previous-word)
1583 (looking-at "\\<[pP][rR][oO][cC][eE][dD][uU][rR][eE]\\>\\|\\<[fF][uU][nN][cC][tT][iI][oO][nN]\\>\\|\\<[tT][yY][pP][eE]\\>\\|\\<[tT][aA][sS][kK]\\>\\|\\<[pP][aA][cC][kK][aA][gG][eE]\\>\\|\\<[bB][oO][dD][yY]\\>"))))
1585 ;; check if it is nearer than the ones before if any
1587 (< (abs (- (point) orgpos)) diff))
1589 (setq newpos (point)
1590 diff (abs (- newpos orgpos))))))
1595 (message "ATTENTION: this declaration is only a (good) guess ...")
1600 ;; Find the parent library file of the current file
1601 (defun ada-goto-parent ()
1602 "Go to the parent library file."
1604 (ada-require-project-file)
1606 (let ((buffer (ada-get-ali-buffer (buffer-file-name)))
1612 (goto-char (point-min))
1613 (re-search-forward "^U \\([^ \t%]+\\)%[bs][ \t]+\\([^ \t]+\\)")
1614 (setq unit-name (match-string 1))
1615 (if (not (string-match "\\(.*\\)\\.[^.]+" unit-name))
1617 (kill-buffer buffer)
1618 (error "No parent unit !"))
1619 (setq unit-name (match-string 1 unit-name))
1622 ;; look for the file name for the parent unit specification
1623 (goto-char (point-min))
1624 (re-search-forward (concat "^W " unit-name
1625 "%s[ \t]+\\([^ \t]+\\)[ \t]+"
1627 (setq body-name (match-string 1))
1628 (setq ali-name (match-string 2))
1629 (kill-buffer buffer)
1632 (setq ali-name (ada-find-ali-file-in-dir ali-name))
1635 ;; Tries to open the new ali file to find the spec file
1638 (find-file ali-name)
1639 (goto-char (point-min))
1640 (re-search-forward (concat "^U " unit-name "%s[ \t]+"
1642 (setq body-name (match-string 1))
1643 (kill-buffer (current-buffer))
1648 (find-file body-name)
1651 (defun ada-make-filename-from-adaname (adaname)
1652 "Determine the filename in which ADANAME is found.
1653 This is a GNAT specific function that uses gnatkrunch."
1655 (setq krunch-buf (generate-new-buffer "*gkrunch*"))
1657 (set-buffer krunch-buf)
1658 ;; send adaname to external process `gnatkr'.
1659 (call-process "gnatkr" nil krunch-buf nil
1660 adaname ada-krunch-args)
1661 ;; fetch output of that process
1662 (setq adaname (buffer-substring
1665 (goto-char (point-min))
1668 (kill-buffer krunch-buf)))
1673 (defun ada-make-body-gnatstub ()
1674 "Create an Ada package body in the current buffer.
1675 This function uses the `gnatstub' program to create the body.
1676 This function typically is to be hooked into `ff-file-created-hooks'."
1679 (save-some-buffers nil nil)
1681 (ada-require-project-file)
1683 (delete-region (point-min) (point-max))
1685 ;; Call the external process gnatstub
1686 (let* ((gnatstub-opts (ada-treat-cmd-string ada-gnatstub-opts))
1687 (filename (buffer-file-name (car (cdr (buffer-list)))))
1688 (output (concat (file-name-sans-extension filename) ".adb"))
1689 (gnatstub-cmd (concat "gnatstub " gnatstub-opts " " filename))
1690 (buffer (get-buffer-create "*gnatstub*")))
1694 (compilation-minor-mode 1)
1696 (insert gnatstub-cmd)
1699 ;; call gnatstub to create the body file
1700 (call-process shell-file-name nil buffer nil "-c" gnatstub-cmd)
1704 (goto-char (point-min))
1705 (search-forward "command not found" nil t))
1707 (message "gnatstub was not found -- using the basic algorithm")
1709 (kill-buffer buffer)
1712 ;; Else clean up the output
1714 ;; Kill the temporary buffer created by find-file
1715 (set-buffer-modified-p nil)
1716 (kill-buffer (current-buffer))
1718 (if (file-exists-p output)
1721 (kill-buffer buffer))
1723 ;; display the error buffer
1724 (display-buffer buffer)
1729 (defun ada-xref-initialize ()
1730 "Function called by ada-mode-hook to initialize the ada-xref.el package.
1731 For instance, it creates the gnat-specific menus, set some hooks for
1734 (make-local-hook 'ff-file-created-hooks)
1735 (setq ff-file-created-hooks 'ada-make-body-gnatstub)
1737 ;; Read the project file and update the search path
1738 ;; before looking for the other file
1739 (make-local-hook 'ff-pre-find-hooks)
1740 (add-hook 'ff-pre-find-hooks 'ada-require-project-file)
1742 ;; Completion for file names in the mini buffer should ignore .ali files
1743 (add-to-list 'completion-ignored-extensions ".ali")
1747 ;; ----- Add to ada-mode-hook ---------------------------------------------
1749 ;; Set the keymap once and for all, so that the keys set by the user in his
1750 ;; config file are not overwritten every time we open a new file.
1753 (add-hook 'ada-mode-hook 'ada-xref-initialize)
1757 ;;; ada-xref.el ends here