aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorElías Gabriel Pérez2025-07-05 13:29:24 -0600
committerJuri Linkov2025-08-21 20:22:33 +0300
commitade6608e2587452c8ea565ce3057879379ebd0b5 (patch)
tree9b35104089cffced12a85c76ac64cd62a08037a4
parent3f7c16d858e579ed03a195841ba9805fbc2899ba (diff)
downloademacs-ade6608e2587452c8ea565ce3057879379ebd0b5.tar.gz
emacs-ade6608e2587452c8ea565ce3057879379ebd0b5.zip
project: Improve pruning of zombie projects.
* etc/NEWS: Update 'project-prune-zombie-projects' entry. * lisp/progmodes/project.el (project-prune-zombie-projects): Change default value (bug#77566). (project--ensure-read-project-list, project--write-project-list) (project-prompt-project-dir, project-prompt-project-name): Rework for use 'project-prune-zombie-projects' value. (project-forget-zombie-projects): Move code... (project--delete-zombie-projects): ... to this new function.
-rw-r--r--etc/NEWS11
-rw-r--r--lisp/progmodes/project.el65
2 files changed, 49 insertions, 27 deletions
diff --git a/etc/NEWS b/etc/NEWS
index e94464b203b..99026f936b6 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -504,12 +504,13 @@ This user option controls the automatic deletion of projects from
504'project-list-file' that cannot be accessed when prompting for a 504'project-list-file' that cannot be accessed when prompting for a
505project. 505project.
506 506
507The value can be a predicate which takes one argument and should return 507The value must be an alist where each element must be in the form:
508non-nil if the project should be removed. If set to nil, all the
509inaccessible projects will not be removed automatically.
510 508
511By default this is set to 'project-prune-zombies-default' function 509 (WHEN . PREDICATE)
512which removes all non-remote projects. 510
511where WHEN specifies where the deletion will be performed, and PREDICATE
512a function which takes one argument, and must return non-nil if the
513project should be removed.
513 514
514--- 515---
515*** New command 'project-save-some-buffers' bound to 'C-x p C-x s'. 516*** New command 'project-save-some-buffers' bound to 'C-x p C-x s'.
diff --git a/lisp/progmodes/project.el b/lisp/progmodes/project.el
index efc00ac8733..8438060afa3 100644
--- a/lisp/progmodes/project.el
+++ b/lisp/progmodes/project.el
@@ -1650,16 +1650,28 @@ general form of conditions."
1650 :group 'project 1650 :group 'project
1651 :package-version '(project . "0.8.2")) 1651 :package-version '(project . "0.8.2"))
1652 1652
1653(defcustom project-prune-zombie-projects #'project-prune-zombies-default 1653(defcustom project-prune-zombie-projects
1654 "Remove automatically from project list all the projects that were removed. 1654 '((prompt . project-prune-zombies-default))
1655The value can be a predicate function which takes one argument, and 1655 "Remove automatically from project list the projects that were removed.
1656should return non-nil if the project should be removed. 1656Each element of this alist must be in the form:
1657If set to nil, all the inaccessible projects will not be removed automatically." 1657 (WHEN . PREDICATE)
1658 :type '(choice (const :tag "Default (remove non-remote projects)" 1658
1659 project-prune-zombies-default) 1659where WHEN specifies where the deletion will be performed,
1660 (const :tag "Remove any project" identity) 1660the value can be:
1661 (function :tag "Custom function") 1661
1662 (const :tag "Disable auto-deletion" nil)) 1662 `list-first-read' - delete on the first reading of the list.
1663 `list-write' - delete after saving project list to `project-list-file'.
1664 `prompt' - delete before every prompting.
1665 `interactively' - delete only when `project-forget-zombie-projects'
1666 is called interactively.
1667
1668PREDICATE must be a function which takes one argument, and should return
1669non-nil if the project must be removed."
1670 :type 'alist
1671 :options '((list-first-read function)
1672 (list-write function)
1673 (prompt function)
1674 (interactively function))
1663 :version "31.1" 1675 :version "31.1"
1664 :group 'project) 1676 :group 'project)
1665 1677
@@ -2029,10 +2041,10 @@ With some possible metadata (to be decided).")
2029 "Initialize `project--list' if it isn't already initialized." 2041 "Initialize `project--list' if it isn't already initialized."
2030 (when (eq project--list 'unset) 2042 (when (eq project--list 'unset)
2031 (project--read-project-list) 2043 (project--read-project-list)
2032 (if-let* (project-prune-zombie-projects 2044 (if-let* ((pred (alist-get 'list-first-read project-prune-zombie-projects))
2033 ((consp project--list)) 2045 ((consp project--list))
2034 (inhibit-message t)) 2046 (inhibit-message t))
2035 (project-forget-zombie-projects)))) 2047 (project--delete-zombie-projects pred))))
2036 2048
2037(defun project--write-project-list () 2049(defun project--write-project-list ()
2038 "Save `project--list' in `project-list-file'." 2050 "Save `project--list' in `project-list-file'."
@@ -2041,6 +2053,10 @@ With some possible metadata (to be decided).")
2041 (insert ";;; -*- lisp-data -*-\n") 2053 (insert ";;; -*- lisp-data -*-\n")
2042 (let ((print-length nil) 2054 (let ((print-length nil)
2043 (print-level nil)) 2055 (print-level nil))
2056 (if-let* ((pred (alist-get 'list-write project-prune-zombie-projects))
2057 ((consp project--list))
2058 (inhibit-message t))
2059 (project--delete-zombie-projects pred))
2044 (pp (mapcar (lambda (elem) 2060 (pp (mapcar (lambda (elem)
2045 (let ((name (car elem))) 2061 (let ((name (car elem)))
2046 (list (if (file-remote-p name) name 2062 (list (if (file-remote-p name) name
@@ -2124,9 +2140,9 @@ function; see `project-prompter' for more details.
2124Unless REQUIRE-KNOWN is non-nil, it's also possible to enter an 2140Unless REQUIRE-KNOWN is non-nil, it's also possible to enter an
2125arbitrary directory not in the list of known projects." 2141arbitrary directory not in the list of known projects."
2126 (project--ensure-read-project-list) 2142 (project--ensure-read-project-list)
2127 (if-let* (project-prune-zombie-projects 2143 (if-let* ((pred (alist-get 'prompt project-prune-zombie-projects))
2128 (inhibit-message t)) 2144 (inhibit-message t))
2129 (project-forget-zombie-projects)) 2145 (project--delete-zombie-projects pred))
2130 (let* ((dir-choice "... (choose a dir)") 2146 (let* ((dir-choice "... (choose a dir)")
2131 (choices 2147 (choices
2132 ;; XXX: Just using this for the category (for the substring 2148 ;; XXX: Just using this for the category (for the substring
@@ -2165,9 +2181,9 @@ If PREDICATE is non-nil, filter possible project choices using this
2165function; see `project-prompter' for more details. 2181function; see `project-prompter' for more details.
2166Unless REQUIRE-KNOWN is non-nil, it's also possible to enter an 2182Unless REQUIRE-KNOWN is non-nil, it's also possible to enter an
2167arbitrary directory not in the list of known projects." 2183arbitrary directory not in the list of known projects."
2168 (if-let* (project-prune-zombie-projects 2184 (if-let* ((pred (alist-get 'prompt project-prune-zombie-projects))
2169 (inhibit-message t)) 2185 (inhibit-message t))
2170 (project-forget-zombie-projects)) 2186 (project--delete-zombie-projects pred))
2171 (let* ((dir-choice "... (choose a dir)") 2187 (let* ((dir-choice "... (choose a dir)")
2172 project--name-history 2188 project--name-history
2173 (choices 2189 (choices
@@ -2295,16 +2311,21 @@ Return the number of detected projects."
2295 count) count)) 2311 count) count))
2296 count)) 2312 count))
2297 2313
2298(defun project-forget-zombie-projects () 2314(defun project--delete-zombie-projects (predicate)
2299 "Forget all known projects that don't exist any more." 2315 "Helper function used by `project-forget-zombie-projects'.
2300 (interactive) 2316PREDICATE can be a function with 1 argument which determines which
2317projects should be deleted."
2301 (dolist (proj (project-known-project-roots)) 2318 (dolist (proj (project-known-project-roots))
2302 (when (and (if project-prune-zombie-projects 2319 (when (and (funcall (or predicate #'identity) proj)
2303 (funcall project-prune-zombie-projects proj)
2304 t)
2305 (not (file-exists-p proj))) 2320 (not (file-exists-p proj)))
2306 (project-forget-project proj)))) 2321 (project-forget-project proj))))
2307 2322
2323(defun project-forget-zombie-projects (&optional interactive)
2324 "Forget all known projects that don't exist any more."
2325 (interactive (list t))
2326 (let ((pred (when interactive (alist-get 'interactively project-prune-zombie-projects))))
2327 (project--delete-zombie-projects pred)))
2328
2308(defun project-forget-projects-under (dir &optional recursive) 2329(defun project-forget-projects-under (dir &optional recursive)
2309 "Forget all known projects below a directory DIR. 2330 "Forget all known projects below a directory DIR.
2310Interactively, prompt for DIR. 2331Interactively, prompt for DIR.