Allow automatically delete non-existent projects. (Bug#77566)

* etc/NEWS: Announce changes.
* lisp/progmodes/project.el (project-prune-zombie-projects): New
user option.
(project-prune-zombies-default): New function.
(project-forget-zombie-projects): Rework.
(project--ensure-read-project-list, project-prompt-project-dir)
(project-prompt-project-name): Call
'project-forget-zombie-projects' but inhibit its message.
This commit is contained in:
Elías Gabriel Pérez 2025-04-08 22:30:28 -06:00 committed by Juri Linkov
parent 189ae7b1ed
commit 6b078013f1
2 changed files with 46 additions and 2 deletions

View file

@ -1562,6 +1562,24 @@ general form of conditions."
:group 'project
:package-version '(project . "0.8.2"))
(defcustom project-prune-zombie-projects #'project-prune-zombies-default
"Remove automatically from project list all the projects that were removed.
The value can be a predicate function which takes one argument, and
should return non-nil if the project should be removed.
If set to nil, all the inaccessible projects will not be removed automatically."
:type '(choice (const :tag "Default (remove non-remote projects)"
project-prune-zombies-default)
(const :tag "Remove any project" identity)
(function :tag "Custom function")
(const :tag "Disable auto-deletion" nil))
:version "31.1"
:group 'project)
(defun project-prune-zombies-default (project)
"Default function used in `project-prune-zombie-projects'.
Return non-nil if PROJECT is not a remote project."
(not (file-remote-p project)))
(defun project--read-project-buffer ()
(let* ((pr (project-current t))
(current-buffer (current-buffer))
@ -1914,7 +1932,11 @@ With some possible metadata (to be decided).")
(defun project--ensure-read-project-list ()
"Initialize `project--list' if it isn't already initialized."
(when (eq project--list 'unset)
(project--read-project-list)))
(project--read-project-list)
(if-let* (project-prune-zombie-projects
((consp project--list))
(inhibit-message t))
(project-forget-zombie-projects))))
(defun project--write-project-list ()
"Save `project--list' in `project-list-file'."
@ -1995,6 +2017,9 @@ see `project-list-file'.
It's also possible to enter an arbitrary directory not in the list.
When PROMPT is non-nil, use it as the prompt string."
(project--ensure-read-project-list)
(if-let* (project-prune-zombie-projects
(inhibit-message t))
(project-forget-zombie-projects))
(let* ((dir-choice "... (choose a dir)")
(choices
;; XXX: Just using this for the category (for the substring
@ -2024,6 +2049,9 @@ The project is chosen among projects known from the project list,
see `project-list-file'.
It's also possible to enter an arbitrary directory not in the list.
When PROMPT is non-nil, use it as the prompt string."
(if-let* (project-prune-zombie-projects
(inhibit-message t))
(project-forget-zombie-projects))
(let* ((dir-choice "... (choose a dir)")
project--name-history
(choices
@ -2153,7 +2181,10 @@ Return the number of detected projects."
"Forget all known projects that don't exist any more."
(interactive)
(dolist (proj (project-known-project-roots))
(unless (file-exists-p proj)
(when (and (if project-prune-zombie-projects
(funcall project-prune-zombie-projects proj)
t)
(not (file-exists-p proj)))
(project-forget-project proj))))
(defun project-forget-projects-under (dir &optional recursive)