Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
### New features

* Add `projectile-uniquify-dirname-transform`, a project-aware value for `uniquify-dirname-transform` that disambiguates same-named buffers using the project name. Mirrors `project.el`'s `project-uniquify-dirname-transform`.
* Add `projectile-dispatch`, a `transient` menu mirroring `projectile-command-map` for more discoverable command access. `transient` is an optional dependency (it requires Emacs 28+); the menu is only available when it's installed and is not bound to a key by default.
* [#2008](https://github.com/bbatsov/projectile/issues/2008): Add `projectile-remove-project-type` to unregister a project type. This is the supported way to stop Projectile auto-detecting a type; clearing its `marker-files` does not work (see the related bug fix below).
* [#1936](https://github.com/bbatsov/projectile/issues/1936): Add `projectile-discard-root-cache` command to clear `projectile-project-root-cache` without touching other Projectile caches. Useful after creating or removing a project marker, since the existing `projectile-invalidate-cache` either also drops the file list cache or prompts for a project depending on context.
* Warn once per session when `projectile-indexing-method' is `alien' but the project has a non-empty `.projectile' file, so users notice their dirconfig rules are being bypassed. Controlled by the new `projectile-warn-when-dirconfig-is-ignored' option.
Expand Down
19 changes: 19 additions & 0 deletions doc/modules/ROOT/pages/usage.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,25 @@ You can add additional commands to the commander like this:

Place such snippets after ``projectile-mode``'s init code.

== Transient Dispatch Menu

If you have the `transient` library available (it's bundled with Emacs 28+;
older Emacsen are not supported by current `transient` releases), Projectile
provides `projectile-dispatch`, a
transient menu that mirrors `projectile-command-map`. It's a more discoverable
alternative to memorizing the keybindings or using the Commander: the menu keys
match the command map (e.g. kbd:[f] to find a file, kbd:[c c] to compile,
kbd:[s g] to grep).

`transient` is an optional dependency, so `projectile-dispatch` is only defined
when it's present. It is not bound to a key by default; bind it to whatever you
like, for instance:

[source,elisp]
----
(define-key projectile-mode-map (kbd "C-c P") #'projectile-dispatch)
----

== Using Projectile with project.el

Starting with version 2.7 Projectile bundles some integration with
Expand Down
83 changes: 83 additions & 0 deletions projectile.el
Original file line number Diff line number Diff line change
Expand Up @@ -7045,6 +7045,89 @@ Magit that don't trigger `find-file-hook'."
"Keymap for Projectile commands after `projectile-keymap-prefix'.")
(fset 'projectile-command-map projectile-command-map)

;; `projectile-dispatch' is a transient menu mirroring `projectile-command-map'.
;; `transient' is an optional dependency that requires Emacs 28.1+, so it can be
;; entirely absent (including on Emacs 27, which Projectile still supports and
;; where transient cannot even be installed). The prefix is therefore defined
;; at load time only when transient is present, and via `eval' so the
;; `transient-define-prefix' macro is not needed at byte-compile time. The menu
;; keys deliberately match the `projectile-command-map' bindings.
(when (require 'transient nil t)
(eval
'(transient-define-prefix projectile-dispatch ()
"Dispatch menu for Projectile commands."
[["Find"
("f" "file" projectile-find-file)
("g" "file dwim" projectile-find-file-dwim)
("a" "other file" projectile-find-other-file)
("l" "file in dir" projectile-find-file-in-directory)
("F" "file in known projects" projectile-find-file-in-known-projects)
("d" "dir" projectile-find-dir)
("D" "dired" projectile-dired)
("e" "recentf" projectile-recentf)
("E" "edit .dir-locals" projectile-edit-dir-locals)
("T" "test file" projectile-find-test-file)
("t" "toggle impl/test" projectile-toggle-between-implementation-and-test)]
["Buffers"
("b" "switch buffer" projectile-switch-to-buffer)
("I" "ibuffer" projectile-ibuffer)
("k" "kill buffers" projectile-kill-buffers)
("S" "save buffers" projectile-save-project-buffers)]
["Search / Replace"
("ss" "ag" projectile-ag)
("sg" "grep" projectile-grep)
("sr" "ripgrep" projectile-ripgrep)
("sx" "references" projectile-find-references)
("o" "multi-occur" projectile-multi-occur)
("r" "replace" projectile-replace)
("j" "find tag" projectile-find-tag)
("R" "regenerate tags" projectile-regenerate-tags)]]
[["Project"
("p" "switch project" projectile-switch-project)
("q" "switch open project" projectile-switch-open-project)
("A" "add known project" projectile-add-known-project)
("m" "commander" projectile-commander)
("V" "browse dirty projects" projectile-browse-dirty-projects)
("v" "vc" projectile-vc)]
["Lifecycle"
("cc" "compile" projectile-compile-project)
("ct" "test" projectile-test-project)
("cr" "run" projectile-run-project)
("co" "configure" projectile-configure-project)
("ci" "install" projectile-install-project)
("cp" "package" projectile-package-project)]
["Shells / Run"
("xe" "eshell" projectile-run-eshell)
("xs" "shell" projectile-run-shell)
("xt" "term" projectile-run-term)
("xi" "ielm" projectile-run-ielm)
("xg" "gdb" projectile-run-gdb)
("xv" "vterm" projectile-run-vterm)
("xx" "eat" projectile-run-eat)
("!" "shell command" projectile-run-shell-command-in-root)
("&" "async shell command" projectile-run-async-shell-command-in-root)]
["Cache"
("i" "invalidate cache" projectile-invalidate-cache)
("z" "cache current file" projectile-cache-current-file)]]
[["Other window"
("4f" "file" projectile-find-file-other-window)
("4g" "file dwim" projectile-find-file-dwim-other-window)
("4a" "other file" projectile-find-other-file-other-window)
("4d" "dir" projectile-find-dir-other-window)
("4D" "dired" projectile-dired-other-window)
("4b" "buffer" projectile-switch-to-buffer-other-window)
("4t" "impl/test" projectile-find-implementation-or-test-other-window)
("4o" "display buffer" projectile-display-buffer)]
["Other frame"
("5f" "file" projectile-find-file-other-frame)
("5g" "file dwim" projectile-find-file-dwim-other-frame)
("5a" "other file" projectile-find-other-file-other-frame)
("5d" "dir" projectile-find-dir-other-frame)
("5D" "dired" projectile-dired-other-frame)
("5b" "buffer" projectile-switch-to-buffer-other-frame)
("5t" "impl/test" projectile-find-implementation-or-test-other-frame)]])
t))

(defvar projectile-mode-map
(let ((map (make-sparse-keymap)))
(when projectile-keymap-prefix
Expand Down
5 changes: 5 additions & 0 deletions test/projectile-test.el
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,11 @@ You'd normally combine this with `projectile-test-with-sandbox'."
;;; buttercup allowed us to specify a custom error message like this:
;;;
;;; (expect (funcall 'foo) :to-throw 'error nil "Custom error message here")
(describe "projectile-dispatch"
(it "is defined as a command when transient is available"
(assume (require 'transient nil t) "transient is not available")
(expect (commandp 'projectile-dispatch) :to-be-truthy)))

(defmacro assert-friendly-error-when-no-project (fn)
"Write a test that ensures FN throws a friendly error when called without a project."
(let ((description (concat "when calling " (symbol-name fn) " without a project")))
Expand Down