diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b45450c6..3ec6355b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ * [#1870](https://github.com/bbatsov/projectile/pull/1870): Add package command for CMake projects. * [#1875](https://github.com/bbatsov/projectile/pull/1875): Add support for Sapling VCS. * [#1876](https://github.com/bbatsov/projectile/pull/1876): Add support for Jujutsu VCS. +* [#1877](https://github.com/bbatsov/projectile/pull/1877): Add custom variable `projectile-cmd-hist-ignoredups`. ## 2.8.0 (2023-10-13) diff --git a/doc/modules/ROOT/pages/projects.adoc b/doc/modules/ROOT/pages/projects.adoc index 9400dd7b4..e974560c4 100644 --- a/doc/modules/ROOT/pages/projects.adoc +++ b/doc/modules/ROOT/pages/projects.adoc @@ -838,3 +838,9 @@ external command or an Emacs Lisp function: In addition caching of commands can be disabled by setting the variable `projectile-project-enable-cmd-caching` is to `nil`. This is useful for preset-based CMake projects. + +By default, Projectile will not add consecutive duplicate commands to its +command history. To alter this behaviour you can use `projectile-cmd-hist-ignoredups`. + The default value of `t` means consecutive duplicates are ignore, a value +of `nil` means nothing is ignored, and a value of `'erase'` means only +the last duplicate is kept in the command history. diff --git a/projectile.el b/projectile.el index 886fdf202..b0ecdf6fc 100644 --- a/projectile.el +++ b/projectile.el @@ -874,6 +874,17 @@ If the value is nil, there is no limit to the opend buffers count." :type 'integer :package-version '(projectile . "2.2.0")) +(defcustom projectile-cmd-hist-ignoredups t + "Controls when inputs are added to projectile's command history. + +A value of t means consecutive duplicates are ignored. +A value of `erase' means only the last duplicate is kept. +A value of nil means nothing is ignored." + :type '(choice (const :tag "Don't ignore anything" nil) + (const :tag "Ignore consecutive duplicates" t) + (const :tag "Only keep last duplicate" erase)) + :package-version '(projectile . "2.9.0")) + (defvar projectile-project-test-suffix nil "Use this variable to override the current project's test-suffix property. It takes precedence over the test-suffix for the project type when set. @@ -5227,8 +5238,17 @@ The command actually run is returned." (when command-map (puthash default-directory command command-map) (let ((hist (projectile--get-command-history project-root))) - (unless (string= (car-safe (ring-elements hist)) command) - (ring-insert hist command)))) + (cond + ((eq projectile-cmd-hist-ignoredups t) + (unless (string= (car-safe (ring-elements hist)) command) + (ring-insert hist command))) + ((eq projectile-cmd-hist-ignoredups 'erase) + (let ((idx (ring-member hist command))) + (while idx + (ring-remove hist idx) + (setq idx (ring-member hist command)))) + (ring-insert hist command)) + (t (ring-insert hist command))))) (when save-buffers (save-some-buffers (not compilation-ask-about-save) (lambda () diff --git a/test/projectile-test.el b/test/projectile-test.el index 24ad73f3e..104bebf1b 100644 --- a/test/projectile-test.el +++ b/test/projectile-test.el @@ -2138,25 +2138,43 @@ projectile-process-current-project-buffers-current to have similar behaviour" (expect 'async-shell-command :to-have-been-called-with "cmd" nil nil)))) (describe "projectile--run-project-cmd" - (it "command history is not duplicated" + + (before-each (spy-on 'projectile-run-compilation) (spy-on 'projectile-maybe-read-command :and-call-fake (lambda (arg default-cmd prompt) default-cmd)) ;; Stops projectile--run-project-cmd from creating a new directory for ;; the compilation dir - (spy-on 'file-directory-p :and-return-value t) + (spy-on 'file-directory-p :and-return-value t)) + + (it "projectile-cmd-hist-ignoredups set to t" + (let ((command-map (make-hash-table :test 'equal)) + (projectile-cmd-hist-ignoredups t) ;; history is based on the project root, so we set it to a random ;; path to ensure there are no existing commands in history (projectile-project-root "/a/random/path")) (projectile--run-project-cmd "foo" command-map) (projectile--run-project-cmd "foo" command-map) + (projectile--run-project-cmd "bar" command-map) + (projectile--run-project-cmd "foo" command-map) + (expect 'projectile-run-compilation :to-have-been-called-times 4) + (expect (ring-elements + (projectile--get-command-history projectile-project-root)) + :to-equal '("foo" "bar" "foo")))) + + (it "projectile-cmd-hist-ignoredups set to erase" + (let ((command-map (make-hash-table :test 'equal)) + (projectile-cmd-hist-ignoredups 'erase) + (projectile-project-root "/a/random/path")) (projectile--run-project-cmd "foo" command-map) (projectile--run-project-cmd "bar" command-map) + (projectile--run-project-cmd "foo" command-map) + (projectile--run-project-cmd "foo" command-map) (expect 'projectile-run-compilation :to-have-been-called-times 4) (expect (ring-elements (projectile--get-command-history projectile-project-root)) - :to-equal '("bar" "foo"))))) + :to-equal '("foo" "bar"))))) (describe "projectile-test-prefix" :var ((mock-projectile-project-types