`projectile-root-top-down` searches bottom-up
Expected behavior
With the following directory structure, I expect (projectile-root-top-down "~/projectile-find/subdir") to return ~/projectile-find, based on the existence of ~/projectile-find/Makefile
~/projectile-find/
├── Makefile
├── .projectile
└── subdir
├── Makefile
└── .projectile
(The .projectile files are for the benefit of comparing to projectile-root-bottom-up, below)
Actual behavior
projectile-root-top-down appears to search in the same order as projectile-root-bottom-up.
Steps to reproduce the problem
In the scratch buffer, with the directory tree described above:
(projectile-root-top-down "~/projectile-find/subdir")
"/home/me/projectile-find/subdir/"
(projectile-root-bottom-up "~/projectile-find/subdir")
"/home/me/projectile-find/subdir/"
I think the reason is that projectile-root-top-down calls projectile-locate-dominating-file which does a bottom-up search.
I got the behaviour I wanted locally with:
(defun my/projectile-locate-dominating-file-top-down (file name)
"Look up the directory hierarchy from FILE for a directory containing NAME.
Return the highest parent directory containing a file NAME,
and return the directory. Return nil if not found.
Instead of a string, NAME can also be a predicate taking one argument
\(a directory) and returning a non-nil value if that directory is the one for
which we're looking."
(setq file (abbreviate-file-name file))
(let ((root nil)
try)
(while (not (or (equal file "/")
(null file)
(string-match locate-dominating-stop-dir-regexp file)))
(setq try (if (stringp name)
(projectile-file-exists-p (expand-file-name name file))
(funcall name file)))
(if try (setq root file))
(setq file (file-name-directory
(directory-file-name file)))
(setq try nil)
)
(and root (expand-file-name (file-name-as-directory root)))))
(defun my/projectile-root-top-down (dir &optional list)
"Identify a project root in DIR by top-down search for files in LIST.
If LIST is nil, use `projectile-project-root-files' instead.
Return the first (topmost) matched directory or nil if not found."
(my/projectile-locate-dominating-file-top-down
dir
(lambda (dir)
(cl-find-if (lambda (f) (projectile-file-exists-p (expand-file-name f dir)))
(or list projectile-project-root-files)))))
and setting projectile-project-root-files-functions to my/projectile-root-top-down. I'm not sure whether the implementation of my/projectile-locate-dominating-file-top-down is sensible, as my Emacs Lisp is rather weak, but it works for me so far.
Environment & Version information
Projectile version information
Projectile 2.3.0
Emacs version
GNU Emacs 27.2 (build 1, x86_64-conda-linux-gnu, GTK+ Version 3.24.28, cairo version 1.16.0)
Operating system
CentOS Linux release 7.9.2009 (Core)
I had this issue too, wondering why this isn't a more "important" issue. Maybe this is intended but it feels very backwards to me.
It's definitely a bug, but it affects relatively few projects. We should totally address this, though.