(c-lineup-multi-inher): Handle lines with
[bpt/emacs.git] / lisp / progmodes / ada-xref.el
CommitLineData
797aab3c
GM
1;; @(#) ada-xref.el --- use Gnat for lookup and completion in Ada mode
2
eec3232e 3;; Copyright (C) 1994, 1995--1998, 1999 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>
8;; Maintainer: Emmanuel Briot <briot@gnat.com>
9;; Ada Core Technologies's version: $Revision: 1.75 $
10;; Keywords: languages ada xref
11
12;; This file is not part of GNU Emacs.
13
14;; This program is free software; you can redistribute it and/or modify
15;; it under the terms of the GNU General Public License as published by
16;; the Free Software Foundation; either version 2, or (at your option)
17;; any later version.
18
19;; This program is distributed in the hope that it will be useful,
20;; but WITHOUT ANY WARRANTY; without even the implied warranty of
21;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22;; GNU General Public License for more details.
23
24;; You should have received a copy of the GNU General Public License
25;; along with GNU Emacs; see the file COPYING. If not, write to
26;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
27
28;;; Commentary:
29;;; This Package provides a set of functions to use the output of the
30;;; cross reference capabilities of the GNAT Ada compiler
31;;; for lookup and completion in Ada mode.
32;;;
33;;; The functions provided are the following ones :
34;;; - `ada-complete-identifier': completes the current identifier as much as
35;;; possible, depending of the known identifier in the unit
36;;; - `ada-point-and-xref': moves the mouse pointer and shows the declaration
37;;; of the selected identifier (either in the same buffer or in another
38;;; buffer
39;;; - `ada-goto-declaration': shows the declaration of the selected
40;;; identifier (the one under the cursor), either in the same buffer or in
41;;; another buffer
42;;; - `ada-goto-declaration-other-frame': same as previous, but opens a new
43;; frame to show the declaration
44;;; - `ada-compile-application': recompile your whole application, provided
45;;; that a project file exists in your directory
eec3232e
GM
46;;; - `ada-run-application': run your application directly from Emacs
47;;; - `ada-reread-prj-file': force Emacs to read your project file again.
48;;; Otherwise, this file is only read the first time Emacs needs some
797aab3c
GM
49;;; informations, which are then kept in memory
50;;; - `ada-change-prj': change the prj file associated with a buffer
51;;; - `ada-change-default-prj': change the default project file used for
52;;; every new buffer
53;;;
54;;; If a file *.`adp' exists in the ada-file directory, then it is
55;;; read for configuration informations. It is read only the first
56;;; time a cross-reference is asked for, and is not read later.
57
58;;; You need Emacs >= 20.2 to run this package
59
60;; ----- Requirements -----------------------------------------------------
61
62(require 'compile)
63(require 'comint)
64
65;; ----- Dynamic byte compilation -----------------------------------------
66(defvar byte-compile-dynamic nil)
67(make-local-variable 'byte-compile-dynamic)
68(setq byte-compile-dynamic t)
69
70;; ------ Use variables
71(defcustom ada-xref-other-buffer t
eec3232e
GM
72 "*If nil, always display the cross-references in the same buffer.
73Otherwise create either a new buffer or a new frame."
797aab3c
GM
74 :type 'boolean :group 'ada)
75
76(defcustom ada-xref-create-ali t
eec3232e
GM
77 "*If non-nil, run gcc whenever the cross-references are not up-to-date.
78If nil, the cross-reference mode will never run gcc."
797aab3c
GM
79 :type 'boolean :group 'ada)
80
81(defcustom ada-xref-confirm-compile nil
eec3232e
GM
82 "*If non-nil, always ask for user confirmation before compiling or running
83the application."
797aab3c
GM
84 :type 'boolean :group 'ada)
85
86(defcustom ada-krunch-args "0"
eec3232e
GM
87 "*Maximum number of characters for filenames created by gnatkr.
88Set to 0, if you don't use crunched filenames. This should be a string."
797aab3c
GM
89 :type 'string :group 'ada)
90
eec3232e
GM
91(defcustom ada-prj-default-comp-cmd
92 "${cross_prefix}gcc -c -g -gnatq ${comp_opt} -I${src_dir}"
797aab3c
GM
93 "*Default command to be used to compile a single file.
94Emacs will add the filename at the end of this command.
95This is the same syntax as in the project file."
96 :type 'string :group 'ada)
97
98(defcustom ada-prj-default-make-cmd
99 (concat "${cross_prefix}gnatmake ${main} -aI${src_dir} -aO${obj_dir} "
100 "-g -gnatq -cargs ${comp_opt} "
101 "-bargs ${bind_opt} -largs ${link_opt}")
102 "*Default command to be used to compile the application.
103This is the same syntax as in the project file."
104 :type 'string :group 'ada)
105
106(defcustom ada-prj-default-project-file ""
eec3232e
GM
107 "*Name of the project file to use for every Ada file.
108Emacs will not try to use the standard algorithm to find the project file if
109this string is not empty."
797aab3c
GM
110 :type '(file :must-match t) :group 'ada)
111
112(defcustom ada-gnatstub-opts "-q -I${src_dir}"
eec3232e
GM
113 "*List of the options to pass to gnatsub to generate the body of a package.
114This has the same syntax as in the project file (with variable substitution)."
797aab3c
GM
115 :type 'string :group 'ada)
116
117(defcustom ada-always-ask-project nil
eec3232e
GM
118 "*If nil, use default values when no project file was found.
119Otherwise, ask the user for the name of the project file to use.")
797aab3c
GM
120
121;; ------- Nothing to be modified by the user below this
122(defvar ada-last-prj-file ""
eec3232e 123 "Name of the last project file entered by the user.")
797aab3c
GM
124
125(defvar ada-check-switch " -gnats "
eec3232e 126 "Switch added to the command line to check the current file.")
797aab3c
GM
127
128(defvar ada-project-file-extension ".adp"
eec3232e 129 "The extension used for project files.")
797aab3c
GM
130
131(defconst is-windows (memq system-type (quote (windows-nt)))
eec3232e 132 "True if we are running on windows NT or windows 95.")
797aab3c
GM
133
134(defvar ada-xref-pos-ring '()
eec3232e
GM
135 "List of positions selected by the cross-references functions.
136Used to go back to these positions.")
797aab3c
GM
137
138(defconst ada-xref-pos-ring-max 16
eec3232e 139 "Number of positions kept in the list ada-xref-pos-ring.")
797aab3c
GM
140
141(defvar ada-operator-re
142 "\\+\\|-\\|/\\|\\*\\|=\\|mod\\|and\\|not\\|or\\|xor\\|<=\\|<\\|>=\\|>"
eec3232e 143 "Regexp to match for operators.")
797aab3c
GM
144
145(defvar ada-xref-default-prj-file nil
eec3232e
GM
146 "Name of the default prj file, per directory.
147Every directory is potentially associated with a default project file.
797aab3c 148If it is nil, then the first prj file loaded will be the default for this
eec3232e 149Emacs session.")
797aab3c
GM
150
151;; These variables will be overwritted by buffer-local variables
152(defvar ada-prj-prj-file nil
eec3232e 153 "Name of the project file for the current ada buffer.")
797aab3c 154(defvar ada-prj-src-dir nil
eec3232e 155 "List of directories to look into for ada sources.")
797aab3c 156(defvar ada-prj-obj-dir nil
eec3232e 157 "List of directories to look into for object and .ali files.")
797aab3c 158(defvar ada-prj-comp-opt nil
eec3232e 159 "Switches to use on the command line for the default compile command.")
797aab3c 160(defvar ada-prj-bind-opt nil
eec3232e 161 "Switches to use on the command line for the default bind command.")
797aab3c 162(defvar ada-prj-link-opt nil
eec3232e 163 "Switches to use on the command line for the default link command.")
797aab3c 164(defvar ada-prj-comp-cmd nil
eec3232e 165 "Command to use to compile the current file only.")
797aab3c 166(defvar ada-prj-make-cmd nil
eec3232e 167 "Command to use to compile the whole current application.")
797aab3c 168(defvar ada-prj-run-cmd nil
eec3232e 169 "Command to use to run the current application.")
797aab3c 170(defvar ada-prj-debug-cmd nil
eec3232e 171 "Command to use to run the debugger.")
797aab3c 172(defvar ada-prj-main nil
eec3232e 173 "Name of the main programm of the current application.")
797aab3c 174(defvar ada-prj-remote-machine nil
eec3232e 175 "Name of the machine to log on before a compilation.")
797aab3c
GM
176(defvar ada-prj-cross-prefix nil
177 "Prefix to be added to the gnatmake, gcc, ... commands when
178using a cross-compilation environment.
179A '-' is automatically added at the end if not already present.
eec3232e 180For instance, the compiler is called `ada-prj-cross-prefix'gnatmake.")
797aab3c
GM
181
182;; ----- Keybindings ------------------------------------------------------
183
184(defun ada-add-keymap ()
eec3232e 185 "Add new key bindings when using `ada-xrel.el'."
797aab3c
GM
186 (interactive)
187 (if ada-xemacs
188 (progn
189 (define-key ada-mode-map '(shift button3) 'ada-point-and-xref)
190 (define-key ada-mode-map '(control tab) 'ada-complete-identifier))
191 (define-key ada-mode-map [C-tab] 'ada-complete-identifier)
192 (define-key ada-mode-map [S-mouse-3] 'ada-point-and-xref))
193
194 (define-key ada-mode-map "\C-co" 'ff-find-other-file)
195 (define-key ada-mode-map "\C-c5\C-d" 'ada-goto-declaration-other-frame)
196 (define-key ada-mode-map "\C-c\C-d" 'ada-goto-declaration)
197 (define-key ada-mode-map "\C-c\C-s" 'ada-xref-goto-previous-reference)
198 (define-key ada-mode-map "\C-c\C-x" 'ada-reread-prj-file)
199 (define-key ada-mode-map [f10] 'next-error)
200 (define-key ada-mode-map "\C-c\C-c" 'ada-compile-application)
201 (define-key ada-mode-map "\C-cb" 'ada-buffer-list)
202 (define-key ada-mode-map "\C-cc" 'ada-change-prj)
203 (define-key ada-mode-map "\C-cd" 'ada-change-default-prj)
204 (define-key ada-mode-map "\C-cg" 'ada-gdb-application)
205 (define-key ada-mode-map "\C-cr" 'ada-run-application)
206 (define-key ada-mode-map "\C-c\C-o" 'ada-goto-parent)
207 (define-key ada-mode-map "\C-c\C-r" 'ada-find-references)
208 (define-key ada-mode-map "\C-c\C-v" 'ada-check-current)
209 )
210
211;; ----- Menus --------------------------------------------------------------
212(defun ada-add-ada-menu ()
eec3232e 213 "Add some items to the standard Ada mode menu."
797aab3c
GM
214 (interactive)
215
216 (if ada-xemacs
217 (progn
218 (add-menu-button '("Ada") ["Check file" ada-check-current t] "Goto")
eec3232e
GM
219 (add-menu-button '("Ada") ["Compile file" ada-compile-current t]
220 "Goto")
797aab3c
GM
221 (add-menu-button '("Ada") ["Build" ada-compile-application t] "Goto")
222 (add-menu-button '("Ada") ["Run" ada-run-application t] "Goto")
223 (add-menu-button '("Ada") ["Debug" ada-gdb-application t] "Goto")
224 (add-menu-button '("Ada") ["--" nil t] "Goto")
225 (add-submenu '("Ada") '("Project"
226 ["Associate" ada-change-prj t]
227 ["Set Default" ada-set-default-project-file t]
228 ["List" ada-buffer-list t])
229 "Goto")
230 (add-menu-button '("Ada" "Goto") ["Goto Parent Unit" ada-goto-parent t]
231 "Next compilation error")
eec3232e
GM
232 (add-menu-button '("Ada" "Goto") ["Goto References to any entity"
233 ada-find-any-references t]
797aab3c 234 "Next compilation error")
eec3232e
GM
235 (add-menu-button '("Ada" "Goto") ["List References"
236 ada-find-references t]
797aab3c
GM
237 "Next compilation error")
238 (add-menu-button '("Ada" "Goto") ["Goto Declaration Other Frame"
239 ada-goto-declaration-other-frame t]
240 "Next compilation error")
eec3232e
GM
241 (add-menu-button '("Ada" "Goto") ["Goto Declaration/Body"
242 ada-goto-declaration t]
797aab3c 243 "Next compilation error")
eec3232e
GM
244 (add-menu-button '("Ada" "Goto") ["Goto Previous Reference"
245 ada-xref-goto-previous-reference t]
797aab3c
GM
246 "Next compilation error")
247 (add-menu-button '("Ada" "Goto") ["--" nil t]
248 "Next compilation error")
eec3232e
GM
249 (add-menu-button '("Ada" "Edit") ["Complete Identifier"
250 ada-complete-identifier t]
797aab3c
GM
251 "Indent Line")
252 (add-menu-button '("Ada" "Edit") ["--------" nil t]
253 "Indent Line")
254 (add-menu-button '("Ada" "Help") ["Gnat User Guide" (info "gnat_ug")])
255 (add-menu-button '("Ada" "Help") ["Gnat Reference Manual"
256 (info "gnat_rm")])
257 (add-menu-button '("Ada" "Help") ["Gcc Documentation" (info "gcc")])
258 (add-menu-button '("Ada" "Help") ["Gdb Documentation" (info "gdb")])
259 )
260
261 ;; for Emacs
262 (define-key-after (lookup-key ada-mode-map [menu-bar Ada]) [Check]
263 '("Check file" . ada-check-current) 'Customize)
264 (define-key-after (lookup-key ada-mode-map [menu-bar Ada]) [Compile]
265 '("Compile file" . ada-compile-current) 'Check)
266 (define-key-after (lookup-key ada-mode-map [menu-bar Ada]) [Build]
267 '("Build" . ada-compile-application) 'Compile)
268 (define-key-after (lookup-key ada-mode-map [menu-bar Ada]) [Run]
269 '("Run" . ada-run-application) 'Build)
270 (define-key-after (lookup-key ada-mode-map [menu-bar Ada]) [Debug]
271 '("Debug" . ada-gdb-application) 'Run)
272 (define-key-after (lookup-key ada-mode-map [menu-bar Ada]) [rem]
273 '("--" . nil) 'Debug)
274 (define-key-after (lookup-key ada-mode-map [menu-bar Ada]) [Project]
275 (cons "Project" (easy-menu-create-menu
276 "Project"
277 '(["Associate" ada-change-prj t]
278 ["Set Default" ada-set-default-project-file t]
279 ["List" ada-buffer-list t])))
280 'rem)
281
282 (let ((help-submenu (lookup-key ada-mode-map [menu-bar Ada Help]))
283 (goto-submenu (lookup-key ada-mode-map [menu-bar Ada Goto]))
284 (edit-submenu (lookup-key ada-mode-map [menu-bar Ada Edit])))
285
286 (define-key help-submenu [Gnat_ug]
287 '("Gnat User Guide" . (lambda() (interactive) (info "gnat_ug"))))
288 (define-key help-submenu [Gnat_rm]
289 '("Gnat Reference Manual" . (lambda() (interactive) (info "gnat_rm"))))
290 (define-key help-submenu [Gcc]
291 '("Gcc Documentation" . (lambda() (interactive) (info "gcc"))))
292 (define-key help-submenu [gdb]
eec3232e
GM
293 '("Ada Aware Gdb Documentation" .
294 (lambda() (interactive) (info "gdb"))))
797aab3c 295 (define-key goto-submenu [rem] '("----" . nil))
eec3232e
GM
296 (define-key goto-submenu [Parent]
297 '("Goto Parent Unit" . ada-goto-parent))
797aab3c
GM
298 (define-key goto-submenu [References-any]
299 '("Goto References to any entity" . ada-find-any-references))
300 (define-key goto-submenu [References]
301 '("List References" . ada-find-references))
302 (define-key goto-submenu [Prev]
303 '("Goto Previous Reference" . ada-xref-goto-previous-reference))
304 (define-key goto-submenu [Decl-other]
305 '("Goto Declaration Other Frame" . ada-goto-declaration-other-frame))
306 (define-key goto-submenu [Decl]
307 '("Goto Declaration/Body" . ada-goto-declaration))
308
309 (define-key edit-submenu [rem] '("----" . nil))
310 (define-key edit-submenu [Complete] '("Complete Identifier"
311 . ada-complete-identifier))
312 )
313 ))
314
315;; ----- Utilities -------------------------------------------------
316
317(defun ada-require-project-file ()
eec3232e 318 "If no project file is assigned to this buffer, load one."
797aab3c
GM
319 (if (not ( my-local-variable-if-set-p 'ada-prj-src-dir (current-buffer)))
320 (ada-parse-prj-file (ada-prj-find-prj-file))))
321
322(defun my-local-variable-if-set-p (variable &optional buffer)
eec3232e 323 "Returns t if VARIABLE is local in BUFFER and is non-nil."
797aab3c
GM
324 (and (local-variable-p variable buffer)
325 (save-excursion
326 (set-buffer buffer)
327 (symbol-value variable))))
328
329(defun ada-xref-push-pos (filename position)
eec3232e 330 "Push (FILENAME, POSITION) on the position ring for cross-references."
797aab3c
GM
331 (setq ada-xref-pos-ring (cons (list position filename) ada-xref-pos-ring))
332 (if (> (length ada-xref-pos-ring) ada-xref-pos-ring-max)
333 (setcdr (nthcdr (1- ada-xref-pos-ring-max) ada-xref-pos-ring) nil)))
334
335(defun ada-xref-goto-previous-reference ()
eec3232e 336 "Go to the previous cross-reference we were on."
797aab3c
GM
337 (interactive)
338 (if ada-xref-pos-ring
eec3232e
GM
339 (let ((pos (car ada-xref-pos-ring)))
340 (setq ada-xref-pos-ring (cdr ada-xref-pos-ring))
341 (find-file (car (cdr pos)))
342 (goto-char (car pos)))))
797aab3c
GM
343
344(defun ada-convert-file-name (name)
eec3232e
GM
345 "Converts from NAME to a name that can be used by the compilation commands.
346This is overriden on VMS to convert from VMS filenames to Unix filenames."
797aab3c
GM
347 name)
348
349(defun ada-set-default-project-file (name)
eec3232e
GM
350 "Set the file whose name is NAME as the default project file."
351 (interactive "fProject file:")
797aab3c
GM
352 (set 'ada-prj-default-project-file name)
353 (ada-reread-prj-file t)
354 )
355
797aab3c 356(defun ada-replace-substring (cmd-string search-for replace-with)
eec3232e 357 "Replace all instances of SEARCH-FOR with REPLACE-WITH in CMD-STRING."
797aab3c
GM
358 (while (string-match search-for cmd-string)
359 (setq cmd-string (replace-match replace-with t t cmd-string)))
360 cmd-string)
361
362(defun ada-treat-cmd-string (cmd-string)
eec3232e
GM
363 "Replace meta-sequences like ${...} in CMD-STRING with the appropriate value.
364The current buffer must be the one where all local variable are defined (that
797aab3c 365is the ada source)"
797aab3c
GM
366 (if ( my-local-variable-if-set-p 'ada-prj-src-dir (current-buffer))
367 (if (string-match "\\(-[^-\$I]*I\\)\${src_dir}" cmd-string)
368 (progn
369 (let ((str-def (substring cmd-string (match-beginning 1)
370 (match-end 1))))
371 (setq cmd-string
372 (ada-replace-substring cmd-string
373 "\\(-[^-\$I]*I\\)\${src_dir}"
374 (mapconcat
375 (lambda (x) (concat str-def x))
376 ada-prj-src-dir " ")))))))
377 (if ( my-local-variable-if-set-p 'ada-prj-obj-dir (current-buffer))
378 (if (string-match "\\(-[^-\$O]*O\\)\${obj_dir}" cmd-string)
379 (progn
380 (let ((str-def (substring cmd-string (match-beginning 1)
381 (match-end 1))))
382 (setq cmd-string
383 (ada-replace-substring cmd-string
384 "\\(-[^-\$O]*O\\)\${obj_dir}"
385 (mapconcat
386 (lambda (x) (concat str-def x))
387 ada-prj-obj-dir
388 " ")))))))
389 (if ( my-local-variable-if-set-p 'ada-prj-remote-machine (current-buffer))
390 (setq cmd-string
391 (ada-replace-substring cmd-string "\${remote_machine}"
392 ada-prj-remote-machine)))
393 (if ( my-local-variable-if-set-p 'ada-prj-comp-opt (current-buffer))
394 (setq cmd-string
395 (ada-replace-substring cmd-string "\${comp_opt}"
396 ada-prj-comp-opt)))
397 (if ( my-local-variable-if-set-p 'ada-prj-bind-opt (current-buffer))
398 (setq cmd-string
399 (ada-replace-substring cmd-string "\${bind_opt}"
400 ada-prj-bind-opt)))
401 (if ( my-local-variable-if-set-p 'ada-prj-link-opt (current-buffer))
402 (setq cmd-string
403 (ada-replace-substring cmd-string "\${link_opt}"
404 ada-prj-link-opt)))
405 (if ( my-local-variable-if-set-p 'ada-prj-main (current-buffer))
406 (setq cmd-string
407 (ada-replace-substring cmd-string "\${main}"
408 ada-prj-main)))
409 (if ( my-local-variable-if-set-p 'ada-prj-cross-prefix (current-buffer))
410 (setq cmd-string
411 (ada-replace-substring cmd-string "\${cross_prefix}"
412 ada-prj-cross-prefix)))
413 cmd-string)
414
415
416(defun ada-prj-find-prj-file (&optional no-user-question)
eec3232e 417 "Find the prj file associated with the current buffer.
797aab3c
GM
418The rules are the following ones :
419- If the buffer is already associated with a prj file, use this one
420- else if there's a default prj file for the same directory use it
421- else if a prj file with the same filename exists, use it
422- else if there's only one prj file in the directory, use it
423- else if there are more than one prj file, ask the user
eec3232e 424- else if there is no prj file and NO-USER-QUESTION is nil, ask the user
797aab3c
GM
425 for the project file to use."
426 (let* ((current-file (buffer-file-name))
427 (first-choice (concat
428 (file-name-sans-extension current-file)
429 ada-project-file-extension))
430 (dir (file-name-directory current-file))
431
432 ;; on Emacs 20.2, directory-files does not work if
433 ;; parse-sexp-lookup-properties is set
434 (parse-sexp-lookup-properties nil)
435 (prj-files (directory-files
436 dir t
437 (concat ".*" (regexp-quote ada-project-file-extension) "$")))
438 (choice nil)
439 (default (assoc dir ada-xref-default-prj-file))
440 )
441
442 (cond
443
444 ((my-local-variable-if-set-p 'ada-prj-prj-file (current-buffer))
445 ada-prj-prj-file)
446
447 (default ;; directory default project file
448 (cdr default))
449
450 ;; global default project file
451 ((and ada-prj-default-project-file
452 (not (string= ada-prj-default-project-file "")))
453 ada-prj-default-project-file)
454
455 ((file-exists-p first-choice)
456 first-choice)
457
458 ((= (length prj-files) 1)
459 (car prj-files))
460
461 ((> (length prj-files) 1)
462 ;; more than one possible prj file => ask the user
463 (with-output-to-temp-buffer "*choice list*"
464 (princ "There are more than one possible project file. Which one should\n")
465 (princ "I use ?\n\n")
466 (princ " no. file name \n")
467 (princ " --- ------------------------\n")
468 (let ((counter 1))
469 (while (<= counter (length prj-files))
470 (princ (format " %2d) %s\n"
471 counter
472 (nth (1- counter) prj-files)))
473 (setq counter (1+ counter))
474 ) ; end of while
475 ) ; end of let
476 ) ; end of with-output-to ...
477 (setq choice nil)
478 (while (or
479 (not choice)
480 (not (integerp choice))
481 (< choice 1)
482 (> choice (length prj-files)))
483 (setq choice (string-to-int
484 (read-from-minibuffer "Enter No. of your choice: "
485 ))))
486 (nth (1- choice) prj-files))
487
488 ((= (length prj-files) 0)
489 ;; no project file found. Ask the user about it (the default value
490 ;; is the last one the user entered.
491 (if (or no-user-question (not ada-always-ask-project))
492 nil
493 (setq ada-last-prj-file
494 (read-file-name "project file:" nil ada-last-prj-file))
495 (if (string= ada-last-prj-file "") nil ada-last-prj-file))
496 )
497 )))
498
499
500(defun ada-parse-prj-file (prj-file)
eec3232e
GM
501 "Reads and parses the project file PRJ-FILE.
502Does nothing if PRJ-FILE was not found.
797aab3c
GM
503The current buffer should be the ada-file buffer"
504
505 (let ((tmp-src-dir nil)
506 (tmp-obj-dir nil)
507 (tmp-comp-opt nil)
508 (tmp-bind-opt nil)
509 (tmp-link-opt nil)
510 (tmp-main nil)
511 (tmp-comp-cmd nil)
512 (tmp-make-cmd nil)
513 (tmp-run-cmd nil)
514 (tmp-debug-cmd nil)
515 (tmp-remote-machine nil)
516 (tmp-cross-prefix nil)
517 (tmp-cd-cmd (if prj-file
518 (concat "cd " (file-name-directory prj-file) " && ")
519 (concat "cd " (file-name-directory (buffer-file-name (current-buffer))) " && ")))
520 (ada-buffer (current-buffer))
521 )
522 ;; tries to find a project file in the current directory
523 (if prj-file
524 (progn
525 (find-file prj-file)
526
527 ;; first look for the src_dir lines
528 (widen)
529 (goto-char (point-min))
530 (while
531 (re-search-forward "^src_dir=\\(.*\\)" nil t)
532 (progn
533 (setq tmp-src-dir (cons
534 (file-name-as-directory
535 (match-string 1))
536 tmp-src-dir
537 ))))
538 ;; then for the obj_dir lines
539 (goto-char (point-min))
540 (while (re-search-forward "^obj_dir=\\(.*\\)" nil t)
541 (setq tmp-obj-dir (cons
542 (file-name-as-directory
543 (match-string 1))
544 tmp-obj-dir
545 )))
546
547 ;; then for the options lines
548 (goto-char (point-min))
549 (if (re-search-forward "^comp_opt=\\(.*\\)" nil t)
550 (setq tmp-comp-opt (match-string 1)))
551 (goto-char (point-min))
552 (if (re-search-forward "^bind_opt=\\(.*\\)" nil t)
553 (setq tmp-bind-opt (match-string 1)))
554 (goto-char (point-min))
555 (if (re-search-forward "^link_opt=\\(.*\\)" nil t)
556 (setq tmp-link-opt (match-string 1)))
557 (goto-char (point-min))
558 (if (re-search-forward "^main=\\(.*\\)" nil t)
559 (setq tmp-main (match-string 1)))
560 (goto-char (point-min))
561 (if (re-search-forward "^comp_cmd=\\(.*\\)" nil t)
562 (setq tmp-comp-cmd (match-string 1)))
563 (goto-char (point-min))
564 (if (re-search-forward "^remote_machine=\\(.*\\)" nil t)
565 (setq tmp-remote-machine (match-string 1)))
566 (goto-char (point-min))
567 (if (re-search-forward "^cross_prefix=\\(.*\\)" nil t)
568 (setq tmp-cross-prefix (match-string 1)))
569 (goto-char (point-min))
570 (if (re-search-forward "^make_cmd=\\(.*\\)" nil t)
571 (setq tmp-make-cmd (match-string 1)))
572 (goto-char (point-min))
573 (if (re-search-forward "^run_cmd=\\(.*\\)" nil t)
574 (setq tmp-run-cmd (match-string 1)))
575 (goto-char (point-min))
576 (if (re-search-forward "^debug_cmd=\\(.*\\)" nil t)
577 (setq tmp-debug-cmd (match-string 1)))
578
579 ;; kills the project file buffer, and go back to the ada buffer
580 (kill-buffer nil)
581 (set-buffer ada-buffer)
582 ))
583
584 ;; creates local variables (with default values if needed)
585 (set (make-local-variable 'ada-prj-prj-file) prj-file)
586
587 (set (make-local-variable 'ada-prj-src-dir)
588 (if tmp-src-dir (reverse tmp-src-dir) '("./")))
589
590 (set (make-local-variable 'ada-prj-obj-dir)
591 (if tmp-obj-dir (reverse tmp-obj-dir) '("./")))
592
593 (set (make-local-variable 'ada-prj-comp-opt)
594 (if tmp-comp-opt tmp-comp-opt ""))
595
596 (set (make-local-variable 'ada-prj-bind-opt)
597 (if tmp-bind-opt tmp-bind-opt ""))
598
599 (set (make-local-variable 'ada-prj-link-opt)
600 (if tmp-link-opt tmp-link-opt ""))
601
602 (set (make-local-variable 'ada-prj-cross-prefix)
603 (if tmp-cross-prefix
604 (if (or (string= tmp-cross-prefix "")
605 (= (aref tmp-cross-prefix (1- (length tmp-cross-prefix))) ?-))
606 tmp-cross-prefix
607 (concat tmp-cross-prefix "-"))
608 ""))
609
610 (set (make-local-variable 'ada-prj-main)
611 (if tmp-main tmp-main
612 (substring (buffer-file-name) 0 -4)))
613
614 (set (make-local-variable 'ada-prj-remote-machine)
615 (ada-treat-cmd-string
616 (if tmp-remote-machine tmp-remote-machine "")))
617
618 (set (make-local-variable 'ada-prj-comp-cmd)
619 (ada-treat-cmd-string
620 (if tmp-comp-cmd tmp-comp-cmd
621 (concat tmp-cd-cmd ada-prj-default-comp-cmd))))
622
623 (set (make-local-variable 'ada-prj-make-cmd)
624 (ada-treat-cmd-string
625 (if tmp-make-cmd tmp-make-cmd
626 (concat tmp-cd-cmd ada-prj-default-make-cmd))))
627
628 (set (make-local-variable 'ada-prj-run-cmd)
629 (ada-treat-cmd-string
630 (if tmp-run-cmd tmp-run-cmd
631 (if is-windows "${main}.exe" "${main}"))))
632
633 (set (make-local-variable 'ada-prj-debug-cmd)
634 (ada-treat-cmd-string
635 (if tmp-debug-cmd tmp-debug-cmd
636 (if is-windows
637 "${cross_prefix}gdb ${main}.exe"
638 "${cross_prefix}gdb ${main}"))))
639
640 ;; Add each directory in src_dir to the default prj list
641 (if prj-file
642 (mapcar (lambda (x)
643 (if (not (assoc (expand-file-name x)
644 ada-xref-default-prj-file))
645 (setq ada-xref-default-prj-file
646 (cons (cons (expand-file-name x)
647 prj-file)
648 ada-xref-default-prj-file))))
649 ada-prj-src-dir))
650
651 ;; Add the directories to the search path for ff-find-other-file
652 ;; Do not add the '/' or '\' at the end
653 (set (make-local-variable 'ff-search-directories)
654 (append (mapcar 'directory-file-name ada-prj-src-dir)
655 ada-search-directories))
656
657 ;; Sets up the compilation-search-path so that Emacs is able to
658 ;; go to the source of the errors in a compilation buffer
659 (setq compilation-search-path ada-prj-src-dir)
660
661 ))
662
663
664(defun ada-find-references (&optional pos)
eec3232e
GM
665 "Find all references to the entity under POS.
666Calls gnatfind to find the references."
797aab3c
GM
667 (interactive "")
668 (unless pos
669 (set 'pos (point)))
670 (ada-require-project-file)
671
672 (let* ((identlist (ada-read-identifier pos))
673 (alifile (ada-get-ali-file-name (ada-file-of identlist))))
674
675 (set-buffer (get-file-buffer (ada-file-of identlist)))
676
677 ;; if the file is more recent than the executable
678 (if (or (buffer-modified-p (current-buffer))
679 (file-newer-than-file-p (ada-file-of identlist) alifile))
680 (ada-find-any-references (ada-name-of identlist)
681 (ada-file-of identlist)
682 nil nil)
683 (ada-find-any-references (ada-name-of identlist)
684 (ada-file-of identlist)
685 (ada-line-of identlist)
686 (ada-column-of identlist))))
687 )
688
689(defun ada-find-any-references (entity &optional file line column)
eec3232e
GM
690 "Search for references to any entity whose name is ENTITY.
691ENTITY was first found the location given by FILE, LINE and COLUMN."
797aab3c
GM
692 (interactive "sEntity name: ")
693 (ada-require-project-file)
694
695 (let* ((command (concat "gnatfind -rf " entity
696 (if file (concat ":" (file-name-nondirectory file)))
697 (if line (concat ":" line))
698 (if column (concat ":" column)))))
699
700 ;; If a project file is defined, use it
701 (if (my-local-variable-if-set-p 'ada-prj-prj-file (current-buffer))
702 (setq command (concat command " -p" ada-prj-prj-file)))
703
eec3232e 704 (compile-internal command "No more references" "gnatfind")
797aab3c
GM
705
706 ;; Hide the "Compilation" menu
707 (save-excursion
708 (set-buffer "*gnatfind*")
709 (local-unset-key [menu-bar compilation-menu]))
710 )
711 )
712
713(defun ada-buffer-list ()
eec3232e 714 "Display a buffer with all the Ada buffers and their associated project."
797aab3c
GM
715 (interactive)
716 (save-excursion
717 (set-buffer (get-buffer-create "*Buffer List*"))
718 (setq buffer-read-only nil)
719 (erase-buffer)
720 (setq standard-output (current-buffer))
721 (princ "The following line is a list showing the associations between
722directories and project file. It has the format : ((directory_1 . project_file1)
723(directory2 . project_file2)...)\n\n")
724 (princ ada-xref-default-prj-file)
725 (princ "\n
726 Buffer Mode Project file
727 ------ ---- ------------
728\n")
729 (let ((bl (buffer-list)))
730 (while bl
731 (let* ((buffer (car bl))
732 (buffer-name (buffer-name buffer))
733 this-buffer-mode-name
734 this-buffer-project-file)
735 (save-excursion
736 (set-buffer buffer)
737 (setq this-buffer-mode-name
738 (if (eq buffer standard-output)
739 "Buffer Menu" mode-name))
740 (if (string= this-buffer-mode-name
741 "Ada")
742 (setq this-buffer-project-file
743 (if ( my-local-variable-if-set-p 'ada-prj-prj-file
744 (current-buffer))
745 (expand-file-name ada-prj-prj-file)
746 ""))))
747 (if (string= this-buffer-mode-name
748 "Ada")
749 (progn
750 (princ (format "%-19s " buffer-name))
751 (princ (format "%-6s " this-buffer-mode-name))
752 (princ this-buffer-project-file)
753 (princ "\n")
754 ))
755 ) ;; end let*
756 (setq bl (cdr bl))
757 ) ;; end while
758 );; end let
759 ) ;; end save-excursion
760 (display-buffer "*Buffer List*")
761 (other-window 1)
762 )
763
764(defun ada-change-prj (filename)
eec3232e 765 "Set FILENAME to be the project file for current buffer."
797aab3c
GM
766 (interactive "fproject file:")
767
768 ;; make sure we are using an Ada file
769 (if (not (string= mode-name "Ada"))
770 (error "You must be in ada-mode to use this function"))
771
772 ;; create the local variable if necessay
773 (if (not ( my-local-variable-if-set-p 'ada-prj-prj-file (current-buffer)))
774 (make-local-variable 'ada-prj-prj-file))
775
776 ;; ask the user for the new file name
777 (setq ada-prj-prj-file filename)
778
eec3232e 779 ;; force Emacs to reread the prj file next-time
797aab3c
GM
780 (ada-reread-prj-file)
781 )
782
783(defun ada-change-default-prj (filename)
eec3232e 784 "Set FILENAME to be the default project file for the current directory."
797aab3c
GM
785 (interactive "ffile name:")
786 (let ((dir (file-name-directory (buffer-file-name)))
787 (prj (expand-file-name filename)))
788
eec3232e 789 ;; Associate the directory with a project file
797aab3c 790 (if (assoc dir ada-xref-default-prj-file)
797aab3c 791 (setcdr (assoc dir ada-xref-default-prj-file) prj)
797aab3c
GM
792 (add-to-list 'ada-xref-default-prj-file (list dir prj)))
793
794 ;; Reparse the project file
795 (ada-parse-prj-file ada-prj-default-project-file)))
796
797
798;; ----- Identlist manipulation -------------------------------------------
799;; An identlist is a vector that is used internally to reference an identifier
800;; To facilitate its use, we provide the following macros
801
802(defmacro ada-make-identlist () (make-vector 8 nil))
803(defmacro ada-name-of (identlist) (list 'aref identlist 0))
804(defmacro ada-line-of (identlist) (list 'aref identlist 1))
805(defmacro ada-column-of (identlist) (list 'aref identlist 2))
806(defmacro ada-file-of (identlist) (list 'aref identlist 3))
807(defmacro ada-ali-index-of (identlist) (list 'aref identlist 4))
808(defmacro ada-declare-file-of (identlist) (list 'aref identlist 5))
809(defmacro ada-references-of (identlist) (list 'aref identlist 6))
810(defmacro ada-on-declaration (identlist) (list 'aref identlist 7))
811
812(defmacro ada-set-name (identlist name) (list 'aset identlist 0 name))
813(defmacro ada-set-line (identlist line) (list 'aset identlist 1 line))
814(defmacro ada-set-column (identlist col) (list 'aset identlist 2 col))
815(defmacro ada-set-file (identlist file) (list 'aset identlist 3 file))
816(defmacro ada-set-ali-index (identlist index) (list 'aset identlist 4 index))
817(defmacro ada-set-declare-file (identlist file) (list 'aset identlist 5 file))
818(defmacro ada-set-references (identlist ref) (list 'aset identlist 6 ref))
819(defmacro ada-set-on-declaration (ident value) (list 'aset ident 7 value))
820
821(defsubst ada-get-ali-buffer (file)
822 "Reads the ali file into a new buffer, and returns this buffer's name"
823 (find-file-noselect (ada-get-ali-file-name file)))
824
825
826
827;; ----- Identifier Completion --------------------------------------------
828(defun ada-complete-identifier (pos)
829 "Tries to complete the identifier around POS.
eec3232e
GM
830The feature is only available if the files where not compiled using the -gnatx
831option."
797aab3c
GM
832 (interactive "d")
833 (ada-require-project-file)
834
835 ;; Initialize function-local variablesand jump to the .ali buffer
836 ;; Note that for regexp search is case insensitive too
837 (let* ((curbuf (current-buffer))
838 (identlist (ada-read-identifier pos))
839 (sofar (concat "^[0-9]+[a-zA-Z][0-9]+[ *]\\("
840 (regexp-quote (ada-name-of identlist))
841 "[a-zA-Z0-9_]*\\)"))
842 (completed nil)
843 (symalist nil)
844 (insertpos nil))
845
846 ;; we are already in the .ali buffer
847 (goto-char (point-max))
848
849 ;; build an alist of possible completions
850 (while (re-search-backward sofar nil t)
851 (setq symalist (cons (cons (match-string 1) nil) symalist)))
852
853 (setq completed (try-completion "" symalist))
854
855 ;; kills .ali buffer
856 (kill-buffer nil)
857
858 ;; deletes the incomplete identifier in the buffer
859 (set-buffer curbuf)
860 (looking-at "[a-zA-Z0-9_]+")
861 (replace-match "")
862 ;; inserts the completed symbol
863 (insert completed)
864 ))
865
866;; ----- Cross-referencing ----------------------------------------
867
868(defun ada-point-and-xref ()
869 "Calls `mouse-set-point' and then `ada-goto-declaration'."
870 (interactive)
871 (mouse-set-point last-input-event)
872 (ada-goto-declaration (point)))
873
874(defun ada-goto-declaration (pos)
eec3232e
GM
875 "Display the declaration of the identifier around POS.
876The declaration is shown in another buffer if `ada-xref-other-buffer' is
877non-nil."
797aab3c
GM
878 (interactive "d")
879 (ada-require-project-file)
880 (push-mark pos)
881 (ada-xref-push-pos (buffer-file-name) pos)
882 (ada-find-in-ali (ada-read-identifier pos)))
883
884(defun ada-goto-declaration-other-frame (pos)
eec3232e
GM
885 "Display the declaration of the identifier around POS.
886The declation is shown in another frame if `ada-xref-other-buffer' is non-nil."
797aab3c
GM
887 (interactive "d")
888 (ada-require-project-file)
889 (push-mark pos)
890 (ada-xref-push-pos (buffer-file-name) pos)
891 (ada-find-in-ali (ada-read-identifier pos) t))
892
893(defun ada-compile (command)
eec3232e 894 "Start COMMAND on the machine specified in the project file."
797aab3c
GM
895 (if (and (my-local-variable-if-set-p 'ada-prj-remote-machine (current-buffer))
896 (not (string= ada-prj-remote-machine "")))
897 (set 'command
898 (concat "rsh " ada-prj-remote-machine " '"
899 command "'")))
900 (compile command))
901
902(defun ada-compile-application ()
eec3232e 903 "Compiles the application, using the command found in the project file."
797aab3c
GM
904 (interactive)
905 (ada-require-project-file)
906
907 ;; prompt for command to execute
908 (ada-compile
909 (if ada-xref-confirm-compile
910 (read-from-minibuffer "enter command to compile: "
911 ada-prj-make-cmd)
912 ada-prj-make-cmd))
913 )
914
915(defun ada-compile-current ()
eec3232e 916 "Recompile the current file."
797aab3c
GM
917 (interactive)
918 (ada-require-project-file)
919
920 ;; prompt for command to execute
921 (ada-compile
922 (if ada-xref-confirm-compile
923 (read-from-minibuffer "enter command to compile: "
924 (concat
925 ada-prj-comp-cmd " " (ada-convert-file-name (buffer-file-name))))
926 (concat ada-prj-comp-cmd " " (ada-convert-file-name (buffer-file-name)))))
927 )
928
929(defun ada-check-current ()
eec3232e 930 "Recompile the current file."
797aab3c
GM
931 (interactive)
932 (ada-require-project-file)
933
934 ;; prompt for command to execute
935 (let ((command (concat ada-prj-comp-cmd ada-check-switch
936 (ada-convert-file-name (buffer-file-name)))))
937 (compile
938 (if ada-xref-confirm-compile
939 (read-from-minibuffer "enter command to compile: " command)
940 command))))
941
942
943(defun ada-run-application ()
eec3232e 944 "Run the application."
797aab3c
GM
945 (interactive)
946 (ada-require-project-file)
947
948 (if (and (my-local-variable-if-set-p 'ada-prj-cross-prefix (current-buffer))
949 (not (string= ada-prj-cross-prefix "")))
950 (error "This feature is not supported yet for cross-compilation environments"))
951
952 (let ((command ada-prj-run-cmd)
953 (buffer (current-buffer)))
954 ;; Search the command name if necessary
955 (if (not (my-local-variable-if-set-p 'ada-prj-run-cmd (current-buffer)))
956 (setq command (file-name-sans-extension (buffer-name)))
957 )
958
959 ;; Ask for the arguments to the command
960 (setq command
961 (read-from-minibuffer "Enter command to execute: "
962 command))
963
964 ;; Run the command
965 (save-excursion
966 (set-buffer (get-buffer-create "*run*"))
967 (goto-char (point-max))
968 (insert "\nRunning " command "\n\n")
969 (make-comint "run"
970 (comint-arguments command 0 0)
971 nil
972 (comint-arguments command 1 nil))
973 )
974 (display-buffer "*run*")
975
976 ;; change to buffer *run* for interactive programs
977 (other-window 1)
978 (switch-to-buffer "*run*")
979 )
980 )
981
982
983(defun ada-gdb-application ()
eec3232e 984 "Start the debugger on the application."
797aab3c
GM
985 (interactive)
986
987 (require 'gud)
988 (let ((buffer (current-buffer))
989 gdb-buffer)
990 (ada-require-project-file)
991
992 (if (and (my-local-variable-if-set-p 'ada-prj-cross-prefix buffer)
993 (not (string= ada-prj-cross-prefix "")))
994 (error "This feature is not supported yet for cross-compilation environments"))
995
996 ;; If the command to use was given in the project file
997 (if (my-local-variable-if-set-p 'ada-prj-debug-cmd buffer)
998 (gdb ada-prj-debug-cmd)
999 ;; Else the user will have to enter the command himself
1000 (gdb "")
1001 )
1002
1003 (set 'gdb-buffer (current-buffer))
1004
1005 ;; Switch back to the source buffer
1006 ;; and Activate the debug part in the contextual menu
1007 (switch-to-buffer buffer)
1008
1009 (if (functionp 'gud-make-debug-menu)
1010 (gud-make-debug-menu))
1011
1012 ;; Warning: on Emacs >= 20.3.8, same-window-regexps includes gud-*,
1013 ;; so the following call to display buffer will select the
1014 ;; buffer instead of displaying it in another window
1015 ;; This is why the second argument to display-buffer is 't'
1016 (display-buffer gdb-buffer t)
1017 ))
1018
1019
1020(defun ada-reread-prj-file (&optional for-all-buffer)
eec3232e 1021 "Forces Emacs to read the project file again.
797aab3c 1022Otherwise, this file is only read once, and never read again
eec3232e
GM
1023If FOR-ALL-BUFFER is non-nil, or the function was called with \C-u prefix,
1024then do this for every opened buffer."
797aab3c
GM
1025 (interactive "P")
1026 (if for-all-buffer
1027
1028 ;; do this for every buffer
1029 (mapcar (lambda (x)
1030 (save-excursion
1031 (set-buffer x)
1032 ;; if we have the ada-mode and there is a real file
1033 ;; associated with the buffer
1034 (if (and (string= mode-name "Ada")
1035 (buffer-file-name))
1036 (progn
1037 (kill-local-variable 'ada-prj-src-dir)
1038 (kill-local-variable 'ada-prj-obj-dir)
1039 (ada-parse-prj-file (ada-prj-find-prj-file))))
1040 ))
1041 (buffer-list))
1042
1043 ;; else do this just for the current buffer
1044 (kill-local-variable 'ada-prj-src-dir)
1045 (kill-local-variable 'ada-prj-obj-dir)
1046 (ada-parse-prj-file (ada-prj-find-prj-file)))
1047 )
1048
1049;; ------ Private routines
1050
1051(defun ada-xref-current (file &optional ali-file-name)
eec3232e
GM
1052 "Update the cross-references for FILE.
1053This in fact recompiles FILE to create ALI-FILE-NAME."
797aab3c
GM
1054 ;; kill old buffer
1055 (if (and ali-file-name
1056 (get-file-buffer ali-file-name))
1057 (kill-buffer (get-file-buffer ali-file-name)))
1058 ;; prompt for command to execute
1059 (setq compile-command (concat ada-prj-comp-cmd
1060 " "
1061 file))
1062 (compile
1063 (if ada-xref-confirm-compile
1064 (read-from-minibuffer "enter command to execute gcc: "
1065 compile-command)
1066 compile-command))
1067 )
1068
1069(defun ada-first-non-nil (list)
eec3232e 1070 "Returns the first non-nil element of the LIST"
797aab3c
GM
1071 (cond
1072 ((not list) nil)
1073 ((car list) (car list))
1074 (t (ada-first-non-nil (cdr list)))
1075 ))
1076
1077
1078(defun ada-find-ali-file-in-dir (file)
eec3232e
GM
1079 "Search for FILE in obj_dir.
1080The current buffer must be the Ada file."
797aab3c
GM
1081 (ada-first-non-nil
1082 (mapcar (lambda (x)
1083 (if (file-exists-p (concat (file-name-directory x)
1084 file))
1085 (concat (file-name-directory x) file)
1086 nil))
1087 ada-prj-obj-dir))
1088 )
1089
1090(defun ada-get-ali-file-name (file)
eec3232e
GM
1091 "Create the ali file name for the ada-file FILE.
1092The file is searched for in every directory shown in the obj_dir lines of
1093the project file."
797aab3c
GM
1094
1095 ;; This function has to handle the special case of non-standard
1096 ;; file names (i.e. not .adb or .ads)
1097 ;; The trick is the following:
1098 ;; 1- replace the extension of the current file with .ali,
1099 ;; and look for this file
1100 ;; 2- If this file is found:
1101 ;; grep the "^U" lines, and make sure we are not reading the
1102 ;; .ali file for a spec file. If we are, go to step 3.
1103 ;; 3- If the file is not found or step 2 failed:
1104 ;; find the name of the "other file", ie the body, and look
1105 ;; for its associated .ali file by subtituing the extension
1106
1107 (save-excursion
1108 (set-buffer (get-file-buffer file))
1109 (let ((short-ali-file-name
1110 (concat (file-name-sans-extension (file-name-nondirectory file))
1111 ".ali"))
1112 (ali-file-name ""))
1113 ;; First step
1114 ;; we take the first possible completion
1115 (setq ali-file-name (ada-find-ali-file-in-dir short-ali-file-name))
1116
1117 ;; If we have found the .ali file, but the source file was a spec
1118 ;; with a non-standard name, search the .ali file for the body if any,
1119 ;; since the xref information is more complete in that one
1120 (unless ali-file-name
1121 (if (not (string= (file-name-extension file) ".ads"))
1122 (let ((is-spec nil)
1123 (specs ada-spec-suffixes)
1124 body-ali)
1125 (while specs
1126 (if (string-match (concat (regexp-quote (car specs)) "$")
1127 file)
1128 (set 'is-spec t))
1129 (set 'specs (cdr specs)))
1130
1131 (if is-spec
1132 (set 'body-ali
1133 (ada-find-ali-file-in-dir
1134 (concat (file-name-sans-extension
1135 (file-name-nondirectory
1136 (ada-other-file-name)))
1137 ".ali"))))
1138 (if body-ali
1139 (set 'ali-file-name body-ali))))
1140
1141 ;; else we did not find the .ali file
1142 ;; Second chance: in case the files do not have standard names (such
1143 ;; as for instance file_s.ada and file_b.ada), try to go to the
1144 ;; other file and look for its ali file
1145 (setq short-ali-file-name
1146 (concat (file-name-sans-extension
1147 (file-name-nondirectory (ada-other-file-name)))
1148 ".ali"))
1149 (setq ali-file-name (ada-find-ali-file-in-dir short-ali-file-name))
1150
1151 ;; If still not found, try to recompile the file
1152 (if (not ali-file-name)
1153 (progn
1154 ;; recompile only if the user asked for this
1155 (if ada-xref-create-ali
1156 (ada-xref-current file ali-file-name))
1157 (error "Ali file not found. Recompile your file")))
1158 )
1159
1160 ;; same if the .ali file is too old and we must recompile it
1161 (if (and (file-newer-than-file-p file ali-file-name)
1162 ada-xref-create-ali)
1163 (ada-xref-current file ali-file-name))
1164
1165 ;; else returns the correct absolute file name
1166 (expand-file-name ali-file-name))
1167 ))
1168
1169(defun ada-get-ada-file-name (file original-file)
eec3232e
GM
1170 "Create the complete file name (+directory) for FILE.
1171The original file (where the user was) is ORIGINAL-FILE. Search in project
1172file for possible paths."
797aab3c
GM
1173
1174 (save-excursion
1175 (set-buffer (get-file-buffer original-file))
1176 ;; we choose the first possible completion and we
1177 ;; return the absolute file name
1178 (let ((filename
1179 (ada-first-non-nil (mapcar (lambda (x)
1180 (if (file-exists-p (concat (file-name-directory x)
1181 (file-name-nondirectory file)))
1182 (concat (file-name-directory x)
1183 (file-name-nondirectory file))
1184 nil))
1185 ada-prj-src-dir))))
1186
1187 (if filename
1188 (expand-file-name filename)
1189 (error (concat
1190 (file-name-nondirectory file)
1191 " not found in src_dir. Please check your project file")))
1192
1193 )))
1194
1195(defun ada-find-file-number-in-ali (file)
eec3232e 1196 "Returns the file number for FILE in the associated ali file."
797aab3c
GM
1197 (set-buffer (ada-get-ali-buffer file))
1198 (goto-char (point-min))
1199
1200 (let ((begin (re-search-forward "^D")))
1201 (beginning-of-line)
1202 (re-search-forward (concat "^D " (file-name-nondirectory file)))
1203 (count-lines begin (point))))
1204
1205(defun ada-read-identifier (pos)
eec3232e 1206 "Returns the identlist around POS and switch to the .ali buffer."
797aab3c
GM
1207
1208 ;; If there's a compilation in progress, it's probably because the
1209 ;; .ali file didn't exist. So we should wait...
1210 (if compilation-in-progress
1211 (progn
1212 (message "Compilation in progress. Try again when it is finished")
1213 (set 'quit-flag t)))
1214
1215 ;; If at end of buffer (e.g the buffer is empty), error
1216 (if (>= (point) (point-max))
1217 (error "No identifier on point"))
1218
1219 ;; goto first character of the identifier/operator (skip backward < and >
1220 ;; since they are part of multiple character operators
1221 (goto-char pos)
1222 (skip-chars-backward "a-zA-Z0-9_<>")
1223
1224 ;; check if it really is an identifier
1225 (if (ada-in-comment-p)
1226 (error "Inside comment"))
1227
1228 (let (identifier identlist)
1229 ;; Just in front of a string => we could have an operator declaration,
1230 ;; as in "+", "-", ..
1231 (if (= (char-after) ?\")
1232 (forward-char 1))
1233
1234 ;; if looking at an operator
1235 (if (looking-at ada-operator-re)
1236 (progn
1237 (if (and (= (char-before) ?\")
1238 (= (char-after (+ (length (match-string 0)) (point))) ?\"))
1239 (forward-char -1))
1240 (set 'identifier (concat "\"" (match-string 0) "\"")))
1241
1242 (if (ada-in-string-p)
1243 (error "Inside string or character constant"))
1244 (if (looking-at (concat ada-keywords "[^a-zA-Z_]"))
1245 (error "No cross-reference available for reserved keyword"))
1246 (if (looking-at "[a-zA-Z0-9_]+")
1247 (set 'identifier (match-string 0))
1248 (error "No identifier around")))
1249
1250 ;; Build the identlist
1251 (set 'identlist (ada-make-identlist))
1252 (ada-set-name identlist (downcase identifier))
1253 (ada-set-line identlist
1254 (number-to-string (count-lines (point-min) (point))))
1255 (ada-set-column identlist
1256 (number-to-string (1+ (current-column))))
1257 (ada-set-file identlist (buffer-file-name))
1258 identlist
1259 ))
1260
1261(defun ada-get-all-references (identlist)
eec3232e
GM
1262 "Completes and returns the IDENTLIST with the information extracted
1263from the ali file (definition file and places where it is referenced)."
797aab3c
GM
1264
1265 (let ((ali-buffer (ada-get-ali-buffer (ada-file-of identlist)))
1266 declaration-found)
1267 (set-buffer ali-buffer)
1268 (goto-char (point-min))
1269 (ada-set-on-declaration identlist nil)
1270
1271 ;; First attempt: we might already be on the declaration of the identifier
1272 ;; We want to look for the declaration only in a definite interval (after
1273 ;; the "^X ..." line for the current file, and before the next "^X" line
1274
1275 (if (re-search-forward
1276 (concat "^X [0-9]+ " (file-name-nondirectory (ada-file-of identlist)))
1277 nil t)
1278 (let ((bound (save-excursion (re-search-forward "^X " nil t))))
1279 (set 'declaration-found
1280 (re-search-forward
1281 (concat "^" (ada-line-of identlist)
1282 "." (ada-column-of identlist)
1283 "[ *]" (regexp-quote (ada-name-of identlist))
1284 " \\(.*\\)$") bound t))
1285 (if declaration-found
1286 (ada-set-on-declaration identlist t))
1287 ))
1288
1289 ;; If declaration is still nil, then we were not on a declaration, and
1290 ;; have to fall back on other algorithms
1291
1292 (unless declaration-found
1293
1294 ;; Since we alread know the number of the file, search for a direct
1295 ;; reference to it
1296 (goto-char (point-min))
1297 (set 'declaration-found t)
1298 (ada-set-ali-index
1299 identlist
1300 (number-to-string (ada-find-file-number-in-ali
1301 (ada-file-of identlist))))
1302 (unless (re-search-forward (concat (ada-ali-index-of identlist)
1303 "|\\([0-9]+.[0-9]+ \\)*"
1304 (ada-line-of identlist)
1305 "[^0-9]"
1306 (ada-column-of identlist))
1307 nil t)
1308
1309 ;; if we did not find it, it may be because the first reference
1310 ;; is not required to have a 'unit_number|' item included.
1311 ;; Or maybe we are already on the declaration...
1312 (unless (re-search-forward (concat "^\\([a-zA-Z0-9_.\"]+[ *]\\)*"
1313 (ada-line-of identlist)
1314 "[^0-9]"
1315 (ada-column-of identlist))
1316 nil t)
1317
1318 ;; If still not found, then either the declaration is unknown
1319 ;; or the source file has been modified since the ali file was
1320 ;; created
1321 (set 'declaration-found nil)
1322 )
1323 )
1324
1325 ;; Last check to be completly sure we have found the correct line (the
1326 ;; ali might not be up to date for instance)
1327 (if declaration-found
1328 (progn
1329 (beginning-of-line)
1330 ;; while we have a continuation line, go up one line
1331 (while (looking-at "^\\.")
1332 (previous-line 1))
1333 (unless (looking-at (concat "[0-9]+.[0-9]+[ *]"
1334 (ada-name-of identlist) " "))
1335 (set 'declaration-found nil))))
1336
1337 ;; Still no success ! The ali file must be too old, and we need to
1338 ;; use a basic algorithm based on guesses. Note that this only happens
1339 ;; if the user does not want us to automatically recompile files
1340 ;; automatically
1341 (unless declaration-found
1342 (unless (ada-xref-find-in-modified-ali identlist)
1343 ;; no more idea to find the declaration. Give up
1344 (progn
1345 (kill-buffer ali-buffer)
1346 (error (concat "No declaration of " (ada-name-of identlist)
1347 " found."))
1348 )))
1349 )
1350
1351
1352 ;; Now that we have found a suitable line in the .ali file, get the
1353 ;; information available
1354 (beginning-of-line)
1355 (if declaration-found
1356 (let ((current-line (buffer-substring
1357 (point) (save-excursion (end-of-line) (point)))))
1358 (save-excursion
1359 (next-line 1)
1360 (beginning-of-line)
1361 (while (looking-at "^\\.\\(.*\\)")
1362 (set 'current-line (concat current-line (match-string 1)))
1363 (next-line 1))
1364 )
1365
1366 (if (re-search-backward "^X [0-9]+ \\([a-zA-Z0-9_.-]+\\)" nil t)
1367 (ada-set-declare-file
1368 identlist
1369 (ada-get-ada-file-name (match-string 1)
1370 (ada-file-of identlist))))
1371
1372 (ada-set-references identlist current-line)
1373 ))
1374 ))
1375
1376(defun ada-xref-find-in-modified-ali (identlist)
1377 "Find the matching position for IDENTLIST in the current ali buffer.
1378This function is only called when the file was not up-to-date, so we need
1379to make some guesses.
eec3232e 1380This function is disabled for operators, and only works for identifiers."
797aab3c
GM
1381
1382 (unless (= (string-to-char (ada-name-of identlist)) ?\")
1383 (progn
1384 (let ((declist '()) ;;; ( (line_in_ali_file line_in_ada) ( ... ))
1385 (my-regexp (concat "[ *]"
1386 (regexp-quote (ada-name-of identlist)) " "))
1387 (line-ada "--")
1388 (col-ada "--")
1389 (line-ali 0)
1390 (len 0)
1391 (choice 0))
1392
1393 (goto-char (point-max))
1394 (while (re-search-backward my-regexp nil t)
1395 (save-excursion
1396 (set 'line-ali (count-lines (point-min) (point)))
1397 (beginning-of-line)
1398 ;; have a look at the line and column numbers
1399 (if (looking-at "^\\([0-9]+\\).\\([0-9]+\\)[ *]")
1400 (progn
1401 (setq line-ada (match-string 1))
1402 (setq col-ada (match-string 2)))
1403 (setq line-ada "--")
1404 (setq col-ada "--")
1405 )
1406 ;; construct a list with the file names and the positions within
1407 (if (re-search-backward "^X [0-9]+ \\([a-zA-Z0-9._-]+\\)" nil t)
1408 (add-to-list
1409 'declist (list line-ali (match-string 1) line-ada col-ada))
1410 )
1411 )
1412 )
1413
1414 ;; how many possible declarations have we found ?
1415 (setq len (length declist))
1416 (cond
1417 ;; none => error
1418 ((= len 0)
1419 (kill-buffer (current-buffer))
1420 (error (concat "No declaration of "
1421 (ada-name-of identlist)
1422 " recorded in .ali file")))
1423
1424 ;; one => should be the right one
1425 ((= len 1)
1426 (goto-line (caar declist)))
1427
1428 ;; more than one => display choice list
1429 (t
1430 (with-output-to-temp-buffer "*choice list*"
1431
1432 (princ "Identifier is overloaded and Xref information is not up to date.\n")
1433 (princ "Possible declarations are:\n\n")
1434 (princ " no. in file at line col\n")
1435 (princ " --- --------------------- ---- ----\n")
1436 (let ((counter 1))
1437 (while (<= counter len)
1438 (princ (format " %2d) %-21s %4s %4s\n"
1439 counter
1440 (ada-get-ada-file-name
1441 (nth 1 (nth (1- counter) declist))
1442 (ada-file-of identlist))
1443 (nth 2 (nth (1- counter) declist))
1444 (nth 3 (nth (1- counter) declist))
1445 ))
1446 (setq counter (1+ counter))
1447 ) ; end of while
1448 ) ; end of let
1449 ) ; end of with-output-to ...
1450 (setq choice nil)
1451 (while (or
1452 (not choice)
1453 (not (integerp choice))
1454 (< choice 1)
1455 (> choice len))
1456 (setq choice (string-to-int
1457 (read-from-minibuffer "Enter No. of your choice: "))))
1458 (goto-line (car (nth (1- choice) declist)))
1459 ))))))
1460
1461
1462(defun ada-find-in-ali (identlist &optional other-frame)
eec3232e
GM
1463 "Look in the .ali file for the definition of the identifier in IDENTLIST.
1464If OTHER-FRAME is non nil, and `ada-xref-other-buffer' is non nil,
1465opens a new window to show the declaration."
797aab3c
GM
1466
1467 (ada-get-all-references identlist)
1468 (let ((ali-line (ada-references-of identlist))
1469 file line col)
1470
1471 ;; If we were on a declaration, go to the body
1472 (if (ada-on-declaration identlist)
1473 (if (string-match "\\([0-9]+\\)[bc]\\([0-9]+\\)" ali-line)
1474 (progn
1475 (setq line (match-string 1 ali-line)
1476 col (match-string 2 ali-line))
1477 ;; it there was a file number in the same line
1478 (if (string-match "\\([0-9]+\\)|\\([^|bc]+\\)?[bc]" ali-line)
1479 (let ((file-number (match-string 1 ali-line)))
1480 (goto-char (point-min))
1481 (re-search-forward "^D \\([a-zA-Z0-9_.-]+\\)" nil t
1482 (string-to-number file-number))
1483 (set 'file (match-string 1))
1484 )
1485 ;; Else get the nearest file
1486 (re-search-backward "^X [0-9]+ \\([a-zA-Z0-9_.-]+\\)" nil t)
1487 (set 'file (match-string 1))
1488 )
1489 )
1490 (error "No body found"))
1491
1492 ;; Else we were not on the declaration, find the place for it
1493 (string-match "\\([0-9]+\\)[a-zA-Z+]\\([0-9]+\\)[ *]" ali-line)
1494 (setq line (match-string 1 ali-line)
1495 col (match-string 2 ali-line)
1496 file (ada-declare-file-of identlist))
1497 )
1498
1499 ;; Now go to the buffer
1500 (ada-xref-change-buffer
1501 (ada-get-ada-file-name file (ada-file-of identlist))
1502 (string-to-number line)
1503 (1- (string-to-number col))
1504 identlist
1505 other-frame)
1506 ))
1507
1508(defun ada-xref-change-buffer
1509 (file line column identlist &optional other-frame)
1510 "Select and display FILE, at LINE and COLUMN. The new file is
1511associated with the same project file as the one for IDENTLIST.
1512If we do not end on the same identifier as IDENTLIST, find the closest
eec3232e
GM
1513match. Kills the .ali buffer at the end.
1514If OTHER-FRAME is non-nil, creates a new frame to show the file."
797aab3c
GM
1515
1516 (let (prj-file
1517 declaration-buffer
1518 (ali-buffer (current-buffer)))
1519
1520 ;; get the current project file for the source ada file
1521 (save-excursion
1522 (set-buffer (get-file-buffer (ada-file-of identlist)))
1523 (set 'prj-file ada-prj-prj-file))
1524
1525 ;; Select and display the destination buffer
1526 (if ada-xref-other-buffer
1527 (if other-frame
1528 (find-file-other-frame file)
1529 (set 'declaration-buffer (find-file-noselect file))
1530 (set-buffer declaration-buffer)
1531 (switch-to-buffer-other-window declaration-buffer)
1532 )
1533 (find-file file)
1534 )
1535
1536 ;; If the new buffer is not already associated with a project file, do it
1537 (unless (my-local-variable-if-set-p 'ada-prj-prj-file (current-buffer))
1538 (progn
1539 (make-local-variable 'ada-prj-prj-file)
1540 (set 'ada-prj-prj-file prj-file)))
1541
1542 ;; move the cursor to the correct position
1543 (push-mark)
1544 (goto-line line)
1545 (move-to-column column)
1546
1547 ;; If we are not on the identifier, the ali file was not up-to-date.
1548 ;; Try to find the nearest position where the identifier is found,
1549 ;; this is probably the right one.
1550 (unless (looking-at (ada-name-of identlist))
1551 (ada-xref-search-nearest (ada-name-of identlist)))
1552
1553 (kill-buffer ali-buffer)))
1554
1555
1556(defun ada-xref-search-nearest (name)
1557 "Searches for NAME nearest to the position recorded in the Xref file.
1558It returns the position of the declaration in the buffer or nil if not found."
1559 (let ((orgpos (point))
1560 (newpos nil)
1561 (diff nil))
1562
1563 (goto-char (point-max))
1564
1565 ;; loop - look for all declarations of name in this file
1566 (while (search-backward name nil t)
1567
1568 ;; check if it really is a complete Ada identifier
1569 (if (and
1570 (not (save-excursion
1571 (goto-char (match-end 0))
1572 (looking-at "_")))
1573 (not (ada-in-string-or-comment-p))
1574 (or
1575 ;; variable declaration ?
1576 (save-excursion
1577 (skip-chars-forward "a-zA-Z_0-9" )
1578 (ada-goto-next-non-ws)
1579 (looking-at ":[^=]"))
1580 ;; procedure, function, task or package declaration ?
1581 (save-excursion
1582 (ada-goto-previous-word)
1583 (looking-at "\\<[pP][rR][oO][cC][eE][dD][uU][rR][eE]\\>\\|\\<[fF][uU][nN][cC][tT][iI][oO][nN]\\>\\|\\<[tT][yY][pP][eE]\\>\\|\\<[tT][aA][sS][kK]\\>\\|\\<[pP][aA][cC][kK][aA][gG][eE]\\>\\|\\<[bB][oO][dD][yY]\\>"))))
1584
1585 ;; check if it is nearer than the ones before if any
1586 (if (or (not diff)
1587 (< (abs (- (point) orgpos)) diff))
1588 (progn
1589 (setq newpos (point)
1590 diff (abs (- newpos orgpos))))))
1591 )
1592
1593 (if newpos
1594 (progn
1595 (message "ATTENTION: this declaration is only a (good) guess ...")
1596 (goto-char newpos))
1597 nil)))
1598
1599
1600;; Find the parent library file of the current file
1601(defun ada-goto-parent ()
eec3232e 1602 "Go to the parent library file."
797aab3c
GM
1603 (interactive)
1604 (ada-require-project-file)
1605
1606 (let ((buffer (ada-get-ali-buffer (buffer-file-name)))
1607 (unit-name nil)
1608 (body-name nil)
1609 (ali-name nil))
1610 (save-excursion
1611 (set-buffer buffer)
1612 (goto-char (point-min))
1613 (re-search-forward "^U \\([^ \t%]+\\)%[bs][ \t]+\\([^ \t]+\\)")
1614 (setq unit-name (match-string 1))
1615 (if (not (string-match "\\(.*\\)\\.[^.]+" unit-name))
1616 (progn
1617 (kill-buffer buffer)
1618 (error "No parent unit !"))
1619 (setq unit-name (match-string 1 unit-name))
1620 )
1621
1622 ;; look for the file name for the parent unit specification
1623 (goto-char (point-min))
1624 (re-search-forward (concat "^W " unit-name
1625 "%s[ \t]+\\([^ \t]+\\)[ \t]+"
1626 "\\([^ \t\n]+\\)"))
1627 (setq body-name (match-string 1))
1628 (setq ali-name (match-string 2))
1629 (kill-buffer buffer)
1630 )
1631
1632 (setq ali-name (ada-find-ali-file-in-dir ali-name))
1633
1634 (save-excursion
1635 ;; Tries to open the new ali file to find the spec file
1636 (if ali-name
1637 (progn
1638 (find-file ali-name)
1639 (goto-char (point-min))
1640 (re-search-forward (concat "^U " unit-name "%s[ \t]+"
1641 "\\([^ \t]+\\)"))
1642 (setq body-name (match-string 1))
1643 (kill-buffer (current-buffer))
1644 )
1645 )
1646 )
1647
1648 (find-file body-name)
1649 ))
1650
1651(defun ada-make-filename-from-adaname (adaname)
eec3232e
GM
1652 "Determine the filename in which ADANAME is found.
1653This is a GNAT specific function that uses gnatkrunch."
797aab3c
GM
1654 (let (krunch-buf)
1655 (setq krunch-buf (generate-new-buffer "*gkrunch*"))
1656 (save-excursion
1657 (set-buffer krunch-buf)
1658 ;; send adaname to external process `gnatkr'.
1659 (call-process "gnatkr" nil krunch-buf nil
1660 adaname ada-krunch-args)
1661 ;; fetch output of that process
1662 (setq adaname (buffer-substring
1663 (point-min)
1664 (progn
1665 (goto-char (point-min))
1666 (end-of-line)
1667 (point))))
1668 (kill-buffer krunch-buf)))
1669 adaname
1670 )
1671
1672
1673(defun ada-make-body-gnatstub ()
1674 "Create an Ada package body in the current buffer.
1675This function uses the `gnatstub' program to create the body.
1676This function typically is to be hooked into `ff-file-created-hooks'."
1677 (interactive)
1678
1679 (save-some-buffers nil nil)
1680
1681 (ada-require-project-file)
1682
1683 (delete-region (point-min) (point-max))
1684
1685 ;; Call the external process gnatstub
1686 (let* ((gnatstub-opts (ada-treat-cmd-string ada-gnatstub-opts))
1687 (filename (buffer-file-name (car (cdr (buffer-list)))))
1688 (output (concat (file-name-sans-extension filename) ".adb"))
1689 (gnatstub-cmd (concat "gnatstub " gnatstub-opts " " filename))
1690 (buffer (get-buffer-create "*gnatstub*")))
1691
1692 (save-excursion
1693 (set-buffer buffer)
1694 (compilation-minor-mode 1)
1695 (erase-buffer)
1696 (insert gnatstub-cmd)
1697 (newline)
1698 )
1699 ;; call gnatstub to create the body file
1700 (call-process shell-file-name nil buffer nil "-c" gnatstub-cmd)
1701
1702 (if (save-excursion
1703 (set-buffer buffer)
1704 (goto-char (point-min))
1705 (search-forward "command not found" nil t))
1706 (progn
1707 (message "gnatstub was not found -- using the basic algorithm")
1708 (sleep-for 2)
1709 (kill-buffer buffer)
1710 (ada-make-body))
1711
1712 ;; Else clean up the output
1713
1714 ;; Kill the temporary buffer created by find-file
1715 (set-buffer-modified-p nil)
1716 (kill-buffer (current-buffer))
1717
1718 (if (file-exists-p output)
1719 (progn
1720 (find-file output)
1721 (kill-buffer buffer))
1722
1723 ;; display the error buffer
1724 (display-buffer buffer)
1725 )
1726 )))
1727
1728
1729(defun ada-xref-initialize ()
1730 "Function called by ada-mode-hook to initialize the ada-xref.el package.
1731For instance, it creates the gnat-specific menus, set some hooks for
1732find-file...."
1733 (ada-add-ada-menu)
1734 (make-local-hook 'ff-file-created-hooks)
1735 (setq ff-file-created-hooks 'ada-make-body-gnatstub)
1736
1737 ;; Read the project file and update the search path
1738 ;; before looking for the other file
1739 (make-local-hook 'ff-pre-find-hooks)
1740 (add-hook 'ff-pre-find-hooks 'ada-require-project-file)
1741
1742 ;; Completion for file names in the mini buffer should ignore .ali files
1743 (add-to-list 'completion-ignored-extensions ".ali")
1744 )
1745
1746
1747;; ----- Add to ada-mode-hook ---------------------------------------------
1748
1749;; Set the keymap once and for all, so that the keys set by the user in his
1750;; config file are not overwritten every time we open a new file.
1751(ada-add-keymap)
1752
1753(add-hook 'ada-mode-hook 'ada-xref-initialize)
1754
1755(provide 'ada-xref)
1756
1757;;; ada-xref.el ends here