diff options
| author | Elías Gabriel Pérez | 2025-07-05 13:29:24 -0600 |
|---|---|---|
| committer | Juri Linkov | 2025-08-21 20:22:33 +0300 |
| commit | ade6608e2587452c8ea565ce3057879379ebd0b5 (patch) | |
| tree | 9b35104089cffced12a85c76ac64cd62a08037a4 | |
| parent | 3f7c16d858e579ed03a195841ba9805fbc2899ba (diff) | |
| download | emacs-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/NEWS | 11 | ||||
| -rw-r--r-- | lisp/progmodes/project.el | 65 |
2 files changed, 49 insertions, 27 deletions
| @@ -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 |
| 505 | project. | 505 | project. |
| 506 | 506 | ||
| 507 | The value can be a predicate which takes one argument and should return | 507 | The value must be an alist where each element must be in the form: |
| 508 | non-nil if the project should be removed. If set to nil, all the | ||
| 509 | inaccessible projects will not be removed automatically. | ||
| 510 | 508 | ||
| 511 | By default this is set to 'project-prune-zombies-default' function | 509 | (WHEN . PREDICATE) |
| 512 | which removes all non-remote projects. | 510 | |
| 511 | where WHEN specifies where the deletion will be performed, and PREDICATE | ||
| 512 | a function which takes one argument, and must return non-nil if the | ||
| 513 | project 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)) |
| 1655 | The value can be a predicate function which takes one argument, and | 1655 | "Remove automatically from project list the projects that were removed. |
| 1656 | should return non-nil if the project should be removed. | 1656 | Each element of this alist must be in the form: |
| 1657 | If 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) | 1659 | where WHEN specifies where the deletion will be performed, |
| 1660 | (const :tag "Remove any project" identity) | 1660 | the 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 | |||
| 1668 | PREDICATE must be a function which takes one argument, and should return | ||
| 1669 | non-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. | |||
| 2124 | Unless REQUIRE-KNOWN is non-nil, it's also possible to enter an | 2140 | Unless REQUIRE-KNOWN is non-nil, it's also possible to enter an |
| 2125 | arbitrary directory not in the list of known projects." | 2141 | arbitrary 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 | |||
| 2165 | function; see `project-prompter' for more details. | 2181 | function; see `project-prompter' for more details. |
| 2166 | Unless REQUIRE-KNOWN is non-nil, it's also possible to enter an | 2182 | Unless REQUIRE-KNOWN is non-nil, it's also possible to enter an |
| 2167 | arbitrary directory not in the list of known projects." | 2183 | arbitrary 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) | 2316 | PREDICATE can be a function with 1 argument which determines which |
| 2317 | projects 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. |
| 2310 | Interactively, prompt for DIR. | 2331 | Interactively, prompt for DIR. |