diff --git a/CHANGELOG.md b/CHANGELOG.md index 95e8668..8c952b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ ## master (unreleased) +### Bugs fixed + +* [#221](https://github.com/bbatsov/helm-projectile/issues/221): Build the search ignores from Projectile's gitignore-style patterns (`projectile-filtering-patterns`), instead of the `projectile-ignored-files-rel`/`projectile-ignored-directories-rel`/`projectile-patterns-to-ignore` path lists that Projectile 3.3 removed. + +### Changes + +* Require Projectile 3.3.0, which is where the ignore rules became a single list of patterns. + ## 1.7.0 (2026-07-08) ### New features diff --git a/helm-projectile.el b/helm-projectile.el index fcb303a..82dd1a7 100644 --- a/helm-projectile.el +++ b/helm-projectile.el @@ -8,7 +8,7 @@ ;; Created: 2011-31-07 ;; Keywords: project, convenience ;; Version: 1.7.0 -;; Package-Requires: ((emacs "28.1") (helm "3.0") (projectile "3.1.0")) +;; Package-Requires: ((emacs "28.1") (helm "3.0") (projectile "3.3.0")) ;; This file is NOT part of GNU Emacs. @@ -1448,18 +1448,40 @@ When set to `search-tool', the above does not happen." "True if the ignore strategy is `projectile'." (eq 'projectile helm-projectile-ignore-strategy)) +(defun helm-projectile--ignore-glob (pattern) + "Turn the gitignore-style PATTERN into a plain glob. +Projectile anchors a rule to the project root with a leading `/' and +floats one explicitly with a leading `**/'. The search tools take plain +globs with no notion of anchoring, so those markers - and the trailing +`/' of a directory-only rule - come off." + (string-remove-prefix + "/" (string-remove-prefix "**/" (string-remove-suffix "/" pattern)))) + +(defun helm-projectile--ignore-globs (directories) + "Return the project's ignore rules as plain globs. +With DIRECTORIES non-nil return the directory-only rules, otherwise all +the others. The rules come from `projectile-filtering-patterns', the +same list Projectile's own indexing and search commands work from." + (cl-remove-if + #'string-empty-p + (mapcar #'helm-projectile--ignore-glob + ;; A trailing slash is how Projectile spells a directory-only rule. + (cl-remove-if (lambda (pattern) + (xor directories (string-suffix-p "/" pattern))) + (car (projectile-filtering-patterns)))))) + (defun helm-projectile--ignored-files () "Compute ignored files." ;; `grep' provides `grep-find-ignored-files'; load it on demand rather than ;; at startup, since it is only needed when a search computes ignores. (require 'grep) - (cl-union (projectile-ignored-files-rel) grep-find-ignored-files + (cl-union (helm-projectile--ignore-globs nil) grep-find-ignored-files :test #'equal)) (defun helm-projectile--ignored-directories () "Compute ignored directories." (require 'grep) - (cl-union (mapcar #'file-name-as-directory (projectile-ignored-directories-rel)) + (cl-union (mapcar #'file-name-as-directory (helm-projectile--ignore-globs t)) (mapcar #'file-name-as-directory grep-find-ignored-directories) :test #'equal)) @@ -1637,9 +1659,7 @@ Use `helm-projectile-rg' or `helm-projectile-ag' instead." path (helm-projectile--wildcard-to-ack-match) (shell-quote-argument)))) - (append - (helm-projectile--ignored-files) - (projectile-patterns-to-ignore))) + (helm-projectile--ignored-files)) :test #'equal) " "))) (helm-ack-grep-executable (cond diff --git a/test/helm-projectile-test.el b/test/helm-projectile-test.el index 12db0ac..80d4f1f 100644 --- a/test/helm-projectile-test.el +++ b/test/helm-projectile-test.el @@ -344,10 +344,11 @@ (describe "helm-projectile ignore lists" ;; The union of Projectile's ignores with the `grep-find-ignored-*' ;; defaults feeds every search command; several past bugs lived here. + ;; Projectile hands over gitignore-style patterns: a trailing slash means + ;; directory-only, a leading `/' or `**/' is an anchoring marker. (before-each - (spy-on 'projectile-ignored-files-rel :and-return-value '("TAGS" "a.log")) - (spy-on 'projectile-ignored-directories-rel - :and-return-value '("build" "node_modules"))) + (spy-on 'projectile-filtering-patterns + :and-return-value '(("TAGS" "a.log" "build/" "node_modules/")))) (it "unions the Projectile and grep ignored files, de-duplicated" (let ((grep-find-ignored-files '("*.o" "TAGS"))) @@ -357,7 +358,37 @@ (it "unions ignored directories as directory names, de-duplicated" (let ((grep-find-ignored-directories '(".git" "build"))) (expect (sort (copy-sequence (helm-projectile--ignored-directories)) #'string<) - :to-equal '(".git/" "build/" "node_modules/"))))) + :to-equal '(".git/" "build/" "node_modules/")))) + + (it "strips the anchoring markers the search tools don't understand" + ;; A rule that is nothing but markers ("/" here) is left with no glob at + ;; all and drops out. + (spy-on 'projectile-filtering-patterns + :and-return-value '(("/TAGS" "**/*.log" "/" "/build/" "**/vendor/"))) + (let ((grep-find-ignored-files nil) + (grep-find-ignored-directories nil)) + (expect (helm-projectile--ignored-files) :to-equal '("TAGS" "*.log")) + (expect (helm-projectile--ignored-directories) + :to-equal '("build/" "vendor/"))))) + +(describe "helm-projectile ignore lists, unstubbed" + ;; Stubs can't catch API drift - the suite kept passing against a Projectile + ;; that had dropped the functions these helpers called (#221). + (it "reads what Projectile actually computes for a project" + (helm-projectile-test-with-sandbox + ;; The matching files exist on disk to pin down that the rules come back + ;; as patterns: `*.log' stays a glob instead of expanding to `a.log', + ;; which is what the removed path-list API used to hand over. + (helm-projectile-test-with-files '("build/" "a.log") + (write-region "-build/\n-*.log\n" nil ".projectile" nil 'silent) + (let ((projectile-globally-ignored-directories '(".git")) + (projectile-globally-ignored-files '("TAGS")) + (projectile-globally-ignored-file-suffixes nil) + (grep-find-ignored-files nil) + (grep-find-ignored-directories nil)) + (expect (helm-projectile--ignored-files) :to-equal '("TAGS" "*.log")) + (expect (helm-projectile--ignored-directories) + :to-equal '(".git/" "build/"))))))) (describe "helm-projectile-grep-or-ack command construction" (before-each @@ -436,8 +467,7 @@ (before-each (spy-on 'projectile-project-root :and-return-value "/proj/") (spy-on 'helm-projectile--ignored-directories :and-return-value '("build/")) - (spy-on 'helm-projectile--ignored-files :and-return-value '("TAGS")) - (spy-on 'projectile-patterns-to-ignore :and-return-value '("*.log"))) + (spy-on 'helm-projectile--ignored-files :and-return-value '("TAGS"))) (it "detects ack and builds --ignore-dir/--ignore-file arguments" (spy-on 'executable-find