comparison lisp/energize/energize-mode.el @ 0:376386a54a3c r19-14

Import from CVS: tag r19-14
author cvs
date Mon, 13 Aug 2007 08:45:50 +0200
parents
children ec9a17fef872
comparison
equal deleted inserted replaced
-1:000000000000 0:376386a54a3c
1 ;;; -*- Mode:Emacs-Lisp -*-
2 ;;; Copyright © 1991-1993 by Lucid, Inc. All Rights Reserved.
3
4 (eval-when-compile
5 (require 'etags))
6
7 ;; true if current-buffer is an energize buffer that does not support
8 ;; the real write-file and so has to do the special energize way of doing
9 ;; write-file that loses the annotations.
10 (defun energize-write-file-buffer-p ()
11 ;; (and (energize-buffer-p (current-buffer)
12 ;; (not (eq major-mode 'energize-project-mode)))
13 (energize-buffer-p (current-buffer)))
14
15
16 (defun energize-beginning-of-defun (&optional arg)
17 "Move point to the beginning of the current top-level form.
18 With a numeric argument, move back that many forms."
19 (interactive "_p")
20 (or arg (setq arg 1))
21 (if (not (energize-buffer-p (current-buffer)))
22 (error "Not an Energize buffer")
23 (if (< arg 0)
24 (energize-end-of-defun (- arg))
25 (while (> arg 0)
26 (or (bobp) (forward-char -1))
27 (while (and (not (bobp)) (null (energize-extent-at (point))))
28 (forward-char -1))
29 (let ((pos (point)))
30 (map-extents
31 (function
32 (lambda (extent dummy)
33 (if (< (setq pos (extent-start-position extent)) (point))
34 (goto-char pos))))
35 (current-buffer) (point) (point) nil t))
36 (setq arg (1- arg))))))
37
38 (defun energize-end-of-defun (&optional arg)
39 "Move point to the end of the current top-level form.
40 With a numeric argument, move forward over that many forms."
41 (interactive "_p")
42 (or arg (setq arg 1))
43 (if (not (energize-buffer-p (current-buffer)))
44 (error "Not an Energize buffer")
45 (if (< arg 0)
46 (energize-beginning-of-defun (- arg))
47 (while (> arg 0)
48 (or (eobp) (forward-char 1))
49 (while (and (not (eobp)) (null (energize-extent-at (point))))
50 (forward-char 1))
51 (let ((pos (point)))
52 (map-extents
53 (function
54 (lambda (extent dummy)
55 (if (> (setq pos (extent-end-position extent)) (point))
56 (goto-char pos))))
57 (current-buffer) (point) (point) nil t))
58 (setq arg (1- arg))))))
59
60
61 ;;; Patching Energize into file I/O via the standard hooks.
62
63 (defun energize-write-data-hook (name)
64 ;; for use as the last element of write-file-data-hooks
65 ;; in energize buffers.
66 (if (energize-buffer-p (current-buffer))
67 (progn
68 (message "saving %s to Energize..." name)
69 (energize-execute-command "save")
70 (energize-update-menubar)
71 (message "saved %s to Energize." name)
72 t)
73 nil))
74
75 (defun energize-revert-buffer-insert-file-contents-hook (file noconfirm)
76 ;; for use as the value of revert-buffer-insert-file-contents-function
77 ;; in energize buffers.
78 (if (not (energize-buffer-p (current-buffer)))
79 (error "energize-revert-buffer-hook called for a non-energize buffer"))
80 (widen)
81 (cond ((equal file buffer-file-name) ; reverting from energize
82 ;; Do the default as in files.el
83 (if (file-exists-p file)
84 (progn
85 ;; Bind buffer-file-name to nil
86 ;; so that we don't try to lock the file.
87 (let ((buffer-file-name nil))
88 (unlock-buffer)
89 (erase-buffer))
90 (insert-file-contents file t)))
91 ;; Then asks the extents from Energize
92 (energize-execute-command "revert"))
93 (t ; reverting from autosave
94 (if (not (file-exists-p file))
95 (error "File %s no longer exists!" file))
96 (erase-buffer)
97 (insert-file-contents file)))
98 t)
99
100
101 (defun energize-kill-buffer-hook ()
102 ;; for use as the value of kill-buffer-hook in energize buffers.
103 (if (energize-buffer-p (current-buffer))
104 (energize-request-kill-buffer (current-buffer))
105 (error "energize-kill-buffer-hook called on a non-energize buffer"))
106 t)
107
108
109 ;;;
110
111 (defun energize-edit-definition-default ()
112 (save-excursion
113 (if (not (memq (char-syntax (preceding-char)) '(?w ?_)))
114 (while (not (looking-at "\\sw\\|\\s_\\|\\'"))
115 (forward-char 1)))
116 (while (looking-at "\\sw\\|\\s_")
117 (forward-char 1))
118 (if (re-search-backward "\\sw\\|\\s_" nil t)
119 (progn (forward-char 1)
120 (buffer-substring (point)
121 (progn (forward-sexp -1)
122 (while (looking-at "\\s'")
123 (forward-char 1))
124 (point))))
125 nil)))
126
127 ;;; This prompts in the minibuffer, ##### with no completion.
128 (defun energize-edit-definition (def)
129 "If connected to Energize, the Energize database is used.
130 Otherwise, `find-tag' is invoked.
131 The X selection is used as a default, if it exists and contains no
132 newlines. Otherwise, the preceeding token is used as a default.
133 If invoked from a mouse command, prompting happens with a dialog box;
134 otherwise, the minibuffer is used."
135 (interactive
136 (if (and (connected-to-energize-p)
137 (or (menu-event-p last-command-event)
138 (button-press-event-p last-command-event)
139 (button-release-event-p last-command-event)))
140 '(nil)
141 (list
142 (let (default
143 def)
144 (cond ((x-selection-owner-p)
145 (setq default (x-get-selection))
146 (while (string-match "\\`[ \t\n]+" default)
147 (setq default (substring default (match-end 0))))
148 (while (string-match "[ \t\n]+\\'" default)
149 (setq default (substring default 0 (match-beginning 0))))
150 (if (string-match "[ \t\n]" default)
151 (setq default nil))))
152 (or default (setq default (energize-edit-definition-default)))
153 (setq def
154 (if (connected-to-energize-p)
155 (completing-read
156 (if default
157 (format "Edit definition [%s]: " default)
158 "Edit definition: ")
159 nil nil; 'energize-edit-def-predicate
160 nil nil)
161 (or (and (fboundp 'find-tag-tag) (fboundp 'find-tag-default))
162 (require 'tags "etags"))
163 (find-tag-tag "Edit definition: ")))
164 (if (or (equal "" def)
165 (equal '("") def))
166 (setq def default))
167 def))))
168 (if (connected-to-energize-p)
169 ;; FIXME - this should fall back on tags if it fails...we might be
170 ;; searching for elisp or something...
171 (energize-execute-command "editdef" () (if (consp def) (car def) def) t)
172 (find-tag def)))
173
174 (define-key global-map "\M-." 'energize-edit-definition)
175 (define-key global-map "\M-B" 'energize-build-a-target) ; M-Sh-B
176
177 (defun disconnect-from-energize-query ()
178 "Disconnect this emacs from the Energize server, after confirming."
179 (interactive)
180 (or (y-or-n-p "Disconnect from Energize? ") (error "not confirmed"))
181 (disconnect-from-energize))
182
183
184 ;;; Functions to add commands to the project buffers
185 (defun energize-insert-slots (got-to-top-p l)
186 (if (not (eq major-mode 'energize-project-mode))
187 (error "Command available only in project buffers"))
188 ;; move to a suitable place
189 (if got-to-top-p
190 (beginning-of-buffer)
191 (beginning-of-line))
192 ;; go before "Associated Projects" and "Related Files"
193 (if (or (search-backward "Related Projects:" () t)
194 (search-backward "Associated Files:" () t)
195 (looking-at "Related Projects:")
196 (looking-at "Associated Files:"))
197 (previous-line 2))
198 ;; find empty space
199 (while (and (not (looking-at "$"))
200 (not (eq (point) (point-max))))
201 (next-line 1))
202 (newline)
203 (save-excursion
204 (mapcar '(lambda (i) (insert i) (newline)) l))
205 ;; this is magic
206 (forward-char 18))
207
208 (defun energize-insert-rule ()
209 (interactive)
210 (energize-insert-slots
211 t
212 '(" Rules:"
213 " <rule>: lcc -Xez -c -g -Xa -o $object $source")))
214
215 (defun energize-insert-object-file-target ()
216 (interactive)
217 (energize-insert-slots
218 ()
219 '(" Object File: <object-file>"
220 " Source File: <source-file>"
221 " Build Rule: <rule>")))
222
223 (defun energize-insert-executable-target ()
224 (interactive)
225 (energize-insert-slots
226 ()
227 '(" Executable: <executable>"
228 " Build Command: lcc -Xf -Xez -o $object <object-file> ...")))
229
230 (defun energize-insert-library-target ()
231 (interactive)
232 (energize-insert-slots
233 ()
234 '(" Library: <library>"
235 " Build Command: energize_ar -Xez -remove -ranlib clq $object \\"
236 " <object-file> ...")))
237
238 (defun energize-insert-collection-target ()
239 (interactive)
240 (energize-insert-slots
241 ()
242 '(" Collection: <collection>"
243 " Build Command: energize_collect -Xez -o $object <object-file> ...")))
244
245 (defun energize-insert-file-target ()
246 (interactive)
247 (energize-insert-slots
248 ()
249 '(" File Target: <target>"
250 " Dependencies: <target> ..."
251 " Build Command: <shell-command>")))
252
253 (defun energize-insert-target-target ()
254 (interactive)
255 (energize-insert-slots
256 ()
257 '(" Target: <target>"
258 " Dependencies: <target> ..."
259 " Build Command: <shell-command>")))
260
261
262
263 ;;; Keymaps for Energize buffers.
264
265 (defvar energize-map nil "*Parent keymap for all Energize buffers")
266 (defvar energize-top-level-map nil "*Keymap for the Energize top-level buffer")
267 (defvar energize-debugger-map nil "*Keymap for Energize debugger buffers")
268 (defvar energize-breakpoint-map nil "*Keymap for Energize breakpoint-lists")
269 (defvar energize-browser-map nil "*Keymap for Energize browser buffers")
270 (defvar energize-project-map nil "*Keymap for Energize project buffers")
271 (defvar energize-no-file-project-map nil
272 "*Keymap for Energize project buffers not associated with a file")
273 (defvar energize-source-map nil "*Keymap for Energize source buffers")
274
275 (defvar energize-mode-hook nil
276 "Hook called when each energize buffer is created.")
277 (defvar energize-top-level-mode-hook nil
278 "Hook called when the energize top-level buffer is created.")
279 (defvar energize-project-mode-hook nil
280 "Hook called when an Energize project buffer is created.")
281 (defvar energize-no-file-project-mode-hook nil
282 "Hook called when an Energize project buffer with no file is created.")
283 (defvar energize-breakpoint-mode-hook nil
284 "Hook called when an Energize breakpoint-list buffer is created.")
285 (defvar energize-browser-mode-hook nil
286 "Hook called when an Energize browser buffer is created.")
287 (defvar energize-log-mode-hook nil
288 "Hook called when an Energize log buffer is created.")
289 (defvar energize-manual-mode-hook nil
290 "Hook called when an Energize manual buffer is created.")
291 (defvar energize-source-mode-hook nil
292 "Hook called when any source buffer is placed in the Energize minor-mode.")
293
294
295 (if energize-map
296 nil
297 (setq energize-map (make-sparse-keymap))
298 (set-keymap-name energize-map 'energize-map)
299 (define-key energize-map "\^C\^F" 'energize-find-project)
300 (define-key energize-map "\^C\^B\^E" 'energize-browse-error)
301 (define-key energize-map "\^C\^B\^L" 'energize-browse-language-elt)
302 (define-key energize-map "\^C\^B\^T" 'energize-browse-tree)
303 (define-key energize-map "\^C\^B\^C" 'energize-browse-class)
304 ;; now in global-map
305 ;; (define-key energize-map "\M-B" 'energize-build-a-target) ; M-Sh-B
306 (define-key energize-map "\M-C" 'energize-default-compile-file) ; M-Sh-C
307 (define-key energize-map 'button3 'energize-popup-menu)
308 )
309
310 (if energize-top-level-map
311 nil
312 (setq energize-top-level-map (make-sparse-keymap))
313 (set-keymap-name energize-top-level-map 'energize-top-level-map)
314 (set-keymap-parent energize-top-level-map energize-map)
315 (suppress-keymap energize-top-level-map)
316 (define-key energize-top-level-map "?" 'describe-mode)
317 (define-key energize-top-level-map " " 'energize-top-next-project)
318 (define-key energize-top-level-map "n" 'energize-top-next-project)
319 (define-key energize-top-level-map "p" 'energize-top-prev-project)
320 (define-key energize-top-level-map "N" 'energize-top-next-project)
321 (define-key energize-top-level-map "P" 'energize-top-prev-project)
322 (define-key energize-top-level-map "\t" 'energize-top-next-project)
323 (define-key energize-top-level-map '(shift tab) 'energize-top-prev-project)
324 (define-key energize-top-level-map '(control I) 'energize-top-prev-project)
325
326 (define-key energize-top-level-map "Q" 'disconnect-from-energize-query)
327
328 (define-key energize-top-level-map "d" 'energize-top-debug)
329 (define-key energize-top-level-map "\^D" 'energize-top-delete-project)
330 (define-key energize-top-level-map "e" 'energize-top-edit-project)
331 )
332
333 (if energize-project-map
334 nil
335 (setq energize-project-map (make-sparse-keymap))
336 (set-keymap-name energize-project-map 'energize-project-map)
337 (set-keymap-parent energize-project-map energize-map)
338 ;;(suppress-keymap energize-project-map)
339 ;;(define-key energize-project-map "\t" 'energize-project-next-field)
340 ;;(define-key energize-project-map '(shift tab) 'energize-project-prev-field)
341 ;;(define-key energize-project-map '(control I) 'energize-project-prev-field)
342
343 (define-key energize-project-map "\^C\^I" 'energize-import-file)
344 (define-key energize-project-map "\^C\^E" 'energize-project-edit-file)
345 (define-key energize-project-map "\^C\^S\^A" 'energize-project-sort-alpha)
346 (define-key energize-project-map "\^C\^S\^L" 'energize-project-sort-link)
347 (define-key energize-project-map "\^C\^V\^N" 'energize-project-view-names)
348 ; (define-key energize-project-map "\^C\^V\^L" 'energize-project-view-long)
349 (define-key energize-project-map "\^C\^V\^C" 'energize-project-view-options)
350 )
351
352
353 (if energize-no-file-project-map
354 nil
355 (setq energize-no-file-project-map (make-sparse-keymap))
356 (set-keymap-name energize-no-file-project-map 'energize-no-file-project-map)
357 (set-keymap-parent energize-no-file-project-map energize-map))
358
359 (if energize-breakpoint-map
360 nil
361 (setq energize-breakpoint-map (make-sparse-keymap))
362 (set-keymap-name energize-breakpoint-map 'energize-breakpoint-map)
363 (set-keymap-parent energize-breakpoint-map energize-map)
364 )
365
366 (if energize-browser-map
367 nil
368 (setq energize-browser-map (make-sparse-keymap))
369 (set-keymap-name energize-browser-map 'energize-browser-map)
370 (set-keymap-parent energize-browser-map energize-map)
371 )
372
373 (if energize-source-map
374 nil
375 (setq energize-source-map (make-sparse-keymap))
376 (set-keymap-name energize-source-map 'energize-source-map)
377 (set-keymap-parent energize-source-map energize-map)
378 ;; There are too many problems with using extents to determine where the
379 ;; top level forms are...
380 ;; (define-key energize-source-map "\M-\C-a" 'energize-beginning-of-defun)
381 ;; (define-key energize-source-map "\M-\C-e" 'energize-end-of-defun)
382 )
383
384 (defvar energize-menu-state nil
385 "State of the energize menu items of the buffer.
386 Automatically updated by the kernel when the state changes")
387
388 (defvar energize-default-menu-state nil
389 "State of the energize default menu items.
390 Automatically updated by the kernel when the state changes")
391
392 (defun energize-mode-internal ()
393 ;; initialize stuff common to all energize buffers (hooks, etc).
394 (make-local-hook 'write-file-data-hooks)
395 (add-hook 'write-file-data-hooks 'energize-write-data-hook t t)
396 ;;
397 (make-local-variable 'revert-buffer-insert-file-contents-function)
398 (setq revert-buffer-insert-file-contents-function
399 'energize-revert-buffer-insert-file-contents-hook)
400 ;;
401 (make-local-hook 'kill-buffer-hook)
402 (add-hook 'kill-buffer-hook 'energize-kill-buffer-hook nil t)
403 ;;
404 (make-local-variable 'require-final-newline)
405 (setq require-final-newline t)
406 ;;
407 (make-local-variable 'energize-menu-state)
408 ;;
409 (run-hooks 'energize-mode-hook))
410
411 (defun energize-non-file-mode-internal ()
412 ;; do magic associated with energize-modes for buffers which are not
413 ;; and cannot be associated with files.
414 ; (or (null buffer-file-name)
415 ; (equal buffer-file-name mode-name)
416 ; (error
417 ; "This buffer is associated with a file, it can't be placed in %s mode"
418 ; mode-name))
419 ;; hack so that save-file doesn't prompt for a filename.
420 (or buffer-file-name
421 (setq buffer-file-name (buffer-name)))
422 (set (make-local-variable 'version-control) 'never)
423 nil)
424
425 ;; don't create random new buffers in these modes
426 (put 'energize-top-level-mode 'mode-class 'special)
427 (put 'energize-project-mode 'mode-class 'special)
428 (put 'energize-no-file-project-mode 'mode-class 'special)
429 (put 'energize-breakpoint-mode 'mode-class 'special)
430 (put 'energize-browser-mode 'mode-class 'special)
431 (put 'energize-log-mode 'mode-class 'special)
432
433 (defun energize-top-level-mode ()
434 "Major mode for the Energize top-level buffer.
435 In addition to normal cursor-motion commands, the following keys are bound:
436 \\{energize-top-level-map}"
437 (interactive)
438 (energize-mode-internal)
439 (use-local-map energize-top-level-map)
440 (setq major-mode 'energize-top-level-mode
441 mode-name "Energize")
442 (energize-non-file-mode-internal)
443 ;; the default of "energize: Energize" is not very attractive.
444 (if (equal frame-title-format "%S: %b")
445 (set (make-local-variable 'frame-title-format) "%S: Top-Level"))
446 (run-hooks 'energize-top-level-mode-hook))
447
448
449 (defun energize-project-mode ()
450 "Major mode for the Energize Project buffers.
451 In addition to the normal editing commands, the following keys are bound:
452 \\{energize-project-map}"
453 (interactive)
454 (energize-mode-internal)
455 (use-local-map energize-project-map)
456 (setq major-mode 'energize-project-mode
457 mode-name "Project")
458 ;; in later revisions of the kernel the project is really a file.
459 (if (< (cdr (energize-protocol-level)) 8)
460 (energize-non-file-mode-internal))
461 (run-hooks 'energize-project-mode-hook))
462
463 (defun energize-no-file-project-mode ()
464 "Major mode for the Energize Project buffers not associated with a file.
465 In addition to the normal editing commands, the following keys are bound:
466 \\{energize-no-file-project-map}"
467 (interactive)
468 (energize-mode-internal)
469 (use-local-map energize-no-file-project-map)
470 (setq major-mode 'energize-no-file-project-mode
471 mode-name "NoFileProject")
472 (energize-non-file-mode-internal)
473 (run-hooks 'energize-no-file-project-mode-hook))
474
475 (defun energize-breakpoint-mode ()
476 "Major mode for the Energize Breakpoint-list buffers.
477 In addition to the normal editing commands, the following keys are bound:
478 \\{energize-breakpoint-map}"
479 (interactive)
480 (energize-mode-internal)
481 (use-local-map energize-breakpoint-map)
482 (setq major-mode 'energize-breakpoint-mode
483 mode-name "Breakpoint")
484 (energize-non-file-mode-internal)
485 (run-hooks 'energize-breakpoint-mode-hook))
486
487 (defun energize-browser-mode ()
488 "Major mode for the Energize Browser buffers.
489 In addition to the normal editing commands, the following keys are bound:
490 \\{energize-browser-map}"
491 (interactive)
492 (energize-mode-internal)
493 (use-local-map energize-browser-map)
494 (setq major-mode 'energize-browser-mode
495 mode-name "Browser")
496 (energize-non-file-mode-internal)
497 (run-hooks 'energize-browser-mode-hook))
498
499 (defun energize-log-mode ()
500 "Major mode for the Energize Error Log and System Log buffers.
501 In addition to the normal editing commands, the following keys are bound:
502 \\{energize-map}"
503 (interactive)
504 (energize-mode-internal)
505 (use-local-map energize-map)
506 (setq major-mode 'energize-log-mode
507 mode-name "Energize-Log")
508 (energize-non-file-mode-internal)
509 (run-hooks 'energize-log-mode-hook))
510
511 (defun energize-manual-mode ()
512 "Major mode for the Energize UNIX Manual buffers.
513 In addition to the normal editing commands, the following keys are bound:
514 \\{energize-map}"
515 (interactive)
516 (energize-mode-internal)
517 (use-local-map energize-map)
518 (setq major-mode 'energize-manual-mode
519 mode-name "Energize-Manual")
520 (energize-non-file-mode-internal)
521 (run-hooks 'energize-manual-mode-hook))
522
523 (defvar energize-source-mode nil)
524 ;;(put 'energize-source-mode 'permanent-local t) ; persists beyond mode-change
525
526 ;;; Add energize-source-mode to minor-mode-alist so that it shows up in
527 ;;; the modeline when true.
528 ;;;
529 (or (assq 'energize-source-mode minor-mode-alist)
530 (setq minor-mode-alist
531 (append minor-mode-alist
532 '((energize-source-mode " Energize")))))
533
534
535 (defun energize-source-minor-mode ()
536 "Minor mode for adding additional keybindings to Energize source buffers.
537 The following key bindings are added:
538 \\{energize-source-map}
539
540 Since this minor mode defines keys, once it gets turned on you can't really
541 turn it off."
542 (interactive)
543 (energize-mode-internal)
544 (make-local-variable 'energize-source-mode)
545 (setq energize-source-mode t)
546 (let ((source-map energize-source-map)
547 (dest-map (make-sparse-keymap)))
548 (set-keymap-parent dest-map (current-local-map))
549 (set-keymap-name dest-map 'energize-minor-mode-map)
550 (while source-map
551 (let (mapper prefixes)
552 (setq mapper (function (lambda (key val)
553 (if (keymapp val)
554 (let ((prefixes (append prefixes
555 (cons key nil))))
556 (map-keymap val mapper))
557 (define-key dest-map
558 (apply 'vector
559 (append prefixes (cons key nil)))
560 val)
561 ))))
562 (map-keymap source-map mapper))
563 (setq source-map (keymap-parent source-map)))
564 (use-local-map dest-map))
565 (run-hooks 'energize-source-mode-hook))
566
567
568 ;;; Commands in source buffers
569
570 (defun recenter-definition ()
571 "Position the beginning of the current definition at the top of the frame."
572 (interactive)
573 (end-of-line)
574 (if (eq major-mode 'c++-mode)
575 (c++-beginning-of-defun 1)
576 (beginning-of-defun 1))
577 (recenter 1))
578
579 (define-key global-map "\M-\C-r" 'recenter-definition)
580
581 (defun energize-hide-error-glyphs-in-form ()
582 "Hides the error icons in the current toplevel form.
583 You cannot get them back until you recompile the file."
584 (interactive)
585 (save-excursion
586 (save-restriction
587 (let ((start (progn (energize-beginning-of-defun) (point)))
588 (end (progn (energize-end-of-defun) (point)))
589 e)
590 (narrow-to-region start end)
591 (goto-char (point-min))
592 (setq e (extent-at (point)))
593 (while (and e
594 (< (extent-end-position e) (point-max)))
595 (if (extent-property e 'begin-glyph)
596 (set-extent-begin-glyph e nil))
597 (setq e (next-extent e)))))))
598
599 ;;; Dired-like commands
600
601 (defun energize-next-extent-for (command prev not-this-one)
602 (let ((last-e (if not-this-one 'none nil))
603 e result)
604 (save-excursion
605 (while (not (or result
606 (if prev (bobp) (eobp))))
607 (setq e (extent-at (point) (current-buffer)))
608 (if (and (not (eq e last-e))
609 (not (eq last-e 'none)))
610 (setq result
611 (energize-menu-item-for-name e command)))
612 (forward-char (if prev -1 1))
613 (setq last-e e)))
614 (if result e)))
615
616 (defun energize-next-extent-on-line-for (command not-this-one)
617 (save-excursion
618 (save-restriction
619 (narrow-to-region (point) (progn (end-of-line) (point)))
620 (goto-char (point-min))
621 (energize-next-extent-for command nil not-this-one))))
622
623
624 ;;; commands in the top-level buffer
625
626 (defun energize-top-next-project ()
627 "Position the cursor at the beginning of the following project."
628 (interactive)
629 (let ((p (point)))
630 (let ((e (energize-next-extent-for "editproject" nil t)))
631 (if (and e (= p (extent-start-position e)))
632 (save-excursion
633 (forward-char (extent-length e))
634 (setq e (energize-next-extent-for "editproject" nil t))))
635 (if e
636 (goto-char (extent-start-position e))
637 (error "no next project")))))
638
639 (defun energize-top-prev-project ()
640 "Position the cursor at the beginning of the preceeding project."
641 (interactive)
642 (let ((p (point)))
643 (let ((e (energize-next-extent-for "editproject" t t)))
644 (if (and e (= p (extent-start-position e)))
645 (save-excursion
646 (forward-char -1)
647 (setq e (energize-next-extent-for "editproject" t t))))
648 (if e
649 (goto-char (extent-start-position e))
650 (error "no previous project")))))
651
652 (defun energize-top-execute-command (command)
653 (let ((e (or (energize-next-extent-on-line-for command nil)
654 (error
655 (concat "no following field on this line that handles the `"
656 command "' Energize command.")))))
657 (energize-execute-command command e)))
658
659 (defun energize-top-debug ()
660 "Execute the `Debug' command on the project at or following point."
661 (interactive)
662 (energize-top-execute-command "debugprogram"))
663
664 (defun energize-top-delete-project ()
665 "Delete the project at or following point."
666 (interactive)
667 (energize-top-execute-command "deleteproject"))
668
669 (defun energize-top-edit-project ()
670 "Edit the project at or following point."
671 (interactive)
672 (energize-top-execute-command "editproject"))
673
674 ;;; commands in the project buffer
675
676 (defun energize-project-next-field (&optional prev)
677 (interactive)
678 (let ((e (extent-at (point) (current-buffer))))
679 (if e
680 (if prev
681 (goto-char (1- (extent-start-position e)))
682 (goto-char (1+ (extent-end-position e)))))
683 (while (null (extent-at (point) (current-buffer)))
684 (forward-char (if prev -1 1)))
685 (while (extent-at (point) (current-buffer) 'write-protected)
686 (forward-char (if prev -1 1)))
687 (if prev
688 (if (setq e (extent-at (point) (current-buffer)))
689 (goto-char (extent-start-position e))
690 (while (not (extent-at (point) (current-buffer)))
691 (forward-char -1))))))
692
693 (defun energize-project-prev-field () (interactive)
694 (energize-project-next-field t))
695
696 (defun energize-project-edit-file () (interactive)
697 (energize-top-execute-command "editfile"))
698
699
700 (defun energize-project-prune-unused-rules ()
701 "Deletes all unused rules from the Rules: part of a Project buffer,
702 and renumbers the remaining rules sequentially."
703 (interactive)
704 (save-excursion
705 (goto-char (point-min))
706 (re-search-forward "^[ \t]+Rules:")
707 (forward-line 1)
708 (let ((rules-regexp "^[ \t]*\\(\\.[a-zA-Z]+\\(([0-9]+)\\)?\\):")
709 (all-rules nil)
710 eor)
711 ;;
712 ;; Gather the contents of the Rule section, and find its end.
713 ;;
714 (save-excursion
715 (while (looking-at rules-regexp)
716 (setq all-rules (cons (list (buffer-substring (match-beginning 1)
717 (match-end 1))
718 (point-marker))
719 all-rules))
720 (while (progn (end-of-line) (= (preceding-char) ?\\))
721 (forward-line 1))
722 (forward-line 1))
723 (setq eor (point-marker)))
724 (setq all-rules (nreverse all-rules))
725 (let ((rest all-rules)
726 rule)
727 ;;
728 ;; Walk through the buffer gathering references to the rules.
729 ;;
730 (while rest
731 (setq rule (car rest))
732 (goto-char eor)
733 (let ((pattern (concat "^[ \t]+" (regexp-quote (car rule)) ":")))
734 (while (re-search-forward pattern nil t)
735 (setcdr (cdr rule)
736 (cons (set-marker (make-marker) (match-beginning 0))
737 (cdr (cdr rule))))))
738 (setq rest (cdr rest)))
739 ;;
740 ;; Delete those rules that have no references.
741 ;;
742 (goto-char eor)
743 (setq rest all-rules)
744 (while rest
745 (setq rule (car rest))
746 (if (null (cdr (cdr rule)))
747 (let ((p (nth 1 rule)))
748 (goto-char p)
749 (while (progn (end-of-line) (= (preceding-char) ?\\))
750 (forward-line 1))
751 (forward-line 1)
752 (delete-region p (point))
753 (set-marker p nil)
754 (setq all-rules (delq rule all-rules))
755 ))
756 (setq rest (cdr rest)))
757 ;;
758 ;; Renumber the remaining rules sequentially.
759 ;;
760 (goto-char eor)
761 (setq rest all-rules)
762 (let ((i 1))
763 (while rest
764 (setq rule (car rest))
765 (let ((referents (cdr rule))) ; including definition
766 (while referents
767 (goto-char (car referents))
768 (or (and (looking-at
769 (concat "^[ \t]+" (regexp-quote (car rule)) ":"))
770 (looking-at "[^:(]+\\((\\([0-9]+\\))\\|\\):"))
771 (error "internal error"))
772 (if (null (match-beginning 2))
773 (progn
774 (goto-char (match-beginning 1))
775 (insert "(" (int-to-string i) ")"))
776 (goto-char (match-beginning 2))
777 (delete-region (match-beginning 2) (match-end 2))
778 (insert (int-to-string i)))
779 (set-marker (car referents) nil)
780 (setq referents (cdr referents))))
781 (setq i (1+ i))
782 (setq rest (cdr rest))))
783 ;;
784 ;; TODO:
785 ;; - order the Rule Users list in the same order as the Rules list.
786 ;; - or, order the Rule Users list by number of files, and then
787 ;; order the Rules list the same as that (numbered sequentially.)
788 ;; - or, order the Rules list by length-of-rule (= complicatedness.)
789 )
790 (set-marker eor nil))))