From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Juri Linkov Newsgroups: gmane.emacs.bugs Subject: bug#63648: 29.0.90; project.el: with switch-use-entire-map, switch-project errors on non-project commands Date: Thu, 21 Sep 2023 09:58:57 +0300 Organization: LINKOV.NET Message-ID: <86msxgatuy.fsf@mail.linkov.net> References: <86y1hs4kkg.fsf@mail.linkov.net> <86h6of66o3.fsf@mail.linkov.net> <86wmxb2qvh.fsf@mail.linkov.net> <8634zyjt0k.fsf@mail.linkov.net> <8d1fb7ac-5c82-0ec2-8ae2-d09c131ec165@gutov.dev> <86edj6hyem.fsf@mail.linkov.net> <8634zitwoy.fsf@mail.linkov.net> <50d46d30-a796-b855-0d4c-690d6cb3d15b@gutov.dev> <86il88x9cy.fsf@mail.linkov.net> <4367c45c-95b3-6a29-4ba3-068a3c748452@gutov.dev> <2e34e515-a921-a969-0915-bea94c745f8b@gutov.dev> <868r9258oi.fsf@mail.linkov.net> <86edishisp.fsf@mail.linkov.net> <6fc81cbf-a21f-c5b4-aa56-e8518b8570d7@gutov.dev> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="16696"; mail-complaints-to="usenet@ciao.gmane.io" User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/30.0.50 (x86_64-pc-linux-gnu) Cc: Spencer Baugh , 63648@debbugs.gnu.org, sbaugh@catern.com To: Dmitry Gutov Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Thu Sep 21 09:18:05 2023 Return-path: Envelope-to: geb-bug-gnu-emacs@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1qjDwq-0004C8-Kj for geb-bug-gnu-emacs@m.gmane-mx.org; Thu, 21 Sep 2023 09:18:04 +0200 Original-Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1qjDwg-0007EW-2C; Thu, 21 Sep 2023 03:17:54 -0400 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1qjDwe-0007DM-Rj for bug-gnu-emacs@gnu.org; Thu, 21 Sep 2023 03:17:52 -0400 Original-Received: from debbugs.gnu.org ([2001:470:142:5::43]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1qjDwe-0002Om-Il for bug-gnu-emacs@gnu.org; Thu, 21 Sep 2023 03:17:52 -0400 Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1qjDwo-0006EY-59 for bug-gnu-emacs@gnu.org; Thu, 21 Sep 2023 03:18:02 -0400 X-Loop: help-debbugs@gnu.org Resent-From: Juri Linkov Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Thu, 21 Sep 2023 07:18:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 63648 X-GNU-PR-Package: emacs Original-Received: via spool by 63648-submit@debbugs.gnu.org id=B63648.169528063623909 (code B ref 63648); Thu, 21 Sep 2023 07:18:02 +0000 Original-Received: (at 63648) by debbugs.gnu.org; 21 Sep 2023 07:17:16 +0000 Original-Received: from localhost ([127.0.0.1]:60922 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1qjDw3-0006DY-CB for submit@debbugs.gnu.org; Thu, 21 Sep 2023 03:17:16 -0400 Original-Received: from relay3-d.mail.gandi.net ([217.70.183.195]:45045) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1qjDw0-0006DI-JA for 63648@debbugs.gnu.org; Thu, 21 Sep 2023 03:17:14 -0400 Original-Received: by mail.gandi.net (Postfix) with ESMTPSA id 5690660004; Thu, 21 Sep 2023 07:16:53 +0000 (UTC) In-Reply-To: <6fc81cbf-a21f-c5b4-aa56-e8518b8570d7@gutov.dev> (Dmitry Gutov's message of "Thu, 21 Sep 2023 04:16:32 +0300") X-GND-Sasl: juri@linkov.net X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-BeenThere: bug-gnu-emacs@gnu.org List-Id: "Bug reports for GNU Emacs, the Swiss army knife of text editors" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Original-Sender: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Xref: news.gmane.io gmane.emacs.bugs:270977 Archived-At: --=-=-= Content-Type: text/plain >>>>> E.g. would people who customized project-switch-commands to a symbol be >>>>> okay with not being able to use the other functionality of >>>>> other-project-prefix? >>>> Symbolic project-switch-commands is supported by other-project-prefix. >>> >>> Yes, and that could be a problem: if I as a user want 'C-x p p' to always >>> run 'project-find-file', for example (in the other project), but I also >>> want to be able to sometimes call 'M-x other-project-prefix' (or use >>> a custom binding) to run an arbitrary command in the other project. >> With other-project-prefix, users can run 'project-find-file' >> in another project with just: >> ``` >> (defun other-project-find-file () >> (interactive) >> (call-interactively 'other-project-prefix) >> (call-interactively 'project-find-file)) >> ``` > > Right... but they'd have to define a custom function for any such > command. I think the idea behind 'other-project-prefix' is that one > wouldn't have to. Only one custom function. Or they can just bind the most used command to the most easily typed key RET. >>>>> Or what about the variable project-switch-use-entire-map? I'm not sure >>>>> about dropping it, at least without notifying about that in the UI somehow. >>>> project-switch-use-entire-map could be handled in other-project-prefix. >>>> But is this really needed? I can't imagine why anyone might want >>>> to disable keys from project-prefix-map. >>> >>> I think if project-switch-use-entire-map is still wanted, we should just >>> keep these commands separate (the same logic as in the previous paragraph). >> No problem, project-switch-use-entire-map can be easily supported in >> other-project-prefix. > > I'm not saying it's hard to support it there. But it could make the command > less useful. When all global keybindings are available, it's strange to remove keys from project-prefix-map. But ok, the patch below supports project-switch-use-entire-map. >>> Why not remove that option? Consider the current UI (which looks almost the >>> same in your latest patch): user types 'C-x p p', chooses the project and >>> then sees the prompt which only offers the options from >>> project-switch-commands. They are not informed that any other keys could be >>> used too. So if we were to change the default behavior, I think this >>> UI/prompt needs to change, at the very least. >> Agreed, a better prompt needed. Maybe with the standard C-h key for >> help. > > Some short version initially, and perhaps a more expanded if the user hits > 'C-h'. I tried to use 'help-form' in a new patch, but actually 'C-h' is quite useless when not using the ad-hoc loop as e.g. in y-or-n-p that forces to type a limited set of keys. >>>>> Finally, the current patch drops the loop, so if the user types the wrong >>>>> key, they will need to repeat the command and choose the project again. >>>> Indeed, the loop now is the command loop, and maybe it's possible to >>>> write a hack to set a catch-all in set-transient-map that is not >>>> finished until a key in the map is typed. But as with any command, >>>> if the user types a wrong key sequence, then need to type it again. >>>> I do this all the time with mistakenly typed keys. I don't see why >>>> project keys are the exception. >>> >>> Maybe because in the middle of it all you have interacted with Emacs to >>> choose the other project, and that can be a significant amount of typing? >> Then for repeating the previously selected project it will be in the >> history: >> 'C-x p p M-p'. > > Fair point. Still, that's more typing and required one to remember/know > about M-p. If 'project-prompt-project-dir' remembered the selected directory like 'project-current' does, then would be possible to repeat with: 'C-x p p M-n'. >>>> A different key binding for the commands that do the same thing? >>>> Only because the new command uses the command loop? >>> >>> project-switch-project would continue to show the menu with commands, only >>> work with them by default, and obey project-switch-use-entire-map, which, >>> when set to t, would enable other commands from the project keymap, but >>> only them. All accessible via one character binding. And when >>> project-switch-commands is set to a symbol, it would only invoke that one >>> command without additional prompting. >>> >>> other-project-prefix would accept all commands but require full key >>> sequence for the invoked command. >> And also other-project-prefix will fix such bugs as bug#65558. >> >>> Would it add shortcuts to the commands from the project keymap? >>> Possibly. Any ideas how to inform the user about that? >> By a different prompt? > > Probably. Would you like to propose one? So that I have something to > compare to, and have something specific to put to the vote as well. Ok, something like this: --=-=-= Content-Type: text/x-diff Content-Disposition: inline; filename=project-default-directory.patch diff --git a/lisp/progmodes/project.el b/lisp/progmodes/project.el index 2e6ae89a443..6d5664c0e98 100644 --- a/lisp/progmodes/project.el +++ b/lisp/progmodes/project.el @@ -197,6 +197,14 @@ project-current-directory-override "Value to use instead of `default-directory' when detecting the project. When it is non-nil, `project-current' will always skip prompting too.") +(defvar-local project-default-directory nil + "Value to use instead of `default-directory' when detecting the project. +For the next command after switching the project, this buffer-local +variable contains the original value of `default-directory'. +Whereas the buffer-local `default-directory' is temporarily set +to the root directory of the switched project. +When it is non-nil, `project-current' will always skip prompting too.") + (defcustom project-prompter #'project-prompt-project-dir "Function to call to prompt for a project. Called with no arguments and should return a project root dir." @@ -232,7 +240,8 @@ project-current (let ((pr (project--find-in-directory directory))) (cond (pr) - ((unless project-current-directory-override + ((unless (or project-current-directory-override + project-default-directory) maybe-prompt) (setq directory (funcall project-prompter) pr (project--find-in-directory directory)))) @@ -397,8 +406,10 @@ project-buffers (let ((root (expand-file-name (file-name-as-directory (project-root project)))) bufs) (dolist (buf (buffer-list)) - (when (string-prefix-p root (expand-file-name - (buffer-local-value 'default-directory buf))) + (when (string-prefix-p + root (expand-file-name + (or (buffer-local-value 'project-default-directory buf) + (buffer-local-value 'default-directory buf)))) (push buf bufs))) (nreverse bufs))) @@ -813,7 +824,9 @@ project-buffers dd bufs) (dolist (buf (buffer-list)) - (setq dd (expand-file-name (buffer-local-value 'default-directory buf))) + (setq dd (expand-file-name + (or (buffer-local-value 'project-default-directory buf) + (buffer-local-value 'default-directory buf)))) (when (and (string-prefix-p root dd) (not (cl-find-if (lambda (module) (string-prefix-p module dd)) modules))) @@ -842,7 +855,9 @@ project-prefix-map (define-key map "c" 'project-compile) (define-key map "e" 'project-eshell) (define-key map "k" 'project-kill-buffers) - (define-key map "p" 'project-switch-project) + (define-key map "p" (if (< emacs-major-version 30) + 'project-switch-project + 'other-project-prefix)) (define-key map "g" 'project-find-regexp) (define-key map "G" 'project-or-external-find-regexp) (define-key map "r" 'project-query-replace-regexp) @@ -889,10 +904,16 @@ project-other-window-command \\{project-prefix-map} \\{project-other-window-map}" (interactive) - (project--other-place-command '((display-buffer-pop-up-window) - (inhibit-same-window . t)) - project-other-window-map)) + (if (< emacs-major-version 30) + (project--other-place-command '((display-buffer-pop-up-window) + (inhibit-same-window . t)) + project-other-window-map) + (let ((inhibit-message t)) (other-window-prefix)) + (message "Display next project command buffer in a new window...") + (set-transient-map (make-composed-keymap project-prefix-map + project-other-window-map)))) +;; TODO: maybe rename to project-other-window-prefix ;;;###autoload (define-key ctl-x-4-map "p" #'project-other-window-command) ;;;###autoload @@ -904,8 +925,13 @@ project-other-frame-command \\{project-prefix-map} \\{project-other-frame-map}" (interactive) - (project--other-place-command '((display-buffer-pop-up-frame)) - project-other-frame-map)) + (if (< emacs-major-version 30) + (project--other-place-command '((display-buffer-pop-up-frame)) + project-other-frame-map) + (let ((inhibit-message t)) (other-frame-prefix)) + (message "Display next project command buffer in a new frame...") + (set-transient-map (make-composed-keymap project-prefix-map + project-other-frame-map)))) ;;;###autoload (define-key ctl-x-5-map "p" #'project-other-frame-command) @@ -917,7 +943,11 @@ project-other-tab-command \\{project-prefix-map}" (interactive) - (project--other-place-command '((display-buffer-in-new-tab)))) + (if (< emacs-major-version 30) + (project--other-place-command '((display-buffer-in-new-tab))) + (let ((inhibit-message t)) (other-tab-prefix)) + (message "Display next project command buffer in a new tab...") + (set-transient-map project-prefix-map))) ;;;###autoload (when (bound-and-true-p tab-prefix-map) @@ -1000,14 +1030,16 @@ project--find-default-from "Ensure FILENAME is in PROJECT. Usually, just return FILENAME. But if -`project-current-directory-override' is set, adjust it to be +`project-default-directory' is set, adjust it to be relative to PROJECT instead. This supports using a relative file name from the current buffer when switching projects with `project-switch-project' and then using a command like `project-find-file'." - (if-let (filename-proj (and project-current-directory-override - (project-current nil default-directory))) + (if-let (filename-proj (or (and project-current-directory-override + (project-current nil default-directory)) + (and project-default-directory + (project-current nil project-default-directory)))) ;; file-name-concat requires Emacs 28+ (concat (file-name-as-directory (project-root project)) (file-relative-name filename (project-root filename-proj))) @@ -1993,6 +2025,60 @@ project-switch-project (let ((project-current-directory-override dir)) (call-interactively command)))) +;;;###autoload +(defun other-project-prefix (dir) + "\"Switch\" to another project before running an Emacs command. +The available commands are presented as a dispatch menu +made from `project-switch-commands'. + +When called in a program, it will use the project corresponding +to directory DIR." + (interactive (list (funcall project-prompter))) + (if (symbolp project-switch-commands) + (let* ((project-default-directory default-directory) + (default-directory dir)) + (call-interactively project-switch-commands)) + (prefix-command-preserve-state) + (letrec ((minibuffer-depth (minibuffer-depth)) + (command this-command) + (old-buffer (current-buffer)) + (echofun (lambda () "[switch-project]")) + (postfun + (lambda () + (unless (or (eq this-command command) + (> (minibuffer-depth) minibuffer-depth)) + (remove-hook 'post-command-hook postfun) + (remove-hook 'prefix-command-echo-keystrokes-functions + echofun) + (when (buffer-live-p old-buffer) + (with-current-buffer old-buffer + (when project-default-directory + (setq-local default-directory project-default-directory) + (kill-local-variable 'project-default-directory)))))))) + (add-hook 'post-command-hook postfun) + (add-hook 'prefix-command-echo-keystrokes-functions echofun) + (setq-local project-default-directory default-directory) + (setq-local default-directory dir) + (message (concat (project--keymap-prompt) " or any global key.")) + (let ((map (make-sparse-keymap))) + (if project-switch-use-entire-map + (set-keymap-parent map project-prefix-map) + (dolist (row project-switch-commands map) + (when-let* ((cmd (nth 0 row)) + (key (nth 2 row)) + (keychar (if key (vector key) + (where-is-internal + cmd (list project-prefix-map) t)))) + (define-key map keychar cmd)))) + (define-key map (vector help-char) + (lambda () + (interactive) + (let ((help-form "\ +You can use any global keybinding.")) + (help-form-show)))) + ;; Should return exitfun from set-transient-map + (set-transient-map map))))) + ;;;###autoload (defun project-uniquify-dirname-transform (dirname) "Uniquify name of directory DIRNAME using `project-name', if in a project. --=-=-=--