167fdc9df731ef2bab6457f99b52f8714ee128d0
[eproject.git] / eproject.el
blob167fdc9df731ef2bab6457f99b52f8714ee128d0
1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
2 ;;
3 ;; eproject.el --- project workspaces for emacs
4 ;;
5 ;; Copyright (C) 2008,2009 grischka
6 ;;
7 ;; Author: grischka -- grischka@users.sourceforge.net
8 ;; Created: 24 Jan 2008
9 ;; Version: 0.4
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 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
24 ;; User-configurable items:
26 ;; The default tools for new projects,
27 (defun prj-default-config ()
28 (setq prj-tools (copy-tree '(
29 ("Make" "make" "f9")
30 ("Clean" "make clean" "C-f9")
31 ("Run" "echo run what" "f8")
32 ("Stop" "-e eproject-killtool" "C-f8")
33 ("---")
34 ("Configure" "./configure")
35 ("---")
36 ("Explore Project" "nautilus --browser `pwd` &")
37 ("XTerm In Project" "xterm &")
38 )))
42 ;; Should the project directory be set as default-directory
43 ;; for all project files (nil/t):
44 (defvar prj-set-default-directory nil)
46 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
47 ;; no user-configurable items below (probably)
49 ;; There is a global file (~/.emacs.d/eproject.lst)
50 (defun prj-globalfile ()
51 (expand-file-name "eproject.lst"
52 (if (boundp 'user-emacs-directory) user-emacs-directory
53 "~/.emacs.d/")
56 ;; with the list of all projects
58 (defvar prj-list)
60 ;; and the project that was open in the last session (if any)
61 (defvar prj-last-open nil)
63 ;; and the frame coords from last session
64 (defvar prj-frame-pos nil)
66 ;; eproject version that created the config file
67 (defvar prj-version nil)
69 ;; Here is a function to reset these
70 (defun prj-init ()
71 (setq prj-version nil)
72 (setq prj-list nil)
73 (setq prj-last-open nil)
74 (setq prj-frame-pos nil)
77 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
78 ;; Each project has a directory
80 (defvar prj-directory)
82 ;; with a configuration files in it
83 (defvar prj-default-cfg "eproject.cfg")
85 ;; This file defines:
87 ;; the list of files
88 (defvar prj-files)
90 ;; the current file
91 (defvar prj-curfile)
93 ;; an alist of settings
94 (defvar prj-config)
96 ;; a list of tools
97 (defvar prj-tools)
99 ;; a list of utility functions (feature incomplete)
100 (defvar prj-functions nil)
102 ;; directory to run commands, default to prj-directory
103 (defvar prj-exec-directory)
105 ;; The current project
106 (defvar prj-current)
108 ;; A list with generated functions for each tool
109 (defvar prj-tools-fns)
111 ;; A list with files removed from the project
112 (defvar prj-removed-files)
114 ;; Here is a function to reset/close the project
115 (defun prj-reset ()
116 (setq prj-version nil)
117 (setq prj-current nil)
118 (setq prj-directory nil)
119 (setq prj-exec-directory nil)
120 (setq prj-files nil)
121 (setq prj-removed-files nil)
122 (setq prj-curfile nil)
123 (setq prj-config nil)
124 (setq prj-tools nil)
125 (setq prj-tools-fns nil)
126 (prj-reset-functions)
127 (prj-default-config)
130 (defun prj-reset-functions ()
131 (dolist (l prj-functions)
132 (if (eq (car l) 'setq)
133 (makunbound (cadr l))
134 (fmakunbound (cadr l))
136 (setq prj-functions nil)
139 (defun prj-set-functions (s)
140 (prj-reset-functions)
141 (setq prj-functions s)
142 (dolist (l s) (eval l))
145 ;; Some more variables:
147 ;; the frame that exists on startup
148 (defvar prj-initial-frame nil)
150 ;; this is put into minor-mode-alist
151 (defvar eproject-mode t)
153 ;; where this file is in
154 (defvar eproject-directory)
156 ;; eproject version that created the files
157 (defvar eproject-version "0.3")
159 ;; Configuration UI
160 (eval-and-compile
161 (defun eproject-setup-toggle () (interactive))
162 (defun eproject-setup-quit () (interactive))
163 (defun prj-config-get-result (s))
164 (defun prj-config-reset ())
165 (defun prj-config-print ())
166 (defun prj-config-parse ())
169 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
170 ;; Small functions
172 (defun caddr (l) (car (cddr l)))
174 (defun prj-del-list (l e)
175 (let ((a (assoc (car e) l)))
176 (if a
177 (delq a l)
178 l)))
180 (defun prj-add-list (l e)
181 (nconc (prj-del-list l e) (list e))
184 (defun prj-next-file (l e)
185 (and (setq e (assoc (car e) l))
186 (cadr (memq e l))
189 (defun prj-prev-file (l e)
190 (prj-next-file (reverse l) e)
193 ; replace a closed file, either by the previous or the next.
194 (defun prj-otherfile (l f)
195 (or (prj-prev-file l f)
196 (prj-next-file l f)
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 (and t (write-region nil nil (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 (mapcar
282 (lambda (parm) (cons parm (frame-parameter f parm)))
283 '(top left width height)
286 (defun prj-savelist ()
287 (let ((g (prj-globalfile)) fp)
288 (unless (file-exists-p g)
289 (make-directory (file-name-directory g) t)
291 (setq prj-last-open (car prj-current))
292 (when (frame-live-p prj-initial-frame)
293 (setq prj-frame-pos (prj-get-frame-pos prj-initial-frame))
295 (setq fp (prj-create-file g))
296 (when fp
297 (prj-print-list 'prj-version fp)
298 (prj-print-list 'prj-list fp)
299 (prj-print-list 'prj-last-open fp)
300 (prj-print-list 'prj-frame-pos fp)
301 (prj-close-file fp)
304 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
305 ;; Load/Save local per-project configuration file
307 (defun prj-update-config ()
308 (let ((d (prj-get-directory prj-current))
309 (e (prj-getconfig "exec-root"))
311 (if e (setq d (expand-file-name e d)))
312 (setq prj-exec-directory (file-name-as-directory d))
315 (defun prj-get-directory (a)
316 (file-name-as-directory (expand-file-name (cadr a)))
319 (defun prj-get-cfg ()
320 (expand-file-name (or (caddr prj-current) prj-default-cfg) prj-directory)
323 (defun prj-loadconfig (a)
324 (let (lf e)
325 (prj-reset)
326 (setq prj-current a)
327 (setq prj-directory (prj-get-directory a))
328 (when (file-regular-p (setq lf (prj-get-cfg)))
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 files)
347 (prj-removehooks)
348 (setq w (selected-window))
349 (setq c (window-buffer w))
350 (dolist (f prj-files)
351 (cond ((setq b (get-buffer (car f)))
352 (set-window-buffer w b t)
353 (with-current-buffer b
354 (let ((s (line-number-at-pos (window-start w)))
355 (p (line-number-at-pos (window-point w)))
357 (push (list (car f) s p) files)
359 (t ;;(consp (cdr f))
360 (push f files)
362 (set-window-buffer w c t)
363 (prj-addhooks)
364 (let ((fp (prj-create-file (prj-get-cfg)))
365 (prj-curfile (car prj-curfile))
366 (prj-files (nreverse files))
368 (when fp
369 (prj-print-list 'prj-version fp)
370 (prj-print-list 'prj-config fp)
371 (prj-print-list 'prj-tools fp)
372 (prj-print-list 'prj-files fp)
373 (prj-print-list 'prj-curfile fp)
374 (prj-print-list 'prj-functions fp)
375 (prj-close-file fp)
379 (defun prj-saveall ()
380 (prj-saveconfig)
381 (prj-savelist)
384 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
385 ;; The core functions: Open / Close / Add / Remove Project
387 (defun eproject-open (a)
388 "Open another project."
389 (interactive
390 (list
391 (or (prj-config-get-result 'p)
392 (completing-read "Open Project: " (mapcar 'car prj-list))
394 (unless (consp a)
395 (let ((b (assoc a prj-list)))
396 (unless b
397 (error "No such project: %s" a)
399 (setq a b)
401 (setq a (or (car (member a prj-list)) a))
402 (unless (eq a prj-current)
403 (unless (file-directory-p (prj-get-directory a))
404 (error "No such directory: %s" (cadr a))
406 (setq prj-list (cons a (delq a prj-list)))
407 (eproject-close)
408 (prj-loadconfig a)
410 (prj-addhooks)
411 (prj-setup-all)
412 (prj-isearch-setup)
413 (unless (prj-edit-file prj-curfile)
414 (eproject-dired)
417 (defun eproject-close ()
418 "Close the current project."
419 (interactive)
420 (when prj-current
421 (prj-saveconfig)
422 (prj-removehooks)
423 (let (f)
424 (unwind-protect
425 (progn
426 (save-some-buffers nil)
427 (eproject-killbuffers t)
428 (setq f t)
430 (or f (prj-addhooks))
432 (prj-reset)
433 (prj-config-reset)
434 (prj-setup-all)
435 (prj-isearch-setup)
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-buffer (car f)))
445 (if b
446 (setq a (cons (list b) a))
448 (dolist (b (buffer-list))
449 (when (eq (consp (assoc b a)) from-project)
450 (kill-buffer b)
451 ))))
453 (defun eproject-add (dir &optional name cfg)
454 "Add a new or existing project to the list."
455 (interactive
456 (let (d n f)
457 (setq d (read-directory-name "Add project in directory: " prj-directory nil t))
458 (setq n (file-name-nondirectory (directory-file-name d)))
459 (setq n (read-string "Project name: " n))
460 (setq f (read-string "Project file: " prj-default-cfg))
461 (list d n f)
463 (when dir
464 (setq dir (directory-file-name dir))
465 (setq name (file-name-nondirectory dir))
466 (when (and cfg (string-equal cfg prj-default-cfg))
467 (setq cfg nil)
469 (let ((a (if cfg (list name dir cfg) (list name dir))))
470 (push a prj-list)
471 (eproject-open a)
474 (defun eproject-remove (a)
475 "Remove a project from the list."
476 (interactive
477 (list
478 (or (prj-config-get-result 'p)
479 (completing-read "Remove project: " (mapcar 'car prj-list))
481 (unless (consp a)
482 (let ((b (assoc a prj-list)))
483 (unless b
484 (error "No such project: %s" a)
486 (setq a b)
488 (when (progn
489 (beep)
490 (prog1 (y-or-n-p (format "Remove \"%s\"? " (car a)))
491 (message "")
493 (setq prj-list (prj-del-list prj-list a))
494 (prj-setup-all)
497 (defun eproject-save ()
498 "Save the project configuration to file."
499 (interactive)
500 (prj-config-parse)
501 (prj-config-print)
502 (prj-saveall)
505 (defun eproject-revert ()
506 "Reload the project configuration from file."
507 (interactive)
508 (prj-loadlist)
509 (if prj-current
510 (prj-loadconfig prj-current)
512 (prj-setup-all)
515 (defun eproject-addfile (f)
516 "Add a file to the current project."
517 (interactive
518 (and prj-current
519 (list
520 (read-file-name "Add file to project: " nil nil t nil)
522 (unless prj-current (error "No project open"))
523 (let ((a (prj-insert-file f (prj-config-get-result 'f))))
524 (unless (cdr a)
525 (message "Added to project: %s" (car a))
527 (prj-config-print)
528 (prj-setmenu)
531 (defun eproject-removefile (a)
532 "Remove a file from the current project."
533 (interactive (prj-get-existing-file-1 "Remove file from project: "))
534 (setq a (prj-get-existing-file-2 a))
535 (prj-remove-file a)
538 (defun eproject-visitfile (a)
539 "Visit a file from the current project."
540 (interactive (prj-get-existing-file-1 "Visit file: "))
541 (setq a (prj-get-existing-file-2 a))
542 (prj-edit-file a)
545 (defun prj-get-existing-file-1 (msg)
546 (and prj-current
547 (list
548 (or (prj-config-get-result 'f)
549 (completing-read msg (mapcar 'car prj-files))
550 ))))
552 (defun prj-get-existing-file-2 (a)
553 (unless prj-current (error "No project open"))
554 (if (consp a)
556 (let ((b (assoc (prj-relative-path a) prj-files)))
557 (unless b (error "No such file in project: %s" a))
561 (defun eproject-help ()
562 "Show the eproject README."
563 (interactive)
564 (view-file (eproject-addon "eproject.txt"))
567 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
568 ;; Hook functions to track opening/closing files from emacs
570 (defun prj-addhooks ()
571 (add-hook 'kill-buffer-hook 'prj-kill-buffer-hook)
572 (add-hook 'find-file-hook 'prj-find-file-hook)
573 (add-hook 'window-configuration-change-hook 'prj-wcc-hook)
576 (defun prj-removehooks ()
577 (remove-hook 'window-configuration-change-hook 'prj-wcc-hook)
578 (remove-hook 'find-file-hook 'prj-find-file-hook)
579 (remove-hook 'kill-buffer-hook 'prj-kill-buffer-hook)
582 (defun prj-wcc-hook ()
583 (let ((w (selected-window)) (b (window-buffer (selected-window))))
584 ;;(message "wcc-hook: %s" (prin1-to-string (list w b)))
585 (prj-register-buffer b)
588 (defun prj-find-file-hook ()
589 (run-with-idle-timer 0.2 nil 'prj-wcc-hook)
592 (defun prj-kill-buffer-hook ()
593 (let ((b (current-buffer)) a)
594 (if (setq a (rassq b prj-files))
595 (prj-remove-file a t)
596 (if (setq a (rassq b prj-removed-files))
597 (setq prj-removed-files (delq a prj-removed-files))
598 ))))
600 (defun prj-register-buffer (b)
601 (let (f a)
602 (setq f (buffer-file-name b))
603 (when (and f t) ;;(not (string-match "^\\." (file-name-nondirectory f))))
604 (setq a (rassq b prj-files))
605 (unless a
606 (setq a (prj-insert-file f nil t))
607 (when a
608 (unless (cdr a)
609 (message "Added to project: %s" (car a))
611 (prj-init-buffer a b)
613 (when (and a (null (eq a prj-curfile)))
614 (setq prj-curfile a)
615 (prj-setmenu)
619 (defun prj-insert-file (f &optional after on-the-fly)
620 (let ((r (prj-relative-path f)) a m)
621 (setq a (assoc r prj-files))
622 (unless (or a (and on-the-fly (assoc r prj-removed-files)))
623 (setq a (list r))
624 (setq m (memq (or after prj-curfile) prj-files))
625 (if m
626 (setcdr m (cons a (cdr m)))
627 (setq prj-files (prj-add-list prj-files a))
629 (setq prj-removed-files (prj-del-list prj-removed-files a))
633 (defun prj-remove-file (a &optional on-the-fly)
634 (let ((n (prj-otherfile prj-files a)) b)
635 (setq prj-files (prj-del-list prj-files a))
636 (when (eq prj-curfile a)
637 (setq prj-curfile n)
639 (unless on-the-fly
640 (setq prj-removed-files (prj-add-list prj-removed-files a))
642 (unless (prj-config-print)
643 (prj-edit-file prj-curfile)
645 (prj-setmenu)
646 (message "Removed from project: %s" (car a))
649 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
650 ;; Edit another file
652 (defun prj-init-buffer (a b)
653 (with-current-buffer b
654 (rename-buffer (car a) t)
655 (when prj-set-default-directory
656 (cd prj-directory)
658 (setcdr a b)
661 (defun prj-find-file (a)
662 (when a
663 (let (f b pos)
664 (setq b (cdr a))
665 (setq f (expand-file-name (car a) prj-directory))
666 (setq b (get-file-buffer f))
667 (unless b
668 (prj-removehooks)
669 (setq b (find-file-noselect f))
670 (prj-addhooks)
671 (when (and b (consp (cdr a)))
672 (setq pos (cdr a))
674 (when b
675 (prj-init-buffer a b)
676 (cons b pos)
677 ))))
679 (defun prj-edit-file (a)
680 (let ((f (prj-find-file a)))
681 (when f
682 (eproject-setup-quit)
683 (switch-to-buffer (car f))
684 (prj-restore-edit-pos (cdr f) (selected-window))
685 (prj-setmenu)
686 ;;(message "dir: %s" default-directory)
688 (setq prj-curfile a)
691 (defun prj-restore-edit-pos (pos w)
692 (let ((top (car pos)) (line (cadr pos)))
693 (when (and (numberp top) (numberp line))
694 (prj-goto-line top)
695 (set-window-start w (point))
696 (prj-goto-line line)
699 (defun prj-select-window (w)
700 (let (focus-follows-mouse)
701 (select-window w)
702 (select-frame-set-input-focus (window-frame w))
705 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
706 ;; choose next/previous file
708 (defun eproject-nextfile ()
709 "Switch to the next file that belongs to the current project."
710 (interactive)
711 (prj-switch-file 'prj-next-file 'next-buffer)
714 (defun eproject-prevfile ()
715 "Switch to the previous file that belongs to the current project."
716 (interactive)
717 (prj-switch-file 'prj-prev-file 'previous-buffer)
720 (defun prj-switch-file (fn1 fn2)
721 (let ((a (rassoc (current-buffer) prj-files)))
722 (cond (a
723 (prj-edit-file (or (funcall fn1 prj-files a) a))
725 (prj-curfile
726 (prj-edit-file prj-curfile)
729 (funcall fn2)
730 ))))
732 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
733 ;; Set key shortcuts
735 (defun prj-setkeys ()
736 (let ((f (consp prj-current))
737 (a (assoc 'eproject-mode minor-mode-map-alist))
738 (map (make-sparse-keymap))
740 (if a
741 (setcdr a map)
742 (push (cons 'eproject-mode map) minor-mode-map-alist)
744 (when f
745 (define-key map [M-right] 'eproject-nextfile)
746 (define-key map [M-left] 'eproject-prevfile)
747 (define-key map [C-f5] 'eproject-dired)
748 (let ((n 0) fn s)
749 (dolist (a prj-tools)
750 (unless (setq fn (nth n prj-tools-fns))
751 (setq fn (list 'lambda))
752 (setq prj-tools-fns (nconc prj-tools-fns (list fn)))
754 (setcdr fn `(() (interactive) (prj-run-tool ',a)))
755 (setq n (1+ n))
756 (when (setq s (caddr a))
757 (define-key map (prj-parse-key s) (and f fn))
758 ))))
759 (define-key map [f5] 'eproject-setup-toggle)
762 (defun prj-parse-key (s)
763 (read
764 (if (string-match "[a-z][a-z0-9]+$" s)
765 (concat "[" s "]")
766 (concat "\"\\" s "\""))))
768 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
769 ;; Set menus
771 (defun prj-list-sorted ()
772 (sort (append prj-list nil)
773 '(lambda (a b) (string-lessp (car a) (car b)))
776 (defun prj-setmenu ()
777 (let ((f (consp prj-current)) m1 m2 m3)
779 (setq m1
780 `(("Open" open ,@(prj-menulist-maker prj-list prj-current 'prj-menu-open))
781 ("Add/Remove" other
782 ("Add ..." "Add new or existing project to the list" . eproject-add)
783 ("Remove ..." "Remove project from the list" . eproject-remove)
784 ,@(and f '(("Close" "Close current project" . eproject-close)))
785 ("--")
786 ("Setup" "Enter the project setup area." . eproject-setup-toggle)
787 ("Help" "View eproject.txt" . eproject-help)
790 (when f
791 (nconc m1 (cons '("--") (prj-menulist-maker prj-tools nil prj-tools-fns)))
792 (setq m2
793 `(("Dired" "Browse project directory in Dired - Use 'a' to add file(s) to the project" . eproject-dired)
794 ("--")
795 ,@(prj-menulist-maker prj-files prj-curfile 'prj-menu-edit)
798 (prj-menu-maker
799 global-map
800 `((buffer "Project" project ,@m1)
801 (file "List" list ,@m2)
803 '(menu-bar)
806 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
808 (defun prj-menu-edit ()
809 (interactive)
810 (let ((a (nth last-command-event prj-files)))
811 (if a (prj-edit-file a))
814 (defun prj-menu-open ()
815 (interactive)
816 (let ((a (nth last-command-event prj-list)))
817 (if a (eproject-open (car a)))
820 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
822 (defun prj-menu-maker (map l v)
823 (let ((e (list nil)))
824 (setq v (append v e))
825 (dolist (k (reverse l))
826 (let (s a)
827 (when (symbolp (car k))
828 (setq a (pop k))
830 (cond
831 ((numberp (car k))
832 (setcar e (pop k))
834 ((and (consp (cdr k)) (symbolp (cadr k)))
835 (setcar e (cadr k))
836 (setq s (cddr k))
837 (setq k (and s (cons (car k) (make-sparse-keymap (car k)))))
840 (setcar e (intern (downcase (car k))))
842 (if a
843 (define-key-after map (vconcat v) k a)
844 (define-key map (vconcat v) k)
846 (if s (prj-menu-maker map s v))
847 ))))
849 (defun prj-copy-head (l n)
850 (let (r)
851 (while (and l (> n 0))
852 (push (pop l) r)
853 (setq n (1- n))
855 (nreverse r)
858 (defun prj-split-list (l n)
859 (let (r)
860 (while l
861 (push (prj-copy-head l n) r)
862 (setq l (nthcdr n l))
864 (nreverse r)
867 (defun prj-menulist-maker (l act fns)
868 (let (r (w 30) s (m 0) (n 0) k)
869 (cond
870 ((< (length l) w)
871 (prj-menulist-maker-1 (list l fns n) act)
874 ;; menu too long; split into submenus
875 (setq s (prj-split-list l w))
876 (setq k (prj-menulist-maker-1 (list (append (pop s) '(("--"))) fns n) act))
877 (setq r (nreverse k))
878 (dolist (l s)
879 (when (consp fns)
880 (setq fns (nthcdr w fns))
882 (setq n (+ n w))
883 (setq k (prj-menulist-maker-1 (list l fns n) act))
884 (push (cons (concat (prj-shortname (caar l)) " ...")
885 (cons (intern (format "m_%d" (setq m (1+ m))))
886 k)) r)
888 (nreverse r)
889 ))))
891 (defun prj-menulist-maker-1 (l act)
892 (let (r e f s i n a)
893 (while (car l)
894 (setq a (caar l))
895 (setcar l (cdar l))
896 (setq n (caddr l))
897 (setcar (cddr l) (1+ n))
898 (setq f (if (consp (cadr l))
899 (prog1 (car (cadr l)) (setcar (cdr l) (cdr (cadr l))))
900 (cadr l)))
902 (setq i (car a))
903 (unless (string-match "^ *#" i)
904 (setq s (if (and (consp (cdr a)) (stringp (cadr a))) (cadr a) i))
905 (cond ((equal ">" i)
906 (setq e (cons s (cons (intern s) (prj-menulist-maker-1 l act))))
907 (setq r (cons e r))
909 ((equal "<" i)
910 (setq l nil)
913 (setq i (prj-shortname i))
914 (setq e (cons n (if (eq a act)
915 `(menu-item ,i ,f :button (:toggle . t) :help ,s)
916 (cons i (cons s f)))))
917 (setq r (cons e r))
920 (nreverse r)
923 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
924 ;; Run make and other commands
926 (defun prj-setup-tool-window ()
927 (let ((bn "*compilation*") w h b c f)
928 (unless (get-buffer-window bn t)
929 (setq b (get-buffer-create bn))
930 (setq f (frame-list))
931 (cond ((cdr f)
932 (setq w (frame-first-window (car f)))
933 (delete-other-windows w)
936 (setq h (/ (* 70 (frame-height)) 100))
937 (delete-other-windows w)
938 (setq w (split-window w h))
940 (set-window-buffer w b)
943 (defun prj-run (cmd)
944 (cond ((string-match "^-e +" cmd)
945 (setq cmd (read (substring cmd (match-end 0))))
946 (unless (commandp cmd)
947 (setq cmd `(lambda () (interactive) ,cmd))
949 (command-execute cmd)
951 ((let ((b (current-buffer)) (old-dir default-directory) (new-dir "."))
952 (when (string-match "^-in +\\([^[:space:]]+\\) +" cmd)
953 (setq new-dir (match-string-no-properties 1 cmd))
954 (setq cmd (substring cmd (match-end 0)))
956 (when prj-exec-directory
957 (setq new-dir (expand-file-name new-dir prj-exec-directory))
959 (cd new-dir)
960 (cond ((string-match "\\(.+\\)& *$" cmd)
961 (start-process-shell-command "eproject-async" nil (match-string 1 cmd))
962 (message (match-string 1 cmd))
965 (unless (or (fboundp 'ecb-activate) (fboundp 'ewm-init))
966 (prj-setup-tool-window)
968 (let ((display-buffer-reuse-frames t) (f (selected-frame)))
969 (compile cmd)
970 (select-frame-set-input-focus f)
972 (with-current-buffer b (cd old-dir))
973 ))))
975 (defun prj-run-tool (a)
976 (unless (string-match "^--+$" (car a))
977 (prj-run (or (cadr a) (car a)))
980 (defun eproject-killtool ()
981 (interactive)
982 (let ((bn "*compilation*") w0 w1)
983 (when (setq w1 (get-buffer-window bn t))
984 (when (fboundp 'kill-compilation)
985 (setq w0 (selected-window))
986 (select-window w1)
987 (kill-compilation)
988 (select-window w0)
989 ))))
991 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
992 ;; run grep on project files
994 (defun eproject-grep (command-args)
995 "Run the grep command on all the project files."
996 (interactive
997 (progn
998 (require 'grep)
999 (grep-compute-defaults)
1000 (let ((default (grep-default-command)))
1001 (list (read-from-minibuffer
1002 "Run grep on project files: "
1003 (if current-prefix-arg default grep-command)
1006 'grep-history
1007 (if current-prefix-arg nil default)
1008 )))))
1009 (let ((b (current-buffer)) (old-dir default-directory))
1010 (dolist (f (mapcar 'car prj-files))
1011 (setq command-args (concat command-args " " f))
1013 (when prj-directory (cd prj-directory))
1014 (grep command-args)
1015 (with-current-buffer b (cd old-dir))
1018 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1019 ;; add files to the project with dired
1021 (require 'dired)
1023 (defun prj-dired-addfiles ()
1024 (interactive)
1025 (when prj-current
1026 (let ((n 0) a)
1027 (dolist (f (dired-get-marked-files))
1028 (setq a (prj-insert-file f))
1029 (unless (cdr a)
1030 (setq n (1+ n))
1031 (setq prj-curfile a)
1033 (message "Added to project: %d file(s)" n)
1034 (prj-setmenu)
1037 (defun prj-dired-run ()
1038 (interactive)
1039 (let ((f (dired-get-marked-files)) c)
1040 (and (setq c (pop f))
1041 (null f)
1042 (let ((prj-directory (file-name-directory c)))
1043 (prj-run c)))))
1045 (defun eproject-dired ()
1046 "Start a dired window with the project directory."
1047 (interactive)
1048 (when prj-directory
1049 (eproject-setup-quit)
1050 ;;(message "Use 'a' to add marked or single files to the project.")
1051 (dired prj-directory)
1052 (let ((map dired-mode-map))
1053 (define-key map [mouse-2] 'dired-find-file)
1054 (define-key map "a" 'prj-dired-addfiles)
1055 (define-key map "r" 'prj-dired-run)
1056 (define-key map [menu-bar operate command] '("Add to Project"
1057 "Add current or marked file(s) to project" . prj-dired-addfiles))
1060 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1062 (defun prj-setup-all ()
1063 (prj-setkeys)
1064 (prj-setmenu)
1065 (prj-settitle)
1066 (prj-config-print)
1069 (defun prj-getconfig (n)
1070 (let ((a (cdr (assoc n prj-config))))
1071 (and (stringp a) a)
1074 (defun prj-setconfig (n v)
1075 (let ((a (assoc n prj-config)))
1076 (unless a
1077 (setq a (list n))
1078 (setq prj-config (nconc prj-config (list a)))
1080 (setcdr a v)
1083 (defun prj-on-kill ()
1084 (prj-saveall)
1087 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1088 ;; isearch in all project files
1090 (defun prj-isearch-function (b wrap)
1091 (let (a d)
1092 (or b (setq b (current-buffer)))
1093 (cond (wrap
1094 (if isearch-forward
1095 (setq a (car prj-files))
1096 (setq a (car (last prj-files)))
1098 ((setq a (rassoc b prj-files))
1099 (if isearch-forward
1100 (setq a (prj-next-file prj-files a))
1101 (setq a (prj-prev-file prj-files a))
1104 (when a
1105 (if (buffer-live-p (cdr a))
1106 (setq d (cdr a))
1107 (setq d (car (prj-find-file a)))
1109 ;; (print `(prj-isearch (wrap . ,wrap) ,b ,d) (get-buffer "*Messages*"))
1113 (defun prj-isearch-setup ()
1114 (cond (prj-current
1115 (setq multi-isearch-next-buffer-function 'prj-isearch-function)
1116 (setq multi-isearch-pause 'initial)
1117 (add-hook 'isearch-mode-hook 'multi-isearch-setup)
1120 (remove-hook 'isearch-mode-hook 'multi-isearch-setup)
1123 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1124 ;; Initialize
1126 (defun prj-startup-delayed ()
1127 ;; load UI support
1128 (load (eproject-addon "eproject-config") nil t)
1130 ;; When no projects are specified yet, load the eproject project itself.
1131 (unless prj-list
1132 (load (eproject-addon prj-default-cfg))
1135 ;; no project so far
1136 (prj-reset)
1137 (prj-setup-all)
1138 (add-hook 'kill-emacs-hook 'prj-on-kill)
1140 ;; inhibit open last project when a file was on the commandline
1141 (unless (buffer-file-name (window-buffer))
1142 (when prj-last-open
1144 ;; open last project
1145 (eproject-open prj-last-open)
1147 ;; restore frame position
1148 (unless (fboundp 'ewm-init)
1149 (when (and prj-frame-pos prj-initial-frame)
1150 (modify-frame-parameters prj-initial-frame prj-frame-pos)
1151 ;; emacs bug: when it's too busy it doesn't set frames correctly.
1152 (sit-for 0.2)
1153 ))))
1155 (when (fboundp 'ecb-activate)
1156 (ecb-activate)
1160 (defun prj-command-line-switch (option)
1161 (setq prj-last-open (pop argv))
1162 (setq inhibit-startup-screen t)
1165 (defun eproject-startup ()
1166 ;; where is this file
1167 (if load-file-name
1168 (setq eproject-directory (file-name-directory load-file-name)))
1169 (if (boundp 'prj-list)
1170 (progn
1171 (load (eproject-addon "eproject-config"))
1172 (prj-setup-all))
1173 (progn
1174 (prj-loadlist)
1175 (when prj-last-open (setq inhibit-startup-screen t))
1176 (when (display-graphic-p) (setq prj-initial-frame (selected-frame)))
1177 (push '("project" . prj-command-line-switch) command-switch-alist)
1178 (run-with-idle-timer 0.1 nil 'prj-startup-delayed)
1181 ;;;###autoload(require 'eproject)
1182 (provide 'eproject)
1183 (eproject-startup)
1185 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1186 ;; eproject.el ends here