projectile icon indicating copy to clipboard operation
projectile copied to clipboard

`projectile-root-top-down` searches bottom-up

Open philiprodrigues opened this issue 4 years ago • 2 comments

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)

philiprodrigues avatar Dec 01 '21 12:12 philiprodrigues

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.

ghosty141 avatar Jul 09 '22 20:07 ghosty141

It's definitely a bug, but it affects relatively few projects. We should totally address this, though.

bbatsov avatar Jul 09 '22 20:07 bbatsov