diff --git a/lisp/progmodes/project.el b/lisp/progmodes/project.el index 57d9d8e99ab..9186af663cb 100644 --- a/lisp/progmodes/project.el +++ b/lisp/progmodes/project.el @@ -855,6 +855,7 @@ project-prefix-map (define-key map "G" 'project-or-external-find-regexp) (define-key map "r" 'project-query-replace-regexp) (define-key map "x" 'project-execute-extended-command) + (define-key map "o" 'project-any-command) (define-key map "\C-b" 'project-list-buffers) map) "Keymap for project commands.") @@ -1817,6 +1818,46 @@ project-execute-extended-command (let ((default-directory (project-root (project-current t)))) (call-interactively #'execute-extended-command))) +;;;###autoload +(defun project-any-command (&optional overriding-map prompt-format) + "Run the next command in the current project. +If the command is in `project-prefix-map', it gets passed that +info with `project-current-directory-override'. Otherwise, +`default-directory' is temporarily set to the current project's +root. + +If OVERRIDING-MAP is non-nil, it will be used as +`overriding-local-map' to provide shorter bindings from that map +which will take priority over the global ones." + (interactive) + (let* ((pr (project-current t)) + (prompt-format (or prompt-format "[execute in %s]:")) + (command (let ((overriding-local-map overriding-map)) + (key-binding (read-key-sequence + (format prompt-format (project-root pr))) + t))) + (root (project-root pr)) + found) + (when command + ;; We could also check the command name against "\\`project-", + ;; and/or (get command 'project-command). + (map-keymap + (lambda (_evt cmd) (if (eq cmd command) (setq found t))) + project-prefix-map) + (if found + (let ((project-current-directory-override root)) + (call-interactively command)) + (let ((default-directory root)) + (call-interactively command)))))) + +;;;###autoload +(defun project-prefix-or-any-command () + "Run the next command in the current project. +Works like `project-any-command', but also mixes in the shorter +bindings from `project-prefix-map'." + (interactive) + (project-any-command project-prefix-map "[execute in %s]:")) + (defun project-remember-projects-under (dir &optional recursive) "Index all projects below a directory DIR. If RECURSIVE is non-nil, recurse into all subdirectories to find @@ -1895,7 +1936,8 @@ project-switch-commands (project-find-regexp "Find regexp") (project-find-dir "Find directory") (project-vc-dir "VC-Dir") - (project-eshell "Eshell")) + (project-eshell "Eshell") + (project-any-command "Other")) "Alist mapping commands to descriptions. Used by `project-switch-project' to construct a dispatch menu of commands available upon \"switching\" to another project. @@ -1907,7 +1949,17 @@ project-switch-commands key is looked up in that map. The value can also be a symbol, the name of the command to be -invoked immediately without any dispatch menu." +invoked immediately without any dispatch menu. + +Or it can be one of two special symbols: + +`short-keys' means the dispatch menu will feature all the +one-character bindings from `project-prefix-map'. The prompt +will loop until one of those characters is pressed. + +`short-or-any' means that the dispatch will accept either a +short key from the above map, or any global binding not shadowed +by the above keys." :version "28.1" :group 'project :package-version '(project . "0.6.0") @@ -1919,7 +1971,9 @@ project-switch-commands (choice :tag "Key to press" (const :tag "Infer from the keymap" nil) (character :tag "Explicit key")))) - (symbol :tag "Single command"))) + (symbol :tag "Single command") + (const :tag "Use short keys from `project-prefix-map'" short-keys) + (const :tag "Use short keys and global bindings" short-or-any))) (defcustom project-switch-use-entire-map nil "Whether `project-switch-project' will use the entire `project-prefix-map'. @@ -1932,6 +1986,13 @@ project-switch-use-entire-map :group 'project :version "28.1") +(make-obsolete-variable + 'project-switch-use-entire-map + (format-message + "set `project-switch-commands' to the value `short-keys' instead.") + "30.1" + 'set) + (defcustom project-key-prompt-style (if (facep 'help-key-binding) t 'brackets) @@ -1984,7 +2045,8 @@ project--switch-project-command ;; XXX: Add a warning about it? (reverse row) row)) - project-switch-commands)) + (and (listp project-switch-commands) + project-switch-commands))) (commands-map (let ((temp-map (make-sparse-keymap))) (set-keymap-parent temp-map project-prefix-map) @@ -1992,11 +2054,13 @@ project--switch-project-command (when-let ((cmd (nth 0 row)) (keychar (nth 2 row))) (define-key temp-map (vector keychar) cmd))))) + (use-all-short-keys (or project-switch-use-entire-map + (eq project-switch-commands 'short-keys))) command choice) (while (not command) (let* ((overriding-local-map commands-map) - (prompt (if project-switch-use-entire-map + (prompt (if use-all-short-keys (project--keymap-prompt) (project--menu-prompt)))) (when choice @@ -2008,7 +2072,7 @@ project--switch-project-command (setq choice (read-key-sequence (concat "Choose: " prompt))) (when (setq command (lookup-key commands-map choice)) (when (numberp command) (setq command nil)) - (unless (or project-switch-use-entire-map + (unless (or use-all-short-keys (assq command commands-menu)) (setq command nil))) (let ((global-command (lookup-key (current-global-map) choice))) @@ -2027,9 +2091,14 @@ project-switch-project When called in a program, it will use the project corresponding to directory DIR." (interactive (list (funcall project-prompter))) - (let ((command (if (symbolp project-switch-commands) - project-switch-commands - (project--switch-project-command))) + (let ((command + (cond + ((eq project-switch-commands 'short-or-any) + #'project-prefix-or-any-command) + ((and (symbolp project-switch-commands) + (not (eq project-switch-commands 'short-keys))) + project-switch-commands) + (t (project--switch-project-command)))) (buffer (current-buffer))) (unwind-protect (progn