Subject: bug#76307: [PATCH] Add 'project-forget-projects-under-dirs', limit dir selection to relevant dirs

From: Eshel Yaron <[email-masked]>
Date: Sun, 16 Feb 2025 19:09:21 +0100

Eshel Yaron <[email-masked]> writes:

> Hi,
>
> Ship Mints <[email-masked]> writes:
>
>> I tried your code to do a side-by-side but:
>>
>> Debugger entered--Lisp error: (wrong-number-of-arguments read-directory-name 6)
>>   (read-directory-name prompt nil nil nil nil (let ((ps (mapcar #'(lambda (p) (expand-file-name ...))
>> project--list))) #'(lambda (dir) (catch 'ball (let ((tail ps)) (while tail (let ... ... ...)))))))
>
> Try with a current build from master :)
>
>> In any case, my approach produces all legitimate possibilities a user could want to clear projects under any
>> common prefix, including "/" (which means forget everything), common prefixes for remote directories, common
>> roots for multiple projects at the same level and common roots up the tree for projects that are in a deeper
>> hierarchy.  This is effectively what I think people would want, and I assume you agree.
>
> I don't: that's the flat list which I find inappropriate in this case.
> Projects may (and do) reside all over the filesystem, so producing all
> possible prefixes of one or more project roots amounts to a huge list
> with potentially many irrelevant entries.  Here I prefer completing the
> directory name incrementally, by following the filesystem hierarchy,
> with the usual Emacs file/directory name completion.
>
>> If your code produces the same and is more efficient, that's great.
>
> My suggestion basically preserves the current behavior, it just filters
> the directories you can complete at each level to only include those
> that actually lead to one or more project roots.

BTW, here's a slightly improved project-read-ancestor-directory, it uses
try-completion to check if a candidate directory is a prefix of any
project root, which seems to be faster than the Lisp implementation I
tried at first:

--8<---------------cut here---------------start------------->8---
(defun project-read-ancestor-directory (prompt)
  "Prompt with PROMPT for an ancestor directory of one or more project roots."
  (project--ensure-read-project-list)
  (read-directory-name
   prompt nil nil nil nil
   (let ((ps (mapcar (lambda (p) (expand-file-name (car p))) project--list)))
     (lambda (dir) (try-completion (expand-file-name dir) ps)))))
--8<---------------cut here---------------end--------------->8---