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
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
32 changes: 26 additions & 6 deletions helm-projectile.el
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down Expand Up @@ -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))

Expand Down Expand Up @@ -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
Expand Down
42 changes: 36 additions & 6 deletions test/helm-projectile-test.el
Original file line number Diff line number Diff line change
Expand Up @@ -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")))
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down
Loading