Subject: bug#63648: 29.0.90; project.el: with switch-use-entire-map, switch-project errors on non-project commands

From: Juri Linkov <[email-masked]>
Date: Mon, 05 Jun 2023 19:31:37 +0300

>>       (prefix-command-update)
>
> I think you need to call `prefix-command-preserve-state` as well, so
> that the order of prefix commands doesn't matter (e.g. user can do `C-u
> C-x p` instead of `C-x p C-u`).

I confirm that after replacing `prefix-command-update` with
`prefix-command-preserve-state` (replacing since the latter calls the
former), now `C-u C-x p p f lisp/international/emoji-labels.el` works ok
(without C-u it can't find this non-registered file).

> [ Note: I'm still not really happy with the way prefix commands work.
>   I can't remember what problems are still lurking, but IIRC interaction
>   with minibuffer is a source of problems (e.g. prefix commands from
>   before we entered the minibuffer can affect operations within the
>   minibuffer and prefix commands from within the minibuffer can affect
>   the behavior after exiting the minibuffer).  I think last time
>   I looked at it, I concluded that maybe it should be reimplemented such
>   that the state is kept in a single object to which prefix commands can
>   add/remove properties, and the minibuffer code would automatically
>   suspend and then reinstall that state (and could emit a warning when
>   throwing away "unused state", such as when leaving a minibuffer).  ]

I tested this with such test command and there are no problems:

```
(defun test (arg1 arg2)
  (interactive
   (list (progn
           (message "%S" default-directory)
           (read-string "1: ")
           (message "%S" default-directory))
         (progn
           (message "%S" default-directory)
           (read-string "2: ")
           (message "%S" default-directory))))
  (message "%S" default-directory))
```

`C-x p p M-x test RET` prints only the new directory.

However, I found a problem with `C-x p 4 p`.  To fix it,
`project-other-window-command` should be completely rewritten to:

```
(defun project-other-window-command ()
  (interactive)
  (other-window-prefix)
  (set-transient-map (make-composed-keymap project-prefix-map
                                           project-other-window-map)))
```

plus a small fix that I don't know how to generalize:

```
diff --git a/lisp/window.el b/lisp/window.el
index ab7dd5ced12..52ba407d9c8 100644
--- a/lisp/window.el
+++ b/lisp/window.el
@@ -9099,7 +9091,8 @@ display-buffer-override-next-command
 		     (> (minibuffer-depth) minibuffer-depth)
 		     ;; But don't remove immediately after
 		     ;; adding the hook by the same command below.
-		     (eq this-command command))
+		     (eq this-command command)
+		     (memq this-command '(other-project-prefix)))
               (funcall exitfun))))
     ;; Call post-function after the next command finishes (bug#49057).
     (add-hook 'post-command-hook postfun)
```

Then everything works as before and even better.

>> If this is conceptually ok, then more customization could be ported
>> from project--switch-project-command such as project-switch-use-entire-map.
>
> Looks OK to me [ modulo the fact that I don't really understand what
> this is doing (I don't understand the `project-switch-commands` bit, nor
> do I understand why a prefix command which temporarily changes the
> `default-directory` would be called `project-switch-project`).  ]

It's possible to create another general command that will read arbitrary
directory, but this command is project-specific with `project-prompter`.

`project-switch-project` really should be renamed to `other-project-prefix`
like other similar prefix commands.  Here is the latest version without
much changes:

```
(defun other-project-prefix (dir)
  (interactive (list (funcall project-prompter)))
  (if (symbolp project-switch-commands)
      (let ((default-directory dir))
        (call-interactively project-switch-commands))
    (let* ((echofun (lambda () "[switch-project]"))
           (postfun (lambda () (remove-hook
                                'prefix-command-echo-keystrokes-functions
                                echofun))))
      (setq next-default-directory dir)
      (message (project--keymap-prompt))
      (add-hook 'prefix-command-echo-keystrokes-functions echofun)
      (prefix-command-preserve-state)
      (set-transient-map project-prefix-map nil postfun))))
```