From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Spencer Baugh Newsgroups: gmane.emacs.bugs Subject: bug#66993: [PATCH] project.el: avoid asking user about project-list-file lock Date: Wed, 15 Nov 2023 15:49:11 -0500 Message-ID: References: <83sf5g1lko.fsf@gnu.org> <9d460f36-6035-da54-3abc-12171ac8977f@gutov.dev> <83h6lw19zw.fsf@gnu.org> <894f674f-76ea-90af-3acc-73ca6e7caf35@gutov.dev> <834jhv1lfw.fsf@gnu.org> <83ttpvyxn0.fsf@gnu.org> <42fe7d0e-024c-3e0d-3bc5-b0e6ec50f260@gutov.dev> <83r0kzyo93.fsf@gnu.org> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="35928"; mail-complaints-to="usenet@ciao.gmane.io" User-Agent: Gnus/5.13 (Gnus v5.13) Cc: Dmitry Gutov , 66993@debbugs.gnu.org To: Eli Zaretskii Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Wed Nov 15 21:50:21 2023 Return-path: Envelope-to: geb-bug-gnu-emacs@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1r3Mq5-00098Q-AH for geb-bug-gnu-emacs@m.gmane-mx.org; Wed, 15 Nov 2023 21:50:21 +0100 Original-Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1r3Mpo-0002ij-4o; Wed, 15 Nov 2023 15:50:04 -0500 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1r3Mpn-0002iP-8f for bug-gnu-emacs@gnu.org; Wed, 15 Nov 2023 15:50:03 -0500 Original-Received: from debbugs.gnu.org ([2001:470:142:5::43]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1r3Mpn-0003za-0Y for bug-gnu-emacs@gnu.org; Wed, 15 Nov 2023 15:50:03 -0500 Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1r3Mpm-0003n9-6Z for bug-gnu-emacs@gnu.org; Wed, 15 Nov 2023 15:50:02 -0500 X-Loop: help-debbugs@gnu.org Resent-From: Spencer Baugh Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Wed, 15 Nov 2023 20:50:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 66993 X-GNU-PR-Package: emacs X-GNU-PR-Keywords: patch Original-Received: via spool by 66993-submit@debbugs.gnu.org id=B66993.170008135814494 (code B ref 66993); Wed, 15 Nov 2023 20:50:02 +0000 Original-Received: (at 66993) by debbugs.gnu.org; 15 Nov 2023 20:49:18 +0000 Original-Received: from localhost ([127.0.0.1]:53951 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1r3Mp3-0003lh-CI for submit@debbugs.gnu.org; Wed, 15 Nov 2023 15:49:18 -0500 Original-Received: from mxout5.mail.janestreet.com ([64.215.233.18]:55471) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1r3Mp1-0003lJ-FX for 66993@debbugs.gnu.org; Wed, 15 Nov 2023 15:49:16 -0500 In-Reply-To: (Spencer Baugh's message of "Wed, 15 Nov 2023 10:40:33 -0500") X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-BeenThere: bug-gnu-emacs@gnu.org List-Id: "Bug reports for GNU Emacs, the Swiss army knife of text editors" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Original-Sender: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Xref: news.gmane.io gmane.emacs.bugs:274420 Archived-At: --=-=-= Content-Type: text/plain Spencer Baugh writes: > Eli Zaretskii writes: >>> Date: Thu, 9 Nov 2023 13:33:29 +0200 >>> Cc: sbaugh@janestreet.com, 66993@debbugs.gnu.org >>> From: Dmitry Gutov >>> >>> On 09/11/2023 13:27, Eli Zaretskii wrote: >>> >> Date: Thu, 9 Nov 2023 13:05:09 +0200 >>> >> Cc: sbaugh@janestreet.com, 66993@debbugs.gnu.org >>> >> From: Dmitry Gutov >>> >> >>> >> And what happens after the restart? The list needs to be recovered. >>> > >>> > Why does it need to be recovered? Isn't it built on-the-fly anyway? >>> > Is this just some kind of optimization? >>> >>> If we didn't need to read it after restart, there would be no point in >>> writing the list to disk. >>> >>> > I guess I don't understand well enough why this file exists and how it >>> > is used? >>> >>> Same reason as for savehist-file. >> >> It's a history of projects in the last session? Then it's one more >> reason to write the file only at exit, I guess? > > This comparison to savehist-file actually makes me think: Why don't we > just make project--list into a history variable, and persist it like > savehist? > > It would already be quite nice to have a history variable > project-history for project-prompter, I've been thinking about > implementing that. It would be convenient for: > > - switching repeatedly between two projects > > - running a project command accidentally outside a project and wanting > to run it in the most recently used project. > > But then, if we have project-history, could the project file just be a > persisted project-history? > > If we had this model, project-remember-project would be basically > (push project 'project-history) > > project-forget-zombie-projects would naturally not be needed, because > any zombie projects wouldn't be visited as part of the project-history, > and so if the history was limited in size, they'd eventually just drop > off the end. > > project-switch-project would prompt for anything on project-history. > > I think it simplifies things quite a lot! Here's a patch which implements this. It drops compatibility with the existing project-file, just to demonstrate the simplicity of this approach. project--list is the new history variable. Using it is mostly trivial; a small bit of work is necessary in project-prompt-project-name to get project-names for the history, but that worked out great. Everything else just seems to work. And with savehist-mode enabled, project--list just gets stored automatically without any code in project.el. I can implement a backwards-compatible version if this seems like a reasonable direction to go in. --=-=-= Content-Type: text/x-patch Content-Disposition: inline; filename=0001-Incompatibly-change-project-list-to-be-a-project-his.patch >From 86714ac9c82967e9d93e32d9bd172311fc4aed00 Mon Sep 17 00:00:00 2001 From: Spencer Baugh Date: Wed, 15 Nov 2023 15:44:03 -0500 Subject: [PATCH] Incompatibly change project--list to be a project history variable With savehist-mode enabled, this Just Works. --- lisp/progmodes/project.el | 114 +++++++++++--------------------------- 1 file changed, 33 insertions(+), 81 deletions(-) diff --git a/lisp/progmodes/project.el b/lisp/progmodes/project.el index 95db9d0ef4c..59404e5729d 100644 --- a/lisp/progmodes/project.el +++ b/lisp/progmodes/project.el @@ -1671,70 +1671,16 @@ project-kill-buffers ;;; Project list -(defcustom project-list-file (locate-user-emacs-file "projects") - "File in which to save the list of known projects." - :type 'file - :version "28.1" - :group 'project) - -(defvar project--list 'unset - "List structure containing root directories of known projects. -With some possible metadata (to be decided).") - -(defun project--read-project-list () - "Initialize `project--list' using contents of `project-list-file'." - (let ((filename project-list-file)) - (setq project--list - (when (file-exists-p filename) - (with-temp-buffer - (insert-file-contents filename) - (mapcar - (lambda (elem) - (let ((name (car elem))) - (list (if (file-remote-p name) name - (abbreviate-file-name name))))) - (read (current-buffer)))))) - (unless (seq-every-p - (lambda (elt) (stringp (car-safe elt))) - project--list) - (warn "Contents of %s are in wrong format, resetting" - project-list-file) - (setq project--list nil)))) - -(defun project--ensure-read-project-list () - "Initialize `project--list' if it isn't already initialized." - (when (eq project--list 'unset) - (project--read-project-list))) - -(defun project--write-project-list () - "Save `project--list' in `project-list-file'." - (let ((filename project-list-file)) - (with-temp-buffer - (insert ";;; -*- lisp-data -*-\n") - (let ((print-length nil) - (print-level nil)) - (pp (mapcar (lambda (elem) - (let ((name (car elem))) - (list (if (file-remote-p name) name - (expand-file-name name))))) - project--list) - (current-buffer))) - (write-region nil nil filename nil 'silent)))) +(defvar project--list nil + "List of root directories of recently-accessed projects.") ;;;###autoload -(defun project-remember-project (pr &optional no-write) - "Add project PR to the front of the project list. -Save the result in `project-list-file' if the list of projects -has changed, and NO-WRITE is nil." - (project--ensure-read-project-list) +(defun project-remember-project (pr &optional _no-write) + "Add project PR to the front of the project list." (let ((dir (abbreviate-file-name (project-root pr)))) - (unless (equal (caar project--list) dir) - (dolist (ent project--list) - (when (equal dir (car ent)) - (setq project--list (delq ent project--list)))) - (push (list dir) project--list) - (unless no-write - (project--write-project-list))))) + (unless (equal (car project--list) dir) + (setq project--list (delq dir project--list)) + (push dir project--list)))) (defun project--remove-from-project-list (project-root report-message) "Remove directory PROJECT-ROOT of a missing project from the project list. @@ -1742,11 +1688,9 @@ project--remove-from-project-list result in `project-list-file'. Announce the project's removal from the list using REPORT-MESSAGE, which is a format string passed to `message' as its first argument." - (project--ensure-read-project-list) - (when-let ((ent (assoc (abbreviate-file-name project-root) project--list))) - (setq project--list (delq ent project--list)) - (message report-message project-root) - (project--write-project-list))) + (let ((dir (abbreviate-file-name project-root))) + (setq project--list (delq dir project--list)) + (message report-message project-root))) ;;;###autoload (defun project-forget-project (project-root) @@ -1762,7 +1706,6 @@ project-prompt-project-dir 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." - (project--ensure-read-project-list) (let* ((dir-choice "... (choose a dir)") (choices ;; XXX: Just using this for the category (for the substring @@ -1772,43 +1715,55 @@ project-prompt-project-dir (pr-dir "")) (while (equal pr-dir "") ;; If the user simply pressed RET, do this again until they don't. - (setq pr-dir (completing-read "Select project: " choices nil t))) + (setq pr-dir (completing-read "Select project: " choices nil t nil 'project--list))) (if (equal pr-dir dir-choice) (read-directory-name "Select directory: " default-directory nil t) pr-dir))) +(defvar project--name-history) + (defun project-prompt-project-name () "Prompt the user for a project, by name, that is one of the known project roots. 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." (let* ((dir-choice "... (choose a dir)") + project--name-history (choices (let (ret) - (dolist (dir (project-known-project-roots)) + ;; Iterate in reverse order so project--name-history is in + ;; the correct order. + (dolist (dir (reverse project--list)) ;; we filter out directories that no longer map to a project, ;; since they don't have a clean project-name. - (if-let (proj (project--find-in-directory dir)) - (push (cons (project-name proj) proj) ret))) + (when-let (proj (project--find-in-directory dir)) + (let ((name (project-name proj))) + (push name project--name-history) + (push (cons name proj) ret)))) ret)) ;; XXX: Just using this for the category (for the substring ;; completion style). (table (project--file-completion-table - (reverse (cons dir-choice choices)))) + (cons dir-choice choices))) (pr-name "")) + (setq project--name-history (delete-consecutive-dups project--name-history)) (while (equal pr-name "") ;; If the user simply pressed RET, do this again until they don't. - (setq pr-name (completing-read "Select project: " table nil t))) + (setq pr-name + (completing-read "Select project: " table nil t nil 'project--name-history))) (if (equal pr-name dir-choice) (read-directory-name "Select directory: " default-directory nil t) - (let ((proj (assoc pr-name choices))) - (if (stringp proj) proj (project-root (cdr proj))))))) + (let* ((proj (assoc pr-name choices)) + (ret (project-root (cdr proj)))) + ;; Record this return value in history, since + ;; project--name-history is purely local. + (push ret project--list) + ret)))) ;;;###autoload (defun project-known-project-roots () "Return the list of root directories of all known projects." - (project--ensure-read-project-list) - (mapcar #'car project--list)) + project--list) ;;;###autoload (defun project-execute-extended-command () @@ -1865,14 +1820,13 @@ project-remember-projects-under the progress. The function returns the number of detected projects." (interactive "DDirectory: \nP") - (project--ensure-read-project-list) (let ((dirs (if recursive (directory-files-recursively dir "" t) (directory-files dir t))) (known (make-hash-table :size (* 2 (length project--list)) :test #'equal)) (count 0)) - (dolist (project (mapcar #'car project--list)) + (dolist (project project--list) (puthash project t known)) (dolist (subdir dirs) (when-let (((file-directory-p subdir)) @@ -1885,7 +1839,6 @@ project-remember-projects-under (setq count (1+ count)))) (if (zerop count) (message "No projects were found") - (project--write-project-list) (message "%d project%s were found" count (if (= count 1) "" "s"))) count)) @@ -1916,7 +1869,6 @@ project-forget-projects-under (setq count (1+ count))))) (if (zerop count) (message "No projects were forgotten") - (project--write-project-list) (message "%d project%s were forgotten" count (if (= count 1) "" "s"))) count)) -- 2.39.3 --=-=-=--