don't require grep in vc-git
[bpt/emacs.git] / lisp / tmm.el
index 4bc1c9a..0972975 100644 (file)
@@ -1,9 +1,9 @@
-;;; tmm.el --- text mode access to menu-bar
+;;; tmm.el --- text mode access to menu-bar  -*- lexical-binding: t -*-
 
-;; Copyright (C) 1994-1996, 2000-2012 Free Software Foundation, Inc.
+;; Copyright (C) 1994-1996, 2000-2014 Free Software Foundation, Inc.
 
 ;; Author: Ilya Zakharevich <ilya@math.mps.ohio-state.edu>
-;; Maintainer: FSF
+;; Maintainer: emacs-devel@gnu.org
 ;; Keywords: convenience
 
 ;; This file is part of GNU Emacs.
   "Text-mode emulation of looking and choosing from a menubar.
 See the documentation for `tmm-prompt'.
 X-POSITION, if non-nil, specifies a horizontal position within the menu bar;
-we make that menu bar item (the one at that position) the default choice."
+we make that menu bar item (the one at that position) the default choice.
+
+Note that \\[menu-bar-open] by default drops down TTY menus; if you want it
+to invoke `tmm-menubar' instead, customize the variable
+\`tty-menu-open-use-tmm' to a non-nil value."
   (interactive)
   (run-hooks 'menu-bar-update-hook)
   ;; Obey menu-bar-final-items; put those items last.
-  (let ((menu-bar (tmm-get-keybind [menu-bar]))
+  (let ((menu-bar '())
+        (menu-end '())
        menu-bar-item)
-    (let ((list menu-bar-final-items))
-      (while list
-       (let ((item (car list)))
-         ;; ITEM is the name of an item that we want to put last.
-         ;; Find it in MENU-BAR and move it to the end.
-         (let ((this-one (assq item menu-bar)))
-           (setq menu-bar (append (delq this-one menu-bar)
-                                  (list this-one)))))
-       (setq list (cdr list))))
+    (map-keymap
+     (lambda (key binding)
+       (push (cons key binding)
+             ;; If KEY is the name of an item that we want to put last,
+             ;; move it to the end.
+             (if (memq key menu-bar-final-items)
+                 menu-end
+               menu-bar)))
+     (tmm-get-keybind [menu-bar]))
+    (setq menu-bar `(keymap ,@(nreverse menu-bar) ,@(nreverse menu-end)))
     (if x-position
-       (let ((tail menu-bar) (column 0)
-             this-one name visible)
-         (while (and tail (<= column x-position))
-           (setq this-one (car tail))
-           (if (and (consp this-one)
-                    (consp (cdr this-one))
-                    (setq name  ;simple menu
-                          (cond ((stringp (nth 1  this-one))
-                                 (nth 1  this-one))
-                                ;extended menu
-                                ((stringp (nth 2 this-one))
-                                 (setq visible (plist-get
-                                                (nthcdr 4 this-one) :visible))
-                                 (unless (and visible (not (eval visible)))
-                                   (nth 2 this-one))))))
-               (setq column (+ column (length name) 1)))
-           (setq tail (cdr tail)))
-         (setq menu-bar-item (car this-one))))
+       (let ((column 0))
+          (catch 'done
+            (map-keymap
+             (lambda (key binding)
+               (when (> column x-position)
+                 (setq menu-bar-item key)
+                 (throw 'done nil))
+               (pcase binding
+                 ((or `(,(and (pred stringp) name) . ,_) ;Simple menu item.
+                      `(menu-item ,name ,_cmd            ;Extended menu item.
+                        . ,(and props
+                                (guard (let ((visible
+                                              (plist-get props :visible)))
+                                         (or (null visible)
+                                             (eval visible)))))))
+                  (setq column (+ column (length name) 1)))))
+             menu-bar))))
     (tmm-prompt menu-bar nil menu-bar-item)))
 
 ;;;###autoload
