comparison lisp/imenu.el @ 41305:71197bcff33c

(imenu--split-menu): Use dolist and copy-sequence. (imenu--create-keymap-2): Remove. (imenu--create-keymap-1): Simplify, remove third argument. (imenu--generic-function): Use dolist. (imenu-find-default): New function. (imenu--completion-buffer): Use it. (imenu--mouse-menu): Use popup-menu. (imenu--menubar-select): Return t rather than calling imenu.
author Stefan Monnier <monnier@iro.umontreal.ca>
date Tue, 20 Nov 2001 00:17:15 +0000
parents 253f761ad37b
children 5d834c3f5d18
comparison
equal deleted inserted replaced
41304:eecd5a100096 41305:71197bcff33c
205 205
206 The variable `imenu-case-fold-search' determines whether or not the 206 The variable `imenu-case-fold-search' determines whether or not the
207 regexp matches are case sensitive, and `imenu-syntax-alist' can be 207 regexp matches are case sensitive, and `imenu-syntax-alist' can be
208 used to alter the syntax table for the search. 208 used to alter the syntax table for the search.
209 209
210 For example, see the value of `lisp-imenu-generic-expression' used by 210 For example, see the value of `fortran-imenu-generic-expression' used by
211 `fortran-mode' with `imenu-syntax-alist' set locally to give the 211 `fortran-mode' with `imenu-syntax-alist' set locally to give the
212 characters which normally have \"symbol\" syntax \"word\" syntax 212 characters which normally have \"symbol\" syntax \"word\" syntax
213 during matching.") 213 during matching.")
214 214
215 ;;;###autoload 215 ;;;###autoload
515 (let (keep-at-top tail) 515 (let (keep-at-top tail)
516 (if (memq imenu--rescan-item menulist) 516 (if (memq imenu--rescan-item menulist)
517 (setq keep-at-top (cons imenu--rescan-item nil) 517 (setq keep-at-top (cons imenu--rescan-item nil)
518 menulist (delq imenu--rescan-item menulist))) 518 menulist (delq imenu--rescan-item menulist)))
519 (setq tail menulist) 519 (setq tail menulist)
520 (while tail 520 (dolist (item tail)
521 (if (imenu--subalist-p (car tail)) 521 (if (imenu--subalist-p item)
522 (setq keep-at-top (cons (car tail) keep-at-top) 522 (setq keep-at-top (cons item keep-at-top)
523 menulist (delq (car tail) menulist))) 523 menulist (delq item menulist))))
524 (setq tail (cdr tail)))
525 (if imenu-sort-function 524 (if imenu-sort-function
526 (setq menulist 525 (setq menulist
527 (sort 526 (sort
528 (let ((res nil) 527 (copy-sequence menulist)
529 (oldlist menulist))
530 ;; Copy list method from the cl package `copy-list'
531 (while (consp oldlist) (push (pop oldlist) res))
532 (if res ; in case, e.g. no functions defined
533 (prog1 (nreverse res) (setcdr res oldlist))))
534 imenu-sort-function))) 528 imenu-sort-function)))
535 (if (> (length menulist) imenu-max-items) 529 (if (> (length menulist) imenu-max-items)
536 (let ((count 0)) 530 (let ((count 0))
537 (setq menulist 531 (setq menulist
538 (mapcar 532 (mapcar
629 ((imenu--subalist-p item) 623 ((imenu--subalist-p item)
630 (imenu--cleanup (cdr item))))) 624 (imenu--cleanup (cdr item)))))
631 alist) 625 alist)
632 t)) 626 t))
633 627
634 (defun imenu--create-keymap-2 (alist counter &optional commands) 628 (defun imenu--create-keymap-1 (title alist)
635 (let ((map nil)) 629 (let ((counter 0))
636 (mapcar 630 (list* 'keymap title
637 (lambda (item) 631 (mapcar
638 (cond 632 (lambda (item)
639 ((imenu--subalist-p item) 633 (list* (car item) (car item)
640 (nconc (list (setq counter (1+ counter)) 634 (cond
641 (car item) 'keymap (car item)) 635 ((imenu--subalist-p item)
642 (imenu--create-keymap-2 (cdr item) (+ counter 10) commands))) 636 (imenu--create-keymap-1 (car item) (cdr item)))
643 (t 637 (t
644 (let ((end (if commands `(lambda () 638 `(lambda () (interactive)
645 (interactive) 639 (imenu--menubar-select ',item))))))
646 (imenu--menubar-select ',item)) 640 alist))))
647 (cons '(nil) item))))
648 (cons (car item)
649 (cons (car item) end)
650 ;; Fixme: Using this (to speded up menus), instead of
651 ;; the line above, breaks the case where `imenu' is
652 ;; bound to a mouse key. The code in imenu needs
653 ;; fixing somehow to cope.
654 ;; (list 'menu-item (car item) end :key-sequence nil)
655 )))))
656 alist)))
657
658 ;; If COMMANDS is non-nil, make a real keymap
659 ;; with a real command used as the definition.
660 ;; If it is nil, make something suitable for x-popup-menu.
661 (defun imenu--create-keymap-1 (title alist &optional commands)
662 (cons 'keymap (cons title (imenu--create-keymap-2 alist 0 commands))))
663 641
664 (defun imenu--in-alist (str alist) 642 (defun imenu--in-alist (str alist)
665 "Check whether the string STR is contained in multi-level ALIST." 643 "Check whether the string STR is contained in multi-level ALIST."
666 (let (elt head tail res) 644 (let (elt head tail res)
667 (setq res nil) 645 (setq res nil)
684 (string= str head)) 662 (string= str head))
685 (setq alist nil res elt)))) 663 (setq alist nil res elt))))
686 res)) 664 res))
687 665
688 (defvar imenu-syntax-alist nil 666 (defvar imenu-syntax-alist nil
689 "Alist of syntax table modifiers to use while executing `imenu--generic-function'. 667 "Alist of syntax table modifiers to use while in `imenu--generic-function'.
690 668
691 The car of the assocs may be either a character or a string and the 669 The car of the assocs may be either a character or a string and the
692 cdr is a syntax description appropriate fo `modify-syntax-entry'. For 670 cdr is a syntax description appropriate fo `modify-syntax-entry'. For
693 a string, all the characters in the string get the specified syntax. 671 a string, all the characters in the string get the specified syntax.
694 672
755 ;;; 733 ;;;
756 734
757 (defvar imenu-case-fold-search t 735 (defvar imenu-case-fold-search t
758 "Defines whether `imenu--generic-function' should fold case when matching. 736 "Defines whether `imenu--generic-function' should fold case when matching.
759 737
760 This buffer-local variable should be set (only) by initialization code 738 This variable should be set (only) by initialization code
761 for modes which use `imenu--generic-function'. If it is not set, that 739 for modes which use `imenu--generic-function'. If it is not set, that
762 function will use the current value of `case-fold-search' to match 740 function will use the current value of `case-fold-search' to match
763 patterns.") 741 patterns.")
764 ;;;###autoload 742 ;;;###autoload
765 (make-variable-buffer-local 'imenu-case-fold-search) 743 (make-variable-buffer-local 'imenu-case-fold-search)
795 (case-fold-search imenu-case-fold-search) 773 (case-fold-search imenu-case-fold-search)
796 (old-table (syntax-table)) 774 (old-table (syntax-table))
797 (table (copy-syntax-table (syntax-table))) 775 (table (copy-syntax-table (syntax-table)))
798 (slist imenu-syntax-alist)) 776 (slist imenu-syntax-alist))
799 ;; Modify the syntax table used while matching regexps. 777 ;; Modify the syntax table used while matching regexps.
800 (while slist 778 (dolist (syn slist)
801 ;; The character(s) to modify may be a single char or a string. 779 ;; The character(s) to modify may be a single char or a string.
802 (if (numberp (caar slist)) 780 (if (numberp (car syn))
803 (modify-syntax-entry (caar slist) (cdar slist) table) 781 (modify-syntax-entry (car syn) (cdr syn) table)
804 (mapc (lambda (c) 782 (dolist (c (car syn))
805 (modify-syntax-entry c (cdar slist) table)) 783 (modify-syntax-entry c (cdr syn) table))))
806 (caar slist)))
807 (setq slist (cdr slist)))
808 (goto-char (point-max)) 784 (goto-char (point-max))
809 (imenu-progress-message prev-pos 0 t) 785 (imenu-progress-message prev-pos 0 t)
810 (unwind-protect ; for syntax table 786 (unwind-protect ; for syntax table
811 (save-match-data 787 (save-match-data
812 (set-syntax-table table) 788 (set-syntax-table table)
813 ;; map over the elements of imenu-generic-expression 789 ;; map over the elements of imenu-generic-expression
814 ;; (typically functions, variables ...) 790 ;; (typically functions, variables ...)
815 (mapc 791 (dolist (pat patterns)
816 (lambda (pat) 792 (let ((menu-title (car pat))
817 (let ((menu-title (car pat)) 793 (regexp (nth 1 pat))
818 (regexp (nth 1 pat)) 794 (index (nth 2 pat))
819 (index (nth 2 pat)) 795 (function (nth 3 pat))
820 (function (nth 3 pat)) 796 (rest (nthcdr 4 pat)))
821 (rest (nthcdr 4 pat))) 797 ;; Go backwards for convenience of adding items in order.
822 ;; Go backwards for convenience of adding items in order. 798 (goto-char (point-max))
823 (goto-char (point-max)) 799 (while (re-search-backward regexp nil t)
824 (while (re-search-backward regexp nil t) 800 (imenu-progress-message prev-pos nil t)
825 (imenu-progress-message prev-pos nil t) 801 (setq beg (match-beginning index))
826 (setq beg (match-beginning index)) 802 ;; Add this sort of submenu only when we've found an
827 ;; Add this sort of submenu only when we've found an 803 ;; item for it, avoiding empty, duff menus.
828 ;; item for it, avoiding empty, duff menus. 804 (unless (assoc menu-title index-alist)
829 (unless (assoc menu-title index-alist) 805 (push (list menu-title) index-alist))
830 (push (list menu-title) index-alist)) 806 (if imenu-use-markers
831 (if imenu-use-markers 807 (setq beg (copy-marker beg)))
832 (setq beg (copy-marker beg))) 808 (let ((item
833 (let ((item 809 (if function
834 (if function 810 (nconc (list (match-string-no-properties index)
835 (nconc (list (match-string-no-properties index) 811 beg function)
836 beg function) 812 rest)
837 rest) 813 (cons (match-string-no-properties index)
838 (cons (match-string-no-properties index) 814 beg)))
839 beg))) 815 ;; This is the desired submenu,
840 ;; This is the desired submenu, 816 ;; starting with its title (or nil).
841 ;; starting with its title (or nil). 817 (menu (assoc menu-title index-alist)))
842 (menu (assoc menu-title index-alist))) 818 ;; Insert the item unless it is already present.
843 ;; Insert the item unless it is already present. 819 (unless (member item (cdr menu))
844 (unless (member item (cdr menu)) 820 (setcdr menu
845 (setcdr menu 821 (cons item (cdr menu))))))))
846 (cons item (cdr menu))))))))
847 patterns)
848 (set-syntax-table old-table))) 822 (set-syntax-table old-table)))
849 (imenu-progress-message prev-pos 100 t) 823 (imenu-progress-message prev-pos 100 t)
850 ;; Sort each submenu by position. 824 ;; Sort each submenu by position.
851 ;; This is in case one submenu gets items from two different regexps. 825 ;; This is in case one submenu gets items from two different regexps.
852 (let ((tail index-alist)) 826 (dolist (item index-alist)
853 (while tail 827 (when (listp item)
854 (if (listp (car tail)) 828 (setcdr item (sort (cdr item) 'imenu--sort-by-position))))
855 (setcdr (car tail)
856 (sort (cdr (car tail)) 'imenu--sort-by-position)))
857 (setq tail (cdr tail))))
858 (let ((main-element (assq nil index-alist))) 829 (let ((main-element (assq nil index-alist)))
859 (nconc (delq main-element (delq 'dummy index-alist)) 830 (nconc (delq main-element (delq 'dummy index-alist))
860 (cdr main-element))))) 831 (cdr main-element)))))
861 832
862 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 833 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
863 ;;; 834 ;;;
864 ;;; The main functions for this package! 835 ;;; The main functions for this package!
865 ;;; 836 ;;;
866 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 837 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
838
839 ;; See also info-lookup-find-item
840 (defun imenu-find-default (guess completions)
841 "Fuzzily find an item based on GUESS inside the alist COMPLETIONS."
842 (catch 'found
843 (let ((case-fold-search t))
844 (if (assoc guess completions) guess
845 (dolist (re (list (concat "\\`" (regexp-quote guess) "\\'")
846 (concat "\\`" (regexp-quote guess))
847 (concat (regexp-quote guess) "\\'")
848 (regexp-quote guess)))
849 (dolist (x completions)
850 (if (string-match re (car x)) (throw 'found (car x)))))))))
867 851
868 (defun imenu--completion-buffer (index-alist &optional prompt) 852 (defun imenu--completion-buffer (index-alist &optional prompt)
869 "Let the user select from INDEX-ALIST in a completion buffer with PROMPT. 853 "Let the user select from INDEX-ALIST in a completion buffer with PROMPT.
870 854
871 Returns t for rescan and otherwise a position number." 855 Returns t for rescan and otherwise a position number."
877 (lambda (item) 861 (lambda (item)
878 (cons (subst-char-in-string ?\ (aref imenu-space-replacement 0) 862 (cons (subst-char-in-string ?\ (aref imenu-space-replacement 0)
879 (car item)) 863 (car item))
880 (cdr item))) 864 (cdr item)))
881 index-alist))) 865 index-alist)))
866 (when (stringp name)
867 (setq name (or (imenu-find-default name prepared-index-alist) name)))
882 (cond (prompt) 868 (cond (prompt)
883 ((and name (imenu--in-alist name prepared-index-alist)) 869 ((and name (imenu--in-alist name prepared-index-alist))
884 (setq prompt (format "Index item (default %s): " name))) 870 (setq prompt (format "Index item (default %s): " name)))
885 (t (setq prompt "Index item: "))) 871 (t (setq prompt "Index item: ")))
886 (if (eq imenu-always-use-completion-buffer-p 'never) 872 (if (eq imenu-always-use-completion-buffer-p 'never)
894 (all-completions "" prepared-index-alist ))) 880 (all-completions "" prepared-index-alist )))
895 (let ((minibuffer-setup-hook 881 (let ((minibuffer-setup-hook
896 (function 882 (function
897 (lambda () 883 (lambda ()
898 (let ((buffer (current-buffer))) 884 (let ((buffer (current-buffer)))
899 (save-excursion 885 (with-current-buffer "*Completions*"
900 (set-buffer "*Completions*")
901 (setq completion-reference-buffer buffer))))))) 886 (setq completion-reference-buffer buffer)))))))
902 ;; Make a completion question 887 ;; Make a completion question
903 (setq name (completing-read prompt 888 (setq name (completing-read prompt
904 prepared-index-alist 889 prepared-index-alist
905 nil t nil 'imenu--history-list name))))) 890 nil t nil 'imenu--history-list name)))))
906 (cond ((not (stringp name)) 891 (cond ((not (stringp name)) nil)
907 nil) 892 ((string= name (car imenu--rescan-item)) t)
908 ((string= name (car imenu--rescan-item))
909 t)
910 (t 893 (t
911 (setq choice (assoc name prepared-index-alist)) 894 (setq choice (assoc name prepared-index-alist))
912 (if (imenu--subalist-p choice) 895 (if (imenu--subalist-p choice)
913 (imenu--completion-buffer (cdr choice) prompt) 896 (imenu--completion-buffer (cdr choice) prompt)
914 choice))))) 897 choice)))))
918 901
919 INDEX-ALIST is the buffer index and EVENT is a mouse event. 902 INDEX-ALIST is the buffer index and EVENT is a mouse event.
920 903
921 Returns t for rescan and otherwise an element or subelement of INDEX-ALIST." 904 Returns t for rescan and otherwise an element or subelement of INDEX-ALIST."
922 (setq index-alist (imenu--split-submenus index-alist)) 905 (setq index-alist (imenu--split-submenus index-alist))
923 (let* ((menu (imenu--split-menu index-alist 906 (let* ((menu (imenu--split-menu index-alist (or title (buffer-name))))
924 (or title (buffer-name)))) 907 (map (imenu--create-keymap-1 (car menu)
925 position) 908 (if (< 1 (length (cdr menu)))
926 (setq menu (imenu--create-keymap-1 (car menu) 909 (cdr menu)
927 (if (< 1 (length (cdr menu))) 910 (cdr (car (cdr menu)))))))
928 (cdr menu) 911 (popup-menu map event)))
929 (cdr (car (cdr menu))))))
930 (setq position (x-popup-menu event menu))
931 (cond ((eq position nil)
932 position)
933 ;; If one call to x-popup-menu handled the nested menus,
934 ;; find the result by looking down the menus here.
935 ((and (listp position)
936 (numberp (car position))
937 (stringp (nth (1- (length position)) position)))
938 (let ((final menu))
939 (while position
940 (setq final (assq (car position) final))
941 (setq position (cdr position)))
942 (or (string= (car final) (car imenu--rescan-item))
943 (nthcdr 3 final))))
944 ;; If x-popup-menu went just one level and found a leaf item,
945 ;; return the INDEX-ALIST element for that.
946 ((and (consp position)
947 (stringp (car position))
948 (null (cdr position)))
949 (or (string= (car position) (car imenu--rescan-item))
950 (assq (car position) index-alist)))
951 ;; If x-popup-menu went just one level
952 ;; and found a non-leaf item (a submenu),
953 ;; recurse to handle the rest.
954 ((listp position)
955 (imenu--mouse-menu position event
956 (if title
957 (concat title imenu-level-separator
958 (car (rassq position index-alist)))
959 (car (rassq position index-alist))))))))
960 912
961 (defun imenu-choose-buffer-index (&optional prompt alist) 913 (defun imenu-choose-buffer-index (&optional prompt alist)
962 "Let the user select from a buffer index and return the chosen index. 914 "Let the user select from a buffer index and return the chosen index.
963 915
964 If the user originally activated this function with the mouse, a mouse 916 If the user originally activated this function with the mouse, a mouse
976 not. 928 not.
977 929
978 The returned value is of the form (INDEX-NAME . INDEX-POSITION)." 930 The returned value is of the form (INDEX-NAME . INDEX-POSITION)."
979 (let (index-alist 931 (let (index-alist
980 (mouse-triggered (listp last-nonmenu-event)) 932 (mouse-triggered (listp last-nonmenu-event))
981 (result t) ) 933 (result t))
982 ;; If selected by mouse, see to that the window where the mouse is 934 ;; If selected by mouse, see to that the window where the mouse is
983 ;; really is selected. 935 ;; really is selected.
984 (and mouse-triggered 936 (and mouse-triggered
985 (not (equal last-nonmenu-event '(menu-bar))) 937 (not (equal last-nonmenu-event '(menu-bar)))
986 (let ((window (posn-window (event-start last-nonmenu-event)))) 938 (let ((window (posn-window (event-start last-nonmenu-event))))
1038 (let (menu menu1 old) 990 (let (menu menu1 old)
1039 (setq imenu--last-menubar-index-alist index-alist) 991 (setq imenu--last-menubar-index-alist index-alist)
1040 (setq index-alist (imenu--split-submenus index-alist)) 992 (setq index-alist (imenu--split-submenus index-alist))
1041 (setq menu (imenu--split-menu index-alist 993 (setq menu (imenu--split-menu index-alist
1042 (buffer-name))) 994 (buffer-name)))
1043 (setq menu1 (imenu--create-keymap-1 (car menu) 995 (setq menu1 (imenu--create-keymap-1 (car menu)
1044 (if (< 1 (length (cdr menu))) 996 (if (< 1 (length (cdr menu)))
1045 (cdr menu) 997 (cdr menu)
1046 (cdr (car (cdr menu)))) 998 (cdr (car (cdr menu))))))
1047 t))
1048 (setq old (lookup-key (current-local-map) [menu-bar index])) 999 (setq old (lookup-key (current-local-map) [menu-bar index]))
1049 (setcdr old (cdr menu1))))))) 1000 (setcdr old (cdr menu1)))))))
1050 1001
1051 (defun imenu--menubar-select (item) 1002 (defun imenu--menubar-select (item)
1052 "Use Imenu to select the function or variable named in this menu item." 1003 "Use Imenu to select the function or variable named in this menu ITEM."
1053 (if (equal item imenu--rescan-item) 1004 (if (equal item imenu--rescan-item)
1054 (progn 1005 (progn
1055 (imenu--cleanup) 1006 (imenu--cleanup)
1056 (setq imenu--index-alist nil) 1007 (setq imenu--index-alist nil)
1057 (imenu-update-menubar)) 1008 (imenu-update-menubar)
1058 (imenu item))) 1009 t)
1010 (imenu item)
1011 nil))
1059 1012
1060 (defun imenu-default-goto-function (name position &optional rest) 1013 (defun imenu-default-goto-function (name position &optional rest)
1061 "Move the point to the given position. 1014 "Move the point to the given position.
1062 1015
1063 NAME is ignored. POSITION is where to move. REST is also ignored. 1016 NAME is ignored. POSITION is where to move. REST is also ignored.
1076 for more information." 1029 for more information."
1077 (interactive (list (imenu-choose-buffer-index))) 1030 (interactive (list (imenu-choose-buffer-index)))
1078 ;; Convert a string to an alist element. 1031 ;; Convert a string to an alist element.
1079 (if (stringp index-item) 1032 (if (stringp index-item)
1080 (setq index-item (assoc index-item (imenu--make-index-alist)))) 1033 (setq index-item (assoc index-item (imenu--make-index-alist))))
1081 (and index-item 1034 (when index-item
1082 (progn 1035 (push-mark)
1083 (push-mark) 1036 (let* ((is-special-item (listp (cdr index-item)))
1084 ;; Fixme: sort this out so that we can use menu-item with 1037 (function
1085 ;; :key-sequence in imenu--create-keymap-2. 1038 (if is-special-item
1086 (let* ((is-special-item (listp (cdr index-item))) 1039 (nth 2 index-item) imenu-default-goto-function))
1087 (function 1040 (position (if is-special-item
1088 (if is-special-item 1041 (cadr index-item) (cdr index-item)))
1089 (nth 2 index-item) imenu-default-goto-function)) 1042 (rest (if is-special-item (cddr index-item))))
1090 (position (if is-special-item 1043 (apply function (car index-item) position rest))
1091 (cadr index-item) (cdr index-item))) 1044 (run-hooks 'imenu-after-jump-hook)))
1092 (rest (if is-special-item (cddr index-item))))
1093 (apply function (car index-item) position rest))))
1094 (run-hooks 'imenu-after-jump-hook))
1095 1045
1096 (dolist (mess 1046 (dolist (mess
1097 '("^No items suitable for an index found in this buffer$" 1047 '("^No items suitable for an index found in this buffer$"
1098 "^This buffer cannot use `imenu-default-create-index-function'$" 1048 "^This buffer cannot use `imenu-default-create-index-function'$"
1099 "^The mode `.*' does not support Imenu$")) 1049 "^The mode `.*' does not support Imenu$"))