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 @@ -26,6 +26,7 @@

### Bugs fixed

- [#2172](https://github.com/bbatsov/projectile/pull/2172): Fix subproject detection in a Go workspace. A `go.work` names its modules as `./api`, and the leading `./` survived into the subproject name, which then matched none of the project's file paths - so `projectile-find-file-in-subproject` offered nothing at all in a Go monorepo. Members are now spelled relative to the project root whatever the manifest wrote.
- [#2171](https://github.com/bbatsov/projectile/pull/2171): The commands that list the known projects now load the persisted list first. They read the `projectile-known-projects` variable, which stays nil until the same-named accessor loads it from disk and which nothing loads at startup - so whichever of them you reached for first in a session offered nothing. `projectile-find-file-in-known-projects` completed over an empty list; `projectile-vc`, `projectile-remove-known-project` and the "Switch to project" prompt of `projectile-require-project-root` were affected too.
- [#2170](https://github.com/bbatsov/projectile/pull/2170): Navigating a search results buffer while the scan is still running is no longer undone by the next chunk. Each redraw ended by going to the top of the buffer, so point jumped back on every batch of matches; it now stays where you left it.
- [#2167](https://github.com/bbatsov/projectile/pull/2167): The Emacs Lisp search scanner no longer decompresses or decrypts the files it reads. It went through `file-name-handler-alist`, so every archive in the candidate set was run through gzip and every `.gpg` handed to EPA - work discarded a moment later as binary, and on an encrypted file a passphrase prompt in the middle of a project search.
Expand Down
9 changes: 8 additions & 1 deletion projectile.el
Original file line number Diff line number Diff line change
Expand Up @@ -12407,7 +12407,14 @@ narrow."
;; passing it makes a glob be read as a regular expression.
(dolist (hit (file-expand-wildcards (directory-file-name pattern)))
(when (file-directory-p (expand-file-name hit root))
(push (file-name-as-directory hit) dirs)))))
;; Re-spell the hit relative to ROOT rather than keeping it as
;; written. A `go.work' says `./api', and the leading `./' would
;; otherwise survive into a subproject name that matches none of
;; the project's file paths - leaving find-file-in-subproject
;; with nothing to offer.
(push (file-name-as-directory
(file-relative-name (expand-file-name hit root) root))
dirs)))))
(sort (delete-dups dirs) #'string<)))

(defun projectile--workspace-member-patterns (root)
Expand Down
21 changes: 21 additions & 0 deletions test/projectile-commands-test.el
Original file line number Diff line number Diff line change
Expand Up @@ -857,6 +857,27 @@
(expect (projectile-subprojects-from-manifest repo)
:to-equal '("core/" "util/"))))))

(it "reads the modules a Go workspace puts to use"
(projectile-test-with-sandbox
(projectile-test-with-files
("repo/api/" "repo/worker/" "repo/scratch/")
(with-temp-file "repo/go.work"
(insert "go 1.22\n\nuse (\n\t./api\n\t// not this one\n\t./worker\n)\n"))
(let ((repo (file-truename (expand-file-name "repo/"))))
;; the comment inside the block is not a module, and `scratch' is
;; simply not listed
(expect (projectile-subprojects-from-manifest repo)
:to-equal '("api/" "worker/"))))))

(it "reads a single-line Go workspace use directive"
(projectile-test-with-sandbox
(projectile-test-with-files
("repo/only/")
(with-temp-file "repo/go.work" (insert "go 1.22\n\nuse ./only\n"))
(let ((repo (file-truename (expand-file-name "repo/"))))
(expect (projectile-subprojects-from-manifest repo)
:to-equal '("only/"))))))

(it "has no answer when the members are not declared statically"
;; Gradle builds its module list in a Kotlin program and Bazel in
;; Starlark; neither can be read without running the tool, so the scan
Expand Down
42 changes: 42 additions & 0 deletions test/projectile-dispatch-test.el
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,48 @@
;; dispatch menu is always available.
(expect (get 'projectile-dispatch 'transient--prefix) :to-be-truthy)))

(describe "projectile-dispatch-search-siblings"
;; The sibling search is not a `projectile--define-display-variants' wrapper
;; like its neighbours - it translates the switches itself - so it needs its
;; own coverage.
:var (captured)
(before-each
(setq captured nil)
(spy-on 'projectile-dispatch--args :and-return-value nil)
(spy-on 'projectile-search-in-sibling-projects :and-call-fake
(lambda (&optional regexp)
(setq captured (list :regexp regexp
:case-fold case-fold-search
:word projectile-search-whole-word)))))

(it "searches for a literal term with no switches active"
(projectile-dispatch-search-siblings)
(expect (plist-get captured :regexp) :to-be nil))

(it "reads the term as a regexp when --regexp is active"
(spy-on 'projectile-dispatch--args :and-return-value '("--regexp"))
(projectile-dispatch-search-siblings)
(expect (plist-get captured :regexp) :to-be t))

(it "seeds the search case-sensitive when --case-sensitive is active"
(spy-on 'projectile-dispatch--args :and-return-value '("--case-sensitive"))
(let ((case-fold-search t))
(projectile-dispatch-search-siblings))
(expect (plist-get captured :case-fold) :to-be nil))

(it "seeds whole-word matching when --word is active"
(spy-on 'projectile-dispatch--args :and-return-value '("--word"))
(let ((projectile-search-whole-word nil))
(projectile-dispatch-search-siblings))
(expect (plist-get captured :word) :to-be t))

(it "leaves the search settings alone when the switches are off"
(let ((case-fold-search t)
(projectile-search-whole-word nil))
(projectile-dispatch-search-siblings))
(expect (plist-get captured :case-fold) :to-be t)
(expect (plist-get captured :word) :to-be nil)))

(provide 'projectile-dispatch-test)

;;; projectile-dispatch-test.el ends here
40 changes: 40 additions & 0 deletions test/projectile-project-group-test.el
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,46 @@ the two truename'd roots and `parent' to the directory holding them."
:to-equal '("src/todo.txt")))))


;;; The sibling wrappers themselves

(describe "projectile-switch-to-buffer-in-sibling-projects"
(it "offers the buffers of the whole family"
(projectile-group-test--with-projects
(let ((ba (find-file-noselect (expand-file-name "src/a.txt" alpha)))
(bb (find-file-noselect (expand-file-name "lib/b.txt" beta))))
(ignore ba)
(spy-on 'projectile-sibling-projects :and-return-value (list alpha beta))
(spy-on 'projectile-completing-read :and-return-value (buffer-name bb))
(spy-on 'switch-to-buffer)
(projectile-switch-to-buffer-in-sibling-projects)
(let ((offered (cadr (spy-calls-args-for 'projectile-completing-read 0))))
(expect offered :to-contain "a.txt")
(expect offered :to-contain "b.txt"))
(expect 'switch-to-buffer :to-have-been-called-with (buffer-name bb)))))

(it "says so when the project has no siblings"
(projectile-group-test--with-projects
(spy-on 'projectile-sibling-projects :and-return-value nil)
(spy-on 'projectile-acquire-root :and-return-value alpha)
(expect (projectile-switch-to-buffer-in-sibling-projects)
:to-throw 'user-error))))

(describe "projectile-multi-occur-in-sibling-projects"
(it "runs multi-occur over the family's buffers, not over files on disk"
(projectile-group-test--with-projects
(let ((ba (find-file-noselect (expand-file-name "src/a.txt" alpha)))
(bb (find-file-noselect (expand-file-name "lib/b.txt" beta))))
(spy-on 'projectile-sibling-projects :and-return-value (list alpha beta))
(spy-on 'occur-read-primary-args :and-return-value '("needle"))
(spy-on 'multi-occur)
(projectile-multi-occur-in-sibling-projects 3)
(let ((args (spy-calls-args-for 'multi-occur 0)))
(expect (nth 0 args) :to-contain ba)
(expect (nth 0 args) :to-contain bb)
(expect (nth 1 args) :to-equal "needle")
;; the prefix argument is passed through as the context line count
(expect (nth 2 args) :to-equal 3))))))

;;; Ripgrep across a group

(defun projectile-group-test--seed-rg (buf root projects term)
Expand Down
Loading