adjust for latest emacs peculiarities
[eproject.git] / eproject.el
blobaebd88398b29a6b2ad9ae334d8a95c4565584db6
1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2 ;;
3 ;; eproject.el --- project workspaces for emacs
4 ;;
5 ;; Copyright (C) 2008 grischka
6 ;;
7 ;; Author: grischka -- grischka@users.sourceforge.net
8 ;; Created: 24 Jan 2008
9 ;; Version: 0.2
11 ;; This program is free software, released under the GNU General
12 ;; Public License (GPL, version 2). For details see:
14 ;; http://www.fsf.org/licenses/gpl.html
16 ;; This program 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 GNU
19 ;; General Public License for more details.
21 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
23 ;; There is a global file
24 (defun prj-globalfile ()
25 (expand-file-name (concat user-emacs-directory "eproject.lst"))
28 ;; with the list of all projects
29 (defvar prj-list)
31 ;; and the project that was open in the last session (if any)
32 (defvar prj-last-open nil)
34 ;; and the frame coords from last session
35 (defvar prj-frame-pos nil)
37 ;; eproject version that created the config file
38 (defvar prj-version nil)
40 ;; Here is a function to reset these
41 (defun prj-init ()
42 (setq prj-version nil)
43 (setq prj-list nil)
44 (setq prj-last-open nil)
45 (setq prj-frame-pos nil)
48 ;; Each project has a directory
49 (defvar prj-directory)
51 ;; with a configuration files in it
52 (defun prj-localfile ()
53 (expand-file-name "eproject.cfg" prj-directory)
56 ;; This file defines:
58 ;; the list of files
59 (defvar prj-files)
61 ;; the current file
62 (defvar prj-curfile)
64 ;; an alist of settings
65 (defvar prj-config)
67 ;; a list of tools
68 (defvar prj-tools)
70 ;; a list of utility functions (feature incomplete)
71 (defvar prj-functions nil)
73 ;; directory to run commands, default to prj-directory
74 (defvar prj-directory-run)
76 ;; Here are some default tools for new projects,
77 ;; (which you might want to adjust to your needs)
79 (defun prj-default-config ()
80 (setq prj-tools (copy-tree '(
81 ("Make" "make" "f9")
82 ("Clean" "make clean" "C-f9")
83 ("Run" "echo run what" "f8")
84 ("Stop" "-e eproject-killtool" "C-f8")
85 ("---")
86 ("Configure" "./configure")
87 ("---")
88 ("Explore Project" "nautilus --browser `pwd` &")
89 ("XTerm In Project" "xterm &")
90 )))
93 ;; This defines the current project
94 (defvar prj-current)
96 ;; There is an internal list with generated functions
97 ;; for each tool
98 (defvar prj-tools-fns)
100 ;; and a list with files removed from the project
101 (defvar prj-removed-files)
103 ;; Here is a function to reset/close the project
104 (defun prj-reset ()
105 (setq prj-version nil)
106 (setq prj-current nil)
107 (setq prj-directory nil)
108 (setq prj-directory-run nil)
109 (setq prj-files nil)
110 (setq prj-removed-files nil)
111 (setq prj-curfile nil)
112 (setq prj-config nil)
113 (setq prj-tools nil)
114 (setq prj-tools-fns nil)
115 (prj-reset-functions)
116 (prj-default-config)
119 (defun prj-reset-functions ()
120 (dolist (l prj-functions)
121 (if (eq (car l) 'setq)
122 (makunbound (cadr l))
123 (fmakunbound (cadr l))
125 (setq prj-functions nil)
128 (defun prj-set-functions (s)
129 (prj-reset-functions)
130 (setq prj-functions s)
131 (dolist (l s) (eval l))
134 ;; Some more variables
136 ;; the frame that exists on startup
137 (defvar prj-initial-frame nil)
139 ;; this is put into minor-mode-alist
140 (defvar eproject-mode t)
142 ;; where this file is in
143 (defvar eproject-directory)
145 ;; eproject version that created the files
146 (defvar eproject-version "0.2")
148 ;; Configuration UI
149 (eval-and-compile
150 (defun eproject-setup-toggle () (interactive))
151 (defun eproject-setup-quit () (interactive))
152 (defun prj-config-get-result (s))
153 (defun prj-config-reset ())
154 (defun prj-config-print ())
155 (defun prj-config-parse ())
158 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
159 ;; Small functions
161 (defun prj-del-list (l e)
162 (let ((a (assoc (car e) l)))
163 (if a
164 (delq a l)
165 l)))
167 (defun prj-add-list (l e)
168 (nconc (prj-del-list l e) (list e))
171 (defun prj-next-file (l e)
172 (let ((a (assoc (car e) l)))
173 (when a
174 (setq l (memq a l))
175 (if (cdr l) (cadr l) a)
178 (defun prj-prev-file (l e)
179 (let ((a (assoc (car e) l)) (p l))
180 (when a
181 (while (and l (null (eq (car l) a)))
182 (setq p l l (cdr l))
184 (car p)
187 ;; replace a closed file, either by the previous or the next.
188 (defun prj-otherfile (l f)
189 (let ((n (prj-prev-file l f)))
190 (when (equal f n)
191 (setq n (prj-next-file l f))
192 (when (equal f n)
193 (setq n nil)
197 (defun caddr (l) (car (cddr l)))
199 ;; make relative path, but only up to the second level of ..
200 (defun prj-relative-path (f)
201 (let ((r (file-relative-name f prj-directory)))
202 (if (string-match "^\\.\\.[/\\]\\.\\.[/\\]\\.\\.[/\\]" r)
207 ;; friendly truncate filename
208 (defun prj-shortname (s)
209 (let ((l (length s)) (x 30) n)
210 (cond ((>= x l) s)
211 ((progn
212 (setq x (- x 3))
213 (setq n (length (file-name-nondirectory s)))
214 (if (< n l) (setq n (1+ n)))
215 (>= x n)
217 (concat (substring s 0 (- x n)) "..." (substring s (- n)))
219 ((= n l)
220 (concat (substring s 0 x) "...")
223 (concat "..." (substring s (- n) (- (- x 3) n)) "...")
224 ))))
226 (defun prj-settitle ()
227 (modify-frame-parameters
229 (list (cons 'title
230 (and prj-current
231 (format "emacs - %s" (car prj-current))
232 )))))
234 (defun eproject-addon (f)
235 (concat eproject-directory f)
238 (defun prj-goto-line (n)
239 (goto-char 1)
240 (beginning-of-line n)
243 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
244 ;; Write configuration to file
246 (defun prj-print-list (s fp)
247 (let ((v (eval s)))
248 (setq v (list 'setq s
249 (if (and (atom v) (null (and (symbolp v) v)))
251 (list 'quote v)
253 ;;(print v fp)
254 (pp v fp) (princ "\n" fp)
257 (defun prj-create-file (filename)
258 (let ((fp (generate-new-buffer filename)))
259 (princ ";; -*- mode: Lisp; -*-\n\n" fp)
260 fp))
262 (defun prj-close-file (fp)
263 (with-current-buffer fp
264 (condition-case nil
265 (write-region 1 (point-max) (buffer-name fp) nil 0)
266 (error nil)
268 (kill-buffer fp)
271 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
272 ;; Load/Save global project list and initial frame sizes
274 (defun prj-loadlist ()
275 (prj-init)
276 (load (prj-globalfile) t t)
277 (setq prj-version eproject-version)
280 (defun prj-get-frame-pos (f)
281 (and f
282 (mapcar
283 (lambda (parm) (cons parm (frame-parameter f parm)))
284 '(top left width height)
287 (defun prj-savelist ()
288 (let ((g (prj-globalfile))
291 (unless (file-exists-p g)
292 (make-directory (file-name-directory g) t)
294 (setq prj-last-open (car prj-current))
295 (when (frame-live-p prj-initial-frame)
296 (setq prj-frame-pos (prj-get-frame-pos prj-initial-frame))
298 (setq fp (prj-create-file g))
299 (when fp
300 (prj-print-list 'prj-version fp)
301 (prj-print-list 'prj-list fp)
302 (prj-print-list 'prj-last-open fp)
303 (prj-print-list 'prj-frame-pos fp)
304 (prj-close-file fp)
307 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
308 ;; Load/Save local per-project configuration file
310 (defun prj-update-config ()
311 (setq prj-directory-run
312 (file-name-as-directory
313 (expand-file-name
314 (or (prj-getconfig "run-directory") ".")
315 prj-directory
319 (defun prj-loadconfig (a)
320 (let (lf e)
321 (prj-reset)
322 (setq prj-current a)
323 (setq prj-directory
324 (file-name-as-directory
325 (expand-file-name (cadr a))
328 (when (file-exists-p (setq lf (prj-localfile)))
329 (load lf nil t)
330 (setq prj-curfile
331 (or (assoc prj-curfile prj-files)
332 (car prj-files)
335 (if (setq e (prj-getconfig "project-name"))
336 (setcar a e)
337 (prj-setconfig "project-name" (car a))
339 (prj-update-config)
340 (prj-set-functions prj-functions)
341 (setq prj-version eproject-version)
344 (defun prj-saveconfig ()
345 (when prj-current
346 (let (w c b path files)
347 (prj-removehooks)
348 (setq w (selected-window))
349 (setq c (window-buffer w))
350 (dolist (f prj-files)
351 (setq path (expand-file-name (car f) prj-directory))
352 (cond ((setq b (get-file-buffer path))
353 (set-window-buffer w b t)
354 (with-current-buffer b
355 (let ((s (line-number-at-pos (window-start w)))
356 (p (line-number-at-pos (window-point w)))
358 (push (list (car f) s p) files)
360 ((consp (cdr f))
361 (push f files)
363 (set-window-buffer w c t)
364 (prj-addhooks)
365 (let ((fp (prj-create-file (prj-localfile)))
366 (prj-curfile (car prj-curfile))
367 (prj-files (nreverse files))
369 (when fp
370 (prj-print-list 'prj-version fp)
371 (prj-print-list 'prj-config fp)
372 (prj-print-list 'prj-tools fp)
373 (prj-print-list 'prj-files fp)
374 (prj-print-list 'prj-curfile fp)
375 (prj-print-list 'prj-functions fp)
376 (prj-close-file fp)
380 (defun prj-saveall ()
381 (prj-saveconfig)
382 (prj-savelist)
385 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
386 ;; The core functions: Open / Close / Add / Remove Project
388 (defun eproject-open (a)
389 "Open another project."
390 (interactive
391 (list
392 (or (prj-config-get-result 'p)
393 (completing-read "Open Project: " (mapcar 'car prj-list))
395 (unless (consp a)
396 (let ((b (assoc a prj-list)))
397 (unless b
398 (error "No such project: %s" a)
400 (setq a b)
402 (setq a (or (car (member a prj-list)) a))
403 (unless (eq a prj-current)
404 (unless (file-directory-p (cadr a))
405 (error "Error: No such directory: %s" (cadr a))
407 (setq prj-list (cons a (delq a prj-list)))
408 (eproject-close)
409 (prj-loadconfig a)
411 (prj-addhooks)
412 (prj-setup-all)
413 (cd prj-directory)
414 (unless (prj-edit-file prj-curfile)
415 (eproject-dired)
418 (defun eproject-close ()
419 "Close the current project."
420 (interactive)
421 (when prj-current
422 (prj-saveconfig)
423 (prj-removehooks)
424 (let (f)
425 (unwind-protect
426 (progn
427 (save-some-buffers nil)
428 (eproject-killbuffers t)
429 (setq f t)
431 (or f (prj-addhooks))
433 (prj-reset)
434 (prj-config-reset)
435 (prj-setup-all)
438 (defun eproject-killbuffers (&optional from-project)
439 "If called interactively kills all buffers that
440 do not belong to project files"
441 (interactive)
442 (let (a b)
443 (dolist (f prj-files)
444 (setq b (get-file-buffer (expand-file-name (car f) prj-directory)))
445 (if b (setq a (cons (list b) a)))
447 (dolist (b (buffer-list))
448 (when (eq (consp (assoc b a)) from-project)
449 (kill-buffer b)
450 ))))
452 (defun eproject-add (d)
453 "Add a new or existing project to the list."
454 (interactive
455 (list
456 (read-directory-name "Add project in directory: " prj-directory nil t)
458 (when d
459 (setq d (directory-file-name d))
461 (when (= 0 (length d))
462 (error "Error: Empty directory name.")
464 (let (n a)
465 (setq n (file-name-nondirectory d))
466 (setq a (list n d))
467 (push a prj-list)
468 (prj-setup-all)
471 (defun eproject-remove (a)
472 "Remove a project from the list."
473 (interactive
474 (list
475 (or (prj-config-get-result 'p)
476 (completing-read "Remove project: " (mapcar 'car prj-list))
478 (unless (consp a)
479 (let ((b (assoc a prj-list)))
480 (unless b
481 (error "No such project: %s" a)
483 (setq a b)
485 (when (progn
486 (beep)
487 (prog1 (y-or-n-p (format "Remove \"%s\"? " (car a)))
488 (message "")
490 (setq prj-list (prj-del-list prj-list a))
491 (prj-setup-all)
494 (defun eproject-save ()
495 "Save the project configuration to file."
496 (interactive)
497 (prj-config-parse)
498 (prj-config-print)
499 (prj-saveall)
502 (defun eproject-revert ()
503 "Reload the project configuration from file."
504 (interactive)
505 (prj-loadlist)
506 (if prj-current
507 (prj-loadconfig prj-current)
509 (prj-setup-all)
512 (defun eproject-addfile (f)
513 "Add a file to the current project."
514 (interactive
515 (and prj-current
516 (list
517 (read-file-name "Add file to project: " nil nil t nil)
519 (unless prj-current (error "No project open"))
520 (let ((a (prj-insert-file f (prj-config-get-result 'f))))
521 (unless (cdr a)
522 (message "Added to project: %s" (car a))
524 (prj-config-print)
525 (prj-setmenu)
528 (defun eproject-removefile (a)
529 "Remove a file from the current project."
530 (interactive (prj-get-existing-file-1 "Remove file from project: "))
531 (setq a (prj-get-existing-file-2 a))
532 (prj-remove-file a)
535 (defun eproject-visitfile (a)
536 "Visit a file from the current project."
537 (interactive (prj-get-existing-file-1 "Visit file: "))
538 (setq a (prj-get-existing-file-2 a))
539 (prj-edit-file a)
542 (defun prj-get-existing-file-1 (msg)
543 (and prj-current
544 (list
545 (or (prj-config-get-result 'f)
546 (completing-read msg (mapcar 'car prj-files))
547 ))))
549 (defun prj-get-existing-file-2 (a)
550 (unless prj-current (error "No project open"))
551 (if (consp a)
553 (let ((b (assoc (prj-relative-path a) prj-files)))
554 (unless b (error "No such file in project: %s" a))
558 (defun eproject-help ()
559 "Show the eproject README."
560 (interactive)
561 (view-file (eproject-addon "eproject.txt"))
564 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
565 ;; Hook functions to track opening/closing files from emacs
567 (defun prj-addhooks ()
568 (add-hook 'kill-buffer-hook 'prj-kill-buffer-hook)
569 (add-hook 'find-file-hook 'prj-find-file-hook)
570 (add-hook 'window-configuration-change-hook 'prj-wcc-hook)
573 (defun prj-removehooks ()
574 (remove-hook 'window-configuration-change-hook 'prj-wcc-hook)
575 (remove-hook 'find-file-hook 'prj-find-file-hook)
576 (remove-hook 'kill-buffer-hook 'prj-kill-buffer-hook)
579 (defun prj-wcc-hook ()
580 (let* ((w (selected-window))
581 (b (window-buffer w))
583 ;; (message "wcc-hook: %s" (prin1-to-string (list wcc-count w b n)))
584 (prj-register-buffer b)
587 (defun prj-find-file-hook ()
588 (run-with-idle-timer
589 0 nil
590 `(lambda ()
591 (let* ((b ,(current-buffer))
592 (a (prj-register-buffer b))
594 (when a
595 (with-current-buffer b
596 (rename-buffer (car a) t)
597 ))))))
599 (defun prj-kill-buffer-hook ()
600 (let ((b (current-buffer)) a)
601 (if (setq a (rassq b prj-files))
602 (prj-remove-file a t)
603 (if (setq a (rassq b prj-removed-files))
604 (setq prj-removed-files (delq a prj-removed-files))
605 ))))
607 (defun prj-register-buffer (b)
608 (let (f a i)
609 (setq f (buffer-file-name b))
610 (when f
611 (setq a (rassq b prj-files))
612 (unless a
613 (setq a (prj-insert-file f nil t))
614 (when a
615 (unless (cdr a)
616 (message "Added to project: %s" (car a))
618 (setcdr a b)
620 (when (and a (null (eq a prj-curfile)))
621 (setq prj-curfile a)
622 (prj-setmenu)
626 (defun prj-insert-file (f &optional after on-the-fly)
627 (let ((r (prj-relative-path f)) a m)
628 (setq a (assoc r prj-files))
629 (unless (or a (and on-the-fly (assoc r prj-removed-files)))
630 (setq a (list r))
631 (setq m (memq (or after prj-curfile) prj-files))
632 (if m
633 (setcdr m (cons a (cdr m)))
634 (setq prj-files (prj-add-list prj-files a))
636 (setq prj-removed-files (prj-del-list prj-removed-files a))
640 (defun prj-remove-file (a &optional on-the-fly)
641 (let ((n (prj-otherfile prj-files a)) b)
642 (setq prj-files (prj-del-list prj-files a))
643 (when (eq prj-curfile a)
644 (setq prj-curfile n)
646 (unless on-the-fly
647 (setq prj-removed-files (prj-add-list prj-removed-files a))
649 (unless (prj-config-print)
650 (prj-edit-file prj-curfile)
652 (prj-setmenu)
653 (message "Removed from project: %s" (car a))
656 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
657 ;; Edit another file
659 (defun prj-edit-file (a)
660 (when a
661 (let* ((n (car a))
662 (f (expand-file-name n prj-directory))
663 (b (get-file-buffer f))
666 (unless b
667 (prj-removehooks)
668 (setq b (find-file-noselect f))
669 (prj-addhooks)
670 (when b
671 (with-current-buffer b
672 (rename-buffer n t)
674 (setq pos (cdr a))
676 (when b
677 (setcdr a b)
678 (eproject-setup-quit)
679 (switch-to-buffer b)
680 (prj-restore-edit-pos pos (selected-window))
681 (prj-setmenu)
683 (setq prj-curfile a)
686 (defun prj-restore-edit-pos (pos w)
687 (when (consp pos)
688 (let* ((b (current-buffer))
689 (top (car pos))
690 (line (cadr pos))
692 (when (and (numberp top) (numberp line))
693 (prj-goto-line top)
694 (set-window-start w (point))
695 (prj-goto-line line)
696 ))))
698 (defun prj-select-window (w)
699 (let (focus-follows-mouse)
700 (select-window w)
701 (select-frame-set-input-focus (window-frame w))
704 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
705 ;; choose next/previous file
707 (defun eproject-nextfile ()
708 "Switch to the next file that belongs to the current project."
709 (interactive)
710 (prj-switch-file 'prj-next-file 'next-buffer)
713 (defun eproject-prevfile ()
714 "Switch to the previous file that belongs to the current project."
715 (interactive)
716 (prj-switch-file 'prj-prev-file 'previous-buffer)
719 (defun prj-switch-file (fn1 fn2)
720 (let* ((a (rassoc (current-buffer) prj-files)))
721 (cond (a
722 (prj-edit-file (funcall fn1 prj-files a))
724 (prj-curfile
725 (prj-edit-file prj-curfile)
728 (funcall fn2)
729 ))))
731 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
732 ;; Set key shortcuts
734 (defun prj-setkeys ()
735 (let ((f (consp prj-current))
736 (a (assoc 'eproject-mode minor-mode-map-alist))
737 (map (make-sparse-keymap))
739 (if a
740 (setcdr a map)
741 (push (cons 'eproject-mode map) minor-mode-map-alist)
743 (when f
744 (define-key map [M-right] 'eproject-nextfile)
745 (define-key map [M-left] 'eproject-prevfile)
746 (define-key map [C-f5] 'eproject-dired)
747 (let ((n 0) fn s)
748 (dolist (a prj-tools)
749 (unless (setq fn (nth n prj-tools-fns))
750 (setq fn (list 'lambda))
751 (setq prj-tools-fns (nconc prj-tools-fns (list fn)))
753 (setcdr fn `(() (interactive) (prj-run-tool ',a)))
754 (setq n (1+ n))
755 (when (setq s (caddr a))
756 (define-key map (prj-parse-key s) (and f fn))
757 ))))
758 (define-key map [f5] 'eproject-setup-toggle)
761 (defun prj-parse-key (s)
762 (read
763 (if (string-match "[a-z][a-z0-9]+$" s)
764 (concat "[" s "]")
765 (concat "\"\\" s "\""))))
767 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
768 ;; Set menus
770 (defun prj-list-sorted ()
771 (sort (append prj-list nil)
772 '(lambda (a b) (string-lessp (car a) (car b)))
775 (defun prj-setmenu ()
776 (let ((f (consp prj-current)) m1 m2 m3)
778 (setq m1
779 (list
780 `("Open" open
781 ,@(prj-menulist-maker prj-list prj-current 'prj-menu-open)
782 ("--")
783 ("Add ..." "Add new or existing project to the list" . eproject-add)
784 ("Remove ..." "Remove project from the list" . eproject-remove)
785 ,@(and f '(("Close" "Close current project" . eproject-close)))
787 '("Setup" "Enter the project setup area." . eproject-setup-toggle)
789 (when f
790 (nconc m1 (cons '("--") (prj-menulist-maker prj-tools nil prj-tools-fns)))
791 (setq m2
792 `(("Dired" "Browse project directory in Dired - Use 'a' to add file(s) to the project" . eproject-dired)
793 ("--")
794 ,@(prj-menulist-maker prj-files prj-curfile 'prj-menu-edit)
797 (prj-menu-maker
798 global-map
799 `((buffer "Project" project ,@m1)
800 (file "List" list ,@m2)
802 '(menu-bar)
805 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
807 (defun prj-menu-edit ()
808 (interactive)
809 (let ((a (nth last-command-event prj-files)))
810 (if a (prj-edit-file a))
813 (defun prj-menu-open ()
814 (interactive)
815 (let ((a (nth last-command-event prj-list)))
816 (if a (eproject-open (car a)))
819 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
821 (defun prj-menu-maker (map l v)
822 (let ((e (list nil)))
823 (setq v (append v e))
824 (dolist (k (reverse l))
825 (let (s a)
826 (when (symbolp (car k))
827 (setq a (pop k))
829 (cond
830 ((numberp (car k))
831 (setcar e (pop k))
833 ((and (consp (cdr k)) (symbolp (cadr k)))
834 (setcar e (cadr k))
835 (setq s (cddr k))
836 (setq k (and s (cons (car k) (make-sparse-keymap (car k)))))
839 (setcar e (intern (downcase (car k))))
841 (if a
842 (define-key-after map (vconcat v) k a)
843 (define-key map (vconcat v) k)
845 (if s (prj-menu-maker map s v))
846 ))))
848 (defun prj-copy-head (l n)
849 (let (r)
850 (while (and l (> n 0))
851 (push (pop l) r)
852 (setq n (1- n))
854 (nreverse r)
857 (defun prj-split-list (l n)
858 (let (r)
859 (while l
860 (push (prj-copy-head l n) r)
861 (setq l (nthcdr n l))
863 (nreverse r)
866 (defun prj-menulist-maker (l act fns)
867 (let (r (w 30) s (m 0) (n 0) k)
868 (cond
869 ((< (length l) w)
870 (prj-menulist-maker-1 (list l fns n) act)
873 ;; menu too long; split into submenus
874 (setq s (prj-split-list l w))
875 (setq k (prj-menulist-maker-1 (list (append (pop s) '(("--"))) fns n) act))
876 (setq r (nreverse k))
877 (dolist (l s)
878 (when (consp fns)
879 (setq fns (nthcdr w fns))
881 (setq n (+ n w))
882 (setq k (prj-menulist-maker-1 (list l fns n) act))
883 (push (cons (concat (prj-shortname (caar l)) " ...")
884 (cons (intern (format "m_%d" (setq m (1+ m))))
885 k)) r)
887 (nreverse r)
888 ))))
890 (defun prj-menulist-maker-1 (l act)
891 (let (r e f s i n a)
892 (while (car l)
893 (setq a (caar l))
894 (setcar l (cdar l))
895 (setq n (caddr l))
896 (setcar (cddr l) (1+ n))
897 (setq f (if (consp (cadr l))
898 (prog1 (car (cadr l)) (setcar (cdr l) (cdr (cadr l))))
899 (cadr l)))
901 (setq i (car a))
902 (unless (string-match "^ *#" i)
903 (setq s (if (and (consp (cdr a)) (stringp (cadr a))) (cadr a) i))
904 (cond ((equal ">" i)
905 (setq e (cons s (cons (intern s) (prj-menulist-maker-1 l act))))
906 (setq r (cons e r))
908 ((equal "<" i)
909 (setq l nil)
912 (setq i (prj-shortname i))
913 (setq e (cons n (if (eq a act)
914 `(menu-item ,i ,f :button (:toggle . t) :help ,s)
915 (cons i (cons s f)))))
916 (setq r (cons e r))
919 (nreverse r)
922 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
923 ;; Run make and other commands
925 (defun prj-setup-tool-window ()
926 (let ((bn "*compilation*") w h b c f)
927 (unless (get-buffer-window bn t)
928 (setq b (get-buffer-create bn))
929 (setq f (frame-list))
930 (cond ((cdr f)
931 (setq w (frame-first-window (car f)))
932 (delete-other-windows w)
935 (setq h (/ (* 70 (frame-height)) 100))
936 (delete-other-windows w)
937 (setq w (split-window w h))
939 (set-window-buffer w b)
942 (defun prj-run (cmd)
943 (let (dir)
944 (when (string-match "^-in +\\([^[:space:]]+\\) +" cmd)
945 (setq dir (match-string-no-properties 1 cmd))
946 (setq cmd (substring cmd (match-end 0)))
948 (when prj-directory-run
949 (setq dir (expand-file-name (or dir ".") prj-directory-run))
951 (if dir (cd dir))
952 (cond ((string-match "^-e +" cmd)
953 (setq cmd (read (substring cmd (match-end 0))))
954 (unless (commandp cmd)
955 (setq cmd `(lambda () (interactive) ,cmd))
957 (command-execute cmd)
959 ((string-match "\\(.+\\)& *$" cmd)
960 (start-process-shell-command "eproject-async" nil (match-string 1 cmd))
961 (message (match-string 1 cmd))
964 (unless (or (fboundp 'ecb-activate) (fboundp 'ewm-init))
965 (prj-setup-tool-window)
967 (let ((display-buffer-reuse-frames t))
968 (compile cmd)
969 )))))
971 (defun prj-run-tool (a)
972 (unless (string-match "^--+$" (car a))
973 (prj-run (or (cadr a) (car a)))
976 (defun eproject-killtool ()
977 (interactive)
978 (let ((bn "*compilation*") w0 w1)
979 (when (setq w1 (get-buffer-window bn t))
980 (when (fboundp 'kill-compilation)
981 (setq w0 (selected-window))
982 (select-window w1)
983 (kill-compilation)
984 (select-window w0)
985 ))))
987 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
988 ;; run grep on project files
990 (require 'grep)
992 (defun eproject-grep (command-args)
993 "Run the grep command on all the project files."
994 (interactive
995 (progn
996 (grep-compute-defaults)
997 (let ((default (grep-default-command)))
998 (list (read-from-minibuffer
999 "Run grep on project files: "
1000 (if current-prefix-arg default grep-command)
1003 'grep-history
1004 (if current-prefix-arg nil default)
1005 )))))
1006 (let ((default-directory prj-directory))
1007 (dolist (f (mapcar 'car prj-files))
1008 (setq command-args (concat command-args " " f))
1010 (grep command-args)
1013 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1014 ;; add files to the project with dired
1016 (require 'dired)
1018 (defun prj-dired-addfiles ()
1019 (interactive)
1020 (when prj-current
1021 (let ((n 0) a)
1022 (dolist (f (dired-get-marked-files))
1023 (setq a (prj-insert-file f))
1024 (unless (cdr a)
1025 (setq n (1+ n))
1026 (setq prj-curfile a)
1028 (message "Added to project: %d file(s)" n)
1029 (prj-setmenu)
1032 (defun prj-dired-run ()
1033 (interactive)
1034 (let ((f (dired-get-marked-files)) c)
1035 (and (setq c (pop f))
1036 (null f)
1037 (let ((prj-directory (file-name-directory c)))
1038 (prj-run c)))))
1040 (defun eproject-dired ()
1041 "Start a dired window with the project directory."
1042 (interactive)
1043 (when prj-directory-run
1044 (eproject-setup-quit)
1045 ;;(message "Use 'a' to add marked or single files to the project.")
1046 (dired prj-directory-run)
1047 (let ((map dired-mode-map))
1048 (define-key map [mouse-2] 'dired-find-file)
1049 (define-key map "a" 'prj-dired-addfiles)
1050 (define-key map "r" 'prj-dired-run)
1051 (define-key map [menu-bar operate command] '("Add to Project"
1052 "Add current or marked file(s) to project" . prj-dired-addfiles))
1055 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1057 (defun prj-setup-all ()
1058 (prj-setkeys)
1059 (prj-setmenu)
1060 (prj-settitle)
1061 (prj-config-print)
1064 (defun prj-getconfig (n)
1065 (let ((a (cdr (assoc n prj-config))))
1066 (and (stringp a) a)
1069 (defun prj-setconfig (n v)
1070 (let ((a (assoc n prj-config)))
1071 (unless a
1072 (setq a (list n))
1073 (setq prj-config (nconc prj-config (list a)))
1075 (setcdr a v)
1078 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1079 ;; Initialize
1081 (defun prj-startup-delayed ()
1082 ;; where is this file
1083 (setq eproject-directory
1084 (file-name-directory (symbol-file 'eproject-startup)))
1086 ;; load UI support
1087 (load (eproject-addon "eproject-config") nil t)
1089 ;; When no projects are specified yet, load the eproject project itself.
1090 (unless prj-list
1091 (load (eproject-addon "eproject.cfg"))
1094 ;; no project so far
1095 (prj-reset)
1096 (prj-setup-all)
1097 (add-hook 'kill-emacs-hook 'prj-saveall)
1099 ;; inhibit open last project when a file was on the commandline
1100 (unless (buffer-file-name (window-buffer))
1101 (when prj-last-open
1103 ;; open last project
1104 (eproject-open prj-last-open)
1106 ;; restore frame position
1107 (unless (fboundp 'ewm-init)
1108 (when prj-frame-pos
1109 (modify-frame-parameters prj-initial-frame prj-frame-pos)
1110 ;; emacs bug: when it's too busy it doesn't set frames correctly.
1111 (sit-for 0.2)
1112 ))))
1114 (when (fboundp 'ecb-activate)
1115 (ecb-activate)
1119 (defun prj-command-line-switch (option)
1120 (setq prj-last-open (pop argv))
1121 (setq inhibit-startup-screen t)
1124 (defun eproject-startup ()
1125 (if (boundp 'prj-list)
1126 (progn
1127 (load (eproject-addon "eproject-config"))
1128 (prj-setup-all))
1129 (progn
1130 (prj-loadlist)
1131 (when prj-last-open (setq inhibit-startup-screen t))
1132 (setq prj-initial-frame (selected-frame))
1133 (push '("project" . prj-command-line-switch) command-switch-alist)
1134 (run-with-idle-timer 0.1 nil 'prj-startup-delayed)
1137 ;;;###autoload(require 'eproject)
1138 (provide 'eproject)
1139 (eproject-startup)
1141 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1142 ;; eproject.el ends here