commit 75a1403ff3e006e4b0fc71b2da3f3c48a70da766 Author: Dmitry Gutov Date: Thu Dec 18 03:48:11 2025 +0200 Fix "No such file or directory" "vc-nil" in project-files * lisp/progmodes/project.el (project-files): Fix vc-nil backend error (bug#80013). diff --git a/lisp/progmodes/project.el b/lisp/progmodes/project.el index ddc79dc6944..03d74a353e2 100644 --- a/lisp/progmodes/project.el +++ b/lisp/progmodes/project.el @@ -685,7 +685,8 @@ See `project-vc-extra-root-markers' for the marker value format.") (backend (cadr project))) (when backend (require (intern (concat "vc-" (downcase (symbol-name backend)))))) - (if (file-in-directory-p dir (nth 2 project)) + (if (and backend + (file-in-directory-p dir (nth 2 project))) (condition-case nil (project--vc-list-files dir backend ignores) (vc-not-supported commit 22719d64e972c369acf58ff237723ea30de49827 Author: F. Jason Park Date: Sat Nov 8 16:19:19 2025 -0800 ; Tweak failing autojoin display-context test for ERC * test/lisp/erc/erc-scenarios-join-display-context.el (erc-scenarios-join-display-context--errors): Attempt to indirectly fix a reoccurring failure on EMBA. Instead of asserting the membership of `erc-join--requested-channels', have the client attempt to manually join an autojoined channel previously denied by an error response. But this time assert that the display context for the now successfully joined channel doesn't indicate it originated from the autojoin module. * test/lisp/erc/resources/base/gapless-connect/barnet.eld: Increase timeout. * test/lisp/erc/resources/join/buffer-display/mode-context.eld: Update accordingly. diff --git a/test/lisp/erc/erc-scenarios-join-display-context.el b/test/lisp/erc/erc-scenarios-join-display-context.el index 0e1bb2b081d..d35c32c8ada 100644 --- a/test/lisp/erc/erc-scenarios-join-display-context.el +++ b/test/lisp/erc/erc-scenarios-join-display-context.el @@ -26,6 +26,13 @@ (let ((load-path (cons (ert-resource-directory) load-path))) (require 'erc-scenarios-common))) +;; This module uses the list `erc-join--requested-channels' to detect +;; whether a JOIN response was likely triggered by an outgoing JOIN +;; emitted on behalf of `erc-autojoin-channels-alist'. When a related +;; error response, such as a 471, arrives in lieu of a JOIN, the module +;; removes the affected channel's name from the list so that any calls +;; to `display-buffer' in `erc-setup-buffer' on subsequent JOINs aren't +;; misled into thinking the JOIN was module-initiated. (ert-deftest erc-scenarios-join-display-context--errors () :tags '(:expensive-test) (erc-scenarios-common-with-cleanup @@ -33,11 +40,14 @@ (erc-server-flood-penalty 0.1) (dumb-server (erc-d-run "localhost" t 'mode-context)) (port (process-contact dumb-server :service)) - (erc-buffer-display (lambda (buf action) - (when (equal - (alist-get 'erc-autojoin-mode action) - "#chan") - (pop-to-buffer buf)))) + (calls ()) + (erc-buffer-display + (lambda (buf action) + (when (equal (alist-get 'erc-buffer-display action) 'JOIN) + (when (equal (buffer-name buf) "#chan") + (should (equal (alist-get 'erc-autojoin-mode action) "#chan"))) + (push (cons buf action) calls) + (pop-to-buffer buf)))) (erc-autojoin-channels-alist '((foonet "#chan" "#spam" "#foo"))) (expect (erc-d-t-make-expecter))) @@ -47,20 +57,27 @@ :nick "tester" :full-name "tester") (should (string= (buffer-name) (format "127.0.0.1:%d" port))) - ;; FIXME test for effect rather than inspecting interval variables. - (erc-d-t-wait-for 10 (equal erc-join--requested-channels - '("#foo" "#spam" "#chan"))) (funcall expect 10 "Max occupancy for channel #spam exceeded") (funcall expect 10 "Channel #foo is invitation only"))) (ert-info ("New #chan buffer displayed in new window") (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#chan")) (should (eq (window-buffer) (current-buffer))) + (should (equal calls `((,(current-buffer) nil + (erc-buffer-display . JOIN) + (erc-autojoin-mode . "#chan"))))) + (setq calls nil) + (funcall expect 10 "invites you to channel #foo") (funcall expect 10 "#chan was created on"))) - ;; FIXME find a less dishonest way to do this than inspecting - ;; interval variables. - (ert-info ("Ensure channels no longer tracked") - (should-not erc-join--requested-channels)))) + (with-current-buffer "foonet" + ;; Don't simulate an interactive /join because that overrides the + ;; display context. + (erc-cmd-JOIN "#foo")) + + (with-current-buffer (erc-d-t-wait-for 10 (get-buffer "#foo")) + ;; `erc-autojoin-mode' is absent from the action alist. + (should (equal calls `((,(current-buffer) nil + (erc-buffer-display . JOIN)))))))) ;;; erc-scenarios-join-display-context.el ends here diff --git a/test/lisp/erc/resources/base/gapless-connect/barnet.eld b/test/lisp/erc/resources/base/gapless-connect/barnet.eld index 4e658802ef6..315c1f86478 100644 --- a/test/lisp/erc/resources/base/gapless-connect/barnet.eld +++ b/test/lisp/erc/resources/base/gapless-connect/barnet.eld @@ -1,7 +1,7 @@ ;; -*- mode: lisp-data; -*- ((pass 10 "PASS :barnet:changeme")) ((nick 10 "NICK tester")) -((user 0.2 "USER user 0 * :tester") +((user 10 "USER user 0 * :tester") (0 ":irc.barnet.org 001 tester :Welcome to the barnet IRC Network tester") (0 ":irc.barnet.org 002 tester :Your host is irc.barnet.org, running version oragono-2.5.1-4860c5cad0179db1") (0 ":irc.barnet.org 003 tester :This server was created Fri, 19 Mar 2021 10:23:19 UTC") diff --git a/test/lisp/erc/resources/join/buffer-display/mode-context.eld b/test/lisp/erc/resources/join/buffer-display/mode-context.eld index 6ebbdc7e824..9c5e335c7b8 100644 --- a/test/lisp/erc/resources/join/buffer-display/mode-context.eld +++ b/test/lisp/erc/resources/join/buffer-display/mode-context.eld @@ -23,7 +23,9 @@ (0.02 ":irc.foonet.org 221 tester +i")) ((join-chan 10 "JOIN #chan") - (0.03 ":tester!~u@w9rfqveugz722.irc JOIN #chan")) + (0.03 ":tester!~u@w9rfqveugz722.irc JOIN #chan") + (0.03 ":irc.foonet.org 353 tester = #chan :@fsbot bob alice tester") + (0.03 ":irc.foonet.org 366 tester #chan :End of NAMES list")) ((~mode-chan 10 "MODE #chan") (0.01 ":irc.foonet.org 353 tester = #chan :@tester") @@ -31,8 +33,18 @@ (0.01 ":irc.foonet.org 324 tester #chan +nt") (0.03 ":irc.foonet.org 329 tester #chan 1653370308")) -((~join-spam 10 "JOIN #spam") +((join-spam 10 "JOIN #spam") (0.03 ":irc.foonet.org 471 tester #spam :Cannot join channel (+l)")) -((~join-foo 10 "JOIN #foo") - (0.03 ":irc.foonet.org 473 tester #foo :Cannot join channel (+i)")) +((join-foo 10 "JOIN #foo") + (0.03 ":irc.foonet.org 473 tester #foo :Cannot join channel (+i)") + (0.01 ":bob!~u@pgq8itg3w7n3a.irc INVITE tester #foo")) + +((join-chan 10 "JOIN #foo") + (0.03 ":tester!~u@w9rfqveugz722.irc JOIN #foo")) + +((mode-foo 10 "MODE #foo") + (0.01 ":irc.foonet.org 353 tester = #foo :@tester") + (0.00 ":irc.foonet.org 366 tester #foo :End of NAMES list") + (0.01 ":irc.foonet.org 324 tester #foo +nt") + (0.03 ":irc.foonet.org 329 tester #foo 1653370308")) commit 821c0d36dfa2f2cddb9d05a024a8248f2390f478 Author: Dmitry Gutov Date: Thu Dec 18 01:43:03 2025 +0200 vc-git-project-list-files: Restore support for Git < 2.13 * lisp/progmodes/project.el (vc-git-project-list-files): Restore support for Git < 2.13 (discussed in bug#79809). diff --git a/lisp/progmodes/project.el b/lisp/progmodes/project.el index d543f183216..ddc79dc6944 100644 --- a/lisp/progmodes/project.el +++ b/lisp/progmodes/project.el @@ -722,30 +722,31 @@ See `project-vc-extra-root-markers' for the marker value format.") (and include-untracked '("-o")))) (when extra-ignores (setq args (append args - (cons "--" - (mapcar - (lambda (i) - (format - ":(exclude,glob,top)%s" - (if (string-match "\\*\\*" i) - ;; Looks like pathspec glob - ;; format already. - i - (if (string-match "\\./" i) - ;; ./abc -> abc - (setq i (substring i 2)) - ;; abc -> **/abc - (setq i (concat "**/" i)) - ;; FIXME: '**/abc' should also - ;; match a directory with that - ;; name, but doesn't (git 2.25.1). - ;; Maybe we should replace - ;; such entries with two. - (if (string-match "/\\'" i) - ;; abc/ -> abc/** - (setq i (concat i "**")))) - i))) - extra-ignores))))) + ;; Git <2.13 needs a positive pathspec first. + '("--" ".") + (mapcar + (lambda (i) + (format + ":(exclude,glob,top)%s" + (if (string-match "\\*\\*" i) + ;; Looks like pathspec glob + ;; format already. + i + (if (string-match "\\./" i) + ;; ./abc -> abc + (setq i (substring i 2)) + ;; abc -> **/abc + (setq i (concat "**/" i)) + ;; FIXME: '**/abc' should also + ;; match a directory with that + ;; name, but doesn't (git 2.25.1). + ;; Maybe we should replace + ;; such entries with two. + (if (string-match "/\\'" i) + ;; abc/ -> abc/** + (setq i (concat i "**")))) + i))) + extra-ignores)))) (setq files (delq nil (mapcar commit 7425e33287547b879c22c4f4ac1b7c78fcbdd6f9 Author: Sean Whitton Date: Wed Dec 17 13:22:39 2025 +0000 New M-RET, M-p, M-n commands in Log View mode * lisp/vc/log-view.el (log-view-msg-and-diff-next) (log-view-msg-and-diff-prev, log-view-display-entry-and-diff): New commands. (log-view-mode-map): Bind them. * doc/emacs/maintaining.texi (VC Change Log): * etc/NEWS: Document them. diff --git a/doc/emacs/maintaining.texi b/doc/emacs/maintaining.texi index 8e9fcc2cc23..cc5e813f018 100644 --- a/doc/emacs/maintaining.texi +++ b/doc/emacs/maintaining.texi @@ -1256,6 +1256,26 @@ In a compact-style log buffer (e.g., the one created by @kbd{C-x v L}), toggle between showing and hiding the full log entry for the revision at point (@code{log-view-toggle-entry-display}). +@item M-@key{RET} +Show the full log entry like @kbd{RET} does, if relevant, and also +display the diff like @kbd{D} does, but without selecting the window +showing the diff (@code{log-view-display-entry-and-diff}). Never hides +the full log entry text if it is already shown. + +@item M-p +Like @kbd{p} followed by @kbd{M-@key{RET}}: move to the previous entry, +display it in full if relevant, and display its diff +(@code{log-view-msg-and-diff-prev}). A numeric prefix argument is a +repeat count for the movement part; at most one entry is displayed in +full. + +@item M-n +Like @kbd{n} followed by @kbd{M-@key{RET}}: move to the previous entry, +display it in full if relevant, and display its diff +(@code{log-view-msg-and-diff-next}). A numeric prefix argument is a +repeat count for the movement part; at most one entry is displayed in +full. + @item m Mark the entry at point (@code{log-view-mark-entry}). diff --git a/etc/NEWS b/etc/NEWS index 6a6166ec6c1..e90000a9f58 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -2642,6 +2642,16 @@ You can get back the old behavior with something like this: In addition, a new command 'U' removes all marks. ++++ +*** New commands 'M-RET', 'M-p' and 'M-n' in Log View mode. +'M-RET' expands the current entry, if relevant, and displays its diff in +another window. 'M-p' and 'M-n' move to the previous and next entries, +respectively, expand them if relevant, and display their diffs. + +You can use these three commands together to more easily view all the +log entries and diffs of a series of revisions: use 'M-RET' on the first +revision, then either 'M-n' or 'M-p' repeatedly to view the others. + +++ *** New command 'w' in Log View mode. The new command 'log-view-copy-revision-as-kill', bound to 'w' in Log diff --git a/lisp/vc/log-view.el b/lisp/vc/log-view.el index 1ee427c345a..c8140c1c41b 100644 --- a/lisp/vc/log-view.el +++ b/lisp/vc/log-view.el @@ -130,6 +130,7 @@ (defvar-keymap log-view-mode-map "RET" #'log-view-toggle-entry-display + "M-" #'log-view-display-entry-and-diff "m" #'log-view-mark-entry "u" #'log-view-unmark-entry "U" #'log-view-unmark-all-entries @@ -141,6 +142,8 @@ "f" #'log-view-find-revision "n" #'log-view-msg-next "p" #'log-view-msg-prev + "M-n" #'log-view-msg-and-diff-next + "M-p" #'log-view-msg-and-diff-prev "w" #'log-view-copy-revision-as-kill "TAB" #'log-view-msg-next "" #'log-view-msg-prev @@ -298,6 +301,20 @@ The match group number 1 should match the revision number itself.") (log-view-msg-next)) t)) +(defun log-view-msg-and-diff-next (count) + "Go to next COUNT'th entry, expand it if possible, and show its diff. +Interactively, COUNT is the numeric prefix argument, and defaults to 1." + (interactive "p") + (log-view-msg-next count) + (log-view-display-entry-and-diff)) + +(defun log-view-msg-and-diff-prev (count) + "Go to previous COUNT'th entry, expand it if possible, and show its diff. +Interactively, COUNT is the numeric prefix argument, and defaults to 1." + (interactive "p") + (log-view-msg-next (- count)) + (log-view-display-entry-and-diff)) + ;;;; ;;;; Linkage to PCL-CVS (mostly copied from cvs-status.el) ;;;; @@ -487,7 +504,8 @@ Added to `revert-buffer-restore-functions' by Log View mode." (defun log-view-toggle-entry-display () "If possible, expand the current Log View entry. -This calls `log-view-expanded-log-entry-function' to do the work." +This calls `log-view-expanded-log-entry-function' to do the work. +See also `log-view-display-entry-and-diff'." (interactive) ;; Don't do anything unless `log-view-expanded-log-entry-function' ;; is defined in this mode. @@ -523,6 +541,20 @@ This calls `log-view-expanded-log-entry-function' to do the work." '(font-lock-face log-view-commit-body log-view-comment t)) (goto-char opoint)))))))) +(defun log-view-display-entry-and-diff () + "Expand current Log View entry, if possible, and also display its diff. +If the current Log View entry is already expanded, only display its +diff; this command never collapses entries. +In contrast with `log-view-diff-changeset', the window displaying the +diff is not selected. +See also `log-view-toggle-entry-display'." + (interactive) + (when-let* ((beg (car (log-view-current-entry)))) + (unless (get-text-property beg 'log-view-entry-expanded) + (log-view-toggle-entry-display))) + (save-selected-window + (log-view-diff (point) (point)))) + (defun log-view-beginning-of-defun (&optional arg) "Move backward to the beginning of a Log View entry. With ARG, do it that many times. Negative ARG means move forward commit 7844710e87f157ab95b68f145749aedfc8d4d2e4 Author: Sean Whitton Date: Wed Dec 17 13:21:37 2025 +0000 log-view-toggle-entry-display: Don't deactivate the mark * lisp/vc/log-view.el (log-view-toggle-entry-display): Bind inhibit-read-only instead of buffer-read-only. Bind deactivate-mark to nil. diff --git a/lisp/vc/log-view.el b/lisp/vc/log-view.el index 752638b4fda..1ee427c345a 100644 --- a/lisp/vc/log-view.el +++ b/lisp/vc/log-view.el @@ -495,7 +495,8 @@ This calls `log-view-expanded-log-entry-function' to do the work." (let* ((opoint (point)) (entry (log-view-current-entry nil t)) (beg (car entry)) - (buffer-read-only nil)) + (inhibit-read-only t) + deactivate-mark) (when entry (if (get-text-property beg 'log-view-entry-expanded) ;; If the entry is expanded, collapse it. commit 6d7ff7478fcbc4af478d198babbb76ec49ee3780 Author: Sean Whitton Date: Wed Dec 17 13:20:50 2025 +0000 ; Log View beg and end of defun functions: Style improvements. diff --git a/lisp/vc/log-view.el b/lisp/vc/log-view.el index 504989a9dd6..752638b4fda 100644 --- a/lisp/vc/log-view.el +++ b/lisp/vc/log-view.el @@ -530,28 +530,27 @@ to the beginning of the ARGth following entry. This is Log View mode's default `beginning-of-defun-function'. It assumes that a log entry starts with a line matching `log-view-message-re'." - (when (null arg) (setf arg 1)) - (if (< arg 0) + (when (null arg) (setq arg 1)) + (if (minusp arg) ;; In log view, the end of one defun is the beginning of the ;; next, so punting to log-view-end-of-defun is safe in this ;; context. (log-view-end-of-defun (- arg)) (let ((found t)) - (while (> arg 0) - (setf arg (1- arg)) + (while (plusp arg) + (decf arg) (let ((cur-start (log-view-current-entry))) - (setf found - (cond ((null cur-start) - (goto-char (point-min)) - nil) - ((>= (car cur-start) (point)) - (unless (bobp) - (forward-line -1) - (setf arg (1+ arg))) - nil) - (t - (goto-char (car cur-start)) - t))))) + (setq found (cond ((null cur-start) + (goto-char (point-min)) + nil) + ((>= (car cur-start) (point)) + (unless (bobp) + (forward-line -1) + (incf arg)) + nil) + (t + (goto-char (car cur-start)) + t))))) found))) (defun log-view-end-of-defun-1 () @@ -578,12 +577,11 @@ It assumes that a log entry starts with a line matching (defun log-view-end-of-defun (&optional arg) "Move forward to the next Log View entry. Works like `end-of-defun'." - (when (null arg) (setf arg 1)) - (if (< arg 0) + (when (null arg) (setq arg 1)) + (if (minusp arg) (log-view-beginning-of-defun (- arg)) (dotimes (_n arg) - (log-view-end-of-defun-1) - t))) + (log-view-end-of-defun-1)))) (defvar cvs-minor-current-files) (defvar cvs-branch-prefix) commit b7601bfb70ee06e7ed97c5f80bfc8c8e0dd80d6c Author: Eli Zaretskii Date: Wed Dec 17 15:14:25 2025 +0200 Fix 'mode-line-format-right-align' in Olivetti mode * lisp/bindings.el (mode--line-format-right-align): Account for the left fringe. (Bug#80019) diff --git a/lisp/bindings.el b/lisp/bindings.el index 8b021a05ce2..e34dea6ee1f 100644 --- a/lisp/bindings.el +++ b/lisp/bindings.el @@ -383,6 +383,7 @@ the symbol `mode-line-format-right-align' is processed by (window-right-divider-width) (* (or (car (window-margins)) 0) (frame-char-width)) + (car (window-fringes)) ;; Manually account for value of ;; `mode-line-right-align-edge' even ;; when display is non-graphical commit 0c7420fe3859af6a4a26c174e7b040873723ce5a Author: Sean Whitton Date: Wed Dec 17 12:42:41 2025 +0000 ; * lisp/bookmark.el (bookmark-after-load-file-hook): Fix quotation. diff --git a/lisp/bookmark.el b/lisp/bookmark.el index f2a1c69929b..895fb9091fd 100644 --- a/lisp/bookmark.el +++ b/lisp/bookmark.el @@ -1764,8 +1764,8 @@ This is a helper for `bookmark-import-new-list'." (defcustom bookmark-after-load-file-hook nil - "Hook run after a bookmark file is loaded by `bookmark-load`. -This hook can be used, for example, to reconcile 'bookmark-alist' + "Hook run after a bookmark file is loaded by `bookmark-load'. +This hook can be used, for example, to reconcile `bookmark-alist' against bookmark state that you, or a package that you use, maintains." :type 'hook :version "31.1") commit f26068dac746cb61db57b86187c5033b9d8dd8b2 Author: Liu Hui Date: Wed Dec 17 17:45:46 2025 +0800 calendar-mode-line-format: Fix regexp * lisp/calendar/calendar.el (calendar-mode-line-format): Replace '*' with '\\*'. (calendar-get-buffer, calendar-exit): * lisp/calendar/diary-lib.el (diary-mark-entries): Improve comment and doc string. diff --git a/lisp/calendar/calendar.el b/lisp/calendar/calendar.el index 43a159782ec..6805a84a80d 100644 --- a/lisp/calendar/calendar.el +++ b/lisp/calendar/calendar.el @@ -1103,7 +1103,7 @@ Otherwise, use symbolic time zones like \"CET\"." "Name of the buffer used for the calendar.") (defun calendar-get-buffer () - "Return current usable calendar buffer." + "Return current usable calendar-mode buffer." (if (derived-mode-p 'calendar-mode) (current-buffer) (get-buffer calendar-buffer))) @@ -1780,7 +1780,7 @@ is COMMAND's keybinding, STRING describes the binding." (defcustom calendar-mode-line-format (list (calendar-mode-line-entry 'calendar-scroll-right "previous month" "<") - '(string-trim (buffer-name) "*" "*") + '(string-trim (buffer-name) "\\*" "\\*") (concat (calendar-mode-line-entry 'calendar-goto-info-node "read Info on Calendar" nil "info") @@ -1915,8 +1915,10 @@ concatenated and the result truncated." If KILL (interactively, the prefix), kill the buffers instead of hiding them." (interactive "P") - ;; Don't handle other buffers when exiting a non-default calendar - ;; buffer. + ;; FIXME: We need to record the associations between each + ;; calendar-mode buffer and its auxiliary buffers explicitly. For the + ;; moment, don't handle buffers from `calendar-buffer-list' when + ;; exiting a calendar-mode buffer not named `calendar-buffer'. (if (and (derived-mode-p 'calendar-mode) (not (equal (current-buffer) (get-buffer calendar-buffer)))) (quit-windows-on nil kill) diff --git a/lisp/calendar/diary-lib.el b/lisp/calendar/diary-lib.el index 418dede88d2..51a2977f583 100644 --- a/lisp/calendar/diary-lib.el +++ b/lisp/calendar/diary-lib.el @@ -1401,7 +1401,7 @@ marks. This is intended to deal with deleted diary entries." (let ((diary-marking-entries-flag t) (diary-buffer (find-buffer-visiting diary-file)) ;; Record current calendar buffer in case this function is - ;; called in a non-default calendar buffer. + ;; called in a calendar-mode buffer not named `calendar-buffer'. (calendar-buffer (calendar-get-buffer)) ;; Dynamically bound in diary-include-files. (d-incp (and (boundp 'diary-including) diary-including)) commit f0912d5cb06961395cba4637d2afd5fe92774731 Author: João Távora Date: Wed Dec 17 12:04:24 2025 +0000 Eglot: speed up file watching outside project (bug#79809) Use find-based directory listing when watching outside project root, avoiding the expensive delete-dups/file-name-directory/project-files combo. * lisp/progmodes/eglot.el (eglot--list-directories): New helper. (eglot--watch-globs): Use it for directories outside project root. diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el index 82cb95392fb..0b5720ae440 100644 --- a/lisp/progmodes/eglot.el +++ b/lisp/progmodes/eglot.el @@ -4320,6 +4320,18 @@ at point. With prefix argument, prompt for ACTION-KIND." (defvar eglot-watch-files-outside-project-root t "If non-nil, allow watching files outside project root") +(defun eglot--list-directories (dir) + (with-temp-buffer + (condition-case oops + (call-process find-program nil t nil dir "-type" "d" "-print0") + (error + (eglot--warn "Can't list directories in %s: %s" dir oops))) + (cl-loop initially (goto-char (point-min)) + for start = (point) while (search-forward "\0" nil t) + collect (expand-file-name + (buffer-substring-no-properties start (1- (point))) + dir)))) + (defun eglot--watch-globs (server id globs &optional base-path) "Set up file watching for files matching GLOBS under BASE-PATH. GLOBS is a list of (COMPILED-GLOB . KIND) pairs, where COMPILED-GLOB @@ -4327,10 +4339,17 @@ is a compiled glob predicate and KIND is a bitmask of change types. BASE-PATH is the directory to watch (nil means entire project). Returns success status for SERVER and registration ID." (let* ((project (eglot--project server)) - (dirs (delete-dups - (mapcar #'file-name-directory - (project-files project (and base-path - (list base-path)))))) + (root (project-root project)) + (dirs (if (and base-path + (not (file-in-directory-p base-path root))) + ;; Outside root, use faster find-based listing + (eglot--list-directories base-path) + ;; Inside project or entire project: use project-files + ;; which respects ignores + (delete-dups + (mapcar #'file-name-directory + (project-files project (and base-path + (list base-path))))))) (success nil)) (cl-labels ((handle-event (event) commit a8b9f2daf1877b525c0574ccdc904efbf1d54ab2 Author: Sean Whitton Date: Wed Dec 17 12:02:33 2025 +0000 ; * etc/NEWS: Elisp -> Lisp. diff --git a/etc/NEWS b/etc/NEWS index fb69ffc49c2..6a6166ec6c1 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1546,7 +1546,7 @@ bookmark state that you, or a package that you use, maintains. Customize user option 'recentf-autosave-interval' to the number of seconds between auto saving recently opened files. For example, customize this variable to 300 to save recently opened files every 5 -minutes. In Elisp, use 'setopt' not 'setq'. +minutes. From Lisp, use 'setopt' not 'setq'. If 'recentf-autosave-interval' is nil, auto saving is disabled; this is the default. @@ -1574,7 +1574,7 @@ option is nil. *** You can now regularly auto-save places. Customize user option 'save-place-autosave-interval' to the number of seconds between auto saving places. For example, customize this -variable to 300 to save places every 5 minutes. In Elisp, use 'setopt' +variable to 300 to save places every 5 minutes. From Lisp, use 'setopt' not 'setq'. If 'save-place-autosave-interval' is nil, auto saving is disabled; this commit d76e61d218742396a5f48cbe44af27cacecbea22 Author: Stéphane Marks Date: Mon Dec 15 15:21:23 2025 -0500 New bookmark-after-load-file-hook (bug#80003) * lisp/bookmark.el (bookmark-after-load-file-hook): New defcustom (bug#80003). (bookmark-load): Call the new hook. * etc/NEWS: Announce the new hook. diff --git a/etc/NEWS b/etc/NEWS index f74c17e4f36..fb69ffc49c2 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1533,6 +1533,12 @@ This user option controls the width of the type column on the bookmark menu 'bookmark-bmenu-list'. The default value is 8 which is backwards compatible. +--- +*** New hook 'bookmark-after-load-file-hook'. +This hook is run by 'bookmark-load' after loading a bookmark file. This +hook can be used, for example, to reconcile 'bookmark-alist' against +bookmark state that you, or a package that you use, maintains. + ** Recentf --- diff --git a/lisp/bookmark.el b/lisp/bookmark.el index 75071ddb9ce..f2a1c69929b 100644 --- a/lisp/bookmark.el +++ b/lisp/bookmark.el @@ -1763,6 +1763,13 @@ This is a helper for `bookmark-import-new-list'." (bookmark-set-name full-record new-name))))) +(defcustom bookmark-after-load-file-hook nil + "Hook run after a bookmark file is loaded by `bookmark-load`. +This hook can be used, for example, to reconcile 'bookmark-alist' +against bookmark state that you, or a package that you use, maintains." + :type 'hook + :version "31.1") + ;;;###autoload (defun bookmark-load (file &optional overwrite no-msg default) "Load bookmarks from FILE (which must be in bookmark format). @@ -1824,6 +1831,7 @@ unique numeric suffixes \"<2>\", \"<3>\", etc." (bookmark-bmenu-surreptitiously-rebuild-list) (setq bookmark-file-coding-system buffer-file-coding-system)) (kill-buffer (current-buffer))) + (run-hooks 'bookmark-after-load-file-hook) (unless no-msg (progress-reporter-done reporter))))) commit 91d4120289e79c7c8097691cbf89ff5b304b74e4 Author: Stéphane Marks Date: Tue Dec 16 08:38:33 2025 -0500 ; lisp/saveplace.el, etc/NEWS: Refinements to bug#75837. diff --git a/etc/NEWS b/etc/NEWS index 311bf044476..f74c17e4f36 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1567,18 +1567,16 @@ option is nil. --- *** You can now regularly auto-save places. Customize user option 'save-place-autosave-interval' to the number of -seconds between auto saving places. For example, to save places every 5 -minutes: +seconds between auto saving places. For example, customize this +variable to 300 to save places every 5 minutes. In Elisp, use 'setopt' +not 'setq'. - M-x customize-option RET save-place-autosave-interval RET 300 - -Or in Elisp: +If 'save-place-autosave-interval' is nil, auto saving is disabled; this +is the default. - (setopt save-place-autosave-interval (* 60 5)) +This user option is in sympathy with recentf, and savehist autosave +timers. -If 'save-place-autosave-interval' is nil, auto saving is disabled; this -is the default. As before, saved places are scheduled to be saved at -Emacs exit. ** Savehist diff --git a/lisp/saveplace.el b/lisp/saveplace.el index 51df1edeaa7..d3119fd6af2 100644 --- a/lisp/saveplace.el +++ b/lisp/saveplace.el @@ -237,7 +237,7 @@ If `save-place-mode' is enabled, set the timer, otherwise cancel the timer." "The interval between auto saves of buffer places. If set to nil, disables timer-based auto saving." :type '(choice (const :tag "Disabled" nil) - (integer :tag "Seconds")) + (integer :tag "Auto-save interval in seconds")) :version "31.1" :set (lambda (sym val) (set-default sym val) commit 2b1440946e20bcf910f03cd665f68a268c687192 Author: Stéphane Marks Date: Mon Dec 15 16:07:14 2025 -0500 New optional recentf autosave timer (bug#80002) * etc/NEWS: Announce the new user option. * lisp/recentf.el (recentf-auto-cleanup): When canceling the auto-cleanup timer, set recentf-auto-cleanup-timer to nil to avoid false positive timerp. (recentf-autosave-interval): New defcustom. (recentf--autosave-timer, recentf--cancel-autosave-timer) (recentf--manage-autosave-timer): New defun. (recentf-mode): Call recentf--manage-autosave-timer. diff --git a/etc/NEWS b/etc/NEWS index 1c53e0789a6..311bf044476 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1535,6 +1535,19 @@ compatible. ** Recentf +--- +*** You can now regularly auto-save recently opened files. +Customize user option 'recentf-autosave-interval' to the number of +seconds between auto saving recently opened files. For example, +customize this variable to 300 to save recently opened files every 5 +minutes. In Elisp, use 'setopt' not 'setq'. + +If 'recentf-autosave-interval' is nil, auto saving is disabled; this is +the default. + +This user option is in sympathy with save-place, and savehist autosave +timers. + --- *** New user option 'recentf-show-messages'. 'recentf-save-list' can print a message when saving the recentf list. diff --git a/lisp/recentf.el b/lisp/recentf.el index 519a6e1dfec..4b9c1d3d270 100644 --- a/lisp/recentf.el +++ b/lisp/recentf.el @@ -393,7 +393,8 @@ See also the option `recentf-auto-cleanup'.") (defun recentf-auto-cleanup () "Automatic cleanup of the recent list." (when (timerp recentf-auto-cleanup-timer) - (cancel-timer recentf-auto-cleanup-timer)) + (cancel-timer recentf-auto-cleanup-timer) + (setq recentf-auto-cleanup-timer nil)) (when recentf-mode (setq recentf-auto-cleanup-timer (cond @@ -1353,6 +1354,41 @@ Optional argument N must be a valid digit number. It defaults to 1. ;;; Save/load/cleanup the recent list ;; +(defvar recentf--autosave-timer nil) + +(defun recentf--cancel-autosave-timer () + "Cancel `recentf--autosave-timer', if set." + (when (timerp recentf--autosave-timer) + (cancel-timer recentf--autosave-timer)) + (setq recentf--autosave-timer nil)) + +(defvar recentf-autosave-interval) + +(defun recentf--manage-autosave-timer () + "Set or cancel an invocation of `recentf-save-list' on a timer. +If `recentf-mode' is enabled, set the timer, otherwise cancel the timer." + (if (and recentf-mode + recentf-autosave-interval + (null recentf--autosave-timer)) + (setq recentf--autosave-timer + (run-with-timer + recentf-autosave-interval + recentf-autosave-interval #'recentf-save-list)) + (recentf--cancel-autosave-timer))) + +(defcustom recentf-autosave-interval nil + "The interval between auto saves of recently opened files. +If set to nil, disables timer-based auto saving. +Do not set this variable via `setq', use either `setopt' or +`customize-option' instead." + :type '(choice (const :tag "Disabled" nil) + (integer :tag "Auto-save interval in seconds")) + :version "31.1" + :set (lambda (sym val) + (set-default sym val) + (recentf--cancel-autosave-timer) + (recentf--manage-autosave-timer))) + (defconst recentf-save-file-header ;; FIXME: This should arguably be a `lisp-data' file, but currently ;; it contains and is used as an executable Elisp code. @@ -1469,6 +1505,7 @@ buffers you switch to a lot, you can say something like the following: (recentf-hide-menu) (recentf-save-list)) (recentf-auto-cleanup) + (recentf--manage-autosave-timer) (let ((hook-setup (if recentf-mode 'add-hook 'remove-hook))) (dolist (hook recentf-used-hooks) (apply hook-setup hook))))) commit 14f16535ed219f601a423f9a7e0d2adac30fa897 Author: Christoph Wedler Date: Wed Dec 17 12:27:46 2025 +0100 Fix init of auto/not-auto buffer-local vars in antlr-mode * progmodes/antlr-mode.el (antlr-mode): Fix initialization of variables, some are actually auto buffer-local and others not. Patch by Arash Esbati . diff --git a/lisp/progmodes/antlr-mode.el b/lisp/progmodes/antlr-mode.el index b31e44c3581..89da0e34148 100644 --- a/lisp/progmodes/antlr-mode.el +++ b/lisp/progmodes/antlr-mode.el @@ -3127,14 +3127,14 @@ This function is used in `hack-local-variables-hook'." (set (make-local-variable 'require-final-newline) mode-require-final-newline) (set (make-local-variable 'outline-regexp) "[^#\n\^M]") (set (make-local-variable 'outline-level) #'c-outline-level) ;TODO: define own - (setq comment-start "// " - comment-end "" - comment-start-skip "/\\*+ *\\|//+ *") + (setq-local comment-start "// " + comment-end "" + comment-start-skip "/\\*+ *\\|//+ *") ;; various ----------------------------------------------------------------- - (set (make-local-variable 'font-lock-defaults) antlr-font-lock-defaults) - (set (make-local-variable 'imenu-create-index-function) - #'antlr-imenu-create-index-function) - (set (make-local-variable 'imenu-generic-expression) t) ; fool stupid test + ;; the following vars are auto buffer-local: + (setq font-lock-defaults antlr-font-lock-defaults) + (setq imenu-create-index-function #'antlr-imenu-create-index-function) + (setq imenu-generic-expression t) ; fool stupid test ;; FIXME: How does this hook differ from `antlr-mode-hook'? (run-mode-hooks 'antlr-delayed-mode-hook)) commit 118b88dd22a46189be7c40560f7039db080cc8ae Author: João Távora Date: Wed Dec 17 09:28:07 2025 +0000 Eglot: fix thinko, group file watchers by base URI (bug#79809) It's fairly important to do this to reduce both the number of file-notify watches and the number of expensive project-files among "watcher" items pertaining to the same baseUri. * lisp/progmodes/eglot.el (eglot--watch-globs): Rename from eglot--watch-glob. Accept list of compiled glob/kind pairs. (eglot-register-capability): Group watchers by base-path before setting up watches. diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el index 4f7ef6b57ac..82cb95392fb 100644 --- a/lisp/progmodes/eglot.el +++ b/lisp/progmodes/eglot.el @@ -4320,32 +4320,33 @@ at point. With prefix argument, prompt for ACTION-KIND." (defvar eglot-watch-files-outside-project-root t "If non-nil, allow watching files outside project root") -(defun eglot--watch-glob (server id pat kind &optional base-path) - "Set up file watching for files matching PAT under BASEPATH. -PAT is a glob pattern, KIND is a bitmask of change types, -BASEPATH is the directory to watch (nil means entire project). +(defun eglot--watch-globs (server id globs &optional base-path) + "Set up file watching for files matching GLOBS under BASE-PATH. +GLOBS is a list of (COMPILED-GLOB . KIND) pairs, where COMPILED-GLOB +is a compiled glob predicate and KIND is a bitmask of change types. +BASE-PATH is the directory to watch (nil means entire project). Returns success status for SERVER and registration ID." (let* ((project (eglot--project server)) (dirs (delete-dups (mapcar #'file-name-directory (project-files project (and base-path (list base-path)))))) - (success nil) - (compiled (eglot--glob-compile pat t t))) + (success nil)) (cl-labels ((handle-event (event) (pcase-let* ((`(,desc ,action ,file ,file1) event) (action-type (cl-case action (created 1) (changed 2) (deleted 3))) (action-bit (when action-type - (ash 1 (1- action-type))))) + (ash 1 (1- action-type)))) + (candidate (if base-path + (file-relative-name file base-path) + file))) (cond ((and (memq action '(created changed deleted)) - (> (logand kind action-bit) 0) - (funcall compiled - (if base-path - (file-relative-name file base-path) - file))) + (cl-loop for (compiled . kind) in globs + thereis (and (> (logand kind action-bit) 0) + (funcall compiled candidate)))) (jsonrpc-notify server :workspace/didChangeWatchedFiles `(:changes ,(vector `(:uri ,(eglot-path-to-uri file) @@ -4373,29 +4374,36 @@ Returns success status for SERVER and registration ID." &aux (root (project-root (eglot--project server)))) "Handle dynamic registration of workspace/didChangeWatchedFiles." (eglot-unregister-capability server method id) - (mapc - (eglot--lambda ((FileSystemWatcher) ((:globPattern pat)) kind) - (pcase-let* - ((`(,pat ,base-uri) - (if (consp pat) - (list (plist-get pat :pattern) - (plist-get pat :baseUri)) - (list pat nil))) - (base-path - (when base-uri - (if (stringp base-uri) - (eglot-uri-to-path base-uri) - (eglot-uri-to-path (plist-get base-uri :uri)))))) - (when (or eglot-watch-files-outside-project-root - (null base-path) - (file-in-directory-p base-path root)) - (eglot--watch-glob server id pat - ;; the default "7" means bitwise OR of - ;; WatchKind.Create (1), WatchKind.Change - ;; (2), WatchKind.Delete (4) - (or kind 7) - base-path)))) - watchers)) + (let ((groups (make-hash-table :test 'equal))) + ;; Parse, compile, and group by base-path + (mapc + (eglot--lambda ((FileSystemWatcher) ((:globPattern pat)) kind) + (pcase-let* + ((`(,pat ,base-uri) + (if (consp pat) + (list (plist-get pat :pattern) + (plist-get pat :baseUri)) + (list pat nil))) + (base-path + (when base-uri + (if (stringp base-uri) + (eglot-uri-to-path base-uri) + (eglot-uri-to-path (plist-get base-uri :uri)))))) + (when (or eglot-watch-files-outside-project-root + (null base-path) + (file-in-directory-p base-path root)) + (push (cons (eglot--glob-compile pat t t) + ;; the default "7" means bitwise OR of + ;; WatchKind.Create (1), WatchKind.Change + ;; (2), WatchKind.Delete (4) + (or kind 7)) + (gethash base-path groups))))) + watchers) + ;; For each group, set up watches + (maphash + (lambda (base-path globs) + (eglot--watch-globs server id globs base-path)) + groups))) (cl-defmethod eglot-unregister-capability (server (_method (eql workspace/didChangeWatchedFiles)) id)