]> code.delx.au - gnu-emacs-elpa/blob - packages/ada-mode/gpr-mode.el
81aa760df4c94182294245e3309360546b14b530
[gnu-emacs-elpa] / packages / ada-mode / gpr-mode.el
1 ;;; gpr-mode --- major-mode for editing GNAT project files
2
3 ;; Copyright (C) 2007, 2008, 2012, 2013 Stephen Leake
4 ;; Copyright (C) 2004 Rolf Ebert
5
6 ;; Author: Stephen Leake <stephen_leake@member.fsf.org>
7 ;; Maintainer: Stephen Leake <stephen_leake@member.fsf.org>
8 ;; Version: 5.0
9
10 ;; This file is part of GNU Emacs.
11
12 ;; gpr-mode is free software; you can redistribute it and/or modify
13 ;; it under the terms of the GNU General Public License as published by
14 ;; the Free Software Foundation; either version 3, or (at your option)
15 ;; any later version.
16
17 ;; gpr-mode is distributed in the hope that it will be useful,
18 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 ;; GNU General Public License for more details.
21
22 ;; You should have received a copy of the GNU General Public License
23 ;; along with GNU Emacs; see the file COPYING. If not, write to the
24 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
25 ;; Boston, MA 02111-1307, USA.
26 ;;
27 ;;; Commentary:
28 ;;
29 ;;; History:
30 ;;
31 ;; The first gpr-mode was written by Rolf Ebert
32 ;; <rolf.ebert_nosp...@gmx.net> in 2004.
33 ;;
34 ;; Stephen Leake <stephen_leake@member.fsf.org> rewrote it in 2013 to
35 ;; use the wisi indentation engine.
36 ;;
37 ;;;;; Code:
38
39 ;; we reuse several ada-mode functions
40 (require 'ada-mode)
41
42 (defun gpr-align ()
43 "If region is active, apply 'align'. If not, attempt to align
44 current construct."
45 (interactive)
46 (if (use-region-p)
47 (progn
48 (align (region-beginning) (region-end))
49 (deactivate-mark))
50
51 (align-current)
52 ))
53
54 (defvar gpr-mode-map
55 (let ((map (make-sparse-keymap)))
56 ;; C-c <letter> are reserved for users
57
58 ;; global-map has C-x ` 'next-error
59 (define-key map [return] 'ada-indent-newline-indent)
60 (define-key map "\C-c`" 'ada-show-secondary-error)
61 (define-key map "\C-c\C-a" 'gpr-align)
62 (define-key map "\C-c\C-c" 'compile)
63 (define-key map "\C-c\C-e" 'gpr-expand)
64 (define-key map "\C-c\C-f" 'gpr-show-parse-error)
65 (define-key map "\C-c\C-i" 'gpr-indent-statement)
66 ;; FIXME (later): implement?
67 ;; (define-key map "\C-c\C-n" 'ada-next-statement-keyword)
68 ;; (define-key map "\C-c\C-p" 'ada-prev-statement-keyword)
69 (define-key map "\C-c\C-S-p" 'gpr-set-as-project)
70 (define-key map "\C-c\C-t" 'ada-case-read-all-exceptions)
71 (define-key map "\C-c\C-w" 'ada-case-adjust-at-point)
72 (define-key map "\C-c\C-y" 'ada-case-create-exception)
73 (define-key map "\C-c\C-\M-y" (lambda () (ada-case-create-exception nil nil t)))
74 map
75 ) "Local keymap used for GPR mode.")
76
77 (defvar gpr-mode-menu (make-sparse-keymap "gpr"))
78 (easy-menu-define gpr-mode-menu gpr-mode-map "Menu keymap for gpr mode"
79 '("gpr"
80 ("Help"
81 ["gpr Mode" (info "gpr-mode") t]
82 ["GNAT Reference Manual" (info "gnat_rm") t]
83 ["GNAT User Guide" (info "gnat_ugn") t]
84 ["Key bindings" describe-bindings t]
85 )
86
87 ["Customize" (customize-group 'ada)];; we reuse the Ada indentation options
88 ["------" nil nil]
89 ["Find and select project ..." ada-build-prompt-select-prj-file t]
90 ["Select project ..." ada-prj-select t]
91 ["Set as current project" gpr-set-as-project t]
92 ["Show current project" ada-prj-show t]
93 ["Next compilation error" next-error t]
94 ["Show secondary error" ada-show-secondary-error t]
95 ["Show last parse error" gpr-show-parse-error t]
96 ("Edit"
97 ["Indent Line" indent-for-tab-command t]
98 ["Indent Lines in Selection" indent-region t]
99 ["Indent Lines in File" (indent-region (point-min) (point-max)) t]
100 ["Expand skeleton" gpr-expand t] ;; FIXME: only if skeleton
101 ["Align" gpr-align t]
102 ["Comment Selection" comment-region t]
103 ["Uncomment Selection" (lambda () (comment-region t)) t]
104 ["Fill Comment Paragraph" fill-paragraph t]
105
106 ["Fill Comment Paragraph Justify" ada-fill-comment-paragraph-justify t]
107 ["Fill Comment Paragraph Postfix" ada-fill-comment-paragraph-postfix t]
108 )
109 ))
110
111 (defvar gpr-show-parse-error nil
112 ;; Supplied by indentation engine parser
113 "Function to show last error reported by indentation parser."
114 )
115
116 (defun gpr-show-parse-error ()
117 (interactive)
118 (when gpr-show-parse-error
119 (funcall gpr-show-parse-error)))
120
121 (defvar gpr-expand nil
122 ;; skeleton function
123 "Function to call to expand tokens (ie insert skeletons).")
124
125 (defun gpr-expand ()
126 "Expand previous word into a statement skeleton."
127 (interactive)
128 (when gpr-expand
129 (funcall gpr-expand)))
130
131 (defvar gpr-indent-statement nil
132 ;; indentation function
133 "Function to indent the statement/declaration point is in or after.
134 Function is called with no arguments.")
135
136 (defun gpr-indent-statement ()
137 "Indent current statement."
138 (interactive)
139 (when gpr-indent-statement
140 (funcall gpr-indent-statement)))
141
142 (defvar gpr-font-lock-keywords
143 (progn
144 (list
145 ;;
146 ;; keyword plus name.
147 (list (concat
148 "\\<\\("
149 "package\\|"
150 "project\\|"
151 "for"
152 "\\)\\>[ \t]*"
153 "\\(\\sw+\\(\\.\\sw*\\)*\\)?")
154 '(1 font-lock-keyword-face) '(2 font-lock-function-name-face nil t))
155 ;;
156 ;; Main keywords
157 (list (concat "\\<"
158 (regexp-opt
159 '("abstract" "aggregate" "case" "configuration" "external" "is" "library" "null" "others"
160 "renames" "standard" "type" "use" "when" "with") t)
161 "\\>")
162 '(1 font-lock-keyword-face))
163 ;;
164 ;; Anything following end and not already fontified is a body name.
165 '("\\<\\(end\\)\\>\\([ \t]+\\)?\\(\\(\\sw\\|[_.]\\)+\\)?"
166 (1 font-lock-keyword-face) (3 font-lock-function-name-face nil t))
167 ;;
168 ))
169 "Expressions to highlight in gpr mode.")
170
171 (defun gpr-ff-special-with ()
172 (ada-require-project-file)
173 (let ((project-path (match-string 1)))
174 ;; project-path may be any of "foo", "foo.gpr", "../foo.gpr"
175 ;;
176 ;; The result of ff-special-constructs is used by
177 ;; ff-find-the-other-file with ff-search-directories and nil
178 ;; suffix list, so it must contain the relative path and the
179 ;; suffix
180 (if (file-name-extension project-path)
181 project-path
182 (concat project-path ".gpr"))
183 ))
184
185 (defun gpr-set-ff-special-constructs ()
186 "Add gpr-specific pairs to `ff-special-constructs'."
187 (set (make-local-variable 'ff-special-constructs) nil)
188 (mapc (lambda (pair) (add-to-list 'ff-special-constructs pair))
189 ;; Each car is a regexp; if it matches at point, the cdr is
190 ;; invoked. Each cdr should return the absolute file name to
191 ;; go to.
192 (list
193 ;; A "with" clause; allow "foo_bar.gpr" and "../foo"
194 (cons "^with[ \t]+\"\\(\\(?:\\(?:\\sw\\|\\s.\\)\\|\\s_\\)+\\)\";"
195 'gpr-ff-special-with)
196 )))
197
198 (defvar gpr-which-function nil
199 ;; supplied by the indentation engine
200 "Function called with no parameters; it should return the name
201 of the package or project point is in or just after, or nil.")
202
203 (defun gpr-which-function ()
204 "See `gpr-which-function' variable."
205 (when gpr-which-function
206 (funcall gpr-which-function)))
207
208 (defun gpr-add-log-current-function ()
209 "For `add-log-current-defun-function'. Returns enclosing package or project name."
210 ;; add-log-current-defun is typically called with point at the start
211 ;; of an ediff change section, which is before the start of the
212 ;; declaration of a new item. So go to the end of the current line
213 ;; first
214 (save-excursion
215 (end-of-line 1)
216 (gpr-which-function)))
217
218 (defun gpr-set-as-project (&optional file)
219 "Set FILE (default current buffer file) as Emacs project file."
220 (interactive)
221 (ada-parse-prj-file (or file (buffer-file-name)))
222 (ada-select-prj-file (or file (buffer-file-name))))
223
224 ;;;;
225 (defun gpr-mode ()
226 "The major mode for editing GNAT project files."
227
228 (interactive)
229 (kill-all-local-variables)
230 (setq major-mode 'gpr-mode)
231 (setq mode-name "GNAT Project")
232 (use-local-map gpr-mode-map)
233 (set-syntax-table ada-mode-syntax-table)
234 (set (make-local-variable 'syntax-begin-function) nil)
235 (set 'case-fold-search t); gpr is case insensitive; the syntax parsing requires this setting
236 (set (make-local-variable 'comment-start) "--")
237 (set (make-local-variable 'comment-end) "")
238 (set (make-local-variable 'comment-start-skip) "---*[ \t]*")
239 (set (make-local-variable 'comment-multi-line) nil)
240
241 (set (make-local-variable 'require-final-newline) t)
242
243 (set (make-local-variable 'font-lock-defaults)
244 '(gpr-font-lock-keywords
245 nil t
246 ((?\_ . "w"))))
247
248 (gpr-set-ff-special-constructs)
249 (setq ff-search-directories 'ada-project-search-path)
250
251 (set (make-local-variable 'add-log-current-defun-function)
252 'gpr-add-log-current-function)
253
254 ;; used by autofill to break a comment line and continue it on
255 ;; another line. The reason we need this one is that the default
256 ;; behavior does not work correctly with the definition of
257 ;; paragraph-start above when the comment is right after a
258 ;; multi-line subprogram declaration (the comments are aligned under
259 ;; the latest parameter, not under the declaration start).
260 ;; FIXME: need test - should be in gpr-wisi?
261 (set (make-local-variable 'comment-line-break-function)
262 (lambda (&optional soft) (let ((fill-prefix nil))
263 (indent-new-comment-line soft))))
264
265 (run-hooks 'gpr-mode-hook)
266
267 )
268
269 ;; user needs to add this somewhere:
270 ;; FIXME: add autoload processing?
271 (add-to-list 'auto-mode-alist '("\\.gpr\\'" . gpr-mode)) ; GNAT project files
272
273 (provide 'gpr-mode)
274
275 (unless (featurep 'gpr-indent-engine)
276 (require 'gpr-wisi))
277
278 ;;; end of file