1;;; msf-abbrev.el --- maintain abbrevs in a directory tree
3;; Copyright (C) 2004,2005 Free Software Foundation, Inc.
5;; Author: Benjamin Rutt <brutt@bloomington.in.us>
8;; This file is free software; you can redistribute it and/or modify
9;; it under the terms of the GNU General Public License as published by
10;; the Free Software Foundation; either version 2, or (at your option)
13;; This file is distributed in the hope that it will be useful,
14;; but WITHOUT ANY WARRANTY; without even the implied warranty of
15;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16;; GNU General Public License for more details.
18;; You should have received a copy of the GNU General Public License
19;; along with GNU Emacs; see the file COPYING. If not, write to
20;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21;; Boston, MA 02111-1307, USA.
25;; This package allows you to place your abbrevs into your filesystem,
26;; in a special directory tree. More information and a demo available at
27;; http://www.bloomington.in.us/~brutt/msf-abbrev.html
32(unless (boundp 'undo-in-progress)
33 (defvar undo-in-progress nil
34 "Placeholder defvar from msf-abbrev package.")
35 (defadvice undo-more (around msf-abbrev-undo-more activate)
36 (let ((undo-in-progress t)) (ad-do-it))))
38(defgroup msf-abbrev nil
39 "Load abbrevs from a filesystem tree."
42;; begin user customizable vars
43(defcustom msf-abbrev-root nil
44 "*Root directory of user abbreviation files.
46This directory should have subdirectories such as c-mode, lisp-mode, etc."
50(defcustom msf-abbrev-verbose nil
51 "*Whether to be verbose for various msf-abbrev actions."
55(defcustom msf-abbrev-expand-function 'msf-abbrev-expand-function-default
56 "*Which function should be called to expand a abbrev in a file.
58The function should take one argument, the filename to expand.
59This function will be used for all files except those with .el
60extensions, which will be handled by the elisp interpreter
65(defcustom msf-abbrev-expand-hook nil
66 "Hook called after expansion of an msf abbrev."
70(defcustom msf-abbrev-indent-after-expansion nil
71 "*Whether to indent the region inserted after the abbrev is expanded.
73This is only relevant when the default expandsion function is
74used (see `msf-abbrev-expand-function')."
77;; end of user customizable vars
79(defvar msf-abbrev-fields-created 0)
81;; begin inlined fld.el stuff
82(defvar fld-last-group-id nil)
83(defvar fld-id-to-group-id (make-hash-table))
84(defvar fld-group-id-to-exit-point (make-hash-table))
86(defvar fld-keymap (make-sparse-keymap))
87(define-key fld-keymap (kbd "M-RET") 'fld-cleanup-form-at-point)
88(define-key fld-keymap (kbd "TAB") 'fld-next)
89(define-key fld-keymap (kbd "S-TAB") 'fld-prev)
90(define-key fld-keymap (kbd "<S-iso-lefttab>") 'fld-prev)
91(defvar fld-choose-keymap (copy-keymap fld-keymap))
92(define-key fld-choose-keymap (kbd "RET") 'fld-choose)
93(defvar fld-category-defaults nil)
94(setq fld-category-defaults
95 `(face highlight front-sticky t rear-sticky t
97(setplist 'fld-category fld-category-defaults)
101 (setq fld-id-next (1+ fld-id-next))
104(defvar fld-group-id-next 0)
105(defun fld-nextgroupid ()
106 (setq fld-group-id-next (1+ fld-group-id-next))
109(defun fld-currgroupid ()
114 (if (get-text-property (point) 'fld-id) t nil))
125 (assert (or (fld-in) (fld-after)))
129 (get-text-property (point) 'fld-id)))
131(defun fld-group-id ()
133 (assert (or (fld-in) (fld-after)))
137 (get-text-property (point) 'fld-group-id)))
140 (assert (or (fld-in) (fld-after)))
144(defun fld-beginning ()
145 (assert (or (fld-in) (fld-after)))
151 (setq thisid (get-text-property (point) 'fld-id))
153 (setq pt (point-min))
155 (if (eq thisid (get-text-property (point) 'fld-id))
158 (when (eq (point) (point-min))
159 (setq pt (point-min))
160 (if (not (eq thisid (get-text-property (point) 'fld-id)))
163 (setq pt (1+ (point)))
169 (assert (or (fld-in) (fld-after)))
174 (setq pt (or (next-single-property-change (point) 'fld-id)
178(defun fld-cleanup-form-at-point ( )
180 (when (or (fld-in) (fld-after))
181 (fld-cleanup (fld-group-id))))
183(defun fld-cleanup (gid)
185 (fld-disable-monitoring)
186 (setq fld-ressurection-id nil
187 fld-ressurection-now nil
188 fld-ressurection-pos nil
189 fld-transition-to-typed-id nil
190 fld-transition-to-typed-now nil)
195 (if (eq gid (caddr ls))
198 (fld-list-in-buffer)))))
201 (remhash id fld-id-to-group-id))
203 (remhash gid fld-group-id-to-exit-point)
206 (lambda (id_point_groupid)
207 (when (member (car id_point_groupid) ids)
208 (goto-char (cadr id_point_groupid))
209 (when (get-text-property (point) 'fld-choices)
210 (remove-text-properties
212 '(keymap nil fld-choices nil)))
213 (remove-text-properties
215 '(category nil fld-id nil fld-group-id nil fld-state nil))))
216 (fld-list-in-buffer))))
217 (fld-enable-monitoring))
219(defun fld-find-next-startpos-same-group ()
220 (assert (or (fld-in) (fld-after)))
224 (gid (fld-group-id)))
226 (goto-char (fld-end))
228 (eq gid (fld-group-id)))
234 (setq tmp (next-single-property-change (fld-end) 'fld-id))
237 (if (eq gid (get-text-property tmp 'fld-group-id))
243(defun fld-find-prev-startpos-same-group ()
244 (assert (or (fld-in) (fld-after)))
248 (gid (fld-group-id)))
251 (setq tmp (previous-single-property-change (fld-beginning) 'fld-id))
256 (if (eq gid (get-text-property (point) 'fld-group-id))
258 result (fld-beginning))))))
263 (assert (or (fld-in) (fld-after)))
264 (let ((next (fld-find-next-startpos-same-group))
272 (setq gid (get-text-property (point) 'fld-group-id))
273 (setq loc (gethash gid fld-group-id-to-exit-point)))
275 ;; cleanup form, and go to departure location
281 (assert (or (fld-in) (fld-after)))
282 (let ((prev (fld-find-prev-startpos-same-group)))
286(defun fld-make (text group-id)
287 (let* ((id (fld-nextid)))
288 (puthash id group-id fld-id-to-group-id)
289 (add-text-properties 0 (length text)
290 `(category fld-category
292 fld-group-id ,group-id
297(defun fld-choose-1 (prompt choices-list existing-choice)
298 "Choose an item from a list."
299 (let* (i map done o choice-index choice-info out)
300 (setq map (make-sparse-keymap))
302 (setq choice-index 0)
303 (kill-buffer (get-buffer-create " fld-choose"))
304 (with-current-buffer (get-buffer-create " fld-choose")
306 (insert "Make your choice, C-g aborts:\n\n")
310 (setq choice-info (cons (cons i (list (line-number-at-pos)
313 (when (equal existing-choice c)
314 (setq choice-index i))
318 (setq choice-info (reverse choice-info))
319 (goto-char (point-min))
321 (forward-line choice-index)
322 (setq o (make-overlay (line-beginning-position)
323 (+ (line-beginning-position)
324 (cadr (cdr (assoc choice-index choice-info))))))
325 (overlay-put o 'face 'bold-italic))
326 (save-window-excursion
327 (delete-other-windows)
328 (let ((buf (get-buffer " fld-choose")))
329 (fit-window-to-buffer (display-buffer buf))
332;; (cursor-in-echo-area t)
334 (setq keys (read-key-sequence-vector prompt))
335;; (message "keys are %s" keys)
338 (when (> choice-index 0)
340 (setq choice-index (1- choice-index))
341 (goto-line (car (cdr (assoc choice-index choice-info))))
343 (line-beginning-position)
344 (+ (line-beginning-position)
345 (cadr (cdr (assoc choice-index
348 (when (< choice-index (1- i))
350 (setq choice-index (1+ choice-index))
351 (goto-line (car (cdr (assoc choice-index choice-info))))
353 (line-beginning-position)
354 (+ (line-beginning-position)
355 (cadr (cdr (assoc choice-index
358 (setq out (nth choice-index choices-list)
366;; (fld-choose-1 "Pick a letter" '("a" "b" "cabbie") "b")
370 (assert (or (fld-in) (fld-after)))
373 (existing-choice nil))
376 (setq choices (get-text-property (point) 'fld-choices))
377 (setq existing-choice (buffer-substring-no-properties (fld-beginning)
379 (setq choice (fld-choose-1 "choose one" choices existing-choice))
380 (when (member choice choices)
381;; (message "replacing with %s" choice)
385 (inhibit-modification-hooks t))
387 (goto-char (fld-beginning))
388 (setq props (text-properties-at (point)))
389 (delete-region (fld-beginning) (fld-end))
392 (add-text-properties oldpt (point) props))))))
394(defun fld-insert-choice (default choices group-id)
396 (let ((old-pt (point)))
397 (fld-insert default group-id)
398 (add-text-properties old-pt (point)
402 (set-text-properties 0 (length x) nil x)
404 (put-text-property old-pt (point) 'keymap fld-choose-keymap)))
406(defun fld-list-in-buffer ()
407 ;; return list of 3-tuples: (id starting-position group-id)
410 (flds (make-hash-table)))
412 (goto-char (point-min))
414 (puthash (fld-id) (list (point) (fld-group-id)) flds)
415 (goto-char (fld-end)))
419 (puthash (fld-id) (list (point) (fld-group-id)) flds)
420 (goto-char (fld-end)))
421 (setq pt (next-single-property-change (point) 'fld-id))
423 (goto-char (point-max))
425 (puthash (fld-id) (list (point) (fld-group-id)) flds)
426 (goto-char (fld-end))))))
429 (setq out (cons (cons k v) out)))
431 ;; (message "lsout: %s" (reverse out))
434(defun fld-kill-replaced-regions ()
435 (let ((inhibit-modification-hooks t))
437 (goto-char (point-min))
439 (if (get-text-property (point) 'fld-about-to-be-replaced)
440 (delete-region (point) (1+ (point)))
441 (forward-char 1))))))
443(defvar fld-detect-before nil)
444(defvar fld-ressurection-pos nil)
445(defvar fld-ressurection-id nil)
446(defvar fld-ressurection-now nil)
447(defvar fld-transition-to-typed-now nil)
448(defvar fld-transition-to-typed-id nil)
449(defvar fld-transition-to-typed-gid nil)
450(defvar fld-during-save nil)
451(defun fld-detect-pre (beg end)
454 (if (eq beg end) ;; insertion
457 ((or (fld-in) (fld-after))
460 (when (and (eq (get-text-property (point) 'fld-state) 'untyped)
461 (not fld-during-save)) ;; avoid
462 ;; require-final-newline
464 (setq fld-transition-to-typed-id
465 (get-text-property (point) 'fld-id)
466 fld-transition-to-typed-gid
467 (get-text-property (point) 'fld-group-id)
468 fld-transition-to-typed-now t)
470 (fld-beginning) (fld-end)
471 '(fld-about-to-be-replaced t)))))
472 ((eq (point) fld-ressurection-pos)
473 (setq fld-ressurection-now t))
476 (setq fld-detect-before (fld-list-in-buffer)))))
477(defun fld-detect-post (beg end len)
481;; (message "fld-detect-post: beg < end ? %s < %s" beg end)
487 (let ((flds-now (fld-list-in-buffer)))
488 (when (< (length flds-now) (length fld-detect-before))
489;; (message "yo, we lost fields: %s"
490;; (set-difference (mapcar 'car fld-detect-before)
491;; (mapcar 'car flds-now)))
492 (let ((ls (copy-sequence fld-detect-before))
495 (while (and (not done) ls)
496 (if (eq (point) (cadr (car ls)))
499 (setq id (car (car ls))))
502 (setq fld-ressurection-pos nil)
503;; (message "could ressurect id %s" id)
504 (setq fld-ressurection-pos (point))
505 (setq fld-ressurection-id id))))))
508 ((eq fld-transition-to-typed-now t)
509 (setq fld-transition-to-typed-now nil)
512 `(category fld-category
513 fld-id ,fld-transition-to-typed-id
514 fld-group-id ,fld-transition-to-typed-gid
516 (remove-text-properties beg end
517 '(fld-about-to-be-replaced nil))
518 (fld-kill-replaced-regions))
519 ((eq fld-ressurection-now t)
520 (setq fld-ressurection-now nil)
521 (let ((inhibit-modification-hooks t))
524 `(category fld-category
525 fld-id ,fld-ressurection-id
526 fld-group-id ,(gethash fld-ressurection-id
531(defun fld-before-save ()
532 (setq fld-during-save t))
533(defun fld-after-save ()
534 (setq fld-during-save nil))
536(defadvice yank (around fld-handle-yank activate)
540 (if (or (fld-in) (fld-after))
544 (let ((inhibit-modification-hooks t)
545 (yank-excluded-properties t)
547 (old-id (save-excursion (fld-focus) (fld-id))))
548 (when (eq (get-text-property (point) 'fld-state) 'untyped)
550 (fld-beginning) (fld-end)
551 '(fld-about-to-be-replaced t)))
555 `(category fld-category
557 fld-group-id ,(gethash old-id fld-id-to-group-id)
559 (fld-kill-replaced-regions)))
562;; faster yank advice?
563(defadvice yank (after fld-handle-yank-after activate)
565 (let ((fld-id-at-mark (get-text-property (mark) 'fld-id)))
566 (if (or fld-id-at-mark
567 (next-single-property-change (mark) 'fld-id nil (point)))
571 (when (get-text-property pos 'fld-id)
572 (add-text-properties pos (1+ pos)
573 '(category fld-category)))
574 (setq pos (1+ pos))))))))
577;; (defadvice yank (after fld-handle-yank-after activate)
578;; (let ((yanked (copy-sequence (car kill-ring)))
580;; ;; (debug-on-error t)
581;; (preserve-fld-props nil))
582;; (while (> (length yanked) 0)
583;; (setq s (substring yanked 0 1))
584;; (if (get-text-property 0 'fld-id s)
586;; (setq preserve-fld-props t)
588;; (setq yanked (substring yanked 1))))
589;; (if preserve-fld-props
593;; (when (get-text-property pos 'fld-id)
594;; (add-text-properties pos (1+ pos)
595;; '(category fld-category)))
596;; (setq pos (1+ pos)))))))
598(defadvice expand-abbrev (around fld-handle-expand-abbrev activate)
599 (let* ((from-fld (or (fld-in) (fld-after)))
600 (from-fld-id (and from-fld (fld-id)))
601 (from-fld-start (and from-fld (fld-beginning)))
602 (fields-before (fld-currgroupid)))
605 (let ((inhibit-modification-hooks t))
606 (if (eq (fld-currgroupid) fields-before) ;; didn't make new
609 from-fld-start (point)
610 `(category fld-category
612 fld-group-id ,(gethash from-fld-id fld-id-to-group-id)
614 ;; made new form, assimilate into our group
616 (let ((flds (fld-list-in-buffer)))
618 (lambda (id_point_groupid)
619 (let ((id (car id_point_groupid))
620 (pt (cadr id_point_groupid))
621 (gid (caddr id_point_groupid)))
622 (when (eq gid fld-last-group-id)
624 (puthash id (gethash from-fld-id fld-id-to-group-id)
629 ,(gethash from-fld-id fld-id-to-group-id))))))
632(defadvice dabbrev-expand (around fld-handle-dabbrev-expand activate)
633 (let* ((from-fld (or (fld-in) (fld-after)))
635 (from-fld-start (and from-fld (fld-beginning)))
636 (from-fld-id (and from-fld (fld-id))))
640 from-fld-start (point)
641 `(category fld-category
643 fld-group-id ,(gethash from-fld-id fld-id-to-group-id)
646(defun fld-enable-monitoring ()
647 (add-hook 'before-change-functions 'fld-detect-pre nil t)
648 (add-hook 'after-change-functions 'fld-detect-post nil t)
649 (add-hook 'before-save-hook 'fld-before-save nil t)
650 (add-hook 'after-save-hook 'fld-after-save nil t))
652(defun fld-disable-monitoring ()
653 (remove-hook 'before-change-functions 'fld-detect-pre t)
654 (remove-hook 'after-change-functions 'fld-detect-post t)
655 (remove-hook 'before-save-hook 'fld-before-save t)
656 (remove-hook 'after-save-hook 'fld-after-save t))
658(defun fld-insert (text group-id)
659 (fld-disable-monitoring)
660 (setq fld-last-group-id group-id)
661 (let ((fld (fld-make text group-id)))
664(defun fld-set-exit-location (point-or-marker)
665 (puthash fld-last-group-id point-or-marker fld-group-id-to-exit-point))
667(defun fld-activate ()
668 (fld-enable-monitoring))
670(defun msf-abbrev-expand-function-default (file &optional transform-func)
671 (let* ((orig-buffer (current-buffer))
672 (cursor-leave-point nil)
673 (insertion-point-begin (point-marker))
674 (insertion-point-end nil)
675 (trigger-line-opening-whitespace nil)
678 (fields-created-this-abbrev 0)
679 (gid (1+ (fld-currgroupid)))
681 (first-field-marker nil))
684 (insert-file-contents file)
685 (buffer-substring-no-properties (point-min) (point-max))))
687 ;; replace any <query "Loop iterator: ">-style snippets first before
690 (let ((query-alist nil))
692 (insert text-expanded)
693 (goto-char (point-min))
694 (while (re-search-forward "\\(<\\(QUERY\\|query\\) \"\\(.*?\\)\">\\)" nil t)
695 (let ((beginpt (match-beginning 1))
696 (endpt (match-end 1))
697 (key (match-string 3)))
698 (when (not (assoc key query-alist))
700 (cons (list key (read-from-minibuffer key))
703 (delete-region beginpt endpt)
704 (insert (cadr (assoc key query-alist)))))
705 (buffer-substring-no-properties (point-min) (point-max)))))
708 (insert text-expanded)
709 (setq insertion-point-end (point-marker))
710 (set-marker-insertion-type insertion-point-end t)
713 (set-text-properties insertion-point-begin
718 (goto-char insertion-point-begin)
719 (apply transform-func (list insertion-point-begin insertion-point-end)))
721 ;; replace any <varlookup "user-full-name">-style snippets
722 (goto-char insertion-point-begin)
723 (while (re-search-forward "<\\(VARLOOKUP\\|varlookup\\) \"\\(.*?\\)\">"
724 insertion-point-end t)
725 (let ((v (match-string 2)))
726 (replace-match (eval (intern v)) nil t)))
728 ;; replace any <ELISP "(insert "hi")">-style snippets
729 (goto-char insertion-point-begin)
730 (while (re-search-forward "<\\(ELISP\\|elisp\\) \"\\(.*?\\)\">" insertion-point-end t)
731 (let ((v (match-string 2)))
735 ;; replace any <COMMENT "blah blah"> snippets
736 (goto-char insertion-point-begin)
737 (while (re-search-forward "^<\\(COMMENT\\|comment\\) \"\\(.*?\\)\">$" insertion-point-end t)
739 (let ((kill-ring-old kill-ring))
741 (setq kill-ring kill-ring-old)))
742 (goto-char insertion-point-begin)
743 (while (re-search-forward "<\\(COMMENT\\|comment\\) \"\\(.*?\\)\">" insertion-point-end t)
746 ;; calculate the whitespace on the beginning of the trigger line
747 ;; and mimic it as a prefix throughout insertions
748 (setq trigger-line-opening-whitespace
750 (goto-char insertion-point-begin)
755 (while (looking-at "[ \t]")
758 (buffer-substring beg end))))
760 ;; expand any trigger-line opening whitespace on subsequent lines
761 (goto-char insertion-point-begin)
763 (while (< (point) insertion-point-end)
764 (insert trigger-line-opening-whitespace)
767 ;; position at end of insertion
768 (goto-char insertion-point-end)
771 (goto-char insertion-point-begin)
772 (while (re-search-forward "<\\(FORMJUMP\\|field\\) \"\\(.*?\\)\">"
773 (marker-position insertion-point-end) t)
774 (let ((txt (match-string 2)))
775 (replace-match "" nil t)
776 (if (not first-field-marker)
777 (setq first-field-marker (point-marker)))
778 (fld-insert txt gid))
779 (setq fields-created-this-abbrev
780 (1+ fields-created-this-abbrev))))
782 ;; handle <choose><choice "OH"><choice "TX"></choose>
784 (let ((choice-start nil)
787 (goto-char insertion-point-begin)
788 (while (re-search-forward "<choose>"
789 (marker-position insertion-point-end) t)
790 (replace-match "" nil t)
791 (setq choice-start (point-marker))
792 (assert (re-search-forward "</choose>"
793 (marker-position insertion-point-end) t))
794 (replace-match "" nil t)
795 (setq choice-stop (point-marker))
796 (goto-char choice-start)
797 (if (or (not first-field-marker)
798 (< (point-marker) first-field-marker))
799 (setq first-field-marker (point-marker)))
801 (while (re-search-forward "<choice \"\\(.*?\\)\">"
802 (marker-position choice-stop) t)
803 (setq choices (cons (match-string 1) choices))
804 (replace-match "" nil t))
805 (setq choices (reverse choices))
806 (fld-insert-choice (car choices) choices gid)
807 (setq fields-created-this-abbrev
808 (1+ fields-created-this-abbrev)))))
811 (goto-char insertion-point-begin)
812 (when (re-search-forward "<endpoint>"
813 (marker-position insertion-point-end) t)
814 (replace-match "" nil t)
815 (fld-set-exit-location (point-marker))
816 (setq set-endpoint t)))
818 (when (> fields-created-this-abbrev 0)
819 (when (not set-endpoint)
820 (fld-set-exit-location insertion-point-end)
821 (setq set-endpoint t))
822 (fld-nextgroupid) ;; only use the group id if we added some form fields
826 ;; leave cursor at first field location from this expansion
827 (goto-char first-field-marker)
828 (set-marker first-field-marker nil))
832 (goto-char insertion-point-begin)
833 (when (re-search-forward "<\\(CURSOR\\|cursor\\)>"
834 (marker-position insertion-point-end) t)
836 (setq cursor-leave-point (point)))))
837 (when cursor-leave-point
838 (goto-char cursor-leave-point))
840 ;; possibly indent the expanded text
841 (when msf-abbrev-indent-after-expansion
842 (indent-region insertion-point-begin insertion-point-end))
844 (setq msf-abbrev-fields-created
845 (+ msf-abbrev-fields-created fields-created-this-abbrev))))
847(defun msf-abbrev-expand-file (file)
848 (if (string-match "\\.el$" file)
849 ;; if the abbrev is an .el file, just use elisp to evaluate it
852 (insert-file-contents file)
853 (buffer-substring (point-min) (point-max)))))
854 (msf-abbrev-eval (read evalstr)))
855 (let ((transform-func nil)
856 (transform-file (concat file "_")))
857 (when (file-exists-p transform-file)
861 (insert-file-contents transform-file)
862 (buffer-substring (point-min) (point-max))))))
863 (apply msf-abbrev-expand-function (list file transform-func))))
864 (run-hooks 'msf-abbrev-expand-hook))
866(defun msf-abbrev-directory-files (dir)
870 (let ((basenm (file-name-nondirectory x)))
871 (if (or (string-match "^\\." basenm)
872 (string-match "~$" basenm)
873 (string-match "_$" basenm))
875 (directory-files dir t))))
877(defun msf-abbrev-report-if-verbose (abbr modename)
878 (when msf-abbrev-verbose
879 (message "defined abbrev %10s for mode %s" abbr modename)))
881(defun msf-abbrev-eval (text)
882;; (message "about to eval %s" text)
885(defun msf-abbrev-locate-mode-dir (modename)
887 (file-name-all-completions
888 (concat modename ".aliases.") msf-abbrev-root))
891 (assert (string-match "\\(.*\\)\\.aliases\\.\\(.*\\)" (car cpls)))
892 (setq dest (match-string 2 (car cpls))))
893 (concat (file-name-as-directory msf-abbrev-root) dest)))
895(defmacro msf-abbrev-try-require (lib)
896 `(let ((succeeded t))
899 (error (setq succeeded nil)))
901(defvar msf-abbrev-table nil)
903(defun msf-abbrev-load ()
904 "Load all abbrevs under `msf-abbrev-root'.
906`msf-abbrev-root' should have subdirectories like c-mode,
907c++-mode, cperl-mode. etc. each of which contain files whose
908names will be used as abbreviations, expanding to the file's
909contents. The subdirectory `global' is special and loads up
910global-abbrev-table, thus its abbrevs are active in every mode."
911 (let* ((modedirs (msf-abbrev-directory-files msf-abbrev-root))
918 (abbrs-this-mode nil))
921 (setq modename (file-name-nondirectory modedir))
922 (if (string-match "^\\(.*\\)\\.aliases\\.\\(.*\\)$" modename)
923 (setq fetchdir (match-string 2 modename)
924 modename (match-string 1 modename))
926 (setq fetchdir modename))
927 (setq abbrs-this-mode nil)
929 ((and (boundp (setq sym (intern (concat modename "-abbrev-table"))))
931 (setq method 'abbrev-table))
932 ((boundp (setq sym (intern (concat modename "-hook"))))
933 (setq method 'mode-hook))
935 (message "WARNING from msf-abbrev.el: no abbrev table %s-abbrev-table and no hook %s-hook, abbrevs for mode %s will not be loaded"
936 modename modename modename)
938 (setq symstr (symbol-name sym))
939 (let ((abbrs (msf-abbrev-directory-files (concat (file-name-as-directory msf-abbrev-root) fetchdir)))
940 (abbr-sans-extension nil))
943 (setq abbr-sans-extension
944 (file-name-sans-extension abbr))
945 (setq abbrs-this-mode
946 (cons (file-name-nondirectory abbr-sans-extension)
948 ;; define the new function
950 (format "msf-abbrev-generatedfunc-%s-%s"
951 modename (file-name-nondirectory
952 abbr-sans-extension))))
955 `(defun ,(intern newfuncnm) ()
957 (msf-abbrev-expand-file ,abbr)
958 'returning-nonnil-here-inhibits-self-insertion))
960 ;; add property to inhibit expansion of trigger (e.g. SPC)
962 `(put ',(intern newfuncnm) 'no-self-insert t))
965 ;; abbrev table method
966 ((eq method 'abbrev-table)
968 `(define-abbrev ,sym ,(file-name-nondirectory
969 abbr-sans-extension) ""
970 ',(intern newfuncnm)))
971 (msf-abbrev-report-if-verbose
972 (file-name-nondirectory abbr-sans-extension) modename))
975 ((eq method 'mode-hook)
980 (define-abbrev local-abbrev-table
981 ,(file-name-nondirectory
982 abbr-sans-extension) ""
983 ',(intern newfuncnm))))))
984 (msf-abbrev-eval evtext))
985 (msf-abbrev-report-if-verbose
986 (file-name-nondirectory abbr-sans-extension) modename))
988 ;; do nothing if no <MODE>-abbrev-table or <MODE>-hook exists
992 (add-to-list 'msf-abbrev-table
993 (list (file-name-nondirectory modedir)
994 (sort abbrs-this-mode 'string<)))))
997(defun msf-abbrev-reload-after-save ()
998 (let* ((bfn (expand-file-name (buffer-file-name)))
999 (root (expand-file-name msf-abbrev-root)))
1000 (when (string-match (concat "^" root) bfn)
1001 ;; we just saved an msf-abbrev file, so reload the tree
1002 (msf-abbrev-load))))
1003(add-hook 'after-save-hook 'msf-abbrev-reload-after-save)
1005(defun msf-abbrev-goto-root ()
1007 (let ((current-mode-str (format "%s" major-mode)))
1008 (if (assoc current-mode-str msf-abbrev-table)
1009 (dired (msf-abbrev-locate-mode-dir current-mode-str))
1010 (dired msf-abbrev-root))))
1012(defun msf-abbrev-define-new-abbrev-this-mode ()
1014 (let* ((current-mode-str
1016 ;; create an exception case for AUCTeX
1018 (eq major-mode 'latex-mode)
1019 (boundp 'AUCTeX-version))
1022 (eq major-mode 'tex-mode)
1023 (boundp 'AUCTeX-version))
1025 (t (format "%s" major-mode))))
1026 (d (msf-abbrev-locate-mode-dir current-mode-str)))
1027 (when (or (file-exists-p d)
1030 "Could not find directory %s, create it? " d))
1034 (let ((name (read-from-minibuffer "Abbrev name: ")))
1035 (find-file (concat (file-name-as-directory d) name))))))
1037(defun msf-abbrev-abbrev-choose ()
1039 (let ((tbl (assoc (format "%s" major-mode) msf-abbrev-table))
1042 (setq choice (completing-read "Choose abbrev: " (cadr tbl) nil t))
1046(defun msf-abbrev-string-no-properties (str)
1049 (buffer-substring-no-properties (point-min) (point-max))))
1051(defun msf-abbrev-abbrev-complete ()
1053 (let ((tbl (assoc (format "%s" major-mode) msf-abbrev-table))
1055 (thing (thing-at-point 'word))
1058 (when (and thing tbl)
1059 (setq tbl (cadr tbl))
1060 (setq s (msf-abbrev-string-no-properties thing))
1061 (setq result (try-completion s tbl))
1065 (delete-windows-on (get-buffer-create "*msf-abbrev completions*"))
1066 (delete-region (- (point) (length s)) (point))
1069 ((not (string= result s))
1070 (delete-windows-on (get-buffer-create "*msf-abbrev completions*"))
1071 (delete-region (- (point) (length s)) (point))
1073 (when (member result tbl)
1076 (with-output-to-temp-buffer "*msf-abbrev completions*"
1077 (display-completion-list
1078 (all-completions s tbl)))))))))
1080(provide 'msf-abbrev)