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

Reply via email to