Skip to content

projectile-root-top-down searches bottom-up #1729

@philiprodrigues

Description

@philiprodrigues

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)

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions