Subject: Re: bug#41890: 28.0.50; [PATCH]: Add bindings for project.el

From: "Philip K." <[email-masked]>
Date: Thu, 18 Jun 2020 16:09:03 +0200

Dmitry Gutov <[email-masked]> writes:

> On 18.06.2020 00:05, Juri Linkov wrote:
>>>> I tried implementig it, and it seems to work. The patch below isn't a
>>>> full commit, since I changed the structure of project-switch-commands
>>>> (it's now mapping command to description), but didn't update the
>>>> docstring.
>>>
>>> This is looking pretty good.
>> 
>> I agree.
>> 
>>> It's a backward incompatibility, though.
>> 
>> Not a problem since it was added recently.
>
> Here's another concern: right now, if the user types some other 
> character by accident, the command will keep showing the prompt until 
> the user hits one of the chars corresponding to the displayed options.
>
> If we just look in the (bigger) project keymap, in some cases we would 
> call commands that are not shown at the screen as a result of accidental 
> presses. And that's a negative.
>
> Perhaps we could add a user option that would default to the current 
> behavior? But then the implementation couldn't use the transient map, 
> though.

The patch below fixes that, but allows changing if you only want the
listed keys to be valid (the default) or every key in
project-prefix-map.

It turned out that the transiment map approach didn't work, as it
ignored the value in default-directory, thus running all commands in
whatever the current project was.

-- 
	Philip K.

>>From 608a94a2fee42b89b0ae395ce9d5622cb884d9d1 Mon Sep 17 00:00:00 2001
From: Philip K <[email-masked]>
Date: Thu, 18 Jun 2020 16:06:19 +0200
Subject: [PATCH] Use same keys in project-switch-project as in
 project-prefix-map

* project.el (project-switch-commands): Convert to user option and
change structure.
(project-switch-use-entire-map): Add new option.
(project--keymap-prompt): Adapt to change in project-switch-commands
(project-switch-project): Use project-prefix-map instead of
project-switch-commands to query valid commands.
---
 lisp/progmodes/project.el | 63 +++++++++++++++++++++++++--------------
 1 file changed, 41 insertions(+), 22 deletions(-)

diff --git a/lisp/progmodes/project.el b/lisp/progmodes/project.el
index e24d81c1b4..33946f78a8 100644
--- a/lisp/progmodes/project.el
+++ b/lisp/progmodes/project.el
@@ -900,27 +900,46 @@ project-prompt-project-dir
 ;;; Project switching
 
 ;;;###autoload
-(defvar project-switch-commands
-  '((?f "Find file" project-find-file)
-    (?g "Find regexp" project-find-regexp)
-    (?d "Dired" project-dired)
-    (?v "VC-Dir" project-vc-dir)
-    (?e "Eshell" project-eshell))
-  "Alist mapping keys to project switching menu entries.
+(defcustom project-switch-commands
+  '((project-find-file . "Find file")
+    (project-find-regexp . "Find regexp")
+    (project-dired . "Dired")
+    (project-vc-dir . "VC-Dir")
+    (project-shell . "Shell")
+    (project-eshell . "Eshell"))
+  "Alist mapping commands to descriptions.
 Used by `project-switch-project' to construct a dispatch menu of
 commands available upon \"switching\" to another project.
 
-Each element looks like (KEY LABEL COMMAND), where COMMAND is the
-command to run when KEY is pressed.  LABEL is used to distinguish
-the choice in the dispatch menu.")
+Each element looks like (COMMAND LABEL), where COMMAND should be
+bound in `project-prefix-map'.  LABEL is used to distinguish the
+choice in the dispatch menu."
+  :type '(alist :key-type function
+                :value-type string)
+  :options (mapcan (lambda (ent)
+                     (and (commandp (cdr ent))
+                          (list (cdr ent))))
+                   (cdr project-prefix-map))
+  :version "28.1")
+
+(defcustom project-switch-use-entire-map t
+  "Make `project-switch-project' use entire `project-prefix-map'.
+If nil, `project-switch-project' will only recognize commands
+listed in `project-switch-commands', and signal an error when
+others are invoked.  Otherwise, all keys in
+`project-switch-commands', are legal even if they aren't listed
+in the minibuffer."
+  :type 'bool
+  :version "28.1")
 
 (defun project--keymap-prompt ()
   "Return a prompt for the project swithing dispatch menu."
   (mapconcat
-   (pcase-lambda (`(,key ,label))
-     (format "[%s] %s"
-             (propertize (key-description `(,key)) 'face 'bold)
-             label))
+   (pcase-lambda (`(,cmd . ,label))
+     (let ((key (where-is-internal cmd project-prefix-map t)))
+       (format "[%s] %s"
+               (propertize (key-description key) 'face 'bold)
+               label)))
    project-switch-commands
    "  "))
 
@@ -930,14 +949,14 @@ project-switch-project
 The available commands are picked from `project-switch-commands'
 and presented in a dispatch menu."
   (interactive)
-  (let ((dir (project-prompt-project-dir))
-        (choice nil))
-    (while (not choice)
-      (setq choice (assq (read-event (project--keymap-prompt))
-                         project-switch-commands)))
-    (let ((default-directory dir)
-          (project-current-inhibit-prompt t))
-      (call-interactively (nth 2 choice)))))
+  (let* ((default-directory (project-prompt-project-dir))
+         (project-current-inhibit-prompt t)
+         (key (read-key-sequence-vector (project--keymap-prompt)))
+         (cmd (lookup-key project-prefix-map key)))
+    (if (and cmd (or project-switch-use-entire-map
+                     (assq cmd project-switch-commands)))
+        (call-interactively cmd)
+      (user-error "%s is undefined" (key-description key)))))
 
 (provide 'project)
 ;;; project.el ends here
-- 
2.20.1