From c90d74ce2fd5ebbdd2145c991ead19755d0cfb37 Mon Sep 17 00:00:00 2001 From: =?utf8?q?El=C3=ADas=20Gabriel=20P=C3=A9rez?= Date: Tue, 8 Apr 2025 22:30:28 -0600 Subject: [PATCH] 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. (cherry picked from commit 6b078013f19e9e2ee4e7ac8bf5d22fb0b8989035) --- lisp/progmodes/project.el | 35 +++++++++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/lisp/progmodes/project.el b/lisp/progmodes/project.el index 61694c3ec6a..2d57579513d 100644 --- a/lisp/progmodes/project.el +++ b/lisp/progmodes/project.el @@ -1476,6 +1476,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)) @@ -1839,7 +1857,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'." @@ -1939,6 +1961,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 @@ -2012,6 +2037,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 @@ -2140,7 +2168,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-read-ancestor-directory (prompt) -- 2.39.5