branch: elpa/projectile
commit 4c72d6767de9aef1182d72ddfc0482cc98b0b8fd
Author: Bozhidar Batsov <[email protected]>
Commit: Bozhidar Batsov <[email protected]>
Auto-populate the bottom-up root list from projectile-register-project-type
The previous commit hard-coded ~45 manifest filenames into the default
of projectile-project-root-files-bottom-up, duplicating what every
projectile-register-project-type call already declares via :project-file.
A new project type would have needed two places updated.
Instead, have projectile--build-project-plist append :project-file to
the bottom-up list too (it already appends to the top-down list), with
two skip rules: wildcards (the bottom-up search uses file-exists-p, not
glob expansion) and a new projectile-non-root-manifest-files defconst
that names the small set of manifests which legitimately recur in
non-root subdirectories (Makefile, GNUMakefile, application.yml,
manage.py, requirements.txt, gradlew).
Net result: defcustom seed shrinks back to VCS markers only, the
manifests are derived from existing registrations, and the polyglot
fix from the previous commit still applies (the regression test passes
unchanged).
---
CHANGELOG.md | 2 +-
doc/modules/ROOT/pages/projects.adoc | 26 ++++++-----
projectile.el | 87 +++++++++++++-----------------------
3 files changed, 48 insertions(+), 67 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 44d20b9178..1149c30cdb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,7 +4,7 @@
### Changes
-* `projectile-project-root-files-bottom-up` now ships with the common
per-language project manifests (`deps.edn`, `project.clj`, `Cargo.toml`,
`pyproject.toml`, `pom.xml`, `package.json`, etc.) alongside the VC markers. In
a polyglot or monorepo layout where the language manifest sits in a
subdirectory of an enclosing `.git` root, the closer subproject now wins.
Markers that legitimately appear at multiple levels of a single project
(Makefile, application.yml, requirements.txt, manage.py [...]
+* `projectile-register-project-type` now appends each type's `:project-file`
to `projectile-project-root-files-bottom-up` (in addition to the existing
top-down list), so a deeper language manifest beats an enclosing VC root in
polyglot or monorepo layouts. Filenames listed in the new
`projectile-non-root-manifest-files` defconst (Makefile, GNUMakefile,
application.yml, manage.py, requirements.txt, gradlew) and wildcard patterns
(`?*.csproj`, etc.) are skipped, since they either legitimat [...]
* `projectile-get-immediate-sub-projects` skips the `git submodule foreach`
shell-out for git projects with no `.gitmodules` file anywhere up the parent
chain. Hot path for monorepos that index the project root often.
* `projectile-discover-projects-in-directory` now uses
`directory-files-no-dot-files-regexp` to skip `.` and `..` at the C level
instead of doing the post-filter in Elisp - matches the indexing walker.
* Document the anchored vs `*`-prefixed semantics of
`projectile-globally-ignored-directories`, the `find` fallback's lack of common
directory exclusions when `fd` isn't available, and how `fd`/`git ls-files`
handle deleted-but-unstaged files differently.
diff --git a/doc/modules/ROOT/pages/projects.adoc
b/doc/modules/ROOT/pages/projects.adoc
index e617d89213..55a3e75c1e 100644
--- a/doc/modules/ROOT/pages/projects.adoc
+++ b/doc/modules/ROOT/pages/projects.adoc
@@ -757,22 +757,26 @@ precedence with respect to project detection. Let's
examine the defaults:
file/folder (e.g. `.projectile`, `.hg`, `.git`, `deps.edn`,
`Cargo.toml`, `pyproject.toml`) from the current folder
(a.k.a. `default-directory` in Emacs lingo) up the directory tree. It will
- return the first match it discovers. The default list (configurable via
- `projectile-project-root-files-bottom-up`) contains VCS markers and the
- language-specific project manifests registered via
- `projectile-register-project-type`. The bottom-most (closest to the
+ return the first match it discovers. The bottom-most (closest to the
current dir) match wins, so in a polyglot or monorepo layout where a
`.git` sits at the top and a language manifest lives in a subdirectory,
- the language subdirectory wins.
+ the language subdirectory wins. The seed list
+ (`projectile-project-root-files-bottom-up`) contains only VCS markers;
+ per-language manifests are appended automatically by
+ `projectile-register-project-type` from each type's `:project-file`
+ value, with two exclusions: filenames listed in
+ `projectile-non-root-manifest-files` (e.g. `Makefile`,
+ `application.yml`, `requirements.txt`, `manage.py`, `gradlew`), which
+ legitimately appear at multiple levels of a single project, and
+ wildcard patterns (e.g. `?*.csproj`), which the bottom-up search does
+ not expand.
* `projectile-root-top-down` is similar, but it will return the top-most
(farthest from the current directory) match. It is configurable via
- `projectile-project-root-files`, which is also populated automatically
- from `projectile-register-project-type`. It is only consulted when no
- bottom-up match was found, so today it primarily covers projects whose
- marker is a wildcard pattern (e.g. `?*.csproj`, `?*.sln`), since
- `projectile-root-bottom-up` matches via `file-exists-p` and does not
- expand globs.
+ `projectile-project-root-files`, also populated automatically from
+ `projectile-register-project-type`. It is consulted after bottom-up
+ fails, so today it primarily covers project types whose marker is a
+ wildcard pattern (e.g. `?*.csproj`, `?*.sln`).
* `projectile-root-top-down-recurring` will look for project markers that can
appear at every level of a project (e.g. `Makefile` or `.svn`) and will
return
diff --git a/projectile.el b/projectile.el
index d37f43444b..e726ec63e2 100644
--- a/projectile.el
+++ b/projectile.el
@@ -353,65 +353,33 @@ See `projectile-register-project-type'."
".pijul" ; Pijul VCS root dir
".sl" ; Sapling VCS root dir
".jj" ; Jujutsu VCS root dir
- ;; Per-language project manifests. Including these alongside VC
- ;; markers means that in a polyglot or monorepo layout (e.g. a
- ;; `.git' at the repo root with a `deps.edn' in a language
- ;; subdirectory), the inner project marker wins for tools that ask
- ;; `projectile-project-root' which subproject the buffer belongs to.
- ;; Markers that legitimately appear at multiple levels of a single
- ;; project (Makefile, application.yml, requirements.txt, manage.py,
- ;; gradlew) are intentionally left in the top-down list instead.
- "Cargo.toml" ; Rust
- "Package.swift" ; Swift
- "build.zig.zon" ; Zig
- "dune-project" ; OCaml
- "Project.toml" ; Julia
- "elm.json" ; Elm
- "pubspec.yaml" ; Dart
- "info.rkt" ; Racket
- "stack.yaml" ; Haskell
- "DESCRIPTION" ; R
- "Eldev" ; Emacs (Eldev)
- "Eask" ; Emacs (Eask)
- "Cask" ; Emacs (Cask)
- "shard.yml" ; Crystal
- "Gemfile" ; Ruby
- "deps.edn" ; Clojure CLI
- "build.boot" ; Boot
- "project.clj" ; Leiningen
- "build.mill" ; Mill
- "build.sc" ; Mill (legacy)
- "build.sbt" ; sbt
- "build.gradle" ; Gradle
- "pom.xml" ; Maven
- "pyproject.toml" ; Python (PEP 518)
- "poetry.lock" ; Python (Poetry)
- "Pipfile" ; Python (pipenv)
- "tox.ini" ; Python (tox)
- "setup.py" ; Python (setuptools)
- "angular.json" ; Angular
- "package.json" ; npm/yarn/pnpm
- "Gruntfile.js" ; Grunt
- "gulpfile.js" ; Gulp
- "Taskfile.yml" ; go-task
- "composer.json" ; PHP
- "rebar.config" ; Erlang (rebar3)
- "mix.exs" ; Elixir
- "CMakeLists.txt" ; CMake
- "WORKSPACE" ; Bazel
- "flake.nix" ; Nix flake
- "default.nix" ; Nix
- "meson.build" ; Meson
- "SConstruct" ; SCons
- "xmake.lua" ; xmake
- "debian/control" ; Debian source package
)
"A list of files considered to mark the root of a project.
-The bottommost (parentmost) match has precedence -- so a project
-manifest in a subdirectory wins over an enclosing VC root."
+The bottommost (parentmost) match has precedence.
+
+This seed list contains only VCS markers; per-language project
+manifests (e.g. `deps.edn', `Cargo.toml', `pom.xml') are appended at
+load time by `projectile-register-project-type' from each type's
+`:project-file' value, so the closer language subproject wins over an
+enclosing VC root in a polyglot or monorepo layout.
+
+Filenames listed in `projectile-non-root-manifest-files' and wildcard
+patterns are *not* auto-added here; they remain top-down only."
:group 'projectile
:type '(repeat string))
+(defconst projectile-non-root-manifest-files
+ '("Makefile"
+ "GNUMakefile"
+ "application.yml" ; Spring Boot config, found in many resource dirs
+ "manage.py" ; Django, can appear in subapps
+ "requirements.txt" ; Python, often present in multiple subdirs
+ "gradlew") ; Gradle wrapper; only the top-level one matters
+ "Manifest filenames that legitimately appear at multiple levels of a
+single project, so they must not be treated as bottom-up root markers.
+`projectile-register-project-type' skips these when auto-populating
+`projectile-project-root-files-bottom-up'.")
+
(defcustom projectile-project-root-files-top-down-recurring
'(".svn" ; Svn VCS root dir
"CVS" ; CVS VCS root dir
@@ -3655,7 +3623,16 @@ files such as test/impl/other files as below:
(list project-file))))
(dolist (project-file project-files)
(when (and project-file (not (member project-file
projectile-project-root-files)))
- (add-to-list 'projectile-project-root-files project-file)))
+ (add-to-list 'projectile-project-root-files project-file))
+ ;; Also seed the bottom-up list so a deeper project manifest beats
+ ;; an outer VC root. Skip wildcards (the bottom-up search uses
+ ;; `file-exists-p' rather than glob expansion) and filenames known
+ ;; to legitimately recur in non-root subdirectories.
+ (when (and project-file
+ (not (string-match-p "[*?]" project-file))
+ (not (member project-file projectile-non-root-manifest-files))
+ (not (member project-file
projectile-project-root-files-bottom-up)))
+ (add-to-list 'projectile-project-root-files-bottom-up project-file t)))
(when test-suffix
(plist-put project-plist 'test-suffix test-suffix))
(when test-prefix