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

From: Sean Whitton <[email-masked]>
Date: Sat, 18 Jul 2020 09:06:25 -0700

Hello Dmitry and others,

On Sun 12 Jul 2020 at 11:12PM +03, Dmitry Gutov wrote:

> There's no decision indeed. I'd like to know what people think, and if
> there's no strong opinion, how the proposal would look in practice.
>
> So unless you're strapped for time, a prototype patch would help.

Okay, here's a prototype.  I had to rebase Philip's patch so I thought I
might as well attach my version of that too.

I have been wanting the new C-x 4 p bindings all week as C-x p f with
fido-mode has replaced a lot of my use of C-x C-f, so I hope I can
replace my use of C-x 4 f with C-x 4 p f soon :)

-- 
Sean Whitton

>From c9f20a5a547631ab12cd3cf8da2ca51b71cfb28e Mon Sep 17 00:00:00 2001
From: Philip K <[email-masked]>
Date: Thu, 18 Jun 2020 16:06:19 +0200
Subject: [PATCH 1/2] 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 67ce3dc7d9..4f0233c8b7 100644
--- a/lisp/progmodes/project.el
+++ b/lisp/progmodes/project.el
@@ -974,27 +974,46 @@ project-known-project-roots
 ;;; 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 is of the form (KEY LABEL COMMAND), where COMMAND is the
-command to run when KEY is pressed.  LABEL is used to distinguish
-the menu entries 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
    "  "))
 
@@ -1004,14 +1023,14 @@ project-switch-project
 The available commands are presented as a dispatch menu
 made from `project-switch-commands'."
   (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.27.0

>From 37bc752289bd101ceb725446b425944630971f61 Mon Sep 17 00:00:00 2001
From: Sean Whitton <[email-masked]>
Date: Sat, 18 Jul 2020 08:59:19 -0700
Subject: [PATCH 2/2] WIP: Add project-other-place-commands and functions which
 use it

---
 lisp/progmodes/project.el | 51 ++++++++++++++++++++++++++++++++++++---
 1 file changed, 48 insertions(+), 3 deletions(-)

diff --git a/lisp/progmodes/project.el b/lisp/progmodes/project.el
index 4f0233c8b7..f67698ac96 100644
--- a/lisp/progmodes/project.el
+++ b/lisp/progmodes/project.el
@@ -985,6 +985,36 @@ project-switch-commands
 Used by `project-switch-project' to construct a dispatch menu of
 commands available upon \"switching\" to another project.
 
+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")
+
+;; "other-place" because non-prototype patch will also add an entry
+;; in ctl-x-5-map and under C-x t p
+(defcustom project-other-place-commands
+  '((project-find-file . "Find file")
+    (project-switch-to-buffer . "Switch to buffer")
+    (project-dired . "Dired")
+    ;; Eshell uses the current window by default, but Shell defaults
+    ;; to using the other window.  If a user has added an entry to
+    ;; `display-buffer-alist' for Shell, they probably want to add an
+    ;; entry here, too
+    (project-eshell . "Eshell")
+    (project-switch-project . "Switch project"))
+    "Alist mapping commands to descriptions.
+Used by `project-other-window-command' to construct a dispatch menu of
+commands available to be displayed in another window.
+
+Commands in this list should be ones which normally display their
+buffer in the current window.
+
 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."
@@ -1006,7 +1036,7 @@ project-switch-use-entire-map
   :type 'bool
   :version "28.1")
 
-(defun project--keymap-prompt ()
+(defun project--keymap-prompt (cmds)
   "Return a prompt for the project swithing dispatch menu."
   (mapconcat
    (pcase-lambda (`(,cmd . ,label))
@@ -1014,7 +1044,7 @@ project--keymap-prompt
        (format "[%s] %s"
                (propertize (key-description key) 'face 'bold)
                label)))
-   project-switch-commands
+   cmds
    "  "))
 
 ;;;###autoload
@@ -1025,12 +1055,27 @@ project-switch-project
   (interactive)
   (let* ((default-directory (project-prompt-project-dir))
          (project-current-inhibit-prompt t)
-         (key (read-key-sequence-vector (project--keymap-prompt)))
+         (key (read-key-sequence-vector
+               (project--keymap-prompt project-switch-commands)))
          (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)))))
 
+(defun project-other-window-command ()
+  (interactive)
+  (let* ((key (read-key-sequence-vector
+               (project--keymap-prompt project-other-place-commands)))
+         (cmd (lookup-key project-prefix-map key)))
+    (if (and cmd (assq cmd project-other-place-commands))
+        (let ((display-buffer-overriding-action
+               '((display-buffer-pop-up-window)
+		 (inhibit-same-window . t))))
+          (call-interactively cmd))
+      (user-error "%s is undefined" (key-description key)))))
+
+;;;###autoload (define-key ctl-x-4-map "p" 'project-other-window-command)
+
 (provide 'project)
 ;;; project.el ends here
-- 
2.27.0