Doc fixes for package.el.
[bpt/emacs.git] / lisp / progmodes / ada-xref.el
CommitLineData
417451fe 1;; ada-xref.el --- for lookup and completion in Ada mode
797aab3c 2
95df8112 3;; Copyright (C) 1994-2011 Free Software Foundation, Inc.
797aab3c
GM
4
5;; Author: Markus Heritsch <Markus.Heritsch@studbox.uni-stuttgart.de>
6;; Rolf Ebert <ebert@inf.enst.fr>
7;; Emmanuel Briot <briot@gnat.com>
417451fe 8;; Maintainer: Stephen Leake <stephen_leake@stephe-leake.org>
797aab3c 9;; Keywords: languages ada xref
bd78fa1d 10;; Package: ada-mode
797aab3c 11
874d7995 12;; This file is part of GNU Emacs.
797aab3c 13
b1fc2b50 14;; GNU Emacs is free software: you can redistribute it and/or modify
797aab3c 15;; it under the terms of the GNU General Public License as published by
b1fc2b50
GM
16;; the Free Software Foundation, either version 3 of the License, or
17;; (at your option) any later version.
797aab3c 18
2be7dabc 19;; GNU Emacs is distributed in the hope that it will be useful,
797aab3c
GM
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.
23
24;; You should have received a copy of the GNU General Public License
b1fc2b50 25;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
797aab3c
GM
26
27;;; Commentary:
28;;; This Package provides a set of functions to use the output of the
29;;; cross reference capabilities of the GNAT Ada compiler
30;;; for lookup and completion in Ada mode.
31;;;
797aab3c 32;;; If a file *.`adp' exists in the ada-file directory, then it is
ced16f5d 33;;; read for configuration informations. It is read only the first
797aab3c
GM
34;;; time a cross-reference is asked for, and is not read later.
35
36;;; You need Emacs >= 20.2 to run this package
37
417451fe
JB
38
39;;; History:
40;;
41
3afbc435
PJ
42;;; Code:
43
797aab3c
GM
44;; ----- Requirements -----------------------------------------------------
45
46(require 'compile)
47(require 'comint)
7abe197c
JB
48(require 'find-file)
49(require 'ada-mode)
797aab3c 50
417451fe 51;; ------ User variables
797aab3c 52(defcustom ada-xref-other-buffer t
eec3232e
GM
53 "*If nil, always display the cross-references in the same buffer.
54Otherwise create either a new buffer or a new frame."
797aab3c
GM
55 :type 'boolean :group 'ada)
56
93cdce20 57(defcustom ada-xref-create-ali nil
eec3232e 58 "*If non-nil, run gcc whenever the cross-references are not up-to-date.
ced16f5d 59If nil, the cross-reference mode never runs gcc."
797aab3c
GM
60 :type 'boolean :group 'ada)
61
62(defcustom ada-xref-confirm-compile nil
32b1a27f 63 "*If non-nil, ask for confirmation before compiling or running the application."
797aab3c
GM
64 :type 'boolean :group 'ada)
65
66(defcustom ada-krunch-args "0"
ced16f5d
RS
67 "*Maximum number of characters for filenames created by `gnatkr'.
68Set to 0, if you don't use crunched filenames. This should be a string."
797aab3c
GM
69 :type 'string :group 'ada)
70
d5875b25
JB
71(defcustom ada-gnat-cmd "gnat"
72 "Default GNAT project file parser.
73Will be run with args \"list -v -Pfile.gpr\".
74Default is standard GNAT distribution; alternate \"gnatpath\"
75is faster, available from Ada mode web site."
76 :type 'string :group 'ada)
77
c94ca9e0 78(defcustom ada-gnatls-args '("-v")
f020b5c9 79 "*Arguments to pass to `gnatls' to find location of the runtime.
ced16f5d 80Typical use is to pass `--RTS=soft-floats' on some systems that support it.
c94ca9e0 81
ced16f5d 82You can also add `-I-' if you do not want the current directory to be included.
c94ca9e0 83Otherwise, going from specs to bodies and back will first look for files in the
ced16f5d 84current directory. This only has an impact if you are not using project files,
c94ca9e0
JB
85but only ADA_INCLUDE_PATH."
86 :type '(repeat string) :group 'ada)
87
4884c50b 88(defcustom ada-prj-default-comp-opt "-gnatq -gnatQ"
15ea3b67
GM
89 "Default compilation options."
90 :type 'string :group 'ada)
91
92(defcustom ada-prj-default-bind-opt ""
93 "Default binder options."
94 :type 'string :group 'ada)
95
96(defcustom ada-prj-default-link-opt ""
97 "Default linker options."
98 :type 'string :group 'ada)
99
100(defcustom ada-prj-default-gnatmake-opt "-g"
ced16f5d 101 "Default options for `gnatmake'."
15ea3b67
GM
102 :type 'string :group 'ada)
103
d5875b25
JB
104(defcustom ada-prj-default-gpr-file ""
105 "Default GNAT project file.
106If non-empty, this file is parsed to set the source and object directories for
107the Ada mode project."
108 :type 'string :group 'ada)
109
110(defcustom ada-prj-ada-project-path-sep
8175cb90
JB
111 (cond ((boundp 'path-separator) path-separator) ; 20.3+
112 ((memq system-type '(windows-nt ms-dos)) ";")
113 (t ":"))
d5875b25
JB
114 "Default separator for ada_project_path project variable."
115 :type 'string :group 'ada)
116
4884c50b 117(defcustom ada-prj-gnatfind-switches "-rf"
ced16f5d
RS
118 "Default switches to use for `gnatfind'.
119You should modify this variable, for instance to add `-a', if you are working
4884c50b 120in an environment where most ALI files are write-protected.
ced16f5d 121The command `gnatfind' is used every time you choose the menu
4884c50b
SM
122\"Show all references\"."
123 :type 'string :group 'ada)
124
0262d5e1
JB
125(defcustom ada-prj-default-check-cmd
126 (concat "${cross_prefix}gnatmake -u -c -gnatc ${gnatmake_opt} ${full_current}"
127 " -cargs ${comp_opt}")
128 "*Default command to be used to compile a single file.
129Emacs will substitute the current filename for ${full_current}, or add
130the filename at the end. This is the same syntax as in the project file."
131 :type 'string :group 'ada)
132
eec3232e 133(defcustom ada-prj-default-comp-cmd
93cdce20
SM
134 (concat "${cross_prefix}gnatmake -u -c ${gnatmake_opt} ${full_current} -cargs"
135 " ${comp_opt}")
797aab3c 136 "*Default command to be used to compile a single file.
417451fe 137Emacs will substitute the current filename for ${full_current}, or add
7977c946 138the filename at the end. This is the same syntax as in the project file."
15ea3b67
GM
139 :type 'string :group 'ada)
140
141(defcustom ada-prj-default-debugger "${cross_prefix}gdb"
417451fe 142 "*Default name of the debugger."
797aab3c
GM
143 :type 'string :group 'ada)
144
145(defcustom ada-prj-default-make-cmd
d5875b25 146 (concat "${cross_prefix}gnatmake -o ${main} ${main} ${gnatmake_opt} "
417451fe 147 "-cargs ${comp_opt} -bargs ${bind_opt} -largs ${link_opt}")
797aab3c
GM
148 "*Default command to be used to compile the application.
149This is the same syntax as in the project file."
150 :type 'string :group 'ada)
151
152(defcustom ada-prj-default-project-file ""
417451fe
JB
153 "*Name of the current project file.
154Emacs will not try to use the search algorithm to find the project file if
155this string is not empty. It is set whenever a project file is found."
797aab3c
GM
156 :type '(file :must-match t) :group 'ada)
157
158(defcustom ada-gnatstub-opts "-q -I${src_dir}"
ff6d2a9e 159 "*Options to pass to `gnatsub' to generate the body of a package.
eec3232e 160This has the same syntax as in the project file (with variable substitution)."
797aab3c
GM
161 :type 'string :group 'ada)
162
163(defcustom ada-always-ask-project nil
eec3232e 164 "*If nil, use default values when no project file was found.
15ea3b67
GM
165Otherwise, ask the user for the name of the project file to use."
166 :type 'boolean :group 'ada)
797aab3c 167
72bc50c0 168(defconst ada-on-ms-windows (memq system-type '(windows-nt))
7977c946 169 "True if we are running on Windows.")
4884c50b
SM
170
171(defcustom ada-tight-gvd-integration nil
172 "*If non-nil, a new Emacs frame will be swallowed in GVD when debugging.
75497862
KS
173If GVD is not the debugger used, nothing happens."
174 :type 'boolean :group 'ada)
4884c50b 175
93cdce20
SM
176(defcustom ada-xref-search-with-egrep t
177 "*If non-nil, use egrep to find the possible declarations for an entity.
178This alternate method is used when the exact location was not found in the
ced16f5d 179information provided by GNAT. However, it might be expensive if you have a lot
93cdce20
SM
180of sources, since it will search in all the files in your project."
181 :type 'boolean :group 'ada)
182
183(defvar ada-load-project-hook nil
184 "Hook that is run when loading a project file.
185Each function in this hook takes one argument FILENAME, that is the name of
186the project file to load.
187This hook should be used to support new formats for the project files.
188
189If the function can load the file with the given filename, it should create a
190buffer that contains a conversion of the file to the standard format of the
ced16f5d
RS
191project files, and return that buffer. (The usual \"src_dir=\" or \"obj_dir=\"
192lines.) It should return nil if it doesn't know how to convert that project
93cdce20
SM
193file.")
194
195
797aab3c
GM
196;; ------- Nothing to be modified by the user below this
197(defvar ada-last-prj-file ""
eec3232e 198 "Name of the last project file entered by the user.")
797aab3c 199
0262d5e1 200(defconst ada-prj-file-extension ".adp"
eec3232e 201 "The extension used for project files.")
797aab3c 202
15ea3b67
GM
203(defvar ada-xref-runtime-library-specs-path '()
204 "Directories where the specs for the standard library is found.
205This is used for cross-references.")
206
207(defvar ada-xref-runtime-library-ali-path '()
208 "Directories where the ali for the standard library is found.
209This is used for cross-references.")
210
797aab3c 211(defvar ada-xref-pos-ring '()
eec3232e
GM
212 "List of positions selected by the cross-references functions.
213Used to go back to these positions.")
797aab3c 214
4884c50b
SM
215(defvar ada-cd-command
216 (if (string-match "cmdproxy.exe" shell-file-name)
217 "cd /d"
218 "cd")
ced16f5d
RS
219 "Command to use to change to a specific directory.
220On Windows systems using `cmdproxy.exe' as the shell,
221we need to use `/d' or the drive is never changed.")
4884c50b 222
72bc50c0 223(defvar ada-command-separator (if ada-on-ms-windows " && " "\n")
ced16f5d
RS
224 "Separator to use between multiple commands to `compile' or `start-process'.
225`cmdproxy.exe' doesn't recognize multiple-line commands, so we have to use
4884c50b
SM
226\"&&\" for now.")
227
797aab3c 228(defconst ada-xref-pos-ring-max 16
7977c946 229 "Number of positions kept in the list `ada-xref-pos-ring'.")
797aab3c
GM
230
231(defvar ada-operator-re
15ea3b67 232 "\\+\\|-\\|/\\|\\*\\*\\|\\*\\|=\\|&\\|abs\\|mod\\|rem\\|and\\|not\\|or\\|xor\\|<=\\|<\\|>=\\|>"
eec3232e 233 "Regexp to match for operators.")
797aab3c 234
15ea3b67 235(defvar ada-xref-project-files '()
0262d5e1
JB
236 "Associative list of project files with properties.
237It has the format: (project project ...)
238A project has the format: (project-file . project-plist)
7977c946 239\(See 'apropos plist' for operations on property lists).
d5875b25 240See `ada-default-prj-properties' for the list of valid properties.
7977c946
JB
241The current project is retrieved with `ada-xref-current-project'.
242Properties are retrieved with `ada-xref-get-project-field', set with
243`ada-xref-set-project-field'. If project properties are accessed with no
0262d5e1 244project file, a (nil . default-properties) entry is created.")
15ea3b67 245
c94ca9e0
JB
246
247;; ----- Identlist manipulation -------------------------------------------
248;; An identlist is a vector that is used internally to reference an identifier
249;; To facilitate its use, we provide the following macros
250
251(defmacro ada-make-identlist () (make-vector 8 nil))
252(defmacro ada-name-of (identlist) (list 'aref identlist 0))
253(defmacro ada-line-of (identlist) (list 'aref identlist 1))
254(defmacro ada-column-of (identlist) (list 'aref identlist 2))
255(defmacro ada-file-of (identlist) (list 'aref identlist 3))
256(defmacro ada-ali-index-of (identlist) (list 'aref identlist 4))
257(defmacro ada-declare-file-of (identlist) (list 'aref identlist 5))
258(defmacro ada-references-of (identlist) (list 'aref identlist 6))
259(defmacro ada-on-declaration (identlist) (list 'aref identlist 7))
260
261(defmacro ada-set-name (identlist name) (list 'aset identlist 0 name))
262(defmacro ada-set-line (identlist line) (list 'aset identlist 1 line))
263(defmacro ada-set-column (identlist col) (list 'aset identlist 2 col))
264(defmacro ada-set-file (identlist file) (list 'aset identlist 3 file))
265(defmacro ada-set-ali-index (identlist index) (list 'aset identlist 4 index))
266(defmacro ada-set-declare-file (identlist file) (list 'aset identlist 5 file))
267(defmacro ada-set-references (identlist ref) (list 'aset identlist 6 ref))
268(defmacro ada-set-on-declaration (ident value) (list 'aset ident 7 value))
269
270(defsubst ada-get-ali-buffer (file)
417451fe 271 "Read the ali file FILE into a new buffer, and return the buffer's name."
c94ca9e0
JB
272 (find-file-noselect (ada-get-ali-file-name file)))
273
274
275;; -----------------------------------------------------------------------
276
4884c50b 277(defun ada-quote-cmd (cmd)
7977c946 278 "Duplicate all `\\' characters in CMD so that it can be passed to `compile'."
4884c50b 279 (mapconcat 'identity (split-string cmd "\\\\") "\\\\"))
15ea3b67 280
0262d5e1
JB
281(defun ada-find-executable (exec-name)
282 "Find the full path to the executable file EXEC-NAME.
d5875b25 283If not found, throw an error.
32b1a27f 284On Windows systems, this will properly handle .exe extension as well."
d5875b25
JB
285 (let ((result (or (ada-find-file-in-dir exec-name exec-path)
286 (ada-find-file-in-dir (concat exec-name ".exe") exec-path))))
287 (if result
288 result
289 (error "'%s' not found in path" exec-name))))
0262d5e1 290
4884c50b 291(defun ada-initialize-runtime-library (cross-prefix)
ced16f5d 292 "Initialize the variables for the runtime library location.
417451fe 293CROSS-PREFIX is the prefix to use for the `gnatls' command."
d5875b25
JB
294 (let ((gnatls
295 (condition-case nil
296 ;; if gnatls not found, just give up (may not be using GNAT)
297 (ada-find-executable (concat cross-prefix "gnatls"))
298 (error nil))))
299 (if gnatls
300 (save-excursion
301 (setq ada-xref-runtime-library-specs-path '()
302 ada-xref-runtime-library-ali-path '())
303 (set-buffer (get-buffer-create "*gnatls*"))
304 (widen)
305 (erase-buffer)
306 ;; Even if we get an error, delete the *gnatls* buffer
307 (unwind-protect
308 (let ((status (apply 'call-process gnatls (append '(nil t nil) ada-gnatls-args))))
309 (goto-char (point-min))
310
311 ;; Since we didn't provide all the inputs gnatls expects, it returns status 4
312 (if (/= 4 status)
313 (error (buffer-substring (point) (line-end-position))))
314
315 ;; Source path
316
317 (search-forward "Source Search Path:")
318 (forward-line 1)
319 (while (not (looking-at "^$"))
320 (back-to-indentation)
321 (if (looking-at "<Current_Directory>")
322 (add-to-list 'ada-xref-runtime-library-specs-path ".")
323 (add-to-list 'ada-xref-runtime-library-specs-path
324 (buffer-substring-no-properties
325 (point)
e180ab9f 326 (point-at-eol))))
d5875b25
JB
327 (forward-line 1))
328
329 ;; Object path
330
331 (search-forward "Object Search Path:")
332 (forward-line 1)
333 (while (not (looking-at "^$"))
334 (back-to-indentation)
335 (if (looking-at "<Current_Directory>")
336 (add-to-list 'ada-xref-runtime-library-ali-path ".")
337 (add-to-list 'ada-xref-runtime-library-ali-path
338 (buffer-substring-no-properties
339 (point)
e180ab9f 340 (point-at-eol))))
d5875b25
JB
341 (forward-line 1))
342 )
343 (kill-buffer nil))))
344
15ea3b67
GM
345 (set 'ada-xref-runtime-library-specs-path
346 (reverse ada-xref-runtime-library-specs-path))
347 (set 'ada-xref-runtime-library-ali-path
348 (reverse ada-xref-runtime-library-ali-path))
349 ))
350
d5875b25
JB
351(defun ada-gnat-parse-gpr (plist gpr-file)
352 "Set gpr_file, src_dir and obj_dir properties in PLIST by parsing GPR-FILE.
32b1a27f 353Return new value of PLIST.
d5875b25
JB
354GPR_FILE must be full path to file, normalized.
355src_dir, obj_dir will include compiler runtime.
356Assumes environment variable ADA_PROJECT_PATH is set properly."
9a529312 357 (with-current-buffer (get-buffer-create "*gnatls*")
d5875b25
JB
358 (erase-buffer)
359
360 ;; this can take a long time; let the user know what's up
361 (message "Parsing %s ..." gpr-file)
362
363 ;; Even if we get an error, delete the *gnatls* buffer
364 (unwind-protect
365 (let* ((cross-prefix (plist-get plist 'cross_prefix))
366 (gnat (concat cross-prefix ada-gnat-cmd))
367 ;; Putting quotes around gpr-file confuses gnatpath on Lynx; not clear why
368 (gpr-opt (concat "-P" gpr-file))
369 (src-dir '())
370 (obj-dir '())
371 (status (call-process gnat nil t nil "list" "-v" gpr-opt)))
372 (goto-char (point-min))
373
374 (if (/= 0 status)
375 (error (buffer-substring (point) (line-end-position))))
376
377 ;; Source path
378
379 (search-forward "Source Search Path:")
380 (forward-line 1) ; first directory in list
381 (while (not (looking-at "^$")) ; terminate on blank line
382 (back-to-indentation) ; skip whitespace
6dc439cb
SM
383 (add-to-list 'src-dir
384 (if (looking-at "<Current_Directory>")
385 default-directory
d5875b25
JB
386 (expand-file-name
387 (buffer-substring-no-properties
388 (point) (line-end-position)))))
389 (forward-line 1))
390
391 ;; Object path
392
393 (search-forward "Object Search Path:")
394 (forward-line 1)
395 (while (not (looking-at "^$"))
396 (back-to-indentation)
6dc439cb
SM
397 (add-to-list 'obj-dir
398 (if (looking-at "<Current_Directory>")
399 default-directory
d5875b25
JB
400 (expand-file-name
401 (buffer-substring-no-properties
402 (point) (line-end-position)))))
403 (forward-line 1))
404
405 ;; Set properties
406 (setq plist (plist-put plist 'gpr_file gpr-file))
dd4fdc44
JB
407 (setq plist (plist-put plist 'src_dir src-dir))
408 (plist-put plist 'obj_dir obj-dir)
d5875b25
JB
409 )
410 (kill-buffer nil)
411 (message "Parsing %s ... done" gpr-file)
412 )
413 ))
414
15ea3b67 415(defun ada-treat-cmd-string (cmd-string)
d5875b25
JB
416 "Replace variable references ${var} in CMD-STRING with the appropriate value.
417Also replace standard environment variables $var.
417451fe
JB
418Assumes project exists.
419As a special case, ${current} is replaced with the name of the current
420file, minus extension but with directory, and ${full_current} is
4884c50b 421replaced by the name including the extension."
15ea3b67
GM
422
423 (while (string-match "\\(-[^-\$IO]*[IO]\\)?\${\\([^}]+\\)}" cmd-string)
4884c50b
SM
424 (let (value
425 (name (match-string 2 cmd-string)))
426 (cond
427 ((string= name "current")
428 (setq value (file-name-sans-extension (buffer-file-name))))
429 ((string= name "full_current")
430 (setq value (buffer-file-name)))
431 (t
15ea3b67 432 (save-match-data
4884c50b
SM
433 (setq value (ada-xref-get-project-field (intern name))))))
434
435 ;; Check if there is an environment variable with the same name
436 (if (null value)
437 (if (not (setq value (getenv name)))
f020b5c9 438 (message "%s" (concat "No project or environment variable " name " found"))))
a1506d29 439
15ea3b67
GM
440 (cond
441 ((null value)
4884c50b 442 (setq cmd-string (replace-match "" t t cmd-string)))
15ea3b67 443 ((stringp value)
4884c50b 444 (setq cmd-string (replace-match value t t cmd-string)))
15ea3b67
GM
445 ((listp value)
446 (let ((prefix (match-string 1 cmd-string)))
4884c50b 447 (setq cmd-string (replace-match
15ea3b67
GM
448 (mapconcat (lambda(x) (concat prefix x)) value " ")
449 t t cmd-string)))))
450 ))
d5875b25 451 (substitute-in-file-name cmd-string))
15ea3b67 452
a1506d29 453
15ea3b67 454(defun ada-xref-get-project-field (field)
4884c50b 455 "Extract the value of FIELD from the current project file.
417451fe 456Project variables are substituted.
4884c50b
SM
457
458Note that for src_dir and obj_dir, you should rather use
7977c946
JB
459`ada-xref-get-src-dir-field' or `ada-xref-get-obj-dir-field'
460which will in addition return the default paths."
15ea3b67 461
85187d83 462 (let* ((project-plist (cdr (ada-xref-current-project)))
d5875b25 463 (value (plist-get project-plist field)))
15ea3b67 464
d5875b25
JB
465 (cond
466 ((eq field 'gnatmake_opt)
467 (let ((gpr-file (plist-get project-plist 'gpr_file)))
468 (if (not (string= gpr-file ""))
469 (setq value (concat "-P\"" gpr-file "\" " value)))))
4884c50b 470
d5875b25
JB
471 ;; FIXME: check for src_dir, obj_dir here, rather than requiring user to do it
472 (t
473 nil))
474
475 ;; Substitute the ${...} constructs in all the strings, including
476 ;; inside lists
4884c50b
SM
477 (cond
478 ((stringp value)
479 (ada-treat-cmd-string value))
480 ((null value)
481 nil)
482 ((listp value)
483 (mapcar (lambda(x) (if x (ada-treat-cmd-string x) x)) value))
484 (t
485 value)
486 )
487 ))
488
4884c50b
SM
489(defun ada-xref-get-src-dir-field ()
490 "Return the full value for src_dir, including the default directories.
491All the directories are returned as absolute directories."
492
493 (let ((build-dir (ada-xref-get-project-field 'build_dir)))
494 (append
495 ;; Add ${build_dir} in front of the path
496 (list build-dir)
a1506d29 497
4884c50b
SM
498 (ada-get-absolute-dir-list (ada-xref-get-project-field 'src_dir)
499 build-dir)
a1506d29 500
4884c50b
SM
501 ;; Add the standard runtime at the end
502 ada-xref-runtime-library-specs-path)))
503
504(defun ada-xref-get-obj-dir-field ()
505 "Return the full value for obj_dir, including the default directories.
506All the directories are returned as absolute directories."
507
508 (let ((build-dir (ada-xref-get-project-field 'build_dir)))
509 (append
510 ;; Add ${build_dir} in front of the path
511 (list build-dir)
a1506d29 512
4884c50b
SM
513 (ada-get-absolute-dir-list (ada-xref-get-project-field 'obj_dir)
514 build-dir)
a1506d29 515
4884c50b
SM
516 ;; Add the standard runtime at the end
517 ada-xref-runtime-library-ali-path)))
518
85187d83
JB
519(defun ada-xref-set-project-field (field value)
520 "Set FIELD to VALUE in current project. Assumes project exists."
521 ;; same algorithm to find project-plist as ada-xref-current-project
522 (let* ((file-name (ada-xref-current-project-file))
523 (project-plist (cdr (assoc file-name ada-xref-project-files))))
524
525 (setq project-plist (plist-put project-plist field value))
526 (setcdr (assoc file-name ada-xref-project-files) project-plist)))
527
4884c50b
SM
528(defun ada-xref-update-project-menu ()
529 "Update the menu Ada->Project, with the list of available project files."
96a483d9
SM
530 ;; Create the standard items.
531 (let ((submenu
532 `("Project"
533 ["Load..." ada-set-default-project-file t]
534 ["New..." ada-prj-new t]
535 ["Edit..." ada-prj-edit t]
536 "---"
d5875b25 537 ;; Add the project files
96a483d9
SM
538 ,@(mapcar
539 (lambda (x)
d5875b25
JB
540 (let* ((name (or (car x) "<default>"))
541 (command `(lambda ()
542 "Select the current project file."
543 (interactive)
544 (ada-select-prj-file ,name))))
96a483d9 545 (vector
d5875b25 546 (file-name-nondirectory name)
96a483d9
SM
547 command
548 :button (cons
549 :toggle
550 (equal ada-prj-default-project-file
551 (car x))
552 ))))
75497862 553
96a483d9 554 (or ada-xref-project-files '(nil))))))
75497862 555
96a483d9 556 (easy-menu-add-item ada-mode-menu '() submenu)))
4884c50b
SM
557
558
559;;-------------------------------------------------------------
560;;-- Searching a file anywhere on the source path.
561;;--
562;;-- The following functions provide support for finding a file anywhere
563;;-- on the source path, without providing an explicit directory.
564;;-- They also provide file name completion in the minibuffer.
565;;--
566;;-- Public subprograms: ada-find-file
567;;--
568;;-------------------------------------------------------------
569
570(defun ada-do-file-completion (string predicate flag)
571 "Completion function when reading a file from the minibuffer.
32b1a27f
JB
572Completion is attempted in all the directories in the source path,
573as defined in the project file."
417451fe 574 ;; FIXME: doc arguments
f020b5c9
JB
575
576 ;; This function is not itself interactive, but it is called as part
577 ;; of the prompt of interactive functions, so we require a project
578 ;; file.
579 (ada-require-project-file)
4884c50b
SM
580 (let (list
581 (dirs (ada-xref-get-src-dir-field)))
582
583 (while dirs
584 (if (file-directory-p (car dirs))
585 (set 'list (append list (file-name-all-completions string (car dirs)))))
586 (set 'dirs (cdr dirs)))
587 (cond ((equal flag 'lambda)
588 (assoc string list))
589 (flag
590 list)
591 (t
592 (try-completion string
593 (mapcar (lambda (x) (cons x 1)) list)
594 predicate)))))
595
596;;;###autoload
597(defun ada-find-file (filename)
417451fe 598 "Open FILENAME, from anywhere in the source path.
4884c50b
SM
599Completion is available."
600 (interactive
601 (list (completing-read "File: " 'ada-do-file-completion)))
602 (let ((file (ada-find-src-file-in-dir filename)))
603 (if file
604 (find-file file)
a867ead0 605 (error "%s not found in src_dir" filename))))
4884c50b 606
797aab3c 607
797aab3c
GM
608;; ----- Utilities -------------------------------------------------
609
610(defun ada-require-project-file ()
85187d83
JB
611 "If the current project does not exist, load or create a default one.
612Should only be called from interactive functions."
d5875b25
JB
613 (if (string= "" ada-prj-default-project-file)
614 (ada-reread-prj-file (ada-prj-find-prj-file t))))
a1506d29 615
d5875b25
JB
616(defun ada-xref-current-project-file ()
617 "Return the current project file name; never nil.
618Call `ada-require-project-file' first if a project must exist."
85187d83
JB
619 (if (not (string= "" ada-prj-default-project-file))
620 ada-prj-default-project-file
d5875b25 621 (ada-prj-find-prj-file t)))
85187d83 622
d5875b25
JB
623(defun ada-xref-current-project ()
624 "Return the current project.
625Call `ada-require-project-file' first to ensure a project exists."
32b1a27f 626 (let ((file-name (ada-xref-current-project-file)))
85187d83
JB
627 (assoc file-name ada-xref-project-files)))
628
629(defun ada-show-current-project ()
630 "Display current project file name in message buffer."
631 (interactive)
632 (message (ada-xref-current-project-file)))
633
634(defun ada-show-current-main ()
d5875b25 635 "Display current main file name in message buffer."
85187d83 636 (interactive)
d5875b25 637 (message "ada-mode main: %s" (ada-xref-get-project-field 'main)))
85187d83 638
797aab3c 639(defun ada-xref-push-pos (filename position)
eec3232e 640 "Push (FILENAME, POSITION) on the position ring for cross-references."
797aab3c
GM
641 (setq ada-xref-pos-ring (cons (list position filename) ada-xref-pos-ring))
642 (if (> (length ada-xref-pos-ring) ada-xref-pos-ring-max)
643 (setcdr (nthcdr (1- ada-xref-pos-ring-max) ada-xref-pos-ring) nil)))
644
645(defun ada-xref-goto-previous-reference ()
eec3232e 646 "Go to the previous cross-reference we were on."
797aab3c
GM
647 (interactive)
648 (if ada-xref-pos-ring
eec3232e
GM
649 (let ((pos (car ada-xref-pos-ring)))
650 (setq ada-xref-pos-ring (cdr ada-xref-pos-ring))
651 (find-file (car (cdr pos)))
652 (goto-char (car pos)))))
797aab3c
GM
653
654(defun ada-convert-file-name (name)
417451fe 655 "Convert from NAME to a name that can be used by the compilation commands.
a8b6a3a6 656This is overridden on VMS to convert from VMS filenames to Unix filenames."
797aab3c 657 name)
417451fe 658;; FIXME: use convert-standard-filename instead
797aab3c 659
d5875b25
JB
660(defun ada-set-default-project-file (file)
661 "Set FILE as the current project file."
eec3232e 662 (interactive "fProject file:")
d5875b25
JB
663 (ada-parse-prj-file file)
664 (ada-select-prj-file file))
15ea3b67
GM
665
666;; ------ Handling the project file -----------------------------
797aab3c 667
d5875b25
JB
668(defun ada-prj-find-prj-file (&optional no-user-question)
669 "Find the project file associated with the current buffer.
85187d83
JB
670If the buffer is not in Ada mode, or not associated with a file,
671return `ada-prj-default-project-file'. Otherwise, search for a file with
672the same base name as the Ada file, but extension given by
673`ada-prj-file-extension' (default .adp). If not found, search for *.adp
674in the current directory; if several are found, and NO-USER-QUESTION
675is non-nil, prompt the user to select one. If none are found, return
676'default.adp'."
797aab3c 677
15ea3b67 678 (let (selected)
797aab3c 679
3da360a7 680 (if (not (and (derived-mode-p 'ada-mode)
d5875b25 681 buffer-file-name))
c94ca9e0 682
85187d83
JB
683 ;; Not in an Ada buffer, or current buffer not associated
684 ;; with a file (for instance an emerge buffer)
d5875b25 685 (setq selected nil)
a1506d29 686
4884c50b 687 ;; other cases: use a more complex algorithm
a1506d29 688
d5875b25 689 (let* ((current-file (buffer-file-name))
4884c50b
SM
690 (first-choice (concat
691 (file-name-sans-extension current-file)
0262d5e1 692 ada-prj-file-extension))
4884c50b 693 (dir (file-name-directory current-file))
a1506d29 694
4884c50b
SM
695 (prj-files (directory-files
696 dir t
697 (concat ".*" (regexp-quote
0262d5e1 698 ada-prj-file-extension) "$")))
4884c50b 699 (choice nil))
a1506d29 700
4884c50b 701 (cond
a1506d29 702
4884c50b 703 ((file-exists-p first-choice)
85187d83 704 ;; filename.adp
4884c50b 705 (set 'selected first-choice))
a1506d29 706
4884c50b 707 ((= (length prj-files) 1)
85187d83 708 ;; Exactly one project file was found in the current directory
4884c50b 709 (set 'selected (car prj-files)))
a1506d29 710
4884c50b 711 ((and (> (length prj-files) 1) (not no-user-question))
85187d83 712 ;; multiple project files in current directory, ask the user
4884c50b
SM
713 (save-window-excursion
714 (with-output-to-temp-buffer "*choice list*"
715 (princ "There are more than one possible project file.\n")
716 (princ "Which one should we use ?\n\n")
717 (princ " no. file name \n")
718 (princ " --- ------------------------\n")
719 (let ((counter 1))
720 (while (<= counter (length prj-files))
721 (princ (format " %2d) %s\n"
722 counter
723 (nth (1- counter) prj-files)))
724 (setq counter (1+ counter))
c94ca9e0 725
4884c50b
SM
726 ))) ; end of with-output-to ...
727 (setq choice nil)
728 (while (or
729 (not choice)
730 (not (integerp choice))
731 (< choice 1)
732 (> choice (length prj-files)))
027a4b6b 733 (setq choice (string-to-number
4884c50b
SM
734 (read-from-minibuffer "Enter No. of your choice: "))))
735 (set 'selected (nth (1- choice) prj-files))))
a1506d29 736
4884c50b 737 ((= (length prj-files) 0)
85187d83 738 ;; No project file in the current directory; ask user
4884c50b
SM
739 (unless (or no-user-question (not ada-always-ask-project))
740 (setq ada-last-prj-file
741 (read-file-name
742 (concat "project file [" ada-last-prj-file "]:")
743 nil ada-last-prj-file))
744 (unless (string= ada-last-prj-file "")
745 (set 'selected ada-last-prj-file))))
746 )))
c94ca9e0
JB
747
748 (or selected "default.adp")
797aab3c
GM
749 ))
750
d5875b25
JB
751(defun ada-default-prj-properties ()
752 "Return the default project properties list with the current buffer as main."
753
754 (let ((file (buffer-file-name nil)))
755 (list
756 ;; variable name alphabetical order
8794c483 757 'ada_project_path (or (getenv "ADA_PROJECT_PATH") "")
d5875b25
JB
758 'ada_project_path_sep ada-prj-ada-project-path-sep
759 'bind_opt ada-prj-default-bind-opt
760 'build_dir default-directory
761 'casing (if (listp ada-case-exception-file)
762 ada-case-exception-file
763 (list ada-case-exception-file))
764 'check_cmd (list ada-prj-default-check-cmd) ;; FIXME: should not a list
765 'comp_cmd (list ada-prj-default-comp-cmd) ;; FIXME: should not a list
766 'comp_opt ada-prj-default-comp-opt
767 'cross_prefix ""
768 'debug_cmd (concat ada-prj-default-debugger
72bc50c0 769 " ${main}" (if ada-on-ms-windows ".exe")) ;; FIXME: don't need .exe?
d5875b25
JB
770 'debug_post_cmd (list nil)
771 'debug_pre_cmd (list (concat ada-cd-command " ${build_dir}"))
772 'gnatmake_opt ada-prj-default-gnatmake-opt
773 'gnatfind_opt ada-prj-gnatfind-switches
774 'gpr_file ada-prj-default-gpr-file
775 'link_opt ada-prj-default-link-opt
776 'main (if file
777 (file-name-nondirectory
778 (file-name-sans-extension file))
779 "")
780 'make_cmd (list ada-prj-default-make-cmd) ;; FIXME: should not a list
781 'obj_dir (list ".")
782 'remote_machine ""
72bc50c0 783 'run_cmd (list (concat "./${main}" (if ada-on-ms-windows ".exe")))
d5875b25
JB
784 ;; FIXME: should not a list
785 ;; FIXME: don't need .exe?
786 'src_dir (list ".")
787 )))
797aab3c 788
15ea3b67 789(defun ada-parse-prj-file (prj-file)
d5875b25
JB
790 "Read PRJ-FILE, set project properties in `ada-xref-project-files'."
791 (let ((project (ada-default-prj-properties)))
c94ca9e0 792
d5875b25
JB
793 (setq prj-file (expand-file-name prj-file))
794 (if (string= (file-name-extension prj-file) "gpr")
795 (set 'project (ada-gnat-parse-gpr project prj-file))
796
797 (set 'project (ada-parse-prj-file-1 prj-file project))
798 )
c94ca9e0 799
d5875b25
JB
800 ;; Store the project properties
801 (if (assoc prj-file ada-xref-project-files)
802 (setcdr (assoc prj-file ada-xref-project-files) project)
803 (add-to-list 'ada-xref-project-files (cons prj-file project)))
4884c50b 804
d5875b25
JB
805 (ada-xref-update-project-menu)
806 ))
a1506d29 807
d5875b25
JB
808(defun ada-parse-prj-file-1 (prj-file project)
809 "Parse the Ada mode project file PRJ-FILE, set project properties in PROJECT.
810Return new value of PROJECT."
811 (let ((ada-buffer (current-buffer))
812 ;; fields that are lists or otherwise require special processing
813 ada_project_path casing comp_cmd check_cmd
814 debug_pre_cmd debug_post_cmd gpr_file make_cmd obj_dir src_dir run_cmd)
815
816 ;; Give users a chance to use compiler-specific project file formats
817 (let ((buffer (run-hook-with-args-until-success
818 'ada-load-project-hook prj-file)))
819 (unless buffer
820 ;; we load the project file with no warnings; if it does not
821 ;; exist, we stay in the Ada buffer; no project variable
822 ;; settings will be found. That works for the default
823 ;; "default.adp", which does not exist as a file.
824 (setq buffer (find-file-noselect prj-file nil)))
825 (set-buffer buffer))
4884c50b 826
d5875b25
JB
827 (widen)
828 (goto-char (point-min))
4884c50b 829
d5875b25
JB
830 ;; process each line
831 (while (not (eobp))
a1506d29 832
d5875b25
JB
833 ;; ignore lines that don't have the format "name=value", put
834 ;; 'name', 'value' in match-string.
835 (if (looking-at "^\\([^=\n]+\\)=\\(.*\\)")
836 (cond
837 ;; FIXME: strip trailing spaces
838 ;; variable name alphabetical order
839 ((string= (match-string 1) "ada_project_path")
840 (add-to-list 'ada_project_path
841 (expand-file-name
842 (substitute-in-file-name (match-string 2)))))
a1506d29 843
d5875b25
JB
844 ((string= (match-string 1) "build_dir")
845 (set 'project
846 (plist-put project 'build_dir
847 (file-name-as-directory (match-string 2)))))
4884c50b 848
d5875b25
JB
849 ((string= (match-string 1) "casing")
850 (add-to-list 'casing
851 (expand-file-name (substitute-in-file-name (match-string 2)))))
852
853 ((string= (match-string 1) "check_cmd")
854 (add-to-list 'check_cmd (match-string 2)))
855
856 ((string= (match-string 1) "comp_cmd")
857 (add-to-list 'comp_cmd (match-string 2)))
858
859 ((string= (match-string 1) "debug_post_cmd")
860 (add-to-list 'debug_post_cmd (match-string 2)))
861
862 ((string= (match-string 1) "debug_pre_cmd")
863 (add-to-list 'debug_pre_cmd (match-string 2)))
864
865 ((string= (match-string 1) "gpr_file")
866 ;; expand now; path is relative to Emacs project file
867 (setq gpr_file (expand-file-name (match-string 2))))
868
869 ((string= (match-string 1) "make_cmd")
870 (add-to-list 'make_cmd (match-string 2)))
871
872 ((string= (match-string 1) "obj_dir")
873 (add-to-list 'obj_dir
874 (file-name-as-directory
875 (expand-file-name (match-string 2)))))
876
877 ((string= (match-string 1) "run_cmd")
878 (add-to-list 'run_cmd (match-string 2)))
879
880 ((string= (match-string 1) "src_dir")
881 (add-to-list 'src_dir
882 (file-name-as-directory
883 (expand-file-name (match-string 2)))))
884
885 (t
886 ;; any other field in the file is just copied
887 (set 'project (plist-put project
888 (intern (match-string 1))
889 (match-string 2))))))
890
891 (forward-line 1))
892
893 ;; done reading file
894
895 ;; back to the user buffer
896 (set-buffer ada-buffer)
897
898 ;; process accumulated lists
899 (if ada_project_path
900 (let ((sep (plist-get project 'ada_project_path_sep)))
901 (setq ada_project_path (reverse ada_project_path))
902 (setq ada_project_path (mapconcat 'identity ada_project_path sep))
903 (set 'project (plist-put project 'ada_project_path ada_project_path))
904 ;; env var needed now for ada-gnat-parse-gpr
905 (setenv "ADA_PROJECT_PATH" ada_project_path)))
906
907 (if debug_post_cmd (set 'project (plist-put project 'debug_post_cmd (reverse debug_post_cmd))))
908 (if debug_pre_cmd (set 'project (plist-put project 'debug_pre_cmd (reverse debug_pre_cmd))))
909 (if casing (set 'project (plist-put project 'casing (reverse casing))))
910 (if check_cmd (set 'project (plist-put project 'check_cmd (reverse check_cmd))))
911 (if comp_cmd (set 'project (plist-put project 'comp_cmd (reverse comp_cmd))))
912 (if make_cmd (set 'project (plist-put project 'make_cmd (reverse make_cmd))))
913 (if run_cmd (set 'project (plist-put project 'run_cmd (reverse run_cmd))))
914
915 (if gpr_file
916 (progn
917 (set 'project (ada-gnat-parse-gpr project gpr_file))
918 ;; append Ada source and object directories to others from Emacs project file
919 (setq src_dir (append (plist-get project 'src_dir) src_dir))
920 (setq obj_dir (append (plist-get project 'obj_dir) obj_dir))
921 (setq ada-xref-runtime-library-specs-path '()
922 ada-xref-runtime-library-ali-path '()))
923 )
924
925 ;; FIXME: gnatpath.exe doesn't output the runtime libraries, so always call ada-initialize-runtime-library
926 ;; if using a gpr_file, the runtime library directories are
927 ;; included in src_dir and obj_dir; otherwise they are in the
928 ;; 'runtime-library' variables.
929 ;; FIXME: always append to src_dir, obj_dir
930 (ada-initialize-runtime-library (or (ada-xref-get-project-field 'cross_prefix) ""))
931 ;;)
932
933 (if obj_dir (set 'project (plist-put project 'obj_dir (reverse obj_dir))))
934 (if src_dir (set 'project (plist-put project 'src_dir (reverse src_dir))))
935
936 project
15ea3b67 937 ))
a1506d29 938
d5875b25
JB
939(defun ada-select-prj-file (file)
940 "Select FILE as the current project file."
941 (interactive)
942 (setq ada-prj-default-project-file (expand-file-name file))
943
944 (let ((casing (ada-xref-get-project-field 'casing)))
945 (if casing
946 (progn
947 ;; FIXME: use ada-get-absolute-dir here
948 (setq ada-case-exception-file casing)
949 (ada-case-read-exceptions))))
950
951 (let ((ada_project_path (ada-xref-get-project-field 'ada_project_path)))
952 (if ada_project_path
953 ;; FIXME: use ada-get-absolute-dir, mapconcat here
954 (setenv "ADA_PROJECT_PATH" ada_project_path)))
955
956 (setq compilation-search-path (ada-xref-get-src-dir-field))
957
958 (setq ada-search-directories-internal
959 ;; FIXME: why do we need directory-file-name here?
960 (append (mapcar 'directory-file-name compilation-search-path)
961 ada-search-directories))
962
963 ;; return 't', for decent display in message buffer when called interactively
964 t)
a1506d29 965
93cdce20 966(defun ada-find-references (&optional pos arg local-only)
eec3232e 967 "Find all references to the entity under POS.
93cdce20 968Calls gnatfind to find the references.
32b1a27f
JB
969If ARG is non-nil, the contents of the old *gnatfind* buffer is preserved.
970If LOCAL-ONLY is non-nil, only declarations in the current file are returned."
ced16f5d 971 (interactive "d\nP")
797aab3c
GM
972 (ada-require-project-file)
973
974 (let* ((identlist (ada-read-identifier pos))
417451fe 975 (alifile (ada-get-ali-file-name (ada-file-of identlist)))
15ea3b67 976 (process-environment (ada-set-environment)))
797aab3c
GM
977
978 (set-buffer (get-file-buffer (ada-file-of identlist)))
979
980 ;; if the file is more recent than the executable
981 (if (or (buffer-modified-p (current-buffer))
417451fe
JB
982 (file-newer-than-file-p (ada-file-of identlist) alifile))
983 (ada-find-any-references (ada-name-of identlist)
984 (ada-file-of identlist)
985 nil nil local-only arg)
797aab3c 986 (ada-find-any-references (ada-name-of identlist)
417451fe
JB
987 (ada-file-of identlist)
988 (ada-line-of identlist)
989 (ada-column-of identlist) local-only arg)))
797aab3c
GM
990 )
991
93cdce20
SM
992(defun ada-find-local-references (&optional pos arg)
993 "Find all references to the entity under POS.
ced16f5d 994Calls `gnatfind' to find the references.
32b1a27f 995If ARG is non-nil, the contents of the old *gnatfind* buffer is preserved."
ced16f5d 996 (interactive "d\nP")
93cdce20
SM
997 (ada-find-references pos arg t))
998
f020b5c9
JB
999(defconst ada-gnatfind-buffer-name "*gnatfind*")
1000
93cdce20
SM
1001(defun ada-find-any-references
1002 (entity &optional file line column local-only append)
eec3232e 1003 "Search for references to any entity whose name is ENTITY.
93cdce20 1004ENTITY was first found the location given by FILE, LINE and COLUMN.
32b1a27f
JB
1005If LOCAL-ONLY is non-nil, then list only the references in FILE,
1006which is much faster.
1007If APPEND is non-nil, then append the output of the command to the
1008existing buffer `*gnatfind*', if there is one."
797aab3c
GM
1009 (interactive "sEntity name: ")
1010 (ada-require-project-file)
1011
ced16f5d 1012 ;; Prepare the gnatfind command. Note that we must protect the quotes
4884c50b
SM
1013 ;; around operators, so that they are correctly handled and can be
1014 ;; processed (gnatfind \"+\":...).
1015 (let* ((quote-entity
1016 (if (= (aref entity 0) ?\")
72bc50c0 1017 (if ada-on-ms-windows
4884c50b
SM
1018 (concat "\\\"" (substring entity 1 -1) "\\\"")
1019 (concat "'\"" (substring entity 1 -1) "\"'"))
1020 entity))
1021 (switches (ada-xref-get-project-field 'gnatfind_opt))
d5875b25
JB
1022 ;; FIXME: use gpr_file
1023 (cross-prefix (ada-xref-get-project-field 'cross_prefix))
1024 (command (concat cross-prefix "gnat find " switches " "
4884c50b 1025 quote-entity
417451fe
JB
1026 (if file (concat ":" (file-name-nondirectory file)))
1027 (if line (concat ":" line))
1028 (if column (concat ":" column))
93cdce20
SM
1029 (if local-only (concat " " (file-name-nondirectory file)))
1030 ))
1031 old-contents)
797aab3c
GM
1032
1033 ;; If a project file is defined, use it
4884c50b
SM
1034 (if (and ada-prj-default-project-file
1035 (not (string= ada-prj-default-project-file "")))
417451fe
JB
1036 (if (string-equal (file-name-extension ada-prj-default-project-file)
1037 "gpr")
d5875b25
JB
1038 (setq command (concat command " -P\"" ada-prj-default-project-file "\""))
1039 (setq command (concat command " -p\"" ada-prj-default-project-file "\""))))
797aab3c 1040
f020b5c9 1041 (if (and append (get-buffer ada-gnatfind-buffer-name))
9a529312 1042 (with-current-buffer "*gnatfind*"
93cdce20 1043 (setq old-contents (buffer-string))))
a1506d29 1044
ced16f5d 1045 (let ((compilation-error "reference"))
f020b5c9 1046 (compilation-start command 'compilation-mode (lambda (mode) ada-gnatfind-buffer-name)))
797aab3c
GM
1047
1048 ;; Hide the "Compilation" menu
9a529312 1049 (with-current-buffer ada-gnatfind-buffer-name
93cdce20
SM
1050 (local-unset-key [menu-bar compilation-menu])
1051
1052 (if old-contents
1053 (progn
1054 (goto-char 1)
f020b5c9 1055 (set 'buffer-read-only nil)
93cdce20 1056 (insert old-contents)
f020b5c9 1057 (set 'buffer-read-only t)
93cdce20 1058 (goto-char (point-max)))))
797aab3c
GM
1059 )
1060 )
1061
4884c50b 1062(defalias 'ada-change-prj (symbol-function 'ada-set-default-project-file))
797aab3c 1063
797aab3c
GM
1064;; ----- Identifier Completion --------------------------------------------
1065(defun ada-complete-identifier (pos)
417451fe 1066 "Try to complete the identifier around POS, using compiler cross-reference information."
797aab3c
GM
1067 (interactive "d")
1068 (ada-require-project-file)
1069
15ea3b67 1070 ;; Initialize function-local variables and jump to the .ali buffer
797aab3c
GM
1071 ;; Note that for regexp search is case insensitive too
1072 (let* ((curbuf (current-buffer))
417451fe
JB
1073 (identlist (ada-read-identifier pos))
1074 (sofar (concat "^[0-9]+[a-zA-Z][0-9]+[ *]\\("
1075 (regexp-quote (ada-name-of identlist))
1076 "[a-zA-Z0-9_]*\\)"))
1077 (completed nil)
1078 (symalist nil))
797aab3c 1079
15ea3b67
GM
1080 ;; Open the .ali file
1081 (set-buffer (ada-get-ali-buffer (buffer-file-name)))
797aab3c
GM
1082 (goto-char (point-max))
1083
1084 ;; build an alist of possible completions
1085 (while (re-search-backward sofar nil t)
1086 (setq symalist (cons (cons (match-string 1) nil) symalist)))
1087
1088 (setq completed (try-completion "" symalist))
1089
1090 ;; kills .ali buffer
1091 (kill-buffer nil)
1092
1093 ;; deletes the incomplete identifier in the buffer
1094 (set-buffer curbuf)
1095 (looking-at "[a-zA-Z0-9_]+")
1096 (replace-match "")
1097 ;; inserts the completed symbol
1098 (insert completed)
1099 ))
1100
1101;; ----- Cross-referencing ----------------------------------------
1102
1103(defun ada-point-and-xref ()
c94ca9e0 1104 "Jump to the declaration of the entity below the cursor."
797aab3c
GM
1105 (interactive)
1106 (mouse-set-point last-input-event)
1107 (ada-goto-declaration (point)))
1108
c94ca9e0
JB
1109(defun ada-point-and-xref-body ()
1110 "Jump to the body of the entity under the cursor."
1111 (interactive)
1112 (mouse-set-point last-input-event)
1113 (ada-goto-body (point)))
1114
1115(defun ada-goto-body (pos &optional other-frame)
1116 "Display the body of the entity around POS.
417451fe 1117OTHER-FRAME non-nil means display in another frame.
c94ca9e0
JB
1118If the entity doesn't have a body, display its declaration.
1119As a side effect, the buffer for the declaration is also open."
1120 (interactive "d")
1121 (ada-goto-declaration pos other-frame)
1122
1123 ;; Temporarily force the display in the same buffer, since we
1124 ;; already changed previously
1125 (let ((ada-xref-other-buffer nil))
1126 (ada-goto-declaration (point) nil)))
1127
4884c50b 1128(defun ada-goto-declaration (pos &optional other-frame)
eec3232e
GM
1129 "Display the declaration of the identifier around POS.
1130The declaration is shown in another buffer if `ada-xref-other-buffer' is
4884c50b
SM
1131non-nil.
1132If OTHER-FRAME is non-nil, display the cross-reference in another frame."
797aab3c
GM
1133 (interactive "d")
1134 (ada-require-project-file)
1135 (push-mark pos)
1136 (ada-xref-push-pos (buffer-file-name) pos)
797aab3c 1137
4884c50b
SM
1138 ;; First try the standard algorithm by looking into the .ali file, but if
1139 ;; that file was too old or even did not exist, try to look in the whole
1140 ;; object path for a possible location.
1141 (let ((identlist (ada-read-identifier pos)))
0262d5e1 1142 (condition-case err
4884c50b 1143 (ada-find-in-ali identlist other-frame)
0262d5e1
JB
1144 ;; File not found: print explicit error message
1145 (error-file-not-found
1146 (message (concat (error-message-string err)
1147 (nthcdr 1 err))))
1148
93cdce20
SM
1149 (error
1150 (let ((ali-file (ada-get-ali-file-name (ada-file-of identlist))))
1151
1152 ;; If the ALI file was up-to-date, then we probably have a predefined
1153 ;; entity, whose references are not given by GNAT
1154 (if (and (file-exists-p ali-file)
1155 (file-newer-than-file-p ali-file (ada-file-of identlist)))
417451fe 1156 (message "No cross-reference found -- may be a predefined entity.")
93cdce20
SM
1157
1158 ;; Else, look in every ALI file, except if the user doesn't want that
1159 (if ada-xref-search-with-egrep
1160 (ada-find-in-src-path identlist other-frame)
ced16f5d 1161 (message "Cross-referencing information is not up-to-date; please recompile.")
93cdce20 1162 )))))))
4884c50b 1163
96a483d9 1164(defun ada-goto-declaration-other-frame (pos)
eec3232e
GM
1165 "Display the declaration of the identifier around POS.
1166The declation is shown in another frame if `ada-xref-other-buffer' is non-nil."
797aab3c 1167 (interactive "d")
4884c50b 1168 (ada-goto-declaration pos t))
797aab3c 1169
15ea3b67
GM
1170(defun ada-remote (command)
1171 "Return the remote version of COMMAND, or COMMAND if remote_machine is nil."
1172 (let ((machine (ada-xref-get-project-field 'remote_machine)))
1173 (if (or (not machine) (string= machine ""))
1174 command
1175 (format "%s %s '(%s)'"
1176 remote-shell-program
1177 machine
1178 command))))
1179
15ea3b67 1180(defun ada-get-absolute-dir-list (dir-list root-dir)
417451fe 1181 "Return the list of absolute directories found in DIR-LIST.
d5875b25
JB
1182If a directory is a relative directory, ROOT-DIR is prepended.
1183Project and environment variables are substituted."
1184 (mapcar (lambda (x) (expand-file-name x (ada-treat-cmd-string root-dir))) dir-list))
15ea3b67
GM
1185
1186(defun ada-set-environment ()
ced16f5d
RS
1187 "Prepare an environment for Ada compilation.
1188This returns a new value to use for `process-environment',
1189but does not actually put it into use.
15ea3b67
GM
1190It modifies the source path and object path with the values found in the
1191project file."
1192 (let ((include (getenv "ADA_INCLUDE_PATH"))
1193 (objects (getenv "ADA_OBJECTS_PATH"))
1194 (build-dir (ada-xref-get-project-field 'build_dir)))
1195 (if include
4884c50b 1196 (set 'include (concat path-separator include)))
15ea3b67 1197 (if objects
4884c50b 1198 (set 'objects (concat path-separator objects)))
15ea3b67
GM
1199 (cons
1200 (concat "ADA_INCLUDE_PATH="
4884c50b 1201 (mapconcat (lambda(x) (expand-file-name x build-dir))
15ea3b67 1202 (ada-xref-get-project-field 'src_dir)
4884c50b
SM
1203 path-separator)
1204 include)
15ea3b67
GM
1205 (cons
1206 (concat "ADA_OBJECTS_PATH="
4884c50b 1207 (mapconcat (lambda(x) (expand-file-name x build-dir))
15ea3b67 1208 (ada-xref-get-project-field 'obj_dir)
4884c50b
SM
1209 path-separator)
1210 objects)
15ea3b67
GM
1211 process-environment))))
1212
1213(defun ada-compile-application (&optional arg)
ced16f5d 1214 "Compile the application, using the command found in the project file.
15ea3b67
GM
1215If ARG is not nil, ask for user confirmation."
1216 (interactive "P")
797aab3c 1217 (ada-require-project-file)
15ea3b67
GM
1218 (let ((cmd (ada-xref-get-project-field 'make_cmd))
1219 (process-environment (ada-set-environment))
1220 (compilation-scroll-output t))
1221
4884c50b 1222 (setq compilation-search-path (ada-xref-get-src-dir-field))
15ea3b67
GM
1223
1224 ;; If no project file was found, ask the user
1225 (unless cmd
4884c50b 1226 (setq cmd '("") arg t))
15ea3b67 1227
4884c50b
SM
1228 ;; Make a single command from the list of commands, including the
1229 ;; commands to run it on a remote machine.
1230 (setq cmd (ada-remote (mapconcat 'identity cmd ada-command-separator)))
a1506d29 1231
4884c50b
SM
1232 (if (or ada-xref-confirm-compile arg)
1233 (setq cmd (read-from-minibuffer "enter command to compile: " cmd)))
1234
1235 ;; Insert newlines so as to separate the name of the commands to run
ced16f5d 1236 ;; and the output of the commands. This doesn't work with cmdproxy.exe,
4884c50b 1237 ;; which gets confused by newline characters.
c94ca9e0 1238 (if (not (string-match ".exe" shell-file-name))
4884c50b 1239 (setq cmd (concat cmd "\n\n")))
a1506d29 1240
4884c50b 1241 (compile (ada-quote-cmd cmd))))
797aab3c 1242
85187d83 1243(defun ada-set-main-compile-application ()
d5875b25 1244 "Set main project variable to current buffer, build main."
85187d83
JB
1245 (interactive)
1246 (ada-require-project-file)
1247 (let* ((file (buffer-file-name (current-buffer)))
1248 main)
1249 (if (not file)
1250 (error "No file for current buffer")
1251
1252 (setq main
1253 (if file
1254 (file-name-nondirectory
1255 (file-name-sans-extension file))
1256 ""))
1257 (ada-xref-set-project-field 'main main)
85187d83
JB
1258 (ada-compile-application))))
1259
15ea3b67
GM
1260(defun ada-compile-current (&optional arg prj-field)
1261 "Recompile the current file.
32b1a27f 1262If ARG is non-nil, ask for user confirmation of the command.
15ea3b67 1263PRJ-FIELD is the name of the field to use in the project file to get the
7977c946 1264command, and should be either `comp_cmd' (default) or `check_cmd'."
15ea3b67 1265 (interactive "P")
797aab3c 1266 (ada-require-project-file)
15ea3b67
GM
1267 (let* ((field (if prj-field prj-field 'comp_cmd))
1268 (cmd (ada-xref-get-project-field field))
1269 (process-environment (ada-set-environment))
1270 (compilation-scroll-output t))
a1506d29 1271
4884c50b
SM
1272 (unless cmd
1273 (setq cmd '("") arg t))
a1506d29 1274
4884c50b
SM
1275 ;; Make a single command from the list of commands, including the
1276 ;; commands to run it on a remote machine.
1277 (setq cmd (ada-remote (mapconcat 'identity cmd ada-command-separator)))
a1506d29 1278
15ea3b67 1279 ;; If no project file was found, ask the user
4884c50b
SM
1280 (if (or ada-xref-confirm-compile arg)
1281 (setq cmd (read-from-minibuffer "enter command to compile: " cmd)))
1282
4884c50b 1283 (compile (ada-quote-cmd cmd))))
15ea3b67
GM
1284
1285(defun ada-check-current (&optional arg)
ab074cb1 1286 "Check the current file for syntax errors.
32b1a27f 1287If ARG is non-nil, ask for user confirmation of the command."
15ea3b67
GM
1288 (interactive "P")
1289 (ada-compile-current arg 'check_cmd))
797aab3c 1290
15ea3b67
GM
1291(defun ada-run-application (&optional arg)
1292 "Run the application.
32b1a27f 1293If ARG is non-nil, ask for user confirmation."
797aab3c
GM
1294 (interactive)
1295 (ada-require-project-file)
1296
15ea3b67
GM
1297 (let ((machine (ada-xref-get-project-field 'cross_prefix)))
1298 (if (and machine (not (string= machine "")))
1299 (error "This feature is not supported yet for cross environments")))
797aab3c 1300
15ea3b67 1301 (let ((command (ada-xref-get-project-field 'run_cmd)))
797aab3c 1302
15ea3b67 1303 ;; Guess the command if it wasn't specified
4884c50b 1304 (if (not command)
417451fe 1305 (set 'command (list (file-name-sans-extension (buffer-name)))))
797aab3c 1306
4884c50b
SM
1307 ;; Modify the command to run remotely
1308 (setq command (ada-remote (mapconcat 'identity command
1309 ada-command-separator)))
a1506d29 1310
15ea3b67
GM
1311 ;; Ask for the arguments to the command if required
1312 (if (or ada-xref-confirm-compile arg)
4884c50b
SM
1313 (setq command (read-from-minibuffer "Enter command to execute: "
1314 command)))
797aab3c
GM
1315
1316 ;; Run the command
9a529312 1317 (with-current-buffer (get-buffer-create "*run*")
15ea3b67 1318 (set 'buffer-read-only nil)
4884c50b 1319
15ea3b67 1320 (erase-buffer)
4884c50b
SM
1321 (start-process "run" (current-buffer) shell-file-name
1322 "-c" command)
1323 (comint-mode)
1324 ;; Set these two variables to their default values, since otherwise
1325 ;; the output buffer is scrolled so that only the last output line
1326 ;; is visible at the top of the buffer.
1327 (set (make-local-variable 'scroll-step) 0)
1328 (set (make-local-variable 'scroll-conservatively) 0)
797aab3c
GM
1329 )
1330 (display-buffer "*run*")
1331
1332 ;; change to buffer *run* for interactive programs
1333 (other-window 1)
1334 (switch-to-buffer "*run*")
15ea3b67 1335 ))
797aab3c 1336
4884c50b 1337(defun ada-gdb-application (&optional arg executable-name)
15ea3b67 1338 "Start the debugger on the application.
417451fe 1339If ARG is non-nil, ask the user to confirm the command.
4884c50b 1340EXECUTABLE-NAME, if non-nil, is debugged instead of the file specified in the
417451fe 1341project file."
15ea3b67 1342 (interactive "P")
85187d83 1343 (ada-require-project-file)
797aab3c 1344 (let ((buffer (current-buffer))
4884c50b 1345 cmd pre-cmd post-cmd)
4884c50b
SM
1346 (setq cmd (if executable-name
1347 (concat ada-prj-default-debugger " " executable-name)
1348 (ada-xref-get-project-field 'debug_cmd))
1349 pre-cmd (ada-xref-get-project-field 'debug_pre_cmd)
1350 post-cmd (ada-xref-get-project-field 'debug_post_cmd))
15ea3b67
GM
1351
1352 ;; If the command was not given in the project file, start a bare gdb
1353 (if (not cmd)
1354 (set 'cmd (concat ada-prj-default-debugger
1355 " "
4884c50b
SM
1356 (or executable-name
1357 (file-name-sans-extension (buffer-file-name))))))
1358
1359 ;; For gvd, add an extra switch so that the Emacs window is completly
1360 ;; swallowed inside the Gvd one
1361 (if (and ada-tight-gvd-integration
1362 (string-match "^[^ \t]*gvd" cmd))
1363 ;; Start a new frame, so that when gvd exists we do not kill Emacs
1364 ;; We make sure that gvd swallows the new frame, not the one the
1365 ;; user has been using until now
1366 ;; The frame is made invisible initially, so that GtkPlug gets a
ced16f5d 1367 ;; chance to fully manage it. Then it works fine with Enlightenment
4884c50b
SM
1368 ;; as well
1369 (let ((frame (make-frame '((visibility . nil)))))
1370 (set 'cmd (concat
1371 cmd " --editor-window="
1372 (cdr (assoc 'outer-window-id (frame-parameters frame)))))
1373 (select-frame frame)))
1374
1375 ;; Add a -fullname switch
1376 ;; Use the remote machine
1377 (set 'cmd (ada-remote (concat cmd " -fullname ")))
1378
1379 ;; Ask for confirmation if required
15ea3b67
GM
1380 (if (or arg ada-xref-confirm-compile)
1381 (set 'cmd (read-from-minibuffer "enter command to debug: " cmd)))
1382
96a483d9 1383 (let ((old-comint-exec (symbol-function 'comint-exec)))
4884c50b
SM
1384
1385 ;; Do not add -fullname, since we can have a 'rsh' command in front.
96a483d9 1386 ;; FIXME: This is evil but luckily a nop under Emacs-21.3.50 ! -stef
4884c50b
SM
1387 (fset 'gud-gdb-massage-args (lambda (file args) args))
1388
1389 (set 'pre-cmd (mapconcat 'identity pre-cmd ada-command-separator))
1390 (if (not (equal pre-cmd ""))
1391 (setq pre-cmd (concat pre-cmd ada-command-separator)))
1392
1393 (set 'post-cmd (mapconcat 'identity post-cmd "\n"))
1394 (if post-cmd
96a483d9 1395 (set 'post-cmd (concat post-cmd "\n")))
4884c50b 1396
c94ca9e0 1397
4884c50b
SM
1398 ;; Temporarily replaces the definition of `comint-exec' so that we
1399 ;; can execute commands before running gdb.
96a483d9 1400 ;; FIXME: This is evil and not temporary !!! -stef
a1506d29 1401 (fset 'comint-exec
4884c50b
SM
1402 `(lambda (buffer name command startfile switches)
1403 (let (compilation-buffer-name-function)
1404 (save-excursion
1405 (set 'compilation-buffer-name-function
1406 (lambda(x) (buffer-name buffer)))
1407 (compile (ada-quote-cmd
1408 (concat ,pre-cmd
1409 command " "
1410 (mapconcat 'identity switches " "))))))
1411 ))
1412
1413 ;; Tight integration should force the tty mode
1414 (if (and (string-match "gvd" (comint-arguments cmd 0 0))
1415 ada-tight-gvd-integration
1416 (not (string-match "--tty" cmd)))
1417 (setq cmd (concat cmd "--tty")))
a1506d29 1418
4884c50b
SM
1419 (if (and (string-match "jdb" (comint-arguments cmd 0 0))
1420 (boundp 'jdb))
1421 (funcall (symbol-function 'jdb) cmd)
1422 (gdb cmd))
1423
c94ca9e0
JB
1424 ;; Restore the standard fset command (or for instance C-U M-x shell
1425 ;; wouldn't work anymore
1426
1427 (fset 'comint-exec old-comint-exec)
1428
4884c50b
SM
1429 ;; Send post-commands to the debugger
1430 (process-send-string (get-buffer-process (current-buffer)) post-cmd)
1431
1432 ;; Move to the end of the debugger buffer, so that it is automatically
1433 ;; scrolled from then on.
254746ff 1434 (goto-char (point-max))
4884c50b
SM
1435
1436 ;; Display both the source window and the debugger window (the former
ced16f5d 1437 ;; above the latter). No need to show the debugger window unless it
4884c50b
SM
1438 ;; is going to have some relevant information.
1439 (if (or (not (string-match "gvd" (comint-arguments cmd 0 0)))
1440 (string-match "--tty" cmd))
1441 (split-window-vertically))
1442 (switch-to-buffer buffer)
1443 )))
797aab3c 1444
15ea3b67 1445(defun ada-reread-prj-file (&optional filename)
d5875b25
JB
1446 "Reread either the current project, or FILENAME if non-nil.
1447If FILENAME is non-nil, set it as current project."
797aab3c 1448 (interactive "P")
d5875b25
JB
1449 (if (not filename)
1450 (setq filename ada-prj-default-project-file))
1451 (ada-parse-prj-file filename)
1452 (ada-select-prj-file filename))
15ea3b67 1453
797aab3c
GM
1454;; ------ Private routines
1455
1456(defun ada-xref-current (file &optional ali-file-name)
eec3232e 1457 "Update the cross-references for FILE.
4884c50b
SM
1458This in fact recompiles FILE to create ALI-FILE-NAME.
1459This function returns the name of the file that was recompiled to generate
7977c946
JB
1460the cross-reference information. Note that the ali file can then be deduced
1461by replacing the file extension with `.ali'."
797aab3c
GM
1462 ;; kill old buffer
1463 (if (and ali-file-name
417451fe 1464 (get-file-buffer ali-file-name))
797aab3c 1465 (kill-buffer (get-file-buffer ali-file-name)))
a1506d29 1466
4884c50b
SM
1467 (let* ((name (ada-convert-file-name file))
1468 (body-name (or (ada-get-body-name name) name)))
15ea3b67 1469
ced16f5d 1470 ;; Always recompile the body when we can. We thus temporarily switch to a
4884c50b
SM
1471 ;; buffer than contains the body of the unit
1472 (save-excursion
1473 (let ((body-visible (find-buffer-visiting body-name))
1474 process)
1475 (if body-visible
1476 (set-buffer body-visible)
1477 (find-file body-name))
1478
ced16f5d 1479 ;; Execute the compilation. Note that we must wait for the end of the
4884c50b
SM
1480 ;; process, or the ALI file would still not be available.
1481 ;; Unfortunately, the underlying `compile' command that we use is
1482 ;; asynchronous.
1483 (ada-compile-current)
1484 (setq process (get-buffer-process "*compilation*"))
1485
1486 (while (and process
1487 (not (equal (process-status process) 'exit)))
1488 (sit-for 1))
1489
1490 ;; remove the buffer for the body if it wasn't there before
1491 (unless body-visible
1492 (kill-buffer (find-buffer-visiting body-name)))
1493 ))
1494 body-name))
15ea3b67
GM
1495
1496(defun ada-find-file-in-dir (file dir-list)
1497 "Search for FILE in DIR-LIST."
1498 (let (found)
1499 (while (and (not found) dir-list)
1500 (set 'found (concat (file-name-as-directory (car dir-list))
1501 (file-name-nondirectory file)))
a1506d29 1502
15ea3b67
GM
1503 (unless (file-exists-p found)
1504 (set 'found nil))
1505 (set 'dir-list (cdr dir-list)))
1506 found))
797aab3c
GM
1507
1508(defun ada-find-ali-file-in-dir (file)
417451fe 1509 "Find the ali file FILE, searching obj_dir for the current project.
15ea3b67
GM
1510Adds build_dir in front of the search path to conform to gnatmake's behavior,
1511and the standard runtime location at the end."
4884c50b 1512 (ada-find-file-in-dir file (ada-xref-get-obj-dir-field)))
15ea3b67
GM
1513
1514(defun ada-find-src-file-in-dir (file)
417451fe
JB
1515 "Find the source file FILE, searching src_dir for the current project.
1516Adds the standard runtime location at the end of the search path to conform
1517to gnatmake's behavior."
4884c50b 1518 (ada-find-file-in-dir file (ada-xref-get-src-dir-field)))
797aab3c
GM
1519
1520(defun ada-get-ali-file-name (file)
32b1a27f 1521 "Create the ali file name for the Ada file FILE.
eec3232e
GM
1522The file is searched for in every directory shown in the obj_dir lines of
1523the project file."
797aab3c
GM
1524
1525 ;; This function has to handle the special case of non-standard
1526 ;; file names (i.e. not .adb or .ads)
1527 ;; The trick is the following:
1528 ;; 1- replace the extension of the current file with .ali,
1529 ;; and look for this file
1530 ;; 2- If this file is found:
1531 ;; grep the "^U" lines, and make sure we are not reading the
ced16f5d 1532 ;; .ali file for a spec file. If we are, go to step 3.
797aab3c
GM
1533 ;; 3- If the file is not found or step 2 failed:
1534 ;; find the name of the "other file", ie the body, and look
1535 ;; for its associated .ali file by subtituing the extension
4884c50b
SM
1536 ;;
1537 ;; We must also handle the case of separate packages and subprograms:
1538 ;; 4- If no ali file was found, we try to modify the file name by removing
1539 ;; everything after the last '-' or '.' character, so as to get the
ced16f5d 1540 ;; ali file for the parent unit. If we found an ali file, we check that
4884c50b 1541 ;; it indeed contains the definition for the separate entity by checking
ced16f5d 1542 ;; the 'D' lines. This is done repeatedly, in case the direct parent is
4884c50b 1543 ;; also a separate.
797aab3c 1544
9a529312 1545 (with-current-buffer (get-file-buffer file)
797aab3c 1546 (let ((short-ali-file-name
417451fe
JB
1547 (concat (file-name-sans-extension (file-name-nondirectory file))
1548 ".ali"))
1549 ali-file-name
4884c50b
SM
1550 is-spec)
1551
1552 ;; If we have a non-standard file name, and this is a spec, we first
1553 ;; look for the .ali file of the body, since this is the one that
ced16f5d 1554 ;; contains the most complete information. If not found, we will do what
4884c50b
SM
1555 ;; we can with the .ali file for the spec...
1556
1557 (if (not (string= (file-name-extension file) "ads"))
1558 (let ((specs ada-spec-suffixes))
1559 (while specs
1560 (if (string-match (concat (regexp-quote (car specs)) "$")
1561 file)
1562 (set 'is-spec t))
1563 (set 'specs (cdr specs)))))
1564
1565 (if is-spec
1566 (set 'ali-file-name
1567 (ada-find-ali-file-in-dir
1568 (concat (file-name-sans-extension
1569 (file-name-nondirectory
1570 (ada-other-file-name)))
1571 ".ali"))))
a1506d29 1572
4884c50b
SM
1573
1574 (setq ali-file-name
1575 (or ali-file-name
a1506d29 1576
4884c50b
SM
1577 ;; Else we take the .ali file associated with the unit
1578 (ada-find-ali-file-in-dir short-ali-file-name)
a1506d29 1579
4884c50b
SM
1580
1581 ;; else we did not find the .ali file Second chance: in case
1582 ;; the files do not have standard names (such as for instance
1583 ;; file_s.ada and file_b.ada), try to go to the other file
1584 ;; and look for its ali file
1585 (ada-find-ali-file-in-dir
1586 (concat (file-name-sans-extension
1587 (file-name-nondirectory (ada-other-file-name)))
1588 ".ali"))
1589
a1506d29 1590
4884c50b
SM
1591 ;; If we still don't have an ali file, try to get the one
1592 ;; from the parent unit, in case we have a separate entity.
1593 (let ((parent-name (file-name-sans-extension
1594 (file-name-nondirectory file))))
a1506d29 1595
4884c50b
SM
1596 (while (and (not ali-file-name)
1597 (string-match "^\\(.*\\)[.-][^.-]*" parent-name))
a1506d29 1598
4884c50b
SM
1599 (set 'parent-name (match-string 1 parent-name))
1600 (set 'ali-file-name (ada-find-ali-file-in-dir
1601 (concat parent-name ".ali")))
1602 )
1603 ali-file-name)))
a1506d29 1604
4884c50b
SM
1605 ;; If still not found, try to recompile the file
1606 (if (not ali-file-name)
ced16f5d
RS
1607 ;; Recompile only if the user asked for this, and search the ali
1608 ;; filename again. We avoid a possible infinite recursion by
4884c50b 1609 ;; temporarily disabling the automatic compilation.
a1506d29 1610
4884c50b
SM
1611 (if ada-xref-create-ali
1612 (setq ali-file-name
1613 (concat (file-name-sans-extension (ada-xref-current file))
1614 ".ali"))
797aab3c 1615
ced16f5d 1616 (error "`.ali' file not found; recompile your source file"))
a1506d29
JB
1617
1618
4884c50b
SM
1619 ;; same if the .ali file is too old and we must recompile it
1620 (if (and (file-newer-than-file-p file ali-file-name)
1621 ada-xref-create-ali)
1622 (ada-xref-current file ali-file-name)))
797aab3c 1623
4884c50b 1624 ;; Always return the correct absolute file name
797aab3c 1625 (expand-file-name ali-file-name))
4884c50b 1626 ))
797aab3c
GM
1627
1628(defun ada-get-ada-file-name (file original-file)
eec3232e 1629 "Create the complete file name (+directory) for FILE.
7977c946
JB
1630The original file (where the user was) is ORIGINAL-FILE.
1631Search in project file for possible paths."
797aab3c
GM
1632
1633 (save-excursion
15ea3b67
GM
1634
1635 ;; If the buffer for original-file, use it to get the values from the
1636 ;; project file, otherwise load the file and its project file
1637 (let ((buffer (get-file-buffer original-file)))
1638 (if buffer
1639 (set-buffer buffer)
85187d83 1640 (find-file original-file)))
a1506d29 1641
797aab3c
GM
1642 ;; we choose the first possible completion and we
1643 ;; return the absolute file name
15ea3b67 1644 (let ((filename (ada-find-src-file-in-dir file)))
797aab3c 1645 (if filename
417451fe 1646 (expand-file-name filename)
0262d5e1 1647 (signal 'error-file-not-found (file-name-nondirectory file)))
797aab3c
GM
1648 )))
1649
1650(defun ada-find-file-number-in-ali (file)
417451fe 1651 "Return the file number for FILE in the associated ali file."
797aab3c
GM
1652 (set-buffer (ada-get-ali-buffer file))
1653 (goto-char (point-min))
1654
1655 (let ((begin (re-search-forward "^D")))
1656 (beginning-of-line)
1657 (re-search-forward (concat "^D " (file-name-nondirectory file)))
1658 (count-lines begin (point))))
1659
1660(defun ada-read-identifier (pos)
417451fe 1661 "Return the identlist around POS and switch to the .ali buffer.
4884c50b
SM
1662The returned list represents the entity, and can be manipulated through the
1663macros `ada-name-of', `ada-line-of', `ada-column-of', `ada-file-of',..."
797aab3c
GM
1664
1665 ;; If at end of buffer (e.g the buffer is empty), error
1666 (if (>= (point) (point-max))
1667 (error "No identifier on point"))
a1506d29 1668
797aab3c
GM
1669 ;; goto first character of the identifier/operator (skip backward < and >
1670 ;; since they are part of multiple character operators
1671 (goto-char pos)
1672 (skip-chars-backward "a-zA-Z0-9_<>")
1673
1674 ;; check if it really is an identifier
1675 (if (ada-in-comment-p)
1676 (error "Inside comment"))
1677
1678 (let (identifier identlist)
1679 ;; Just in front of a string => we could have an operator declaration,
1680 ;; as in "+", "-", ..
1681 (if (= (char-after) ?\")
417451fe 1682 (forward-char 1))
797aab3c
GM
1683
1684 ;; if looking at an operator
15ea3b67
GM
1685 ;; This is only true if:
1686 ;; - the symbol is +, -, ...
1687 ;; - the symbol is made of letters, and not followed by _ or a letter
1688 (if (and (looking-at ada-operator-re)
1689 (or (not (= (char-syntax (char-after)) ?w))
1690 (not (or (= (char-syntax (char-after (match-end 0))) ?w)
1691 (= (char-after (match-end 0)) ?_)))))
417451fe
JB
1692 (progn
1693 (if (and (= (char-before) ?\")
1694 (= (char-after (+ (length (match-string 0)) (point))) ?\"))
1695 (forward-char -1))
1696 (set 'identifier (regexp-quote (concat "\"" (match-string 0) "\""))))
797aab3c
GM
1697
1698 (if (ada-in-string-p)
417451fe 1699 (error "Inside string or character constant"))
797aab3c 1700 (if (looking-at (concat ada-keywords "[^a-zA-Z_]"))
417451fe 1701 (error "No cross-reference available for reserved keyword"))
797aab3c 1702 (if (looking-at "[a-zA-Z0-9_]+")
417451fe
JB
1703 (set 'identifier (match-string 0))
1704 (error "No identifier around")))
a1506d29 1705
797aab3c
GM
1706 ;; Build the identlist
1707 (set 'identlist (ada-make-identlist))
1708 (ada-set-name identlist (downcase identifier))
1709 (ada-set-line identlist
93cdce20 1710 (number-to-string (count-lines 1 (point))))
797aab3c
GM
1711 (ada-set-column identlist
1712 (number-to-string (1+ (current-column))))
1713 (ada-set-file identlist (buffer-file-name))
1714 identlist
1715 ))
1716
1717(defun ada-get-all-references (identlist)
417451fe
JB
1718 "Complete IDENTLIST with definition file and places where it is referenced.
1719Information is extracted from the ali file."
a1506d29 1720
797aab3c
GM
1721 (let ((ali-buffer (ada-get-ali-buffer (ada-file-of identlist)))
1722 declaration-found)
1723 (set-buffer ali-buffer)
1724 (goto-char (point-min))
1725 (ada-set-on-declaration identlist nil)
1726
1727 ;; First attempt: we might already be on the declaration of the identifier
1728 ;; We want to look for the declaration only in a definite interval (after
1729 ;; the "^X ..." line for the current file, and before the next "^X" line
a1506d29 1730
797aab3c
GM
1731 (if (re-search-forward
1732 (concat "^X [0-9]+ " (file-name-nondirectory (ada-file-of identlist)))
1733 nil t)
417451fe
JB
1734 (let ((bound (save-excursion (re-search-forward "^X " nil t))))
1735 (set 'declaration-found
797aab3c
GM
1736 (re-search-forward
1737 (concat "^" (ada-line-of identlist)
1738 "." (ada-column-of identlist)
15ea3b67 1739 "[ *]" (ada-name-of identlist)
0262d5e1 1740 "[{\[\(<= ]?\\(.*\\)$") bound t))
797aab3c
GM
1741 (if declaration-found
1742 (ada-set-on-declaration identlist t))
1743 ))
1744
1745 ;; If declaration is still nil, then we were not on a declaration, and
1746 ;; have to fall back on other algorithms
1747
1748 (unless declaration-found
a1506d29 1749
797aab3c
GM
1750 ;; Since we alread know the number of the file, search for a direct
1751 ;; reference to it
1752 (goto-char (point-min))
1753 (set 'declaration-found t)
1754 (ada-set-ali-index
1755 identlist
1756 (number-to-string (ada-find-file-number-in-ali
1757 (ada-file-of identlist))))
1758 (unless (re-search-forward (concat (ada-ali-index-of identlist)
93cdce20 1759 "|\\([0-9]+[^0-9][0-9]+\\(\n\\.\\)? \\)*"
797aab3c 1760 (ada-line-of identlist)
c94ca9e0 1761 "[^etpzkd<>=^]"
93cdce20 1762 (ada-column-of identlist) "\\>")
797aab3c
GM
1763 nil t)
1764
417451fe
JB
1765 ;; if we did not find it, it may be because the first reference
1766 ;; is not required to have a 'unit_number|' item included.
1767 ;; Or maybe we are already on the declaration...
1768 (unless (re-search-forward
4884c50b 1769 (concat
93cdce20
SM
1770 "^[0-9]+.[0-9]+[ *]"
1771 (ada-name-of identlist)
0262d5e1 1772 "[ <{=\(\[]\\(.\\|\n\\.\\)*\\<"
4884c50b
SM
1773 (ada-line-of identlist)
1774 "[^0-9]"
93cdce20 1775 (ada-column-of identlist) "\\>")
4884c50b 1776 nil t)
a1506d29 1777
797aab3c
GM
1778 ;; If still not found, then either the declaration is unknown
1779 ;; or the source file has been modified since the ali file was
1780 ;; created
1781 (set 'declaration-found nil)
417451fe 1782 )
797aab3c
GM
1783 )
1784
1785 ;; Last check to be completly sure we have found the correct line (the
1786 ;; ali might not be up to date for instance)
1787 (if declaration-found
1788 (progn
1789 (beginning-of-line)
1790 ;; while we have a continuation line, go up one line
1791 (while (looking-at "^\\.")
7caf6803 1792 (forward-line -1)
0262d5e1 1793 (beginning-of-line))
797aab3c 1794 (unless (looking-at (concat "[0-9]+.[0-9]+[ *]"
0262d5e1 1795 (ada-name-of identlist) "[ <{=\(\[]"))
797aab3c
GM
1796 (set 'declaration-found nil))))
1797
1798 ;; Still no success ! The ali file must be too old, and we need to
ced16f5d 1799 ;; use a basic algorithm based on guesses. Note that this only happens
797aab3c
GM
1800 ;; if the user does not want us to automatically recompile files
1801 ;; automatically
1802 (unless declaration-found
15ea3b67
GM
1803 (if (ada-xref-find-in-modified-ali identlist)
1804 (set 'declaration-found t)
ced16f5d 1805 ;; No more idea to find the declaration. Give up
797aab3c
GM
1806 (progn
1807 (kill-buffer ali-buffer)
a867ead0 1808
32b1a27f 1809 (error "No declaration of %s found" (ada-name-of identlist))
797aab3c
GM
1810 )))
1811 )
1812
a1506d29 1813
797aab3c
GM
1814 ;; Now that we have found a suitable line in the .ali file, get the
1815 ;; information available
1816 (beginning-of-line)
1817 (if declaration-found
417451fe 1818 (let ((current-line (buffer-substring
e180ab9f 1819 (point) (point-at-eol))))
417451fe 1820 (save-excursion
7caf6803 1821 (forward-line 1)
417451fe
JB
1822 (beginning-of-line)
1823 (while (looking-at "^\\.\\(.*\\)")
1824 (set 'current-line (concat current-line (match-string 1)))
7caf6803 1825 (forward-line 1))
417451fe 1826 )
797aab3c
GM
1827
1828 (if (re-search-backward "^X [0-9]+ \\([a-zA-Z0-9_.-]+\\)" nil t)
4884c50b
SM
1829
1830 ;; If we can find the file
1831 (condition-case err
1832 (ada-set-declare-file
1833 identlist
1834 (ada-get-ada-file-name (match-string 1)
1835 (ada-file-of identlist)))
a1506d29 1836
4884c50b 1837 ;; Else clean up the ali file
0262d5e1
JB
1838 (error-file-not-found
1839 (signal (car err) (cdr err)))
4884c50b
SM
1840 (error
1841 (kill-buffer ali-buffer)
1842 (error (error-message-string err)))
1843 ))
a1506d29 1844
797aab3c
GM
1845 (ada-set-references identlist current-line)
1846 ))
1847 ))
1848
1849(defun ada-xref-find-in-modified-ali (identlist)
1850 "Find the matching position for IDENTLIST in the current ali buffer.
1851This function is only called when the file was not up-to-date, so we need
1852to make some guesses.
eec3232e 1853This function is disabled for operators, and only works for identifiers."
797aab3c
GM
1854
1855 (unless (= (string-to-char (ada-name-of identlist)) ?\")
1856 (progn
417451fe 1857 (let ((declist '()) ;;; ( (line_in_ali_file line_in_ada) ( ... ))
797aab3c
GM
1858 (my-regexp (concat "[ *]"
1859 (regexp-quote (ada-name-of identlist)) " "))
1860 (line-ada "--")
1861 (col-ada "--")
1862 (line-ali 0)
1863 (len 0)
15ea3b67
GM
1864 (choice 0)
1865 (ali-buffer (current-buffer)))
797aab3c 1866
417451fe
JB
1867 (goto-char (point-max))
1868 (while (re-search-backward my-regexp nil t)
1869 (save-excursion
1870 (set 'line-ali (count-lines 1 (point)))
1871 (beginning-of-line)
1872 ;; have a look at the line and column numbers
1873 (if (looking-at "^\\([0-9]+\\).\\([0-9]+\\)[ *]")
1874 (progn
1875 (setq line-ada (match-string 1))
1876 (setq col-ada (match-string 2)))
1877 (setq line-ada "--")
1878 (setq col-ada "--")
1879 )
1880 ;; construct a list with the file names and the positions within
1881 (if (re-search-backward "^X [0-9]+ \\([a-zA-Z0-9._-]+\\)" nil t)
797aab3c
GM
1882 (add-to-list
1883 'declist (list line-ali (match-string 1) line-ada col-ada))
417451fe
JB
1884 )
1885 )
1886 )
1887
1888 ;; how many possible declarations have we found ?
1889 (setq len (length declist))
1890 (cond
1891 ;; none => error
1892 ((= len 0)
1893 (kill-buffer (current-buffer))
a867ead0
DG
1894 (error "No declaration of %s recorded in .ali file"
1895 (ada-name-of identlist)))
417451fe
JB
1896 ;; one => should be the right one
1897 ((= len 1)
e6ce8c42
GM
1898 (goto-char (point-min))
1899 (forward-line (1- (caar declist))))
417451fe
JB
1900
1901 ;; more than one => display choice list
1902 (t
4884c50b
SM
1903 (save-window-excursion
1904 (with-output-to-temp-buffer "*choice list*"
a1506d29 1905
4884c50b
SM
1906 (princ "Identifier is overloaded and Xref information is not up to date.\n")
1907 (princ "Possible declarations are:\n\n")
1908 (princ " no. in file at line col\n")
1909 (princ " --- --------------------- ---- ----\n")
1910 (let ((counter 0))
1911 (while (< counter len)
1912 (princ (format " %2d) %-21s %4s %4s\n"
417451fe 1913 (1+ counter)
797aab3c 1914 (ada-get-ada-file-name
4884c50b 1915 (nth 1 (nth counter declist))
797aab3c 1916 (ada-file-of identlist))
417451fe
JB
1917 (nth 2 (nth counter declist))
1918 (nth 3 (nth counter declist))
1919 ))
4884c50b
SM
1920 (setq counter (1+ counter))
1921 ) ; end of while
1922 ) ; end of let
1923 ) ; end of with-output-to ...
1924 (setq choice nil)
1925 (while (or
1926 (not choice)
1927 (not (integerp choice))
1928 (< choice 1)
1929 (> choice len))
1930 (setq choice
027a4b6b 1931 (string-to-number
4884c50b
SM
1932 (read-from-minibuffer "Enter No. of your choice: "))))
1933 )
15ea3b67 1934 (set-buffer ali-buffer)
e6ce8c42
GM
1935 (goto-char (point-min))
1936 (forward-line (1- (car (nth (1- choice) declist))))
417451fe 1937 ))))))
797aab3c
GM
1938
1939
1940(defun ada-find-in-ali (identlist &optional other-frame)
eec3232e 1941 "Look in the .ali file for the definition of the identifier in IDENTLIST.
09aba29c 1942If OTHER-FRAME is non-nil, and `ada-xref-other-buffer' is non-nil,
eec3232e 1943opens a new window to show the declaration."
797aab3c
GM
1944
1945 (ada-get-all-references identlist)
1946 (let ((ali-line (ada-references-of identlist))
4884c50b
SM
1947 (locations nil)
1948 (start 0)
797aab3c 1949 file line col)
4884c50b
SM
1950
1951 ;; Note: in some cases, an entity can have multiple references to the
1952 ;; bodies (this is for instance the case for a separate subprogram, that
1953 ;; has a reference both to the stub and to the real body).
1954 ;; In that case, we simply go to each one in turn.
1955
1956 ;; Get all the possible locations
0262d5e1 1957 (string-match "^\\([0-9]+\\)[a-zA-Z+*]\\([0-9]+\\)[ *]" ali-line)
4884c50b
SM
1958 (set 'locations (list (list (match-string 1 ali-line) ;; line
1959 (match-string 2 ali-line) ;; column
1960 (ada-declare-file-of identlist))))
c94ca9e0
JB
1961 (while (string-match "\\([0-9]+\\)[bc]\\(<[^>]+>\\)?\\([0-9]+\\)"
1962 ali-line start)
4884c50b 1963 (setq line (match-string 1 ali-line)
c94ca9e0
JB
1964 col (match-string 3 ali-line)
1965 start (match-end 3))
4884c50b
SM
1966
1967 ;; it there was a file number in the same line
0262d5e1
JB
1968 ;; Make sure we correctly handle the case where the first file reference
1969 ;; on the line is the type reference.
1970 ;; 1U2 T(2|2r3) 34r23
1971 (if (string-match (concat "[^{(<0-9]\\([0-9]+\\)|\\([^|bc]+\\)?"
4884c50b
SM
1972 (match-string 0 ali-line))
1973 ali-line)
1974 (let ((file-number (match-string 1 ali-line)))
1975 (goto-char (point-min))
1976 (re-search-forward "^D \\([a-zA-Z0-9_.-]+\\)" nil t
1977 (string-to-number file-number))
1978 (set 'file (match-string 1))
1979 )
1980 ;; Else get the nearest file
1981 (set 'file (ada-declare-file-of identlist)))
a1506d29 1982
4884c50b
SM
1983 (set 'locations (append locations (list (list line col file)))))
1984
1985 ;; Add the specs at the end again, so that from the last body we go to
1986 ;; the specs
1987 (set 'locations (append locations (list (car locations))))
1988
1989 ;; Find the new location we want to go to.
1990 ;; If we are on none of the locations listed, we simply go to the specs.
1991
1992 (setq line (caar locations)
1993 col (nth 1 (car locations))
1994 file (nth 2 (car locations)))
a1506d29 1995
4884c50b
SM
1996 (while locations
1997 (if (and (string= (caar locations) (ada-line-of identlist))
1998 (string= (nth 1 (car locations)) (ada-column-of identlist))
1999 (string= (file-name-nondirectory (nth 2 (car locations)))
2000 (file-name-nondirectory (ada-file-of identlist))))
2001 (setq locations (cadr locations)
2002 line (car locations)
2003 col (nth 1 locations)
2004 file (nth 2 locations)
2005 locations nil)
2006 (set 'locations (cdr locations))))
2007
2008 ;; Find the file in the source path
2009 (set 'file (ada-get-ada-file-name file (ada-file-of identlist)))
2010
2011 ;; Kill the .ali buffer
2012 (kill-buffer (current-buffer))
797aab3c
GM
2013
2014 ;; Now go to the buffer
4884c50b
SM
2015 (ada-xref-change-buffer file
2016 (string-to-number line)
2017 (1- (string-to-number col))
2018 identlist
2019 other-frame)
797aab3c
GM
2020 ))
2021
4884c50b
SM
2022(defun ada-find-in-src-path (identlist &optional other-frame)
2023 "More general function for cross-references.
2024This function should be used when the standard algorithm that parses the
2025.ali file has failed, either because that file was too old or even did not
2026exist.
2027This function attempts to find the possible declarations for the identifier
2028anywhere in the object path.
2029This command requires the external `egrep' program to be available.
2030
f020b5c9 2031This works well when one is using an external library and wants to find
7977c946 2032the declaration and documentation of the subprograms one is using."
417451fe 2033;; FIXME: what does this function do?
4884c50b
SM
2034 (let (list
2035 (dirs (ada-xref-get-obj-dir-field))
2036 (regexp (concat "[ *]" (ada-name-of identlist)))
2037 line column
2038 choice
2039 file)
a1506d29 2040
9a529312
SM
2041 ;; Do the grep in all the directories. We do multiple shell
2042 ;; commands instead of one in case there is no .ali file in one
2043 ;; of the directory and the shell stops because of that.
a1506d29 2044
9a529312 2045 (with-current-buffer (get-buffer-create "*grep*")
4884c50b
SM
2046 (while dirs
2047 (insert (shell-command-to-string
81fe5f4b
KS
2048 (concat
2049 "grep -E -i -h "
2050 (shell-quote-argument (concat "^X|" regexp "( |$)"))
2051 " "
2052 (shell-quote-argument (file-name-as-directory (car dirs)))
2053 "*.ali")))
4884c50b 2054 (set 'dirs (cdr dirs)))
a1506d29 2055
4884c50b
SM
2056 ;; Now parse the output
2057 (set 'case-fold-search t)
2058 (goto-char (point-min))
2059 (while (re-search-forward regexp nil t)
2060 (save-excursion
2061 (beginning-of-line)
2062 (if (not (= (char-after) ?X))
2063 (progn
2064 (looking-at "\\([0-9]+\\).\\([0-9]+\\)")
2065 (setq line (match-string 1)
2066 column (match-string 2))
2067 (re-search-backward "^X [0-9]+ \\(.*\\)$")
2068 (set 'file (list (match-string 1) line column))
a1506d29 2069
4884c50b
SM
2070 ;; There could be duplicate choices, because of the structure
2071 ;; of the .ali files
2072 (unless (member file list)
2073 (set 'list (append list (list file))))))))
a1506d29 2074
4884c50b
SM
2075 ;; Current buffer is still "*grep*"
2076 (kill-buffer "*grep*")
2077 )
a1506d29 2078
4884c50b
SM
2079 ;; Now display the list of possible matches
2080 (cond
a1506d29 2081
4884c50b
SM
2082 ;; No choice found => Error
2083 ((null list)
2084 (error "No cross-reference found, please recompile your file"))
a1506d29 2085
4884c50b
SM
2086 ;; Only one choice => Do the cross-reference
2087 ((= (length list) 1)
2088 (set 'file (ada-find-src-file-in-dir (caar list)))
2089 (if file
2090 (ada-xref-change-buffer file
2091 (string-to-number (nth 1 (car list)))
2092 (string-to-number (nth 2 (car list)))
2093 identlist
2094 other-frame)
a867ead0 2095 (error "%s not found in src_dir" (caar list)))
4884c50b
SM
2096 (message "This is only a (good) guess at the cross-reference.")
2097 )
a1506d29 2098
4884c50b
SM
2099 ;; Else, ask the user
2100 (t
2101 (save-window-excursion
2102 (with-output-to-temp-buffer "*choice list*"
a1506d29 2103
4884c50b
SM
2104 (princ "Identifier is overloaded and Xref information is not up to date.\n")
2105 (princ "Possible declarations are:\n\n")
2106 (princ " no. in file at line col\n")
2107 (princ " --- --------------------- ---- ----\n")
2108 (let ((counter 0))
2109 (while (< counter (length list))
2110 (princ (format " %2d) %-21s %4s %4s\n"
2111 (1+ counter)
2112 (nth 0 (nth counter list))
2113 (nth 1 (nth counter list))
2114 (nth 2 (nth counter list))
2115 ))
2116 (setq counter (1+ counter))
2117 )))
2118 (setq choice nil)
2119 (while (or (not choice)
2120 (not (integerp choice))
2121 (< choice 1)
2122 (> choice (length list)))
2123 (setq choice
027a4b6b 2124 (string-to-number
4884c50b
SM
2125 (read-from-minibuffer "Enter No. of your choice: "))))
2126 )
2127 (set 'choice (1- choice))
2128 (kill-buffer "*choice list*")
2129
2130 (set 'file (ada-find-src-file-in-dir (car (nth choice list))))
2131 (if file
2132 (ada-xref-change-buffer file
2133 (string-to-number (nth 1 (nth choice list)))
2134 (string-to-number (nth 2 (nth choice list)))
2135 identlist
2136 other-frame)
0262d5e1 2137 (signal 'error-file-not-found (car (nth choice list))))
4884c50b
SM
2138 (message "This is only a (good) guess at the cross-reference.")
2139 ))))
2140
797aab3c
GM
2141(defun ada-xref-change-buffer
2142 (file line column identlist &optional other-frame)
4884c50b 2143 "Select and display FILE, at LINE and COLUMN.
32b1a27f
JB
2144If we do not end on the same identifier as IDENTLIST, find the
2145closest match. Kills the .ali buffer at the end.
eec3232e 2146If OTHER-FRAME is non-nil, creates a new frame to show the file."
797aab3c 2147
4884c50b 2148 (let (declaration-buffer)
797aab3c
GM
2149
2150 ;; Select and display the destination buffer
2151 (if ada-xref-other-buffer
417451fe
JB
2152 (if other-frame
2153 (find-file-other-frame file)
2154 (set 'declaration-buffer (find-file-noselect file))
2155 (set-buffer declaration-buffer)
2156 (switch-to-buffer-other-window declaration-buffer)
2157 )
797aab3c
GM
2158 (find-file file)
2159 )
2160
797aab3c
GM
2161 ;; move the cursor to the correct position
2162 (push-mark)
e6ce8c42
GM
2163 (goto-char (point-min))
2164 (forward-line (1- line))
797aab3c
GM
2165 (move-to-column column)
2166
2167 ;; If we are not on the identifier, the ali file was not up-to-date.
2168 ;; Try to find the nearest position where the identifier is found,
2169 ;; this is probably the right one.
2170 (unless (looking-at (ada-name-of identlist))
2171 (ada-xref-search-nearest (ada-name-of identlist)))
4884c50b 2172 ))
797aab3c
GM
2173
2174
2175(defun ada-xref-search-nearest (name)
417451fe
JB
2176 "Search for NAME nearest to the position recorded in the Xref file.
2177Return the position of the declaration in the buffer, or nil if not found."
797aab3c 2178 (let ((orgpos (point))
417451fe
JB
2179 (newpos nil)
2180 (diff nil))
797aab3c
GM
2181
2182 (goto-char (point-max))
2183
2184 ;; loop - look for all declarations of name in this file
2185 (while (search-backward name nil t)
2186
2187 ;; check if it really is a complete Ada identifier
2188 (if (and
417451fe
JB
2189 (not (save-excursion
2190 (goto-char (match-end 0))
2191 (looking-at "_")))
2192 (not (ada-in-string-or-comment-p))
2193 (or
2194 ;; variable declaration ?
2195 (save-excursion
2196 (skip-chars-forward "a-zA-Z_0-9" )
2197 (ada-goto-next-non-ws)
2198 (looking-at ":[^=]"))
2199 ;; procedure, function, task or package declaration ?
2200 (save-excursion
2201 (ada-goto-previous-word)
2202 (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]\\>"))))
2203
2204 ;; check if it is nearer than the ones before if any
2205 (if (or (not diff)
2206 (< (abs (- (point) orgpos)) diff))
2207 (progn
2208 (setq newpos (point)
797aab3c
GM
2209 diff (abs (- newpos orgpos))))))
2210 )
2211
2212 (if newpos
417451fe
JB
2213 (progn
2214 (message "ATTENTION: this declaration is only a (good) guess ...")
2215 (goto-char newpos))
797aab3c
GM
2216 nil)))
2217
2218
2219;; Find the parent library file of the current file
2220(defun ada-goto-parent ()
eec3232e 2221 "Go to the parent library file."
797aab3c
GM
2222 (interactive)
2223 (ada-require-project-file)
2224
2225 (let ((buffer (ada-get-ali-buffer (buffer-file-name)))
417451fe
JB
2226 (unit-name nil)
2227 (body-name nil)
2228 (ali-name nil))
9a529312 2229 (with-current-buffer buffer
797aab3c
GM
2230 (goto-char (point-min))
2231 (re-search-forward "^U \\([^ \t%]+\\)%[bs][ \t]+\\([^ \t]+\\)")
2232 (setq unit-name (match-string 1))
2233 (if (not (string-match "\\(.*\\)\\.[^.]+" unit-name))
417451fe
JB
2234 (progn
2235 (kill-buffer buffer)
2236 (error "No parent unit !"))
2237 (setq unit-name (match-string 1 unit-name))
2238 )
797aab3c
GM
2239
2240 ;; look for the file name for the parent unit specification
2241 (goto-char (point-min))
2242 (re-search-forward (concat "^W " unit-name
417451fe
JB
2243 "%s[ \t]+\\([^ \t]+\\)[ \t]+"
2244 "\\([^ \t\n]+\\)"))
797aab3c
GM
2245 (setq body-name (match-string 1))
2246 (setq ali-name (match-string 2))
2247 (kill-buffer buffer)
2248 )
2249
2250 (setq ali-name (ada-find-ali-file-in-dir ali-name))
2251
2252 (save-excursion
2253 ;; Tries to open the new ali file to find the spec file
2254 (if ali-name
417451fe
JB
2255 (progn
2256 (find-file ali-name)
2257 (goto-char (point-min))
2258 (re-search-forward (concat "^U " unit-name "%s[ \t]+"
2259 "\\([^ \t]+\\)"))
2260 (setq body-name (match-string 1))
2261 (kill-buffer (current-buffer))
2262 )
2263 )
797aab3c
GM
2264 )
2265
2266 (find-file body-name)
2267 ))
2268
2269(defun ada-make-filename-from-adaname (adaname)
eec3232e
GM
2270 "Determine the filename in which ADANAME is found.
2271This is a GNAT specific function that uses gnatkrunch."
d5875b25 2272 (let ((krunch-buf (generate-new-buffer "*gkrunch*"))
ff6d2a9e 2273 (cross-prefix (plist-get (cdr (ada-xref-current-project)) 'cross_prefix)))
9a529312 2274 (with-current-buffer krunch-buf
797aab3c 2275 ;; send adaname to external process `gnatkr'.
0262d5e1
JB
2276 ;; Add a dummy extension, since gnatkr versions have two different
2277 ;; behaviors depending on the version:
2278 ;; Up to 3.15: "AA.BB.CC" => aa-bb-cc
2279 ;; After: "AA.BB.CC" => aa-bb.cc
d5875b25 2280 (call-process (concat cross-prefix "gnatkr") nil krunch-buf nil
0262d5e1 2281 (concat adaname ".adb") ada-krunch-args)
797aab3c
GM
2282 ;; fetch output of that process
2283 (setq adaname (buffer-substring
417451fe
JB
2284 (point-min)
2285 (progn
2286 (goto-char (point-min))
2287 (end-of-line)
2288 (point))))
0262d5e1
JB
2289 ;; Remove the extra extension we added above
2290 (setq adaname (substring adaname 0 -4))
2291
797aab3c
GM
2292 (kill-buffer krunch-buf)))
2293 adaname
2294 )
2295
2dccd96f 2296(defun ada-make-body-gnatstub (&optional interactive)
797aab3c 2297 "Create an Ada package body in the current buffer.
d5875b25
JB
2298This function uses the `gnat stub' program to create the body.
2299This function typically is to be hooked into `ff-file-created-hook'.
2300If INTERACTIVE is nil, assume this is called from `ff-file-created-hook'."
2dccd96f 2301 (interactive "p")
85187d83 2302 (ada-require-project-file)
797aab3c 2303
d5875b25
JB
2304 ;; If not interactive, assume we are being called from
2305 ;; ff-file-created-hook. Then the current buffer is for the body
2306 ;; file, but we will create a new one after gnat stub runs
2dccd96f 2307 (unless interactive
ab074cb1
JB
2308 (set-buffer-modified-p nil)
2309 (kill-buffer (current-buffer)))
a1506d29 2310
d5875b25 2311 (save-some-buffers nil nil)
797aab3c 2312
d5875b25
JB
2313 ;; Make sure the current buffer is the spec, so gnat stub gets the
2314 ;; right package parameter (this might not be the case if for
2315 ;; instance the user was asked for a project file)
4884c50b
SM
2316
2317 (unless (buffer-file-name (car (buffer-list)))
2318 (set-buffer (cadr (buffer-list))))
2319
d5875b25
JB
2320 ;; Call the external process
2321 (let* ((project-plist (cdr (ada-xref-current-project)))
2322 (gnatstub-opts (ada-treat-cmd-string ada-gnatstub-opts))
2323 (gpr-file (plist-get project-plist 'gpr_file))
417451fe
JB
2324 (filename (buffer-file-name (car (buffer-list))))
2325 (output (concat (file-name-sans-extension filename) ".adb"))
d5875b25
JB
2326 (cross-prefix (plist-get project-plist 'cross_prefix))
2327 (gnatstub-cmd (concat cross-prefix "gnat stub"
2328 (if (not (string= gpr-file ""))
2329 (concat " -P\"" gpr-file "\""))
2330 " " gnatstub-opts " " filename))
2331 (buffer (get-buffer-create "*gnat stub*")))
797aab3c 2332
9a529312 2333 (with-current-buffer buffer
797aab3c
GM
2334 (compilation-minor-mode 1)
2335 (erase-buffer)
2336 (insert gnatstub-cmd)
2337 (newline)
2338 )
797aab3c 2339
d5875b25 2340 (call-process shell-file-name nil buffer nil "-c" gnatstub-cmd)
797aab3c 2341
d5875b25 2342 ;; clean up the output
797aab3c 2343
d5875b25
JB
2344 (if (file-exists-p output)
2345 (progn
2346 (find-file output)
2347 (kill-buffer buffer))
797aab3c 2348
d5875b25
JB
2349 ;; file not created; display the error message
2350 (display-buffer buffer))))
797aab3c 2351
797aab3c 2352(defun ada-xref-initialize ()
fea24571
SM
2353 "Function called by `ada-mode-hook' to initialize the ada-xref.el package.
2354For instance, it creates the gnat-specific menus, sets some hooks for
7977c946 2355`find-file'."
ce883133
JB
2356 (remove-hook 'ff-file-created-hook 'ada-make-body) ; from global hook
2357 (remove-hook 'ff-file-created-hook 'ada-make-body t) ; from local hook
2358 (add-hook 'ff-file-created-hook 'ada-make-body-gnatstub nil t)
797aab3c 2359
797aab3c
GM
2360 ;; Completion for file names in the mini buffer should ignore .ali files
2361 (add-to-list 'completion-ignored-extensions ".ali")
c94ca9e0
JB
2362
2363 (ada-xref-update-project-menu)
797aab3c
GM
2364 )
2365
797aab3c
GM
2366;; ----- Add to ada-mode-hook ---------------------------------------------
2367
4884c50b 2368;; This must be done before initializing the Ada menu.
797aab3c
GM
2369(add-hook 'ada-mode-hook 'ada-xref-initialize)
2370
0262d5e1
JB
2371;; Define a new error type
2372(put 'error-file-not-found
2373 'error-conditions
2374 '(error ada-mode-errors error-file-not-found))
2375(put 'error-file-not-found
2376 'error-message
2377 "File not found in src-dir (check project file): ")
2378
797aab3c
GM
2379(provide 'ada-xref)
2380
383d5bbb 2381;;; ada-xref.el ends here