@@ -138,6 +143,14 @@ specify nil for this variable."
   "Face used for inactive menu items."
   :group 'tmm)
 
+(defun tmm--completion-table (items)
+  (lambda (string pred action)
+    (if (eq action 'metadata)
+       '(metadata (display-sort-function . identity))
+      (complete-with-action action items string pred))))
+
+(defvar tmm--history nil)
+
 ;;;###autoload
 (defun tmm-prompt (menu &optional in-popup default-item)
   "Text-mode emulation of calling the bindings in keymap.
@@ -156,7 +169,7 @@ Its value should be an event that has a binding in MENU."
   ;; That is used for recursive calls only.
   (let ((gl-str "Menu bar")  ;; The menu bar itself is not a menu keymap
                                        ; so it doesn't have a name.
-       tmm-km-list out history history-len tmm-table-undef tmm-c-prompt
+       tmm-km-list out history-len tmm-table-undef tmm-c-prompt
        tmm-old-mb-map tmm-short-cuts
        chosen-string choice
        (not-menu (not (keymapp menu))))
@@ -165,13 +178,16 @@ Its value should be an event that has a binding in MENU."
     ;; tmm-km-list is an alist of (STRING . MEANING).
     ;; It has no other elements.
     ;; The order of elements in tmm-km-list is the order of the menu bar.
-    (dolist (elt menu)
-      (cond
-       ((stringp elt) (setq gl-str elt))
-       ((listp elt) (tmm-get-keymap elt not-menu))
-       ((vectorp elt)
-        (dotimes (i (length elt))
-          (tmm-get-keymap (cons i (aref elt i)) not-menu)))))
+    (if (not not-menu)
+        (map-keymap (lambda (k v) (tmm-get-keymap (cons k v))) menu)
+      (dolist (elt menu)
+        (cond
+         ((stringp elt) (setq gl-str elt))
+         ((listp elt) (tmm-get-keymap elt not-menu))
+         ((vectorp elt)
+          (dotimes (i (length elt))
+            (tmm-get-keymap (cons i (aref elt i)) not-menu))))))
+    (setq tmm-km-list (nreverse tmm-km-list))
     ;; Choose an element of tmm-km-list; put it in choice.
     (if (and not-menu (= 1 (length tmm-km-list)))
        ;; If this is the top-level of an x-popup-menu menu,
@@ -207,16 +223,18 @@ Its value should be an event that has a binding in MENU."
                         (setq index-of-default (1+ index-of-default)))
                     (setq tail (cdr tail)))))
              (let ((prompt (concat "^." (regexp-quote tmm-mid-prompt))))
-               (setq history
+               (setq tmm--history
                      (reverse (delq nil
                                     (mapcar
                                      (lambda (elt)
                                        (if (string-match prompt (car elt))
                                            (car elt)))
                                      tmm-km-list)))))
-            (setq history-len (length history))
-            (setq history (append history history history history))
-            (setq tmm-c-prompt (nth (- history-len 1 index-of-default) history))
+            (setq history-len (length tmm--history))
+            (setq tmm--history (append tmm--history tmm--history
+                                        tmm--history tmm--history))
+            (setq tmm-c-prompt (nth (- history-len 1 index-of-default)
+                                     tmm--history))
              (setq out
                    (if default-item
                        (car (nth index-of-default tmm-km-list))
@@ -224,8 +242,8 @@ Its value should be an event that has a binding in MENU."
                        (completing-read
                         (concat gl-str
                                 " (up/down to change, PgUp to menu): ")
-                        tmm-km-list nil t nil
-                        (cons 'history
+                        (tmm--completion-table tmm-km-list) nil t nil
+                        (cons 'tmm--history
                               (- (* 2 history-len) index-of-default))))))))
       (setq choice (cdr (assoc out tmm-km-list)))
       (and (null choice)
@@ -432,7 +450,7 @@ It uses the free variable `tmm-table-undef' to keep undefined keys."
                   (or (keymapp (cdr-safe (cdr-safe elt)))
                       (eq (car (cdr-safe (cdr-safe elt))) 'lambda))
                 (and (symbolp (cdr-safe (cdr-safe elt)))
-                              (fboundp (cdr-safe (cdr-safe elt)))))
+                      (fboundp (cdr-safe (cdr-safe elt)))))
               (setq km (cddr elt))
               (and (stringp (car elt)) (setq str (car elt))))
 
@@ -458,14 +476,15 @@ It uses the free variable `tmm-table-undef' to keep undefined keys."
                       (eq (car (cdr-safe (cdr-safe (cdr-safe elt)))) 'lambda))
                 (and (symbolp (cdr-safe (cdr-safe (cdr-safe elt))))
                      (fboundp (cdr-safe (cdr-safe (cdr-safe elt))))))
-                                        ; New style of easy-menu
+                                        ; New style of easy-menu
               (setq km (cdr (cddr elt)))
               (and (stringp (car elt)) (setq str (car elt))))
 
              ((stringp event)          ; x-popup or x-popup element
-              (if (or in-x-menu (stringp (car-safe elt)))
-                  (setq str event event nil km elt)
-                (setq str event event nil km (cons 'keymap elt)))))
+               (setq str event)
+               (setq event nil)
+              (setq km (if (or in-x-menu (stringp (car-safe elt)))
+                            elt (cons 'keymap elt)))))
         (unless (or (eq km 'ignore) (null str))
           (let ((binding (where-is-internal km nil t)))
             (when binding
@@ -494,46 +513,7 @@ If KEYSEQ is a prefix key that has local and global bindings,
 we merge them into a single keymap which shows the proper order of the menu.
 However, for the menu bar itself, the value does not take account
 of `menu-bar-final-items'."
-  (let (allbind bind minorbind localbind globalbind)
-    (setq bind (key-binding keyseq))
-    ;; If KEYSEQ is a prefix key, then BIND is either nil
-    ;; or a symbol defined as a keymap (which satisfies keymapp).
-    (if (keymapp bind)
-       (setq bind nil))
-    ;; If we have a non-keymap definition, return that.
-    (or bind
-       (progn
-         ;; Otherwise, it is a prefix, so make a list of the subcommands.
-         ;; Make a list of all the bindings in all the keymaps.
-          ;; FIXME: we'd really like to just use `key-binding' now that it
-          ;; returns a keymap that contains really all the bindings under that
-          ;; prefix, but `keyseq' is always [menu-bar], so the desired order of
-          ;; the bindings is difficult to recover.
-         (setq minorbind (mapcar 'cdr (minor-mode-key-binding keyseq)))
-         (setq localbind (local-key-binding keyseq))
-         (setq globalbind (copy-sequence (cdr (global-key-binding keyseq))))
-
-         ;; If items have been redefined/undefined locally, remove them from
-         ;; the global list.
-         (dolist (minor minorbind)
-           (dolist (item (cdr minor))
-             (setq globalbind (assq-delete-all (car-safe item) globalbind))))
-         (dolist (item (cdr localbind))
-           (setq globalbind (assq-delete-all (car-safe item) globalbind)))
-
-         (setq globalbind (cons 'keymap globalbind))
-         (setq allbind (cons globalbind (cons localbind minorbind)))
-
-         ;; Merge all the elements of ALLBIND into one keymap.
-         (dolist (in allbind)
-            (if (and (symbolp in) (keymapp in))
-                (setq in (symbol-function in)))
-            (and in (keymapp in)
-                 (setq bind (if (keymapp bind)
-                                (nconc bind (copy-sequence (cdr in)))
-                              (copy-sequence in)))))
-         ;; Return that keymap.
-         bind))))
+  (lookup-key (cons 'keymap (nreverse (current-active-maps))) keyseq))
 
 (provide 'tmm)