Commit | Line | Data |
---|---|---|
acc33231 CY |
1 | ;;; ede.el --- Emacs Development Environment gloss |
2 | ||
ba318903 | 3 | ;; Copyright (C) 1998-2005, 2007-2014 Free Software Foundation, Inc. |
acc33231 CY |
4 | |
5 | ;; Author: Eric M. Ludlam <zappo@gnu.org> | |
6 | ;; Keywords: project, make | |
be798504 | 7 | ;; Version: 1.2 |
acc33231 CY |
8 | |
9 | ;; This file is part of GNU Emacs. | |
10 | ||
11 | ;; GNU Emacs is free software: you can redistribute it and/or modify | |
12 | ;; it under the terms of the GNU General Public License as published by | |
13 | ;; the Free Software Foundation, either version 3 of the License, or | |
14 | ;; (at your option) any later version. | |
15 | ||
16 | ;; GNU Emacs is distributed in the hope that it will be useful, | |
17 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
18 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
19 | ;; GNU General Public License for more details. | |
20 | ||
21 | ;; You should have received a copy of the GNU General Public License | |
22 | ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. | |
23 | ||
24 | ;;; Commentary: | |
25 | ;; | |
26 | ;; EDE is the top level Lisp interface to a project management scheme | |
27 | ;; for Emacs. Emacs does many things well, including editing, | |
28 | ;; building, and debugging. Folks migrating from other IDEs don't | |
29 | ;; seem to think this qualifies, however, because they still have to | |
30 | ;; write the makefiles, and specify parameters to programs. | |
31 | ;; | |
32 | ;; This EDE mode will attempt to link these diverse programs together | |
33 | ;; into a comprehensive single interface, instead of a bunch of | |
34 | ;; different ones. | |
35 | ||
36 | ;;; Install | |
37 | ;; | |
38 | ;; This command enables project mode on all files. | |
39 | ;; | |
40 | ;; (global-ede-mode t) | |
41 | ||
715f35a5 | 42 | (require 'cedet) |
acc33231 CY |
43 | (require 'eieio) |
44 | (require 'eieio-speedbar) | |
45 | (require 'ede/source) | |
cb85c0d8 EL |
46 | (require 'ede/base) |
47 | (require 'ede/auto) | |
48 | ||
1730d963 GM |
49 | (eval-and-compile |
50 | (load "ede/loaddefs" nil 'nomessage)) | |
acc33231 | 51 | |
cb85c0d8 | 52 | (declare-function ede-commit-project "ede/custom") |
acc33231 CY |
53 | (declare-function ede-convert-path "ede/files") |
54 | (declare-function ede-directory-get-open-project "ede/files") | |
55 | (declare-function ede-directory-get-toplevel-open-project "ede/files") | |
56 | (declare-function ede-directory-project-p "ede/files") | |
57 | (declare-function ede-find-subproject-for-directory "ede/files") | |
58 | (declare-function ede-project-directory-remove-hash "ede/files") | |
9a1a272a | 59 | (declare-function ede-toplevel "ede/base") |
acc33231 CY |
60 | (declare-function ede-toplevel-project "ede/files") |
61 | (declare-function ede-up-directory "ede/files") | |
acc33231 CY |
62 | (declare-function semantic-lex-make-spp-table "semantic/lex-spp") |
63 | ||
be798504 | 64 | (defconst ede-version "1.2" |
acc33231 CY |
65 | "Current version of the Emacs EDE.") |
66 | ||
67 | ;;; Code: | |
68 | (defun ede-version () | |
69 | "Display the current running version of EDE." | |
70 | (interactive) (message "EDE %s" ede-version)) | |
71 | ||
72 | (defgroup ede nil | |
ff90f4b0 | 73 | "Emacs Development Environment." |
acc33231 | 74 | :group 'tools |
ff90f4b0 | 75 | :group 'extensions) |
acc33231 CY |
76 | |
77 | (defcustom ede-auto-add-method 'ask | |
bd2afec2 | 78 | "Whether a new source file should be automatically added to a target. |
acc33231 CY |
79 | Whenever a new file is encountered in a directory controlled by a |
80 | project file, all targets are queried to see if it should be added. | |
81 | If the value is 'always, then the new file is added to the first | |
82 | target encountered. If the value is 'multi-ask, then if more than one | |
83 | target wants the file, the user is asked. If only one target wants | |
9b053e76 | 84 | the file, then it is automatically added to that target. If the |
acc33231 CY |
85 | value is 'ask, then the user is always asked, unless there is no |
86 | target willing to take the file. 'never means never perform the check." | |
87 | :group 'ede | |
88 | :type '(choice (const always) | |
89 | (const multi-ask) | |
90 | (const ask) | |
91 | (const never))) | |
92 | ||
93 | (defcustom ede-debug-program-function 'gdb | |
94 | "Default Emacs command used to debug a target." | |
95 | :group 'ede | |
96 | :type 'sexp) ; make this be a list of options some day | |
97 | ||
a62d5ee1 EL |
98 | (defcustom ede-project-directories nil |
99 | "Directories in which EDE may search for project files. | |
100 | If the value is t, EDE may search in any directory. | |
101 | ||
102 | If the value is a function, EDE calls that function with one | |
d136f184 | 103 | argument, the directory name; the function should return t if |
a62d5ee1 EL |
104 | EDE should look for project files in the directory. |
105 | ||
106 | Otherwise, the value should be a list of fully-expanded directory | |
107 | names. EDE searches for project files only in those directories. | |
108 | If you invoke the commands \\[ede] or \\[ede-new] on a directory | |
109 | that is not listed, Emacs will offer to add it to the list. | |
110 | ||
111 | Any other value disables searching for EDE project files." | |
112 | :group 'ede | |
113 | :type '(choice (const :tag "Any directory" t) | |
114 | (repeat :tag "List of directories" | |
115 | (directory)) | |
116 | (function :tag "Predicate")) | |
117 | :version "23.4" | |
118 | :risky t) | |
119 | ||
120 | (defun ede-directory-safe-p (dir) | |
121 | "Return non-nil if DIR is a safe directory to load projects from. | |
122 | Projects that do not load a project definition as Emacs Lisp code | |
123 | are safe, and can be loaded automatically. Other project types, | |
124 | such as those created with Project.ede files, are safe only if | |
125 | specified by `ede-project-directories'." | |
126 | (setq dir (directory-file-name (expand-file-name dir))) | |
127 | ;; Load only if allowed by `ede-project-directories'. | |
128 | (or (eq ede-project-directories t) | |
129 | (and (functionp ede-project-directories) | |
130 | (funcall ede-project-directories dir)) | |
131 | (and (listp ede-project-directories) | |
132 | (member dir ede-project-directories)))) | |
133 | ||
acc33231 CY |
134 | \f |
135 | ;;; Management variables | |
136 | ||
137 | (defvar ede-projects nil | |
138 | "A list of all active projects currently loaded in Emacs.") | |
139 | ||
140 | (defvar ede-object-root-project nil | |
141 | "The current buffer's current root project. | |
142 | If a file is under a project, this specifies the project that is at | |
143 | the root of a project tree.") | |
144 | (make-variable-buffer-local 'ede-object-root-project) | |
145 | ||
146 | (defvar ede-object-project nil | |
147 | "The current buffer's current project at that level. | |
148 | If a file is under a project, this specifies the project that contains the | |
149 | current target.") | |
150 | (make-variable-buffer-local 'ede-object-project) | |
151 | ||
152 | (defvar ede-object nil | |
153 | "The current buffer's target object. | |
154 | This object's class determines how to compile and debug from a buffer.") | |
155 | (make-variable-buffer-local 'ede-object) | |
156 | ||
157 | (defvar ede-selected-object nil | |
158 | "The currently user-selected project or target. | |
159 | If `ede-object' is nil, then commands will operate on this object.") | |
160 | ||
161 | (defvar ede-constructing nil | |
cb85c0d8 EL |
162 | "Non nil when constructing a project hierarchy. |
163 | If the project is being constructed from an autoload, then the | |
164 | value is the autoload object being used.") | |
acc33231 CY |
165 | |
166 | (defvar ede-deep-rescan nil | |
167 | "Non nil means scan down a tree, otherwise rescans are top level only. | |
168 | Do not set this to non-nil globally. It is used internally.") | |
acc33231 CY |
169 | |
170 | \f | |
171 | ;;; Prompting | |
172 | ;; | |
173 | (defun ede-singular-object (prompt) | |
174 | "Using PROMPT, choose a single object from the current buffer." | |
175 | (if (listp ede-object) | |
176 | (ede-choose-object prompt ede-object) | |
177 | ede-object)) | |
178 | ||
179 | (defun ede-choose-object (prompt list-o-o) | |
180 | "Using PROMPT, ask the user which OBJECT to use based on the name field. | |
181 | Argument LIST-O-O is the list of objects to choose from." | |
182 | (let* ((al (object-assoc-list 'name list-o-o)) | |
183 | (ans (completing-read prompt al nil t))) | |
184 | (setq ans (assoc ans al)) | |
185 | (cdr ans))) | |
186 | \f | |
187 | ;;; Menu and Keymap | |
188 | ||
715f35a5 | 189 | (defvar ede-minor-mode-map |
acc33231 CY |
190 | (let ((map (make-sparse-keymap)) |
191 | (pmap (make-sparse-keymap))) | |
192 | (define-key pmap "e" 'ede-edit-file-target) | |
193 | (define-key pmap "a" 'ede-add-file) | |
194 | (define-key pmap "d" 'ede-remove-file) | |
195 | (define-key pmap "t" 'ede-new-target) | |
196 | (define-key pmap "g" 'ede-rescan-toplevel) | |
197 | (define-key pmap "s" 'ede-speedbar) | |
acc33231 CY |
198 | (define-key pmap "f" 'ede-find-file) |
199 | (define-key pmap "C" 'ede-compile-project) | |
200 | (define-key pmap "c" 'ede-compile-target) | |
201 | (define-key pmap "\C-c" 'ede-compile-selected) | |
202 | (define-key pmap "D" 'ede-debug-target) | |
67d3ffe4 | 203 | (define-key pmap "R" 'ede-run-target) |
acc33231 CY |
204 | ;; bind our submap into map |
205 | (define-key map "\C-c." pmap) | |
206 | map) | |
207 | "Keymap used in project minor mode.") | |
208 | ||
715f35a5 CY |
209 | (defvar global-ede-mode-map |
210 | (let ((map (make-sparse-keymap))) | |
211 | (define-key map [menu-bar cedet-menu] | |
212 | (cons "Development" cedet-menu-map)) | |
213 | map) | |
bd2afec2 | 214 | "Keymap used in `global-ede-mode'.") |
715f35a5 CY |
215 | |
216 | ;; Activate the EDE items in cedet-menu-map | |
217 | ||
218 | (define-key cedet-menu-map [ede-find-file] | |
ebf5c4f5 CY |
219 | '(menu-item "Find File in Project..." ede-find-file :enable ede-object |
220 | :visible global-ede-mode)) | |
715f35a5 | 221 | (define-key cedet-menu-map [ede-speedbar] |
ebf5c4f5 CY |
222 | '(menu-item "View Project Tree" ede-speedbar :enable ede-object |
223 | :visible global-ede-mode)) | |
715f35a5 | 224 | (define-key cedet-menu-map [ede] |
ebf5c4f5 CY |
225 | '(menu-item "Load Project" ede |
226 | :visible global-ede-mode)) | |
715f35a5 CY |
227 | (define-key cedet-menu-map [ede-new] |
228 | '(menu-item "Create Project" ede-new | |
ebf5c4f5 CY |
229 | :enable (not ede-object) |
230 | :visible global-ede-mode)) | |
715f35a5 CY |
231 | (define-key cedet-menu-map [ede-target-options] |
232 | '(menu-item "Target Options" ede-target-options | |
ebf5c4f5 CY |
233 | :filter ede-target-forms-menu |
234 | :visible global-ede-mode)) | |
715f35a5 CY |
235 | (define-key cedet-menu-map [ede-project-options] |
236 | '(menu-item "Project Options" ede-project-options | |
ebf5c4f5 CY |
237 | :filter ede-project-forms-menu |
238 | :visible global-ede-mode)) | |
715f35a5 CY |
239 | (define-key cedet-menu-map [ede-build-forms-menu] |
240 | '(menu-item "Build Project" ede-build-forms-menu | |
241 | :filter ede-build-forms-menu | |
ebf5c4f5 CY |
242 | :enable ede-object |
243 | :visible global-ede-mode)) | |
acc33231 | 244 | |
cb85c0d8 EL |
245 | (defun ede-buffer-belongs-to-target-p () |
246 | "Return non-nil if this buffer belongs to at least one target." | |
247 | (let ((obj ede-object)) | |
248 | (if (consp obj) | |
249 | (setq obj (car obj))) | |
250 | (and obj (obj-of-class-p obj ede-target)))) | |
251 | ||
252 | (defun ede-buffer-belongs-to-project-p () | |
cd1181db | 253 | "Return non-nil if this buffer belongs to at least one project." |
cb85c0d8 | 254 | (if (or (null ede-object) (consp ede-object)) nil |
62a81506 | 255 | (obj-of-class-p ede-object-project ede-project))) |
cb85c0d8 | 256 | |
acc33231 CY |
257 | (defun ede-menu-obj-of-class-p (class) |
258 | "Return non-nil if some member of `ede-object' is a child of CLASS." | |
259 | (if (listp ede-object) | |
b90caf50 | 260 | (eval (cons 'or (mapcar (lambda (o) (obj-of-class-p o class)) ede-object))) |
acc33231 CY |
261 | (obj-of-class-p ede-object class))) |
262 | ||
263 | (defun ede-build-forms-menu (menu-def) | |
264 | "Create a sub menu for building different parts of an EDE system. | |
265 | Argument MENU-DEF is the menu definition to use." | |
266 | (easy-menu-filter-return | |
267 | (easy-menu-create-menu | |
268 | "Build Forms" | |
269 | (let ((obj (ede-current-project)) | |
270 | (newmenu nil) ;'([ "Build Selected..." ede-compile-selected t ])) | |
271 | targets | |
272 | targitems | |
273 | ede-obj | |
274 | (tskip nil)) | |
275 | (if (not obj) | |
276 | nil | |
277 | (setq targets (when (slot-boundp obj 'targets) | |
278 | (oref obj targets)) | |
279 | ede-obj (if (listp ede-object) ede-object (list ede-object))) | |
280 | ;; First, collect the build items from the project | |
281 | (setq newmenu (append newmenu (ede-menu-items-build obj t))) | |
cd1181db | 282 | ;; Second, declare the current target menu items |
acc33231 CY |
283 | (if (and ede-obj (ede-menu-obj-of-class-p ede-target)) |
284 | (while ede-obj | |
285 | (setq newmenu (append newmenu | |
286 | (ede-menu-items-build (car ede-obj) t)) | |
287 | tskip (car ede-obj) | |
288 | ede-obj (cdr ede-obj)))) | |
289 | ;; Third, by name, enable builds for other local targets | |
290 | (while targets | |
291 | (unless (eq tskip (car targets)) | |
292 | (setq targitems (ede-menu-items-build (car targets) nil)) | |
293 | (setq newmenu | |
294 | (append newmenu | |
295 | (if (= 1 (length targitems)) | |
296 | targitems | |
297 | (cons (ede-name (car targets)) | |
298 | targitems)))) | |
299 | ) | |
300 | (setq targets (cdr targets))) | |
301 | ;; Fourth, build sub projects. | |
302 | ;; -- nerp | |
cd1181db | 303 | ;; Fifth, add make distribution |
acc33231 CY |
304 | (append newmenu (list [ "Make distribution" ede-make-dist t ])) |
305 | ))))) | |
306 | ||
307 | (defun ede-target-forms-menu (menu-def) | |
308 | "Create a target MENU-DEF based on the object belonging to this buffer." | |
309 | (easy-menu-filter-return | |
310 | (easy-menu-create-menu | |
311 | "Target Forms" | |
312 | (let ((obj (or ede-selected-object ede-object))) | |
313 | (append | |
e6e267fc CY |
314 | '([ "Add File" ede-add-file |
315 | (and (ede-current-project) | |
316 | (oref (ede-current-project) targets)) ] | |
acc33231 | 317 | [ "Remove File" ede-remove-file |
cb85c0d8 | 318 | (ede-buffer-belongs-to-project-p) ] |
acc33231 CY |
319 | "-") |
320 | (if (not obj) | |
321 | nil | |
322 | (if (and (not (listp obj)) (oref obj menu)) | |
323 | (oref obj menu) | |
324 | (when (listp obj) | |
325 | ;; This is bad, but I'm not sure what else to do. | |
326 | (oref (car obj) menu))))))))) | |
327 | ||
328 | (defun ede-project-forms-menu (menu-def) | |
329 | "Create a target MENU-DEF based on the object belonging to this buffer." | |
330 | (easy-menu-filter-return | |
331 | (easy-menu-create-menu | |
332 | "Project Forms" | |
333 | (let* ((obj (ede-current-project)) | |
e8cc7880 | 334 | (class (if obj (eieio-object-class obj))) |
acc33231 CY |
335 | (menu nil)) |
336 | (condition-case err | |
337 | (progn | |
338 | (while (and class (slot-exists-p class 'menu)) | |
339 | ;;(message "Looking at class %S" class) | |
340 | (setq menu (append menu (oref class menu)) | |
e8cc7880 | 341 | class (eieio-class-parent class)) |
acc33231 CY |
342 | (if (listp class) (setq class (car class)))) |
343 | (append | |
344 | '( [ "Add Target" ede-new-target (ede-current-project) ] | |
345 | [ "Remove Target" ede-delete-target ede-object ] | |
62a81506 | 346 | ( "Default configuration" :filter ede-configuration-forms-menu ) |
acc33231 CY |
347 | "-") |
348 | menu | |
349 | )) | |
350 | (error (message "Err found: %S" err) | |
351 | menu) | |
352 | ))))) | |
353 | ||
62a81506 CY |
354 | (defun ede-configuration-forms-menu (menu-def) |
355 | "Create a submenu for selecting the default configuration for this project. | |
356 | The current default is in the current object's CONFIGURATION-DEFAULT slot. | |
357 | All possible configurations are in CONFIGURATIONS. | |
358 | Argument MENU-DEF specifies the menu being created." | |
359 | (easy-menu-filter-return | |
360 | (easy-menu-create-menu | |
361 | "Configurations" | |
362 | (let* ((obj (ede-current-project)) | |
363 | (conf (when obj (oref obj configurations))) | |
364 | (cdef (when obj (oref obj configuration-default))) | |
365 | (menu nil)) | |
366 | (dolist (C conf) | |
367 | (setq menu (cons (vector C (list 'ede-project-configurations-set C) | |
368 | :style 'toggle | |
369 | :selected (string= C cdef)) | |
370 | menu)) | |
371 | ) | |
372 | (nreverse menu))))) | |
373 | ||
374 | (defun ede-project-configurations-set (newconfig) | |
375 | "Set the current project's current configuration to NEWCONFIG. | |
376 | This function is designed to be used by `ede-configuration-forms-menu' | |
377 | but can also be used interactively." | |
378 | (interactive | |
379 | (list (let* ((proj (ede-current-project)) | |
380 | (configs (oref proj configurations))) | |
381 | (completing-read "New configuration: " | |
382 | configs nil t | |
383 | (oref proj configuration-default))))) | |
384 | (oset (ede-current-project) configuration-default newconfig) | |
385 | (message "%s will now build in %s mode." | |
e8cc7880 | 386 | (eieio-object-name (ede-current-project)) |
62a81506 CY |
387 | newconfig)) |
388 | ||
acc33231 CY |
389 | (defun ede-customize-forms-menu (menu-def) |
390 | "Create a menu of the project, and targets that can be customized. | |
391 | Argument MENU-DEF is the definition of the current menu." | |
392 | (easy-menu-filter-return | |
393 | (easy-menu-create-menu | |
394 | "Customize Project" | |
395 | (let* ((obj (ede-current-project)) | |
8bf997ef | 396 | targ) |
acc33231 | 397 | (when obj |
cb85c0d8 | 398 | (setq targ (when (and obj (slot-boundp obj 'targets)) |
8bf997ef | 399 | (oref obj targets))) |
acc33231 CY |
400 | ;; Make custom menus for everything here. |
401 | (append (list | |
402 | (cons (concat "Project " (ede-name obj)) | |
403 | (eieio-customize-object-group obj)) | |
404 | [ "Reorder Targets" ede-project-sort-targets t ] | |
405 | ) | |
406 | (mapcar (lambda (o) | |
407 | (cons (concat "Target " (ede-name o)) | |
408 | (eieio-customize-object-group o))) | |
409 | targ))))))) | |
410 | ||
411 | ||
412 | (defun ede-apply-object-keymap (&optional default) | |
413 | "Add target specific keybindings into the local map. | |
414 | Optional argument DEFAULT indicates if this should be set to the default | |
415 | version of the keymap." | |
62a81506 CY |
416 | (let ((object (or ede-object ede-selected-object)) |
417 | (proj ede-object-project)) | |
acc33231 CY |
418 | (condition-case nil |
419 | (let ((keys (ede-object-keybindings object))) | |
62a81506 CY |
420 | ;; Add keys for the project to whatever is in the current object |
421 | ;; so long as it isn't the same. | |
422 | (when (not (eq object proj)) | |
423 | (setq keys (append keys (ede-object-keybindings proj)))) | |
acc33231 CY |
424 | (while keys |
425 | (local-set-key (concat "\C-c." (car (car keys))) | |
426 | (cdr (car keys))) | |
427 | (setq keys (cdr keys)))) | |
428 | (error nil)))) | |
429 | ||
430 | ;;; Menu building methods for building | |
431 | ;; | |
432 | (defmethod ede-menu-items-build ((obj ede-project) &optional current) | |
433 | "Return a list of menu items for building project OBJ. | |
434 | If optional argument CURRENT is non-nil, return sub-menu code." | |
435 | (if current | |
436 | (list [ "Build Current Project" ede-compile-project t ]) | |
437 | (list (vector | |
438 | (list | |
439 | (concat "Build Project " (ede-name obj)) | |
440 | `(project-compile-project ,obj)))))) | |
441 | ||
442 | (defmethod ede-menu-items-build ((obj ede-target) &optional current) | |
443 | "Return a list of menu items for building target OBJ. | |
444 | If optional argument CURRENT is non-nil, return sub-menu code." | |
445 | (if current | |
446 | (list [ "Build Current Target" ede-compile-target t ]) | |
447 | (list (vector | |
448 | (concat "Build Target " (ede-name obj)) | |
449 | `(project-compile-target ,obj) | |
450 | t)))) | |
451 | \f | |
452 | ;;; Mode Declarations | |
453 | ;; | |
acc33231 CY |
454 | |
455 | (defun ede-apply-target-options () | |
456 | "Apply options to the current buffer for the active project/target." | |
62a81506 CY |
457 | (ede-apply-project-local-variables) |
458 | ;; Apply keymaps and preprocessor symbols. | |
acc33231 CY |
459 | (ede-apply-object-keymap) |
460 | (ede-apply-preprocessor-map) | |
461 | ) | |
462 | ||
463 | (defun ede-turn-on-hook () | |
464 | "Turn on EDE minor mode in the current buffer if needed. | |
465 | To be used in hook functions." | |
466 | (if (or (and (stringp (buffer-file-name)) | |
467 | (stringp default-directory)) | |
468 | ;; Emacs 21 has no buffer file name for directory edits. | |
469 | ;; so we need to add these hacks in. | |
470 | (eq major-mode 'dired-mode) | |
471 | (eq major-mode 'vc-dired-mode)) | |
472 | (ede-minor-mode 1))) | |
473 | ||
715f35a5 CY |
474 | (define-minor-mode ede-minor-mode |
475 | "Toggle EDE (Emacs Development Environment) minor mode. | |
ac6c8639 CY |
476 | With a prefix argument ARG, enable EDE minor mode if ARG is |
477 | positive, and disable it otherwise. If called from Lisp, enable | |
478 | EDE minor mode if ARG is omitted or nil. | |
acc33231 | 479 | |
715f35a5 CY |
480 | If this file is contained, or could be contained in an EDE |
481 | controlled project, then this mode is activated automatically | |
482 | provided `global-ede-mode' is enabled." | |
483 | :group 'ede | |
484 | (cond ((or (eq major-mode 'dired-mode) | |
485 | (eq major-mode 'vc-dired-mode)) | |
486 | (ede-dired-minor-mode (if ede-minor-mode 1 -1))) | |
487 | (ede-minor-mode | |
cb85c0d8 EL |
488 | (if (not ede-constructing) |
489 | (ede-initialize-state-current-buffer) | |
715f35a5 CY |
490 | ;; If we fail to have a project here, turn it back off. |
491 | (ede-minor-mode -1))))) | |
acc33231 | 492 | |
cb85c0d8 EL |
493 | (defun ede-initialize-state-current-buffer () |
494 | "Initialize the current buffer's state for EDE. | |
495 | Sets buffer local variables for EDE." | |
890f7890 DE |
496 | ;; due to inode recycling, make sure we don't |
497 | ;; we flush projects deleted off the system. | |
498 | (ede-flush-deleted-projects) | |
499 | ||
500 | ;; Init the buffer. | |
cb85c0d8 EL |
501 | (let* ((ROOT nil) |
502 | (proj (ede-directory-get-open-project default-directory | |
a62d5ee1 EL |
503 | 'ROOT)) |
504 | (projauto nil)) | |
505 | ||
cb85c0d8 | 506 | (when (or proj ROOT |
a62d5ee1 EL |
507 | ;; If there is no open project, look up the project |
508 | ;; autoloader to see if we should initialize. | |
509 | (setq projauto (ede-directory-project-p default-directory t))) | |
510 | ||
511 | (when (and (not proj) projauto) | |
512 | ||
513 | ;; No project was loaded, but we have a project description | |
514 | ;; object. This means that we can check if it is a safe | |
515 | ;; project to load before requesting it to be loaded. | |
cb85c0d8 | 516 | |
a62d5ee1 EL |
517 | (when (or (oref projauto safe-p) |
518 | ;; The project style is not safe, so check if it is | |
519 | ;; in `ede-project-directories'. | |
520 | (let ((top (ede-toplevel-project default-directory))) | |
521 | (ede-directory-safe-p top))) | |
cb85c0d8 | 522 | |
a62d5ee1 EL |
523 | ;; The project is safe, so load it in. |
524 | (setq proj (ede-load-project-file default-directory 'ROOT)))) | |
525 | ||
526 | ;; Only initialize EDE state in this buffer if we found a project. | |
527 | (when proj | |
528 | ||
529 | (setq ede-object (ede-buffer-object (current-buffer) | |
cb85c0d8 EL |
530 | 'ede-object-project)) |
531 | ||
a62d5ee1 EL |
532 | (setq ede-object-root-project |
533 | (or ROOT (ede-project-root ede-object-project))) | |
cb85c0d8 | 534 | |
a62d5ee1 EL |
535 | (if (and (not ede-object) ede-object-project) |
536 | (ede-auto-add-to-target)) | |
cb85c0d8 | 537 | |
a62d5ee1 | 538 | (ede-apply-target-options))))) |
cb85c0d8 | 539 | |
62a81506 CY |
540 | (defun ede-reset-all-buffers () |
541 | "Reset all the buffers due to change in EDE." | |
542 | (interactive) | |
acc33231 CY |
543 | (let ((b (buffer-list))) |
544 | (while b | |
545 | (when (buffer-file-name (car b)) | |
cb85c0d8 EL |
546 | (with-current-buffer (car b) |
547 | ;; Reset all state variables | |
548 | (setq ede-object nil | |
549 | ede-object-project nil | |
550 | ede-object-root-project nil) | |
551 | ;; Now re-initialize this buffer. | |
552 | (ede-initialize-state-current-buffer) | |
553 | ) | |
acc33231 CY |
554 | ) |
555 | (setq b (cdr b))))) | |
556 | ||
557 | ;;;###autoload | |
715f35a5 CY |
558 | (define-minor-mode global-ede-mode |
559 | "Toggle global EDE (Emacs Development Environment) mode. | |
ac6c8639 CY |
560 | With a prefix argument ARG, enable global EDE mode if ARG is |
561 | positive, and disable it otherwise. If called from Lisp, enable | |
562 | the mode if ARG is omitted or nil. | |
715f35a5 CY |
563 | |
564 | This global minor mode enables `ede-minor-mode' in all buffers in | |
565 | an EDE controlled project." | |
566 | :global t | |
567 | :group 'ede | |
568 | (if global-ede-mode | |
569 | ;; Turn on global-ede-mode | |
570 | (progn | |
ebf5c4f5 CY |
571 | (if semantic-mode |
572 | (define-key cedet-menu-map [cedet-menu-separator] '("--"))) | |
715f35a5 CY |
573 | (add-hook 'semanticdb-project-predicate-functions 'ede-directory-project-p) |
574 | (add-hook 'semanticdb-project-root-functions 'ede-toplevel-project-or-nil) | |
575 | (add-hook 'ecb-source-path-functions 'ede-ecb-project-paths) | |
890f7890 DE |
576 | ;; Append our hook to the end. This allows mode-local to finish |
577 | ;; it's stuff before we start doing misc file loads, etc. | |
578 | (add-hook 'find-file-hook 'ede-turn-on-hook t) | |
715f35a5 CY |
579 | (add-hook 'dired-mode-hook 'ede-turn-on-hook) |
580 | (add-hook 'kill-emacs-hook 'ede-save-cache) | |
581 | (ede-load-cache) | |
62a81506 | 582 | (ede-reset-all-buffers)) |
715f35a5 | 583 | ;; Turn off global-ede-mode |
ebf5c4f5 | 584 | (define-key cedet-menu-map [cedet-menu-separator] nil) |
715f35a5 CY |
585 | (remove-hook 'semanticdb-project-predicate-functions 'ede-directory-project-p) |
586 | (remove-hook 'semanticdb-project-root-functions 'ede-toplevel-project-or-nil) | |
587 | (remove-hook 'ecb-source-path-functions 'ede-ecb-project-paths) | |
588 | (remove-hook 'find-file-hook 'ede-turn-on-hook) | |
589 | (remove-hook 'dired-mode-hook 'ede-turn-on-hook) | |
590 | (remove-hook 'kill-emacs-hook 'ede-save-cache) | |
591 | (ede-save-cache) | |
62a81506 | 592 | (ede-reset-all-buffers))) |
acc33231 CY |
593 | |
594 | (defvar ede-ignored-file-alist | |
595 | '( "\\.cvsignore$" | |
596 | "\\.#" | |
597 | "~$" | |
598 | ) | |
599 | "List of file name patters that EDE will never ask about.") | |
600 | ||
601 | (defun ede-ignore-file (filename) | |
602 | "Should we ignore FILENAME?" | |
603 | (let ((any nil) | |
604 | (F ede-ignored-file-alist)) | |
605 | (while (and (not any) F) | |
606 | (when (string-match (car F) filename) | |
607 | (setq any t)) | |
608 | (setq F (cdr F))) | |
609 | any)) | |
610 | ||
611 | (defun ede-auto-add-to-target () | |
612 | "Look for a target that wants to own the current file. | |
613 | Follow the preference set with `ede-auto-add-method' and get the list | |
614 | of objects with the `ede-want-file-p' method." | |
cd1181db | 615 | (if ede-object (error "ede-object already defined for %s" (buffer-name))) |
acc33231 CY |
616 | (if (or (eq ede-auto-add-method 'never) |
617 | (ede-ignore-file (buffer-file-name))) | |
618 | nil | |
619 | (let (wants desires) | |
620 | ;; Find all the objects. | |
621 | (setq wants (oref (ede-current-project) targets)) | |
622 | (while wants | |
623 | (if (ede-want-file-p (car wants) (buffer-file-name)) | |
624 | (setq desires (cons (car wants) desires))) | |
625 | (setq wants (cdr wants))) | |
626 | (if desires | |
627 | (cond ((or (eq ede-auto-add-method 'ask) | |
628 | (and (eq ede-auto-add-method 'multi-ask) | |
629 | (< 1 (length desires)))) | |
630 | (let* ((al (append | |
631 | ;; some defaults | |
632 | '(("none" . nil) | |
633 | ("new target" . new)) | |
634 | ;; If we are in an unparented subdir, | |
635 | ;; offer new a subproject | |
636 | (if (ede-directory-project-p default-directory) | |
637 | () | |
638 | '(("create subproject" . project))) | |
639 | ;; Here are the existing objects we want. | |
640 | (object-assoc-list 'name desires))) | |
641 | (case-fold-search t) | |
642 | (ans (completing-read | |
643 | (format "Add %s to target: " (buffer-file-name)) | |
644 | al nil t))) | |
645 | (setq ans (assoc ans al)) | |
646 | (cond ((eieio-object-p (cdr ans)) | |
647 | (ede-add-file (cdr ans))) | |
648 | ((eq (cdr ans) 'new) | |
649 | (ede-new-target)) | |
650 | (t nil)))) | |
651 | ((or (eq ede-auto-add-method 'always) | |
652 | (and (eq ede-auto-add-method 'multi-ask) | |
653 | (= 1 (length desires)))) | |
654 | (ede-add-file (car desires))) | |
655 | (t nil)))))) | |
656 | ||
657 | \f | |
658 | ;;; Interactive method invocations | |
659 | ;; | |
a62d5ee1 EL |
660 | (defun ede (dir) |
661 | "Start up EDE for directory DIR. | |
662 | If DIR has an existing project file, load it. | |
663 | Otherwise, create a new project for DIR." | |
664 | (interactive | |
665 | ;; When choosing a directory to turn on, and we see some directory here, | |
666 | ;; provide that as the default. | |
667 | (let* ((top (ede-toplevel-project default-directory)) | |
668 | (promptdflt (or top default-directory))) | |
669 | (list (read-directory-name "Project directory: " | |
670 | promptdflt promptdflt t)))) | |
671 | (unless (file-directory-p dir) | |
672 | (error "%s is not a directory" dir)) | |
673 | (when (ede-directory-get-open-project dir) | |
674 | (error "%s already has an open project associated with it" dir)) | |
675 | ||
676 | ;; Check if the directory has been added to the list of safe | |
677 | ;; directories. It can also add the directory to the safe list if | |
678 | ;; the user chooses. | |
679 | (if (ede-check-project-directory dir) | |
680 | (progn | |
62a81506 | 681 | ;; Load the project in DIR, or make one. |
a62d5ee1 EL |
682 | (ede-load-project-file dir) |
683 | ||
684 | ;; Check if we loaded anything on the previous line. | |
685 | (if (ede-current-project dir) | |
686 | ||
687 | ;; We successfully opened an existing project. Some open | |
688 | ;; buffers may also be referring to this project. | |
689 | ;; Resetting all the buffers will get them to also point | |
690 | ;; at this new open project. | |
62a81506 | 691 | (ede-reset-all-buffers) |
a62d5ee1 EL |
692 | |
693 | ;; ELSE | |
694 | ;; There was no project, so switch to `ede-new' which is how | |
695 | ;; a user can select a new kind of project to create. | |
696 | (let ((default-directory (expand-file-name dir))) | |
697 | (call-interactively 'ede-new)))) | |
698 | ||
699 | ;; If the proposed directory isn't safe, then say so. | |
700 | (error "%s is not an allowed project directory in `ede-project-directories'" | |
701 | dir))) | |
702 | ||
703 | (defun ede-check-project-directory (dir) | |
704 | "Check if DIR should be in `ede-project-directories'. | |
705 | If it is not, try asking the user if it should be added; if so, | |
706 | add it and save `ede-project-directories' via Customize. | |
d136f184 | 707 | Return nil if DIR should not be in `ede-project-directories'." |
a62d5ee1 EL |
708 | (setq dir (directory-file-name (expand-file-name dir))) ; strip trailing / |
709 | (or (eq ede-project-directories t) | |
710 | (and (functionp ede-project-directories) | |
711 | (funcall ede-project-directories dir)) | |
712 | ;; If `ede-project-directories' is a list, maybe add it. | |
713 | (when (listp ede-project-directories) | |
714 | (or (member dir ede-project-directories) | |
715 | (when (y-or-n-p (format "`%s' is not listed in `ede-project-directories'. | |
716 | Add it to the list of allowed project directories? " | |
717 | dir)) | |
718 | (push dir ede-project-directories) | |
719 | ;; If possible, save `ede-project-directories'. | |
720 | (if (or custom-file user-init-file) | |
721 | (let ((coding-system-for-read nil)) | |
722 | (customize-save-variable | |
723 | 'ede-project-directories | |
724 | ede-project-directories))) | |
725 | t))))) | |
acc33231 CY |
726 | |
727 | (defun ede-new (type &optional name) | |
cd1181db | 728 | "Create a new project starting from project type TYPE. |
acc33231 CY |
729 | Optional argument NAME is the name to give this project." |
730 | (interactive | |
731 | (list (completing-read "Project Type: " | |
732 | (object-assoc-list | |
733 | 'name | |
734 | (let* ((l ede-project-class-files) | |
735 | (cp (ede-current-project)) | |
e8cc7880 | 736 | (cs (when cp (eieio-object-class cp))) |
acc33231 CY |
737 | (r nil)) |
738 | (while l | |
739 | (if cs | |
740 | (if (eq (oref (car l) :class-sym) | |
741 | cs) | |
742 | (setq r (cons (car l) r))) | |
743 | (if (oref (car l) new-p) | |
744 | (setq r (cons (car l) r)))) | |
745 | (setq l (cdr l))) | |
746 | (when (not r) | |
747 | (if cs | |
748 | (error "No valid interactive sub project types for %s" | |
749 | cs) | |
750 | (error "EDE error: Can't fin project types to create"))) | |
751 | r) | |
752 | ) | |
753 | nil t))) | |
cb85c0d8 | 754 | (require 'ede/custom) |
acc33231 CY |
755 | ;; Make sure we have a valid directory |
756 | (when (not (file-exists-p default-directory)) | |
bd2afec2 | 757 | (error "Cannot create project in non-existent directory %s" default-directory)) |
acc33231 CY |
758 | (when (not (file-writable-p default-directory)) |
759 | (error "No write permissions for %s" default-directory)) | |
a62d5ee1 EL |
760 | (unless (ede-check-project-directory default-directory) |
761 | (error "%s is not an allowed project directory in `ede-project-directories'" | |
762 | default-directory)) | |
763 | ;; Make sure the project directory is loadable in the future. | |
764 | (ede-check-project-directory default-directory) | |
acc33231 CY |
765 | ;; Create the project |
766 | (let* ((obj (object-assoc type 'name ede-project-class-files)) | |
767 | (nobj (let ((f (oref obj file)) | |
768 | (pf (oref obj proj-file))) | |
769 | ;; We are about to make something new, changing the | |
770 | ;; state of existing directories. | |
771 | (ede-project-directory-remove-hash default-directory) | |
772 | ;; Make sure this class gets loaded! | |
773 | (require f) | |
774 | (make-instance (oref obj class-sym) | |
775 | :name (or name (read-string "Name: ")) | |
776 | :directory default-directory | |
777 | :file (cond ((stringp pf) | |
778 | (expand-file-name pf)) | |
779 | ((fboundp pf) | |
780 | (funcall pf)) | |
781 | (t | |
782 | (error | |
783 | "Unknown file name specifier %S" | |
784 | pf))) | |
785 | :targets nil))) | |
786 | (inits (oref obj initializers))) | |
787 | ;; Force the name to match for new objects. | |
e8cc7880 | 788 | (eieio-object-set-name-string nobj (oref nobj :name)) |
acc33231 CY |
789 | ;; Handle init args. |
790 | (while inits | |
791 | (eieio-oset nobj (car inits) (car (cdr inits))) | |
792 | (setq inits (cdr (cdr inits)))) | |
793 | (let ((pp (ede-parent-project))) | |
794 | (when pp | |
795 | (ede-add-subproject pp nobj) | |
796 | (ede-commit-project pp))) | |
797 | (ede-commit-project nobj)) | |
a62d5ee1 EL |
798 | ;; Once the project is created, load it again. This used to happen |
799 | ;; lazily, but with project loading occurring less often and with | |
800 | ;; security in mind, this is now the safe time to reload. | |
801 | (ede-load-project-file default-directory) | |
acc33231 CY |
802 | ;; Have the menu appear |
803 | (setq ede-minor-mode t) | |
804 | ;; Allert the user | |
805 | (message "Project created and saved. You may now create targets.")) | |
806 | ||
807 | (defmethod ede-add-subproject ((proj-a ede-project) proj-b) | |
808 | "Add into PROJ-A, the subproject PROJ-B." | |
809 | (oset proj-a subproj (cons proj-b (oref proj-a subproj)))) | |
810 | ||
acc33231 CY |
811 | (defun ede-invoke-method (sym &rest args) |
812 | "Invoke method SYM on the current buffer's project object. | |
cd1181db | 813 | ARGS are additional arguments to pass to method SYM." |
acc33231 CY |
814 | (if (not ede-object) |
815 | (error "Cannot invoke %s for %s" (symbol-name sym) | |
816 | (buffer-name))) | |
817 | ;; Always query a target. There should never be multiple | |
818 | ;; projects in a single buffer. | |
819 | (apply sym (ede-singular-object "Target: ") args)) | |
820 | ||
821 | (defun ede-rescan-toplevel () | |
822 | "Rescan all project files." | |
823 | (interactive) | |
a62d5ee1 EL |
824 | (if (not (ede-directory-get-open-project default-directory)) |
825 | ;; This directory isn't open. Can't rescan. | |
826 | (error "Attempt to rescan a project that isn't open") | |
827 | ||
828 | ;; Continue | |
829 | (let ((toppath (ede-toplevel-project default-directory)) | |
830 | (ede-deep-rescan t)) | |
831 | ||
832 | (project-rescan (ede-load-project-file toppath)) | |
62a81506 | 833 | (ede-reset-all-buffers)))) |
acc33231 CY |
834 | |
835 | (defun ede-new-target (&rest args) | |
836 | "Create a new target specific to this type of project file. | |
837 | Different projects accept different arguments ARGS. | |
838 | Typically you can specify NAME, target TYPE, and AUTOADD, where AUTOADD is | |
839 | a string \"y\" or \"n\", which answers the y/n question done interactively." | |
840 | (interactive) | |
841 | (apply 'project-new-target (ede-current-project) args) | |
62a81506 CY |
842 | (when (and buffer-file-name |
843 | (not (file-directory-p buffer-file-name))) | |
844 | (setq ede-object nil) | |
845 | (setq ede-object (ede-buffer-object (current-buffer))) | |
846 | (ede-apply-target-options))) | |
acc33231 CY |
847 | |
848 | (defun ede-new-target-custom () | |
849 | "Create a new target specific to this type of project file." | |
850 | (interactive) | |
851 | (project-new-target-custom (ede-current-project))) | |
852 | ||
853 | (defun ede-delete-target (target) | |
854 | "Delete TARGET from the current project." | |
855 | (interactive (list | |
856 | (let ((ede-object (ede-current-project))) | |
857 | (ede-invoke-method 'project-interactive-select-target | |
858 | "Target: ")))) | |
859 | ;; Find all sources in buffers associated with the condemned buffer. | |
860 | (let ((condemned (ede-target-buffers target))) | |
861 | (project-delete-target target) | |
862 | ;; Loop over all project controlled buffers | |
863 | (save-excursion | |
864 | (while condemned | |
865 | (set-buffer (car condemned)) | |
866 | (setq ede-object nil) | |
867 | (setq ede-object (ede-buffer-object (current-buffer))) | |
868 | (setq condemned (cdr condemned)))) | |
869 | (ede-apply-target-options))) | |
870 | ||
871 | (defun ede-add-file (target) | |
872 | "Add the current buffer to a TARGET in the current project." | |
873 | (interactive (list | |
874 | (let ((ede-object (ede-current-project))) | |
875 | (ede-invoke-method 'project-interactive-select-target | |
876 | "Target: ")))) | |
877 | (when (stringp target) | |
878 | (let* ((proj (ede-current-project)) | |
879 | (ob (object-assoc-list 'name (oref proj targets)))) | |
880 | (setq target (cdr (assoc target ob))))) | |
881 | ||
882 | (when (not target) | |
883 | (error "Could not find specified target %S" target)) | |
884 | ||
885 | (project-add-file target (buffer-file-name)) | |
886 | (setq ede-object nil) | |
62a81506 CY |
887 | |
888 | ;; Setup buffer local variables. | |
889 | (ede-initialize-state-current-buffer) | |
890 | ||
acc33231 CY |
891 | (when (not ede-object) |
892 | (error "Can't add %s to target %s: Wrong file type" | |
893 | (file-name-nondirectory (buffer-file-name)) | |
e8cc7880 | 894 | (eieio-object-name target))) |
acc33231 CY |
895 | (ede-apply-target-options)) |
896 | ||
897 | (defun ede-remove-file (&optional force) | |
898 | "Remove the current file from targets. | |
899 | Optional argument FORCE forces the file to be removed without asking." | |
900 | (interactive "P") | |
901 | (if (not ede-object) | |
902 | (error "Cannot invoke remove-file for %s" (buffer-name))) | |
903 | (let ((eo (if (listp ede-object) | |
904 | (prog1 | |
905 | ede-object | |
906 | (setq force nil)) | |
907 | (list ede-object)))) | |
908 | (while eo | |
909 | (if (or force (y-or-n-p (format "Remove from %s? " (ede-name (car eo))))) | |
910 | (project-remove-file (car eo) (buffer-file-name))) | |
911 | (setq eo (cdr eo))) | |
912 | (setq ede-object nil) | |
913 | (setq ede-object (ede-buffer-object (current-buffer))) | |
914 | (ede-apply-target-options))) | |
915 | ||
916 | (defun ede-edit-file-target () | |
917 | "Enter the project file to hand edit the current buffer's target." | |
918 | (interactive) | |
919 | (ede-invoke-method 'project-edit-file-target)) | |
920 | ||
921 | (defun ede-compile-project () | |
922 | "Compile the current project." | |
923 | (interactive) | |
924 | ;; @TODO - This just wants the root. There should be a better way. | |
925 | (let ((cp (ede-current-project))) | |
926 | (while (ede-parent-project cp) | |
927 | (setq cp (ede-parent-project cp))) | |
928 | (let ((ede-object cp)) | |
929 | (ede-invoke-method 'project-compile-project)))) | |
930 | ||
931 | (defun ede-compile-selected (target) | |
932 | "Compile some TARGET from the current project." | |
933 | (interactive (list (project-interactive-select-target (ede-current-project) | |
934 | "Target to Build: "))) | |
935 | (project-compile-target target)) | |
936 | ||
937 | (defun ede-compile-target () | |
938 | "Compile the current buffer's associated target." | |
939 | (interactive) | |
940 | (ede-invoke-method 'project-compile-target)) | |
941 | ||
942 | (defun ede-debug-target () | |
bd2afec2 | 943 | "Debug the current buffer's associated target." |
acc33231 CY |
944 | (interactive) |
945 | (ede-invoke-method 'project-debug-target)) | |
946 | ||
67d3ffe4 | 947 | (defun ede-run-target () |
fa5f7c5f | 948 | "Run the current buffer's associated target." |
67d3ffe4 CY |
949 | (interactive) |
950 | (ede-invoke-method 'project-run-target)) | |
951 | ||
acc33231 CY |
952 | (defun ede-make-dist () |
953 | "Create a distribution from the current project." | |
954 | (interactive) | |
cb85c0d8 | 955 | (let ((ede-object (ede-toplevel))) |
acc33231 CY |
956 | (ede-invoke-method 'project-make-dist))) |
957 | ||
acc33231 CY |
958 | \f |
959 | ;;; EDE project target baseline methods. | |
960 | ;; | |
961 | ;; If you are developing a new project type, you need to implement | |
962 | ;; all of these methods, unless, of course, they do not make sense | |
963 | ;; for your particular project. | |
964 | ;; | |
965 | ;; Your targets should inherit from `ede-target', and your project | |
966 | ;; files should inherit from `ede-project'. Create the appropriate | |
967 | ;; methods based on those below. | |
968 | ||
969 | (defmethod project-interactive-select-target ((this ede-project-placeholder) prompt) | |
cb85c0d8 | 970 | ; checkdoc-params: (prompt) |
acc33231 | 971 | "Make sure placeholder THIS is replaced with the real thing, and pass through." |
cb85c0d8 | 972 | (project-interactive-select-target this prompt)) |
acc33231 CY |
973 | |
974 | (defmethod project-interactive-select-target ((this ede-project) prompt) | |
975 | "Interactively query for a target that exists in project THIS. | |
976 | Argument PROMPT is the prompt to use when querying the user for a target." | |
977 | (let ((ob (object-assoc-list 'name (oref this targets)))) | |
978 | (cdr (assoc (completing-read prompt ob nil t) ob)))) | |
979 | ||
980 | (defmethod project-add-file ((this ede-project-placeholder) file) | |
cb85c0d8 | 981 | ; checkdoc-params: (file) |
acc33231 | 982 | "Make sure placeholder THIS is replaced with the real thing, and pass through." |
cb85c0d8 | 983 | (project-add-file this file)) |
acc33231 CY |
984 | |
985 | (defmethod project-add-file ((ot ede-target) file) | |
986 | "Add the current buffer into project project target OT. | |
987 | Argument FILE is the file to add." | |
e8cc7880 | 988 | (error "add-file not supported by %s" (eieio-object-name ot))) |
acc33231 CY |
989 | |
990 | (defmethod project-remove-file ((ot ede-target) fnnd) | |
991 | "Remove the current buffer from project target OT. | |
992 | Argument FNND is an argument." | |
e8cc7880 | 993 | (error "remove-file not supported by %s" (eieio-object-name ot))) |
acc33231 CY |
994 | |
995 | (defmethod project-edit-file-target ((ot ede-target)) | |
cd1181db | 996 | "Edit the target OT associated with this file." |
acc33231 CY |
997 | (find-file (oref (ede-current-project) file))) |
998 | ||
999 | (defmethod project-new-target ((proj ede-project) &rest args) | |
1000 | "Create a new target. It is up to the project PROJ to get the name." | |
e8cc7880 | 1001 | (error "new-target not supported by %s" (eieio-object-name proj))) |
acc33231 CY |
1002 | |
1003 | (defmethod project-new-target-custom ((proj ede-project)) | |
1004 | "Create a new target. It is up to the project PROJ to get the name." | |
e8cc7880 | 1005 | (error "New-target-custom not supported by %s" (eieio-object-name proj))) |
acc33231 CY |
1006 | |
1007 | (defmethod project-delete-target ((ot ede-target)) | |
fa5f7c5f | 1008 | "Delete the current target OT from its parent project." |
e8cc7880 | 1009 | (error "add-file not supported by %s" (eieio-object-name ot))) |
acc33231 CY |
1010 | |
1011 | (defmethod project-compile-project ((obj ede-project) &optional command) | |
1012 | "Compile the entire current project OBJ. | |
1013 | Argument COMMAND is the command to use when compiling." | |
e8cc7880 | 1014 | (error "compile-project not supported by %s" (eieio-object-name obj))) |
acc33231 CY |
1015 | |
1016 | (defmethod project-compile-target ((obj ede-target) &optional command) | |
1017 | "Compile the current target OBJ. | |
1018 | Argument COMMAND is the command to use for compiling the target." | |
e8cc7880 | 1019 | (error "compile-target not supported by %s" (eieio-object-name obj))) |
acc33231 CY |
1020 | |
1021 | (defmethod project-debug-target ((obj ede-target)) | |
1022 | "Run the current project target OBJ in a debugger." | |
e8cc7880 | 1023 | (error "debug-target not supported by %s" (eieio-object-name obj))) |
acc33231 | 1024 | |
67d3ffe4 CY |
1025 | (defmethod project-run-target ((obj ede-target)) |
1026 | "Run the current project target OBJ." | |
e8cc7880 | 1027 | (error "run-target not supported by %s" (eieio-object-name obj))) |
67d3ffe4 | 1028 | |
acc33231 CY |
1029 | (defmethod project-make-dist ((this ede-project)) |
1030 | "Build a distribution for the project based on THIS project." | |
e8cc7880 | 1031 | (error "Make-dist not supported by %s" (eieio-object-name this))) |
acc33231 CY |
1032 | |
1033 | (defmethod project-dist-files ((this ede-project)) | |
fa5f7c5f | 1034 | "Return a list of files that constitute a distribution of THIS project." |
e8cc7880 | 1035 | (error "Dist-files is not supported by %s" (eieio-object-name this))) |
acc33231 CY |
1036 | |
1037 | (defmethod project-rescan ((this ede-project)) | |
cd1181db | 1038 | "Rescan the EDE project THIS." |
e8cc7880 | 1039 | (error "Rescanning a project is not supported by %s" (eieio-object-name this))) |
acc33231 CY |
1040 | |
1041 | (defun ede-ecb-project-paths () | |
1042 | "Return a list of all paths for all active EDE projects. | |
1043 | This functions is meant for use with ECB." | |
1044 | (let ((p ede-projects) | |
1045 | (d nil)) | |
1046 | (while p | |
1047 | (setq d (cons (file-name-directory (oref (car p) file)) | |
1048 | d) | |
1049 | p (cdr p))) | |
1050 | d)) | |
cb85c0d8 EL |
1051 | |
1052 | ;;; PROJECT LOADING/TRACKING | |
acc33231 CY |
1053 | ;; |
1054 | (defun ede-add-project-to-global-list (proj) | |
1055 | "Add the project PROJ to the master list of projects. | |
1056 | On success, return the added project." | |
1057 | (when (not proj) | |
1058 | (error "No project created to add to master list")) | |
1059 | (when (not (eieio-object-p proj)) | |
cd1181db | 1060 | (error "Attempt to add non-object to master project list")) |
acc33231 CY |
1061 | (when (not (obj-of-class-p proj ede-project-placeholder)) |
1062 | (error "Attempt to add a non-project to the ede projects list")) | |
1063 | (add-to-list 'ede-projects proj) | |
1064 | proj) | |
1065 | ||
890f7890 DE |
1066 | (defun ede-flush-deleted-projects () |
1067 | "Scan the projects list for projects which no longer exist. | |
1068 | Flush the dead projects from the project cache." | |
1069 | (interactive) | |
1070 | (let ((dead nil)) | |
1071 | (dolist (P ede-projects) | |
1072 | (when (not (file-exists-p (oref P :file))) | |
1073 | (add-to-list 'dead P))) | |
1074 | (dolist (D dead) | |
1075 | (setq ede-projects (remove D ede-projects))) | |
1076 | )) | |
1077 | ||
acc33231 CY |
1078 | (defun ede-load-project-file (dir &optional rootreturn) |
1079 | "Project file independent way to read a project in from DIR. | |
1080 | Optional ROOTRETURN will return the root project for DIR." | |
1081 | ;; Only load if something new is going on. Flush the dirhash. | |
1082 | (ede-project-directory-remove-hash dir) | |
1083 | ;; Do the load | |
1084 | ;;(message "EDE LOAD : %S" file) | |
1085 | (let* ((file dir) | |
a62d5ee1 | 1086 | (path (file-name-as-directory (expand-file-name dir))) |
acc33231 CY |
1087 | (pfc (ede-directory-project-p path)) |
1088 | (toppath nil) | |
1089 | (o nil)) | |
1090 | (cond | |
1091 | ((not pfc) | |
1092 | ;; @TODO - Do we really need to scan? Is this a waste of time? | |
1093 | ;; Scan upward for a the next project file style. | |
1094 | (let ((p path)) | |
1095 | (while (and p (not (ede-directory-project-p p))) | |
1096 | (setq p (ede-up-directory p))) | |
1097 | (if p (ede-load-project-file p) | |
1098 | nil) | |
1099 | ;; recomment as we go | |
cb85c0d8 | 1100 | ;;nil |
acc33231 | 1101 | )) |
91af3942 | 1102 | ;; Do nothing if we are building an EDE project already. |
acc33231 CY |
1103 | (ede-constructing |
1104 | nil) | |
1105 | ;; Load in the project in question. | |
1106 | (t | |
1107 | (setq toppath (ede-toplevel-project path)) | |
1108 | ;; We found the top-most directory. Check to see if we already | |
cb85c0d8 | 1109 | ;; have an object defining its project. |
acc33231 CY |
1110 | (setq pfc (ede-directory-project-p toppath t)) |
1111 | ||
1112 | ;; See if it's been loaded before | |
1113 | (setq o (object-assoc (ede-dir-to-projectfile pfc toppath) 'file | |
1114 | ede-projects)) | |
a62d5ee1 EL |
1115 | |
1116 | ;; If not open yet, load it. | |
1117 | (unless o | |
1118 | (let ((ede-constructing pfc)) | |
1119 | (setq o (ede-auto-load-project pfc toppath)))) | |
acc33231 CY |
1120 | |
1121 | ;; Return the found root project. | |
1122 | (when rootreturn (set rootreturn o)) | |
1123 | ||
1124 | (let (tocheck found) | |
1125 | ;; Now find the project file belonging to FILE! | |
1126 | (setq tocheck (list o)) | |
1127 | (setq file (ede-dir-to-projectfile pfc (expand-file-name path))) | |
1128 | (while (and tocheck (not found)) | |
1129 | (let ((newbits nil)) | |
1130 | (when (car tocheck) | |
1131 | (if (string= file (oref (car tocheck) file)) | |
1132 | (setq found (car tocheck))) | |
1133 | (setq newbits (oref (car tocheck) subproj))) | |
1134 | (setq tocheck | |
1135 | (append (cdr tocheck) newbits)))) | |
1136 | (if (not found) | |
1137 | (message "No project for %s, but passes project-p test" file) | |
1138 | ;; Now that the file has been reset inside the project object, do | |
1139 | ;; the cache maintenance. | |
1140 | (setq ede-project-cache-files | |
1141 | (delete (oref found file) ede-project-cache-files))) | |
1142 | found))))) | |
1143 | ||
cb85c0d8 EL |
1144 | ;;; PROJECT ASSOCIATIONS |
1145 | ;; | |
1146 | ;; Moving between relative projects. Associating between buffers and | |
1147 | ;; projects. | |
1148 | ||
acc33231 CY |
1149 | (defun ede-parent-project (&optional obj) |
1150 | "Return the project belonging to the parent directory. | |
cb85c0d8 | 1151 | Return nil if there is no previous directory. |
acc33231 CY |
1152 | Optional argument OBJ is an object to find the parent of." |
1153 | (let* ((proj (or obj ede-object-project)) ;; Current project. | |
1154 | (root (if obj (ede-project-root obj) | |
1155 | ede-object-root-project))) | |
1156 | ;; This case is a SHORTCUT if the project has defined | |
1157 | ;; a way to calculate the project root. | |
1158 | (if (and root proj (eq root proj)) | |
1159 | nil ;; we are at the root. | |
1160 | ;; Else, we may have a nil proj or root. | |
1161 | (let* ((thisdir (if obj (oref obj directory) | |
1162 | default-directory)) | |
1163 | (updir (ede-up-directory thisdir))) | |
1164 | (when updir | |
1165 | ;; If there was no root, perhaps we can derive it from | |
1166 | ;; updir now. | |
1167 | (let ((root (or root (ede-directory-get-toplevel-open-project updir)))) | |
1168 | (or | |
1169 | ;; This lets us find a subproject under root based on updir. | |
1170 | (and root | |
1171 | (ede-find-subproject-for-directory root updir)) | |
1172 | ;; Try the all structure based search. | |
a62d5ee1 | 1173 | (ede-directory-get-open-project updir)))))))) |
acc33231 CY |
1174 | |
1175 | (defun ede-current-project (&optional dir) | |
1176 | "Return the current project file. | |
1177 | If optional DIR is provided, get the project for DIR instead." | |
e8cc7880 DE |
1178 | ;; If it matches the current directory, do we have a pre-existing project? |
1179 | (let ((proj (when (and (or (not dir) (string= dir default-directory)) | |
1180 | ede-object-project) | |
1181 | ede-object-project))) | |
acc33231 | 1182 | ;; No current project. |
e8cc7880 DE |
1183 | (if proj |
1184 | proj | |
acc33231 | 1185 | (let* ((ldir (or dir default-directory))) |
e8cc7880 | 1186 | (ede-directory-get-open-project ldir))))) |
acc33231 | 1187 | |
cb85c0d8 | 1188 | (defun ede-buffer-object (&optional buffer projsym) |
acc33231 | 1189 | "Return the target object for BUFFER. |
cb85c0d8 EL |
1190 | This function clears cached values and recalculates. |
1191 | Optional PROJSYM is a symbol, which will be set to the project | |
1192 | that contains the target that becomes buffer's object." | |
acc33231 CY |
1193 | (save-excursion |
1194 | (if (not buffer) (setq buffer (current-buffer))) | |
1195 | (set-buffer buffer) | |
1196 | (setq ede-object nil) | |
cb85c0d8 EL |
1197 | (let* ((localpo (ede-current-project)) |
1198 | (po localpo) | |
1199 | (top (ede-toplevel po))) | |
1200 | (if po (setq ede-object (ede-find-target po buffer))) | |
1201 | ;; If we get nothing, go with the backup plan of slowly | |
1202 | ;; looping upward | |
1203 | (while (and (not ede-object) (not (eq po top))) | |
1204 | (setq po (ede-parent-project po)) | |
1205 | (if po (setq ede-object (ede-find-target po buffer)))) | |
1206 | ;; Filter down to 1 project if there are dups. | |
1207 | (if (= (length ede-object) 1) | |
1208 | (setq ede-object (car ede-object))) | |
1209 | ;; Track the project, if needed. | |
1210 | (when (and projsym (symbolp projsym)) | |
1211 | (if ede-object | |
1212 | ;; If we found a target, then PO is the | |
1213 | ;; project to use. | |
1214 | (set projsym po) | |
1215 | ;; If there is no ede-object, then the projsym | |
1216 | ;; is whichever part of the project is most local. | |
1217 | (set projsym localpo)) | |
1218 | )) | |
1219 | ;; Return our findings. | |
acc33231 CY |
1220 | ede-object)) |
1221 | ||
1222 | (defmethod ede-target-in-project-p ((proj ede-project) target) | |
1223 | "Is PROJ the parent of TARGET? | |
1224 | If TARGET belongs to a subproject, return that project file." | |
1225 | (if (and (slot-boundp proj 'targets) | |
1226 | (memq target (oref proj targets))) | |
1227 | proj | |
1228 | (let ((s (oref proj subproj)) | |
1229 | (ans nil)) | |
1230 | (while (and s (not ans)) | |
1231 | (setq ans (ede-target-in-project-p (car s) target)) | |
1232 | (setq s (cdr s))) | |
1233 | ans))) | |
1234 | ||
1235 | (defun ede-target-parent (target) | |
1236 | "Return the project which is the parent of TARGET. | |
1237 | It is recommended you track the project a different way as this function | |
1238 | could become slow in time." | |
a62d5ee1 EL |
1239 | (or ede-object-project |
1240 | ;; If not cached, derive it from the current directory of the target. | |
1241 | (let ((ans nil) (projs ede-projects)) | |
1242 | (while (and (not ans) projs) | |
1243 | (setq ans (ede-target-in-project-p (car projs) target) | |
1244 | projs (cdr projs))) | |
1245 | ans))) | |
acc33231 | 1246 | |
acc33231 CY |
1247 | (defmethod ede-find-target ((proj ede-project) buffer) |
1248 | "Fetch the target in PROJ belonging to BUFFER or nil." | |
0816d744 | 1249 | (with-current-buffer buffer |
62a81506 CY |
1250 | |
1251 | ;; We can do a short-ut if ede-object local variable is set. | |
1252 | (if ede-object | |
1253 | ;; If the buffer is already loaded with good EDE stuff, make sure the | |
1254 | ;; saved project is the project we're looking for. | |
1255 | (when (and ede-object-project (eq proj ede-object-project)) ede-object) | |
1256 | ||
1257 | ;; If the variable wasn't set, then we are probably initializing the buffer. | |
1258 | ;; In that case, search the file system. | |
1259 | (if (ede-buffer-mine proj buffer) | |
1260 | proj | |
1261 | (let ((targets (oref proj targets)) | |
1262 | (f nil)) | |
1263 | (while targets | |
1264 | (if (ede-buffer-mine (car targets) buffer) | |
1265 | (setq f (cons (car targets) f))) | |
1266 | (setq targets (cdr targets))) | |
1267 | f))))) | |
acc33231 CY |
1268 | |
1269 | (defmethod ede-target-buffer-in-sourcelist ((this ede-target) buffer source) | |
1270 | "Return non-nil if object THIS is in BUFFER to a SOURCE list. | |
1271 | Handles complex path issues." | |
1272 | (member (ede-convert-path this (buffer-file-name buffer)) source)) | |
1273 | ||
1274 | (defmethod ede-buffer-mine ((this ede-project) buffer) | |
1275 | "Return non-nil if object THIS lays claim to the file in BUFFER." | |
1276 | nil) | |
1277 | ||
1278 | (defmethod ede-buffer-mine ((this ede-target) buffer) | |
1279 | "Return non-nil if object THIS lays claim to the file in BUFFER." | |
1280 | (condition-case nil | |
1281 | (ede-target-buffer-in-sourcelist this buffer (oref this source)) | |
1282 | ;; An error implies a bad match. | |
1283 | (error nil))) | |
1284 | ||
1285 | \f | |
1286 | ;;; Project mapping | |
1287 | ;; | |
1288 | (defun ede-project-buffers (project) | |
1289 | "Return a list of all active buffers controlled by PROJECT. | |
1290 | This includes buffers controlled by a specific target of PROJECT." | |
1291 | (let ((bl (buffer-list)) | |
1292 | (pl nil)) | |
1293 | (while bl | |
0816d744 | 1294 | (with-current-buffer (car bl) |
62a81506 CY |
1295 | (when (and ede-object (ede-find-target project (car bl))) |
1296 | (setq pl (cons (car bl) pl)))) | |
acc33231 CY |
1297 | (setq bl (cdr bl))) |
1298 | pl)) | |
1299 | ||
1300 | (defun ede-target-buffers (target) | |
1301 | "Return a list of buffers that are controlled by TARGET." | |
1302 | (let ((bl (buffer-list)) | |
1303 | (pl nil)) | |
1304 | (while bl | |
0816d744 | 1305 | (with-current-buffer (car bl) |
acc33231 CY |
1306 | (if (if (listp ede-object) |
1307 | (memq target ede-object) | |
1308 | (eq ede-object target)) | |
1309 | (setq pl (cons (car bl) pl)))) | |
1310 | (setq bl (cdr bl))) | |
1311 | pl)) | |
1312 | ||
1313 | (defun ede-buffers () | |
bd2afec2 | 1314 | "Return a list of all buffers controlled by an EDE object." |
acc33231 CY |
1315 | (let ((bl (buffer-list)) |
1316 | (pl nil)) | |
1317 | (while bl | |
0816d744 | 1318 | (with-current-buffer (car bl) |
acc33231 CY |
1319 | (if ede-object |
1320 | (setq pl (cons (car bl) pl)))) | |
1321 | (setq bl (cdr bl))) | |
1322 | pl)) | |
1323 | ||
1324 | (defun ede-map-buffers (proc) | |
bd2afec2 | 1325 | "Execute PROC on all buffers controlled by EDE." |
acc33231 CY |
1326 | (mapcar proc (ede-buffers))) |
1327 | ||
1328 | (defmethod ede-map-project-buffers ((this ede-project) proc) | |
1329 | "For THIS, execute PROC on all buffers belonging to THIS." | |
1330 | (mapcar proc (ede-project-buffers this))) | |
1331 | ||
1332 | (defmethod ede-map-target-buffers ((this ede-target) proc) | |
1333 | "For THIS, execute PROC on all buffers belonging to THIS." | |
1334 | (mapcar proc (ede-target-buffers this))) | |
1335 | ||
1336 | ;; other types of mapping | |
1337 | (defmethod ede-map-subprojects ((this ede-project) proc) | |
1338 | "For object THIS, execute PROC on all direct subprojects. | |
1339 | This function does not apply PROC to sub-sub projects. | |
1340 | See also `ede-map-all-subprojects'." | |
1341 | (mapcar proc (oref this subproj))) | |
1342 | ||
1343 | (defmethod ede-map-all-subprojects ((this ede-project) allproc) | |
cd1181db | 1344 | "For object THIS, execute PROC on THIS and all subprojects. |
acc33231 CY |
1345 | This function also applies PROC to sub-sub projects. |
1346 | See also `ede-map-subprojects'." | |
1347 | (apply 'append | |
1348 | (list (funcall allproc this)) | |
1349 | (ede-map-subprojects | |
1350 | this | |
1351 | (lambda (sp) | |
1352 | (ede-map-all-subprojects sp allproc)) | |
1353 | ))) | |
1354 | ||
1355 | ;; (ede-map-all-subprojects (ede-load-project-file "../semantic/") (lambda (sp) (oref sp file))) | |
1356 | ||
1357 | (defmethod ede-map-targets ((this ede-project) proc) | |
1358 | "For object THIS, execute PROC on all targets." | |
1359 | (mapcar proc (oref this targets))) | |
1360 | ||
1361 | (defmethod ede-map-any-target-p ((this ede-project) proc) | |
1362 | "For project THIS, map PROC to all targets and return if any non-nil. | |
1363 | Return the first non-nil value returned by PROC." | |
b90caf50 | 1364 | (eval (cons 'or (ede-map-targets this proc)))) |
acc33231 CY |
1365 | |
1366 | \f | |
1367 | ;;; Some language specific methods. | |
1368 | ;; | |
1369 | ;; These items are needed by ede-cpp-root to add better support for | |
1370 | ;; configuring items for Semantic. | |
62a81506 CY |
1371 | |
1372 | ;; Generic paths | |
1373 | (defmethod ede-system-include-path ((this ede-project)) | |
1374 | "Get the system include path used by project THIS." | |
1375 | nil) | |
735135f9 | 1376 | |
62a81506 CY |
1377 | (defmethod ede-system-include-path ((this ede-target)) |
1378 | "Get the system include path used by project THIS." | |
1379 | nil) | |
1380 | ||
1381 | (defmethod ede-source-paths ((this ede-project) mode) | |
735135f9 | 1382 | "Get the base to all source trees in the current project for MODE. |
62a81506 CY |
1383 | For example, <root>/src for sources of c/c++, Java, etc, |
1384 | and <root>/doc for doc sources." | |
1385 | nil) | |
1386 | ||
1387 | ;; C/C++ | |
acc33231 CY |
1388 | (defun ede-apply-preprocessor-map () |
1389 | "Apply preprocessor tables onto the current buffer." | |
e8cc7880 DE |
1390 | ;; TODO - what if semantic-mode isn't enabled? |
1391 | ;; what if we never want to load a C mode? Does this matter? | |
1392 | ;; Note: This require is needed for the case where EDE ends up | |
1393 | ;; in the hook order before Semantic based hooks. | |
1394 | (require 'semantic/lex-spp) | |
62a81506 | 1395 | (when (and ede-object |
e8cc7880 | 1396 | (boundp 'semantic-lex-spp-project-macro-symbol-obarray)) |
1dc5c6f3 CY |
1397 | (let* ((objs ede-object) |
1398 | (map (ede-preprocessor-map (if (consp objs) | |
1399 | (car objs) | |
1400 | objs)))) | |
acc33231 CY |
1401 | (when map |
1402 | ;; We can't do a require for the below symbol. | |
e8cc7880 | 1403 | (setq semantic-lex-spp-project-macro-symbol-obarray |
1dc5c6f3 CY |
1404 | (semantic-lex-make-spp-table map))) |
1405 | (when (consp objs) | |
1406 | (message "Choosing preprocessor syms for project %s" | |
e8cc7880 | 1407 | (eieio-object-name (car objs))))))) |
acc33231 CY |
1408 | |
1409 | (defmethod ede-system-include-path ((this ede-project)) | |
1410 | "Get the system include path used by project THIS." | |
1411 | nil) | |
1412 | ||
1413 | (defmethod ede-preprocessor-map ((this ede-project)) | |
1414 | "Get the pre-processor map for project THIS." | |
1415 | nil) | |
1416 | ||
acc33231 CY |
1417 | (defmethod ede-preprocessor-map ((this ede-target)) |
1418 | "Get the pre-processor map for project THIS." | |
1419 | nil) | |
1420 | ||
62a81506 CY |
1421 | ;; Java |
1422 | (defmethod ede-java-classpath ((this ede-project)) | |
1423 | "Return the classpath for this project." | |
1424 | ;; @TODO - Can JDEE add something here? | |
1425 | nil) | |
1426 | ||
acc33231 CY |
1427 | \f |
1428 | ;;; Project-local variables | |
62a81506 CY |
1429 | |
1430 | (defun ede-set (variable value &optional proj) | |
1431 | "Set the project local VARIABLE to VALUE. | |
1432 | If VARIABLE is not project local, just use set. Optional argument PROJ | |
1433 | is the project to use, instead of `ede-current-project'." | |
1434 | (interactive "sVariable: \nxExpression: ") | |
1435 | (let ((p (or proj (ede-toplevel))) | |
1436 | a) | |
1437 | ;; Make the change | |
1438 | (ede-make-project-local-variable variable p) | |
1439 | (ede-set-project-local-variable variable value p) | |
1440 | (ede-commit-local-variables p) | |
1441 | ||
1442 | ;; This is a heavy hammer, but will apply variables properly | |
1443 | ;; based on stacking between the toplevel and child projects. | |
1444 | (ede-map-buffers 'ede-apply-project-local-variables) | |
1445 | ||
1446 | value)) | |
1447 | ||
1448 | (defun ede-apply-project-local-variables (&optional buffer) | |
1449 | "Apply project local variables to the current buffer." | |
1450 | (with-current-buffer (or buffer (current-buffer)) | |
1451 | ;; Always apply toplevel variables. | |
1452 | (if (not (eq (ede-current-project) (ede-toplevel))) | |
1453 | (ede-set-project-variables (ede-toplevel))) | |
1454 | ;; Next apply more local project's variables. | |
1455 | (if (ede-current-project) | |
1456 | (ede-set-project-variables (ede-current-project))) | |
1457 | )) | |
1458 | ||
acc33231 CY |
1459 | (defun ede-make-project-local-variable (variable &optional project) |
1460 | "Make VARIABLE project-local to PROJECT." | |
62a81506 | 1461 | (if (not project) (setq project (ede-toplevel))) |
acc33231 CY |
1462 | (if (assoc variable (oref project local-variables)) |
1463 | nil | |
1464 | (oset project local-variables (cons (list variable) | |
62a81506 CY |
1465 | (oref project local-variables))))) |
1466 | ||
1467 | (defun ede-set-project-local-variable (variable value &optional project) | |
1468 | "Set VARIABLE to VALUE for PROJECT. | |
1469 | If PROJ isn't specified, use the current project. | |
1470 | This function only assigns the value within the project structure. | |
1471 | It does not apply the value to buffers." | |
1472 | (if (not project) (setq project (ede-toplevel))) | |
1473 | (let ((va (assoc variable (oref project local-variables)))) | |
1474 | (unless va | |
1475 | (error "Cannot set project variable until it is added with `ede-make-project-local-variable'")) | |
1476 | (setcdr va value))) | |
acc33231 CY |
1477 | |
1478 | (defmethod ede-set-project-variables ((project ede-project) &optional buffer) | |
1479 | "Set variables local to PROJECT in BUFFER." | |
1480 | (if (not buffer) (setq buffer (current-buffer))) | |
0816d744 SM |
1481 | (with-current-buffer buffer |
1482 | (dolist (v (oref project local-variables)) | |
1483 | (make-local-variable (car v)) | |
0816d744 | 1484 | (set (car v) (cdr v))))) |
acc33231 | 1485 | |
acc33231 CY |
1486 | (defmethod ede-commit-local-variables ((proj ede-project)) |
1487 | "Commit change to local variables in PROJ." | |
1488 | nil) | |
1489 | ||
acc33231 CY |
1490 | (provide 'ede) |
1491 | ||
1492 | ;; Include this last because it depends on ede. | |
1493 | (require 'ede/files) | |
1494 | ||
1495 | ;; If this does not occur after the provide, we can get a recursive | |
1496 | ;; load. Yuck! | |
1497 | (if (featurep 'speedbar) | |
1498 | (ede-speedbar-file-setup) | |
1499 | (add-hook 'speedbar-load-hook 'ede-speedbar-file-setup)) | |
1500 | ||
1501 | ;;; ede.el ends here |