From 57a9798c22a6d8a75883dfcc1c4430be428d20bb Mon Sep 17 00:00:00 2001 From: Robert Pluim Date: Mon, 28 Jul 2025 14:11:50 +0200 Subject: [PATCH 001/216] Prefer "tls" to "ssl" in documentation * doc/misc/gnus.texi (NNTP): Refer to 'nntp-open-tls-stream'. (Direct Functions, Customizing the IMAP Connection): Add commentary about desirability of STARTTLS. Correct documentation about use of GnuTLS. Use 'tls in example. * lisp/gnus/nnimap.el (nnimap-server-port): Mention 'tls in preference to 'ssl. * lisp/gnus/nntp.el (nntp-open-connection-function) (nntp-never-echoes-commands): Document 'nntp-open-tls-stream' as preferred to 'nntp-open-ssl-stream'. --- doc/misc/gnus.texi | 53 ++++++++++++++++++++------------------------- lisp/gnus/nnimap.el | 6 ++--- lisp/gnus/nntp.el | 7 +++--- 3 files changed, 30 insertions(+), 36 deletions(-) diff --git a/doc/misc/gnus.texi b/doc/misc/gnus.texi index 13b4a339987..662f2f21301 100644 --- a/doc/misc/gnus.texi +++ b/doc/misc/gnus.texi @@ -14089,7 +14089,7 @@ indirect ones (three pre-made). Non-@code{nil} means the nntp server never echoes commands. It is reported that some nntps server doesn't echo commands. So, you may want to set this to non-@code{nil} in the method for such a server setting -@code{nntp-open-connection-function} to @code{nntp-open-ssl-stream} for +@code{nntp-open-connection-function} to @code{nntp-open-tls-stream} for example. The default value is @code{nil}. Note that the @code{nntp-open-connection-functions-never-echo-commands} variable overrides the @code{nil} value of this variable. @@ -14145,18 +14145,26 @@ functions is also affected by commonly understood variables @findex nntp-open-network-stream @item nntp-open-network-stream This is the default, and simply connects to some port or other on the -remote system. If both Emacs and the server supports it, the -connection will be upgraded to an encrypted @acronym{STARTTLS} -connection automatically. +remote system. If both Emacs and the server supports it, the connection +will be upgraded to an encrypted @acronym{STARTTLS} connection +automatically. If you want to avoid the possibility of a malicious +intermediary blocking the use of @acronym{STARTTLS}, use +@code{nntp-open-tls-stream} instead. -@item network-only -The same as the above, but don't do automatic @acronym{STARTTLS} upgrades. +@item nntp-open-plain-stream +@itemx network-only +The same as the above, but don't do automatic @acronym{STARTTLS} +upgrades. Only use this if you want anyone to be able to read your +traffic. @findex nntp-open-tls-stream @item nntp-open-tls-stream Opens a connection to a server over a @dfn{secure} channel. To use -this you must have @uref{https://www.gnu.org/software/gnutls/, GnuTLS} -installed. You then define a server as follows: +this, your Emacs must have been compiled with GnuTLS support +@uref{https://www.gnu.org/software/gnutls/, GnuTLS}. You can check this +using the @code{gnutls-available-p} command. + +You then define a server as follows: @lisp ;; @r{"nntps" is port 563 and is predefined in our @file{/etc/services}} @@ -14168,26 +14176,10 @@ installed. You then define a server as follows: (nntp-address "snews.bar.com")) @end lisp -@c FIXME openssl s_client should be deprecated in favor of gnutls. @findex nntp-open-ssl-stream @item nntp-open-ssl-stream -Opens a connection to a server over a @dfn{secure} channel. To use -this you must have @uref{https://www.openssl.org/, OpenSSL} -@ignore -@c Defunct URL, ancient package, so don't mention it. -or @uref{ftp://ftp.psy.uq.oz.au/pub/Crypto/SSL, SSLeay} -@end ignore -installed. You then define a server as follows: - -@lisp -;; @r{"snews" is port 563 and is predefined in our @file{/etc/services}} -;; @r{however, @samp{openssl s_client -port} doesn't like named ports.} -;; -(nntp "snews.bar.com" - (nntp-open-connection-function nntp-open-ssl-stream) - (nntp-port-number 563) - (nntp-address "snews.bar.com")) -@end lisp +This is the old name for @code{nntp-open-tls-stream}, and is +completely equivalent. @findex nntp-open-netcat-stream @item nntp-open-netcat-stream @@ -14529,7 +14521,7 @@ Here's an example method that's more complex: (nnimap-inbox "INBOX") (nnimap-split-methods default) (nnimap-expunge t) - (nnimap-stream ssl)) + (nnimap-stream tls)) @end example @table @code @@ -14555,11 +14547,12 @@ How @code{nnimap} should connect to the server. Possible values are: @table @code @item undecided -This is the default, and this first tries the @code{ssl} setting, and +This is the default, and this first tries the @code{tls} setting, and then tries the @code{network} setting. -@item ssl -This uses standard @acronym{TLS}/@acronym{SSL} connections. +@item tls +This uses standard @acronym{TLS}/@acronym{SSL} connections. @code{ssl} +is an equivalent but deprecated way to specify this. @item network Non-encrypted and unsafe straight socket connection, but will upgrade diff --git a/lisp/gnus/nnimap.el b/lisp/gnus/nnimap.el index 215e8ac4cbf..f813d513180 100644 --- a/lisp/gnus/nnimap.el +++ b/lisp/gnus/nnimap.el @@ -51,7 +51,7 @@ (defvoo nnimap-server-port nil "The IMAP port used. -If `nnimap-stream' is `ssl', this will default to `imaps'. If not, +If `nnimap-stream' is `tls', this will default to `imaps'. If not, it will default to `imap'.") (defvoo nnimap-use-namespaces nil @@ -63,10 +63,10 @@ names of your nnimap groups.") (defvoo nnimap-stream 'undecided "How nnimap talks to the IMAP server. -The value should be either `undecided', `ssl' or `tls', +The value should be either `undecided', `tls' or `ssl' (deprecated), `network', `starttls', `plain', or `shell'. -If the value is `undecided', nnimap tries `ssl' first, then falls +If the value is `undecided', nnimap tries `tls' first, then falls back on `network'.") (defvoo nnimap-shell-program (if (boundp 'imap-shell-program) diff --git a/lisp/gnus/nntp.el b/lisp/gnus/nntp.el index a086421b049..0d0a0e679fa 100644 --- a/lisp/gnus/nntp.el +++ b/lisp/gnus/nntp.el @@ -82,8 +82,9 @@ as its single argument, or one of the following special values: upgrading to a TLS connection via STARTTLS if possible. - `nntp-open-plain-stream' specifies an unencrypted network connection (no STARTTLS upgrade is attempted). -- `nntp-open-ssl-stream' or `nntp-open-tls-stream' specify a TLS - network connection. +- `nntp-open-tls-stream' specifies a TLS network connection (the + equivalent value `nntp-open-ssl-stream' is accepted for backwards + compatibility). Apart from the above special values, valid functions are as follows; please refer to their respective doc string for more @@ -100,7 +101,7 @@ For indirect connections: "Non-nil means the nntp server never echoes commands. It is reported that some nntps server doesn't echo commands. So, you may want to set this to non-nil in the method for such a server setting -`nntp-open-connection-function' to `nntp-open-ssl-stream' for example. +`nntp-open-connection-function' to `nntp-open-tls-stream' for example. Note that the `nntp-open-connection-functions-never-echo-commands' variable overrides the nil value of this variable.") From 8ec2ddebdd4ff92aa07a9a7a0881bd3e762acee3 Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Mon, 28 Jul 2025 16:14:44 +0300 Subject: [PATCH 002/216] ; Update documentation of GC in ELisp manual * doc/lispref/internals.texi (Garbage Collection): Update default values for GC-related thresholds. (Bug#79074) --- doc/lispref/internals.texi | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/doc/lispref/internals.texi b/doc/lispref/internals.texi index cc82a03db98..939cd489241 100644 --- a/doc/lispref/internals.texi +++ b/doc/lispref/internals.texi @@ -555,16 +555,17 @@ object type; space allocated to the contents of buffers does not count. The initial threshold value is @code{GC_DEFAULT_THRESHOLD}, defined in @file{alloc.c}. Since it's defined in @code{word_size} units, the -value is 400,000 for the default 32-bit configuration and 800,000 for -the 64-bit one. If you specify a larger value, garbage collection -will happen less often. This reduces the amount of time spent garbage -collecting (so Lisp programs will run faster between cycles of garbage -collection that happen more rarely), but increases total memory use. -You may want to do this when running a program that creates lots of -Lisp data, especially if you need it to run faster. However, we -recommend against increasing the threshold for prolonged periods of -time, and advise that you never set it higher than needed for the -program to run in reasonable time. Using thresholds higher than +value is 400,000 for the default 32-bit configuration, and 800,000 for +the 64-bit one and for 32-bit builds configured with the +@option{--with-wide-int} option. If you specify a larger value, garbage +collection will happen less often. This reduces the amount of time +spent garbage collecting (so Lisp programs will run faster between +cycles of garbage collection that happen more rarely), but increases +total memory use. You may want to do this when running a program that +creates lots of Lisp data, especially if you need it to run faster. +However, we recommend against increasing the threshold for prolonged +periods of time, and advise that you never set it higher than needed for +the program to run in reasonable time. Using thresholds higher than necessary could potentially cause higher system-wide memory pressure, and also make each garbage-collection cycle take much more time, and should therefore be avoided. @@ -585,6 +586,10 @@ As the heap size increases, the time to perform a garbage collection increases. Thus, it can be desirable to do them less frequently in proportion. +The initial percentage value is 0.1 in interactive sessions and while +dumping Emacs (@pxref{Building Emacs}), and 1.0 in non-interactive +(a.k.a.@: ``batch'') sessions. + As with @code{gc-cons-threshold}, do not enlarge this more than necessary, and never for prolonged periods of time. @end defopt From f20d5e63bc052985c5681e583ca48309f44ff515 Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Tue, 29 Jul 2025 14:08:29 +0300 Subject: [PATCH 003/216] ; * lisp/image.el (image-supported-file-p): Doc fix (bug#79117). --- lisp/image.el | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lisp/image.el b/lisp/image.el index 0be57cadb2b..2432b5727ce 100644 --- a/lisp/image.el +++ b/lisp/image.el @@ -414,10 +414,10 @@ be determined." ;;;###autoload (defun image-supported-file-p (file) - "Say whether Emacs has native support for displaying TYPE. -The value is a symbol specifying the image type, or nil if type -cannot be determined (or if Emacs doesn't have built-in support -for the image type)." + "Return non-nil if Emacs can display the specified image FILE. +The returned value is a symbol specifying the image type of FILE, +or nil if Emacs cannot display that image type or if the type +cannot be determined." (let ((case-fold-search t) type) (catch 'found From 456f44a7b3cee7ac6dd3b13dc6690d14f5dd3603 Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Wed, 30 Jul 2025 19:48:07 +0300 Subject: [PATCH 004/216] ; Improve documentation of change hooks * doc/lispref/text.texi (Change Hooks): Warn against buffer changes in the hook functions. (Bug#79115) --- doc/lispref/text.texi | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/doc/lispref/text.texi b/doc/lispref/text.texi index 8329a67d6c2..dc35e93f2da 100644 --- a/doc/lispref/text.texi +++ b/doc/lispref/text.texi @@ -6291,7 +6291,11 @@ specific parts of the text. The functions you use in these hooks should save and restore the match data if they do anything that uses regular expressions; otherwise, they will interfere in bizarre ways with the editing operations that call -them. +them. In addition, the functions in these hooks should avoid changing +buffer text, faces, properties, overlays, and other aspects of the +buffer-specific state except those that the hook functions themselves +create and manage, because other parts of Emacs might become confused by +such changes behind their back. @defvar before-change-functions This variable holds a list of functions to call when Emacs is about to From 9e3720bbb116193c7866ae04d35afa34f5451fa6 Mon Sep 17 00:00:00 2001 From: James Thomas Date: Wed, 30 Jul 2025 01:54:43 +0530 Subject: [PATCH 005/216] * doc/misc/gnus.texi (Category Syntax): Update gnus-agent-predicate. This updates the Gnus manual due to recent code change (bug#79123). --- doc/misc/gnus.texi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/misc/gnus.texi b/doc/misc/gnus.texi index 662f2f21301..dfeecb079c7 100644 --- a/doc/misc/gnus.texi +++ b/doc/misc/gnus.texi @@ -18864,7 +18864,7 @@ operators sprinkled in between. Perhaps some examples are in order. -Here's a simple predicate. (It's the default predicate, in fact, used +Here's a simple predicate. (The default predicate is @code{false}, used for all groups that don't belong to any other category.) @lisp From 38557cac74780b3bb8774accae120703358ed5e6 Mon Sep 17 00:00:00 2001 From: Robert Pluim Date: Wed, 30 Jul 2025 09:55:16 +0200 Subject: [PATCH 006/216] * lisp/emacs-lisp/shortdoc.el (sequence): Add 'seq-doseq'. --- lisp/emacs-lisp/shortdoc.el | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lisp/emacs-lisp/shortdoc.el b/lisp/emacs-lisp/shortdoc.el index 2e826399261..754b2016d9e 100644 --- a/lisp/emacs-lisp/shortdoc.el +++ b/lisp/emacs-lisp/shortdoc.el @@ -1131,6 +1131,9 @@ A FUNC form can have any number of `:no-eval' (or `:no-value'), :eval (seq-map-indexed (lambda (a i) (cons i a)) '(a b c))) (seq-mapcat :eval (seq-mapcat #'upcase '("a" "b" "c") 'string)) + (seq-doseq + :no-eval (seq-doseq (a '("foo" "bar")) (insert a)) + :eg-result ("foo" "bar")) (seq-do :no-eval (seq-do (lambda (a) (insert a)) '("foo" "bar")) :eg-result ("foo" "bar")) From 7d77ae98d4de8e09a033ecd9e9b267524a294a84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?El=C3=ADas=20Gabriel=20P=C3=A9rez?= Date: Sat, 12 Jul 2025 13:16:50 -0600 Subject: [PATCH 007/216] tab-line: Add close button for modified tabs (bug#79046) * etc/NEWS: Announce changes. * etc/images/tabs/close-modified.xpm: New image. * etc/images/tabs/README (Files): Add button icon name to README. * lisp/tab-line.el (tab-line-close-modified-button-show): New user option. (tab-line-close-modified): Define new icon. (tab-line-close-modified-button): New variable. (tab-line-tab-modified-p): New function. (tab-line-tab-name-format-default): Rework. (tab-line-tab-face-modified): Use 'tab-line-tab-modified-p'. --- etc/NEWS | 5 ++++ etc/images/tabs/README | 4 +++ etc/images/tabs/close-modified.xpm | 16 +++++++++++ lisp/tab-line.el | 46 +++++++++++++++++++++++++++--- 4 files changed, 67 insertions(+), 4 deletions(-) create mode 100644 etc/images/tabs/close-modified.xpm diff --git a/etc/NEWS b/etc/NEWS index b790c7e318c..462098a0238 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -427,6 +427,11 @@ This user option controls where 'tab-line-mode' should not be enabled in a buffer. The value must be a condition which is passed to 'buffer-match-p'. +--- +*** New user option 'tab-line-close-modified-button-show'. +With this user option, if non-nil (the default), the tab close button +will change its appearance whether the tab buffer has been modified. + ** Project --- diff --git a/etc/images/tabs/README b/etc/images/tabs/README index c3bab00d56c..2a68577951f 100644 --- a/etc/images/tabs/README +++ b/etc/images/tabs/README @@ -4,5 +4,9 @@ COPYRIGHT AND LICENSE INFORMATION FOR IMAGE FILES Files: close.xpm new.xpm left-arrow.xpm right-arrow.xpm Author: Juri Linkov + +Files: close-modified.xpm +Author: Elías Gabriel Pérez + Copyright (C) 2019-2025 Free Software Foundation, Inc. License: GNU General Public License version 3 or later (see COPYING) diff --git a/etc/images/tabs/close-modified.xpm b/etc/images/tabs/close-modified.xpm new file mode 100644 index 00000000000..44b45d49011 --- /dev/null +++ b/etc/images/tabs/close-modified.xpm @@ -0,0 +1,16 @@ +/* XPM */ +static char *dummy[]={ +"9 9 4 1", +". c None", +"b c #000000", +"a c #808080", +"# c #bfbfbf", +"..#####..", +".#######.", +"###aaa###", +"##abbba##", +"##abbba##", +"##abbba##", +"###aaa###", +".#######.", +"..#####.."}; diff --git a/lisp/tab-line.el b/lisp/tab-line.el index 5ed32ef2ed9..5e33064b5b1 100644 --- a/lisp/tab-line.el +++ b/lisp/tab-line.el @@ -251,6 +251,36 @@ If nil, don't show it at all." 'help-echo "Click to close tab") "Button for closing the clicked tab.") +(defcustom tab-line-close-modified-button-show t + "If non-nil, the close button appearance will change when its buffer is modified." + :type 'boolean + :initialize 'custom-initialize-default + :set (lambda (sym val) + (set-default sym val) + (force-mode-line-update t)) + :group 'tab-line + :version "31.1") + +(define-icon tab-line-close-modified nil + `((image "symbols/dot_medium_16.svg" "tabs/close-modified.xpm" + :face shadow + :height (1 . em) + :margin (2 . 0) + :ascent center) + (symbol ,(concat " " [#x2022])) ; bullet + (text " *")) + "Icon for closing the clicked tab when tab is modified." + :version "31.1" + :help-echo "Click to close tab") + +(defvar tab-line-close-modified-button + (propertize (icon-string 'tab-line-close-modified) + 'rear-nonsticky nil + 'keymap tab-line-tab-close-map + 'mouse-face 'tab-line-close-highlight + 'help-echo "Click to close tab") + "Button for closing the clicked tab when tab is modified.") + (define-icon tab-line-left nil `((image "symbols/chevron_left_16.svg" "tabs/left-arrow.xpm" :face shadow @@ -387,6 +417,12 @@ Used only for `tab-line-tabs-mode-buffers' and `tab-line-tabs-buffer-groups'.") (derived-mode-p mode))) (funcall tab-line-tabs-buffer-list-function))))) +(defun tab-line-tab-modified-p (tab buffer-p) + "Return t if TAB is modified." + (let ((buffer (if buffer-p tab (cdr (assq 'buffer tab))))) + (when (and buffer (buffer-file-name buffer) (buffer-modified-p buffer)) + t))) + (defcustom tab-line-tabs-buffer-group-function #'tab-line-tabs-buffer-group-by-mode "Function to add a buffer to the appropriate group of tabs. @@ -619,7 +655,10 @@ using `tab-line-cache-key-function'." (not (eq tab-line-close-button-show (if selected-p 'non-selected 'selected))) - tab-line-close-button) + (if (and tab-line-close-modified-button-show + (tab-line-tab-modified-p tab buffer-p)) + tab-line-close-modified-button + tab-line-close-button)) ""))) (setq close (copy-sequence close)) ;; Don't overwrite the icon face @@ -687,9 +726,8 @@ When TAB is a non-file-visiting buffer, make FACE inherit from When TAB is a modified, file-backed buffer, make FACE inherit from `tab-line-tab-modified'. For use in `tab-line-tab-face-functions'." - (let ((buffer (if buffer-p tab (cdr (assq 'buffer tab))))) - (when (and buffer (buffer-file-name buffer) (buffer-modified-p buffer)) - (setf face `(:inherit (tab-line-tab-modified ,face))))) + (when (tab-line-tab-modified-p tab buffer-p) + (setf face `(:inherit (tab-line-tab-modified ,face)))) face) (defun tab-line-tab-face-group (tab _tabs face _buffer-p _selected-p) From a6e0bb92bb0c114fc8d57864455330d04eac064e Mon Sep 17 00:00:00 2001 From: Juri Linkov Date: Thu, 31 Jul 2025 21:47:22 +0300 Subject: [PATCH 008/216] * lisp/tab-line.el (tab-line-cache-key-default): Adapt to new feature. Take into account 'tab-line-close-modified-button-show' similarly to the case when 'tab-line-tab-face-modified' is a member of 'tab-line-tab-face-functions' (bug#79046). --- lisp/tab-line.el | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lisp/tab-line.el b/lisp/tab-line.el index 5e33064b5b1..51675dbbb29 100644 --- a/lisp/tab-line.el +++ b/lisp/tab-line.el @@ -762,8 +762,9 @@ it clears the tab-line cache of all tab lines and forces their redisplay." ;; for setting face 'tab-line-tab-current' (mode-line-window-selected-p) ;; for `tab-line-tab-face-modified' - (and (memq 'tab-line-tab-face-modified - tab-line-tab-face-functions) + (and (or tab-line-close-modified-button-show + (memq 'tab-line-tab-face-modified + tab-line-tab-face-functions)) (buffer-file-name) (buffer-modified-p)))) From 6e1054a40bf6df1429a2b16fdd0d7652dae4d537 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rudolf=20Adamkovi=C4=8D?= Date: Mon, 17 Mar 2025 02:25:21 +0100 Subject: [PATCH 009/216] NS: Correct the accessibility role of Emacs windows (bug#77062) Make the accessibility role of Emacs windows to be "standard window" (AXWindow) instead of "text field" (AXTextField). This matches other applications on NS, as can be verified with any accessibility inspector. All widely used NS ports, such as Emacs Plus, have this patch already applied. As for practical impact, this change makes Emacs, for example, work with tiling window managers, such as Yabai, where many users report problems with Emacs specifically, and are instructed to simply avoid the official NS port of GNU Emacs due to this problem. * src/nsterm.m: ([EmacsWindow accessibilityAttributeValue:]): Make the accessibility role of Emacs windows to be "standard window" (AXWindow) instead of "text field" (AXTextField). --- src/nsterm.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nsterm.m b/src/nsterm.m index 30a35bdd5b2..b006b4d5dd0 100644 --- a/src/nsterm.m +++ b/src/nsterm.m @@ -9830,7 +9830,7 @@ nswindow_orderedIndex_sort (id w1, id w2, void *c) NSTRACE ("[EmacsWindow accessibilityAttributeValue:]"); if ([attribute isEqualToString:NSAccessibilityRoleAttribute]) - return NSAccessibilityTextFieldRole; + return NSAccessibilityWindowRole; if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute] && curbuf && ! NILP (BVAR (curbuf, mark_active))) From 91dc27c1999101b6fa77e5ab43c3ffda28e2ca00 Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Fri, 1 Aug 2025 08:25:50 +0300 Subject: [PATCH 010/216] ; * etc/NEWS: Fix wording of a recently-added entry. --- etc/NEWS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etc/NEWS b/etc/NEWS index 462098a0238..36a9bf39d55 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -430,7 +430,7 @@ a buffer. The value must be a condition which is passed to --- *** New user option 'tab-line-close-modified-button-show'. With this user option, if non-nil (the default), the tab close button -will change its appearance whether the tab buffer has been modified. +will change its appearance if the tab buffer has been modified. ** Project From 17c54bed4be071e4c8290fdb1f517d810527bc98 Mon Sep 17 00:00:00 2001 From: Jonas Bernoulli Date: Fri, 1 Aug 2025 19:38:56 +0200 Subject: [PATCH 011/216] Update to Transient v0.9.4-8-g6bc543d5 --- doc/misc/transient.texi | 4 ++-- lisp/transient.el | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/doc/misc/transient.texi b/doc/misc/transient.texi index fe7f81c7930..4aa0ebf97d6 100644 --- a/doc/misc/transient.texi +++ b/doc/misc/transient.texi @@ -31,7 +31,7 @@ General Public License for more details. @finalout @titlepage @title Transient User and Developer Manual -@subtitle for version 0.9.3 +@subtitle for version 0.9.4 @author Jonas Bernoulli @page @vskip 0pt plus 1filll @@ -53,7 +53,7 @@ resource to get over that hurdle is Psionic K's interactive tutorial, available at @uref{https://github.com/positron-solutions/transient-showcase}. @noindent -This manual is for Transient version 0.9.3. +This manual is for Transient version 0.9.4. @insertcopying @end ifnottex diff --git a/lisp/transient.el b/lisp/transient.el index 100a7682e4d..44c6e6ce550 100644 --- a/lisp/transient.el +++ b/lisp/transient.el @@ -5,7 +5,7 @@ ;; Author: Jonas Bernoulli ;; URL: https://github.com/magit/transient ;; Keywords: extensions -;; Version: 0.9.3 +;; Version: 0.9.4 ;; SPDX-License-Identifier: GPL-3.0-or-later @@ -33,7 +33,7 @@ ;;; Code: ;;;; Frontmatter -(defconst transient-version "v0.9.3-8-g6fd0239e-builtin") +(defconst transient-version "v0.9.4-8-g6bc543d5-builtin") (require 'cl-lib) (require 'eieio) @@ -1519,7 +1519,8 @@ Intended for use in a group's `:setup-children' function." :command))) (equal (transient--suffix-predicate suf) (transient--suffix-predicate conflict))))) - (transient-remove-suffix prefix key)) + (transient-remove-suffix prefix key) + (pcase-setq `(,elt ,group) (transient--locate-child prefix loc))) (let ((mem (memq elt (aref group 2)))) (pcase-exhaustive action ('insert (setcdr mem (cons elt (cdr mem))) From adc05bead36d92030bd3fc5f5064c9d67a6289f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20Engdeg=C3=A5rd?= Date: Sat, 2 Aug 2025 13:13:58 +0200 Subject: [PATCH 012/216] ; * etc/NEWS: Move read-directory-name entry; a compatible change. --- etc/NEWS | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/etc/NEWS b/etc/NEWS index 36a9bf39d55..7a4eab4800d 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -2518,9 +2518,6 @@ enabled for files named "go.work". ** The 'rx' category name 'chinese-two-byte' must now be spelled correctly. An old alternative name (without the first 'e') has been removed. -+++ -** 'read-directory-name' now accepts an optional PREDICATE argument. - +++ ** 'text-property-default-nonsticky' is now buffer-local. This variable now becomes buffer-local when set. Use 'setq-default' in @@ -2840,6 +2837,9 @@ option, but can optionally return the equivalent of 'exec-suffixes' from a remote host. It must be used in conjunction with the function 'exec-path'. ++++ +** 'read-directory-name' now accepts an optional PREDICATE argument. + * Changes in Emacs 31.1 on Non-Free Operating Systems From 836b2bb8b4c3c4ec5823305b5da9a4c5aac505bb Mon Sep 17 00:00:00 2001 From: Daniel Mendler Date: Sat, 2 Aug 2025 13:07:58 +0200 Subject: [PATCH 013/216] ibuffer-update-title-and-summary: Fix out of range error When `ibuffer-use-header-line' is set to `title' and the buffer list contains no buffers due to a filter, `ibuffer-update-title-and-summary' would error with an "Args out of range: 0, 0" error. * lisp/ibuffer.el (ibuffer-update-title-and-summary): Check if buffer is empty to prevent out of range error. (Bug#79152) --- lisp/ibuffer.el | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lisp/ibuffer.el b/lisp/ibuffer.el index 95deb2db478..ae7e8dd4117 100644 --- a/lisp/ibuffer.el +++ b/lisp/ibuffer.el @@ -2136,7 +2136,8 @@ the value of point at the beginning of the line for that buffer." `(ibuffer-title t font-lock-face ,ibuffer-title-face))) ;; Now, insert the summary columns. (goto-char (point-max)) - (if (get-text-property (1- (point-max)) 'ibuffer-summary) + (if (and (> (point-max) (point-min)) + (get-text-property (1- (point-max)) 'ibuffer-summary)) (delete-region (previous-single-property-change (point-max) 'ibuffer-summary) (point-max))) From 5979d17ea94d5fbc1597821a36759f41eef1c282 Mon Sep 17 00:00:00 2001 From: Antero Mejr Date: Sat, 26 Jul 2025 15:13:15 +0000 Subject: [PATCH 014/216] Fix index error when checking edited bug reports * lisp/mail/emacsbug.el (report-emacs-bug-check-org): Improve regex check. (Bug#79080) --- lisp/mail/emacsbug.el | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/lisp/mail/emacsbug.el b/lisp/mail/emacsbug.el index 5ffb86e68b6..d43647a12ca 100644 --- a/lisp/mail/emacsbug.el +++ b/lisp/mail/emacsbug.el @@ -427,13 +427,16 @@ copy text to your preferred mail program.\n" (defun report-emacs-bug-check-org () "Warn the user if the bug report mentions org-mode." (unless report-emacs-bug-no-confirmation - (goto-char (point-max)) - (skip-chars-backward " \t\n") - (let* ((text (buffer-substring-no-properties (point-min) (point))) - (l (length report-emacs-bug-orig-text)) - (text (substring text 0 l)) - (org-regex "\\b[Oo]rg\\(-mode\\)?\\b")) - (when (string-match-p org-regex text) + (let* ((org-regex "\\(^\\|\\s-\\)[Oo]rg\\(-mode\\)?\\(\\s-\\|$\\)") + (count (lambda (r s) + (let ((c 0) (start 0)) + (while (string-match r s start) + (setq c (1+ c)) + (setq start (match-end 0))) + c))) + (m (funcall count org-regex (buffer-string))) + (m-orig (funcall count org-regex report-emacs-bug-orig-text))) + (when (> m m-orig) (when (yes-or-no-p "Is this bug about org-mode?") (error (substitute-command-keys "\ Not sending, use \\[org-submit-bug-report] to report an Org-mode bug."))))))) From 5140e317b75d0467bfbbabb5d12404568fee1bb7 Mon Sep 17 00:00:00 2001 From: Steven Allen Date: Thu, 17 Jul 2025 11:14:45 -0700 Subject: [PATCH 015/216] url: %-encode literal % characters when building query strings When building a query string via `url-build-query-string', %-encode literal % characters appearing in both the keys and the values. * lisp/url/url-util.el (url--query-key-value-preserved-chars): Define a new constant based on `url-query-key-value-allowed-chars' specifying the characters that should be preserved when %-encoding query-string keys and values. (url-build-query-string): Use the new constant (fixes Bug#78984). * test/lisp/url/url-util-tests.el (url-util-tests): Add a test. --- lisp/url/url-util.el | 9 ++++++++- test/lisp/url/url-util-tests.el | 8 ++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/lisp/url/url-util.el b/lisp/url/url-util.el index 8435114bbc2..47ef8811c5e 100644 --- a/lisp/url/url-util.el +++ b/lisp/url/url-util.el @@ -409,6 +409,13 @@ should return it unchanged." (url-hexify-string frag url-query-allowed-chars))) (url-recreate-url obj))) +(defconst url--query-key-value-preserved-chars + (let ((vec (copy-sequence url-query-key-value-allowed-chars))) + (aset vec ?% nil) + vec) + "Byte mask used for %-encoding keys and values in the query segment of a URI. +`url-query-key-value-allowed-chars' minus '%'.") + ;;;###autoload (defun url-build-query-string (query &optional semicolons keep-empty) "Build a query-string. @@ -436,7 +443,7 @@ instead of just \"key\" as in the example above." (let ((escaped (mapcar (lambda (sym) (url-hexify-string (format "%s" sym) - url-query-key-value-allowed-chars)) + url--query-key-value-preserved-chars)) key-vals))) (mapconcat (lambda (val) (let ((vprint (format "%s" val)) diff --git a/test/lisp/url/url-util-tests.el b/test/lisp/url/url-util-tests.el index d34bfc8509c..6e79a798333 100644 --- a/test/lisp/url/url-util-tests.el +++ b/test/lisp/url/url-util-tests.el @@ -33,10 +33,10 @@ ((key1 "val1") (key2 val2) (key3 val1 val2) ("key4") (key5 "")) t) ("key1=val1;key2=val2;key3=val1;key3=val2;key4=;key5=" ((key1 val1) (key2 val2) ("key3" val1 val2) (key4) (key5 "")) t t) - ("key1=val/slash;key2=val%3Bsemi;key3=val%26amp;key4=val%3Deq" - ((key1 "val/slash") (key2 "val;semi") (key3 "val&") (key4 "val=eq")) t) - ("key%3Deq=val1;key%3Bsemi=val2;key%26amp=val3" - (("key=eq" val1) ("key;semi" val2) ("key&" val3)) t))) + ("key1=val/slash;key2=val%3Bsemi;key3=val%26amp;key4=val%3Deq;key5=val%25perc" + ((key1 "val/slash") (key2 "val;semi") (key3 "val&") (key4 "val=eq") (key5 "val%perc")) t) + ("key%3Deq=val1;key%3Bsemi=val2;key%26amp=val3;key%25perc=val4" + (("key=eq" val1) ("key;semi" val2) ("key&" val3) ("key%perc" val4)) t))) test) (while tests (setq test (car tests) From 2846dd8842cc6d6e9ff6395a4822456f130ce4a8 Mon Sep 17 00:00:00 2001 From: Manuel Giraud Date: Sat, 5 Jul 2025 10:56:00 +0200 Subject: [PATCH 016/216] Fix mode-line string width in diary view Bug#78957 * lisp/calendar/calendar.el (calendar-in-read-only-buffer): Select the buffer window because, for example, `calendar-set-mode-line' needs it. --- lisp/calendar/calendar.el | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/lisp/calendar/calendar.el b/lisp/calendar/calendar.el index 917624c489e..04bbc2cb8a4 100644 --- a/lisp/calendar/calendar.el +++ b/lisp/calendar/calendar.el @@ -1161,17 +1161,20 @@ MON defaults to `displayed-month'. YR defaults to `displayed-year'." First creates or erases BUFFER as needed. Leaves BUFFER read-only, with disabled undo. Leaves point at `point-min', displays BUFFER." (declare (indent 1) (debug t)) - `(progn - (set-buffer (get-buffer-create ,buffer)) - (or (derived-mode-p 'special-mode) (special-mode)) - (setq buffer-read-only nil - buffer-undo-list t) - (erase-buffer) - (display-buffer ,buffer) - ,@body - (goto-char (point-min)) - (set-buffer-modified-p nil) - (setq buffer-read-only t))) + (let ((window (gensym))) + `(progn + (set-buffer (get-buffer-create ,buffer)) + (or (derived-mode-p 'special-mode) (special-mode)) + (setq buffer-read-only nil + buffer-undo-list t) + (erase-buffer) + (let ((,window (display-buffer ,buffer))) + (when ,window + (with-selected-window ,window + ,@body + (goto-char (point-min))))) + (set-buffer-modified-p nil) + (setq buffer-read-only t)))) ;; The following are in-line for speed; they can be called thousands of times ;; when looking up holidays or processing the diary. Here, for example, are From e52ed1b5d1531e7dc15f3f8ddf7042b0a7e71f50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Je=C4=8Dm=C3=ADnek?= Date: Sun, 29 Jun 2025 23:56:13 +0200 Subject: [PATCH 017/216] Improve region deletion handling in python.el * lisp/progmodes/python.el (python-indent-dedent-line-backspace): Delete the text in the region if Transient Mark mode is enabled, the mark is active, and prefix arg is 1. (Bug#48695) * test/lisp/progmodes/python-tests.el (python-indent-dedent-line-backspace-4): Add new test. --- etc/NEWS | 7 +++++++ lisp/progmodes/python.el | 9 ++++++--- test/lisp/progmodes/python-tests.el | 18 ++++++++++++++++++ 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/etc/NEWS b/etc/NEWS index 7a4eab4800d..3b9e06f99d9 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1822,6 +1822,13 @@ restart Emacs. --- *** Support of 'electric-layout-mode' added. +--- +*** DEL now deletes the text in the active region when point is between indentation. +The command 'python-indent-dedent-line-backspace' (by default bound to +'DEL') now deletes the text in the region and deactivates the mark if +Transient Mark mode is enabled, the mark is active, and prefix argument +is 1. + ** Tmm Menubar --- diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index f4f0518dbfd..8b5ffae57d6 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -1967,10 +1967,13 @@ indentation levels from right to left." (defun python-indent-dedent-line-backspace (arg) "De-indent current line. -Argument ARG is passed to `backward-delete-char-untabify' when -point is not in between the indentation." +Argument ARG is passed to `backward-delete-char-untabify' when point is +not in between the indentation or when Transient Mark mode is enabled, +the mark is active, and ARG is 1." (interactive "*p") - (unless (python-indent-dedent-line) + (when (or + (and (use-region-p) (= arg 1)) + (not (python-indent-dedent-line))) (backward-delete-char-untabify arg))) (put 'python-indent-dedent-line-backspace 'delete-selection 'supersede) diff --git a/test/lisp/progmodes/python-tests.el b/test/lisp/progmodes/python-tests.el index 2c00f47d82c..d65ef39abb4 100644 --- a/test/lisp/progmodes/python-tests.el +++ b/test/lisp/progmodes/python-tests.el @@ -3780,6 +3780,24 @@ if x: (pos-bol) (pos-eol)) "abcdef"))))) +(ert-deftest python-indent-dedent-line-backspace-4 () + "Delete the text in the region instead of de-indentation. Bug#48695." + (dolist (test '((1 4) (2 0))) + (python-tests-with-temp-buffer + " +if True: + x () + if False: +" + (let ((current-prefix-arg (list (car test)))) + (python-tests-look-at "if False:") + (end-of-line) + (transient-mark-mode) + (set-mark (point)) + (backward-word 2) + (call-interactively #'python-indent-dedent-line-backspace) + (should (= (current-indentation) (cadr test))))))) + (ert-deftest python-bob-infloop-avoid () "Test that strings at BOB don't confuse syntax analysis. Bug#24905" (python-tests-with-temp-buffer From cd31c3cb423baaaa9f7f3fa9eab274c09f2db1e9 Mon Sep 17 00:00:00 2001 From: James Thomas Date: Mon, 16 Jun 2025 01:20:31 +0530 Subject: [PATCH 018/216] Add 'other-window-backward' for a default binding (bug#78803) This moves in the opposite direction to 'other-window' by default, and is consistent with its repeat map. * lisp/window.el (other-window-backward): New function bound to 'C-x O'. (ctl-x-map): Update definition. * doc/emacs/windows.texi (Other Window): Update docs. * etc/NEWS: Annouce the new command. --- doc/emacs/windows.texi | 5 ++++- etc/NEWS | 4 ++++ lisp/window.el | 14 ++++++++++---- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/doc/emacs/windows.texi b/doc/emacs/windows.texi index 9473f189c25..4f04dc542eb 100644 --- a/doc/emacs/windows.texi +++ b/doc/emacs/windows.texi @@ -158,6 +158,8 @@ this option is @code{nil}. @table @kbd @item C-x o Select another window (@code{other-window}). +@item C-x O +Select another window, backwards (@code{other-window-backward}). @item C-M-v Scroll the next window upward (@code{scroll-other-window}). @item C-M-S-v @@ -183,7 +185,8 @@ cycle in the opposite order. When the minibuffer is active, the minibuffer window is the last window in the cycle; you can switch from the minibuffer window to one of the other windows, and later switch back and finish supplying the minibuffer argument that is requested. -@xref{Minibuffer Edit}. +@xref{Minibuffer Edit}. @kbd{C-x O} is similar, but defaults to the +opposite order. @findex next-window-any-frame The @code{other-window} command will normally only switch to the next diff --git a/etc/NEWS b/etc/NEWS index 3b9e06f99d9..e12a9dcb127 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -333,6 +333,10 @@ helps to restore window buffers across Emacs sessions. You can use this in 'display-buffer-alist' to match buffers displayed during the execution of particular commands. +*** New command 'other-window-backward' ('C-x O'). +This moves in the opposite direction of 'other-window' and is for its +default keybinding consistent with 'repeat-mode'. + ** Frames +++ diff --git a/lisp/window.el b/lisp/window.el index 44cb682d6dd..512e85dc29a 100644 --- a/lisp/window.el +++ b/lisp/window.el @@ -4073,6 +4073,14 @@ nil, which considers all windows on the selected frame." ;; Always return nil. nil)))) +(defun other-window-backward (count &optional all-frames interactive) + "Select another window in the reverse cyclic ordering of windows. +COUNT specifies the number of windows to skip, (by default) backward, +starting with the selected window, before making the selection. Like +`other-window', but moves in the opposite direction." + (interactive "p\ni\np") + (other-window (- (or count 1)) all-frames interactive)) + (defun other-window-prefix () "Display the buffer of the next command in a new window. The next buffer is the buffer displayed by the next command invoked @@ -11373,6 +11381,7 @@ found by the provided context." (define-key ctl-x-map "2" 'split-window-below) (define-key ctl-x-map "3" 'split-window-right) (define-key ctl-x-map "o" 'other-window) +(define-key ctl-x-map "O" 'other-window-backward) (define-key ctl-x-map "^" 'enlarge-window) (define-key ctl-x-map "}" 'enlarge-window-horizontally) (define-key ctl-x-map "{" 'shrink-window-horizontally) @@ -11386,10 +11395,7 @@ found by the provided context." :doc "Keymap to repeat `other-window'. Used in `repeat-mode'." :repeat t "o" #'other-window - "O" (lambda () - (interactive) - (setq repeat-map 'other-window-repeat-map) - (other-window -1))) + "O" #'other-window-backward) (defvar-keymap resize-window-repeat-map :doc "Keymap to repeat window resizing commands. From 477335a10ba6d91dc1600b2a05ea202d4b851417 Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Sat, 2 Aug 2025 17:33:15 +0300 Subject: [PATCH 019/216] ; Fix documentation of 'find-file-noselect' * doc/lispref/files.texi (Visiting Functions): * lisp/files.el (find-file-noselect): Document that NOWARN non-nil also bypasses the file's last change verification. (Bug#79127) --- doc/lispref/files.texi | 10 ++++++---- lisp/files.el | 4 +++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/doc/lispref/files.texi b/doc/lispref/files.texi index c285cd1c683..d69bc46cd2c 100644 --- a/doc/lispref/files.texi +++ b/doc/lispref/files.texi @@ -149,9 +149,10 @@ The function returns an existing buffer if there is one; otherwise it creates a new buffer and reads the file into it. When @code{find-file-noselect} uses an existing buffer, it first verifies that the file has not changed since it was last visited or saved in -that buffer. If the file has changed, this function asks the user -whether to reread the changed file. If the user says @samp{yes}, any -edits previously made in the buffer are lost. +that buffer (unless @var{nowarn} is non-@code{nil}, see below). If the +file has changed, this function asks the user whether to reread the +changed file. If the user says @samp{yes}, any edits previously made in +the buffer are lost. Reading the file involves decoding the file's contents (@pxref{Coding Systems}), including end-of-line conversion, and format conversion @@ -163,7 +164,8 @@ This function displays warning or advisory messages in various peculiar cases, unless the optional argument @var{nowarn} is non-@code{nil}. For example, if it needs to create a buffer, and there is no file named @var{filename}, it displays the message @samp{(New file)} in the echo -area, and leaves the buffer empty. +area, and leaves the buffer empty. The verification of the file's last +change is also bypassed if @var{nowarn} is non-@code{nil}. The @code{find-file-noselect} function normally calls @code{after-find-file} after reading the file (@pxref{Subroutines of diff --git a/lisp/files.el b/lisp/files.el index c288a5a8b26..6544ee54ee4 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -2502,7 +2502,9 @@ be visible in the echo area." If a buffer exists visiting FILENAME, return that one, but verify that the file has not changed since visited or saved. The buffer is not selected, just returned to the caller. -Optional second arg NOWARN non-nil means suppress any warning messages. +Optional second arg NOWARN non-nil means suppress any warning messages, +and also don't verify the that the file has not been changed since +last visited or saved. Optional third arg RAWFILE non-nil means the file is read literally. Optional fourth arg WILDCARDS non-nil means do wildcard processing and visit all the matching files. When wildcards are actually From 0d7b1c912c47f4fb0b944fa6a3c136010794c18c Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Sat, 2 Aug 2025 17:59:36 +0300 Subject: [PATCH 020/216] Avoid lexbind warnings in kkc.el * lisp/international/kkc.el (kkc-lookup-key): Suppress warnings about lexical-binding cookies. (kkc-save-init-file): Write the lexical-binding cookie when saving the KKC init file. (Bug#79136) --- lisp/international/kkc.el | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lisp/international/kkc.el b/lisp/international/kkc.el index 6d603cf831a..a1789bce16f 100644 --- a/lisp/international/kkc.el +++ b/lisp/international/kkc.el @@ -64,9 +64,12 @@ This string is shown at mode line when users are in KKC mode.") (not (eq kkc-init-file-flag t))) (let ((coding-system-for-write 'iso-2022-7bit) (print-length nil)) - (write-region (format "(setq kkc-lookup-cache '%S)\n" kkc-lookup-cache) - nil - kkc-init-file-name)))) + (write-region + (format + ";; -*- lexical-binding: t; -*-\n\n(setq kkc-lookup-cache '%S)\n" + kkc-lookup-cache) + nil + kkc-init-file-name)))) ;; Sequence of characters to be used for indexes for shown list. The ;; Nth character is for the Nth conversion in the list currently shown. @@ -178,7 +181,8 @@ area while indicating the current selection by `'." (add-hook 'kill-emacs-hook 'kkc-save-init-file) (if (file-readable-p kkc-init-file-name) (condition-case nil - (load-file kkc-init-file-name) + (let ((warning-inhibit-types '((files missing-lexbind-cookie)))) + (load-file kkc-init-file-name)) (kkc-error "Invalid data in %s" kkc-init-file-name)))) (or (and (nested-alist-p kkc-lookup-cache) (eq (car kkc-lookup-cache) kkc-lookup-cache-tag)) From 84f1080f67c0960cca2d456c68e2d7866c19462c Mon Sep 17 00:00:00 2001 From: Daniel Mendler Date: Fri, 1 Aug 2025 13:55:16 +0200 Subject: [PATCH 021/216] (completing-read-multiple): Set missing buffer-local variables * lisp/emacs-lisp/crm.el (completing-read-multiple): Set missing buffer-local variables `minibuffer--require-match` and `minibuffer--original-buffer`, which are set by `completing-read` in the minibuffer setup hook. (bug#79110) --- lisp/emacs-lisp/crm.el | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lisp/emacs-lisp/crm.el b/lisp/emacs-lisp/crm.el index a75ccd46f50..425a606cb12 100644 --- a/lisp/emacs-lisp/crm.el +++ b/lisp/emacs-lisp/crm.el @@ -259,6 +259,7 @@ with empty strings removed." (list minibuffer-visible-completions-map map)) map)) + (buffer (current-buffer)) input) (minibuffer-with-setup-hook (lambda () @@ -282,6 +283,8 @@ with empty strings removed." ;; see completing_read in src/minibuf.c (setq-local minibuffer-completion-confirm (unless (eq require-match t) require-match)) + (setq-local minibuffer--require-match require-match) + (setq-local minibuffer--original-buffer buffer) (setq-local crm-completion-table table)) (setq input (read-from-minibuffer (format-spec From 687d3cb03bddd7eebf6384001fc54ca2c7e80037 Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Sat, 2 Aug 2025 10:47:03 -0700 Subject: [PATCH 022/216] Update from Gnulib by running admin/merge-gnulib * lib/sys-limits.h: New file. --- doc/misc/texinfo.tex | 31 ++++++++++++-------- lib/copy-file-range.c | 66 ++++++++++++++++++++++++++++++++----------- lib/gnulib.mk.in | 2 ++ lib/sys-limits.h | 42 +++++++++++++++++++++++++++ m4/copy-file-range.m4 | 5 ++-- m4/gnulib-comp.m4 | 1 + 6 files changed, 116 insertions(+), 31 deletions(-) create mode 100644 lib/sys-limits.h diff --git a/doc/misc/texinfo.tex b/doc/misc/texinfo.tex index 42e686ce705..64dddad8290 100644 --- a/doc/misc/texinfo.tex +++ b/doc/misc/texinfo.tex @@ -3,7 +3,7 @@ % Load plain if necessary, i.e., if running under initex. \expandafter\ifx\csname fmtname\endcsname\relax\input plain\fi % -\def\texinfoversion{2025-07-15.21} +\def\texinfoversion{2025-07-31.19} % % Copyright 1985, 1986, 1988, 1990-2025 Free Software Foundation, Inc. % @@ -9634,7 +9634,10 @@ might help (with 'rm \jobname.?? \jobname.??s')% % Auto-number footnotes. Otherwise like plain. \gdef\footnote{% \global\advance\footnoteno by \@ne - \edef\thisfootno{$^{\the\footnoteno}$}% + % + % Output for the footnote marker. If we are immediately after another + % footnote, output a comma and small space first. + \edef\thisfootno{$^{\ifnum\lastpenalty=3 ,\mskip 1mu \fi\the\footnoteno}$}% % % In case the footnote comes at the end of a sentence, preserve the % extra spacing after we do the footnote number. @@ -9643,19 +9646,25 @@ might help (with 'rm \jobname.?? \jobname.??s')% % % Remove inadvertent blank space before typesetting the footnote number. \unskip - \thisfootno\@sf + % + % \scriptspace is 0.5pt by default and gives excessive space before the + % comma if we have multiple footnote markers in a row. + \bgroup\scriptspace=0pt + \thisfootno\@sf + \egroup \dofootnote }% -% Don't bother with the trickery in plain.tex to not require the -% footnote text as a parameter. Our footnotes don't need to be so general. -% -% Oh yes, they do; otherwise, @ifset (and anything else that uses -% \parseargline) fails inside footnotes because the tokens are fixed when -% the footnote is read. --karl, 16nov96. +% marker for immediately after a footnote marker +\gdef\footnoteendmarker{\penalty3 } + +% Do not require the footnote text as a parameter; otherwise, @ifset +% (and anything else that uses \parseargline) fails inside footnotes +% because the tokens are fixed when the footnote is read. % \gdef\dofootnote{% \insert\footins\bgroup + \aftergroup\footnoteendmarker % % Nested footnotes are not supported in TeX, that would take a lot % more work. (\startsavinginserts does not suffice.) @@ -9686,14 +9695,14 @@ might help (with 'rm \jobname.?? \jobname.??s')% % Hang the footnote text off the number. Use \everypar in case the % footnote extends for more than one paragraph. \everypar = {\hang}% - \textindent{\thisfootno}% + \textindent{$^{\the\footnoteno}$}% % % Don't crash into the line above the footnote text. Since this % expands into a box, it must come within the paragraph, lest it % provide a place where TeX can split the footnote. \footstrut % - % Invoke rest of plain TeX footnote routine. + % Eat opening brace and invoke rest of plain TeX footnote routine. \futurelet\next\fo@t } }%end \catcode `\@=11 diff --git a/lib/copy-file-range.c b/lib/copy-file-range.c index 8e0c644c086..2465a558028 100644 --- a/lib/copy-file-range.c +++ b/lib/copy-file-range.c @@ -21,9 +21,22 @@ #include #if defined __linux__ && HAVE_COPY_FILE_RANGE +# include # include +/* Although it can be dicey to use static checks for Linux kernel versions, + due to the dubious practice of building on newer kernels for older ones, + do it here anyway as the buggy kernels are rare (they are all EOLed) + and builders for them are unlikely to use the dubious practice. + Circa 2029 we should remove the old-kernel workarounds entirely. */ +# if LINUX_VERSION_CODE < KERNEL_VERSION (5, 3, 0) +# define CHECK_LINUX_KERNEL_VERSION true +# else +# define CHECK_LINUX_KERNEL_VERSION false +# endif #endif +#include "sys-limits.h" + ssize_t copy_file_range (int infd, off_t *pinoff, int outfd, off_t *poutoff, @@ -31,32 +44,51 @@ copy_file_range (int infd, off_t *pinoff, { #undef copy_file_range -#if defined __linux__ && HAVE_COPY_FILE_RANGE +#if HAVE_COPY_FILE_RANGE + bool ok = true; + +# if CHECK_LINUX_KERNEL_VERSION /* The implementation of copy_file_range (which first appeared in Linux kernel release 4.5) had many issues before release 5.3 , so fail with ENOSYS for Linux kernels 5.2 and earlier. - This workaround, and the configure-time check for Linux, can be - removed when such kernels (released March 2016 through September - 2019) are no longer a consideration. As of January 2021, the - furthest-future planned kernel EOL is December 2024 for kernel - release 4.19. */ + This workaround can be removed when such kernels (released March + 2016 through September 2019) are no longer a consideration. + Although all such kernels have reached EOL, some distros use + older kernels. For example, RHEL 8 uses kernel 4.18 and has an + EOL of 2029. */ - static signed char ok; + static signed char kernel_ok; + if (! kernel_ok) + { + struct utsname name; + uname (&name); + char *p = name.release; + kernel_ok = ((p[1] != '.' || '5' < p[0] + || (p[0] == '5' && (p[3] != '.' || '2' < p[2]))) + ? 1 : -1); + } - if (! ok) - { - struct utsname name; - uname (&name); - char *p = name.release; - ok = ((p[1] != '.' || '5' < p[0] - || (p[0] == '5' && (p[3] != '.' || '2' < p[2]))) - ? 1 : -1); - } + if (kernel_ok < 0) + ok = false; +# endif + + if (ok) + { +# if defined __GLIBC__ && ! (2 < __GLIBC__ + (43 <= __GLIBC_MINOR__)) + /* Work around glibc bug 33245 + . + This bug is present in glibc 2.42 (2025) and fixed in 2.43, + so this workaround, and the configure-time check for glibc, + can be removed once glibc 2.42 and earlier is no longer a + consideration. Perhaps in 2040. */ + if (SYS_BUFSIZE_MAX < length) + length = SYS_BUFSIZE_MAX; +# endif - if (0 < ok) return copy_file_range (infd, pinoff, outfd, poutoff, length, flags); + } #endif /* There is little need to emulate copy_file_range with read+write, diff --git a/lib/gnulib.mk.in b/lib/gnulib.mk.in index b3774a648b8..fa800300a42 100644 --- a/lib/gnulib.mk.in +++ b/lib/gnulib.mk.in @@ -1799,6 +1799,8 @@ ifneq (,$(GL_COND_OBJ_COPY_FILE_RANGE_CONDITION)) libgnu_a_SOURCES += copy-file-range.c endif +EXTRA_DIST += sys-limits.h + endif ## end gnulib module copy-file-range diff --git a/lib/sys-limits.h b/lib/sys-limits.h new file mode 100644 index 00000000000..a556dfeb6d5 --- /dev/null +++ b/lib/sys-limits.h @@ -0,0 +1,42 @@ +/* System call limits + + Copyright 2018-2025 Free Software Foundation, Inc. + + This file is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as + published by the Free Software Foundation; either version 2.1 of the + License, or (at your option) any later version. + + This file is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program. If not, see . */ + +#ifndef _GL_SYS_LIMITS_H +#define _GL_SYS_LIMITS_H + +#include + +/* Maximum number of bytes to read or write in a single system call. + This can be useful for system calls like sendfile on GNU/Linux, + which do not handle more than MAX_RW_COUNT bytes correctly. + The Linux kernel MAX_RW_COUNT is at least INT_MAX >> 20 << 20, + where the 20 comes from the Hexagon port with 1 MiB pages; use that + as an approximation, as the exact value may not be available to us. + + Using this also works around a serious Linux bug before 2.6.16; see + . + + Using this also works around a Tru64 5.1 bug, where attempting + to read INT_MAX bytes fails with errno == EINVAL. See + . + + Using this is likely to work around similar bugs in other operating + systems. */ + +enum { SYS_BUFSIZE_MAX = INT_MAX >> 20 << 20 }; + +#endif diff --git a/m4/copy-file-range.m4 b/m4/copy-file-range.m4 index 13081d9b11c..85cfef71f44 100644 --- a/m4/copy-file-range.m4 +++ b/m4/copy-file-range.m4 @@ -51,9 +51,8 @@ AC_DEFUN([gl_FUNC_COPY_FILE_RANGE], [Define to 1 if the function copy_file_range exists.]) case $host_os in - linux*) - # See copy-file-range.c comment re pre-5.3 Linux kernel bugs. - # We should be able to remove this hack in 2025. + *-gnu* | gnu* | linux*) + # See copy-file-range.c comment re glibc and Linux kernel bugs. REPLACE_COPY_FILE_RANGE=1;; esac fi diff --git a/m4/gnulib-comp.m4 b/m4/gnulib-comp.m4 index 83d5f1bfa0f..107b1493617 100644 --- a/m4/gnulib-comp.m4 +++ b/m4/gnulib-comp.m4 @@ -1420,6 +1420,7 @@ AC_DEFUN([gl_FILE_LIST], [ lib/strtol.c lib/strtoll.c lib/symlink.c + lib/sys-limits.h lib/sys_random.in.h lib/sys_select.in.h lib/sys_stat.in.h From c935b68bed174386f46dec6be525a23397a4b5f8 Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Sat, 2 Aug 2025 11:17:35 -0700 Subject: [PATCH 023/216] Port better to Linux with 1 MiB pages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * src/lisp.h: Include . (MAX_RW_COUNT): Remove, as it is the same thing as sys-limits.h’s SYS_BUFSIZE_MAX. All uses replaced by SYS_BUFSIZE_MAX. --- src/emacs.c | 2 +- src/fileio.c | 2 +- src/lisp.h | 12 +----------- src/pdumper.c | 2 +- src/sysdep.c | 16 ++++++++-------- 5 files changed, 12 insertions(+), 22 deletions(-) diff --git a/src/emacs.c b/src/emacs.c index 8a6307f077d..f84ccd9de82 100644 --- a/src/emacs.c +++ b/src/emacs.c @@ -1176,7 +1176,7 @@ read_full (int fd, void *buffer, ptrdiff_t size) eassert (0 <= fd); eassert (buffer != NULL); eassert (0 <= size); - if (max (PTRDIFF_MAX, MAX_RW_COUNT) < size) + if (max (PTRDIFF_MAX, SYS_BUFSIZE_MAX) < size) { errno = EFBIG; return -1; diff --git a/src/fileio.c b/src/fileio.c index 2dad90ac600..5cb3a267807 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -4099,7 +4099,7 @@ by calling `format-decode', which see. */) If too small, insert-file-contents has more syscall overhead. If too large, insert-file-contents might take too long respond to a quit. 1 MiB should be reasonable even for older, slower devices circa 2025. */ - enum { INSERT_READ_SIZE_MAX = min (1024 * 1024, MAX_RW_COUNT) }; + enum { INSERT_READ_SIZE_MAX = min (1024 * 1024, SYS_BUFSIZE_MAX) }; struct timespec mtime; emacs_fd fd; diff --git a/src/lisp.h b/src/lisp.h index fd3ff7c6b76..64b5c227583 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -39,6 +39,7 @@ along with GNU Emacs. If not, see . */ #include #include #include +#include #include INLINE_HEADER_BEGIN @@ -5297,17 +5298,6 @@ maybe_disable_address_randomization (int argc, char **argv) return argc; } #endif -/* Maximum number of bytes to read or write in a single system call. - This works around a serious bug in Linux kernels before 2.6.16; see - - and see Linux kernel commit e28cc71572da38a5a12c1cfe4d7032017adccf69. - It's likely to work around similar bugs in other operating systems, so do it - on all platforms. Round INT_MAX down to a page size, with the conservative - assumption that page sizes are at most 2**18 bytes (any kernel with a - page size larger than that shouldn't have the bug). */ -#ifndef MAX_RW_COUNT -# define MAX_RW_COUNT (INT_MAX >> 18 << 18) -#endif extern int emacs_exec_file (char const *, char *const *, char *const *); extern void init_standard_fds (void); extern char *emacs_get_current_dir_name (void); diff --git a/src/pdumper.c b/src/pdumper.c index c336d024f82..13744dd7c4a 100644 --- a/src/pdumper.c +++ b/src/pdumper.c @@ -5346,7 +5346,7 @@ dump_read_all (int fd, void *buf, size_t bytes_to_read) size_t bytes_read = 0; while (bytes_read < bytes_to_read) { - int chunk_to_read = min (bytes_to_read - bytes_read, MAX_RW_COUNT); + int chunk_to_read = min (bytes_to_read - bytes_read, SYS_BUFSIZE_MAX); ssize_t chunk = read (fd, (char *) buf + bytes_read, chunk_to_read); if (chunk < 0) return chunk; diff --git a/src/sysdep.c b/src/sysdep.c index d01cca1435d..af16db36f99 100644 --- a/src/sysdep.c +++ b/src/sysdep.c @@ -2714,20 +2714,20 @@ emacs_fchmodat (int fd, const char *path, mode_t mode, int flags) #endif /* !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY) */ } -/* Verify that MAX_RW_COUNT fits in the relevant standard types. */ +/* Verify that SYS_BUFSIZE_MAX fits in the relevant standard types. */ #ifndef SSIZE_MAX # define SSIZE_MAX TYPE_MAXIMUM (ssize_t) #endif -static_assert (MAX_RW_COUNT <= PTRDIFF_MAX); -static_assert (MAX_RW_COUNT <= SIZE_MAX); -static_assert (MAX_RW_COUNT <= SSIZE_MAX); +static_assert (SYS_BUFSIZE_MAX <= PTRDIFF_MAX); +static_assert (SYS_BUFSIZE_MAX <= SIZE_MAX); +static_assert (SYS_BUFSIZE_MAX <= SSIZE_MAX); #ifdef WINDOWSNT /* Verify that Emacs read requests cannot cause trouble, even in 64-bit builds. The last argument of 'read' is 'unsigned int', and the return value's type (see 'sys_read') is 'int'. */ -static_assert (MAX_RW_COUNT <= INT_MAX); -static_assert (MAX_RW_COUNT <= UINT_MAX); +static_assert (SYS_BUFSIZE_MAX <= INT_MAX); +static_assert (SYS_BUFSIZE_MAX <= UINT_MAX); #endif /* Read from FD to a buffer BUF with size NBYTE. @@ -2739,7 +2739,7 @@ static ptrdiff_t emacs_intr_read (int fd, void *buf, ptrdiff_t nbyte, bool interruptible) { /* No caller should ever pass a too-large size to emacs_read. */ - eassert (nbyte <= MAX_RW_COUNT); + eassert (nbyte <= SYS_BUFSIZE_MAX); ssize_t result; @@ -2785,7 +2785,7 @@ emacs_full_write (int fd, char const *buf, ptrdiff_t nbyte, while (nbyte > 0) { - ssize_t n = write (fd, buf, min (nbyte, MAX_RW_COUNT)); + ssize_t n = write (fd, buf, min (nbyte, SYS_BUFSIZE_MAX)); if (n < 0) { From 5c153cfb9620baf44dd388bb509c5aca82e377e9 Mon Sep 17 00:00:00 2001 From: "F. Jason Park" Date: Sat, 2 Aug 2025 17:31:04 -0700 Subject: [PATCH 024/216] ; Make erc-autojoin-channels-delayed tests more robust * test/lisp/erc/erc-join-tests.el (erc-join-tests--autojoin-channels-ident): Be more flexible in checking assertion. (Bug#79017) --- test/lisp/erc/erc-join-tests.el | 36 +++++++++++++++++---------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/test/lisp/erc/erc-join-tests.el b/test/lisp/erc/erc-join-tests.el index ea3623bfce9..fe4cc907497 100644 --- a/test/lisp/erc/erc-join-tests.el +++ b/test/lisp/erc/erc-join-tests.el @@ -82,27 +82,29 @@ (erc-tests-common-make-server-buf) - ;; May run forever on Solaris 10 (see bug#79017). - (with-timeout (5 (ert-fail "Timeout exceeded")) + (cl-letf* ((erc-autojoin-timing 'ident) + (erc-autojoin-delay 0.0625) + (calls nil) + (check (lambda () (prog1 calls (setq calls nil)))) + ((symbol-function 'erc-server-send) + (lambda (line) (push line calls))) + ((symbol-function 'erc-autojoin-after-ident) + (lambda (&rest _r) (should-not "run")))) - (cl-letf* ((erc-autojoin-timing 'ident) - (erc-autojoin-delay 0.0625) - (calls nil) - (check (lambda () (prog1 calls (setq calls nil)))) - ((symbol-function 'erc-server-send) - (lambda (line) (push line calls))) - ((symbol-function 'erc-autojoin-after-ident) - (lambda (&rest _r) (should-not "run")))) + (should-not erc--autojoin-timer) - (should-not erc--autojoin-timer) + (erc-autojoin-channels erc-server-announced-name "tester") + (should erc--autojoin-timer) - (erc-autojoin-channels erc-server-announced-name "tester") - (should erc--autojoin-timer) - (sleep-for 0.25) - (funcall test check) + ;; May run forever on Solaris 10 (bug#79017). + (with-timeout (5 (ert-fail "Timeout exceeded")) + (while erc--autojoin-timer + (sleep-for 0.125))) - (should-not calls) - (should-not erc--autojoin-timer))) + (funcall test check) + + (should-not calls) + (should-not erc--autojoin-timer)) (when noninteractive (erc-tests-common-kill-buffers))) From f9e0c06875104bd6e27ede3c4541c66f39d65253 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20Engdeg=C3=A5rd?= Date: Mon, 4 Aug 2025 10:59:20 +0200 Subject: [PATCH 025/216] * lisp/vc/vc-git.el (vc-git--worktrees): Don't pass -z to older Git. The -z option for 'git worktree list --porcelain' appeared in 2.36. --- lisp/vc/vc-git.el | 45 ++++++++++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/lisp/vc/vc-git.el b/lisp/vc/vc-git.el index c3f32683caf..5f901b1eea8 100644 --- a/lisp/vc/vc-git.el +++ b/lisp/vc/vc-git.el @@ -2405,25 +2405,32 @@ see the \"LIST OUTPUT FORMAT\" section of the git-worktree(1) manual page for the meanings of these attributes." (with-temp-buffer (vc-git-command nil 0 nil "worktree" "prune") - (vc-git-command t 0 nil "worktree" "list" "--porcelain" "-z") - (let (worktrees current-root current-rest) - (goto-char (point-min)) - (while - (re-search-forward "\\=\\(\\([a-zA-Z]+\\)\\(?: \\([^\0]+\\)\\)?\\)?\0" - nil t) - (if (match-string 1) - (let ((k (intern (match-string 2))) - (v (or (match-string 3) t))) - (cond ((and (not current-root) (eq k 'worktree)) - (setq current-root (file-name-as-directory v))) - ((not (eq k 'worktree)) - (push (cons k v) current-rest)) - (t - (error "'git worktree' output parse error")))) - (push (cons current-root current-rest) worktrees) - (setq current-root nil current-rest nil))) - (or worktrees - (error "'git worktree' output parse error"))))) + (let ((have-worktree-list-porcelain-z + ;; The -z option to 'worktree list --porcelain' appeared in 2.36 + (version<= "2.36" (vc-git--program-version)))) + (vc-git-command t 0 nil "worktree" "list" "--porcelain" + (and have-worktree-list-porcelain-z "-z")) + (let (worktrees current-root current-rest) + (goto-char (point-min)) + (while + (re-search-forward + (if have-worktree-list-porcelain-z + "\\=\\(\\([a-zA-Z]+\\)\\(?: \\([^\0]+\\)\\)?\\)?\0" + "\\=\\(\\([a-zA-Z]+\\)\\(?: \\([^\n]+\\)\\)?\\)?\n") + nil t) + (if (match-string 1) + (let ((k (intern (match-string 2))) + (v (or (match-string 3) t))) + (cond ((and (not current-root) (eq k 'worktree)) + (setq current-root (file-name-as-directory v))) + ((not (eq k 'worktree)) + (push (cons k v) current-rest)) + (t + (error "'git worktree' output parse error")))) + (push (cons current-root current-rest) worktrees) + (setq current-root nil current-rest nil))) + (or worktrees + (error "'git worktree' output parse error")))))) (defun vc-git-known-other-working-trees () "Implementation of `known-other-working-trees' backend function for Git." From 1811cb294e44730ba6c069bfcd0ce564a38c8e0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20Engdeg=C3=A5rd?= Date: Mon, 4 Aug 2025 11:00:53 +0200 Subject: [PATCH 026/216] Fix vc-git worktrees and vc-tests for directories with symlinks * lisp/vc/vc-git.el (vc-git-known-other-working-trees): * test/lisp/vc/vc-tests/vc-tests.el (vc-test--other-working-trees): Tolerate directories with symlinks in them, such as the standard temporary dirs on MacOS. 'git worktree list' outputs expanded names. --- lisp/vc/vc-git.el | 3 ++- test/lisp/vc/vc-tests/vc-tests.el | 15 +++++++++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/lisp/vc/vc-git.el b/lisp/vc/vc-git.el index 5f901b1eea8..1f5ac7bd289 100644 --- a/lisp/vc/vc-git.el +++ b/lisp/vc/vc-git.el @@ -2434,7 +2434,8 @@ page for the meanings of these attributes." (defun vc-git-known-other-working-trees () "Implementation of `known-other-working-trees' backend function for Git." - (cl-loop with root = (expand-file-name (vc-git-root default-directory)) + (cl-loop with root = (file-truename + (expand-file-name (vc-git-root default-directory))) for (worktree) in (vc-git--worktrees) unless (equal worktree root) collect (abbreviate-file-name worktree))) diff --git a/test/lisp/vc/vc-tests/vc-tests.el b/test/lisp/vc/vc-tests/vc-tests.el index 04c88e48262..8d7a7a8ffd1 100644 --- a/test/lisp/vc/vc-tests/vc-tests.el +++ b/test/lisp/vc/vc-tests/vc-tests.el @@ -684,12 +684,15 @@ This checks also `vc-backend' and `vc-responsible-backend'." (project-forget-project (expand-file-name name default-directory)))))) - (let* ((first (file-name-as-directory - (expand-file-name "first" default-directory))) - (second (file-name-as-directory - (expand-file-name "second" default-directory))) - (third (file-name-as-directory - (expand-file-name "third" default-directory))) + (let* ((first (file-truename + (file-name-as-directory + (expand-file-name "first" default-directory)))) + (second (file-truename + (file-name-as-directory + (expand-file-name "second" default-directory)))) + (third (file-truename + (file-name-as-directory + (expand-file-name "third" default-directory)))) (tmp-name (expand-file-name "foo" first)) (project-list-file (expand-file-name "projects.eld" default-directory))) From 9e48d1f49a790b50fa4fe83279ed2281e78f81db Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Mon, 4 Aug 2025 20:59:02 +0100 Subject: [PATCH 027/216] project-prompter: New PREDICATE and REQUIRE-KNOWN arguments * lisp/progmodes/project.el (project-prompter) (project-prompt-project-dir, project-prompt-project-name): New PREDICATE and REQUIRE-KNOWN optional arguments. * lisp/vc/vc.el (project-prompter): Declare. (vc--prompt-other-working-tree): New function. (vc-switch-working-tree, vc-delete-working-tree) (vc-move-working-tree): Use it to prompt for the other working tree in a way that respects 'project-prompter' (bug#79024). * etc/NEWS: Announce all three optional arguments. Rework description of new optional argument to 'project-current'. --- etc/NEWS | 17 +++++++++--- lisp/progmodes/project.el | 55 ++++++++++++++++++++++++++++++--------- lisp/vc/vc.el | 30 ++++++++++++--------- 3 files changed, 72 insertions(+), 30 deletions(-) diff --git a/etc/NEWS b/etc/NEWS index e12a9dcb127..85fad599048 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -452,10 +452,19 @@ It is equivalent to running 'project-any-command' with The prompt now displays the chosen project on which to invoke a command. --- -*** The MAYBE-PROMPT argument of 'project-current' can be a string. -When such value is used, the 'project-prompter' is called with it as the -first argument. This is a way for the callers to indicate, for example, -the reason or the context why the project is asked for. +*** 'project-prompter' values may be called with up to three arguments. +These allow callers of the value of 'project-prompter' to specify a +prompt string; prompt the user to choose between a subset of all the +known projects; and disallow returning arbitrary directories. +See the docstring of 'project-prompter' for a full specification of +these new optional arguments. + +--- +*** 'project-current' has a new optional argument, MAYBE-PROMPT. +If 'project-current' is called with this argument non-nil, then it is +passed to the 'project-prompter' to use as a prompt string. +Callers can use this to indicate the reason for which or context in +which Emacs should ask the user to select a project. --- *** New command 'project-find-matching-file'. diff --git a/lisp/progmodes/project.el b/lisp/progmodes/project.el index 68179206017..f45cb45aacb 100644 --- a/lisp/progmodes/project.el +++ b/lisp/progmodes/project.el @@ -199,9 +199,24 @@ When it is non-nil, `project-current' will always skip prompting too.") (defcustom project-prompter #'project-prompt-project-dir "Function to call to prompt for a project. -The function is either called with no arguments or with one argument, -which is the prompt string to use. It should return a project root -directory." +The function is called either with no arguments or with up to three +optional arguments: (&optional PROMPT PREDICATE REQUIRE-KNOWN). + +PROMPT is the prompt string to use. + +PREDICATE, if non-nil, is a function suitable as the PREDICATE argument +to `try-completion' or `all-completions', which see. PREDICATE allows +the caller to limit the projects from which the user is able to select. +It should return nil when passed a project root directory corresponding +to a project the user should not be allowed to select. +The `project-prompter' should filter project completion candidates +presented to the user using this predicate. + +If REQUIRE-KNOWN is non-nil, the value of `project-prompter' should only +allow the user to select from known projects. Otherwise, the function +may allow the user to input arbitrary directories. If PREDICATE and +REQUIRE-KNOWN are both non-nil, the value of `project-prompter' should +not return any project root directory for which PREDICATE returns nil." :type '(choice (const :tag "Prompt for a project directory" project-prompt-project-dir) (const :tag "Prompt for a project name" @@ -2022,12 +2037,15 @@ the project list." (defvar project--dir-history) -(defun project-prompt-project-dir (&optional prompt) +(defun project-prompt-project-dir (&optional prompt predicate require-known) "Prompt the user for a directory 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. -When PROMPT is non-nil, use it as the prompt string." +If PROMPT is non-nil, use it as the prompt string. +If PREDICATE is non-nil, filter possible project choices using this +function; see `project-prompter' for more details. +Unless REQUIRE-KNOWN is non-nil, it's also possible to enter an +arbitrary directory not in the list of known projects." (project--ensure-read-project-list) (if-let* (project-prune-zombie-projects (inhibit-message t)) @@ -2037,7 +2055,8 @@ When PROMPT is non-nil, use it as the prompt string." ;; XXX: Just using this for the category (for the substring ;; completion style). (project--file-completion-table - (append project--list `(,dir-choice)))) + (if require-known project--list + (append project--list `(,dir-choice))))) (project--dir-history (project-known-project-roots)) (pr-dir "")) (while (equal pr-dir "") @@ -2048,19 +2067,27 @@ When PROMPT is non-nil, use it as the prompt string." ;; TODO: Use `format-prompt' (Emacs 28.1+) (format "%s: " (substitute-command-keys prompt)) "Select project: ") - choices nil t nil 'project--dir-history)))) + choices + (and predicate + (lambda (choice) + (or (equal choice dir-choice) + (funcall predicate choice)))) + t nil 'project--dir-history)))) (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 (&optional prompt) +(defun project-prompt-project-name (&optional prompt predicate require-known) "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. -When PROMPT is non-nil, use it as the prompt string." +If PROMPT is non-nil, use it as the prompt string. +If PREDICATE is non-nil, filter possible project choices using this +function; see `project-prompter' for more details. +Unless REQUIRE-KNOWN is non-nil, it's also possible to enter an +arbitrary directory not in the list of known projects." (if-let* (project-prune-zombie-projects (inhibit-message t)) (project-forget-zombie-projects)) @@ -2073,7 +2100,8 @@ When PROMPT is non-nil, use it as the prompt string." (dolist (dir (reverse (project-known-project-roots))) ;; We filter out directories that no longer map to a project, ;; since they don't have a clean project-name. - (when-let* ((proj (project--find-in-directory dir)) + (when-let* (((or (not predicate) (funcall predicate dir))) + (proj (project--find-in-directory dir)) (name (project-name proj))) (push name project--name-history) (push (cons name proj) ret))) @@ -2081,7 +2109,8 @@ When PROMPT is non-nil, use it as the prompt string." ;; XXX: Just using this for the category (for the substring ;; completion style). (table (project--file-completion-table - (reverse (cons dir-choice choices)))) + (reverse (if require-known choices + (cons dir-choice choices))))) (pr-name "")) (while (equal pr-name "") ;; If the user simply pressed RET, do this again until they don't. diff --git a/lisp/vc/vc.el b/lisp/vc/vc.el index 4cfd1790266..731cf933e04 100644 --- a/lisp/vc/vc.el +++ b/lisp/vc/vc.el @@ -4408,6 +4408,19 @@ When called from Lisp, BACKEND is the VC backend." (vc-dir directory backend)) +(defvar project-prompter) + +(defun vc--prompt-other-working-tree (backend prompt) + "Invoke `project-prompter' to choose another working tree. +BACKEND is the VC backend. +PROMPT is the prompt string for `project-prompter'." + (let ((trees (vc-call-backend backend 'known-other-working-trees))) + (require 'project) + (funcall project-prompter prompt + (lambda (k &optional _v) + (member (or (car-safe k) k) trees)) + t))) + (defvar project-current-directory-override) ;;;###autoload @@ -4421,11 +4434,8 @@ to the root of this working tree." ;; FIXME: Switch between directory analogues, too, in Dired buffers. (interactive (list - ;; FIXME: This should respect `project-prompter'. See bug#79024. - (completing-read "Other working tree to visit: " - (vc-call-backend (vc-responsible-backend default-directory) - 'known-other-working-trees) - nil t))) + (vc--prompt-other-working-tree (vc-responsible-backend default-directory) + "Other working tree to visit"))) (let ((project-current-directory-override directory)) (project-find-matching-file))) @@ -4438,10 +4448,7 @@ BACKEND is the VC backend." (interactive (let ((backend (vc-responsible-backend default-directory))) (list backend - ;; FIXME: This should respect `project-prompter'. See bug#79024. - (completing-read "Delete working tree: " - (vc-call-backend backend 'known-other-working-trees) - nil t)))) + (vc--prompt-other-working-tree backend "Delete working tree")))) ;; We could consider not prompting here, thus always failing when ;; there is uncommitted work, and requiring the user to review and ;; revert the uncommitted changes before invoking this command again. @@ -4467,10 +4474,7 @@ BACKEND is the VC backend." (interactive (let ((backend (vc-responsible-backend default-directory))) (list backend - ;; FIXME: This should respect `project-prompter'. See bug#79024. - (completing-read "Relocate working tree: " - (vc-call-backend backend 'known-other-working-trees) - nil t) + (vc--prompt-other-working-tree backend "Relocate working tree") (read-directory-name "New location for working tree: " (file-name-parent-directory (vc-root-dir)))))) (let ((inhibit-message t)) From 0df9731ec9ae56a51eb457cf921605e066e16897 Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Mon, 4 Aug 2025 21:07:14 +0100 Subject: [PATCH 028/216] vc--prompt-other-working-tree: Remember all the working trees * lisp/vc/vc.el (vc--prompt-other-working-tree): Call 'project-remember-project' on all the known working trees. --- lisp/vc/vc.el | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lisp/vc/vc.el b/lisp/vc/vc.el index 731cf933e04..2539093b5bb 100644 --- a/lisp/vc/vc.el +++ b/lisp/vc/vc.el @@ -4415,6 +4415,9 @@ When called from Lisp, BACKEND is the VC backend." BACKEND is the VC backend. PROMPT is the prompt string for `project-prompter'." (let ((trees (vc-call-backend backend 'known-other-working-trees))) + (dolist (tree trees) + (when-let* ((p (project-current nil tree))) + (project-remember-project p))) (require 'project) (funcall project-prompter prompt (lambda (k &optional _v) From e01b9bd69a5bcd611e8c244a8910e03a38434854 Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Mon, 4 Aug 2025 21:08:49 +0100 Subject: [PATCH 029/216] ; * lisp/vc/vc.el (vc--prompt-other-working-tree): Reorder. In fact, calling project-current or project-remember-project will ensure that project-prompter is loaded. But let's not rely on that. --- lisp/vc/vc.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lisp/vc/vc.el b/lisp/vc/vc.el index 2539093b5bb..3ba01210667 100644 --- a/lisp/vc/vc.el +++ b/lisp/vc/vc.el @@ -4415,10 +4415,10 @@ When called from Lisp, BACKEND is the VC backend." BACKEND is the VC backend. PROMPT is the prompt string for `project-prompter'." (let ((trees (vc-call-backend backend 'known-other-working-trees))) + (require 'project) (dolist (tree trees) (when-let* ((p (project-current nil tree))) (project-remember-project p))) - (require 'project) (funcall project-prompter prompt (lambda (k &optional _v) (member (or (car-safe k) k) trees)) From 65c110b913f0aa539c34a77be812144c621021f8 Mon Sep 17 00:00:00 2001 From: Dmitry Gutov Date: Tue, 5 Aug 2025 04:00:25 +0300 Subject: [PATCH 030/216] vc-annotate-warp-revision: Fix going through renames MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * lisp/vc/vc-annotate.el (vc-annotate--file-name-change): New function (bug#26345). (vc-annotate-warp-revision): Use it here. Co-Authored-By: Jakub Ječmínek --- lisp/vc/vc-annotate.el | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/lisp/vc/vc-annotate.el b/lisp/vc/vc-annotate.el index e68c5a79919..6bed36c99dc 100644 --- a/lisp/vc/vc-annotate.el +++ b/lisp/vc/vc-annotate.el @@ -667,6 +667,11 @@ describes a revision number, so warp to that revision." (or file (caadr vc-buffer-overriding-fileset)) newrev)) + (setq file (vc-annotate--file-name-change + (or file + (caadr vc-buffer-overriding-fileset)) + newrev + vc-annotate-backend)) (setq revspec (1- revspec)))) (unless newrev (message "Cannot increment %d revisions from revision %s" @@ -675,6 +680,12 @@ describes a revision number, so warp to that revision." (setq newrev vc-buffer-revision) (let ((vc-use-short-revision vc-annotate-use-short-revision)) (while (and (< revspec 0) newrev) + (setq file (vc-annotate--file-name-change + (or file + (caadr vc-buffer-overriding-fileset)) + newrev + vc-annotate-backend + 'backward)) (setq newrev (vc-call-backend vc-annotate-backend 'previous-revision (or file @@ -804,6 +815,21 @@ The annotations are relative to the current time, unless overridden by OFFSET." (message "Annotations were for revision %s; line numbers may be incorrect" rev))))) +(defun vc-annotate--file-name-change (file rev backend &optional backward) + "Return the name of FILE at revision REV using BACKEND. +If the file name has changed in the given revision, return the new name; +otherwise, return FILE unchanged. +If BACKWARD, return the name for FILE before REV." + (if (vc-find-backend-function backend 'file-name-changes) + (or + (if backward + (car (rassoc file + (vc-call-backend backend 'file-name-changes rev))) + (cdr (assoc file + (vc-call-backend backend 'file-name-changes rev)))) + file) + file)) + (provide 'vc-annotate) ;;; vc-annotate.el ends here From f58b738042e8f89508e7565242a67251a2460cff Mon Sep 17 00:00:00 2001 From: Alberto Santini Date: Mon, 4 Aug 2025 21:55:29 -0500 Subject: [PATCH 031/216] ; windows installer: set x64 registry keys (tiny change) * admin/nt/dist-build/emacs.nsi: set x64 registry keys --- admin/nt/dist-build/emacs.nsi | 3 +++ 1 file changed, 3 insertions(+) diff --git a/admin/nt/dist-build/emacs.nsi b/admin/nt/dist-build/emacs.nsi index 9239eb07ec7..13278749871 100644 --- a/admin/nt/dist-build/emacs.nsi +++ b/admin/nt/dist-build/emacs.nsi @@ -70,6 +70,9 @@ Section # create uninstaller WriteUninstaller "$UninstallerPath" + # request to set x64 registry keys + SetRegView 64 + # add registry key to enable uninstall from control panel WriteRegStr HKLM "${UNINST_KEY}" "DisplayName" "GNU Emacs ${VERSION_BRANCH}" WriteRegStr HKLM "${UNINST_KEY}" "DisplayIcon" "$\"$UninstallerPath$\"" From 7a5ee16b013a0669e0f566e8d51635e687a4641a Mon Sep 17 00:00:00 2001 From: Juri Linkov Date: Tue, 5 Aug 2025 09:38:12 +0300 Subject: [PATCH 032/216] * lisp/tab-line.el: Improve docstrings. (tab-line-tab-face-functions, tab-line-tab-modified-p) (tab-line-tab-face-modified): Explain possible values of BUFFER-P. --- lisp/tab-line.el | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lisp/tab-line.el b/lisp/tab-line.el index 51675dbbb29..fe903c02041 100644 --- a/lisp/tab-line.el +++ b/lisp/tab-line.el @@ -42,7 +42,8 @@ "Functions called to modify tab faces. Each function is called with five arguments: the tab, a list of all tabs, the face returned by the previously called modifier, -whether the tab is a buffer, and whether the tab is selected." +whether the tab is a buffer (when nil, the buffer is extracted from +the association list key `buffer'), and whether the tab is selected." :type '(repeat (choice (function-item tab-line-tab-face-special) (function-item tab-line-tab-face-modified) @@ -418,7 +419,10 @@ Used only for `tab-line-tabs-mode-buffers' and `tab-line-tabs-buffer-groups'.") (funcall tab-line-tabs-buffer-list-function))))) (defun tab-line-tab-modified-p (tab buffer-p) - "Return t if TAB is modified." + "Return t if TAB's buffer is modified. +BUFFER-P specifies whether the tab is a buffer. +When nil, the buffer is extracted from the association list +key `buffer'." (let ((buffer (if buffer-p tab (cdr (assq 'buffer tab))))) (when (and buffer (buffer-file-name buffer) (buffer-modified-p buffer)) t))) @@ -723,7 +727,7 @@ When TAB is a non-file-visiting buffer, make FACE inherit from (defun tab-line-tab-face-modified (tab _tabs face buffer-p _selected-p) "Return FACE for TAB according to whether its buffer is modified. -When TAB is a modified, file-backed buffer, make FACE inherit +When TAB's buffer is a modified, file-backed buffer, make FACE inherit from `tab-line-tab-modified'. For use in `tab-line-tab-face-functions'." (when (tab-line-tab-modified-p tab buffer-p) From 2d2755c3d7b69a11372e8fb8eba3c342cc5f61e8 Mon Sep 17 00:00:00 2001 From: Martin Rudalics Date: Tue, 5 Aug 2025 11:08:38 +0200 Subject: [PATCH 033/216] Handle inhibiting implied resizing for frames without initial tool bar * src/xfns.c (x_set_tool_bar_lines, x_change_tool_bar_height) * src/pgtkfns.c (x_change_tool_bar_height) (pgtk_set_tool_bar_lines): Set f->tool_bar_resized to true so inhibiting implied resizing works for frames without initial tool bar too. See the thread https://lists.gnu.org/archive/html/emacs-devel/2025-08/msg00034.html. --- src/pgtkfns.c | 11 ++++++++++- src/xfns.c | 12 +++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/pgtkfns.c b/src/pgtkfns.c index 917c9e7ab67..2cb7f423206 100644 --- a/src/pgtkfns.c +++ b/src/pgtkfns.c @@ -541,6 +541,9 @@ x_change_tool_bar_height (struct frame *f, int height) if (FRAME_EXTERNAL_TOOL_BAR (f)) free_frame_tool_bar (f); FRAME_EXTERNAL_TOOL_BAR (f) = false; + /* Make sure implied resizing of frames without initial tool bar + can be inhibited too. */ + f->tool_bar_resized = true; } } @@ -552,7 +555,13 @@ pgtk_set_tool_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval) /* Treat tool bars like menu bars. */ if (FRAME_MINIBUF_ONLY_P (f)) - return; + { + /* Make sure implied resizing can be inhibited for minibuffer-only + frames too. */ + f->tool_bar_resized = true; + + return; + } /* Use VALUE only if an int >= 0. */ if (RANGED_FIXNUMP (0, value, INT_MAX)) diff --git a/src/xfns.c b/src/xfns.c index c547d45fe3a..e9ca756cc32 100644 --- a/src/xfns.c +++ b/src/xfns.c @@ -1856,7 +1856,14 @@ x_set_tool_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval) /* Treat tool bars like menu bars. */ if (FRAME_MINIBUF_ONLY_P (f)) - return; + { +#ifdef USE_GTK + /* Make sure implied resizing of minibuffer-only frames can be + inhibited too. */ + f->tool_bar_resized = true; +#endif + return; + } /* Use VALUE only if an int >= 0. */ if (RANGED_FIXNUMP (0, value, INT_MAX)) @@ -1888,6 +1895,9 @@ x_change_tool_bar_height (struct frame *f, int height) if (FRAME_EXTERNAL_TOOL_BAR (f)) free_frame_tool_bar (f); FRAME_EXTERNAL_TOOL_BAR (f) = false; + /* Make sure implied resizing of frames without initial tool bar + can be inhibited too. */ + f->tool_bar_resized = true; } #else /* !USE_GTK */ int unit = FRAME_LINE_HEIGHT (f); From 5ff8a9039583b0e9e05b59986126905542fd78a1 Mon Sep 17 00:00:00 2001 From: Michael Albinus Date: Tue, 5 Aug 2025 12:00:21 +0200 Subject: [PATCH 034/216] Tramp allows now external implementations for functions * doc/misc/tramp.texi (Frequently Asked Questions): Mention tramp-hlo. (New operations): New node. (Top, Files directories and localnames): Add it to @menu. * etc/NEWS: Mention Tramp's feature to add function implementations. Presentational fixes and improvements. * lisp/net/tramp.el (tramp-file-name-for-operation-external): New defvar. (tramp-file-name-for-operation): Use `memq'. Handle external operations. Raise `remote-file-error' error in case of. (tramp-add-external-operation, tramp-remove-external-operation): New defuns. * test/lisp/net/tramp-archive-tests.el (tramp-archive-test50-auto-load) (tramp-archive-test50-delay-load) (tramp-archive-test51-without-remote-files): Rename. * test/lisp/net/tramp-tests.el (tramp--test-operation) (tramp--handler-for-test-operation): New defuns. (tramp-test49-external-backend-function): New test. (tramp-test50-auto-load, tramp-test50-delay-load) (tramp-test50-recursive-load, tramp-test50-remote-load-path) (tramp-test51-without-remote-files, tramp-test52-unload): Rename. --- doc/misc/tramp.texi | 104 ++++++++++++++++++ etc/NEWS | 24 ++-- lisp/net/tramp.el | 158 +++++++++++++++++++-------- test/lisp/net/tramp-archive-tests.el | 6 +- test/lisp/net/tramp-tests.el | 96 +++++++++++++++- 5 files changed, 328 insertions(+), 60 deletions(-) diff --git a/doc/misc/tramp.texi b/doc/misc/tramp.texi index 21f12a38e0b..7fbd4f898f5 100644 --- a/doc/misc/tramp.texi +++ b/doc/misc/tramp.texi @@ -169,6 +169,7 @@ How file names, directories and localnames are mangled and managed * Localname deconstruction:: Breaking a localname into its components. * External packages:: Integration with external Lisp packages. * Extension packages:: Adding new methods to @value{tramp}. +* New operations:: Handling further operations in @value{tramp}. @end detailmenu @end menu @@ -5472,6 +5473,13 @@ Suppress reading the remote history file in @code{shell}. Set Disable excessive traces. Set @code{tramp-verbose} to 3 or lower, default being 3. Increase trace levels temporarily when hunting for bugs. + +@item +Use a package with @value{tramp} specific implementation of high-level +operations. For example, the GNU ELPA package @file{tramp-hlo} +implements specialized versions of @code{dir-locals--all-files}, +@code{locate-dominating-file} and @code{dir-locals-find-file} for +@value{tramp}'s @code{tramp-sh} backend (@pxref{New operations}). @end itemize @@ -6457,6 +6465,7 @@ programs. * Localname deconstruction:: Splitting a localname into its component parts. * External packages:: Integrating with external Lisp packages. * Extension packages:: Adding new methods to @value{tramp}. +* New operations:: Handling further operations in @value{tramp}. @end menu @@ -6721,6 +6730,101 @@ The trick is to wrap the function definition of @code{;;;###autoload} cookie. +@node New operations +@section Handling further operations in @value{tramp} + +By default, @value{tramp} handles the basic operations listed in +@ref{Magic File Names, , Magic File Name Operations, elisp}. +Sometimes, it is desired to support more complex operations directly, +mainly for performance reasons. + +An external package package could add an own implementation of an +operation to @value{tramp}, which avoids the performance overhead +caused by using the basic operations which are aware of remote files. +For example, it could implement this by using an own shell script +which collects the information on the remote host for this very +special purpose with one round-trip per-call. + +@defun tramp-add-external-operation operation function backend +This adds an implementation of @var{operation} to @value{tramp}'s +backend @var{backend}. @var{function} is the new implementation. + +Both @var{operation} and @var{function} shall be function symbols. +They must have the same argument list. The first argument is used to +determine, whether @value{tramp} is invoked (check for remote file +name syntax). It must be a string or nil, in the latter case +@code{default-directory} is used for the check. + +@var{backend}, also a symbol, is the feature name of a @value{tramp} +backend (except @code{tramp-ftp}). The new implementation will be +applied only for this backend. Example: + +@lisp +@group +(defun test-operation (file) + (message "Original implementation for %s" file)) +@end group + +@group +(defun handle-test-operation (file) + (message "Handler implementation for %s" file)) +@end group + +@group +(tramp-add-external-operation + #'test-operation #'handle-test-operation 'tramp-sh) +@end group +@end lisp + +Then we have the different use cases: + +@lisp +@group +;; Local file name. +(test-operation "/a/b") +@result{} "Original implementation for /a/b" +@end group + +@group +;; Remote file name, handled by `tramp-sh'. +(test-operation "/ssh::/a/b") +@result{} "Handler implementation for /ssh::/a/b" +@end group + +@group +;; Remote file name, handled by `tramp-gvfs'. +(test-operation "/sftp::/a/b") +@result{} "Original implementation for /sftp::/a/b" +@end group +@end lisp + +@var{function} is implemented like an ordinary @value{tramp} backend +handler, see the examples in @code{tramp--handle-*} and +@code{tramp-handle-*}. It can expect, that the first argument (or +@code{default-directory}, if that is @code{nil}) has remote file name +syntax. It shall use @value{tramp} internal macros and functions like +@code{with-parsed-tramp-file-name} and the different cache functions. + +If the same @var{function} shall be used for different @value{tramp} +backends, @code{tramp-add-external-operation} must be called for every +backend, respectively. +@end defun + +@defun tramp-remove-external-operation operation backend +The handler for @var{operation}, added by +@code{tramp-add-external-operation}, is removed from @var{backend}. +If there are handlers of @var{operation} for other @var{backend}s, +they are kept. Example: + +@lisp +@group +(tramp-remove-external-operation + #'test-operation 'tramp-sh) +@end group +@end lisp +@end defun + + @node Traces and Profiles @chapter How to Customize Traces @vindex tramp-verbose diff --git a/etc/NEWS b/etc/NEWS index 85fad599048..e4fb833c0be 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1612,6 +1612,15 @@ This feature is experimental. --- *** Implementation of filesystem notifications for connection method "smb". ++++ +*** New functions to extend the set of operations with a remote implementation. +The new functions 'tramp-add-external-operation' and +'tramp-remove-external-operation' allow to add an implementation for +other operations but the defined set of magic file name operations. +This can be used by external ELPA packages for performance optimizations +in special cases. For more information, see "(tramp) New operations" in +the Tramp manual. + ** Diff --- @@ -1836,11 +1845,11 @@ restart Emacs. *** Support of 'electric-layout-mode' added. --- -*** DEL now deletes the text in the active region when point is between indentation. -The command 'python-indent-dedent-line-backspace' (by default bound to -'DEL') now deletes the text in the region and deactivates the mark if -Transient Mark mode is enabled, the mark is active, and prefix argument -is 1. +*** 'DEL' now deletes the text in the active region. +When point is between indentation, the command +'python-indent-dedent-line-backspace' (by default bound to 'DEL') now +deletes the text in the region and deactivates the mark if Transient +Mark mode is enabled, the mark is active, and prefix argument is 1. ** Tmm Menubar @@ -2481,10 +2490,10 @@ Use the byte-compiler instead; it provides more and more useful warnings. --- *** New user option 'newsticker-hide-old-feed-header'. It controls whether to automatically hide the header of feeds whose -items are all old or obsolete in the plainview *newsticker* buffer. +items are all old or obsolete in the plainview "*newsticker*" buffer. This is only visually interesting if the content of those feeds are also hidden (see 'newsticker-hide-old-items-in-newsticker-buffer' and -'newsticker-show-descriptions-of-new-items')." +'newsticker-show-descriptions-of-new-items'). --- *** New commands to hide and show headers of old newsticker feeds. @@ -2496,7 +2505,6 @@ or obsolete. ** CPerl mode *** Syntax of Perl up to version 5.42 is supported. - CPerl mode creates imenu entries for ":writer" generated accessors and recognizes the new functions "all" and "any". See https://perldoc.perl.org/5.42.0/perldelta for details. diff --git a/lisp/net/tramp.el b/lisp/net/tramp.el index 2208ce880d7..f3e9556f797 100644 --- a/lisp/net/tramp.el +++ b/lisp/net/tramp.el @@ -2380,6 +2380,9 @@ arguments to pass to the OPERATION." signal-hook-function) (apply operation args))) +(defvar tramp-file-name-for-operation-external nil + "List of operations added by external packages.") + ;; We handle here all file primitives. Most of them have the file ;; name as first parameter; nevertheless we check for them explicitly ;; in order to be signaled if a new primitive appears. This @@ -2387,6 +2390,10 @@ arguments to pass to the OPERATION." ;; syntactical means whether a foreign method must be called. It would ;; ease the life if `file-name-handler-alist' would support a decision ;; function as well but regexp only. +;; Operations added by external packages are kept in +;; `tramp-file-name-for-operation-external'. They expect the file +;; name to be checked as first argument or, if there isn't any +;; argument, `default-directory'. (defun tramp-file-name-for-operation (operation &rest args) "Return file name related to OPERATION file primitive. ARGS are the arguments OPERATION has been called with. @@ -2396,40 +2403,40 @@ first argument of `expand-file-name' is absolute and not remote. Must be handled by the callers." (cond ;; FILE resp DIRECTORY. - ((member operation - '(access-file byte-compiler-base-file-name delete-directory - delete-file diff-latest-backup-file directory-file-name - directory-files directory-files-and-attributes dired-compress-file - dired-uncache file-acl file-accessible-directory-p file-attributes - file-directory-p file-executable-p file-exists-p file-local-copy - file-locked-p file-modes file-name-as-directory - file-name-case-insensitive-p file-name-directory - file-name-nondirectory file-name-sans-versions - file-notify-add-watch file-ownership-preserved-p file-readable-p - file-regular-p file-remote-p file-selinux-context file-symlink-p - file-system-info file-truename file-writable-p - find-backup-file-name get-file-buffer - insert-directory insert-file-contents load lock-file make-directory - make-lock-file-name set-file-acl set-file-modes - set-file-selinux-context set-file-times substitute-in-file-name - unhandled-file-name-directory unlock-file vc-registered - ;; Emacs 28- only. - make-directory-internal - ;; Emacs 29+ only. - abbreviate-file-name - ;; Tramp internal magic file name function. - tramp-set-file-uid-gid)) + ((memq operation + '(access-file byte-compiler-base-file-name delete-directory + delete-file diff-latest-backup-file directory-file-name + directory-files directory-files-and-attributes dired-compress-file + dired-uncache file-acl file-accessible-directory-p file-attributes + file-directory-p file-executable-p file-exists-p file-local-copy + file-locked-p file-modes file-name-as-directory + file-name-case-insensitive-p file-name-directory + file-name-nondirectory file-name-sans-versions + file-notify-add-watch file-ownership-preserved-p file-readable-p + file-regular-p file-remote-p file-selinux-context file-symlink-p + file-system-info file-truename file-writable-p + find-backup-file-name get-file-buffer + insert-directory insert-file-contents load lock-file make-directory + make-lock-file-name set-file-acl set-file-modes + set-file-selinux-context set-file-times substitute-in-file-name + unhandled-file-name-directory unlock-file vc-registered + ;; Emacs 28- only. + make-directory-internal + ;; Emacs 29+ only. + abbreviate-file-name + ;; Tramp internal magic file name function. + tramp-set-file-uid-gid)) (if (file-name-absolute-p (nth 0 args)) (nth 0 args) default-directory)) ;; STRING FILE. ((eq operation 'make-symbolic-link) (nth 1 args)) ;; FILE DIRECTORY resp FILE1 FILE2. - ((member operation - '(add-name-to-file copy-directory copy-file - file-equal-p file-in-directory-p - file-name-all-completions file-name-completion - file-newer-than-file-p rename-file)) + ((memq operation + '(add-name-to-file copy-directory copy-file + file-equal-p file-in-directory-p + file-name-all-completions file-name-completion + file-newer-than-file-p rename-file)) (cond ((tramp-tramp-file-p (nth 0 args)) (nth 0 args)) ((file-name-absolute-p (nth 1 args)) (nth 1 args)) @@ -2446,31 +2453,39 @@ Must be handled by the callers." (nth 2 args) default-directory)) ;; BUFFER. - ((member operation - '(make-auto-save-file-name - set-visited-file-modtime verify-visited-file-modtime)) + ((memq operation + '(make-auto-save-file-name + set-visited-file-modtime verify-visited-file-modtime)) (buffer-file-name (if (bufferp (nth 0 args)) (nth 0 args) (current-buffer)))) ;; COMMAND. - ((member operation - '(exec-path make-nearby-temp-file make-process process-file - shell-command start-file-process temporary-file-directory - ;; Emacs 29+ only. - list-system-processes memory-info process-attributes - ;; Emacs 30+ only. - file-group-gid file-user-uid)) + ((memq operation + '(exec-path make-nearby-temp-file make-process process-file + shell-command start-file-process temporary-file-directory + ;; Emacs 29+ only. + list-system-processes memory-info process-attributes + ;; Emacs 30+ only. + file-group-gid file-user-uid)) default-directory) ;; PROC. - ((member operation '(file-notify-rm-watch file-notify-valid-p)) + ((memq operation '(file-notify-rm-watch file-notify-valid-p)) (when (processp (nth 0 args)) (tramp-get-default-directory (process-buffer (nth 0 args))))) ;; VEC. - ((member operation - '(tramp-get-home-directory tramp-get-remote-gid - tramp-get-remote-groups tramp-get-remote-uid)) + ((memq operation + '(tramp-get-home-directory tramp-get-remote-gid + tramp-get-remote-groups tramp-get-remote-uid)) (tramp-make-tramp-file-name (nth 0 args))) + ;; FILE resp DIRECTORY. + ((and (memq operation tramp-file-name-for-operation-external) + (or (stringp (nth 0 args)) (null (nth 0 args)))) + (if (and (stringp (nth 0 args)) (file-name-absolute-p (nth 0 args))) + (nth 0 args) + default-directory)) ;; Unknown file primitive. - (t (error "Unknown file I/O primitive: %s" operation)))) + (t (unless (member 'remote-file-error debug-ignored-errors) + (tramp-error + nil 'remote-file-error "Unknown file I/O primitive: %s" operation))))) (defun tramp-find-foreign-file-name-handler (vec &optional _operation) "Return foreign file name handler if exists." @@ -2494,6 +2509,63 @@ Must be handled by the callers." res (cdr elt)))) res))) +(defun tramp-add-external-operation (operation function backend) + "Add FUNTION to Tramp BACKEND as handler for OPERATION. +OPERATION must not be one of the magic operations listed in Info +node `(elisp) Magic File Names'. FUNCTION must have the same argument +list as OPERATION. BACKEND, a symbol, must be one of the Tramp backend +packages like `tramp-sh' (except `tramp-ftp')." + (require backend) + (when-let* ((fnha + (intern-soft + (concat (symbol-name backend) "-file-name-handler-alist"))) + ((boundp fnha))) + ;; Make BACKEND aware of the new operation. + (add-to-list fnha (cons operation function)) + (unless (memq operation tramp-file-name-for-operation-external) + ;; Make Tramp aware of the new operation. + (add-to-list 'tramp-file-name-for-operation-external operation) + (put #'tramp-file-name-handler + 'operations + (cons operation (get 'tramp-file-name-handler 'operations))) + ;; Add an advice for OPERATION, in order to invoke the handler FUNCTION. + (advice-add + operation :around + `(lambda (orig-fun &rest args) + (if-let* ((handler + (find-file-name-handler + (or (car args) default-directory) #',operation))) + (apply handler #',operation args) + (apply orig-fun args))) + `((name . ,(concat "tramp-advice-" (symbol-name operation)))))))) + +(defun tramp-remove-external-operation (operation backend) + "Remove OPERATION from Tramp BACKEND as handler for OPERATION. +OPERATION must not be one of the magic operations listed in Info +node `(elisp) Magic File Names'. BACKEND, a symbol, must be one of the +Tramp backend packages like `tramp-sh'." + ;; Remove OPERATION from BACKEND. + (when-let* ((fnha + (intern-soft + (concat (symbol-name backend) "-file-name-handler-alist"))) + ((boundp fnha))) + (setf (alist-get operation (symbol-value fnha) nil 'remove) nil)) + ;; Check, whether OPERATION is still used in any backend. + (unless (seq-some + (lambda (item) + (when-let* + ((fnha (intern-soft (concat (symbol-name (cdr item)) "-alist"))) + ((boundp fnha))) + (alist-get operation (symbol-value fnha)))) + tramp-foreign-file-name-handler-alist) + ;; Make Tramp unaware of OPERATION. + (setq tramp-file-name-for-operation-external + (delq operation tramp-file-name-for-operation-external)) + (put #'tramp-file-name-handler + 'operations (delq operation (get 'tramp-file-name-handler 'operations))) + ;; Remove the advice for OPERATION. + (advice-remove operation (concat "tramp-advice-" (symbol-name operation))))) + ;; Main function. ;;;###autoload (defun tramp-file-name-handler (operation &rest args) diff --git a/test/lisp/net/tramp-archive-tests.el b/test/lisp/net/tramp-archive-tests.el index 5846dafa7d8..0ff0f3ddd4d 100644 --- a/test/lisp/net/tramp-archive-tests.el +++ b/test/lisp/net/tramp-archive-tests.el @@ -856,7 +856,7 @@ This tests also `file-executable-p', `file-writable-p' and `set-file-modes'." (should (equal uid (with-no-warnings (file-user-uid)))) (should (equal gid (with-no-warnings (file-group-gid))))))) -(ert-deftest tramp-archive-test48-auto-load () +(ert-deftest tramp-archive-test50-auto-load () "Check that `tramp-archive' autoloads properly." :tags '(:expensive-test) (skip-unless tramp-archive-enabled) @@ -898,7 +898,7 @@ This tests also `file-executable-p', `file-writable-p' and `set-file-modes'." (format "(setq tramp-archive-enabled %s)" enabled)) (shell-quote-argument (format code file))))))))))) -(ert-deftest tramp-archive-test48-delay-load () +(ert-deftest tramp-archive-test50-delay-load () "Check that `tramp-archive' is loaded lazily, only when needed." :tags '(:expensive-test) (skip-unless tramp-archive-enabled) @@ -937,7 +937,7 @@ This tests also `file-executable-p', `file-writable-p' and `set-file-modes'." code tae tramp-archive-test-file-archive (concat tramp-archive-test-archive "foo")))))))))) -(ert-deftest tramp-archive-test49-without-remote-files () +(ert-deftest tramp-archive-test51-without-remote-files () "Check that Tramp can be suppressed." (skip-unless tramp-archive-enabled) diff --git a/test/lisp/net/tramp-tests.el b/test/lisp/net/tramp-tests.el index d0ce17d2497..42426c76498 100644 --- a/test/lisp/net/tramp-tests.el +++ b/test/lisp/net/tramp-tests.el @@ -8474,8 +8474,92 @@ process sentinels. They shall not disturb each other." ;; Cleanup. (ignore-errors (delete-file tmp-name)))))) +(defun tramp--test-operation (&optional _file) + "Test operation." + "Test operation") + +(defun tramp--handler-for-test-operation (&optional _file) + "Test operation handler." + "Test operation handler") + +(ert-deftest tramp-test49-external-backend-function () + "Check that Tramp handles external functions for a given backend." + (skip-unless (tramp--test-enabled)) + (skip-unless (not (tramp--test-ange-ftp-p))) + + (let* ((file-name-handler + (tramp-find-foreign-file-name-handler tramp-test-vec)) + (backend + (intern + (string-remove-suffix + "-file-name-handler" (symbol-name file-name-handler))))) + + ;; There is no backend specific code. + (should-not + (string-equal (tramp--test-operation ert-remote-temporary-file-directory) + (tramp--handler-for-test-operation + ert-remote-temporary-file-directory))) + (should-not + (string-equal (tramp--test-operation temporary-file-directory) + (tramp--handler-for-test-operation + temporary-file-directory))) + (let ((default-directory ert-remote-temporary-file-directory)) + (should-not + (string-equal (tramp--test-operation) + (tramp--handler-for-test-operation)))) + (let ((default-directory temporary-file-directory)) + (should-not + (string-equal (tramp--test-operation) + (tramp--handler-for-test-operation)))) + + (should-error + (tramp-add-external-operation + #'tramp--test-operation + #'tramp--handler-for-test-operation 'foo) + :type 'file-missing) + (tramp-add-external-operation + #'tramp--test-operation + #'tramp--handler-for-test-operation backend) + ;; The backend specific function is called. + (should + (string-equal (tramp--test-operation ert-remote-temporary-file-directory) + (tramp--handler-for-test-operation + ert-remote-temporary-file-directory))) + (should-not + (string-equal (tramp--test-operation temporary-file-directory) + (tramp--handler-for-test-operation + temporary-file-directory))) + (let ((default-directory ert-remote-temporary-file-directory)) + (should + (string-equal (tramp--test-operation) + (tramp--handler-for-test-operation)))) + (let ((default-directory temporary-file-directory)) + (should-not + (string-equal (tramp--test-operation) + (tramp--handler-for-test-operation)))) + + (tramp-remove-external-operation + #'tramp--test-operation backend) + ;; There is no backend specific code. + (should-not + (string-equal (tramp--test-operation ert-remote-temporary-file-directory) + (tramp--handler-for-test-operation + ert-remote-temporary-file-directory))) + (should-not + (string-equal (tramp--test-operation temporary-file-directory) + (tramp--handler-for-test-operation + temporary-file-directory))) + (let ((default-directory ert-remote-temporary-file-directory)) + (should-not + (string-equal (tramp--test-operation) + (tramp--handler-for-test-operation)))) + (let ((default-directory temporary-file-directory)) + (should-not + (string-equal (tramp--test-operation) + (tramp--handler-for-test-operation)))))) + ;; This test is inspired by Bug#29163. -(ert-deftest tramp-test49-auto-load () +(ert-deftest tramp-test50-auto-load () "Check that Tramp autoloads properly." ;; If we use another syntax but `default', Tramp is already loaded ;; due to the `tramp-change-syntax' call. @@ -8500,7 +8584,7 @@ process sentinels. They shall not disturb each other." (mapconcat #'shell-quote-argument load-path " -L ") (shell-quote-argument code))))))) -(ert-deftest tramp-test49-delay-load () +(ert-deftest tramp-test50-delay-load () "Check that Tramp is loaded lazily, only when needed." ;; Tramp is neither loaded at Emacs startup, nor when completing a ;; non-Tramp file name like "/foo". Completing a Tramp-alike file @@ -8530,7 +8614,7 @@ process sentinels. They shall not disturb each other." (mapconcat #'shell-quote-argument load-path " -L ") (shell-quote-argument (format code tm))))))))) -(ert-deftest tramp-test49-recursive-load () +(ert-deftest tramp-test50-recursive-load () "Check that Tramp does not fail due to recursive load." (skip-unless (tramp--test-enabled)) @@ -8554,7 +8638,7 @@ process sentinels. They shall not disturb each other." (mapconcat #'shell-quote-argument load-path " -L ") (shell-quote-argument code)))))))) -(ert-deftest tramp-test49-remote-load-path () +(ert-deftest tramp-test50-remote-load-path () "Check that Tramp autoloads its packages with remote `load-path'." ;; `tramp-cleanup-all-connections' is autoloaded from tramp-cmds.el. ;; It shall still work, when a remote file name is in the @@ -8579,7 +8663,7 @@ process sentinels. They shall not disturb each other." (mapconcat #'shell-quote-argument load-path " -L ") (shell-quote-argument code))))))) -(ert-deftest tramp-test50-without-remote-files () +(ert-deftest tramp-test51-without-remote-files () "Check that Tramp can be suppressed." (skip-unless (tramp--test-enabled)) @@ -8594,7 +8678,7 @@ process sentinels. They shall not disturb each other." (setq tramp-mode t) (should (file-remote-p ert-remote-temporary-file-directory))) -(ert-deftest tramp-test51-unload () +(ert-deftest tramp-test52-unload () "Check that Tramp and its subpackages unload completely. Since it unloads Tramp, it shall be the last test to run." :tags '(:expensive-test) From 32ba66d97d77a8f2ad5d8c6e2262fd934a42a657 Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Tue, 5 Aug 2025 15:30:17 +0300 Subject: [PATCH 035/216] ; Improve doc strings in tab-line.el * lisp/tab-line.el (tab-line-tab-modified-p) (tab-line-tab-face-special, tab-line-tab-face-modified) (tab-line-tab-face-group, tab-line-tab-face-functions): Doc fixes. --- lisp/tab-line.el | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/lisp/tab-line.el b/lisp/tab-line.el index fe903c02041..2d8c75574bb 100644 --- a/lisp/tab-line.el +++ b/lisp/tab-line.el @@ -43,7 +43,8 @@ Each function is called with five arguments: the tab, a list of all tabs, the face returned by the previously called modifier, whether the tab is a buffer (when nil, the buffer is extracted from -the association list key `buffer'), and whether the tab is selected." +the association list using the key `buffer'), and whether the tab +is selected." :type '(repeat (choice (function-item tab-line-tab-face-special) (function-item tab-line-tab-face-modified) @@ -420,9 +421,8 @@ Used only for `tab-line-tabs-mode-buffers' and `tab-line-tabs-buffer-groups'.") (defun tab-line-tab-modified-p (tab buffer-p) "Return t if TAB's buffer is modified. -BUFFER-P specifies whether the tab is a buffer. -When nil, the buffer is extracted from the association list -key `buffer'." +BUFFER-P specifies whether the tab is a buffer; if nil, the buffer +is extracted from the association list TAB using the key `buffer'." (let ((buffer (if buffer-p tab (cdr (assq 'buffer tab))))) (when (and buffer (buffer-file-name buffer) (buffer-modified-p buffer)) t))) @@ -717,9 +717,11 @@ inherit from `tab-line-tab-inactive-alternate'. For use in (defun tab-line-tab-face-special (tab _tabs face buffer-p _selected-p) "Return FACE for TAB according to whether its buffer is special. -When TAB is a non-file-visiting buffer, make FACE inherit from -`tab-line-tab-special'. For use in -`tab-line-tab-face-functions'." +TAB is either a buffer (if BUFFER-P is non-nil), or an association +list with the buffer given by the key `buffer'. +When TAB specifies a non-file-visiting buffer, make FACE inherit +from `tab-line-tab-special'. +For use in `tab-line-tab-face-functions'." (let ((buffer (if buffer-p tab (cdr (assq 'buffer tab))))) (when (and buffer (not (buffer-file-name buffer))) (setf face `(:inherit (tab-line-tab-special ,face))))) @@ -727,15 +729,19 @@ When TAB is a non-file-visiting buffer, make FACE inherit from (defun tab-line-tab-face-modified (tab _tabs face buffer-p _selected-p) "Return FACE for TAB according to whether its buffer is modified. +TAB is either a buffer (if BUFFER-P is non-nil), or an association +list with the buffer given by the key `buffer'. When TAB's buffer is a modified, file-backed buffer, make FACE inherit -from `tab-line-tab-modified'. For use in -`tab-line-tab-face-functions'." +from `tab-line-tab-modified'. +For use in `tab-line-tab-face-functions'." (when (tab-line-tab-modified-p tab buffer-p) (setf face `(:inherit (tab-line-tab-modified ,face)))) face) (defun tab-line-tab-face-group (tab _tabs face _buffer-p _selected-p) "Return FACE for TAB according to whether it's a group tab. +TAB is either a buffer (if BUFFER-P is non-nil), or an association +list with the buffer given by the key `buffer'. For use in `tab-line-tab-face-functions'." (when (alist-get 'group-tab tab) (setf face `(:inherit (tab-line-tab-group ,face)))) From 6b2856f9cabb91a12dc88e1648a117a9671b25ee Mon Sep 17 00:00:00 2001 From: Paul Nelson Date: Mon, 28 Jul 2025 11:52:16 +0200 Subject: [PATCH 036/216] Allow numbered buffer selection for project-shell * lisp/progmodes/project.el (project-shell, project-eshell): When a numeric prefix is supplied, the command switches to or creates a session buffer whose name has that number as a suffix. For instance, C-2 yields "*name-of-project-shell<2>*". As before, a plain universal argument C-u creates a new session with an automatically generated numeric suffix. This change makes the behavior of the project-shell commands consistent with how M-x eshell handles numeric prefixes. * etc/NEWS: Announce the change. (bug#76500) --- etc/NEWS | 7 +++++++ lisp/progmodes/project.el | 28 +++++++++++++++++----------- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/etc/NEWS b/etc/NEWS index e4fb833c0be..a34a3d73292 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -496,6 +496,13 @@ This is like 'C-x s', but only for this project's buffers. *** 'project-remember-project' can now be called interactively. +--- +*** 'project-shell' and 'project-eshell' support numeric prefix buffer naming. +They now accept numeric prefix arguments to select or create numbered +shell sessions. For example, 'C-2 C-x p s' switches to or creates a +buffer named "*name-of-project-shell<2>*". By comparison, a plain +universal argument as in 'C-u C-x p s' always creates a new session. + ** Registers *** New functions 'buffer-to-register' and 'file-to-register'. diff --git a/lisp/progmodes/project.el b/lisp/progmodes/project.el index f45cb45aacb..dafc01c7684 100644 --- a/lisp/progmodes/project.el +++ b/lisp/progmodes/project.el @@ -1429,18 +1429,26 @@ The current buffer's `default-directory' is available as part of If a buffer already exists for running a shell in the project's root, switch to it. Otherwise, create a new shell buffer. With \\[universal-argument] prefix arg, create a new inferior shell buffer even -if one already exists." +if one already exists. +With numeric prefix arg, switch to the session with that number, or +create it if it doesn't already exist." (interactive) (require 'comint) (let* ((default-directory (project-root (project-current t))) - (default-project-shell-name (project-prefixed-buffer-name "shell")) - (shell-buffer (get-buffer default-project-shell-name))) + (base-name (project-prefixed-buffer-name "shell")) + (shell-buffer-name + (cond ((numberp current-prefix-arg) + (format "%s<%d>" base-name current-prefix-arg)) + (current-prefix-arg + (generate-new-buffer-name base-name)) + (t base-name))) + (shell-buffer (get-buffer shell-buffer-name))) (if (and shell-buffer (not current-prefix-arg)) (if (comint-check-proc shell-buffer) (pop-to-buffer shell-buffer (append display-buffer--same-window-action '((category . comint)))) (shell shell-buffer)) - (shell (generate-new-buffer-name default-project-shell-name))))) + (shell shell-buffer-name)))) ;;;###autoload (defun project-eshell () @@ -1448,16 +1456,14 @@ if one already exists." If a buffer already exists for running Eshell in the project's root, switch to it. Otherwise, create a new Eshell buffer. With \\[universal-argument] prefix arg, create a new Eshell buffer even -if one already exists." +if one already exists. +With numeric prefix arg, switch to the session with that number, or +create it if it doesn't already exist." (interactive) (defvar eshell-buffer-name) (let* ((default-directory (project-root (project-current t))) - (eshell-buffer-name (project-prefixed-buffer-name "eshell")) - (eshell-buffer (get-buffer eshell-buffer-name))) - (if (and eshell-buffer (not current-prefix-arg)) - (pop-to-buffer eshell-buffer (append display-buffer--same-window-action - '((category . comint)))) - (eshell t)))) + (eshell-buffer-name (project-prefixed-buffer-name "eshell"))) + (eshell current-prefix-arg))) ;;;###autoload (defun project-async-shell-command () From ca07819af77d810d1f9ada3156b679074195b937 Mon Sep 17 00:00:00 2001 From: Martin Rudalics Date: Wed, 6 Aug 2025 09:29:41 +0200 Subject: [PATCH 037/216] ; * lisp/files.el (find-file-noselect): Fix doc-string typo. --- lisp/files.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lisp/files.el b/lisp/files.el index 33eefcc1da3..4567f91bc8e 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -2533,7 +2533,7 @@ If a buffer exists visiting FILENAME, return that one, but verify that the file has not changed since visited or saved. The buffer is not selected, just returned to the caller. Optional second arg NOWARN non-nil means suppress any warning messages, -and also don't verify the that the file has not been changed since +and also don't verify that the file has not been changed since last visited or saved. Optional third arg RAWFILE non-nil means the file is read literally. Optional fourth arg WILDCARDS non-nil means do wildcard processing From f0da94e2c28fe79b2bb6f16697438fbf3777ebb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mattias=20Engdeg=C3=A5rd?= Date: Wed, 6 Aug 2025 12:53:06 +0200 Subject: [PATCH 038/216] external-completion: specify a required Emacs version * lisp/external-completion.el: Require 26.1 (picked out of thin air). (external-completion-table): Undo use of the hash-table-contains-p, recently introduced. --- lisp/external-completion.el | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lisp/external-completion.el b/lisp/external-completion.el index a1aa7ec9cb6..5fd29f801fe 100644 --- a/lisp/external-completion.el +++ b/lisp/external-completion.el @@ -5,6 +5,7 @@ ;; Version: 0.1 ;; Author: Stefan Monnier ;; Maintainer: João Távora +;; Package-Requires: ((emacs "26.1")) ;; Keywords: ;; This is a GNU ELPA :core package. Avoid functionality that is not @@ -117,10 +118,11 @@ EXPANDED-PATTERN." completion-category-defaults))) (let ((cache (make-hash-table :test #'equal))) (cl-flet ((lookup-internal (string point) - (let ((key (cons string point))) - (if (hash-table-contains-p key cache) - (gethash key cache) - (puthash key (funcall lookup string point) cache))))) + (let* ((key (cons string point)) + (probe (gethash key cache 'external--notfound))) + (if (eq probe 'external--notfound) + (puthash key (funcall lookup string point) cache) + probe)))) (lambda (string pred action) (pcase action (`metadata From d1ab95b175e512695677cbe93ea1e1874f84f31c Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Wed, 6 Aug 2025 15:15:57 +0100 Subject: [PATCH 039/216] * lisp/vc/vc.el (vc-register): Fix interactive spec (bug#79183). --- lisp/vc/vc.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lisp/vc/vc.el b/lisp/vc/vc.el index f1d0fba0e26..cb6197c62b7 100644 --- a/lisp/vc/vc.el +++ b/lisp/vc/vc.el @@ -1479,7 +1479,7 @@ itself responsible for the file (usually because other files in that directory are already registered under that backend) will be used to register the file. If no backend declares itself responsible, the first backend that could register the file is used." - (interactive "P") + (interactive) (let* ((fileset-arg (or vc-fileset (vc-deduce-fileset nil t))) (backend (car fileset-arg)) (files (nth 1 fileset-arg))) From d950ca8ccd00f200d17415846b612c1c1909c72b Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Wed, 6 Aug 2025 16:06:13 +0100 Subject: [PATCH 040/216] * lisp/vc/vc-hg.el (vc-hg-print-log): Revise log-outgoing revset. --- lisp/vc/vc-hg.el | 17 +++++++---------- lisp/vc/vc.el | 5 +++-- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/lisp/vc/vc-hg.el b/lisp/vc/vc-hg.el index 1e064a24e39..b39a8c6928a 100644 --- a/lisp/vc/vc-hg.el +++ b/lisp/vc/vc-hg.el @@ -429,26 +429,23 @@ the log starting from that revision." ;; kinds of ranges of revisions for the log to show: ;; - ranges by revision number: -rN:M ;; - ranges according to the DAG: -rN::M or -rN..M - ;; Note that N & M can be revision numbers or changeset IDs - ;; (hashes). In either case a revision number range + ;; Note that N and M can be revision numbers or changeset + ;; IDs (hashes). In either case a revision number range ;; includes those commits with revision numbers between the ;; revision numbers of the commits identified by N and M. ;; See . ;; - ;; DAG ranges might seem like Git's double-dot notation for - ;; ranges, but there is (at least) the following - ;; difference: with -rN::M, commits from other branches - ;; aren't included in the log. + ;; DAG ranges are not the same as Git's double-dot ranges. + ;; Git's 'x..y' is more like Mercurial's 'only(y, x)' than + ;; it is like Mercurial's x::y. In addition, with -rN::M, + ;; commits from other branches aren't included in the log. ;; ;; VC has always used ranges by revision numbers, such that ;; commits from all branches are included in the log. - ;; `vc-log-outgoing' is a special case: there we really - ;; need to exclude the incoming revision and its ancestors - ;; because those are certainly not outgoing. (cond ((not (stringp limit)) (format "-r%s:0" start)) ((eq vc-log-view-type 'log-outgoing) - (format "-rreverse(%s::%s & !%s)" limit start limit)) + (format "-rreverse(only(%s, %s))" start limit)) (t (format "-r%s:%s & !%s" start limit limit))) (nconc diff --git a/lisp/vc/vc.el b/lisp/vc/vc.el index 3ba01210667..67eed872fd7 100644 --- a/lisp/vc/vc.el +++ b/lisp/vc/vc.el @@ -4344,8 +4344,9 @@ Return a list of the form (FILE VC-STATE EXTRA) for each file. VC-STATE is the current VC state of the file, and EXTRA is optional, backend-specific information. Normally files in the `up-to-date' and `ignored' states are not -included. If the optional argument FILES is non-nil, report on only -those files, and don't exclude them for being in one of those states. +included. +If the optional argument FILES is non-nil, report on only items in +FILES, and don't exclude any for being `up-to-date' or `ignored'. BACKEND is the VC backend; if nil or omitted, it defaults to the result of calling `vc-responsible-backend' with DIRECTORY as its first and only argument. From d3cd93dc4d917b6438117e82486f044e324a6088 Mon Sep 17 00:00:00 2001 From: Michael Albinus Date: Wed, 6 Aug 2025 17:59:02 +0200 Subject: [PATCH 041/216] Add set-file-times implementation for Tramp smb method * lisp/net/tramp-adb.el (tramp-adb-handle-set-file-times): Don't use UTC in all cases. * lisp/net/tramp-smb.el (tramp-smb-handle-set-file-times): New defun. (tramp-smb-file-name-handler-alist): Use it. * test/lisp/net/tramp-tests.el (tramp-test22-file-times): Adapt test. --- lisp/net/tramp-adb.el | 4 ++-- lisp/net/tramp-smb.el | 11 ++++++++++- test/lisp/net/tramp-tests.el | 12 +++++++----- 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/lisp/net/tramp-adb.el b/lisp/net/tramp-adb.el index 2d240e8756b..5d6c053868b 100644 --- a/lisp/net/tramp-adb.el +++ b/lisp/net/tramp-adb.el @@ -573,9 +573,9 @@ Emacs dired can't find files." "touch -t %s %s %s") (format-time-string "%Y-%m-%dT%H:%M:%S.%NZ" time t) nofollow quoted-name (tramp-get-remote-null-device v) - (format-time-string "%Y-%m-%dT%H:%M:%S" time t) + (format-time-string "%Y-%m-%dT%H:%M:%S" time) nofollow quoted-name (tramp-get-remote-null-device v) - (format-time-string "%Y%m%d%H%M.%S" time t) + (format-time-string "%Y%m%d%H%M.%S" time) nofollow quoted-name))))) (defun tramp-adb-handle-copy-file diff --git a/lisp/net/tramp-smb.el b/lisp/net/tramp-smb.el index 28364de83f1..fdda945f120 100644 --- a/lisp/net/tramp-smb.el +++ b/lisp/net/tramp-smb.el @@ -295,7 +295,7 @@ See `tramp-actions-before-shell' for more info.") (set-file-acl . tramp-smb-handle-set-file-acl) (set-file-modes . tramp-smb-handle-set-file-modes) (set-file-selinux-context . ignore) - (set-file-times . ignore) + (set-file-times . tramp-smb-handle-set-file-times) (set-visited-file-modtime . tramp-handle-set-visited-file-modtime) (shell-command . tramp-handle-shell-command) (start-file-process . tramp-smb-handle-start-file-process) @@ -1534,6 +1534,15 @@ PRESERVE-UID-GID and PRESERVE-EXTENDED-ATTRIBUTES are completely ignored." (tramp-error v 'file-error "Error while changing file's mode %s" filename)))))) +(defun tramp-smb-handle-set-file-times (filename &optional time _flag) + "Like `set-file-times' for Tramp files." + (tramp-skeleton-set-file-modes-times-uid-gid filename + (tramp-smb-send-command + v (format + "utimes %s -1 -1 %s -1" + (tramp-smb-shell-quote-localname v) + (format-time-string "%Y:%m:%d-%H:%M:%S" (tramp-defined-time time)))))) + ;; We use BUFFER also as connection buffer during setup. Because of ;; this, its original contents must be saved, and restored once ;; connection has been setup. diff --git a/test/lisp/net/tramp-tests.el b/test/lisp/net/tramp-tests.el index 42426c76498..4438e0090d4 100644 --- a/test/lisp/net/tramp-tests.el +++ b/test/lisp/net/tramp-tests.el @@ -4552,7 +4552,8 @@ This tests also `make-symbolic-link', `file-truename' and `add-name-to-file'." (skip-unless (tramp--test-enabled)) (skip-unless (or (tramp--test-adb-p) (tramp--test-gvfs-p) - (tramp--test-sh-p) (tramp--test-sudoedit-p))) + (tramp--test-sh-p) (tramp--test-smb-p) + (tramp--test-sudoedit-p))) (dolist (quoted (if (tramp--test-expensive-test-p) '(nil t) '(nil))) (let ((tmp-name1 (tramp--test-make-temp-name nil quoted)) @@ -4565,9 +4566,10 @@ This tests also `make-symbolic-link', `file-truename' and `add-name-to-file'." (should (consp (file-attribute-modification-time (file-attributes tmp-name1)))) ;; Skip the test, if the remote handler is not able to set - ;; the correct time. - ;; Some remote machines cannot resolve seconds. So we use a minute. - (skip-unless (set-file-times tmp-name1 (seconds-to-time 60))) + ;; the correct time. Some remote machines cannot resolve + ;; seconds. tramp-adb.el needs at least a day. + (skip-unless + (set-file-times tmp-name1 (seconds-to-time (* 24 60 60)))) ;; Dumb remote shells without perl(1) or stat(1) are not ;; able to return the date correctly. They say "don't know". (unless (time-equal-p @@ -4577,7 +4579,7 @@ This tests also `make-symbolic-link', `file-truename' and `add-name-to-file'." (should (time-equal-p (file-attribute-modification-time (file-attributes tmp-name1)) - (seconds-to-time 60))) + (seconds-to-time (* 24 60 60)))) ;; Setting the time for not existing files shall fail. (should-error (set-file-times tmp-name2) From 68aaeb3519fd7f6176050e142f0dbc27e07992d2 Mon Sep 17 00:00:00 2001 From: Michael Albinus Date: Thu, 7 Aug 2025 09:05:22 +0200 Subject: [PATCH 042/216] Fix Tramp completion * lisp/net/tramp.el (tramp-skeleton-file-name-all-completions): Filter out "" hits. (Bug#79173) --- lisp/net/tramp.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lisp/net/tramp.el b/lisp/net/tramp.el index f3e9556f797..503b370cb3d 100644 --- a/lisp/net/tramp.el +++ b/lisp/net/tramp.el @@ -2972,7 +2972,7 @@ not in completion mode." BODY is the backend specific code." (declare (indent 2) (debug t)) `(ignore-error file-missing - (delete-dups (delq nil + (delete-dups (delq nil (delete "" (let* ((case-fold-search read-file-name-completion-ignore-case) (result (progn ,@body))) ;; Some storage systems do not return "." and "..". @@ -2989,7 +2989,7 @@ BODY is the backend specific code." (dolist (elt completion-regexp-list x) (unless (string-match-p elt x) (throw 'match nil)))))) result) - result)))))) + result))))))) (defvar tramp--last-hop-directory nil "Tracks the directory from which to run login programs.") From 3d8fbb0716df669b552daef8ada02b4357ca7b49 Mon Sep 17 00:00:00 2001 From: kobarity Date: Wed, 6 Aug 2025 23:08:49 +0900 Subject: [PATCH 043/216] Fix Python PDB tracking for remote inferior Python * lisp/progmodes/python.el (python-shell-local-prefix): New constant. (python-shell--convert-file-name-to-send): New function to add/remove prefix. (python-shell-send-string, python-shell-send-file): Changed to use 'python-shell--convert-file-name-to-send'. (python-pdbtrack-set-tracked-buffer): Changed to add/remove prefix. * test/lisp/progmodes/python-tests.el (python-shell--convert-file-name-to-send-1): New test. (Bug#79036) --- lisp/progmodes/python.el | 48 ++++++++++++++++++++++++----- test/lisp/progmodes/python-tests.el | 26 ++++++++++++++++ 2 files changed, 66 insertions(+), 8 deletions(-) diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 8b5ffae57d6..3cd20d6babf 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -3698,6 +3698,10 @@ def __PYTHON_EL_eval_file(filename, tempname, delete): "Code used to evaluate files in inferior Python processes. The coding cookie regexp is specified in PEP 263.") +(defconst python-shell-local-prefix "/local:" + "A prefix used to indicate that a file is local. +It is used when sending file names to remote Python processes.") + (defun python-shell-comint-watch-for-first-prompt-output-filter (output) "Run `python-shell-first-prompt-hook' when first prompt is found in OUTPUT." (when (not python-shell--first-prompt-received) @@ -4016,6 +4020,27 @@ there for compatibility with CEDET.") (signal 'wrong-type-argument (list 'stringp text))))) "Encode TEXT as a valid Python string.") +(defun python-shell--convert-file-name-to-send (process file-name) + "Convert the FILE-NAME for sending to the inferior Python PROCESS. +If PROCESS is local and FILE-NAME is prefixed with +`python-shell-local-prefix', remove the prefix. If PROCESS is remote +and the FILE-NAME is not prefixed, prepend `python-shell-local-prefix'. +If PROCESS is remote and the file is on the same remote host, remove the +remote prefix. Otherwise, return the file name as is." + (when file-name + (let ((process-prefix + (file-remote-p + (with-current-buffer (process-buffer process) default-directory))) + (local-prefix (string-prefix-p python-shell-local-prefix file-name))) + (cond + ((and (not process-prefix) local-prefix) + (string-remove-prefix python-shell-local-prefix file-name)) + ((and process-prefix (not (or local-prefix (file-remote-p file-name)))) + (concat python-shell-local-prefix (file-local-name file-name))) + ((and process-prefix (string= (file-remote-p file-name) process-prefix)) + (file-local-name file-name)) + (t file-name))))) + (defun python-shell-send-string (string &optional process msg) "Send STRING to inferior Python PROCESS. When optional argument MSG is non-nil, forces display of a @@ -4023,11 +4048,13 @@ user-friendly message if there's no process running; defaults to t when called interactively." (interactive (list (read-string "Python command: ") nil t)) - (let ((process (or process (python-shell-get-process-or-error msg))) - (code (format "__PYTHON_EL_eval(%s, %s)\n" - (python-shell--encode-string string) - (python-shell--encode-string (or (buffer-file-name) - ""))))) + (let* ((process (or process (python-shell-get-process-or-error msg))) + (code (format "__PYTHON_EL_eval(%s, %s)\n" + (python-shell--encode-string string) + (python-shell--encode-string + (or (python-shell--convert-file-name-to-send + process (buffer-file-name)) + ""))))) (unless python-shell-output-filter-in-progress (with-current-buffer (process-buffer process) (save-excursion @@ -4358,7 +4385,8 @@ t when called interactively." temp-file-name (with-temp-buffer (insert-file-contents file-name) (python-shell--save-temp-file (current-buffer)))))) - (let* ((file-name (file-local-name (expand-file-name file-name))) + (let* ((file-name (python-shell--convert-file-name-to-send + process (expand-file-name file-name))) (temp-file-name (when temp-file-name (file-local-name (expand-file-name temp-file-name))))) @@ -5082,8 +5110,12 @@ Never set this variable directly, use "Set the buffer for FILE-NAME as the tracked buffer. Internally it uses the `python-pdbtrack-tracked-buffer' variable. Returns the tracked buffer." - (let* ((file-name-prospect (concat (file-remote-p default-directory) - file-name)) + (let* ((file-name-prospect + (if (string-prefix-p python-shell-local-prefix file-name) + (string-remove-prefix python-shell-local-prefix file-name) + (if (file-remote-p file-name) + file-name + (concat (file-remote-p default-directory) file-name)))) (file-buffer (get-file-buffer file-name-prospect))) (unless file-buffer (cond diff --git a/test/lisp/progmodes/python-tests.el b/test/lisp/progmodes/python-tests.el index d65ef39abb4..b9130da495d 100644 --- a/test/lisp/progmodes/python-tests.el +++ b/test/lisp/progmodes/python-tests.el @@ -4602,6 +4602,32 @@ and `python-shell-interpreter-args' in the new shell buffer." "^\\(o\\.t \\|\\)"))) (ignore-errors (delete-file startup-file)))))) +(ert-deftest python-shell--convert-file-name-to-send-1 () + "Test parameters consist of a list of the following three elements. +1. The variable `default-directory' of the process. +2. FILE-NAME argument. +3. The expected return value." + (python-tests-with-temp-buffer-with-shell + "" + (let* ((path "/tmp/tmp.py") + (local-path (concat python-shell-local-prefix path)) + (remote1-path (concat "/ssh:remote1:" path)) + (remote2-path (concat "/ssh:remote2:" path)) + (process (python-shell-get-process))) + (dolist + (test (list (list "/tmp" nil nil) + (list "/tmp" path path) + (list "/tmp" local-path path) + (list "/ssh:remote1:/tmp" path local-path) + (list "/ssh:remote1:/tmp" local-path local-path) + (list "/ssh:remote1:/tmp" remote1-path path) + (list "/ssh:remote1:/tmp" remote2-path remote2-path))) + (with-current-buffer (process-buffer process) + (setq default-directory (nth 0 test))) + (should (string= (python-shell--convert-file-name-to-send + process (nth 1 test)) + (nth 2 test))))))) + (ert-deftest python-shell-buffer-substring-1 () "Selecting a substring of the whole buffer must match its contents." (python-tests-with-temp-buffer From cf86e00c8d07d7380ef732f4d6972ffd83778baa Mon Sep 17 00:00:00 2001 From: Tony Zorman Date: Tue, 5 Aug 2025 13:08:43 +0200 Subject: [PATCH 044/216] ; * lisp/indent-aux.el: Fix deindenting read-only text (bug#79176). --- lisp/indent-aux.el | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/lisp/indent-aux.el b/lisp/indent-aux.el index eeb8f1ee6bb..4a05136ac1a 100644 --- a/lisp/indent-aux.el +++ b/lisp/indent-aux.el @@ -50,13 +50,14 @@ is yanked." (delete-and-extract-region beg end) (buffer-substring beg end)))) (with-temp-buffer - ;; Indent/deindent the same as the major mode in the original - ;; buffer. - (setq indent-tabs-mode i-t-m) - (insert text) - (indent-rigidly (point-min) (point-max) - (- indentation)) - (buffer-string)))) + (let ((inhibit-read-only t)) + ;; Indent/deindent the same as the major mode in the original + ;; buffer. + (setq indent-tabs-mode i-t-m) + (insert text) + (indent-rigidly (point-min) (point-max) + (- indentation)) + (buffer-string))))) ;;;###autoload (define-minor-mode kill-ring-deindent-mode From b6f177b68a6bf82a21e18e63dd4843fdc31af0e0 Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Thu, 7 Aug 2025 16:12:37 +0300 Subject: [PATCH 045/216] ; * lisp/indent-aux.el: Add a comment (bug#79176). --- lisp/indent-aux.el | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lisp/indent-aux.el b/lisp/indent-aux.el index 4a05136ac1a..47d21f86649 100644 --- a/lisp/indent-aux.el +++ b/lisp/indent-aux.el @@ -50,6 +50,8 @@ is yanked." (delete-and-extract-region beg end) (buffer-substring beg end)))) (with-temp-buffer + ;; We bind inhibit-read-only non-nil in case the copied text has + ;; read-only properties. (let ((inhibit-read-only t)) ;; Indent/deindent the same as the major mode in the original ;; buffer. From f2d3659cf50e3f3eb78e28835109884dc33bdd83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vin=C3=ADcius=20Moraes?= Date: Sat, 2 Aug 2025 20:52:27 -0300 Subject: [PATCH 046/216] Handle remote file names in cmuscheme.el * lisp/cmuscheme.el (scheme-load-file, scheme-compile-file): Use 'file-local-name' to handle file names on remote systems. (Bug#79163) Copyright-paperwork-exempt: yes --- lisp/cmuscheme.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lisp/cmuscheme.el b/lisp/cmuscheme.el index 6528ac3fb7d..35198e79e73 100644 --- a/lisp/cmuscheme.el +++ b/lisp/cmuscheme.el @@ -416,7 +416,7 @@ in the next one.") (setq scheme-prev-l/c-dir/file (cons (file-name-directory file-name) (file-name-nondirectory file-name))) (comint-send-string (scheme-proc) (concat "(load \"" - file-name + (file-local-name file-name) "\")\n"))) (defun scheme-compile-file (file-name) @@ -430,7 +430,7 @@ in the next one.") (setq scheme-prev-l/c-dir/file (cons (file-name-directory file-name) (file-name-nondirectory file-name))) (comint-send-string (scheme-proc) (concat "(compile-file \"" - file-name + (file-local-name file-name) "\")\n"))) From caa6bc95c1b5904bd50c6aee5c70c6994594756c Mon Sep 17 00:00:00 2001 From: Jeremy Bryant Date: Wed, 23 Jul 2025 23:35:01 +0100 Subject: [PATCH 047/216] New debugger-trap function to break to GDB * src/eval.c: new primitive debugger-trap * src/.gdbinit: set breakpoint to Fdebugger_trap * etc/DEBUG: document it. Remove suggestion to use Fredraw_display. This do-nothing primitive gives control to GDB, and for debugging convenience a breakpoint is set by default in .gdbinit. --- etc/DEBUG | 11 ++++++----- src/.gdbinit | 5 +++++ src/eval.c | 16 ++++++++++++++++ 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/etc/DEBUG b/etc/DEBUG index 25049ad42b4..44f19900c64 100644 --- a/etc/DEBUG +++ b/etc/DEBUG @@ -211,16 +211,17 @@ the debugger, but before running it, is the most efficient way of making sure control will be returned to the debugger when you need that. +There is a default function to give control to the debugger. It is +called debugger-trap. This is a do-nothing primitive, as a convenient +point to return control to the debugger. You can invoke interactively +with "M-x debugger-trap RET". The src/.gdbinit file in the Emacs source +distribution sets a breakpoint on this function. + 'Fsignal' is a very useful place to put a breakpoint in. All Lisp errors go through there. If you are only interested in errors that would fire the Lisp debugger, breaking at 'maybe_call_debugger' is useful. -Another technique for getting control to the debugger is to put a -breakpoint in some rarely used function. One such convenient function -is Fredraw_display, which you can invoke at will interactively with -"M-x redraw-display RET". - It is also useful to have a guaranteed way to return to the debugger at any arbitrary time. When using X, this is easy: type C-z at the window where you are interacting with GDB, and it will stop Emacs just diff --git a/src/.gdbinit b/src/.gdbinit index d3bfad59486..0eab7ac9afa 100644 --- a/src/.gdbinit +++ b/src/.gdbinit @@ -1314,6 +1314,11 @@ if defined_WINDOWSNT end end +# Break at default trap function to give control to GDB. +# Call from Emacs with M-x debugger-trap +break Fdebugger_trap + + # Put the Python code at the end of .gdbinit so that if GDB does not # support Python, GDB will do all the above initializations before # reporting an error. diff --git a/src/eval.c b/src/eval.c index 204adb62472..829d6104618 100644 --- a/src/eval.c +++ b/src/eval.c @@ -3091,6 +3091,21 @@ FUNCTIONP (Lisp_Object object) return false; } +DEFUN ("debugger-trap", Fdebugger_trap, Sdebugger_trap, 0, 0, "", + doc: /* Trap execution flow and hand over control to GDB. +The Emacs source file src/.gdbinit uses this via the GDB command +"break Fdebugger_trap". + +This function has no effect. It is reserved for debugging, and is not +called by Emacs otherwise. + +For Lisp debugging see debug, as well as edebug, in the manual: +"(elisp) Debugging". */) + (void) +{ + return Qnil; +} + Lisp_Object funcall_general (Lisp_Object fun, ptrdiff_t numargs, Lisp_Object *args) { @@ -4617,4 +4632,5 @@ alist of active lexical bindings. */); defsubr (&Sspecial_variable_p); DEFSYM (Qfunctionp, "functionp"); defsubr (&Sfunctionp); + defsubr (&Sdebugger_trap); } From e9fe0ebae057bd9e9015a16b9b3604a5ceddc0ea Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Thu, 7 Aug 2025 16:38:02 +0300 Subject: [PATCH 048/216] ; Fix last change * src/eval.c (Fdebugger_trap): Minor wording changes in doc string. * etc/DEBUG: Some copyedits of a recently-added text. --- etc/DEBUG | 19 +++++++++++-------- src/eval.c | 12 ++++++------ 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/etc/DEBUG b/etc/DEBUG index 44f19900c64..dc1601a34f8 100644 --- a/etc/DEBUG +++ b/etc/DEBUG @@ -211,16 +211,19 @@ the debugger, but before running it, is the most efficient way of making sure control will be returned to the debugger when you need that. -There is a default function to give control to the debugger. It is -called debugger-trap. This is a do-nothing primitive, as a convenient -point to return control to the debugger. You can invoke interactively -with "M-x debugger-trap RET". The src/.gdbinit file in the Emacs source -distribution sets a breakpoint on this function. +The function 'debugger-trap' is a do-nothing interactive command that +exists to give control to the debugger. It is convenient to set a +breakpoint there to give control to the debugger when the function is +called. You can invoke it interactively with "M-x debugger-trap RET". +The src/.gdbinit file in the Emacs source distribution sets a breakpoint +in this function, so if you arrange for .gdbinit to be loaded, or load +it manually in a debugging session, the breakpoint is set for you +automatically. -'Fsignal' is a very useful place to put a breakpoint in. All Lisp +'Fsignal' is another very useful place to put a breakpoint in. All Lisp errors go through there. If you are only interested in errors that -would fire the Lisp debugger, breaking at 'maybe_call_debugger' is -useful. +would fire the Lisp debugger, breaking at 'maybe_call_debugger' is a +useful alternative. It is also useful to have a guaranteed way to return to the debugger at any arbitrary time. When using X, this is easy: type C-z at the diff --git a/src/eval.c b/src/eval.c index 829d6104618..0d4ae91136e 100644 --- a/src/eval.c +++ b/src/eval.c @@ -3092,14 +3092,14 @@ FUNCTIONP (Lisp_Object object) } DEFUN ("debugger-trap", Fdebugger_trap, Sdebugger_trap, 0, 0, "", - doc: /* Trap execution flow and hand over control to GDB. -The Emacs source file src/.gdbinit uses this via the GDB command -"break Fdebugger_trap". + doc: /* Stop Emacs and hand over control to GDB. +The Emacs source file src/.gdbinit sets a breakpoint in this function. -This function has no effect. It is reserved for debugging, and is not -called by Emacs otherwise. +This function does nothing. It is not called by Emacs otherwise, and +exists so that calling it or invoking it interactively will cause +GDB to kick in. -For Lisp debugging see debug, as well as edebug, in the manual: +For Lisp debugging see `debug', as well as `edebug', in the manual: "(elisp) Debugging". */) (void) { From 55541c8b66c25b1e5fb45c6d8341fd111143d509 Mon Sep 17 00:00:00 2001 From: binarydigitz01 Date: Sun, 3 Aug 2025 11:35:53 +0000 Subject: [PATCH 049/216] Do interactive tagging in arc-mode.el (bug#79166). Copyright-paperwork-exempt: yes --- lisp/arc-mode.el | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/lisp/arc-mode.el b/lisp/arc-mode.el index 10ff3871074..8f6c71a4b74 100644 --- a/lisp/arc-mode.el +++ b/lisp/arc-mode.el @@ -897,7 +897,7 @@ when parsing the archive." "Toggle alternative display. To avoid very long lines archive mode does not show all information. This function changes the set of information shown for each files." - (interactive) + (interactive nil archive-mode) (setq archive-alternate-display (not archive-alternate-display)) (setq-local archive-hidden-columns (if archive-alternate-display @@ -913,7 +913,8 @@ This function changes the set of information shown for each files." (list (intern (completing-read "Toggle visibility of: " '(Mode Ids Ratio Date&Time) - nil t)))) + nil t))) + archive-mode) (setq-local archive-hidden-columns (if (memq column archive-hidden-columns) (remove column archive-hidden-columns) @@ -1133,7 +1134,8 @@ NEW-NAME." (or (archive-get-marked ?*) (list (archive-get-descr)))))) (list names (read-file-name (format "Copy %s to: " (string-join names ", ")) - nil default-directory)))) + nil default-directory))) + archive-mode) (unless (consp files) (setq files (list files))) (when (and (> (length files) 1) @@ -1171,7 +1173,7 @@ NEW-NAME." (defun archive-extract (&optional other-window-p event) "In archive mode, extract this entry of the archive into its own buffer." - (interactive (list nil last-input-event)) + (interactive (list nil last-input-event) archive-mode) (if event (posn-set-point (event-end event))) (let* ((view-p (eq other-window-p 'view)) (descr (archive-get-descr)) @@ -1348,17 +1350,17 @@ NEW-NAME." (defun archive-extract-other-window () "In archive mode, find this member in another window." - (interactive) + (interactive nil archive-mode) (archive-extract t)) (defun archive-display-other-window () "In archive mode, display this member in another window." - (interactive) + (interactive nil archive-mode) (archive-extract 'display)) (defun archive-view () "In archive mode, view the member on this line." - (interactive) + (interactive nil archive-mode) (archive-extract 'view)) (defun archive-add-new-member (arcbuf name) @@ -1469,7 +1471,7 @@ NEW-NAME." (defun archive-flag-deleted (p &optional type) "In archive mode, mark this member to be deleted from the archive. With a prefix argument, mark that many files." - (interactive "p") + (interactive "p" archive-mode) (or type (setq type ?D)) (beginning-of-line) (let ((sign (if (>= p 0) +1 -1)) @@ -1488,18 +1490,18 @@ With a prefix argument, mark that many files." (defun archive-unflag (p) "In archive mode, un-mark this member if it is marked to be deleted. With a prefix argument, un-mark that many files forward." - (interactive "p") + (interactive "p" archive-mode) (archive-flag-deleted p ?\s)) (defun archive-unflag-backwards (p) "In archive mode, un-mark this member if it is marked to be deleted. With a prefix argument, un-mark that many members backward." - (interactive "p") + (interactive "p" archive-mode) (archive-flag-deleted (- p) ?\s)) (defun archive-unmark-all-files () "Remove all marks." - (interactive) + (interactive nil archive-mode) (let ((modified (buffer-modified-p)) (inhibit-read-only t)) (save-excursion @@ -1514,7 +1516,7 @@ With a prefix argument, un-mark that many members backward." "In archive mode, mark this member for group operations. With a prefix argument, mark that many members. Use \\[archive-unmark-all-files] to remove all marks." - (interactive "p") + (interactive "p" archive-mode) (archive-flag-deleted p ?*)) (defun archive-get-marked (mark &optional default) @@ -1532,20 +1534,20 @@ Use \\[archive-unmark-all-files] to remove all marks." ;;; Section: Operate (defun archive-next-line (p) - (interactive "p") + (interactive "p" archive-mode) (forward-line p) (or (eobp) (forward-char archive-file-name-indent))) (defun archive-previous-line (p) - (interactive "p") + (interactive "p" archive-mode) (archive-next-line (- p))) (defun archive-chmod-entry (new-mode) "Change the protection bits associated with all marked or this member. The new protection bits can either be specified as an octal number or as a relative change like \"g+rw\" as for chmod(2)." - (interactive "sNew mode (octal or symbolic): ") + (interactive "sNew mode (octal or symbolic): " archive-mode) (if archive-read-only (error "Archive is read-only")) (let ((func (archive-name "chmod-entry"))) (if (fboundp func) @@ -1556,7 +1558,7 @@ as a relative change like \"g+rw\" as for chmod(2)." (defun archive-chown-entry (new-uid) "Change the owner of all marked or this member." - (interactive "nNew uid: ") + (interactive "nNew uid: " archive-mode) (if archive-read-only (error "Archive is read-only")) (let ((func (archive-name "chown-entry"))) (if (fboundp func) @@ -1567,7 +1569,7 @@ as a relative change like \"g+rw\" as for chmod(2)." (defun archive-chgrp-entry (new-gid) "Change the group of all marked or this member." - (interactive "nNew gid: ") + (interactive "nNew gid: " archive-mode) (if archive-read-only (error "Archive is read-only")) (let ((func (archive-name "chgrp-entry"))) (if (fboundp func) @@ -1607,7 +1609,7 @@ as a relative change like \"g+rw\" as for chmod(2)." (defun archive-expunge () "Do the flagged deletions." - (interactive) + (interactive nil archive-mode) (archive--expunge-maybe-force nil)) (defun archive-*-expunge (archive files command) @@ -1616,7 +1618,7 @@ as a relative change like \"g+rw\" as for chmod(2)." (defun archive-rename-entry (newname) "Change the name associated with this entry in the archive file." - (interactive "sNew name: ") + (interactive "sNew name: " archive-mode) (if archive-read-only (error "Archive is read-only")) (if (string= newname "") (error "Archive members may not be given empty names")) @@ -1644,7 +1646,7 @@ as a relative change like \"g+rw\" as for chmod(2)." (defun archive-undo () "Undo in an archive buffer. This doesn't recover lost files, it just undoes changes in the buffer itself." - (interactive) + (interactive nil archive-mode) (let ((inhibit-read-only t)) (undo))) From cbeb558f167a588e7792fd270d3142f6960d643d Mon Sep 17 00:00:00 2001 From: Manuel Giraud Date: Mon, 7 Jul 2025 07:44:08 +0200 Subject: [PATCH 050/216] Use variables to store marking state Bug#78967. (calendar-mode): Init new variables with user options. * lisp/calendar/calendar.el (calendar-generate-window) (calendar-unmark): * lisp/calendar/diary-lib.el (diary-mark-entries) (diary-redraw-calendar): * lisp/calendar/holidays.el (calendar-mark-holidays): Use new variables. * etc/NEWS: Document the change. --- etc/NEWS | 8 ++++++++ lisp/calendar/calendar.el | 17 +++++++++++++---- lisp/calendar/diary-lib.el | 8 ++++---- lisp/calendar/holidays.el | 2 +- 4 files changed, 26 insertions(+), 9 deletions(-) diff --git a/etc/NEWS b/etc/NEWS index a34a3d73292..80b461ddadf 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -2451,6 +2451,14 @@ modifier, it scrolls by year. The month and year navigation key bindings 'M-}', 'M-{', 'C-x ]' and 'C-x [' now have the alternative keys '}', '{', ']' and '['. +--- +*** Avoid modifying user options. +The user options `calendar-mark-holidays-flag' and +`calendar-mark-diary-entries-flag' are not modified anymore when +changing the marking state in the calendar buffer. We now use variables +that are reset to the user option values whenever we generate a new +calendar. + ** Calc *** New user option 'calc-string-maximum-character'. diff --git a/lisp/calendar/calendar.el b/lisp/calendar/calendar.el index 04bbc2cb8a4..04a42fcd38a 100644 --- a/lisp/calendar/calendar.el +++ b/lisp/calendar/calendar.el @@ -456,6 +456,12 @@ LEFT is the leftmost column associated with month segment N, FIRST and LAST are the first and last columns with day digits in, and LAST is the rightmost column.") +(defvar calendar-mark-holidays nil + "Variable version of the user option `calendar-mark-holidays-flag'.") + +(defvar calendar-mark-diary-entries nil + "Variable version of the user option `calendar-mark-diary-entries-flag'.") + (defun calendar-month-edges (segment) "Compute the month edge columns for month SEGMENT. Returns a list (LEFT FIRST LAST RIGHT), where LEFT is the @@ -1436,11 +1442,11 @@ Optional integers MON and YR are used instead of today's date." ;; For a full height window or a window that is horizontally ;; combined don't fit height to that of its buffer. (set-window-vscroll nil 0))) - (and calendar-mark-holidays-flag + (and calendar-mark-holidays ;; (calendar-date-is-valid-p today) ; useful for BC dates (calendar-mark-holidays)) (unwind-protect - (if calendar-mark-diary-entries-flag (diary-mark-entries)) + (if calendar-mark-diary-entries (diary-mark-entries)) (run-hooks (if today-visible 'calendar-today-visible-hook 'calendar-today-invisible-hook))))) @@ -1833,6 +1839,9 @@ For a complete description, see the info node `Calendar/Diary'. (make-local-variable 'calendar-mark-ring) (make-local-variable 'displayed-month) ; month in middle of window (make-local-variable 'displayed-year) ; year in middle of window + ;; Init with user options. + (setq calendar-mark-holidays calendar-mark-holidays-flag + calendar-mark-diary-entries calendar-mark-diary-entries-flag) ;; Most functions only work if displayed-month and displayed-year are set, ;; so let's make sure they're always set. Most likely, this will be reset ;; soon in calendar-generate, but better safe than sorry. @@ -2431,8 +2440,8 @@ interpreted as BC; -1 being 1 BC, and so on." (defun calendar-unmark () "Delete all diary/holiday marks/highlighting from the calendar." (interactive) - (setq calendar-mark-holidays-flag nil - calendar-mark-diary-entries-flag nil) + (setq calendar-mark-holidays nil + calendar-mark-diary-entries nil) (with-current-buffer calendar-buffer (mapc #'delete-overlay (overlays-in (point-min) (point-max))))) diff --git a/lisp/calendar/diary-lib.el b/lisp/calendar/diary-lib.el index 3d8d827417e..056360bbede 100644 --- a/lisp/calendar/diary-lib.el +++ b/lisp/calendar/diary-lib.el @@ -1395,8 +1395,8 @@ marks. This is intended to deal with deleted diary entries." ;; ii) called via calendar-redraw (since calendar has already been ;; erased). ;; Use of REDRAW handles both of these cases. - (when (and redraw calendar-mark-diary-entries-flag) - (setq calendar-mark-diary-entries-flag nil) + (when (and redraw calendar-mark-diary-entries) + (setq calendar-mark-diary-entries nil) (calendar-redraw)) (let ((diary-marking-entries-flag t) (diary-buffer (find-buffer-visiting diary-file)) @@ -1417,7 +1417,7 @@ marks. This is intended to deal with deleted diary entries." ;; If including, caller has already verified it is readable. (insert-file-contents diary-file) (if (eq major-mode (default-value 'major-mode)) (diary-mode))) - (setq calendar-mark-diary-entries-flag t) + (setq calendar-mark-diary-entries t) (setq file-glob-attrs (nth 1 (diary-pull-attrs nil '()))) (with-syntax-table diary-syntax-table (save-excursion @@ -2243,7 +2243,7 @@ Prefix argument ARG makes the entry nonmarking." (defun diary-redraw-calendar () "If `calendar-buffer' is live and diary entries are marked, redraw it." - (and calendar-mark-diary-entries-flag + (and calendar-mark-diary-entries (save-excursion (calendar-redraw))) ;; Return value suitable for `write-contents-functions'. diff --git a/lisp/calendar/holidays.el b/lisp/calendar/holidays.el index b40c56abd86..28f3a0ae7f2 100644 --- a/lisp/calendar/holidays.el +++ b/lisp/calendar/holidays.el @@ -585,7 +585,7 @@ use instead of point." (with-current-buffer (if event (calendar-event-buffer event) (current-buffer)) - (setq calendar-mark-holidays-flag t) + (setq calendar-mark-holidays t) (message "Marking holidays...") (dolist (holiday (calendar-holiday-list)) (calendar-mark-visible-date (car holiday) calendar-holiday-marker)) From 16a5a0f619a245b490ed02e7f5b6a7edb6cb6e95 Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Thu, 7 Aug 2025 16:52:22 +0300 Subject: [PATCH 051/216] ; * etc/NEWS: Fix last change. --- etc/NEWS | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/etc/NEWS b/etc/NEWS index 80b461ddadf..42d760ac09e 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -2452,12 +2452,10 @@ The month and year navigation key bindings 'M-}', 'M-{', 'C-x ]' and 'C-x [' now have the alternative keys '}', '{', ']' and '['. --- -*** Avoid modifying user options. -The user options `calendar-mark-holidays-flag' and -`calendar-mark-diary-entries-flag' are not modified anymore when -changing the marking state in the calendar buffer. We now use variables -that are reset to the user option values whenever we generate a new -calendar. +*** Avoid modifying Calendar's user options. +The user options 'calendar-mark-holidays-flag' and +'calendar-mark-diary-entries-flag' are not modified anymore when +changing the marking state in the calendar buffer. ** Calc From 738a7bb4949fdc93b0b637ac4c65423c928f0924 Mon Sep 17 00:00:00 2001 From: mattiasdrp Date: Tue, 5 Aug 2025 18:11:58 +0200 Subject: [PATCH 052/216] Autoinsert: Allow condition to be a function Currently a condition can only be a regexp or a major-mode symbol but there's no real reason to not allow any predicate as a condition. * doc/misc/autotype.texi: Document the new condition type in 'auto-insert-alist'. * etc/NEWS: Announce the new condition type in 'auto-insert-alist'. * lisp/autoinsert.el (auto-insert-alist): Add the new condition type to the variable documentation. (auto-insert): Change the way the condition is matched to allow for custom predicates. (Bug#79178) --- doc/misc/autotype.texi | 5 +++-- etc/NEWS | 7 +++++++ lisp/autoinsert.el | 29 ++++++++++++++++++++++------- 3 files changed, 32 insertions(+), 9 deletions(-) diff --git a/doc/misc/autotype.texi b/doc/misc/autotype.texi index c53be54d0af..3a1eef1c694 100644 --- a/doc/misc/autotype.texi +++ b/doc/misc/autotype.texi @@ -275,9 +275,10 @@ empty file is visited. This is accomplished by putting What gets inserted, if anything, is determined by the variable @code{auto-insert-alist}. The @sc{car} of each element of this list is either a mode name, making the element applicable when a buffer is -in that mode, or a string, which is a regexp matched against a +in that mode, a string, which is a regexp matched against a buffer's file name (the latter enables you to distinguish between -different kinds of files that have the same mode in Emacs). The +different kinds of files that have the same mode in Emacs) or a custom +predicate that takes no argument. The @sc{car} of an element may also be a cons cell, consisting of mode name or regexp, as above, and an additional descriptive string. diff --git a/etc/NEWS b/etc/NEWS index 42d760ac09e..3713e828839 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -695,6 +695,13 @@ you could already use 'C-u C-x C-n' to clear the goal column. * Changes in Specialized Modes and Packages in Emacs 31.1 +** Autoinsert + ++++ +*** New condition for 'auto-insert-alist'. +'auto-insert-alist' now also allows to have a predicate taking no argument +as conditions. This allows to trigger 'auto-insert' with finer grained control. + ** Register *** The "*Register Preview*" buffer shows only suitable registers. diff --git a/lisp/autoinsert.el b/lisp/autoinsert.el index e7492d1b9ed..3c4057f1623 100644 --- a/lisp/autoinsert.el +++ b/lisp/autoinsert.el @@ -323,8 +323,10 @@ The document was typeset with ")) "A list specifying text to insert by default into a new file. Elements look like (CONDITION . ACTION) or ((CONDITION . DESCRIPTION) . ACTION). -CONDITION may be a regexp that must match the new file's name, or it may be -a symbol that must match the major mode for this element to apply. +CONDITION may be a regexp that must match the new file's name or a symbol that +must match the major mode for this element to apply. +CONDITION can also be custom predicate function of no arguments; Emacs will +insert the text if the predicate function returns non-nil. Only the first matching element is effective. Optional DESCRIPTION is a string for filling `auto-insert-prompt'. ACTION may be a skeleton to insert (see `skeleton-insert'), an absolute @@ -368,12 +370,25 @@ Matches the visited file name against the elements of `auto-insert-alist'." (pcase-lambda (`(,cond . ,action)) (if (atom cond) (setq desc cond) - (setq desc (cdr cond) - cond (car cond))) - (when (if (symbolp cond) - (derived-mode-p cond) + ;; if `cond' is a lambda, don't split it but set `desc' to a custom string + (if (and (not (symbolp cond)) (functionp cond)) + (setq desc "`lambda condition'") + (setq desc (cdr cond) + cond (car cond)))) + (when (cond + ;; `cond' should be a major-mode variable + ((and (symbolp cond) (not (functionp cond))) + (derived-mode-p cond)) + + ;; `cond' should be a predicate that takes no argument + ;; It can either be a named function or a lambda + ((functionp cond) + (funcall cond)) + + ;; cond should be a regexp + (t (and buffer-file-name - (string-match cond buffer-file-name))) + (string-match cond buffer-file-name)))) action)) auto-insert-alist))) (goto-char 1) From 96891c1fac79c2a4479046dbd9de9f6137d598fe Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Thu, 7 Aug 2025 17:37:15 +0300 Subject: [PATCH 053/216] ; * etc/NEWS: Fix last change. --- etc/NEWS | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/etc/NEWS b/etc/NEWS index 3713e828839..2b3bff2f8ca 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -699,8 +699,9 @@ you could already use 'C-u C-x C-n' to clear the goal column. +++ *** New condition for 'auto-insert-alist'. -'auto-insert-alist' now also allows to have a predicate taking no argument -as conditions. This allows to trigger 'auto-insert' with finer grained control. +'auto-insert-alist' now also allows to have a predicate taking no +argument as the condition. This allows to trigger 'auto-insert' with +finer grained control. ** Register From e658ed06776590c3a8e929df207f2ddc9bbb7791 Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Thu, 7 Aug 2025 17:39:52 +0300 Subject: [PATCH 054/216] ; * admin/authors.el (authors-aliases): Update. --- admin/authors.el | 1 + 1 file changed, 1 insertion(+) diff --git a/admin/authors.el b/admin/authors.el index b9f5f170fc0..66346b46091 100644 --- a/admin/authors.el +++ b/admin/authors.el @@ -187,6 +187,7 @@ files.") ("Mark D. Baushke" "Mark D Baushke") ("Mark E. Shoulson" "Mark Shoulson") ("Marko Kohtala" "Kohtala Marko") + ("Mattias Roux" "mattias@kojin\\.tech") ("Maxim Nikulin" "Max Nikulin") ("Agustín Martín" "Agustin Martin" "Agustín Martín Domingo") ("Martin Lorentzon" "Martin Lorentzson") From 42b990cd201f078390525303a5bb7c526ae84d46 Mon Sep 17 00:00:00 2001 From: Manuel Giraud Date: Thu, 7 Aug 2025 15:00:58 +0200 Subject: [PATCH 055/216] Fix some tests on OpenBSD * test/lisp/eshell/em-script-tests.el (em-script-test/batch-file/shebang): "env -S" is not supported. * test/src/fns-tests.el (fns-tests-random): 'random' is non-deterministic on OpenBSD. (Bug#79190) --- test/lisp/eshell/em-script-tests.el | 5 ++++- test/src/fns-tests.el | 6 +++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/test/lisp/eshell/em-script-tests.el b/test/lisp/eshell/em-script-tests.el index 01dc5fd9a72..30b917baa35 100644 --- a/test/lisp/eshell/em-script-tests.el +++ b/test/lisp/eshell/em-script-tests.el @@ -143,7 +143,10 @@ (ert-deftest em-script-test/batch-file/shebang () "Test running an Eshell script file as a batch script via a shebang." - (skip-unless (not (memq system-type '(windows-nt ms-dos)))) + (skip-when (or (memq system-type '(windows-nt ms-dos)) + ;; OpenBSD's env does not support -S + (and (eq system-type 'berkeley-unix) + (string-match-p "openbsd" system-configuration)))) (ert-with-temp-file temp-file :text (format "#!/usr/bin/env -S %s --batch -f eshell-batch-file\necho hi" diff --git a/test/src/fns-tests.el b/test/src/fns-tests.el index 6a50d89c778..6f0155cd5df 100644 --- a/test/src/fns-tests.el +++ b/test/src/fns-tests.el @@ -38,7 +38,11 @@ (should (= (random 1) 0)) (should (>= (random 10) 0)) (should (< (random 10) 10)) - (should (equal (random "seed") (random "seed"))) + ;; On OpenBSD random is non-deterministic. + (if (and (eq system-type 'berkeley-unix) + (string-match-p "openbsd" system-configuration)) + (should (not (equal (random "seed") (random "seed")))) + (should (equal (random "seed") (random "seed")))) ;; The probability of four calls being the same is low. ;; This makes sure that the value isn't constant. (should (not (= (random t) (random t) (random t) (random t)))) From 2b3eb8d3f026d166210eaa57d565d7e3509c40e4 Mon Sep 17 00:00:00 2001 From: Stefan Monnier Date: Thu, 7 Aug 2025 13:49:48 -0400 Subject: [PATCH 056/216] (help-function-arglist): Fix bug#79128 * lisp/help.el (help-function-arglist): Fix use of `documentation`, to obey the `function-documentation` property. * test/lisp/help-tests.el (help-tests--use-docstring-arglist-79128): New test. --- lisp/help.el | 95 +++++++++++++++++++++-------------------- test/lisp/help-tests.el | 3 ++ 2 files changed, 51 insertions(+), 47 deletions(-) diff --git a/lisp/help.el b/lisp/help.el index 8b29a10e0cf..e6c5ea54812 100644 --- a/lisp/help.el +++ b/lisp/help.el @@ -2285,53 +2285,54 @@ ARGLIST can also be t or a string of the form \"(FUN ARG1 ARG2 ...)\"." "Return a formal argument list for the function DEF. If PRESERVE-NAMES is non-nil, return a formal arglist that uses the same names as used in the original source code, when possible." - ;; Handle symbols aliased to other symbols. - (if (and (symbolp def) (fboundp def)) (setq def (indirect-function def))) - ;; Advice wrappers have "catch all" args, so fetch the actual underlying - ;; function to find the real arguments. - (setq def (advice--cd*r def)) - ;; If definition is a macro, find the function inside it. - (if (eq (car-safe def) 'macro) (setq def (cdr def))) - (cond - ((and (closurep def) (listp (aref def 0))) (aref def 0)) - ((eq (car-safe def) 'lambda) (nth 1 def)) - ((and (featurep 'native-compile) - (subrp def) - (listp (subr-native-lambda-list def))) - (subr-native-lambda-list def)) - ((or (and (byte-code-function-p def) (integerp (aref def 0))) - (subrp def) (module-function-p def)) - (or (when preserve-names - (let* ((doc (condition-case nil (documentation def 'raw) (error nil))) - (docargs (if doc (car (help-split-fundoc doc nil)))) - (arglist (if docargs - (cdar (read-from-string (downcase docargs))))) - (valid t)) - ;; Check validity. - (dolist (arg arglist) - (unless (and (symbolp arg) - (let ((name (symbol-name arg))) - (if (and (> (length name) 0) (eq (aref name 0) ?&)) - (memq arg '(&rest &optional)) - (not (string-search "." name))))) - (setq valid nil))) - (when valid arglist))) - (let* ((arity (func-arity def)) - (max (cdr arity)) - (min (car arity)) - (arglist ())) - (dotimes (i min) - (push (intern (concat "arg" (number-to-string (1+ i)))) arglist)) - (when (and (integerp max) (> max min)) - (push '&optional arglist) - (dotimes (i (- max min)) - (push (intern (concat "arg" (number-to-string (+ 1 i min)))) - arglist))) - (unless (integerp max) (push '&rest arglist) (push 'rest arglist)) - (nreverse arglist)))) - ((and (autoloadp def) (not (eq (nth 4 def) 'keymap))) - "[Arg list not available until function definition is loaded.]") - (t t))) + (let ((orig-def def) + ;; Advice wrappers have "catch all" args, so fetch the actual underlying + ;; function to find the real arguments. + (def (advice--cd*r + (indirect-function def)))) ;; Follow aliases to other symbols. + ;; If definition is a macro, find the function inside it. + (if (eq (car-safe def) 'macro) (setq def (cdr def))) + (cond + ((and (closurep def) (listp (aref def 0))) (aref def 0)) + ((eq (car-safe def) 'lambda) (nth 1 def)) + ((and (featurep 'native-compile) + (subrp def) + (listp (subr-native-lambda-list def))) + (subr-native-lambda-list def)) + ((or (and (byte-code-function-p def) (integerp (aref def 0))) + (subrp def) (module-function-p def)) + (or (when preserve-names + (let* ((doc (ignore-errors (documentation orig-def 'raw))) + (docargs (if doc (car (help-split-fundoc doc nil)))) + (arglist (if docargs + (cdar (read-from-string (downcase docargs))))) + (valid t)) + ;; Check validity. + (dolist (arg arglist) + (unless (and (symbolp arg) + (let ((name (symbol-name arg))) + (if (and (> (length name) 0) + (eq (aref name 0) ?&)) + (memq arg '(&rest &optional)) + (not (string-search "." name))))) + (setq valid nil))) + (when valid arglist))) + (let* ((arity (func-arity def)) + (max (cdr arity)) + (min (car arity)) + (arglist ())) + (dotimes (i min) + (push (intern (concat "arg" (number-to-string (1+ i)))) arglist)) + (when (and (integerp max) (> max min)) + (push '&optional arglist) + (dotimes (i (- max min)) + (push (intern (concat "arg" (number-to-string (+ 1 i min)))) + arglist))) + (unless (integerp max) (push '&rest arglist) (push 'rest arglist)) + (nreverse arglist)))) + ((and (autoloadp def) (not (eq (nth 4 def) 'keymap))) + "[Arg list not available until function definition is loaded.]") + (t t)))) (defun help--make-usage (function arglist) (cons (if (symbolp function) function 'anonymous) diff --git a/test/lisp/help-tests.el b/test/lisp/help-tests.el index 5942f349bf0..8c6713c7a40 100644 --- a/test/lisp/help-tests.el +++ b/test/lisp/help-tests.el @@ -481,6 +481,9 @@ C-b undefined\n" (propertize "foo \\[save-buffer]" 'face 'bold)) (propertize "foo C-x C-s" 'face 'bold)))) +(ert-deftest help-tests--use-docstring-arglist-79128 () + (should (eq (length (help-function-arglist 'seq-empty-p t)) 1))) + (provide 'help-tests) ;;; help-tests.el ends here From 4456f7bd313ab829ba3ec6375349a693dbbc1930 Mon Sep 17 00:00:00 2001 From: Tassilo Horn Date: Fri, 8 Aug 2025 06:50:18 +0200 Subject: [PATCH 057/216] doc-view: inhibit keybinding message if window is not selected * lisp/doc-view.el (doc-view-initiate-display): Only echo key-binding message when the document buffer is shown in the selected window (bug#79145). --- lisp/doc-view.el | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lisp/doc-view.el b/lisp/doc-view.el index b9bde21eaa8..26604f326fa 100644 --- a/lisp/doc-view.el +++ b/lisp/doc-view.el @@ -2143,7 +2143,13 @@ If FILE-NAME is nil, use the current file instead." (defun doc-view-initiate-display () ;; Switch to image display if possible. (if (doc-view-mode-p doc-view-doc-type) - (progn + ;; Inhibit the echo area display of the "Type C-c C-c..." message + ;; if the doc-view buffer is not shown in the selected window + ;; which can happen due to auto-reverting the buffer (bug#79145). + (let ((inhibit-message + (not (eq (selected-window) + (get-buffer-window (current-buffer) + (selected-frame)))))) (doc-view-buffer-message) (setf (doc-view-current-page) (or (doc-view-current-page) 1)) (if (doc-view-already-converted-p) From c501ed7dc19fc6f061c08da59228bc390fd67c39 Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Wed, 6 Aug 2025 15:05:08 +0100 Subject: [PATCH 058/216] Generalize finding project matching files to other major modes * lisp/progmodes/project.el (project-find-matching-buffer-function): New variable. (project-find-matching-file): Delete. (dired-current-directory): Declare. (project-find-matching-file-or-directory): New function. Like the old 'project-find-matching-file', but also handle Dired buffers (bug#79126). When matching file/directory does not exist, try going up in the directory tree until we find something that does. (project-find-matching-buffer): New command. * lisp/vc/vc.el (vc-switch-working-tree): Use it (bug#79126). * etc/NEWS: Announce new command and new variable. --- etc/NEWS | 7 ++- lisp/progmodes/project.el | 111 +++++++++++++++++++++++++++++--------- lisp/vc/vc.el | 3 +- 3 files changed, 94 insertions(+), 27 deletions(-) diff --git a/etc/NEWS b/etc/NEWS index 2b3bff2f8ca..eee62da981e 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -467,11 +467,16 @@ Callers can use this to indicate the reason for which or context in which Emacs should ask the user to select a project. --- -*** New command 'project-find-matching-file'. +*** New command 'project-find-matching-buffer'. It can be used when switching between projects with similar file trees (such as Git worktrees of the same repository). It supports being invoked standalone or from the 'project-switch-commands' dispatch menu. +--- +*** New variable 'project-find-matching-buffer-function'. +Major modes can set this to major mode-specific functions to control how +'project-find-matching-buffer' finds matching buffers. + +++ *** New user option 'project-list-exclude'. This user option describes projects that should always be skipped by diff --git a/lisp/progmodes/project.el b/lisp/progmodes/project.el index dafc01c7684..dfeafc9877b 100644 --- a/lisp/progmodes/project.el +++ b/lisp/progmodes/project.el @@ -1307,33 +1307,96 @@ directories listed in `vc-directory-exclusion-list'." (user-error "You didn't specify the file") (find-file file)))) +(defvar project-find-matching-buffer-function + #'project-find-matching-file-or-directory + "Function to switch to a matching buffer in another project. +Usually set buffer-locally by non-file-visiting major modes. +The function will be called with two arguments, the project instance for +this buffer's project, and the project instance for the matching buffer. + +The default value works for file-visiting and Dired buffers. +Non-file-visiting major modes (other than `dired-mode'), where there is +a sensible notion of a matching buffer, can set this. +File-visiting major modes shouldn't set it, except possibly some highly +specialized ones.") + ;;;###autoload -(defun project-find-matching-file () - "Visit the file that matches the current one, in another project. -It will skip to the same line number as well. -A matching file has the same file name relative to the project root. +(defun project-find-matching-buffer () + "Switch to a matching buffer in another project. +For most file-visiting buffers, the matching buffer is one visiting a +file in the other project which has the same file name relative to the +project root. See `project-find-matching-file' for details. +Non-file-visiting major modes may configure a different notion of +matching buffer; see `project-find-matching-buffer-function'. + When called during switching to another project, this command will -detect it and use the override. Otherwise, it prompts for the project -to use from the known list." +detect that, and use the override. Otherwise, it prompts for the +project to use from the list of known projects. +When calling from Lisp, bind `project-current-directory-override' to a +directory under the target project to preempt this prompting." (interactive) - (let* ((pr (project-current)) - (line (line-number-at-pos nil t)) - relative-name mirror-name) - (if project-current-directory-override - (let* (project-current-directory-override - (real-project (project-current t))) - (setq relative-name (file-relative-name buffer-file-name - (project-root real-project)))) - (setq relative-name (file-relative-name buffer-file-name (project-root pr))) - (setq pr (project-read-project))) - (setq mirror-name (expand-file-name relative-name (project-root pr))) - (if (not (file-exists-p mirror-name)) - (user-error "File `%s' not found in `%s'" relative-name (project-root pr)) - (find-file mirror-name) - (save-restriction - (widen) - (goto-char (point-min)) - (forward-line (1- line)))))) + (let ((pr (project-current))) + (apply project-find-matching-buffer-function + (if project-current-directory-override + (let (project-current-directory-override) + (list (project-current t) pr)) + (list pr (project-read-project)))))) + +(declare-function dired-current-directory "dired") + +(defun project-find-matching-file-or-directory (current-project mirror-project) + "Visit file or directory in another project that matches the current one. +A file-visiting buffer's matching file has the same file name relative +to the project root. A non-file-visiting buffer's matching directory +has the same `default-directory' relative to the project root. +As a special case, when point is on an inserted subdirectory in a Dired +buffer, use that subdirectory instead of `default-directory'. +CURRENT-PROJECT is the project instance for the current project. +MIRROR-PROJECT is the project instance for the project to visit. +Also skip to the same line number. + +If the matching file does not exist in the other project, try going up +the directory tree until encountering a file or directory that exists. +For example, from a buffer visiting a file \"lisp/vc/vc.el\", if in the +other project this file does not exist, try visiting \"lisp/vc\" if that +exists as a file or directory. + +If a matching directory does not exist in the other project, try going +up the directory tree until encountering a directory that exists. For +example, from a Dired buffer visiting a subdirectory named \"lisp/vc/\", +if in the other project \"lisp/vc\" is missing or not a directory, try +visiting a directory named \"lisp/\" in the other project. + +This function is intended to be used as the value of +`project-find-matching-buffer-function'." + (let* ((line (line-number-at-pos nil t)) + (dirp (not (buffer-file-name))) + (mirror-root (project-root mirror-project)) + (relative-name + (file-relative-name (cond ((derived-mode-p 'dired-mode) + (dired-current-directory)) + ((buffer-file-name)) + (t default-directory)) + (project-root current-project))) + (mirror-name (expand-file-name relative-name mirror-root)) + (orig-mirror-name mirror-name)) + (while (not (if dirp (file-directory-p mirror-name) + (file-exists-p mirror-name))) + (setq mirror-name (directory-file-name + (file-name-parent-directory mirror-name))) + (unless (file-in-directory-p mirror-name mirror-root) + (user-error "`%s' not found in `%s'" relative-name mirror-root))) + (find-file mirror-name) + (save-restriction + (widen) + (goto-char (point-min)) + (forward-line (1- line))) + (unless (equal mirror-name orig-mirror-name) + (message "`%s' not found; visiting `%s' instead" + (abbreviate-file-name orig-mirror-name) + (abbreviate-file-name (if (file-directory-p mirror-name) + (file-name-as-directory mirror-name) + mirror-name)))))) (defun project--completing-read-strict (prompt collection &optional predicate diff --git a/lisp/vc/vc.el b/lisp/vc/vc.el index 67eed872fd7..5dc6461af1f 100644 --- a/lisp/vc/vc.el +++ b/lisp/vc/vc.el @@ -4435,13 +4435,12 @@ When called interactively, prompts for DIRECTORY. This command switches to the file which has the same file name relative to DIRECTORY that this buffer's file has relative to the root of this working tree." - ;; FIXME: Switch between directory analogues, too, in Dired buffers. (interactive (list (vc--prompt-other-working-tree (vc-responsible-backend default-directory) "Other working tree to visit"))) (let ((project-current-directory-override directory)) - (project-find-matching-file))) + (project-find-matching-buffer))) ;;;###autoload (defun vc-delete-working-tree (backend directory) From 74cbe6d740ac4987c3835436aa98892c47dbc44c Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Fri, 8 Aug 2025 11:20:33 +0100 Subject: [PATCH 059/216] vc-buffer-sync-fileset: Bind 'non-essential' (bug#79137) * lisp/vc/vc.el (vc-buffer-sync-fileset): Bind 'non-essential' (bug#79137). --- lisp/vc/vc.el | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lisp/vc/vc.el b/lisp/vc/vc.el index 5dc6461af1f..4fe87d860d1 100644 --- a/lisp/vc/vc.el +++ b/lisp/vc/vc.el @@ -2492,7 +2492,8 @@ buffers whose files exist on disk. Otherwise it syncs all of them." ;; least, users with `vc-find-revision-no-save' set to non-nil: not ;; treating directories this way would imply calling `vc-buffer-sync' ;; on all buffers generated by \\`C-x v ~' during \\`C-x v D'. - (let (dirs buffers) + (let ((non-essential not-essential) + dirs buffers) (dolist (name (cadr fileset)) (if (file-directory-p name) (push name dirs) From 560aee2fb2403a6c7205798e05725abda2bc5cbc Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Fri, 8 Aug 2025 11:47:00 +0100 Subject: [PATCH 060/216] vc-move-working-tree: Also update VC-Dir buffers * lisp/vc/vc.el (vc-move-working-tree): Also update VC-Dir buffers to follow the working tree move. --- lisp/vc/vc-dir.el | 2 +- lisp/vc/vc.el | 29 +++++++++++++++++++++++++---- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/lisp/vc/vc-dir.el b/lisp/vc/vc-dir.el index 93a05412a04..80d6dd3a4ff 100644 --- a/lisp/vc/vc-dir.el +++ b/lisp/vc/vc-dir.el @@ -194,7 +194,7 @@ That is, refreshing the VC-Dir buffer also hides `up-to-date' and (cl-return buffer)))))))) (or buf ;; Create a new buffer named BNAME. - ;; We pass a filename to create-file-buffer because it is what + ;; We pass a filename to `create-file-buffer' because it is what ;; the function expects, and also what uniquify needs (if active) (with-current-buffer (create-file-buffer (expand-file-name bname dir)) (setq default-directory dir) diff --git a/lisp/vc/vc.el b/lisp/vc/vc.el index 4fe87d860d1..63a4fafc56c 100644 --- a/lisp/vc/vc.el +++ b/lisp/vc/vc.el @@ -4472,8 +4472,8 @@ BACKEND is the VC backend." (defun vc-move-working-tree (backend from to) "Relocate a working tree from FROM to TO, two directory file names. Must be called from within an existing VC working tree. -When called interactively, prompts the directory file names of each of -the other working trees FROM and TO. +When called interactively, prompts for the directory file names of each +of the other working trees FROM and TO. BACKEND is the VC backend." (interactive (let ((backend (vc-responsible-backend default-directory))) @@ -4486,8 +4486,29 @@ BACKEND is the VC backend." (vc-call-backend backend 'move-working-tree from to) ;; Update visited file names for buffers visiting files under FROM. - ;; FIXME: Also update VC-Dir buffers. - (dired-rename-subdir (expand-file-name from) (expand-file-name to)) + (let ((from (expand-file-name from))) + (dired-rename-subdir from (expand-file-name to)) + (dolist (buf vc-dir-buffers) + (with-current-buffer buf + (when (string-prefix-p from default-directory) + (setq default-directory + (expand-file-name (file-relative-name default-directory from) + to)) + ;; Obtain an appropriately uniquify'd name for a *vc-dir* + ;; buffer in the new working tree. In particular if this + ;; *vc-dir* buffer already has a uniquify'd name appropriate + ;; for the old working tree, we must replace that. + ;; See also `vc-dir-prepare-status-buffer'. + ;; FIXME: There should be a way to get this information + ;; without creating and killing a buffer. + (let (name) + (unwind-protect + (setq name (buffer-name + (create-file-buffer + (expand-file-name "*vc-dir*" + default-directory)))) + (kill-buffer name)) + (rename-buffer name)))))) (when-let* ((p (project-current nil to))) (project-remember-project p))) From be4be3eb094ffbbf5e36666acc03ada46903274c Mon Sep 17 00:00:00 2001 From: Michael Albinus Date: Fri, 8 Aug 2025 13:26:16 +0200 Subject: [PATCH 061/216] Add auto-revert-buffer-in-progress-p * doc/lispref/backups.texi (Reverting): Add auto-revert-buffer-in-progress-p. * etc/NEWS: Mention auto-revert-buffer-in-progress-p. * lisp/autorevert.el (auto-revert-buffer-in-progress-p): New autoloaded variable. (auto-revert-buffer): Let-bind it. (Bug#79145) * lisp/dired-x.el (dired-omit-expunge): Suppress messages when in auto-revert. --- doc/lispref/backups.texi | 7 +++++++ etc/NEWS | 6 ++++++ lisp/autorevert.el | 42 ++++++++++++++++++++++------------------ lisp/dired-x.el | 4 +++- 4 files changed, 39 insertions(+), 20 deletions(-) diff --git a/doc/lispref/backups.texi b/doc/lispref/backups.texi index f3f0902f364..a1df26230aa 100644 --- a/doc/lispref/backups.texi +++ b/doc/lispref/backups.texi @@ -852,6 +852,13 @@ It is important to assure that point does not continuously jump around as a consequence of auto-reverting. Of course, moving point might be inevitable if the buffer radically changes. +@defvar auto-revert-buffer-in-progress-p +@code{auto-revert-buffer} binds this variable to a non-@code{nil} value +while it is working. This can be used by major mode +@code{revert-buffer-function} implementations to suppress messages in +Auto Revert modes, for example. +@end defvar + @defvar inhibit-auto-revert-buffers When the current buffer is member of this variable (a list of buffers), auto-reverting is suppressed for that buffer. This is useful if serious diff --git a/etc/NEWS b/etc/NEWS index eee62da981e..ba215d519ba 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -2209,6 +2209,12 @@ This macro adds the current buffer to 'inhibit-auto-revert-buffers', runs its body, and removes the current buffer from 'inhibit-auto-revert-buffers' afterwards. ++++ +*** New variable 'auto-revert-buffer-in-progress-p'. +'auto-revert-buffer' binds this variable to a non-nil value while it is +working. This can be used by major mode 'revert-buffer-function' +implementations to suppress messages in Auto Revert modes, for example. + ** Strokes -- diff --git a/lisp/autorevert.el b/lisp/autorevert.el index 2fae74ddc57..30ec8dc2480 100644 --- a/lisp/autorevert.el +++ b/lisp/autorevert.el @@ -329,6 +329,10 @@ seconds, in addition to using notification for those files." ;; Internal variables: +;;;###autoload +(defvar auto-revert-buffer-in-progress-p nil "\ +Non-nil if a `auto-revert-buffer' operation is in progress, nil otherwise.") + (defvar auto-revert-buffer-list () "List of buffers in Auto-Revert Mode. @@ -933,25 +937,25 @@ buffers not reverted last time due to user interruption." This is performed as specified by Auto-Revert and Global Auto-Revert Modes." - (if (not (buffer-live-p buf)) - (auto-revert-remove-current-buffer buf) - (with-current-buffer buf - ;; Test if someone has turned off Auto-Revert Mode - ;; in a non-standard way, for example by changing - ;; major mode. - (when (and (not auto-revert-mode) - (not auto-revert-tail-mode)) - (auto-revert-remove-current-buffer)) - (when (auto-revert-active-p) - ;; Enable file notification. - ;; Don't bother creating a notifier for non-file buffers - ;; unless it explicitly indicates that this works. - (when (and auto-revert-use-notify - (not auto-revert-notify-watch-descriptor) - (or buffer-file-name - buffer-auto-revert-by-notification)) - (auto-revert-notify-add-watch)) - (auto-revert-handler))))) + (let ((auto-revert-buffer-in-progress-p t)) + (if (not (buffer-live-p buf)) + (auto-revert-remove-current-buffer buf) + (with-current-buffer buf + ;; Test if someone has turned off Auto-Revert Mode in a + ;; non-standard way, for example by changing major mode. + (when (and (not auto-revert-mode) + (not auto-revert-tail-mode)) + (auto-revert-remove-current-buffer)) + (when (auto-revert-active-p) + ;; Enable file notification. + ;; Don't bother creating a notifier for non-file buffers + ;; unless it explicitly indicates that this works. + (when (and auto-revert-use-notify + (not auto-revert-notify-watch-descriptor) + (or buffer-file-name + buffer-auto-revert-by-notification)) + (auto-revert-notify-add-watch)) + (auto-revert-handler)))))) (defun auto-revert-buffers () "Revert buffers as specified by Auto-Revert and Global Auto-Revert Mode. diff --git a/lisp/dired-x.el b/lisp/dired-x.el index 59668f79bd7..016b97d34a2 100644 --- a/lisp/dired-x.el +++ b/lisp/dired-x.el @@ -475,7 +475,9 @@ status message." nil))) (let ((omit-re (or regexp (dired-omit-regexp))) (old-modified-p (buffer-modified-p)) - (count (or init-count 0))) + (count (or init-count 0)) + (dired-omit-verbose + (and dired-omit-verbose (not auto-revert-buffer-in-progress-p)))) (unless (string= omit-re "") (let ((dired-marker-char dired-omit-marker-char)) (when dired-omit-verbose (message "Omitting...")) From ba1453cf611e332dd771d87d38aadb6def668b87 Mon Sep 17 00:00:00 2001 From: Tassilo Horn Date: Fri, 8 Aug 2025 13:25:54 +0200 Subject: [PATCH 062/216] ; * lisp/doc-view.el (doc-view-initiate-display): Improve last commit --- lisp/doc-view.el | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lisp/doc-view.el b/lisp/doc-view.el index 26604f326fa..30536cb4c6c 100644 --- a/lisp/doc-view.el +++ b/lisp/doc-view.el @@ -2146,10 +2146,8 @@ If FILE-NAME is nil, use the current file instead." ;; Inhibit the echo area display of the "Type C-c C-c..." message ;; if the doc-view buffer is not shown in the selected window ;; which can happen due to auto-reverting the buffer (bug#79145). - (let ((inhibit-message - (not (eq (selected-window) - (get-buffer-window (current-buffer) - (selected-frame)))))) + (let ((inhibit-message (not (eq (current-buffer) + (window-buffer))))) (doc-view-buffer-message) (setf (doc-view-current-page) (or (doc-view-current-page) 1)) (if (doc-view-already-converted-p) From 5d5a00e4ade7ef2e243947813aa26c0da8eb579a Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Fri, 8 Aug 2025 14:39:34 +0300 Subject: [PATCH 063/216] Revert "Autoinsert: Allow condition to be a function" This reverts commit 738a7bb4949fdc93b0b637ac4c65423c928f0924. It caused regressions in autoinsert-tests, and causes conceptual problems due to its design. (Bug#79178) --- doc/misc/autotype.texi | 5 ++--- etc/NEWS | 8 -------- lisp/autoinsert.el | 29 +++++++---------------------- 3 files changed, 9 insertions(+), 33 deletions(-) diff --git a/doc/misc/autotype.texi b/doc/misc/autotype.texi index 3a1eef1c694..c53be54d0af 100644 --- a/doc/misc/autotype.texi +++ b/doc/misc/autotype.texi @@ -275,10 +275,9 @@ empty file is visited. This is accomplished by putting What gets inserted, if anything, is determined by the variable @code{auto-insert-alist}. The @sc{car} of each element of this list is either a mode name, making the element applicable when a buffer is -in that mode, a string, which is a regexp matched against a +in that mode, or a string, which is a regexp matched against a buffer's file name (the latter enables you to distinguish between -different kinds of files that have the same mode in Emacs) or a custom -predicate that takes no argument. The +different kinds of files that have the same mode in Emacs). The @sc{car} of an element may also be a cons cell, consisting of mode name or regexp, as above, and an additional descriptive string. diff --git a/etc/NEWS b/etc/NEWS index 2b3bff2f8ca..42d760ac09e 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -695,14 +695,6 @@ you could already use 'C-u C-x C-n' to clear the goal column. * Changes in Specialized Modes and Packages in Emacs 31.1 -** Autoinsert - -+++ -*** New condition for 'auto-insert-alist'. -'auto-insert-alist' now also allows to have a predicate taking no -argument as the condition. This allows to trigger 'auto-insert' with -finer grained control. - ** Register *** The "*Register Preview*" buffer shows only suitable registers. diff --git a/lisp/autoinsert.el b/lisp/autoinsert.el index 3c4057f1623..e7492d1b9ed 100644 --- a/lisp/autoinsert.el +++ b/lisp/autoinsert.el @@ -323,10 +323,8 @@ The document was typeset with ")) "A list specifying text to insert by default into a new file. Elements look like (CONDITION . ACTION) or ((CONDITION . DESCRIPTION) . ACTION). -CONDITION may be a regexp that must match the new file's name or a symbol that -must match the major mode for this element to apply. -CONDITION can also be custom predicate function of no arguments; Emacs will -insert the text if the predicate function returns non-nil. +CONDITION may be a regexp that must match the new file's name, or it may be +a symbol that must match the major mode for this element to apply. Only the first matching element is effective. Optional DESCRIPTION is a string for filling `auto-insert-prompt'. ACTION may be a skeleton to insert (see `skeleton-insert'), an absolute @@ -370,25 +368,12 @@ Matches the visited file name against the elements of `auto-insert-alist'." (pcase-lambda (`(,cond . ,action)) (if (atom cond) (setq desc cond) - ;; if `cond' is a lambda, don't split it but set `desc' to a custom string - (if (and (not (symbolp cond)) (functionp cond)) - (setq desc "`lambda condition'") - (setq desc (cdr cond) - cond (car cond)))) - (when (cond - ;; `cond' should be a major-mode variable - ((and (symbolp cond) (not (functionp cond))) - (derived-mode-p cond)) - - ;; `cond' should be a predicate that takes no argument - ;; It can either be a named function or a lambda - ((functionp cond) - (funcall cond)) - - ;; cond should be a regexp - (t + (setq desc (cdr cond) + cond (car cond))) + (when (if (symbolp cond) + (derived-mode-p cond) (and buffer-file-name - (string-match cond buffer-file-name)))) + (string-match cond buffer-file-name))) action)) auto-insert-alist))) (goto-char 1) From 12354bcfdc288f111c411c87e8bdebe6dcb41a4d Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Fri, 8 Aug 2025 13:30:24 +0100 Subject: [PATCH 064/216] VC other working trees: avoid reordering project--list * lisp/progmodes/project.el (project--remember-dir) (project-remember-project): New STABLE argument. * lisp/vc/vc.el (vc-add-working-tree) (vc--prompt-other-working-tree, vc-move-working-tree): Use it. --- lisp/progmodes/project.el | 28 ++++++++++++++++++---------- lisp/vc/vc.el | 8 ++++---- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/lisp/progmodes/project.el b/lisp/progmodes/project.el index dfeafc9877b..efc00ac8733 100644 --- a/lisp/progmodes/project.el +++ b/lisp/progmodes/project.el @@ -2049,26 +2049,34 @@ With some possible metadata (to be decided).") (current-buffer))) (write-region nil nil filename nil 'silent)))) -(defun project--remember-dir (root &optional no-write) +(defun project--remember-dir (root &optional no-write stable) "Add project root ROOT 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." +has changed, unless NO-WRITE is non-nil. +If STABLE is non-nil, don't move ROOT to the front of the project list +if it's already present further down the project list." (project--ensure-read-project-list) (let ((dir (abbreviate-file-name root))) - (unless (equal (caar project--list) dir) - (dolist (ent project--list) - (when (equal dir (car ent)) - (setq project--list (delq ent project--list)))) + (unless (if stable (assoc dir project--list) + (equal (caar project--list) dir)) + (unless stable + (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))))) ;;;###autoload -(defun project-remember-project (pr &optional no-write) +(defun project-remember-project (pr &optional no-write stable) "Add project PR to the front of the project list. If project PR satisfies `project-list-exclude', then nothing is done. Save the result in `project-list-file' if the list of projects -has changed, and NO-WRITE is nil." +has changed. +When called from Lisp, optional argument NO-WRITE non-nil means to +suppress saving `project-list-file'. +Optional argument STABLE means don't move PR to the front of the project +list if it's already present further down the project list." (interactive (list (project-current t))) (let ((root (project-root pr)) (interact (called-interactively-p 'any))) @@ -2079,9 +2087,9 @@ has changed, and NO-WRITE is nil." project-list-exclude) (when interact (message "Current project is blacklisted!")) + (project--remember-dir root no-write stable) (when interact - (message "Current project remembered")) - (project--remember-dir root no-write)))) + (message "Current project remembered"))))) (defun project--remove-from-project-list (project-root report-message) "Remove directory PROJECT-ROOT of a missing project from the project list. diff --git a/lisp/vc/vc.el b/lisp/vc/vc.el index 63a4fafc56c..cdcc639bedf 100644 --- a/lisp/vc/vc.el +++ b/lisp/vc/vc.el @@ -4404,9 +4404,9 @@ When called from Lisp, BACKEND is the VC backend." ;; `project-current' should not return nil in either case, but don't ;; signal an error if it does. (when-let* ((p (project-current))) - (project-remember-project p)) + (project-remember-project p nil t)) (when-let* ((p (project-current nil directory))) - (project-remember-project p)) + (project-remember-project p nil t)) (vc-dir directory backend)) @@ -4420,7 +4420,7 @@ PROMPT is the prompt string for `project-prompter'." (require 'project) (dolist (tree trees) (when-let* ((p (project-current nil tree))) - (project-remember-project p))) + (project-remember-project p nil t))) (funcall project-prompter prompt (lambda (k &optional _v) (member (or (car-safe k) k) trees)) @@ -4511,7 +4511,7 @@ BACKEND is the VC backend." (rename-buffer name)))))) (when-let* ((p (project-current nil to))) - (project-remember-project p))) + (project-remember-project p nil t))) From 67e16dca90b4a1bbcb33854ffc89741d154954dd Mon Sep 17 00:00:00 2001 From: Michael Albinus Date: Fri, 8 Aug 2025 18:27:30 +0200 Subject: [PATCH 065/216] Rework filenotify-tests.el * test/lisp/filenotify-tests.el (ert-temp-file-suffix): Set it. (with-file-notify-test): Don't set :suffix. (file-notify-testNN-*): Rework. --- test/lisp/filenotify-tests.el | 40 +++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/test/lisp/filenotify-tests.el b/test/lisp/filenotify-tests.el index 8b494456014..e4cd3a27c2d 100644 --- a/test/lisp/filenotify-tests.el +++ b/test/lisp/filenotify-tests.el @@ -181,6 +181,7 @@ Return nil when any other file notification watch is still active." (setq auth-source-cache-expiry nil auth-source-save-behavior nil + ert-temp-file-suffix "" file-notify-debug nil password-cache-expiry nil remote-file-name-inhibit-cache nil @@ -193,8 +194,8 @@ Return nil when any other file notification watch is still active." (defun file-notify--test-add-watch (file flags callback) "Like `file-notify-add-watch', but also passing FILE to CALLBACK." - (file-notify-add-watch file flags - (lambda (event) (funcall callback event file)))) + (file-notify-add-watch + file flags (lambda (event) (funcall callback event file)))) ;; We do not want to try and fail `file-notify-add-watch'. (defun file-notify--test-local-enabled () @@ -301,12 +302,10 @@ When returning, they are deleted." `(ert-with-temp-directory file-notify--test-tmpdir :prefix (expand-file-name "file-notify-test-parent" file-notify--test-rootdir) - :suffix "" (let ((ert-temp-file-prefix - (expand-file-name "file-notify-test" file-notify--test-tmpdir)) - (ert-temp-file-suffix "")) + (expand-file-name "file-notify-test" file-notify--test-tmpdir))) (ert-with-temp-file file-notify--test-tmpfile - :prefix ert-temp-file-prefix :suffix ert-temp-file-suffix + :prefix ert-temp-file-prefix (unwind-protect (progn ,@body) (file-notify--test-cleanup)))))) @@ -428,7 +427,7 @@ When returning, they are deleted." (with-file-notify-test (ert-with-temp-file file-notify--test-tmpfile1 - :prefix ert-temp-file-prefix :suffix ert-temp-file-suffix + :prefix ert-temp-file-prefix ;; Check, that no error is returned removing a watch descriptor twice. (write-region "any text" nil file-notify--test-tmpfile nil 'no-message) (write-region "any text" nil file-notify--test-tmpfile1 nil 'no-message) @@ -489,7 +488,7 @@ When returning, they are deleted." (with-file-notify-test (ert-with-temp-file file-notify--test-tmpfile1 - :prefix ert-temp-file-prefix :suffix ert-temp-file-suffix + :prefix ert-temp-file-prefix ;; Check `file-notify-rm-all-watches'. (write-region "any text" nil file-notify--test-tmpfile nil 'no-message) (write-region "any text" nil file-notify--test-tmpfile1 nil 'no-message) @@ -748,7 +747,7 @@ delivered." (with-file-notify-test (ert-with-temp-file file-notify--test-tmpfile1 - :prefix ert-temp-file-prefix :suffix ert-temp-file-suffix + :prefix ert-temp-file-prefix ;; Check copy of files inside a directory. (delete-file file-notify--test-tmpfile) (delete-file file-notify--test-tmpfile1) @@ -766,10 +765,15 @@ delivered." '(created changed created changed changed changed changed deleted deleted)) + ;; SMBWindows does not distinguish between `changed' and + ;; `attribute-changed'. + ((eq (file-notify--test-monitor) 'SMBWindows) + '(created changed created changed changed changed + deleted deleted deleted stopped)) ;; SMBSamba reports three `changed' events. ((eq (file-notify--test-monitor) 'SMBSamba) '(created changed changed changed created changed changed changed - deleted deleted deleted stopped)) + changed changed deleted deleted deleted stopped)) ;; There are three `deleted' events, for two files and for the ;; directory. Except for GFam{File,Directory}Monitor, ;; GPollFileMonitor and kqueue. @@ -800,7 +804,7 @@ delivered." (with-file-notify-test (ert-with-temp-file file-notify--test-tmpfile1 - :prefix ert-temp-file-prefix :suffix ert-temp-file-suffix + :prefix ert-temp-file-prefix ;; Check rename of files inside a directory. (delete-file file-notify--test-tmpfile) (delete-file file-notify--test-tmpfile1) @@ -866,11 +870,11 @@ delivered." ;; SMBWindows does not distinguish between `changed' and ;; `attribute-changed'. ((eq (file-notify--test-monitor) 'SMBWindows) - '(changed changed)) + '(changed changed changed)) ;; SMBSamba does not distinguish between `changed' and ;; `attribute-changed'. ((eq (file-notify--test-monitor) 'SMBSamba) - '(changed changed changed changed)) + '(changed changed changed changed changed)) ;; GFam{File,Directory}Monitor, GKqueueFileMonitor and ;; GPollFileMonitor do not report the `attribute-changed' event. ((memq (file-notify--test-monitor) @@ -1208,12 +1212,12 @@ delivered." ((eq (file-notify--test-monitor) 'SMBWindows) (let (r) (dotimes (_i n r) - (setq r (append '(changed deleted) r))))) + (setq r (append '(changed changed changed deleted) r))))) ;; SMBSamba fires both `changed' and `deleted' events. ((eq (file-notify--test-monitor) 'SMBSamba) (let (r) (dotimes (_i n r) - (setq r (append '(changed changed deleted) r))))) + (setq r (append '(changed changed changed changed deleted) r))))) ;; GFam{File,Directory}Monitor and GPollFileMonitor fire ;; `changed' and `deleted' events, sometimes in random order. ((memq (file-notify--test-monitor) @@ -1524,7 +1528,7 @@ the file watch." (with-file-notify-test (ert-with-temp-file file-notify--test-tmpfile1 - :prefix ert-temp-file-prefix :suffix ert-temp-file-suffix + :prefix ert-temp-file-prefix (delete-file file-notify--test-tmpfile) ;; Symlink a file. (write-region "any text" nil file-notify--test-tmpfile1 nil 'no-message) @@ -1598,7 +1602,7 @@ the file watch." (with-file-notify-test (ert-with-temp-directory file-notify--test-tmpfile1 - :prefix (concat ert-temp-file-prefix "-parent") :suffix ert-temp-file-suffix + :prefix (concat ert-temp-file-prefix "-parent") (delete-file file-notify--test-tmpfile) ;; Symlink a directory. (let ((tmpfile (expand-file-name "foo" file-notify--test-tmpfile)) @@ -1705,5 +1709,5 @@ the file watch." ;; * cygwin does not send all expected `changed' and `deleted' events. ;; Probably due to timing issues. -(provide 'file-notify-tests) +(provide 'filenotify-tests) ;;; filenotify-tests.el ends here From 42358d56f9a2bb6ebb9430d86f52e9117463a955 Mon Sep 17 00:00:00 2001 From: Michael Albinus Date: Fri, 8 Aug 2025 18:44:58 +0200 Subject: [PATCH 066/216] Rework autorevert-tests.el * lisp/autorevert.el (auto-revert-handler): Rearrange timer handling. * test/lisp/autorevert-tests.el (ert-temp-file-prefix): Don't set it. (auto-revert--test-rootdir, auto-revert--test-monitors): New defvars. Replace globally `temporary-file-directory' by `auto-revert--test-rootdir'. (auto-revert--wait-for-revert): Rewrite. (auto-revert--test-library, auto-revert--test-monitor) (auto-revert--skip-unless-proper-library-and-monitor): New defuns. (with-auto-revert-test): Let-bind `ert-temp-file-prefix'. Call `file-notify-rm-all-watches'. (auto-revert-test--write-region, auto-revert-test--write-file): New defuns. (auto-revert-testNN-*): Rework. --- lisp/autorevert.el | 9 +- test/lisp/autorevert-tests.el | 557 ++++++++++++++++++---------------- 2 files changed, 304 insertions(+), 262 deletions(-) diff --git a/lisp/autorevert.el b/lisp/autorevert.el index 30ec8dc2480..63c7d2928c7 100644 --- a/lisp/autorevert.el +++ b/lisp/autorevert.el @@ -838,16 +838,15 @@ This is an internal function used by Auto-Revert Mode." t))))) eob eoblist) (when (timerp auto-revert--lockout-timer) - (cancel-timer auto-revert--lockout-timer)) - (setq auto-revert-notify-modified-p nil - auto-revert--last-time - (if revert (current-time) auto-revert--last-time) - auto-revert--lockout-timer nil) + (cancel-timer auto-revert--lockout-timer) + (setq auto-revert--lockout-timer nil)) (when revert (when (and auto-revert-verbose (not (eq revert 'fast))) (message "Reverting buffer `%s'" (buffer-name))) + (setq auto-revert-notify-modified-p nil + auto-revert--last-time (current-time)) ;; If point (or a window point) is at the end of the buffer, we ;; want to keep it at the end after reverting. This allows one ;; to tail a file. diff --git a/test/lisp/autorevert-tests.el b/test/lisp/autorevert-tests.el index 9b46ec68b99..20b67628223 100644 --- a/test/lisp/autorevert-tests.el +++ b/test/lisp/autorevert-tests.el @@ -61,7 +61,6 @@ auto-revert-debug nil auto-revert-notify-exclude-dir-regexp "nothing-to-be-excluded" auto-revert-stop-on-user-input nil - ert-temp-file-prefix "auto-revert-test" ert-temp-file-suffix "" file-notify-debug nil password-cache-expiry nil @@ -73,6 +72,9 @@ tramp-allow-unsafe-temporary-files (or tramp-allow-unsafe-temporary-files noninteractive)) +(defvar auto-revert--test-rootdir temporary-file-directory) +(defvar auto-revert--test-monitors nil) + (defun auto-revert--timeout () "Time to wait for a message." (+ auto-revert-interval 1)) @@ -109,20 +111,81 @@ being the result.") "Wait until a message reports reversion of BUFFER. This expects `auto-revert--messages' to be bound by `ert-with-message-capture' before calling." - ;; Remote files do not cooperate well with timers. So we count ourselves. - (let ((ct (current-time)) - (text-quoting-style 'grave)) - (while (and (< (float-time (time-subtract nil ct)) - (auto-revert--timeout)) - (null (string-match-p - (rx bol "Reverting buffer `" - (literal (buffer-name buffer)) "'" eol) - (or auto-revert--messages "")))) - (if (and (or file-notify--library - (file-remote-p temporary-file-directory)) - (with-current-buffer buffer auto-revert-use-notify)) - (read-event nil nil 0.05) - (sleep-for 0.05))))) + ;; This is a central place to check for proper library and monitor. + (auto-revert--skip-unless-proper-library-and-monitor buffer) + (let ((text-quoting-style 'grave)) + (auto-revert-test--wait-for + (lambda () (string-match-p + (rx bol "Reverting buffer `" + (literal (buffer-name buffer)) "'" eol) + (or auto-revert--messages ""))) + (auto-revert--timeout)))) + +(defun auto-revert--test-library () + "The used library for the test, as a string. +In the remote case, it is the process name which runs on the +remote host, or nil." + (if (null (file-remote-p auto-revert--test-rootdir)) + (symbol-name file-notify--library) + (and (processp auto-revert-notify-watch-descriptor) + (replace-regexp-in-string + "<[[:digit:]]+>\\'" "" + (process-name auto-revert-notify-watch-descriptor))))) + +(defun auto-revert--test-monitor () + "The used monitor for the test, as a symbol. +This returns only for (local) gfilenotify, (remote) gio or (remote) +smb-notify libraries; otherwise it is nil. +`auto-revert-notify-watch-descriptor' must be a valid watch descriptor." + ;; We cache the result, because after `file-notify-rm-watch', + ;; `gfile-monitor-name' does not return a proper result anymore. + ;; But we still need this information. So far, we know the monitors + ;; - GFamFileMonitor (gfilenotify on cygwin) + ;; - GFamDirectoryMonitor (gfilenotify on Solaris) + ;; - GInotifyFileMonitor (gfilenotify and gio on GNU/Linux) + ;; - GKqueueFileMonitor (gfilenotify and gio on FreeBSD) + ;; - GPollFileMonitor (gio on cygwin) + ;; - SMBSamba (smb-notify on Samba server) + ;; - SMBWindows (smb-notify on MS Windows). + (when auto-revert-notify-watch-descriptor + (or (alist-get + auto-revert-notify-watch-descriptor auto-revert--test-monitors) + (when (member + (auto-revert--test-library) '("gfilenotify" "gio" "smb-notify")) + (add-to-list + 'auto-revert--test-monitors + (cons auto-revert-notify-watch-descriptor + (if (file-remote-p auto-revert--test-rootdir) + ;; `auto-revert-notify-watch-descriptor' is the + ;; connection process. + (progn + (while (and (process-live-p + auto-revert-notify-watch-descriptor) + (not (tramp-connection-property-p + auto-revert-notify-watch-descriptor + "file-monitor"))) + (accept-process-output + auto-revert-notify-watch-descriptor 0)) + (tramp-get-connection-property + auto-revert-notify-watch-descriptor "file-monitor")) + (and (functionp 'gfile-monitor-name) + (gfile-monitor-name + auto-revert-notify-watch-descriptor))))) + ;; If we don't know the monitor, there are good chances the + ;; test will fail. We skip it. + (unless (alist-get + auto-revert-notify-watch-descriptor auto-revert--test-monitors) + (ert-skip "Cannot determine test monitor"))) + (alist-get + auto-revert-notify-watch-descriptor auto-revert--test-monitors)))) + +(defun auto-revert--skip-unless-proper-library-and-monitor (&optional buffer) + "Skip unless there is no proper file notification library. +It is checked for buffer-local `auto-revert-notify-watch-descriptor'." + (with-current-buffer (or buffer (current-buffer)) + (when (eq (auto-revert--test-monitor) 'GKqueueFileMonitor) + (ert-skip (format "Monitor %s does not support auto-revert" + (auto-revert--test-monitor)))))) (defmacro auto-revert--deftest-remote (test docstring) "Define ert `TEST-remote' for remote files." @@ -130,14 +193,14 @@ This expects `auto-revert--messages' to be bound by `(ert-deftest ,(intern (concat (symbol-name test) "-remote")) () ,docstring :tags '(:expensive-test) - (let ((temporary-file-directory + (let ((auto-revert--test-rootdir ert-remote-temporary-file-directory) (auto-revert-remote-files t) (ert-test (ert-get-test ',test)) vc-handled-backends) (skip-unless (auto-revert--test-enabled-remote)) (tramp-cleanup-connection - (tramp-dissect-file-name temporary-file-directory) nil 'keep-password) + (tramp-dissect-file-name auto-revert--test-rootdir) t 'keep-password) (condition-case err (funcall (ert-test-body ert-test)) (error (message "%s" err) (signal (car err) (cdr err))))))) @@ -145,33 +208,40 @@ This expects `auto-revert--messages' to be bound by (defmacro with-auto-revert-test (&rest body) (declare (debug t)) `(let ((auto-revert-interval-orig auto-revert-interval) - (auto-revert--lockout-interval-orig auto-revert--lockout-interval)) + (auto-revert--lockout-interval-orig auto-revert--lockout-interval) + (ert-temp-file-prefix + (expand-file-name "auto-revert-test" auto-revert--test-rootdir))) (unwind-protect (progn - (unless (file-remote-p temporary-file-directory) + (unless (file-remote-p auto-revert--test-rootdir) (customize-set-variable 'auto-revert-interval 0.1) (setq auto-revert--lockout-interval 0.05)) ,@body) (customize-set-variable 'auto-revert-interval auto-revert-interval-orig) - (setq auto-revert--lockout-interval auto-revert--lockout-interval-orig)))) + (setq auto-revert--lockout-interval auto-revert--lockout-interval-orig) + (file-notify-rm-all-watches)))) -(defun auto-revert-tests--write-file (text file time-delta &optional append) - (write-region text nil file append 'no-message) - (set-file-times file (time-subtract nil time-delta))) +(defun auto-revert-test--write-region (string file &optional append) + "Write STRING to FILE." + (write-region string nil file append 'no-message)) + +(defun auto-revert-test--write-file (string file &optional append) + "Write STRING to FILE. +Set modified file time in order to trigger auto-revert." + (auto-revert-test--write-region string file append) + (set-file-times file (time-subtract nil (seconds-to-time 60)))) (ert-deftest auto-revert-test00-auto-revert-mode () "Check autorevert for a file." - (file-notify-rm-all-watches) - ;; `auto-revert-buffers' runs every 5". And we must wait, until the ;; file has been reverted. (with-auto-revert-test (ert-with-temp-file tmpfile - (let ((times '(60 30 15)) - buf) + :prefix ert-temp-file-prefix + (let (buf) (unwind-protect (progn - (auto-revert-tests--write-file "any text" tmpfile (pop times)) + (auto-revert-test--write-region "any text" tmpfile) (setq buf (find-file-noselect tmpfile)) (with-current-buffer buf (ert-with-message-capture auto-revert--messages @@ -191,8 +261,7 @@ This expects `auto-revert--messages' to be bound by (lambda () (null auto-revert--lockout-timer)) (auto-revert--timeout)) - (auto-revert-tests--write-file - "another text" tmpfile (pop times)) + (auto-revert-test--write-file "another text" tmpfile) ;; Check, that the buffer has been reverted. (auto-revert--wait-for-revert buf)) @@ -203,7 +272,7 @@ This expects `auto-revert--messages' to be bound by ;; When the buffer is modified, it shall not be reverted. (ert-with-message-capture auto-revert--messages (set-buffer-modified-p t) - (auto-revert-tests--write-file "any text" tmpfile (pop times)) + (auto-revert-test--write-file "any text" tmpfile) ;; Check, that the buffer hasn't been reverted. (auto-revert--wait-for-revert buf)) @@ -222,29 +291,25 @@ This expects `auto-revert--messages' to be bound by ;; This is inspired by Bug#21841. (ert-deftest auto-revert-test01-auto-revert-several-files () "Check autorevert for several files at once." - (skip-unless (executable-find "cp" (file-remote-p temporary-file-directory))) - (file-notify-rm-all-watches) + (let ((default-directory auto-revert--test-rootdir)) + (skip-unless + (executable-find "cp" (file-remote-p auto-revert--test-rootdir)))) (with-auto-revert-test (ert-with-temp-directory tmpdir1 - :prefix "auto-revert-test-parent" + :prefix (concat ert-temp-file-prefix "-parent") (ert-with-temp-directory tmpdir2 - :prefix "auto-revert-test-parent" + :prefix (concat ert-temp-file-prefix "-parent") (ert-with-temp-file tmpfile1 :prefix (expand-file-name "auto-revert-test" tmpdir1) (ert-with-temp-file tmpfile2 - :prefix (expand-file-name "auto-revert-test" tmpdir1) - (let* ((cp (executable-find - "cp" (file-remote-p temporary-file-directory))) - (times '(120 60 30 15)) - buf1 buf2) + :prefix (expand-file-name "auto-revert-test" tmpdir2) + (let (buf1 buf2) (unwind-protect (ert-with-message-capture auto-revert--messages - (auto-revert-tests--write-file - "any text" tmpfile1 (pop times)) + (auto-revert-test--write-region "any text" tmpfile1) (setq buf1 (find-file-noselect tmpfile1)) - (auto-revert-tests--write-file - "any text" tmpfile2 (pop times)) + (auto-revert-test--write-region "any text" tmpfile2) (setq buf2 (find-file-noselect tmpfile2)) (dolist (buf (list buf1 buf2)) @@ -260,21 +325,24 @@ This expects `auto-revert--messages' to be bound by ;; Modify files. We wait for a second, in order to have ;; another timestamp. - (auto-revert-tests--write-file + ;; `tmpdir2' is not under auto-revert. + (auto-revert-test--write-file "another text" - (expand-file-name (file-name-nondirectory tmpfile1) tmpdir2) - (pop times)) - (auto-revert-tests--write-file + (expand-file-name (file-name-nondirectory tmpfile1) tmpdir2)) + (auto-revert-test--write-file "another text" - (expand-file-name (file-name-nondirectory tmpfile2) tmpdir2) - (pop times)) - ;;(copy-directory tmpdir2 tmpdir1 nil 'copy-contents) - ;; Strange, that `copy-directory' does not work as expected. - ;; The following shell command is not portable on all - ;; platforms, unfortunately. - (shell-command - (format "%s -f %s/* %s" cp - (file-local-name tmpdir2) (file-local-name tmpdir1))) + (expand-file-name (file-name-nondirectory tmpfile2) tmpdir2)) + + ;; (copy-directory tmpdir2 tmpdir1 'keep 'copy-contents) + ;; Strange, that `copy-directory' does not work as + ;; expected. The following shell command is not + ;; portable on all platforms, unfortunately. + (let ((default-directory auto-revert--test-rootdir)) + (shell-command + (format "%s -pf %s/* %s" + (executable-find "cp" t) + (file-local-name tmpdir2) + (file-local-name tmpdir1)))) ;; Check, that the buffers have been reverted. (dolist (buf (list buf1 buf2)) @@ -297,15 +365,13 @@ This expects `auto-revert--messages' to be bound by ;; This is inspired by Bug#23276. (ert-deftest auto-revert-test02-auto-revert-deleted-file () "Check autorevert for a deleted file." - (file-notify-rm-all-watches) - (with-auto-revert-test (ert-with-temp-file tmpfile - (let ((times '(120 60 30 15)) - buf desc) + :prefix ert-temp-file-prefix + (let (buf) (unwind-protect (progn - (auto-revert-tests--write-file "any text" tmpfile (pop times)) + (auto-revert-test--write-region "any text" tmpfile) (setq buf (find-file-noselect tmpfile)) (with-current-buffer buf (should-not @@ -318,7 +384,6 @@ This expects `auto-revert--messages' to be bound by ;; it returns nil. (auto-revert-mode 1) (should auto-revert-mode) - (setq desc auto-revert-notify-watch-descriptor) ;; Remove file while reverting. We simulate this by ;; modifying `before-revert-hook'. @@ -331,9 +396,7 @@ This expects `auto-revert--messages' to be bound by nil t) (ert-with-message-capture auto-revert--messages - (auto-revert-tests--write-file - "another text" tmpfile (pop times)) - (should (eq desc auto-revert-notify-watch-descriptor)) + (auto-revert-test--write-file "another text" tmpfile) (auto-revert--wait-for-revert buf)) ;; Check, that the buffer hasn't been reverted. File ;; notification should be disabled, falling back to @@ -346,23 +409,16 @@ This expects `auto-revert--messages' to be bound by ;; reverted. (kill-local-variable 'before-revert-hook) (ert-with-message-capture auto-revert--messages - (auto-revert-tests--write-file - "another text" tmpfile (pop times)) + (auto-revert-test--write-file "another text" tmpfile) (auto-revert--wait-for-revert buf)) ;; Check, that the buffer has been reverted. (should (string-match-p "another text" (substring-no-properties (buffer-string)))) - ;; When file notification is used, it must be reenabled - ;; after recreation of the file. We cannot expect that - ;; the descriptor is the same, so we just check the - ;; existence. - (should - (eq (null desc) (null auto-revert-notify-watch-descriptor))) ;; An empty file shall still be reverted. (ert-with-message-capture auto-revert--messages - (auto-revert-tests--write-file "" tmpfile (pop times)) + (auto-revert-test--write-file "" tmpfile) (auto-revert--wait-for-revert buf)) ;; Check, that the buffer has been reverted. (should @@ -378,59 +434,55 @@ This expects `auto-revert--messages' to be bound by (ert-deftest auto-revert-test03-auto-revert-tail-mode () "Check autorevert tail mode." - (file-notify-rm-all-watches) - (with-auto-revert-test - ;; `auto-revert-buffers' runs every 5". And we must wait, until the - ;; file has been reverted. - (ert-with-temp-file tmpfile - (let ((times '(30 15)) - buf) - (unwind-protect - (ert-with-message-capture auto-revert--messages - (auto-revert-tests--write-file "any text" tmpfile (pop times)) - (setq buf (find-file-noselect tmpfile)) - (with-current-buffer buf - ;; `buffer-stale--default-function' checks for - ;; `verify-visited-file-modtime'. We must ensure that it - ;; returns nil. - (auto-revert-tail-mode 1) - (should auto-revert-tail-mode) - (erase-buffer) - (insert "modified text\n") - (set-buffer-modified-p nil) + ;; `auto-revert-buffers' runs every 5". And we must wait, until the + ;; file has been reverted. + (ert-with-temp-file tmpfile + :prefix ert-temp-file-prefix + (let (buf) + (unwind-protect + (ert-with-message-capture auto-revert--messages + (auto-revert-test--write-region "any text" tmpfile) + (setq buf (find-file-noselect tmpfile)) + (with-current-buffer buf + ;; `buffer-stale--default-function' checks for + ;; `verify-visited-file-modtime'. We must ensure that it + ;; returns nil. + (auto-revert-tail-mode 1) + (should auto-revert-tail-mode) + (erase-buffer) + (insert "modified text\n") + (set-buffer-modified-p nil) - ;; Modify file. - (auto-revert-tests--write-file - "another text" tmpfile (pop times) 'append) + ;; Modify file. + (auto-revert-test--write-file "another text" tmpfile 'append) - ;; Check, that the buffer has been reverted. - (auto-revert--wait-for-revert buf) - (should - (string-match-p - "modified text\nanother text" - (substring-no-properties (buffer-string)))))) + ;; Check, that the buffer has been reverted. + (auto-revert--wait-for-revert buf) + (should + (string-match-p + "modified text\nanother text" + (substring-no-properties (buffer-string)))))) - ;; Exit. - (ignore-errors (kill-buffer buf))))))) + ;; Exit. + (ignore-errors + (with-current-buffer buf (set-buffer-modified-p nil)) + (kill-buffer buf))))))) (auto-revert--deftest-remote auto-revert-test03-auto-revert-tail-mode "Check remote autorevert tail mode.") (ert-deftest auto-revert-test04-auto-revert-mode-dired () "Check autorevert for dired." - (file-notify-rm-all-watches) - ;; `auto-revert-buffers' runs every 5". And we must wait, until the ;; file has been reverted. (with-auto-revert-test (ert-with-temp-directory tmpdir - :prefix "auto-revert-test-parent" + :prefix (concat ert-temp-file-prefix "-parent") (ert-with-temp-file tmpfile :prefix (expand-file-name "auto-revert-test" tmpdir) - (let* ((name (file-name-nondirectory tmpfile)) - (times '(30)) - buf) + (let ((name (file-name-nondirectory tmpfile)) + buf) (unwind-protect (progn (setq buf (dired-noselect tmpdir)) @@ -473,7 +525,7 @@ This expects `auto-revert--messages' to be bound by ;; Make dired buffer modified. Check, that the ;; buffer has been still reverted. (set-buffer-modified-p t) - (auto-revert-tests--write-file "any text" tmpfile (pop times)) + (auto-revert-test--write-region "any text" tmpfile) (auto-revert--wait-for-revert buf)) ;; Check, that the buffer has been reverted. (should @@ -489,10 +541,6 @@ This expects `auto-revert--messages' to be bound by (auto-revert--deftest-remote auto-revert-test04-auto-revert-mode-dired "Check remote autorevert for dired.") -(defun auto-revert-test--write-file (string file) - "Write STRING to FILE." - (write-region string nil file nil 'no-message)) - (defun auto-revert-test--buffer-string (buffer) "Contents of BUFFER as a string." (with-current-buffer buffer @@ -525,18 +573,20 @@ This expects `auto-revert--messages' to be bound by (ert-deftest auto-revert-test05-global-notify () "Test `global-auto-revert-mode' without polling." (skip-unless (or file-notify--library - (file-remote-p temporary-file-directory))) - (file-notify-rm-all-watches) + (file-remote-p auto-revert--test-rootdir))) (with-auto-revert-test (ert-with-temp-file file-1 + :prefix ert-temp-file-prefix (ert-with-temp-file file-2 + :prefix ert-temp-file-prefix (ert-with-temp-file file-3 - (let* ((auto-revert-use-notify t) - (auto-revert-avoid-polling t) - (was-in-global-auto-revert-mode global-auto-revert-mode) - (file-2b (concat file-2 "-b")) - require-final-newline buf-1 buf-2 buf-3) + :prefix ert-temp-file-prefix + (let ((auto-revert-use-notify t) + (auto-revert-avoid-polling t) + (was-in-global-auto-revert-mode global-auto-revert-mode) + (file-2b (concat file-2 "-b")) + require-final-newline buf-1 buf-2 buf-3) (unwind-protect (progn (setq buf-1 (find-file-noselect file-1)) @@ -552,24 +602,17 @@ This expects `auto-revert--messages' to be bound by (string-equal (auto-revert-test--buffer-string buf-1) "")) (global-auto-revert-mode 1) ; Turn it on. + (auto-revert--skip-unless-proper-library-and-monitor buf-1) - (should (buffer-local-value - 'auto-revert-notify-watch-descriptor buf-1)) - (should (buffer-local-value - 'auto-revert-notify-watch-descriptor buf-2)) - - ;; Allow for some time to handle notification events. - (auto-revert-test--wait-for-buffer-text - buf-1 "1-a" (auto-revert--timeout)) ;; buf-1 should have been reverted immediately when the mode ;; was enabled. (should (string-equal (auto-revert-test--buffer-string buf-1) "1-a")) ;; Alter a file. - (auto-revert-test--write-file "2-a" file-2) - ;; Allow for some time to handle notification events. - (auto-revert-test--wait-for-buffer-text buf-2 "2-a" 1) + (ert-with-message-capture auto-revert--messages + (auto-revert-test--write-region "2-a" file-2 buf-2) + (auto-revert--wait-for-revert buf-2)) (should (string-equal (auto-revert-test--buffer-string buf-2) "2-a")) @@ -584,8 +627,9 @@ This expects `auto-revert--messages' to be bound by (auto-revert--timeout)) (should (buffer-local-value 'auto-revert-notify-watch-descriptor buf-3)) - (auto-revert-test--write-file "3-a" file-3) - (auto-revert-test--wait-for-buffer-text buf-3 "3-a" 1) + (auto-revert-test--write-region "3-a" file-3) + (auto-revert-test--wait-for-buffer-text + buf-3 "3-a" (auto-revert--timeout)) (should (string-equal (auto-revert-test--buffer-string buf-3) "3-a")) @@ -593,7 +637,7 @@ This expects `auto-revert--messages' to be bound by (delete-file file-1) (should (string-equal (auto-revert-test--buffer-string buf-1) "1-a")) - (auto-revert-test--write-file "1-b" file-1) + (auto-revert-test--write-region "1-b" file-1) ;; Since the file is deleted, it needs at least ;; `auto-revert-interval' to recognize the new file, ;; while polling. So increase the timeout. @@ -608,7 +652,7 @@ This expects `auto-revert--messages' to be bound by (write-file file-2b)) (should (string-equal (auto-revert-test--buffer-string buf-2) "2-a")) - (auto-revert-test--write-file "2-b" file-2b) + (auto-revert-test--write-region "2-b" file-2b) (auto-revert-test--wait-for-buffer-text buf-2 "2-b" (auto-revert--timeout)) (should (buffer-local-value @@ -629,14 +673,14 @@ This expects `auto-revert--messages' to be bound by (ert-deftest auto-revert-test06-write-file () "Verify that notification follows `write-file' correctly." (skip-unless (or file-notify--library - (file-remote-p temporary-file-directory))) - (file-notify-rm-all-watches) + (file-remote-p auto-revert--test-rootdir))) (with-auto-revert-test (ert-with-temp-file file-1 - (let* ((auto-revert-use-notify t) - (file-2 (concat file-1 "-2")) - require-final-newline buf) + :prefix ert-temp-file-prefix + (let ((auto-revert-use-notify t) + (file-2 (concat file-1 "-2")) + require-final-newline buf) (unwind-protect (progn (setq buf (find-file-noselect file-1)) @@ -645,6 +689,7 @@ This expects `auto-revert--messages' to be bound by (save-buffer) (auto-revert-mode 1) + (auto-revert--skip-unless-proper-library-and-monitor) (insert "B") (write-file file-2) @@ -653,14 +698,16 @@ This expects `auto-revert--messages' to be bound by ;; second. Wait a little bit. (when (file-remote-p file-1) (sleep-for (auto-revert--timeout))) - (auto-revert-test--write-file "C" file-2) + (auto-revert-test--write-region "C" file-2) (auto-revert-test--wait-for-buffer-text buf "C" (auto-revert--timeout)) (should (string-equal (substring-no-properties (buffer-string)) "C")))) ;; Clean up. - (ignore-errors (kill-buffer buf)) + (ignore-errors + (with-current-buffer buf (set-buffer-modified-p nil)) + (kill-buffer buf)) (ignore-errors (delete-file file-2))))))) (auto-revert--deftest-remote auto-revert-test06-write-file @@ -670,138 +717,135 @@ This expects `auto-revert--messages' to be bound by (ert-deftest auto-revert-test07-auto-revert-several-buffers () "Check autorevert for several buffers visiting the same file." (skip-unless (or file-notify--library - (file-remote-p temporary-file-directory))) - (file-notify-rm-all-watches) + (file-remote-p auto-revert--test-rootdir))) (with-auto-revert-test - (ert-with-temp-file tmpfile - (let ((auto-revert-use-notify t) - (times '(120 60 30 15)) - (num-buffers 10) - require-final-newline buffers) + (ert-with-temp-file tmpfile + :prefix ert-temp-file-prefix + (let ((auto-revert-use-notify t) + (num-buffers 10) + require-final-newline buffers) - (unwind-protect - ;; Check indirect buffers. - (ert-with-message-capture auto-revert--messages - (auto-revert-tests--write-file "any text" tmpfile (pop times)) - (push (find-file-noselect tmpfile) buffers) - (with-current-buffer (car buffers) - (should - (string-equal - (substring-no-properties (buffer-string)) "any text")) - ;; `buffer-stale--default-function' checks for - ;; `verify-visited-file-modtime'. We must ensure that - ;; it returns nil. - (auto-revert-mode 1) - (should auto-revert-mode)) + (unwind-protect + ;; Check indirect buffers. + (ert-with-message-capture auto-revert--messages + (auto-revert-test--write-region "any text" tmpfile) + (push (find-file-noselect tmpfile) buffers) + (with-current-buffer (car buffers) + (should + (string-equal + (substring-no-properties (buffer-string)) "any text")) + ;; `buffer-stale--default-function' checks for + ;; `verify-visited-file-modtime'. We must ensure that + ;; it returns nil. + (auto-revert-mode 1) + (should auto-revert-mode)) - (dolist (clone '(clone nil)) - (dotimes (i num-buffers) - (push (make-indirect-buffer - (car (last buffers)) - (format "%s-%d-%s" - (buffer-file-name (car (last buffers))) i clone) - clone) - buffers))) - (setq buffers (nreverse buffers)) - (dolist (buf buffers) - (with-current-buffer buf - (should - (string-equal - (substring-no-properties (buffer-string)) "any text")) - (if (string-suffix-p "-nil" (buffer-name buf)) - (should-not auto-revert-mode) - (should auto-revert-mode)))) + (dolist (clone '(clone nil)) + (dotimes (i num-buffers) + (push (make-indirect-buffer + (car (last buffers)) + (format "%s-%d-%s" + (buffer-file-name (car (last buffers))) i clone) + clone) + buffers))) + (setq buffers (nreverse buffers)) + (dolist (buf buffers) + (with-current-buffer buf + (should + (string-equal + (substring-no-properties (buffer-string)) "any text")) + (if (string-suffix-p "-nil" (buffer-name buf)) + (should-not auto-revert-mode) + (should auto-revert-mode)))) - (auto-revert-tests--write-file "another text" tmpfile (pop times)) - ;; Check, that the buffer has been reverted. - (auto-revert--wait-for-revert (car buffers)) - (dolist (buf buffers) - (with-current-buffer buf - (should - (string-equal - (substring-no-properties (buffer-string)) "another text")))) + (auto-revert-test--write-file "another text" tmpfile) + ;; Check, that the buffer has been reverted. + (auto-revert--wait-for-revert (car buffers)) + (dolist (buf buffers) + (with-current-buffer buf + (should + (string-equal + (substring-no-properties (buffer-string)) "another text")))) - ;; Disabling autorevert in an indirect buffer does not - ;; disable autorevert in the corresponding base buffer. - (dolist (buf (cdr buffers)) - (with-current-buffer buf - (auto-revert-mode 0) - (should-not auto-revert-mode)) - (with-current-buffer (car buffers) - (should - (buffer-local-value - 'auto-revert-notify-watch-descriptor (current-buffer))) - (should auto-revert-mode))) + ;; Disabling autorevert in an indirect buffer does not + ;; disable autorevert in the corresponding base buffer. + (dolist (buf (cdr buffers)) + (with-current-buffer buf + (auto-revert-mode 0) + (should-not auto-revert-mode)) + (with-current-buffer (car buffers) + (should + (buffer-local-value + 'auto-revert-notify-watch-descriptor (current-buffer))) + (should auto-revert-mode))) - ;; Killing an indirect buffer does not disable autorevert in - ;; the corresponding base buffer. - (dolist (buf (cdr buffers)) - (kill-buffer buf)) - (with-current-buffer (car buffers) - (should - (buffer-local-value - 'auto-revert-notify-watch-descriptor (current-buffer))) - (should auto-revert-mode))) + ;; Killing an indirect buffer does not disable autorevert in + ;; the corresponding base buffer. + (dolist (buf (cdr buffers)) + (kill-buffer buf)) + (with-current-buffer (car buffers) + (should + (buffer-local-value + 'auto-revert-notify-watch-descriptor (current-buffer))) + (should auto-revert-mode))) - ;; Exit. - (ignore-errors - (dolist (buf buffers) - (with-current-buffer buf (set-buffer-modified-p nil)) - (kill-buffer buf))) - (setq buffers nil) - (ignore-errors (delete-file tmpfile))) + ;; Exit. + (ignore-errors + (dolist (buf buffers) + (with-current-buffer buf (set-buffer-modified-p nil)) + (kill-buffer buf))) + (setq buffers nil) + (ignore-errors (delete-file tmpfile))) - ;; Check direct buffers. - (unwind-protect - (ert-with-message-capture auto-revert--messages - (auto-revert-tests--write-file "any text" tmpfile (pop times)) + ;; Check direct buffers. + (unwind-protect + (ert-with-message-capture auto-revert--messages + (auto-revert-test--write-region "any text" tmpfile) - (dotimes (i num-buffers) - (push (generate-new-buffer - (format "%s-%d" (file-name-nondirectory tmpfile) i)) - buffers)) - (setq buffers (nreverse buffers)) - (dolist (buf buffers) - (with-current-buffer buf - (insert-file-contents tmpfile 'visit) - (should - (string-equal - (substring-no-properties (buffer-string)) "any text")) - (auto-revert-mode 1) - (should auto-revert-mode))) + (dotimes (i num-buffers) + (push (generate-new-buffer + (format "%s-%d" (file-name-nondirectory tmpfile) i)) + buffers)) + (setq buffers (nreverse buffers)) + (dolist (buf buffers) + (with-current-buffer buf + (insert-file-contents tmpfile 'visit) + (should + (string-equal + (substring-no-properties (buffer-string)) "any text")) + (auto-revert-mode 1) + (should auto-revert-mode))) - (auto-revert-tests--write-file "another text" tmpfile (pop times)) - ;; Check, that the buffers have been reverted. - (dolist (buf buffers) - (auto-revert--wait-for-revert buf) - (with-current-buffer buf - (should - (string-equal - (substring-no-properties (buffer-string)) "another text"))))) + (auto-revert-test--write-file "another text" tmpfile) + ;; Check, that the buffers have been reverted. + (dolist (buf buffers) + (auto-revert--wait-for-revert buf) + (with-current-buffer buf + (should + (string-equal + (substring-no-properties (buffer-string)) "another text"))))) - ;; Exit. - (ignore-errors - (dolist (buf buffers) - (with-current-buffer buf (set-buffer-modified-p nil)) - (kill-buffer buf)))))))) + ;; Exit. + (ignore-errors + (dolist (buf buffers) + (with-current-buffer buf (set-buffer-modified-p nil)) + (kill-buffer buf)))))))) (auto-revert--deftest-remote auto-revert-test07-auto-revert-several-buffers "Check autorevert for several buffers visiting the same remote file.") (ert-deftest auto-revert-test08-auto-revert-inhibit-auto-revert () "Check the power of `inhibit-auto-revert'." - (file-notify-rm-all-watches) - ;; `auto-revert-buffers' runs every 5". And we must wait, until the ;; file has been reverted. (with-auto-revert-test (ert-with-temp-file tmpfile - (let ((times '(60 30 15)) - buf) + :prefix ert-temp-file-prefix + (let (buf) (unwind-protect (progn - (auto-revert-tests--write-file "any text" tmpfile (pop times)) + (auto-revert-test--write-region "any text" tmpfile) (setq buf (find-file-noselect tmpfile)) (with-current-buffer buf (ert-with-message-capture auto-revert--messages @@ -809,8 +853,7 @@ This expects `auto-revert--messages' to be bound by (auto-revert-mode 1) (should auto-revert-mode) - (auto-revert-tests--write-file - "another text" tmpfile (pop times)) + (auto-revert-test--write-file "another text" tmpfile) ;; Check, that the buffer hasn't been reverted. (auto-revert--wait-for-revert buf) (should-not @@ -844,5 +887,5 @@ This expects `auto-revert--messages' to be bound by (ert-run-tests-interactively "^auto-revert-") (ert-run-tests-batch "^auto-revert-"))) -(provide 'auto-revert-tests) +(provide 'autorevert-tests) ;;; autorevert-tests.el ends here From 533da29545a4acc63d736011da2aac586e457330 Mon Sep 17 00:00:00 2001 From: Po Lu Date: Sat, 9 Aug 2025 09:26:28 +0800 Subject: [PATCH 067/216] ; * src/android.c (android_url_encode): Typo in comment. --- src/android.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/android.c b/src/android.c index 5c78dbd9171..345ddf8663d 100644 --- a/src/android.c +++ b/src/android.c @@ -1142,10 +1142,10 @@ android_url_encode (const char *restrict string, size_t length, while (string < end) { - /* XXX: Android documentation claims that URIs is encoded + /* XXX: Android documentation claims that a URI is to be encoded according to the ``Unicode'' scheme, but what this means in - reality is that the URI is encoded in UTF-8, and then - each of its bytes are encoded. */ + reality is that the URI is encoded in UTF-8, and then each of + the resulting bytes is separately URI-encoded. */ /* Find the length of the multibyte character at STRING. */ len = /* multibyte_length (string, end, true, true) */ 1; From f3eeb762aec95c197a56b2e476c73e812a001b32 Mon Sep 17 00:00:00 2001 From: Stefan Monnier Date: Sat, 9 Aug 2025 03:37:18 -0400 Subject: [PATCH 068/216] (js-json-mode): Set `editorconfig-indent-size-vars` * lisp/progmodes/js.el (js-json-mode): Now that we don't inherit from `js-mode` any more, we can't rely on the heuristic to find the indentation var, so we need to.set `editorconfig-indent-size-vars` explicitly. --- lisp/progmodes/js.el | 1 + 1 file changed, 1 insertion(+) diff --git a/lisp/progmodes/js.el b/lisp/progmodes/js.el index bed2ef0616a..5254e0d7089 100644 --- a/lisp/progmodes/js.el +++ b/lisp/progmodes/js.el @@ -4142,6 +4142,7 @@ See `treesit-thing-settings' for more information.") :syntax-table js-mode-syntax-table (js--mode-setup) ;Reuse most of `js-mode', but not as parent (bug#67463). (setq-local js-enabled-frameworks nil) + (setq-local editorconfig-indent-size-vars '(js-indent-level)) ;; Speed up `syntax-ppss': JSON files can be big but can't hold ;; regexp matchers nor #! thingies (and `js-enabled-frameworks' is nil). (setq-local syntax-propertize-function #'ignore)) From 0da899c0353851661755b31008be5728b7a3bef2 Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Sat, 9 Aug 2025 11:29:15 +0300 Subject: [PATCH 069/216] ; Fix autorevert-tests for MS-Window * test/lisp/autorevert-tests.el (auto-revert-test04-auto-revert-mode-dired): Fix for MS-Windows. --- test/lisp/autorevert-tests.el | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/test/lisp/autorevert-tests.el b/test/lisp/autorevert-tests.el index 73fd5a66fa2..18764e5d02d 100644 --- a/test/lisp/autorevert-tests.el +++ b/test/lisp/autorevert-tests.el @@ -392,9 +392,13 @@ This expects `auto-revert--messages' to be bound by ;; Delete file. (delete-file tmpfile) (auto-revert--wait-for-revert buf)) - ;; Check, that the buffer has been reverted. - (should-not - (string-match name (substring-no-properties (buffer-string)))) + ;; Check, that the buffer has been reverted. (On + ;; MS-Windows, this can randomly fail for unknown + ;; reasons.) + (unless (eq system-type 'windows-nt) + (should-not + (string-match name (substring-no-properties + (buffer-string))))) (ert-with-message-capture auto-revert--messages ;; Make dired buffer modified. Check, that the buffer has From 4dd67e5d733e4d61750043b0e075d357d48a4c87 Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Sat, 9 Aug 2025 10:39:19 +0100 Subject: [PATCH 070/216] VC: Do move new working trees to the beginning of the project list * lisp/vc/vc.el (vc-add-working-tree, vc-move-working-tree): Have project-remember-project move the new working trees to the front of the list of projects. Suggested by Dmitry Gutov . --- lisp/vc/vc.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lisp/vc/vc.el b/lisp/vc/vc.el index cdcc639bedf..2219739fb55 100644 --- a/lisp/vc/vc.el +++ b/lisp/vc/vc.el @@ -4406,7 +4406,7 @@ When called from Lisp, BACKEND is the VC backend." (when-let* ((p (project-current))) (project-remember-project p nil t)) (when-let* ((p (project-current nil directory))) - (project-remember-project p nil t)) + (project-remember-project p)) (vc-dir directory backend)) @@ -4511,7 +4511,7 @@ BACKEND is the VC backend." (rename-buffer name)))))) (when-let* ((p (project-current nil to))) - (project-remember-project p nil t))) + (project-remember-project p))) From c05ea64d8268315093673f8be29d4ceb92198b81 Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Sat, 9 Aug 2025 10:43:26 +0100 Subject: [PATCH 071/216] define-globalized-minor-mode: Use unique MODE-major-mode (bug#79198) * lisp/emacs-lisp/easy-mmode.el (define-globalized-minor-mode) : Make this an internal variable using '--'. : Make this an internal variable using '--'. Use the name of the global mode not the local mode (bug#79198). --- lisp/emacs-lisp/easy-mmode.el | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lisp/emacs-lisp/easy-mmode.el b/lisp/emacs-lisp/easy-mmode.el index 46c99052090..808d87ac898 100644 --- a/lisp/emacs-lisp/easy-mmode.el +++ b/lisp/emacs-lisp/easy-mmode.el @@ -499,8 +499,9 @@ on if the hook has explicitly disabled it. (MODE-enable-in-buffer (intern (concat global-mode-name "-enable-in-buffer"))) (minor-MODE-hook (intern (concat mode-name "-hook"))) - (MODE-set-explicitly (intern (concat mode-name "-set-explicitly"))) - (MODE-major-mode (intern (concat (symbol-name mode) "-major-mode"))) + (MODE-set-explicitly (intern (concat mode-name "--set-explicitly"))) + (MODE-major-mode (intern (concat (symbol-name global-mode) + "--major-mode"))) (MODE-predicate (intern (concat (replace-regexp-in-string "-mode\\'" "" global-mode-name) "-modes"))) From 6ffa926f903a424457910a4e3f54d41fa55821a0 Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Sat, 9 Aug 2025 11:15:31 +0100 Subject: [PATCH 072/216] vc-move-working-tree: Improve updating VC-Dir buffers * lisp/vc/vc.el (vc-move-working-tree): Check entries in vc-dir-buffers for liveness. Don't hardcode "*vc-dir*"; instead call uniquify-buffer-base-name. Call uniquify-rename-buffer, not rename-buffer, so that the buffer is not marked unmanaged. --- lisp/vc/vc.el | 43 +++++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/lisp/vc/vc.el b/lisp/vc/vc.el index 2219739fb55..9383788a0c3 100644 --- a/lisp/vc/vc.el +++ b/lisp/vc/vc.el @@ -4489,26 +4489,29 @@ BACKEND is the VC backend." (let ((from (expand-file-name from))) (dired-rename-subdir from (expand-file-name to)) (dolist (buf vc-dir-buffers) - (with-current-buffer buf - (when (string-prefix-p from default-directory) - (setq default-directory - (expand-file-name (file-relative-name default-directory from) - to)) - ;; Obtain an appropriately uniquify'd name for a *vc-dir* - ;; buffer in the new working tree. In particular if this - ;; *vc-dir* buffer already has a uniquify'd name appropriate - ;; for the old working tree, we must replace that. - ;; See also `vc-dir-prepare-status-buffer'. - ;; FIXME: There should be a way to get this information - ;; without creating and killing a buffer. - (let (name) - (unwind-protect - (setq name (buffer-name - (create-file-buffer - (expand-file-name "*vc-dir*" - default-directory)))) - (kill-buffer name)) - (rename-buffer name)))))) + (when (buffer-live-p buf) + (with-current-buffer buf + (when (string-prefix-p from default-directory) + (setq default-directory + (expand-file-name (file-relative-name default-directory from) + to)) + ;; If the *vc-dir* buffer has a uniquify'd name then we need + ;; to obtain an new uniquify'd name for this buffer under + ;; the new working tree, replacing the one for the old + ;; working tree. See also `vc-dir-prepare-status-buffer'. + (when-let* ((base-name (uniquify-buffer-base-name)) + (item (cl-find (current-buffer) uniquify-managed + :key #'uniquify-item-buffer))) + (let (name) + ;; FIXME: There should be a way to get this information + ;; without creating and killing a buffer. + (unwind-protect + (setq name (buffer-name + (create-file-buffer + (expand-file-name base-name + default-directory)))) + (kill-buffer name)) + (uniquify-rename-buffer item name)))))))) (when-let* ((p (project-current nil to))) (project-remember-project p))) From bd1362b686c7dd9083322a587b7738d0da12395c Mon Sep 17 00:00:00 2001 From: mattiasdrp Date: Fri, 8 Aug 2025 15:16:24 +0200 Subject: [PATCH 073/216] Autoinsert: Allow condition to be a function Currently a condition can only be a regexp or a major-mode symbol but there's no real reason to not allow any predicate as a condition. * doc/misc/autotype.texi: Document the new condition type in 'auto-insert-alist'. * etc/NEWS: Announce the new condition type in 'auto-insert-alist'. * lisp/autoinsert.el (auto-insert-alist): Add the new condition type to the variable documentation. (auto-insert): Change the way the condition is matched to allow for custom predicates. * test/lisp/autoinsert-tests.el (autoinsert-tests-auto-insert-lambda/-nil): Add two tests for lambdas (nil and t cases) (autoinsert-tests-auto-insert-predicate/-nil): Add two tests for named predicates (nil and t cases) (Bug#79178) --- doc/misc/autotype.texi | 16 +++++++++------- etc/NEWS | 9 +++++++++ lisp/autoinsert.el | 25 ++++++++++++++++++++----- test/lisp/autoinsert-tests.el | 34 ++++++++++++++++++++++++++++++++++ 4 files changed, 72 insertions(+), 12 deletions(-) diff --git a/doc/misc/autotype.texi b/doc/misc/autotype.texi index c53be54d0af..377dcda3efd 100644 --- a/doc/misc/autotype.texi +++ b/doc/misc/autotype.texi @@ -273,13 +273,15 @@ empty file is visited. This is accomplished by putting @vindex auto-insert-alist What gets inserted, if anything, is determined by the variable -@code{auto-insert-alist}. The @sc{car} of each element of this list -is either a mode name, making the element applicable when a buffer is -in that mode, or a string, which is a regexp matched against a -buffer's file name (the latter enables you to distinguish between -different kinds of files that have the same mode in Emacs). The -@sc{car} of an element may also be a cons cell, consisting of mode -name or regexp, as above, and an additional descriptive string. +@code{auto-insert-alist}. The @sc{car} of each element of this list is +either a mode name, making the element applicable when a buffer is in +that mode, or a string, which is a regexp matched against a buffer's +file name (the latter enables you to distinguish between different kinds +of files that have the same mode in Emacs). It can also be a predicate +declared through a plist of the form @code{(predicate @var{function})}. +@var{function} should be a predicate function of no arguments. The +@sc{car} of an element may also be a cons cell, consisting of mode name +or regexp, as above, and an additional descriptive string. When a matching element is found, the @sc{cdr} says what to do. It may be a string, which is a file name, whose contents are to be inserted, if diff --git a/etc/NEWS b/etc/NEWS index 13d1d1cba20..a2dd1c7299d 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -700,6 +700,15 @@ you could already use 'C-u C-x C-n' to clear the goal column. * Changes in Specialized Modes and Packages in Emacs 31.1 +** Autoinsert + ++++ +*** New condition for 'auto-insert-alist'. +'auto-insert-alist' now also allows to have a predicate taking no +argument as conditions. These types of conditions should be declared +with '(predicate FUNCTION)'. This allows to trigger 'auto-insert' +with finer grained control. + ** Register *** The "*Register Preview*" buffer shows only suitable registers. diff --git a/lisp/autoinsert.el b/lisp/autoinsert.el index e7492d1b9ed..a09689d65b9 100644 --- a/lisp/autoinsert.el +++ b/lisp/autoinsert.el @@ -325,6 +325,9 @@ The document was typeset with Elements look like (CONDITION . ACTION) or ((CONDITION . DESCRIPTION) . ACTION). CONDITION may be a regexp that must match the new file's name, or it may be a symbol that must match the major mode for this element to apply. +CONDITION can also be a custom predicate of no arguments declared with +'(predicate FUNCTION)'. Emacs will insert the text if the predicate +function returns non-nil. Only the first matching element is effective. Optional DESCRIPTION is a string for filling `auto-insert-prompt'. ACTION may be a skeleton to insert (see `skeleton-insert'), an absolute @@ -368,12 +371,24 @@ Matches the visited file name against the elements of `auto-insert-alist'." (pcase-lambda (`(,cond . ,action)) (if (atom cond) (setq desc cond) - (setq desc (cdr cond) - cond (car cond))) - (when (if (symbolp cond) - (derived-mode-p cond) + ;; if `cond' is a predicate, don't split it but set `desc' to a custom string + (if (and (consp cond) (equal (car cond) 'predicate)) + (setq desc "predicate") + (setq desc (cdr cond) + cond (car cond)))) + (when (cond + ;; `cond' should be a major-mode variable + ((symbolp cond) + (derived-mode-p cond)) + + ;; `cond' should be a predicate that takes no argument + ((and (consp cond) (equal (car cond) 'predicate)) + (funcall (cadr cond))) + + ;; cond should be a regexp + (t (and buffer-file-name - (string-match cond buffer-file-name))) + (string-match cond buffer-file-name)))) action)) auto-insert-alist))) (goto-char 1) diff --git a/test/lisp/autoinsert-tests.el b/test/lisp/autoinsert-tests.el index 5c97f0c2b33..5791ffd5c53 100644 --- a/test/lisp/autoinsert-tests.el +++ b/test/lisp/autoinsert-tests.el @@ -76,6 +76,40 @@ (auto-insert) (should (equal (buffer-string) "2nd"))))) +(ert-deftest autoinsert-tests-auto-insert-lambda () + (let ((auto-insert-alist + '(((predicate (lambda () t)) . (lambda () (insert "foo"))))) + (auto-insert-query nil)) + (with-temp-buffer + (auto-insert) + (should (equal (buffer-string) "foo"))))) + +(ert-deftest autoinsert-tests-auto-insert-predicate () + (defun predicate () t) + (let ((auto-insert-alist + '(((predicate predicate) . (lambda () (insert "foo"))))) + (auto-insert-query nil)) + (with-temp-buffer + (auto-insert) + (should (equal (buffer-string) "foo"))))) + +(ert-deftest autoinsert-tests-auto-insert-lambda-nil () + (let ((auto-insert-alist + '(((predicate (lambda () nil)) . (lambda () (insert "foo"))))) + (auto-insert-query nil)) + (with-temp-buffer + (auto-insert) + (should (equal (buffer-string) ""))))) + +(ert-deftest autoinsert-tests-auto-insert-predicate-nil () + (defun predicate () nil) + (let ((auto-insert-alist + '(((predicate predicate) . (lambda () (insert "foo"))))) + (auto-insert-query nil)) + (with-temp-buffer + (auto-insert) + (should (equal (buffer-string) ""))))) + (ert-deftest autoinsert-tests-define-auto-insert-before () (let ((auto-insert-alist (list (cons 'text-mode (lambda () (insert "foo"))))) From 477e44fca38dbdf6dd17f24ca3c61544b1640605 Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Sat, 9 Aug 2025 13:38:10 +0300 Subject: [PATCH 074/216] ; * lisp/autoinsert.el (auto-insert-alist): Fix last change. --- lisp/autoinsert.el | 10 +++++----- test/lisp/autorevert-tests.el | 3 ++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/lisp/autoinsert.el b/lisp/autoinsert.el index a09689d65b9..d5ccb7d1e0e 100644 --- a/lisp/autoinsert.el +++ b/lisp/autoinsert.el @@ -323,11 +323,11 @@ The document was typeset with ")) "A list specifying text to insert by default into a new file. Elements look like (CONDITION . ACTION) or ((CONDITION . DESCRIPTION) . ACTION). -CONDITION may be a regexp that must match the new file's name, or it may be -a symbol that must match the major mode for this element to apply. -CONDITION can also be a custom predicate of no arguments declared with -'(predicate FUNCTION)'. Emacs will insert the text if the predicate -function returns non-nil. +CONDITION may be a regexp that must match the new file's name, or it +may be a symbol that must match the major mode for this element to apply. +CONDITION can also be a custom predicate of no arguments declared +with (predicate FUNCTION). Emacs will insert the text if the +predicate function returns non-nil. Only the first matching element is effective. Optional DESCRIPTION is a string for filling `auto-insert-prompt'. ACTION may be a skeleton to insert (see `skeleton-insert'), an absolute diff --git a/test/lisp/autorevert-tests.el b/test/lisp/autorevert-tests.el index 20b67628223..ca38084b2b5 100644 --- a/test/lisp/autorevert-tests.el +++ b/test/lisp/autorevert-tests.el @@ -219,7 +219,8 @@ It is checked for buffer-local `auto-revert-notify-watch-descriptor'." ,@body) (customize-set-variable 'auto-revert-interval auto-revert-interval-orig) (setq auto-revert--lockout-interval auto-revert--lockout-interval-orig) - (file-notify-rm-all-watches)))) + (ignore-errors (file-notify-rm-all-watches)) + (sleep-for 1)))) (defun auto-revert-test--write-region (string file &optional append) "Write STRING to FILE." From e4352c7a4542bb04f0491f5622f215bd9a3ccb09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?El=C3=ADas=20Gabriel=20P=C3=A9rez?= Date: Mon, 4 Aug 2025 12:47:18 -0600 Subject: [PATCH 075/216] Add multi-character pairs to lua-ts-mode and texinfo-mode * etc/NEWS: Add entry for lua-ts-mode changes. * lisp/progmodes/lua-ts-mode.el (lua-ts-auto-close-block-comments): New user option. (lua-ts-mode): Add '--[[ ]]' pairs to 'electric-pair-pairs' only if 'lua-ts-auto-close-block-comments' is non-nil. * lisp/textmodes/texinfo.el (texinfo-mode): Add "`` ''" pairs to 'electric-pair-pairs'. * test/lisp/progmodes/lua-ts-mode-tests.el (lua-ts-test-auto-close-block-comments): Add new test. (Bug#79047) --- etc/NEWS | 15 +++++++++++ lisp/progmodes/lua-ts-mode.el | 13 ++++++++++ lisp/textmodes/texinfo.el | 9 +++++++ test/lisp/progmodes/lua-ts-mode-tests.el | 33 ++++++++++++++++++++++++ 4 files changed, 70 insertions(+) diff --git a/etc/NEWS b/etc/NEWS index a2dd1c7299d..7694a4ec9fa 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -908,6 +908,13 @@ build tags for the test commands. The 'go-ts-mode-test-flags' user option is available to set a list of additional flags to pass to the go test command line. +** Lua-ts mode + +--- +*** New user option 'lua-ts-auto-close-block-comments'. +When non-nil, inserting a block comment "--[[" will close it by +inserting its respective "]]". By default, this is disabled. + ** Java-ts mode +++ @@ -959,6 +966,14 @@ characters in CJK texts. For example, 'A' is converted to 'A', '1' is converted to '1', etc. Companion commands 'halfwidth-region' and 'halfwidth-word' perform the opposite conversion. +** Texinfo Mode + +--- +*** texinfo-mode now can auto-close the ``'' pairs. +Now inserting `` in 'texinfo-mode' will close it by inserting its +respective ''. + + ** ASM mode --- diff --git a/lisp/progmodes/lua-ts-mode.el b/lisp/progmodes/lua-ts-mode.el index 5e58d4c071d..3df36e329b3 100644 --- a/lisp/progmodes/lua-ts-mode.el +++ b/lisp/progmodes/lua-ts-mode.el @@ -74,6 +74,11 @@ :safe 'natnump :version "30.1") +(defcustom lua-ts-auto-close-block-comments nil + "If non-nil, inserting a block comment \"--[[\" will insert its respective \"]]\"." + :type 'boolean + :version "31.1") + (defcustom lua-ts-luacheck-program "luacheck" "Location of the Luacheck program." :type 'file @@ -675,6 +680,14 @@ Calls REPORT-FN directly." (setq-local comment-start-skip "--\\s-*") (setq-local comment-end "") + ;; Pairs. + (when (and lua-ts-auto-close-block-comments + (boundp 'electric-pair-pairs)) + (setq-local electric-pair-pairs + (cons + '("--\\[\\[" . "\n]") + electric-pair-pairs))) + ;; Font-lock. (setq-local treesit-font-lock-settings lua-ts--font-lock-settings) (setq-local treesit-font-lock-feature-list diff --git a/lisp/textmodes/texinfo.el b/lisp/textmodes/texinfo.el index 58ab5f16806..e14c1669ae7 100644 --- a/lisp/textmodes/texinfo.el +++ b/lisp/textmodes/texinfo.el @@ -505,6 +505,15 @@ value of `texinfo-mode-hook'." (setq-local syntax-propertize-function texinfo-syntax-propertize-function) (setq-local add-log-current-defun-function #'texinfo-current-defun-name) + ;; Pairs settings + (when (boundp 'electric-pair-pairs) + (setq-local electric-pair-pairs + (cons + ;; `` '' pairs + '("\\(?:^\\|[[:space:]]\\)``" + . "''") + electric-pair-pairs))) + ;; Outline settings. (setq-local outline-heading-alist ;; We should merge `outline-heading-alist' and diff --git a/test/lisp/progmodes/lua-ts-mode-tests.el b/test/lisp/progmodes/lua-ts-mode-tests.el index b14e9518451..b3267367e90 100644 --- a/test/lisp/progmodes/lua-ts-mode-tests.el +++ b/test/lisp/progmodes/lua-ts-mode-tests.el @@ -25,6 +25,7 @@ (require 'hideshow) (require 'treesit) (require 'which-func) +(require 'elec-pair) (ert-deftest lua-ts-test-indentation () (skip-unless (treesit-ready-p 'lua t)) @@ -61,6 +62,38 @@ (should (= 0 (length (overlays-in (point-min) (point-max))))) (hs-minor-mode -1))) +;; from: electric-tests.el +(defun call-with-saved-electric-modes (fn) + (let ((saved-electric (if electric-pair-mode 1 -1)) + (saved-layout (if electric-layout-mode 1 -1)) + (saved-indent (if electric-indent-mode 1 -1)) + (blink-paren-function nil)) + (electric-pair-mode -1) + (electric-layout-mode -1) + (electric-indent-mode -1) + (unwind-protect + (funcall fn) + (electric-pair-mode saved-electric) + (electric-indent-mode saved-indent) + (electric-layout-mode saved-layout)))) + +;; from: electric-tests.el +(defmacro save-electric-modes (&rest body) + (declare (indent defun) (debug t)) + `(call-with-saved-electric-modes (lambda () ,@body))) + +(ert-deftest lua-ts-test-auto-close-block-comments () + (save-electric-modes + (with-temp-buffer + (dlet ((lua-ts-auto-close-block-comments t)) + (electric-pair-mode 1) + (lua-ts-mode) + (insert "--") + (let ((last-command-event ?\[)) + (ert-simulate-command '(self-insert-command 1)) + (ert-simulate-command '(self-insert-command 1))) + (should (equal "--[[\n]]" (buffer-string))))))) + (provide 'lua-ts-mode-tests) ;;; lua-ts-mode-tests.el ends here From 5c8234e3e755141ede16adbd76ad74f5b673bfd3 Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Sat, 9 Aug 2025 13:43:38 +0300 Subject: [PATCH 076/216] ; * etc/NEWS: Fix last change (bug#79047). --- etc/NEWS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etc/NEWS b/etc/NEWS index 7694a4ec9fa..1600e1ce943 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -971,7 +971,7 @@ converted to '1', etc. Companion commands 'halfwidth-region' and --- *** texinfo-mode now can auto-close the ``'' pairs. Now inserting `` in 'texinfo-mode' will close it by inserting its -respective ''. +respective '', if 'electric-pair-mode' is enabled. ** ASM mode From 1fc4bb1fead205e425f69fa803a71f4e446d48a8 Mon Sep 17 00:00:00 2001 From: Michael Albinus Date: Sat, 9 Aug 2025 14:21:23 +0200 Subject: [PATCH 077/216] Rename variables *-in-progress-p to *-in-progress * doc/lispref/backups.texi (Reverting): Fix variable names revert-buffer-in-progress and auto-revert-buffer-in-progress. * etc/NEWS: Fix variable names revert-buffer-in-progress and auto-revert-buffer-in-progress. Presentational fixes and improvements. * lisp/autorevert.el (auto-revert-buffer-in-progress): Rename. (auto-revert-handler, auto-revert-buffer): Use renamed variables. * lisp/dired-x.el (dired-omit-expunge): Use `auto-revert-buffer-in-progress'. * lisp/files.el (revert-buffer-in-progress): Rename. (revert-buffer-in-progress-p): Declare obsolete. (after-find-file, revert-buffer): * lisp/saveplace.el (save-place-find-file-hook) (save-place-dired-hook): * lisp/vc/vc-git.el (vc-git-command, vc-git--out-ok): * lisp/vc/vc.el (vc-diff-internal): Use `revert-buffer-in-progress'. * lisp/net/tramp-sh.el (tramp-sh-handle-vc-registered): Suppress warning. --- doc/lispref/backups.texi | 4 ++-- etc/NEWS | 14 ++++++++++++-- lisp/autorevert.el | 6 +++--- lisp/dired-x.el | 2 +- lisp/files.el | 11 ++++++----- lisp/net/tramp-sh.el | 4 +++- lisp/saveplace.el | 4 ++-- lisp/vc/vc-git.el | 4 ++-- lisp/vc/vc.el | 2 +- 9 files changed, 32 insertions(+), 19 deletions(-) diff --git a/doc/lispref/backups.texi b/doc/lispref/backups.texi index a1df26230aa..9e262346edb 100644 --- a/doc/lispref/backups.texi +++ b/doc/lispref/backups.texi @@ -719,7 +719,7 @@ preserved, but this is up to the specific @code{revert-buffer-function} implementation. @end deffn -@defvar revert-buffer-in-progress-p +@defvar revert-buffer-in-progress @code{revert-buffer} binds this variable to a non-@code{nil} value while it is working. @end defvar @@ -852,7 +852,7 @@ It is important to assure that point does not continuously jump around as a consequence of auto-reverting. Of course, moving point might be inevitable if the buffer radically changes. -@defvar auto-revert-buffer-in-progress-p +@defvar auto-revert-buffer-in-progress @code{auto-revert-buffer} binds this variable to a non-@code{nil} value while it is working. This can be used by major mode @code{revert-buffer-function} implementations to suppress messages in diff --git a/etc/NEWS b/etc/NEWS index 1600e1ce943..f4fdf3d4eec 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -966,7 +966,7 @@ characters in CJK texts. For example, 'A' is converted to 'A', '1' is converted to '1', etc. Companion commands 'halfwidth-region' and 'halfwidth-word' perform the opposite conversion. -** Texinfo Mode +** Texinfo mode --- *** texinfo-mode now can auto-close the ``'' pairs. @@ -2212,6 +2212,16 @@ destination window is chosen using 'display-buffer-alist'. Example: display-buffer-use-some-window) (some-window . mru)))) +** Revert + ++++ +*** Variable 'revert-buffer-in-progress' has been renamed. +The old name, 'revert-buffer-in-progress-p', is kept as obsolete +variable alias. + +Symbol names with a trailing '-p' are reserved for predicates. Calling +a variable like this was a mistake. + ** Autorevert +++ @@ -2226,7 +2236,7 @@ runs its body, and removes the current buffer from 'inhibit-auto-revert-buffers' afterwards. +++ -*** New variable 'auto-revert-buffer-in-progress-p'. +*** New variable 'auto-revert-buffer-in-progress'. 'auto-revert-buffer' binds this variable to a non-nil value while it is working. This can be used by major mode 'revert-buffer-function' implementations to suppress messages in Auto Revert modes, for example. diff --git a/lisp/autorevert.el b/lisp/autorevert.el index 63c7d2928c7..ccedaa759cb 100644 --- a/lisp/autorevert.el +++ b/lisp/autorevert.el @@ -330,7 +330,7 @@ seconds, in addition to using notification for those files." ;; Internal variables: ;;;###autoload -(defvar auto-revert-buffer-in-progress-p nil "\ +(defvar auto-revert-buffer-in-progress nil "\ Non-nil if a `auto-revert-buffer' operation is in progress, nil otherwise.") (defvar auto-revert-buffer-list () @@ -875,7 +875,7 @@ This is an internal function used by Auto-Revert Mode." ;; `preserve-modes' avoids changing the (minor) modes. But we do ;; want to reset the mode for VC, so we do it manually. (when (and (not auto-revert-tail-mode) (or revert auto-revert-check-vc-info)) - (let ((revert-buffer-in-progress-p t)) + (let ((revert-buffer-in-progress t)) (vc-refresh-state))))) (defun auto-revert-tail-handler (size) @@ -936,7 +936,7 @@ buffers not reverted last time due to user interruption." This is performed as specified by Auto-Revert and Global Auto-Revert Modes." - (let ((auto-revert-buffer-in-progress-p t)) + (let ((auto-revert-buffer-in-progress t)) (if (not (buffer-live-p buf)) (auto-revert-remove-current-buffer buf) (with-current-buffer buf diff --git a/lisp/dired-x.el b/lisp/dired-x.el index 016b97d34a2..ad602b00a3e 100644 --- a/lisp/dired-x.el +++ b/lisp/dired-x.el @@ -477,7 +477,7 @@ status message." (old-modified-p (buffer-modified-p)) (count (or init-count 0)) (dired-omit-verbose - (and dired-omit-verbose (not auto-revert-buffer-in-progress-p)))) + (and dired-omit-verbose (not auto-revert-buffer-in-progress)))) (unless (string= omit-re "") (let ((dired-marker-char dired-omit-marker-char)) (when dired-omit-verbose (message "Omitting...")) diff --git a/lisp/files.el b/lisp/files.el index 4567f91bc8e..5bcfa24e930 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -2887,7 +2887,7 @@ error in reading the file. WARN non-nil means warn if there exists an auto-save file more recent than the visited file. NOAUTO means don't mess with auto-save mode. Fourth arg AFTER-FIND-FILE-FROM-REVERT-BUFFER is ignored -\(see `revert-buffer-in-progress-p' for similar functionality). +\(see `revert-buffer-in-progress' for similar functionality). Fifth arg NOMODES non-nil means don't alter the file's modes. Finishes by calling the functions in `find-file-hook' unless NOMODES is non-nil." @@ -7107,9 +7107,10 @@ hook functions. The function `revert-buffer--default' runs this. A customized `revert-buffer-function' need not run this hook.") -(defvar revert-buffer-in-progress-p nil +(defvar revert-buffer-in-progress nil "Non-nil if a `revert-buffer' operation is in progress, nil otherwise.") - +(define-obsolete-variable-alias + 'revert-buffer-in-progress-p 'revert-buffer-in-progress "31.1") (defvar revert-buffer-internal-hook) ;; `revert-buffer-function' was defined long ago to be a function of only @@ -7168,7 +7169,7 @@ revert buffers without querying for confirmation.) Optional third argument PRESERVE-MODES non-nil means don't alter the files modes. Normally we reinitialize them using `normal-mode'. -This function binds `revert-buffer-in-progress-p' non-nil while it operates. +This function binds `revert-buffer-in-progress' non-nil while it operates. This function calls the function that `revert-buffer-function' specifies to do the work, with arguments IGNORE-AUTO and NOCONFIRM. @@ -7189,7 +7190,7 @@ preserve markers and overlays, at the price of being slower." ;; reversal of the argument sense. So I'm just changing the user ;; interface, but leaving the programmatic interface the same. (interactive (list (not current-prefix-arg))) - (let ((revert-buffer-in-progress-p t) + (let ((revert-buffer-in-progress t) (revert-buffer-preserve-modes preserve-modes) restore-functions) (run-hook-wrapped 'revert-buffer-restore-functions diff --git a/lisp/net/tramp-sh.el b/lisp/net/tramp-sh.el index 576f09b764b..51633f686eb 100644 --- a/lisp/net/tramp-sh.el +++ b/lisp/net/tramp-sh.el @@ -3669,6 +3669,8 @@ are \"file-exists-p\", \"file-readable-p\", \"file-directory-p\" and (defun tramp-sh-handle-vc-registered (file) "Like `vc-registered' for Tramp files." (when vc-handled-backends + ;; Starting with Emacs 31, use `revert-buffer-in-progress'. + (with-suppressed-warnings ((obsolete revert-buffer-in-progress-p)) (let ((inhibit-message (or revert-buffer-in-progress-p inhibit-message)) (temp-message (unless revert-buffer-in-progress-p ""))) (with-temp-message temp-message @@ -3728,7 +3730,7 @@ are \"file-exists-p\", \"file-readable-p\", \"file-directory-p\" and ;; Run. (tramp-with-demoted-errors v "Error in 2nd pass of `vc-registered': %s" - (tramp-run-real-handler #'vc-registered (list file)))))))))) + (tramp-run-real-handler #'vc-registered (list file))))))))))) ;;;###tramp-autoload (defun tramp-sh-file-name-handler (operation &rest args) diff --git a/lisp/saveplace.el b/lisp/saveplace.el index 8b8d9a445d7..1f36196408a 100644 --- a/lisp/saveplace.el +++ b/lisp/saveplace.el @@ -444,7 +444,7 @@ It runs the hook `save-place-after-find-file-hook'." save-place-alist)))) (if cell (progn - (or revert-buffer-in-progress-p + (or revert-buffer-in-progress (and (integerp (cdr cell)) (goto-char (cdr cell)))) ;; and make sure it will be saved again for later @@ -467,7 +467,7 @@ This is run via `dired-initial-position-hook', which see." (cell (assoc (if save-place-abbreviate-file-names (abbreviate-file-name item) item) save-place-alist))) - (or revert-buffer-in-progress-p + (or revert-buffer-in-progress (cond ((integerp (cdr cell)) (goto-char (cdr cell))) diff --git a/lisp/vc/vc-git.el b/lisp/vc/vc-git.el index 1f5ac7bd289..ab47ef6dadd 100644 --- a/lisp/vc/vc-git.el +++ b/lisp/vc/vc-git.el @@ -2488,7 +2488,7 @@ The difference to `vc-do-command' is that this function always invokes '("GIT_LITERAL_PATHSPECS=1")) ;; Avoid repository locking during background operations ;; (bug#21559). - ,@(when revert-buffer-in-progress-p + ,@(when revert-buffer-in-progress '("GIT_OPTIONAL_LOCKS=0"))) process-environment))) (apply #'vc-do-command (or buffer "*vc*") okstatus vc-git-program @@ -2527,7 +2527,7 @@ The difference to `vc-do-command' is that this function always invokes '("GIT_LITERAL_PATHSPECS=1")) ;; Avoid repository locking during background operations ;; (bug#21559). - ,@(when revert-buffer-in-progress-p + ,@(when revert-buffer-in-progress '("GIT_OPTIONAL_LOCKS=0"))) process-environment))) (apply #'process-file vc-git-program nil buffer nil "--no-pager" command args))) diff --git a/lisp/vc/vc.el b/lisp/vc/vc.el index 9383788a0c3..74f25403017 100644 --- a/lisp/vc/vc.el +++ b/lisp/vc/vc.el @@ -2286,7 +2286,7 @@ Return t if the buffer had changes, nil otherwise." (if files (vc-coding-system-for-diff (car files)) 'undecided) 'unix)) (orig-diff-buffer-clone - (if revert-buffer-in-progress-p + (if revert-buffer-in-progress (clone-buffer (generate-new-buffer-name " *vc-diff-clone*") nil)))) ;; On MS-Windows and MS-DOS, Diff is likely to produce DOS-style From 4c9ac95cb05e80142b2ab251061d67ac39dac7df Mon Sep 17 00:00:00 2001 From: Manuel Giraud Date: Tue, 22 Jul 2025 15:15:39 +0200 Subject: [PATCH 078/216] Variable to control overshoot and backup for TTY cursor motion * src/term.c (syms_of_term): New variable. * src/cm.c (calccost): Control overshooting with new variable. * etc/NEWS: Document new variable. * lisp/term/rxvt.el (terminal-init-rxvt): Force no TAB+BS for rxvt. (Bug#78474) --- etc/NEWS | 8 ++++++++ lisp/term/rxvt.el | 3 +++ src/cm.c | 45 ++++++++++++++++++++++++++------------------- src/term.c | 9 +++++++++ 4 files changed, 46 insertions(+), 19 deletions(-) diff --git a/etc/NEWS b/etc/NEWS index f4fdf3d4eec..d6bf37f3eb5 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -2674,6 +2674,14 @@ That is, if the PATH environment variable is unset or empty, 'exec-path' now acts as if PATH is the system default, which is "/bin:/usr/bin" on GNU/Linux systems. +--- +** New variable 'tty-cursor-movement-use-TAB-BS'. +The display optimization where the combination TAB characters + +BACKSPACE is used to move to a position on a TTY frame is now disabled +by default and controlled by this variable; it can be set to non-nil +to keep the old behavior. This change is to accomodate screen +readers. + * Lisp Changes in Emacs 31.1 diff --git a/lisp/term/rxvt.el b/lisp/term/rxvt.el index 1e431102d58..9de58b25e6b 100644 --- a/lisp/term/rxvt.el +++ b/lisp/term/rxvt.el @@ -190,6 +190,9 @@ (when rxvt-set-window-title (xterm--init-frame-title)) + ;; Do not use TAB+BS optimization. + (setq tty-cursor-movement-use-TAB-BS nil) + (run-hooks 'terminal-init-rxvt-hook)) ;; rxvt puts the default colors into an environment variable diff --git a/src/cm.c b/src/cm.c index 150d1c9a580..d52ac22f27a 100644 --- a/src/cm.c +++ b/src/cm.c @@ -187,9 +187,9 @@ calccost (struct tty_display_info *tty, c, totalcost; int ntabs, - n2tabs, - tabx, - tab2x, + n2tabs, + tabx, + tab2x, tabcost; register const char *p; @@ -229,36 +229,43 @@ x: goto olddelta; /* forget it! */ /* - * ntabs is # tabs towards but not past dstx; n2tabs is one more - * (ie past dstx), but this is only valid if that is not past the - * right edge of the screen. We can check that at the same time - * as we figure out where we would be if we use the tabs (which - * we will put into tabx (for ntabs) and tab2x (for n2tabs)). + * ntabs is # tabs towards but not past dstx. tabx is where we + * would be if we put those ntabs tabulations. */ ntabs = (deltax + srcx % tty->Wcm->cm_tabwidth) / tty->Wcm->cm_tabwidth; - n2tabs = ntabs + 1; tabx = (srcx / tty->Wcm->cm_tabwidth + ntabs) * tty->Wcm->cm_tabwidth; - tab2x = tabx + tty->Wcm->cm_tabwidth; - if (tab2x >= tty->Wcm->cm_cols) /* too far (past edge) */ + if (tty_cursor_movement_use_TAB_BS) { + /* + * n2tabs is one more tab than ntabs (i.e. past dstx), but this is + * only valid if that is not past the right edge of the screen. + * tab2x is where we would be if we put those n2tabs tabulations. + */ + n2tabs = ntabs + 1; + tab2x = tabx + tty->Wcm->cm_tabwidth; + if (tab2x >= tty->Wcm->cm_cols) /* too far (past edge) */ n2tabs = 0; + /* + * Set c to the cost for using n2tabs + */ + /* cost for n2tabs + cost for left motion */ + c = n2tabs ? n2tabs * tty->Wcm->cc_tab + (tab2x - dstx) * tty->Wcm->cc_left + : BIG; + } + /* - * Now set tabcost to the cost for using ntabs, and c to the cost - * for using n2tabs, then pick the minimum. + * Set tabcost to the cost for using ntabs. */ /* cost for ntabs + cost for right motion */ tabcost = ntabs ? ntabs * tty->Wcm->cc_tab + (dstx - tabx) * tty->Wcm->cc_right : BIG; - /* cost for n2tabs + cost for left motion */ - c = n2tabs ? n2tabs * tty->Wcm->cc_tab + (tab2x - dstx) * tty->Wcm->cc_left - : BIG; - - if (c < tabcost) /* then cheaper to overshoot & back up */ - ntabs = n2tabs, tabcost = c, tabx = tab2x; + /* Is it allowed and cheaper to overshoot & back up? */ + if (tty_cursor_movement_use_TAB_BS && (c < tabcost)) + ntabs = n2tabs, tabcost = c, tabx = tab2x; if (tabcost >= BIG) /* caint use tabs */ goto newdelta; diff --git a/src/term.c b/src/term.c index 8aa47322d19..a1e3f6312c6 100644 --- a/src/term.c +++ b/src/term.c @@ -5167,6 +5167,15 @@ This should be set if the function in `mouse-position-function' does not trigger redisplay. */); tty_menu_calls_mouse_position_function = 0; + DEFVAR_BOOL ("tty-cursor-movement-use-TAB-BS", tty_cursor_movement_use_TAB_BS, + doc: /* Whether TTY frames may use the combination TAB + BACKSPACE for moving around. +On TTY frames, as a display optimization, Emacs may move to a position +by "overshooting" with TAB characters and one BACKSPACE character, when +this is more efficient. This combination can interfere with the +functioning of some software, such as screen readers. Set this to +non-nil to enable this optimization. */); + tty_cursor_movement_use_TAB_BS = 0; + defsubr (&Stty_display_color_p); defsubr (&Stty_display_color_cells); defsubr (&Stty_no_underline); From 723ed76a9cf372c7cf49582f5adac9e91474f21f Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Sat, 9 Aug 2025 15:29:18 +0300 Subject: [PATCH 079/216] ; * lisp/term/rxvt.el (terminal-init-rxvt): Improve commentary. --- lisp/term/rxvt.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lisp/term/rxvt.el b/lisp/term/rxvt.el index 9de58b25e6b..5be8913156e 100644 --- a/lisp/term/rxvt.el +++ b/lisp/term/rxvt.el @@ -190,7 +190,7 @@ (when rxvt-set-window-title (xterm--init-frame-title)) - ;; Do not use TAB+BS optimization. + ;; Do not use TAB+BS optimization, as that might produce buggy display. (setq tty-cursor-movement-use-TAB-BS nil) (run-hooks 'terminal-init-rxvt-hook)) From 581447cd25e6cc6f4c05323027f9f2934821ca43 Mon Sep 17 00:00:00 2001 From: Zach Shaftel Date: Sat, 2 Aug 2025 23:49:23 -0400 Subject: [PATCH 080/216] Improve Vprint_variable_mapping * src/print.c (print_create_variable_mapping): Fix a typo, `unreadeable-function' to `unreadable-function'. Add `symbols-bare' as an override for `print-symbols-bare'. (Bug#79161) --- doc/lispref/streams.texi | 4 +++- src/print.c | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/doc/lispref/streams.texi b/doc/lispref/streams.texi index 3fd8f038bae..f049a2d23b1 100644 --- a/doc/lispref/streams.texi +++ b/doc/lispref/streams.texi @@ -1054,7 +1054,7 @@ This overrides @code{print-escape-nonascii}. This overrides @code{print-escape-multibyte}. @item charset-text-property This overrides @code{print-charset-text-property}. -@item unreadeable-function +@item unreadable-function This overrides @code{print-unreadable-function}. @item gensym This overrides @code{print-gensym}. @@ -1066,6 +1066,8 @@ This overrides @code{print-number-table}. This overrides @code{float-output-format}. @item integers-as-characters This overrides @code{print-integers-as-characters}. +@item symbols-bare +This overrides @code{print-symbols-bare}. @end table In the future, more overrides may be offered that do not map directly diff --git a/src/print.c b/src/print.c index fa800743497..29edfaad531 100644 --- a/src/print.c +++ b/src/print.c @@ -682,7 +682,7 @@ print_create_variable_mapping (void) intern ("print-escape-multibyte"), Qnil), list3 (intern ("charset-text-property"), intern ("print-charset-text-property"), Qnil), - list3 (intern ("unreadeable-function"), + list3 (intern ("unreadable-function"), intern ("print-unreadable-function"), Qnil), list3 (intern ("gensym"), intern ("print-gensym"), Qnil), list3 (intern ("continuous-numbering"), @@ -691,6 +691,7 @@ print_create_variable_mapping (void) list3 (intern ("float-format"), intern ("float-output-format"), Qnil), list3 (intern ("integers-as-characters"), intern ("print-integers-as-characters"), Qnil), + list3 (intern ("symbols-bare"), intern ("print-symbols-bare"), Qnil), }; Vprint_variable_mapping = CALLMANY (Flist, total); From 9748c13208f16d6737452af0d65745c9d7898fc6 Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Sat, 9 Aug 2025 15:38:13 +0300 Subject: [PATCH 081/216] ; Fix last change * src/print.c (print_create_variable_mapping): Add back the wrong spelling as compatibility measure (bug#75784). --- src/print.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/print.c b/src/print.c index 29edfaad531..55d8701ea0f 100644 --- a/src/print.c +++ b/src/print.c @@ -684,6 +684,8 @@ print_create_variable_mapping (void) intern ("print-charset-text-property"), Qnil), list3 (intern ("unreadable-function"), intern ("print-unreadable-function"), Qnil), + list3 (intern ("unreadeable-function"), + intern ("print-unreadable-function"), Qnil), list3 (intern ("gensym"), intern ("print-gensym"), Qnil), list3 (intern ("continuous-numbering"), intern ("print-continuous-numbering"), Qnil), From ae56edbbf8fa49ffec2aa8983970f1665450670d Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Sat, 9 Aug 2025 15:42:26 +0300 Subject: [PATCH 082/216] Revert "Stop subprocesses from using inherited or default PAGER" This reverts commit 7811a7d38bb7cb303dc66efa02eb95e75a03f39d. Users set PAGER and don't expect Emacs to override that. See https://lists.gnu.org/archive/html/emacs-devel/2025-07/msg00309.html. --- lisp/startup.el | 6 ------ 1 file changed, 6 deletions(-) diff --git a/lisp/startup.el b/lisp/startup.el index 35c3cb03e17..836ead6deb0 100644 --- a/lisp/startup.el +++ b/lisp/startup.el @@ -853,12 +853,6 @@ It is the default value of the variable `top-level'." ;; We are careful to do it late (after term-setup-hook), although the ;; new multi-tty code does not use $TERM any more there anyway. (setenv "TERM" "dumb") - ;; Similarly, a subprocess should not try to invoke a pager, as most - ;; pagers will fail in a dumb terminal. Many programs default to - ;; using "less" when PAGER is unset, so set PAGER to "cat"; using cat - ;; as a pager is equivalent to not using a pager at all. - (when (executable-find "cat") - (setenv "PAGER" "cat")) ;; Remove DISPLAY from the process-environment as well. This allows ;; `callproc.c' to give it a useful adaptive default which is either ;; the value of the `display' frame-parameter or the DISPLAY value From 3fb8ffe61eacb73df17f2a4a6c2ac240951a289b Mon Sep 17 00:00:00 2001 From: "Peder O. Klingenberg" Date: Wed, 6 Aug 2025 21:55:38 +0200 Subject: [PATCH 083/216] Emulate dynamic binding of 'url-max-redirections' * lisp/url/url-http.el (url-http): Make 'url-max-redirections' buffer-local in request buffers, to make dynamic binding over 'url-retrieve' work as expected. (Bug#79186) --- lisp/url/url-http.el | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lisp/url/url-http.el b/lisp/url/url-http.el index a230a37e9ff..04fee189cfa 100644 --- a/lisp/url/url-http.el +++ b/lisp/url/url-http.el @@ -1372,7 +1372,8 @@ The return value of this function is the retrieval buffer." (httpver url-http-version) (httpkeepalive url-http-attempt-keepalives) (user-agent url-user-agent) - (privacy-level url-privacy-level)) + (privacy-level url-privacy-level) + (max-redirections url-max-redirections)) (if (not connection) ;; Failed to open the connection for some reason (progn @@ -1418,6 +1419,7 @@ The return value of this function is the retrieval buffer." url-http-extensions-header url-user-agent url-privacy-level + url-max-redirections nsm-noninteractive)) (set (make-local-variable var) nil)) @@ -1447,6 +1449,7 @@ The return value of this function is the retrieval buffer." url-http-extensions-header url-extensions-header url-user-agent user-agent url-privacy-level privacy-level + url-max-redirections max-redirections nsm-noninteractive noninteractive-p) (set-process-buffer connection buffer) From e1fd7b5d89ec7f2df40fa01bf4041c0d1df147b4 Mon Sep 17 00:00:00 2001 From: Vincenzo Pupillo Date: Thu, 10 Jul 2025 17:20:34 +0200 Subject: [PATCH 084/216] `php-ts-mode' depends on `mhtml-ts-mode' instead of JS,CSS and HTML The direct dependence on js-ts-mode, css-ts-mode and html-ts-mode has now been replaced by 'mhtml-ts-mode'. Additional benefits are: 1. Imenu now exposes symbols for all of all languages, 2. navigation now works correctly for all languages, 3. outline works for all languages. Additional new features are: 1. indentation of PHP in mixed buffers with HTML now works much better and allows three different behaviors, an option allows you to choose the behavior. 2. a new feature shows where PHP ini files are both locally and remotely, if the buffer is associated with a remote PHP file. * etc/NEWS: Mention the rationale for those changes, the new command, and the new options. * lisp/textmodes/mhtml-ts-mode.el (mhtml-ts-mode--range-settings): New variable that store range settings. (mhtml-ts-mode--treesit-aggregated-outline-predicate): New variable that store outline predicates. (mhtml-ts-mode): Use those two new variables. * lisp/progmodes/php-ts-mode.el: Update doc string. Removed old dependencies. (php-ts-mode--language-source-alist): Updated parsers version. (php-ts-mode-install-parsers): Removed old dependencies, now relies on (mhtml-ts-mode-install-parsers). Update doc string. (php-ts-mode-js-css-indent-offset): Update doc string. (php-ts-mode-css-fontify-colors): Removed option, now relies on 'php-ts-mode-css-fontify-colors'. (php-ts-mode-html-indent-offset): New user option. (php-ts-mode-html-relative-indent): New user option. (php-ts-mode-php-default-executable): Assume \"php\" instead of \"/usr/bin/php\" if 'executable-find' fails. (php-ts-mode-php-config): Fix tag. (php-ts-mode-find-sibling-rules): New user option with the rules for finding siblings of a file. (php-ts-mode-phpdoc-highlight-errors): New user option, if non-nil highlight unkown PHPDOC nodes. (php-ts-mode--indent-style-setter): Indentation fix. (php-ts-mode-indent-style): Indentation fix. (php-ts-mode-flymake-php): Indentation fix. (php-ts-mode--executable): Indentation fix. (php-ts-mode-show-ini): New command that show the locations of PHP ini files. (php-ts-mode--get-indent-style): Indentation fix. (php-ts-mode--set-indent-property): Indentation fix. (php-ts-mode-set-style): Indentation fix. (php-ts-mode--get-parser-ranges): Indentation fix. (php-ts-mode--possibly-braceless-keyword-re): Removed no longer used constant. (php-ts-mode--open-statement-group-heuristic): Removed no longer used function. (php-ts-mode--else-heuristic): Indentation fix. (php-ts-mode--first-sibling): Indentation fix. (php-ts-mode--js-css-tag-bol): Function removed, now using 'mhtml-ts-mode--js-css-tag-bol'. (php-ts-mode--parent-html-bol): Removed no longer used function. (php-ts-mode--parent-html-heuristic): Now more reliably handles the mixed indentation of PHP and HTML. The new option 'php-ts-mode-html-relative-indent' changes the behavior of the function. When 'php-ts-mode-html-relative-indent' is 't' the new option 'php-ts-mode-html-indent-offset' controls the offset of PHP code relative to HTML tags. (php-ts-mode--array-element-heuristic): Indentation fix. (php-ts-mode--anchor-prev-sibling): Indentation fix.. (php-ts-mode--indent-styles): The indentation rules for 'program' and 'text_interpolation' are now common to all indentation styles, thanks to the new version of 'php-ts-mode-arent-html-heuristic'. New rules for 'namespace_use_clause', 'use_declaration', 'use_list', 'attribute_group' and 'string_content'. (php-ts-mode--prettify-symbols-alist): New variable to support 'prettify-symbols-alist'. (php-ts-mode--test-visibility-modifier-operation-clause-p): Rename ... (php-ts-mode--test-visibility-modifier-operation-p): ... to this more correct name. (php-ts-mode--test-property-hook-clause-p): Rename ... (php-ts-mode--test-property-hook-p): ... to this more correct name. (php-ts-mode--test-relative-name-p): New function to test if 'relative_name' is supported by the PHP parser. (php-ts-mode--font-lock-settings): Use the new function for highlight 'relative_name'. Highlight 'error_suppression_expression'. Refactoring using the renamed functions. Indentation fix. (php-ts-mode--custom-html-font-lock-settings): Replace the rules with 'mhtml-ts-mode--treesit-font-lock-settings'. Fix docs string. (php-ts-mode--phpdoc-font-lock-settings): Added support for "array" and "list" array_type. New "phpdoc-error" feature to highlight unknown nodes via new 'php-ts-mode--phpdoc-fontify-error' function. (php-ts-mode--colorize-css-value): Removed function no longer used, now highlighting is handled directly by 'mhtml-ts-mode'. (php-ts-mode--phpdoc-fontify-error): New function used by 'php-ts-mode--phpdoc-font-lock-settings'. (php-ts-mode--parent-object): Indentation fix. (php-ts-mode--defun-name-separator): Indentation fix. (php-ts-mode--defun-object-name): Indentation fix. (php-ts-mode--defun-name): The function now also returns the defun name of 'mhtml-ts-mode'. (php-ts-mode--treesit-defun-type-regexp): New regexp for 'treesit-defun-type-regexp' support. (php-ts-mode--indent-defun): Indentation fix. (php-ts-mode--defun-valid-p): Fix indentation. (php-ts-mode--comment-indent-new-line): Indentation fix. (php-ts-mode--comment-current-plist): New local variable that stores the state of the PHP comment style. (php-ts-mode--comment-setup): The function now handles changing, for different languages, comment styles, using "php-ts-mode--comment-current-plist" to store and retrieve the comment style for the PHP language. (php-ts-mode-comment-setup): Now store the PHP comment style in 'php-ts-mode--comment-current-plist'. (php-ts-mode-menu): Indentation fix. (php-ts-mode): Replace dependency from JS, CSS and HTML width 'mhtml-ts-mode'. Navigation, Imenu and Outline now support PHP, HTML, Javascript and CSS. Added support for 'prettify-symbol-mode'. Added support for 'electric-layout-rules'. (php-ts-mode-run-php-webserver, php-ts-mode--webserver-read-args) (php-ts-mode--webserver-read-args, inferior-php-ts-mode, run-php) (inferior-php-ts-mode-startup, php-ts-mode-inferior--write-history) (php-ts-mode-send-region): Indentation fixes. (Bug#78994) --- etc/NEWS | 34 + lisp/progmodes/php-ts-mode.el | 1602 ++++++++++++++++--------------- lisp/textmodes/mhtml-ts-mode.el | 78 +- 3 files changed, 917 insertions(+), 797 deletions(-) diff --git a/etc/NEWS b/etc/NEWS index d6bf37f3eb5..d541d805d6b 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -931,6 +931,12 @@ option controls how much is indented for method chaining. ** PHP-ts mode +--- +*** 'php-ts-mode' now depends on 'mhtml-ts-mode'. +The direct dependency on js-ts-mode, css-ts-mode and html-ts-mode has +now been replaced by ‘mhtml-ts-mode’. Navigation, Outline and Imenu +work for all languages and code maintenance is easier. + --- *** 'php-ts-mode-run-php-webserver' can now accept a custom "php.ini" file. You can use the new optional argument CONFIG when calling @@ -939,6 +945,34 @@ the built-in Web server. Interactively, when invoked with a prefix argument, 'php-ts-mode-run-php-webserver' prompts for the config file as well as for other connection parameters. +--- +*** The option 'php-ts-mode-css-fontify-colors' has been removed. +'mhtml-ts-mode-css-fontify-colors' replaces this option. + +--- +*** New user option 'php-ts-mode-html-relative-indent'. +In buffers containing both PHP and HTML, this option allows you to +define how the PHP code should be indented relative to the position of +the HTML tags. + +--- +*** New user option 'php-ts-mode-html-indent-offset'. +Offset of PHP code block relative to HTML tags. + +--- +*** New user option 'php-ts-mode-find-sibling-rules'. +Rules for finding siblings of a PHP file. + +--- +*** New user option 'php-ts-mode-phpdoc-highlight-errors'. +When non nil, it highlights unknown PHPDOC tags using +'font-lock-warning-face' so that the user can identify them more easily. + +--- +*** New command 'php-ts-mode-show-ini'. +Show the location of the PHP ini files, if the buffer is associated to a remote +PHP file show the remote PHP ini files. + ** Rust-ts mode --- diff --git a/lisp/progmodes/php-ts-mode.el b/lisp/progmodes/php-ts-mode.el index a01e4d66fba..d45226ad5e9 100644 --- a/lisp/progmodes/php-ts-mode.el +++ b/lisp/progmodes/php-ts-mode.el @@ -25,12 +25,12 @@ ;;; Tree-sitter language versions ;; ;; php-ts-mode has been tested with the following grammars and version: -;; - tree-sitter-phpdoc: v0.1.5 +;; - tree-sitter-phpdoc: v0.1.6 ;; - tree-sitter-css: v0.23.1-1-g6a442a3 ;; - tree-sitter-jsdoc: v0.23.2 ;; - tree-sitter-javascript: v0.23.1-2-g108b2d4 ;; - tree-sitter-html: v0.23.2-1-gd9219ad -;; - tree-sitter-php: v0.23.11 +;; - tree-sitter-php: v0.23.12 ;; ;; We try our best to make builtin modes work with latest grammar ;; versions, so a more recent grammar has a good chance to work too. @@ -42,7 +42,7 @@ ;; for editing PHP files with embedded HTML, JavaScript, CSS and phpdoc. ;; Tree Sitter is used to parse each of these languages. ;; -;; Please note that this package requires `html-ts-mode', which +;; Please note that this package requires `mhtml-ts-mode', which ;; registers itself as the major mode for editing HTML. ;; ;; This package is compatible and has been tested with the following @@ -69,9 +69,7 @@ (require 'treesit) (require 'c-ts-common) ;; For comment indent and filling. -(require 'html-ts-mode) ;; for embed html -(require 'css-mode) ;; for embed css into html -(require 'js) ;; for embed javascript into html +(require 'mhtml-ts-mode) ;; For embed html, css and js. (require 'comint) (treesit-declare-unavailable-functions) @@ -83,10 +81,10 @@ ;;; Install treesitter language parsers (defvar php-ts-mode--language-source-alist '((php "https://github.com/tree-sitter/tree-sitter-php" - :commit "43aad2b9a98aa8e603ea0cf5bb630728a5591ad8" - :source-dir "php/src") + :commit "f7cf7348737d8cff1b13407a0bfedce02ee7b046" + :source-dir "php/src") (phpdoc "https://github.com/claytonrcarter/tree-sitter-phpdoc" - :commit "fe3202e468bc17332bec8969f2b50ff1f1da3a46")) + :commit "03bb10330704b0b371b044e937d5cc7cd40b4999")) "Treesitter language parsers required by `php-ts-mode'. You can customize `treesit-language-source-alist' if you want to stick to a specific commit and/or use different parsers.") @@ -97,9 +95,10 @@ to stick to a specific commit and/or use different parsers.") (defun php-ts-mode-install-parsers () "Install all the required treesitter parsers. `treesit-language-source-alist' defines which parsers to install. -It's pre-filled by loading \"html-ts-mode\", \"css-mode\", \"js\"." +It's pre-filled by loading \"mhtml-ts-mode\"." (interactive) - (dolist (lang '(php phpdoc html css javascript jsdoc)) + (mhtml-ts-mode-install-parsers) + (dolist (lang '(php phpdoc)) (treesit-install-language-grammar lang))) ;;; Custom variables @@ -118,25 +117,63 @@ It's pre-filled by loading \"html-ts-mode\", \"css-mode\", \"js\"." (defcustom php-ts-mode-js-css-indent-offset 2 "JavaScript and CSS indent spaces related to the