commit da51f73acd21ae6cf52608a6de0306cd1e05ce24 Author: Stefan Monnier Date: Mon Sep 28 01:14:01 2026 -0400 (editorconfig-indentation-alist): Move settings to their modes For (most) modes which are bundled with Emacs, move the setting to the respective major mode definition. * lisp/editorconfig.el (editorconfig-indentation-alist): Remove entries for 'bash-ts-mode', 'elixir-ts-mode', 'js-ts-mode', 'js-jsx-mode', `'xml-mode', 'octave-mode', 'ps-mode', 'python-ts-mode', `'uby-ts-mode', 'tcl-mode', 'typescript-ts-base-mode', `'90-mode', 'lisp-mode', 'emacs-lisp-mode', and 'yaml-ts-mode'. Change entry for LaTeX modes so it applies only to AUCTeX. (editorconfig--get-indentation-python-mode) (editorconfig--get-indentation-latex-mode): Declare obsolete and remove the now unused parts. (editorconfig--get-indentation-nxml-mode): Move to 'nxml-mode.el'. (editorconfig-lisp-use-default-indent) (editorconfig--get-indentation-lisp-mode): Move to 'lisp-mode.el'. * lisp/nxml/nxml-mode.el (nxml--indent-size-vars): Move (and rename) from `'ditorconfig.el'. (nxml-mode): * lisp/textmodes/yaml-ts-mode.el (yaml-ts-mode): * lisp/progmodes/typescript-ts-mode.el (typescript-ts-base-mode): * lisp/progmodes/tcl.el (tcl-mode): * lisp/progmodes/f90.el (f90-mode): * lisp/progmodes/sh-script.el (sh-base-mode): * lisp/progmodes/ruby-ts-mode.el (ruby-ts-mode): * lisp/progmodes/python.el (python-base-mode): * lisp/progmodes/ps-mode.el (ps-mode): * lisp/progmodes/octave.el (octave-mode): * lisp/progmodes/js.el (js--mode-setup, js-ts-mode): * lisp/progmodes/elixir-ts-mode.el (elixir-ts-mode): Set 'editorconfig-indent-size-vars'. * lisp/textmodes/tex-mode.el (latex--indent-size-vars): New function, extracted from 'editorconfig--get-indentation-latex-mode'. (latex-mode): Use it. * lisp/emacs-lisp/lisp-mode.el (lisp--override-editorconfig-indent): New name for 'editorconfig-lisp-use-default-indent'. (editorconfig-lisp-use-default-indent): Move from 'editorconfig.el' and mark it as obsolete alias. (lisp--indent-size-vars): Move from 'editorconfig.el' and rename. (lisp-mode-variables): Use it. diff --git a/lisp/editorconfig.el b/lisp/editorconfig.el index 3c8ac8c52fd..88130ed7a7c 100644 --- a/lisp/editorconfig.el +++ b/lisp/editorconfig.el @@ -127,18 +127,8 @@ This hook will be run even when there are no matching sections in "0.5") (defcustom editorconfig-indentation-alist ;; For contributors: Sort modes in alphabetical order - '((bash-ts-mode sh-basic-offset) - (cmake-mode cmake-tab-width) + '((cmake-mode cmake-tab-width) (coffee-mode coffee-tab-width) - (elixir-ts-mode elixir-ts-indent-offset) - (emacs-lisp-mode . editorconfig--get-indentation-lisp-mode) - (f90-mode f90-associate-indent - f90-continuation-indent - f90-critical-indent - f90-do-indent - f90-if-indent - f90-program-indent - f90-type-indent) (feature-mode feature-indent-offset feature-indent-level) (fsharp-mode fsharp-continuation-offset @@ -154,31 +144,20 @@ This hook will be run even when there are no matching sections in shm-indent-spaces) (haxor-mode haxor-tab-width) (jade-mode jade-tab-width) - (js-ts-mode js-indent-level) - (js-jsx-mode js-indent-level sgml-basic-offset) (js2-jsx-mode js2-basic-offset sgml-basic-offset) (json-mode js-indent-level) (jsonian-mode jsonian-default-indentation) (kotlin-mode kotlin-tab-width) - (latex-mode . editorconfig--get-indentation-latex-mode) - (lisp-mode . editorconfig--get-indentation-lisp-mode) + (LaTeX-mode . editorconfig--get-indentation-latex-mode) (livescript-mode livescript-tab-width) (magik-ts-mode magik-indent-level) (meson-mode meson-indent-basic) (mips-mode mips-tab-width) - (nxml-mode . editorconfig--get-indentation-nxml-mode) - (octave-mode octave-block-offset) - (ps-mode ps-mode-tab) (pug-mode pug-tab-width) (python-mode . editorconfig--get-indentation-python-mode) - (python-ts-mode . editorconfig--get-indentation-python-mode) (rjsx-mode js-indent-level sgml-basic-offset) - (ruby-ts-mode ruby-indent-level) (scala-mode scala-indent:step) (swift-mode swift-mode:basic-offset) - (tcl-mode tcl-indent-level - tcl-continued-indent-level) - (typescript-ts-base-mode typescript-ts-indent-offset) (verilog-mode verilog-indent-level verilog-indent-level-behavioral verilog-indent-level-declaration @@ -186,7 +165,6 @@ This hook will be run even when there are no matching sections in verilog-cexp-indent verilog-case-indent) (web-mode . editorconfig--get-indentation-web-mode) - (yaml-ts-mode yaml-indent-offset) ) "Alist of indentation setting methods by modes. @@ -217,17 +195,6 @@ Otherwise, use `delete-trailing-whitespace'." "Hash object of EditorConfig properties that was enabled for current buffer.") (put 'editorconfig-properties-hash 'permanent-local t) -(defvar editorconfig-lisp-use-default-indent nil - "Selectively ignore the value of indent_size for Lisp files. -Prevents `lisp-indent-offset' from being set selectively. - -nil - `lisp-indent-offset' is always set normally. -t - `lisp-indent-offset' is never set normally - (always use default indent for lisps). -number - `lisp-indent-offset' is not set only if indent_size is - equal to this number. For example, if this is set to 2, - `lisp-indent-offset' will not be set only if indent_size is 2.") - (define-error 'editorconfig-error "Error thrown from editorconfig lib") @@ -255,33 +222,20 @@ Make a message by passing ARGS to `format-message'." (defun editorconfig--get-indentation-python-mode (size) "Vars to set `python-mode' indent size to SIZE." - `((python-indent-offset . ,size) ;For python.el - (py-indent-offset . ,size))) ;For python-mode.el + ;; FIXME: `python.el' now sets `editorconfig-indent-size-vars' so this + ;; is used only in other Python modes; we should get rid of it. + (declare (obsolete editorconfig-indent-size-vars "32.1")) + `((py-indent-offset . ,size))) ;For python-mode.el (defun editorconfig--get-indentation-latex-mode (size) - "Vars to set `latex-mode' indent size to SIZE." - `((tex-indent-basic . ,size) - (tex-indent-item . ,size) - (tex-indent-arg . ,(* 2 size)) - ;; For AUCTeX - (TeX-brace-indent-level . ,size) + "Vars to set `LaTeX-mode' indent size to SIZE." + ;; FIXME: `tex-mode.el' now sets `editorconfig-indent-size-vars' so this + ;; is used only in other LaTeX modes; we should get rid of it. + (declare (obsolete editorconfig-indent-size-vars "32.1")) + `((TeX-brace-indent-level . ,size) (LaTeX-indent-level . ,size) (LaTeX-item-indent . ,(- size)))) -(defun editorconfig--get-indentation-nxml-mode (size) - "Vars to set `nxml-mode' indent size to SIZE." - `((nxml-child-indent . ,size) - (nxml-attribute-indent . ,(* 2 size)))) - -(defun editorconfig--get-indentation-lisp-mode (size) - "Set indent size to SIZE for Lisp mode(s)." - (when (cond ((null editorconfig-lisp-use-default-indent) t) - ((eql t editorconfig-lisp-use-default-indent) nil) - ((numberp editorconfig-lisp-use-default-indent) - (not (eql size editorconfig-lisp-use-default-indent))) - (t t)) - `((lisp-indent-offset . ,size)))) - (cl-defun editorconfig--should-set (symbol) "Determine if editorconfig should set SYMBOL." (display-warning '(editorconfig editorconfig--should-set) diff --git a/lisp/elec-pair.el b/lisp/elec-pair.el index 5bcdabe970d..526bd3db2b5 100644 --- a/lisp/elec-pair.el +++ b/lisp/elec-pair.el @@ -485,7 +485,7 @@ If point is not enclosed by any lists, return ((t) . (t))." (funcall ended-prematurely-fn))))))) (cons innermost outermost))) -(defvar electric-pair-string-bound-function 'point-max +(defvar electric-pair-string-bound-function #'point-max "Next buffer position where strings are syntactically unexpected. Value is a function called with no arguments and returning a buffer position. Major modes should set this variable diff --git a/lisp/emacs-lisp/lisp-mode.el b/lisp/emacs-lisp/lisp-mode.el index f0967c2b16c..144a023cbda 100644 --- a/lisp/emacs-lisp/lisp-mode.el +++ b/lisp/emacs-lisp/lisp-mode.el @@ -691,15 +691,16 @@ font-lock keywords will not be case sensitive." (when lisp-syntax (set-syntax-table lisp-mode-syntax-table)) (setq-local paragraph-ignore-fill-prefix t) - (setq-local fill-paragraph-function 'lisp-fill-paragraph) + (setq-local fill-paragraph-function #'lisp-fill-paragraph) (setq-local adaptive-fill-function #'lisp-adaptive-fill) ;; Adaptive fill mode gets in the way of auto-fill, ;; and should make no difference for explicit fill ;; because lisp-fill-paragraph should do the job. ;; I believe that newcomment's auto-fill code properly deals with it -stef ;;(setq-local adaptive-fill-mode nil) - (setq-local indent-line-function 'lisp-indent-line) - (setq-local indent-region-function 'lisp-indent-region) + (setq-local indent-line-function #'lisp-indent-line) + (setq-local indent-region-function #'lisp-indent-region) + (setq-local editorconfig-indent-size-vars #'lisp--indent-size-vars) (setq-local comment-indent-function #'lisp-comment-indent) (setq-local outline-regexp (concat ";;;;* [^ \t\n]\\|(\\|\\(" lisp-mode-autoload-regexp @@ -714,7 +715,7 @@ font-lock keywords will not be case sensitive." (setq-local comment-use-syntax t) (setq-local imenu-generic-expression lisp-imenu-generic-expression) (setq-local multibyte-syntax-as-symbol t) - ;; (setq-local syntax-begin-function 'beginning-of-defun) ;;Bug#16247. + ;; (setq-local syntax-begin-function #'beginning-of-defun) ;;Bug#16247. (setq font-lock-defaults `(,(if elisp '(lisp-el-font-lock-keywords lisp-el-font-lock-keywords-1 @@ -831,8 +832,8 @@ Blank lines separate paragraphs. Semicolons start comments. \\{lisp-mode-map} Note that `run-lisp' may be used either to start an inferior Lisp job or to switch back to an existing one." - (setq-local lisp-indent-function 'common-lisp-indent-function) - (setq-local find-tag-default-function 'lisp-find-tag-default) + (setq-local lisp-indent-function #'common-lisp-indent-function) + (setq-local find-tag-default-function #'lisp-find-tag-default) (setq-local comment-start-skip "\\(\\(^\\|[^\\\n]\\)\\(\\\\\\\\\\)*\\)\\(;+\\|#|\\) *") (setq-local comment-end-skip "[ \t]*\\(\\s>\\||#\\)") @@ -847,7 +848,7 @@ or to switch back to an existing one." default)))) ;; Used in old LispM code. -(defalias 'common-lisp-mode 'lisp-mode) +(defalias 'common-lisp-mode #'lisp-mode) (autoload 'lisp-eval-defun "inf-lisp" nil t) @@ -877,6 +878,30 @@ function is `common-lisp-indent-function'." :type 'function :group 'lisp) +(define-obsolete-variable-alias 'editorconfig-lisp-use-default-indent + 'lisp-override-editorconfig-indent "32.1") +(defvar lisp--override-editorconfig-indent nil + "Selectively ignore the value of EditorConfig's indent_size for Lisp files. +Prevents selectively `lisp-indent-offset' from being set. + +nil - `lisp-indent-offset' is always set from indent_size. +t - `lisp-indent-offset' is never set from indent_size. + (always use default indent for lisps). +number - `lisp-indent-offset' is set from indent_size unless indent_size + is equal to this number.") + +(defun lisp--indent-size-vars (size) + "Set indent size to SIZE for Lisp mode(s)." + ;; FIXME: This doesn't work if we want to use this indent-size-vars settings + ;; for other purposes than EditorConfig, such as for `indent-bars-mode'. + ;; Maybe we should just get rid of lisp--override-editorconfig-indent? + (when (cond ((null lisp--override-editorconfig-indent) t) + ((eql t lisp--override-editorconfig-indent) nil) + ((numberp lisp--override-editorconfig-indent) + (not (eql size lisp--override-editorconfig-indent))) + (t t)) + `((lisp-indent-offset . ,size)))) + (defun lisp-ppss (&optional pos) "Return Parse-Partial-Sexp State at POS, defaulting to point. Like `syntax-ppss' but includes the character address of the last diff --git a/lisp/nxml/nxml-mode.el b/lisp/nxml/nxml-mode.el index 9b617b40f09..8a2f3d5cfc9 100644 --- a/lisp/nxml/nxml-mode.el +++ b/lisp/nxml/nxml-mode.el @@ -446,6 +446,11 @@ reference.") (defvar tildify-space-string) (defvar tildify-foreach-region-function) +(defun nxml--indent-size-vars (size) + "Vars to set `nxml-mode' indent size to SIZE." + `((nxml-child-indent . ,size) + (nxml-attribute-indent . ,(* 2 size)))) + ;;;###autoload (define-derived-mode nxml-mode text-mode "nXML" ;; We use C-c C-i instead of \\[nxml-balanced-close-start-tag-inline] @@ -520,6 +525,7 @@ Many aspects this mode can be customized using (setq-local adaptive-fill-mode nil) (setq-local forward-sexp-function #'nxml-forward-balanced-item) (setq-local indent-line-function #'nxml-indent-line) + (setq-local editorconfig-indent-size-vars #'nxml--indent-size-vars) (setq-local fill-paragraph-function #'nxml-do-fill-paragraph) ;; Comment support ;; This doesn't seem to work too well; diff --git a/lisp/progmodes/elixir-ts-mode.el b/lisp/progmodes/elixir-ts-mode.el index b040113b3dc..196070b67ba 100644 --- a/lisp/progmodes/elixir-ts-mode.el +++ b/lisp/progmodes/elixir-ts-mode.el @@ -755,6 +755,7 @@ Return nil if NODE is not a defun node or doesn't have a name." ;; Indent. (setq-local treesit-simple-indent-rules elixir-ts--indent-rules) + (setq-local editorconfig-indent-size-vars '(elixir-ts-indent-offset)) ;; Navigation. (setq-local treesit-thing-settings diff --git a/lisp/progmodes/f90.el b/lisp/progmodes/f90.el index ab9e6e76919..3a6c171a1bb 100644 --- a/lisp/progmodes/f90.el +++ b/lisp/progmodes/f90.el @@ -1165,6 +1165,14 @@ with no args, if that value is non-nil." :group 'f90 :abbrev-table f90-mode-abbrev-table (setq-local indent-line-function #'f90-indent-line) + (setq-local editorconfig-indent-size-vars + '(f90-associate-indent + f90-continuation-indent + f90-critical-indent + f90-do-indent + f90-if-indent + f90-program-indent + f90-type-indent)) (setq-local indent-region-function #'f90-indent-region) (setq-local comment-start "!") (setq-local comment-start-skip "!+ *") diff --git a/lisp/progmodes/js.el b/lisp/progmodes/js.el index bd58b16d342..485cb987d0f 100644 --- a/lisp/progmodes/js.el +++ b/lisp/progmodes/js.el @@ -3841,6 +3841,7 @@ Currently there are `js-mode' and `js-ts-mode'." ;; Ensure all CC Mode "lang variables" are set to valid values. (c-init-language-vars js-mode) (setq-local indent-line-function #'js-indent-line) + (setq-local editorconfig-indent-size-vars '(js-indent-level)) (setq-local beginning-of-defun-function #'js-beginning-of-defun) (setq-local end-of-defun-function #'js-end-of-defun) (setq-local open-paren-in-column-0-is-defun-start nil) @@ -4079,6 +4080,7 @@ See `treesit-thing-settings' for more information.") ;; Indent. (setq-local treesit-simple-indent-rules (js--treesit-indent-rules)) + (setq-local editorconfig-indent-size-vars '(js-indent-level)) ;; Navigation. (setq-local treesit-defun-type-regexp js--treesit-defun-type-regexp) (setq-local treesit-defun-name-function #'js--treesit-defun-name) @@ -4175,6 +4177,10 @@ could set `js-jsx-syntax' to t in your init file, or in a one of the aforementioned options instead of using this mode." :group 'js (js-jsx-enable) + ;; According to the comment after `js-jsx-indent-level' we don't use + ;; `sgml-basic-offset' any more. + ;;(setq-local editorconfig-indent-size-vars + ;; '(js-indent-level sgml-basic-offset',)) (setq-local comment-region-function #'js-jsx--comment-region) (js-use-syntactic-mode-name)) diff --git a/lisp/progmodes/octave.el b/lisp/progmodes/octave.el index 18800e29aa5..94a0b555499 100644 --- a/lisp/progmodes/octave.el +++ b/lisp/progmodes/octave.el @@ -577,6 +577,7 @@ Key bindings: :forward-token #'octave-smie-forward-token :backward-token #'octave-smie-backward-token) (setq-local smie-indent-basic 'octave-block-offset) + (setq-local editorconfig-indent-size-vars '(octave-block-offset)) (add-hook 'smie-indent-functions #'octave-indent-comment nil t) (setq-local smie-blink-matching-triggers diff --git a/lisp/progmodes/ps-mode.el b/lisp/progmodes/ps-mode.el index fe8b5ada8e0..4b25737cd09 100644 --- a/lisp/progmodes/ps-mode.el +++ b/lisp/progmodes/ps-mode.el @@ -500,6 +500,7 @@ Typing \\\\[ps-run-goto-error] when the cursor is at the number ps-mode-font-lock-keywords-3) nil)) (smie-setup nil #'ps-mode-smie-rules) + (setq-local editorconfig-indent-size-vars '(ps-mode-tab)) (setq-local electric-indent-chars (append '(?> ?\] ?\}) electric-indent-chars)) (setq-local comment-start "%") diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index c88beb6da96..b196fe44699 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -7367,6 +7367,7 @@ concrete implementations. Currently there are two concrete implementations: `python-mode' and `python-ts-mode'." (setq-local tab-width 8) (setq-local indent-tabs-mode nil) + (setq-local editorconfig-indent-size-vars '(python-indent-offset)) (setq-local comment-start "# ") (setq-local comment-start-skip "#+\\s-*") diff --git a/lisp/progmodes/ruby-ts-mode.el b/lisp/progmodes/ruby-ts-mode.el index 2c561f09e93..5b79be446af 100644 --- a/lisp/progmodes/ruby-ts-mode.el +++ b/lisp/progmodes/ruby-ts-mode.el @@ -1262,6 +1262,7 @@ leading double colon is not added." (kill-local-variable 'outline-level) (setq-local treesit-simple-indent-rules (ruby-ts--indent-rules)) + (setq-local editorconfig-indent-size-vars '(ruby-indent-level)) ;; Font-lock. (setq-local treesit-font-lock-settings (ruby-ts--font-lock-settings 'ruby)) diff --git a/lisp/progmodes/sh-script.el b/lisp/progmodes/sh-script.el index f1ae855f6e1..f5f2d4b405d 100644 --- a/lisp/progmodes/sh-script.el +++ b/lisp/progmodes/sh-script.el @@ -1471,6 +1471,7 @@ implementations. Currently there are two: `sh-mode' and (setq-local skeleton-pair-default-alist sh-skeleton-pair-default-alist) + (setq-local editorconfig-indent-size-vars '(sh-basic-offset)) (setq-local paragraph-start (concat page-delimiter "\\|$")) (setq-local paragraph-separate (concat paragraph-start "\\|#!/")) (setq-local comment-start "# ") diff --git a/lisp/progmodes/tcl.el b/lisp/progmodes/tcl.el index 8ef717967bd..6bf09a40a6f 100644 --- a/lisp/progmodes/tcl.el +++ b/lisp/progmodes/tcl.el @@ -615,6 +615,8 @@ already exist." (setq-local paragraph-ignore-fill-prefix t)) (setq-local indent-line-function #'tcl-indent-line) + (setq-local editorconfig-indent-size-vars + '(tcl-indent-level tcl-continued-indent-level)) (setq-local comment-indent-function #'tcl-comment-indent) ;; Tcl doesn't require a final newline. ;; (setq-local require-final-newline t) diff --git a/lisp/progmodes/typescript-ts-mode.el b/lisp/progmodes/typescript-ts-mode.el index 83a996ad629..3c7c922c72b 100644 --- a/lisp/progmodes/typescript-ts-mode.el +++ b/lisp/progmodes/typescript-ts-mode.el @@ -693,7 +693,8 @@ This mode is intended to be inherited by concrete major modes." (setq-local treesit-simple-imenu-settings typescript-ts-mode--simple-imenu-settings) ;; Outline minor mode - (setq-local treesit-outline-predicate typescript-ts-mode--outline-predicate)) + (setq-local treesit-outline-predicate typescript-ts-mode--outline-predicate) + (setq-local editorconfig-indent-size-vars '(typescript-ts-indent-offset))) ;;;###autoload (define-derived-mode typescript-ts-mode typescript-ts-base-mode "TypeScript" diff --git a/lisp/textmodes/tex-mode.el b/lisp/textmodes/tex-mode.el index d6be9d511d6..1c113df9342 100644 --- a/lisp/textmodes/tex-mode.el +++ b/lisp/textmodes/tex-mode.el @@ -1193,6 +1193,8 @@ subshell is initiated, `tex-shell-hook' is run." (setq-local tex-face-alist tex-latex-face-alist) (add-hook 'fill-nobreak-predicate #'latex-fill-nobreak-predicate nil t) (setq-local indent-line-function #'latex-indent) + (setq-local editorconfig-indent-size-vars #'latex--indent-size-vars) + (setq-local fill-indent-according-to-mode t) (add-hook 'completion-at-point-functions #'latex-complete-data nil 'local) @@ -3042,6 +3044,13 @@ There might be text before point." (min (current-column) (+ tex-indent-arg col)) (skip-syntax-forward " ") (current-column))))))))))) + +(defun latex--indent-size-vars (size) + "Vars to set `latex-mode' indent size to SIZE." + `((tex-indent-basic . ,size) + (tex-indent-item . ,size) + (tex-indent-arg . ,(* 2 size)))) + ;;; DocTeX support (defun doctex-font-lock-^^A () diff --git a/lisp/textmodes/yaml-ts-mode.el b/lisp/textmodes/yaml-ts-mode.el index a356027c271..f7982c569a1 100644 --- a/lisp/textmodes/yaml-ts-mode.el +++ b/lisp/textmodes/yaml-ts-mode.el @@ -276,6 +276,9 @@ Calls REPORT-FN directly." ;; Indentation. (setq-local indent-tabs-mode nil) (setq-local tab-width 2) + ;; FIXME: This is what used to be in `editorconfig-indentation-alist', + ;; but AFAICT we don't use `yaml-indent-offset' at all. + (setq-local editorconfig-indent-size-vars '(yaml-indent-offset)) ;; Font-lock. (setq-local treesit-font-lock-settings yaml-ts-mode--font-lock-settings) commit 593241a5d018a9e0e14d70cbf2e7957338fdd468 Author: Wilson Snyder Date: Sun Sep 27 19:08:49 2026 -0400 Update verilog-mode from upstream. * lisp/progmodes/verilog-mode.el (verilog-auto-inst, verilog-auto-inst-port) (verilog-read-sub-decls-expr, verilog-signals-combine-bus): Add AUTOINST/AUTO_TEMPLATE unpacked array connections and merging. (verilog-simplify-range-expression): Fix AUTO range simplification emitting illegal "+-" constants. diff --git a/lisp/progmodes/verilog-mode.el b/lisp/progmodes/verilog-mode.el index 54adb1ec50f..c6032918559 100644 --- a/lisp/progmodes/verilog-mode.el +++ b/lisp/progmodes/verilog-mode.el @@ -9,7 +9,7 @@ ;; Keywords: languages ;; The "Version" is the date followed by the decimal rendition of the Git ;; commit hex. -;; Version: 2026.04.14.010117132 +;; Version: 2026.08.31.185049335 ;; Yoni Rabkin contacted the maintainer of this ;; file on 19/3/2008, and the maintainer agreed that when a bug is @@ -124,7 +124,7 @@ ;; ;; This variable will always hold the version number of the mode -(defconst verilog-mode-version "2026-04-14-09a600c-vpo-GNU" +(defconst verilog-mode-version "2026-08-31-b07a0f7-vpo-GNU" "Version of this Verilog mode.") (defconst verilog-mode-release-emacs t "If non-nil, this version of Verilog mode was released with Emacs itself.") @@ -9005,10 +9005,11 @@ Duplicate signals are also removed. For example A[2] and A[1] become A[2:1]." (let (combo buswarn out-list - sig highbit lowbit ; Temp information about current signal + sig highbit lowbit mem ; Temp information about current signal sv-name sv-highbit sv-lowbit ; Details about signal we are forming sv-comment sv-memory sv-enum sv-signed sv-type sv-multidim sv-busstring sv-modport + sv-mem-low sv-mem-high ; Bounds of mergeable unpacked numeric ranges bus) ;; Shove signals so duplicated signals will be adjacent (setq in-list (sort in-list #'verilog-signals-sort-compare)) @@ -9020,7 +9021,9 @@ Duplicate signals are also removed. For example A[2] and A[1] become A[2:1]." sv-highbit nil sv-busstring nil sv-comment (verilog-sig-comment sig) - sv-memory (verilog-sig-memory sig) + sv-memory nil + sv-mem-low nil + sv-mem-high nil sv-enum (verilog-sig-enum sig) sv-signed (verilog-sig-signed sig) sv-type (verilog-sig-type sig) @@ -9048,6 +9051,27 @@ Duplicate signals are also removed. For example A[2] and A[1] become A[2:1]." (bus ;; String, probably something like `preproc:0 (setq sv-busstring bus))) + ;; Extract unpacked-array details + (setq mem (verilog-sig-memory sig)) + (cond ((and mem + (string-match "^\\[\\([+-]?[0-9]+\\):\\([+-]?[0-9]+\\)\\]$" mem) + (<= (string-to-number (match-string 1 mem)) + (string-to-number (match-string 2 mem)))) + ;; Merge ascending numeric ranges without expanding every + ;; index. Overlapping or duplicate ranges merge silently, + ;; matching the packed-bit merge above. + ;; Descending ranges are left alone so existing declarations + ;; keep their direction. + (let ((lo (string-to-number (match-string 1 mem))) + (hi (string-to-number (match-string 2 mem)))) + (when sv-memory ; Mixed with an unmergeable form + (setq buswarn ", Couldn't Merge")) + (setq sv-mem-low (if sv-mem-low (min lo sv-mem-low) lo) + sv-mem-high (if sv-mem-high (max hi sv-mem-high) hi)))) + (mem + (when sv-mem-low ; Mixed with a mergeable numeric range + (setq buswarn ", Couldn't Merge")) + (setq sv-memory (or sv-memory mem)))) ;; Peek ahead to next signal (setq in-list (cdr in-list)) (setq sig (car in-list)) @@ -9061,8 +9085,7 @@ Duplicate signals are also removed. For example A[2] and A[1] become A[2:1]." sv-name bus)) (setq buswarn ", Couldn't Merge")) (if (verilog-sig-comment sig) (setq combo ", ...")) - (setq sv-memory (or sv-memory (verilog-sig-memory sig)) - sv-enum (or sv-enum (verilog-sig-enum sig)) + (setq sv-enum (or sv-enum (verilog-sig-enum sig)) sv-signed (or sv-signed (verilog-sig-signed sig)) sv-type (or sv-type (verilog-sig-type sig)) sv-multidim (or sv-multidim (verilog-sig-multidim sig)) @@ -9078,7 +9101,18 @@ Duplicate signals are also removed. For example A[2] and A[1] become A[2:1]." (concat "[" (int-to-string sv-highbit) ":" (int-to-string sv-lowbit) "]"))) (concat sv-comment combo buswarn) - sv-memory sv-enum sv-signed sv-type sv-multidim sv-modport) + (cond + ;; If any range was not safely mergeable, retain that + ;; original unpacked declaration + (sv-memory sv-memory) + (sv-mem-low + ;; Combined unpacked index span, ascending. + (concat "[" + (int-to-string sv-mem-low) + ":" + (int-to-string sv-mem-high) + "]"))) + sv-enum sv-signed sv-type sv-multidim sv-modport) out-list) sv-name nil)))) ;; @@ -9683,6 +9717,20 @@ Return an array of [outputs inouts inputs wire reg assign const gparam intf]." (defun verilog-read-sub-decls-expr (submoddecls par-values comment port expr) "For `verilog-read-sub-decls-line', parse a subexpression and add signals." ;;(message "vrsde: `%s'" expr) + ;; "sig[2]/*[6:0].[2]*/" is a connection of one unpacked-array element + ;; made by an AUTO_TEMPLATE "[].[index]", see `verilog-auto-inst-port'. + ;; Canonicalize it to "sig[6:0].[2:2]" so the executable index is parsed + ;; as an unpacked range below (single-element, mergeable by + ;; `verilog-signals-combine-bus'); non-numeric indexes keep ".[index]". + (when (string-match + "^\\s-*\\(\\(?:[a-zA-Z_][a-zA-Z_0-9]*\\|\\\\[^ \t\n\f]+\\s-\\)\\)\\s-*\\[\\([^][]+\\)\\]\\s-*/\\*\\(\\(?:\\[[^][]+\\]\\)*\\)\\.\\[[^][]+\\]\\*/\\s-*$" + expr) + (let ((idx (match-string 2 expr))) + (setq expr (concat (match-string 1 expr) + (match-string 3 expr) + ".[" idx + (if (string-match "^[+-]?[0-9]+$" idx) (concat ":" idx) "") + "]")))) ;; Replace special /*[....]*/ comments inserted by verilog-auto-inst-port (setq expr (verilog-string-replace-matches "/\\*\\(\\.?\\[\\([^*]+\\|[*][^/]\\)+\\]\\)\\*/" "\\1" nil nil expr)) @@ -11505,23 +11553,36 @@ This repairs those mis-inserted by an AUTOARG." (op (match-string 3 out)) (rhs (string-to-number (match-string 4 out))) (post (match-string 5 out)) - val) + val prefix) (when (equal pre "-") (setq lhs (- lhs))) (setq val (if (equal op "-") (- lhs rhs) - (+ lhs rhs)) - out (replace-match - (concat (cond ((and (equal pre "-") - (< val 0)) - "") ; Not "--20" but just "-20" - ((and (equal pre "-") - (> val 0)) - "+") ; Not "-+20" but just "+20" - (t pre)) - (int-to-string val) - post) - nil nil out)) )) + (+ lhs rhs))) + ;; Pick the printed prefix so two sign characters never adjoin + ;; (no "++", "+-", "--", "-+"). A sign in PRE was already + ;; absorbed into LHS above, so VAL carries the whole sum: for a + ;; negative result drop a sign PRE (int-to-string supplies the + ;; "-"), and for a positive result a PRE of "-" must flip to + ;; "+" (FOO-1+2 is FOO+1). A non-sign PRE (such as "(", "[", + ;; ":") is kept verbatim. For a zero result we keep PRE + ;; unchanged to preserve the long-standing "+0"/"-0" residual + ;; behaviour documented in the test cases below. + (setq prefix + (cond + ((< val 0) + (if (or (equal pre "+") (equal pre "-")) + "" ; val carries "-" + pre)) + ((> val 0) + (if (equal pre "-") + "+" ; FOO-1+2 is FOO+1 + pre)) + (t ; val == 0: keep old "+0"/"-0" + pre))) + (setq out (replace-match + (concat prefix (int-to-string val) post) + nil nil out)))) ;; Next precedence is >>,<< (while (string-match (concat "\\([[({:]\\)" ;; No << as not transitive @@ -11554,7 +11615,8 @@ This repairs those mis-inserted by an AUTOARG." ;;(verilog-simplify-range-expression "[(FOO*4+1-1)]") ; "[FOO*4+0]" ;;(verilog-simplify-range-expression "[(func(BAR))]") ; "[func(BAR)]" ;;(verilog-simplify-range-expression "[FOO-1+1-1+1]") ; "[FOO-0]" -;;(verilog-simplify-range-expression "[FOO-1+2:LSB-3+1]") ; "[FOO+1:LSB-1]" +;;(verilog-simplify-range-expression "[FOO-1+2:LSB-3+1]") ; "[FOO+1:LSB-2]" +;;(verilog-simplify-range-expression "[FOO+1-2:0]") ; "[FOO-1:0]" ;;(verilog-simplify-range-expression "[$clog2(2)]") ; "[1]" ;;(verilog-simplify-range-expression "[$clog2(7)]") ; "[3]" ;;(verilog-simplify-range-expression "[(TEST[1])-1:0]") ; "[(TEST[1])-1:0]" @@ -12377,6 +12439,21 @@ If PAR-VALUES replace final strings with these parameter values." "")) ;; Replace @ and [] magic variables in final output (setq tpl-net (verilog-string-replace-matches "@" tpl-num nil nil tpl-net)) + ;; "sig[].[idx]" connects one element of an unpacked array; keep only + ;; the unpacked index executable and put the ranges into a comment, + ;; e.g. "sig[].[2]" becomes "sig[2]/*[6:0].[2]*/". AUTOWIRE etc + ;; parse the comment back, see `verilog-read-sub-decls-expr'. + (while (string-match "\\[\\]\\.\\(\\[[^][]+\\]\\)" tpl-net) + (setq tpl-net (replace-match + (concat (match-string 1 tpl-net) + "/*" vl-mbits vl-bits + "." (match-string 1 tpl-net) "*/") + t t tpl-net))) + ;; "[].[]" is left when "[].[@]" is used on an instance whose name + ;; gives no @ value; catch it rather than emit a nonsense connection. + (when (string-match "\\[\\]\\.\\[\\]" tpl-net) + (verilog-warn-error "%s: AUTO_TEMPLATE `%s' expanded to an empty [].[] index; instance name has no value for @" + (verilog-point-text) tpl-net)) (setq tpl-net (verilog-string-replace-matches "\\[\\]\\[\\]" dflt-bits nil nil tpl-net)) (setq tpl-net (verilog-string-replace-matches "\\[\\]" auto-inst-vector-tpl nil nil tpl-net))) ;; Insert it @@ -12688,9 +12765,9 @@ Multiple Module Templates: For example: /* InstModule AUTO_TEMPLATE ( - .ptl_mapvalidx (ptl_mapvalid[@]), - .ptl_mapvalidp1x (ptl_mapvalid[@\"(% (+ 1 @) 4)\"]), - ); + .ptl_mapvalidx (ptl_mapvalid[@]), + .ptl_mapvalidp1x (ptl_mapvalid[@\"(% (+ 1 @) 4)\"]), + ); */ InstModule ms2m (/*AUTOINST*/); @@ -12698,17 +12775,42 @@ Multiple Module Templates: InstModule ms2m (/*AUTOINST*/ // Outputs - .ptl_mapvalidx (ptl_mapvalid[2]), - .ptl_mapvalidp1x (ptl_mapvalid[3])); + .ptl_mapvalidx (ptl_mapvalid[2]), + .ptl_mapvalidp1x (ptl_mapvalid[3])); Note the @ character was replaced with the 2 from \"ms2m\". + A connection of \"[].[@]\" connects one element of an unpacked + array. AUTOWIRE will declare the net as an unpacked array + spanning all connected indexes: + + /* InstModule AUTO_TEMPLATE ( + .ptl_mapvalidx (ptl_mapvalid[].[@]), + ); + */ + InstModule ms2m (/*AUTOINST*/); + InstModule ms0m (/*AUTOINST*/); + + Typing \\[verilog-auto] will make this into: + + InstModule ms2m (/*AUTOINST*/ + // Outputs + .ptl_mapvalidx (ptl_mapvalid[2]/*[3:0].[2]*/)); + InstModule ms0m (/*AUTOINST*/ + // Outputs + .ptl_mapvalidx (ptl_mapvalid[0]/*[3:0].[0]*/)); + + and AUTOWIRE will declare \"wire [3:0] ptl_mapvalid [0:2];\". + The indexes must be numeric for AUTOWIRE to combine them; + overlapping or duplicate indexes merge silently into the + covering range. + Alternatively, using a regular expression for @: /* InstModule AUTO_TEMPLATE \"_\\([a-z]+\\)\" ( - .ptl_mapvalidx (@_ptl_mapvalid), - .ptl_mapvalidp1x (ptl_mapvalid_@), - ); + .ptl_mapvalidx (@_ptl_mapvalid), + .ptl_mapvalidp1x (ptl_mapvalid_@), + ); */ InstModule ms2_FOO (/*AUTOINST*/); InstModule ms2_BAR (/*AUTOINST*/); @@ -12717,12 +12819,12 @@ Multiple Module Templates: InstModule ms2_FOO (/*AUTOINST*/ // Outputs - .ptl_mapvalidx (FOO_ptl_mapvalid), - .ptl_mapvalidp1x (ptl_mapvalid_FOO)); + .ptl_mapvalidx (FOO_ptl_mapvalid), + .ptl_mapvalidp1x (ptl_mapvalid_FOO)); InstModule ms2_BAR (/*AUTOINST*/ // Outputs - .ptl_mapvalidx (BAR_ptl_mapvalid), - .ptl_mapvalidp1x (ptl_mapvalid_BAR)); + .ptl_mapvalidx (BAR_ptl_mapvalid), + .ptl_mapvalidp1x (ptl_mapvalid_BAR)); Regexp Templates: commit 4c5790777ad8303f9773e824047163272bd50cd3 Author: Stefan Monnier Date: Sun Sep 27 17:49:48 2026 -0400 (uncomment-region-default-1): Fix corner case Once we remove the comment start marker, the comment end marker may fail to be recognized as a comment end marker since there is no comment to end any more. So make sure we analyse the comment end marker *before* we remove the comment start marker. * lisp/newcomment.el (uncomment-region-default-1): Call `comment-enter-backward` at the beginning. diff --git a/lisp/newcomment.el b/lisp/newcomment.el index 9ec02b0fe4b..fc5c5b31cc7 100644 --- a/lisp/newcomment.el +++ b/lisp/newcomment.el @@ -971,39 +971,44 @@ This function is the default value of `uncomment-region-function'." (box-equal nil)) ;Whether we might be using `=' for boxes. (save-restriction (narrow-to-region spt ept) - - ;; Remove the comment-start. - (goto-char ipt) - (skip-syntax-backward " ") - ;; A box-comment starts with a looong comment-start marker. - (when (and (or (and (= (- (point) (point-min)) 1) - (setq box-equal t) - (looking-at "=\\{7\\}") - (not (eq (char-before (point-max)) ?\n)) - (skip-chars-forward "=")) - (> (- (point) (point-min) (length comment-start)) 7)) - (> (count-lines (point-min) (point-max)) 2)) - (setq box t)) - ;; Skip the padding. Padding can come from comment-padding and/or - ;; from comment-start, so we first check comment-start. - (if (or (save-excursion (goto-char (point-min)) (looking-at csre)) - (looking-at (regexp-quote comment-padding))) + (goto-char (point-max)) + ;; Find the end-of-content before we mess with the rest of + ;; the comment (which flushes the syntax-propertization, + ;; thus impacting the work of `comment-enter-backward'). + (let ((end-of-content (copy-marker (comment-enter-backward)))) + + ;; Remove the comment-start. + (goto-char ipt) + (skip-syntax-backward " ") + ;; A box-comment starts with a looong comment-start marker. + (when (and (or (and (= (- (point) (point-min)) 1) + (setq box-equal t) + (looking-at "=\\{7\\}") + (not (eq (char-before (point-max)) ?\n)) + (skip-chars-forward "=")) + (> (- (point) (point-min) (length comment-start)) 7)) + (> (count-lines (point-min) (point-max)) 2)) + (setq box t)) + ;; Skip the padding. Padding can come from comment-padding and/or + ;; from comment-start, so we first check comment-start. + (if (or (save-excursion (goto-char (point-min)) (looking-at csre)) + (looking-at (regexp-quote comment-padding))) + (goto-char (match-end 0))) + (when (and sre (looking-at (concat "\\s-*\n\\s-*" srei))) (goto-char (match-end 0))) - (when (and sre (looking-at (concat "\\s-*\n\\s-*" srei))) - (goto-char (match-end 0))) - (if (null arg) (delete-region (point-min) (point)) - (let ((opoint (point-marker))) - (skip-syntax-backward " ") - (delete-char (- numarg)) - (unless (and (not (bobp)) - (save-excursion (goto-char (point-min)) - (looking-at comment-start-skip))) - ;; If there's something left but it doesn't look like - ;; a comment-start any more, just remove it. - (delete-region (point-min) opoint)))) - + (if (null arg) (delete-region (point-min) (point)) + (let ((opoint (point-marker))) + (skip-syntax-backward " ") + (delete-char (- numarg)) + (unless (and (not (bobp)) + (save-excursion (goto-char (point-min)) + (looking-at comment-start-skip))) + ;; If there's something left but it doesn't look like + ;; a comment-start any more, just remove it. + (delete-region (point-min) opoint)))) + + (goto-char end-of-content)) ;; Remove the end-comment (and leading padding and such). - (goto-char (point-max)) (comment-enter-backward) ;; Check for special `=' used sometimes in comment-box. (when (and box-equal (not (eq (char-before (point-max)) ?\n))) (let ((pos (point))) commit 5fed96be2971474c33507e4bc396693fe469d3dd Author: Arash Esbati Date: Sun Sep 27 22:40:14 2026 +0200 Limit search to end of value of label key * lisp/textmodes/reftex-vars.el (reftex-label-regexps): Limit search to the end of value of the label key and not to the end of optional argument. Otherwise, RefTeX will not recognize macros added to `reftex-label-alist' correctly. Issue reported: https://lists.gnu.org/archive/html/auctex/2025-03/msg00000.html * test/lisp/textmodes/reftex-tests.el (reftex-tests--key-val-label-regexp): New helper variable. (reftex-renumber-simple-labels) (reftex-key-val-label-in-macro-argument) (reftex-renumber-simple-labels-buffer): Use it. (reftex-key-val-label-in-macro-argument): New test. diff --git a/lisp/textmodes/reftex-vars.el b/lisp/textmodes/reftex-vars.el index e9793c86398..c40ae7a45d2 100644 --- a/lisp/textmodes/reftex-vars.el +++ b/lisp/textmodes/reftex-vars.el @@ -927,11 +927,10 @@ DOWNCASE t: Downcase words before using them." "}[^][]*\\)*" ;; Match the label key "\\ Date: Sun Sep 27 22:36:54 2026 +0200 ; Fix indentation in last change. diff --git a/test/src/emacs-module-tests.el b/test/src/emacs-module-tests.el index cf5984284d4..3bb116efcd9 100644 --- a/test/src/emacs-module-tests.el +++ b/test/src/emacs-module-tests.el @@ -598,10 +598,10 @@ See Bug#36226." (with-temp-file file (set-buffer-multibyte nil) (dotimes (_ (* width height)) - (insert (logand pixel #xff) - (logand (ash pixel -8) #xff) - (logand (ash pixel -16) #xff) - (logand (ash pixel -24) #xff)))))) + (insert (logand pixel #xff) + (logand (ash pixel -8) #xff) + (logand (ash pixel -16) #xff) + (logand (ash pixel -24) #xff)))))) (ert-deftest mod-test-canvas/valid () (skip-unless (image-type-available-p 'canvas)) commit b730ec99a59bc96ecd2850be5368c9b8ff59e9a4 Author: Basil L. Contovounesios Date: Sun Sep 27 22:20:54 2026 +0200 Clean up mod-test-canvas/file test * test/src/emacs-module-tests.el (test-canvas-gen-file): Rename... (emacs-module-tests--gen-canvas-file): ... to this, following file prefix convention. Take file name as argument. Insert bytes directly, rather than going through unibyte string roundtrip. (mod-test-canvas/file): Generate canvas as a temporary file, not under checked in test resource directory. Delete generated file after test. diff --git a/test/src/emacs-module-tests.el b/test/src/emacs-module-tests.el index 1c63d2f6f9d..cf5984284d4 100644 --- a/test/src/emacs-module-tests.el +++ b/test/src/emacs-module-tests.el @@ -592,17 +592,16 @@ See Bug#36226." ;;; Canvas tests -(defun test-canvas-gen-file (width height pixel) - (let* ((bytes (unibyte-string (logand pixel #xff) - (logand (ash pixel -8) #xff) - (logand (ash pixel -16) #xff) - (logand (ash pixel -24) #xff))) - (coding-system-for-write 'no-conversion)) - (with-temp-file "data/image/canvas-argb" +(defun emacs-module-tests--gen-canvas-file (file width height pixel) + "Fill FILE with WIDTH x HEIGHT canvas of PIXEL." + (let ((coding-system-for-write 'no-conversion)) + (with-temp-file file (set-buffer-multibyte nil) (dotimes (_ (* width height)) - (insert bytes))) - t)) + (insert (logand pixel #xff) + (logand (ash pixel -8) #xff) + (logand (ash pixel -16) #xff) + (logand (ash pixel -24) #xff)))))) (ert-deftest mod-test-canvas/valid () (skip-unless (image-type-available-p 'canvas)) @@ -683,22 +682,22 @@ See Bug#36226." (ert-deftest mod-test-canvas/file () (skip-unless (image-type-available-p 'canvas)) - ;; Generate the canvas data file - (test-canvas-gen-file 128 98 #x80800000) - (let* ((width 128) (height 98) - (canvas (create-image "../data/image/canvas-argb" - 'canvas nil - :data-width width :data-height height)) - (hash-before (mod-test-canvas-read canvas width height))) - (should (integerp hash-before)) - (should (mod-test-canvas-write canvas width height)) - (should (not (eql (mod-test-canvas-read canvas width height) hash-before)))) - ;; Mismatched sizes: passing wrong width/height should error. - (let ((canvas (create-image "../data/image/canvas-argb" - 'canvas nil - :data-width 128 :data-height 98))) - (should-error (mod-test-canvas-read canvas 28 76)) - (should-error (mod-test-canvas-write canvas 398 712)))) + (ert-with-temp-file file + (let ((width 128) (height 98)) + ;; Generate the canvas data file. + (emacs-module-tests--gen-canvas-file file width height #x80800000) + (let* ((canvas (create-image file 'canvas nil + :data-width width :data-height height)) + (hash-before (mod-test-canvas-read canvas width height))) + (should (integerp hash-before)) + (should (mod-test-canvas-write canvas width height)) + (should-not (eql (mod-test-canvas-read canvas width height) + hash-before))) + ;; Mismatched sizes: passing wrong width/height should error. + (let ((canvas (create-image file 'canvas nil + :data-width width :data-height height))) + (should-error (mod-test-canvas-read canvas 28 76)) + (should-error (mod-test-canvas-write canvas 398 712)))))) (ert-deftest mod-test-canvas/gc-stress () "Allocate canvases in batches with GC between batches. commit a5a81d387d203a548c123d537a6c0e3669986024 Author: Basil L. Contovounesios Date: Sun Sep 27 21:11:20 2026 +0200 ; Pacify some byte-compiler warnings in tests. diff --git a/test/lisp/emacs-lisp/uuid-tests.el b/test/lisp/emacs-lisp/uuid-tests.el index 7241aa1bf33..ee05c5ac142 100644 --- a/test/lisp/emacs-lisp/uuid-tests.el +++ b/test/lisp/emacs-lisp/uuid-tests.el @@ -47,7 +47,7 @@ (ert-deftest uuid-v4-generator-custom-rnd () "Verifies that a UUID-V4 uses the RND function provided to it." - (let ((id (uuid-v4 :rng (lambda (n) 23)))) + (let ((id (uuid-v4 :rng (lambda (_) 23)))) (should (cl-typep id '(uuid-v 4))) (should (equal (uuid--var id) 2)) (should (equal (uuid--ver id) 4)) @@ -105,7 +105,7 @@ "Verifies that a UUIDv7 uses random, timestamps correctly." (cl-letf (((symbol-function 'float-time) (lambda (&optional _) 0.123))) - (let ((id (uuid-v7 :rng (lambda (n) 789)))) + (let ((id (uuid-v7 :rng (lambda (_) 789)))) (should (cl-typep id '(uuid-v 7))) (should (equal (uuid--var id) 2)) (should (equal (uuid--ver id) 7)) diff --git a/test/lisp/files-tests.el b/test/lisp/files-tests.el index 7f87f7c8ac7..1a6b386d6c8 100644 --- a/test/lisp/files-tests.el +++ b/test/lisp/files-tests.el @@ -2520,7 +2520,7 @@ Prompt users for any modified buffer with `buffer-offer-save' non-nil." (ert-deftest files-tests--make-empty-file--tocttou () (ert-with-temp-directory base (let ((file (file-name-concat base "one" "two" "file.txt"))) - (cl-flet ((advice (&rest args) + (cl-flet ((advice (&rest _) (write-region "" nil file nil nil nil 'excl))) (unwind-protect (progn diff --git a/test/lisp/subr-tests.el b/test/lisp/subr-tests.el index b6e9eb92855..6856e42161f 100644 --- a/test/lisp/subr-tests.el +++ b/test/lisp/subr-tests.el @@ -1275,8 +1275,10 @@ final or penultimate step during initialization.")) (should (equal (kbd "C-x ( C-d C-x )") "\^D")) (should (equal (kbd "C-x ( C-x )") ""))) -(defvar subr-test--global) (ert-deftest test-local-set-state () + (defvar subr-test--global) + (defvar subr-test--local) + (defvar subr-test--unexist) (setq subr-test--global 1) (with-temp-buffer (setq-local subr-test--local 2) diff --git a/test/lisp/vc/vc-tests/vc-test-misc.el b/test/lisp/vc/vc-tests/vc-test-misc.el index 9d893dc6ffb..db76f1b265b 100644 --- a/test/lisp/vc/vc-tests/vc-test-misc.el +++ b/test/lisp/vc/vc-tests/vc-test-misc.el @@ -589,7 +589,6 @@ See bug#80803 and bug#80967." (skip-unless (executable-find vc-git-program)) (vc-test--with-author-identity 'Git (let ((vc-handled-backends '(Git)) - (def-dir default-directory) buffers) (unwind-protect (ert-with-temp-directory tempdir commit 8070ba18f7c4c0b93bcab38633945b5741a3b398 Author: Stefan Monnier Date: Sun Sep 27 14:28:43 2026 -0400 (gnus-undo-register): Use functions rather than ELisp forms * lisp/gnus/gnus-undo.el (gnus-undo-register): Add support for functions. * lisp/gnus/gnus-sum.el (gnus-group-make-articles-read) (gnus-update-read-articles): * lisp/gnus/gnus-group.el (gnus-info-clear-data) (gnus-group-kill-group, gnus-group-yank-group): Pass a function rather than an ELisp form to 'gnus-undo-register'. diff --git a/lisp/gnus/gnus-group.el b/lisp/gnus/gnus-group.el index d3ba67bbc54..f35f760746d 100644 --- a/lisp/gnus/gnus-group.el +++ b/lisp/gnus/gnus-group.el @@ -407,7 +407,6 @@ It is also possible to change and add form fields, but currently that requires an understanding of Lisp expressions. Hopefully this will change in a future release. For now, you can use the same variables in the Lisp expression as in `gnus-group-highlight'." - :group 'gnus-group-icons :type '(repeat (cons (sexp :tag "Form") file)) :risky t) @@ -1338,7 +1337,7 @@ if it is a string, only list groups matching REGEXP." (cl-union not-in-list (setq gnus-killed-list (sort gnus-killed-list #'string<)) - :test 'equal) + :test #'equal) gnus-level-killed ?K regexp)) (gnus-group-set-mode-line) @@ -1447,7 +1446,7 @@ if it is a string, only list groups matching REGEXP." "Force updating the group buffer tool bar." :group 'gnus-group :version "22.1" - :initialize 'custom-initialize-default + :initialize #'custom-initialize-default :set (lambda (symbol value) (set-default symbol value) (when (gnus-alive-p) @@ -3194,7 +3193,7 @@ non-nil SPECS arg must be an alist with `search-query-spec' and (cons 'nnselect-artlist nil))))))) (define-obsolete-function-alias 'gnus-group-make-nnir-group - 'gnus-group-read-ephemeral-search-group "28.1") + #'gnus-group-read-ephemeral-search-group "28.1") (defun gnus-group-read-ephemeral-search-group (no-parse &optional specs) "Read an nnselect group based on a search. @@ -3602,13 +3601,16 @@ Obeys the process/prefix convention." (push `(,(cdr el) add (,(car el))) action)) (push `(,(gnus-info-read info) add (read)) action) (gnus-undo-register - `(progn - (gnus-request-set-mark ,group ',action) - (gnus-info-set-marks ',info ',(gnus-info-marks info) t) - (setf (gnus-info-read ',info) ',(gnus-info-read info)) - (when (gnus-group-jump-to-group ,group) - (gnus-get-unread-articles-in-group ',info ',(gnus-active group) t) - (gnus-group-update-group-line)))) + (let ((gim (gnus-info-marks info)) + (gir (gnus-info-read info)) + (ga (gnus-active group))) + (lambda () + (gnus-request-set-mark group action) + (gnus-info-set-marks info gim t) + (setf (gnus-info-read info) gir) + (when (gnus-group-jump-to-group group) + (gnus-get-unread-articles-in-group info ga t) + (gnus-group-update-group-line))))) (setq action (mapcar (lambda (el) (list (nth 0 el) 'del (nth 2 el))) action)) (gnus-request-set-mark group action) @@ -3955,9 +3957,10 @@ of groups killed." (when (and (not discard) (setq entry (gnus-group-entry group))) (gnus-undo-register - `(progn - (gnus-group-goto-group ,(gnus-group-group-name)) - (gnus-group-yank-group))) + (let ((gn (gnus-group-group-name))) + (lambda () + (gnus-group-goto-group gn) + (gnus-group-yank-group)))) (push (cons (car entry) (nth 1 entry)) gnus-list-of-killed-groups)) (gnus-group-change-level @@ -4019,8 +4022,10 @@ yanked) a list of yanked groups is returned." (gnus-group-insert-group-line-info group) (gnus-request-update-group-status group 'subscribe) (gnus-undo-register - `(when (gnus-group-goto-group ,group) - (gnus-group-kill-group 1)))) + (let ((group group)) + (lambda () + (when (gnus-group-goto-group group) + (gnus-group-kill-group 1)))))) (forward-line -1) (gnus-group-position-point) (if (< (length out) 2) (car out) (nreverse out)))) diff --git a/lisp/gnus/gnus-sum.el b/lisp/gnus/gnus-sum.el index cf635db0f02..8b62184e77c 100644 --- a/lisp/gnus/gnus-sum.el +++ b/lisp/gnus/gnus-sum.el @@ -866,9 +866,9 @@ VALUE should have the form `(FOO nil)' or `(FOO t)', where FOO is an atom. (define-widget 'gnus-widget-reversible 'group "A `group' that convert values." - :match 'gnus-widget-reversible-match - :value-to-internal 'gnus-widget-reversible-to-internal - :value-to-external 'gnus-widget-reversible-to-external) + :match #'gnus-widget-reversible-match + :value-to-internal #'gnus-widget-reversible-to-internal + :value-to-external #'gnus-widget-reversible-to-external) (defcustom gnus-article-sort-functions '(gnus-article-sort-by-number) "List of functions used for sorting articles in the summary buffer. @@ -6337,15 +6337,21 @@ The resulting hash table is returned, or nil if no Xrefs were found." (setq range (gnus-compute-read-articles group articles)) (with-current-buffer gnus-group-buffer (gnus-undo-register - `(progn - (gnus-info-set-marks ',info ',(gnus-info-marks info) t) - (setf (gnus-info-read ',info) ',(gnus-info-read info)) - (gnus-get-unread-articles-in-group ',info (gnus-active ,group)) - (when ,set-marks + ;; FIXME: In `gnus-info-clear-data' we do almost the same, + ;; but we call (gnus-active group) when build the closure rather + ;; than when calling it! + (let ((gim (gnus-info-marks info)) + (gir (gnus-info-read info)) + (range range)) + (lambda () + (gnus-info-set-marks info gim t) + (setf (gnus-info-read info) gir) + (gnus-get-unread-articles-in-group info (gnus-active group)) + (when set-marks (gnus-request-set-mark - ,group (list (list ',range 'del '(read))))) - (gnus-group-jump-to-group ,group) - (gnus-group-update-group ,group t)))) + group (list (list range 'del '(read))))) + (gnus-group-jump-to-group group) + (gnus-group-update-group group t))))) ;; Add the read articles to the range. (setf (gnus-info-read info) range) (when set-marks @@ -6361,9 +6367,9 @@ The resulting hash table is returned, or nil if no Xrefs were found." (car range))))) (t (while range - (if (numberp (car range)) - (setq num (1+ num)) - (setq num (+ num (- (1+ (cdar range)) (caar range))))) + (setq num (if (numberp (car range)) + (1+ num) + (+ num (- (1+ (cdar range)) (caar range))))) (setq range (cdr range))) (setq num (- (cdr active) num)))) ;; Update the number of unread articles. @@ -8507,10 +8513,11 @@ Returns how many articles were removed." (completing-read "Marks: " (let ((mark-list '())) (mapc (lambda (datum) - (cl-pushnew (gnus-data-mark datum) mark-list)) + (cl-pushnew (gnus-data-mark datum) mark-list)) gnus-newsgroup-data) - (mapcar 'char-to-string mark-list))) - current-prefix-arg) gnus-summary-mode) + (mapcar #'char-to-string mark-list))) + current-prefix-arg) + gnus-summary-mode) (gnus-summary-limit-to-marks marks t)) (defun gnus-summary-limit-to-marks (marks &optional reverse) @@ -8524,10 +8531,11 @@ Returns how many articles were removed." (completing-read "Marks: " (let ((mark-list '())) (mapc (lambda (datum) - (cl-pushnew (gnus-data-mark datum) mark-list)) + (cl-pushnew (gnus-data-mark datum) mark-list)) gnus-newsgroup-data) - (mapcar 'char-to-string mark-list))) - current-prefix-arg) gnus-summary-mode) + (mapcar #'char-to-string mark-list))) + current-prefix-arg) + gnus-summary-mode) (prog1 (let ((data gnus-newsgroup-data) (marks (if (listp marks) marks @@ -8655,7 +8663,7 @@ fetched for this group." (cl-merge 'list gnus-newsgroup-headers (gnus-fetch-headers articles nil t) - 'gnus-article-sort-by-number)) + #'gnus-article-sort-by-number)) (setq gnus-newsgroup-articles (gnus-sorted-nunion gnus-newsgroup-articles articles)) (gnus-summary-limit (append articles gnus-newsgroup-limit)))) @@ -9075,7 +9083,7 @@ is non-numeric or nil fetch the number specified by the (setq gnus-newsgroup-headers (gnus-delete-duplicate-headers (cl-merge 'list gnus-newsgroup-headers new-headers - 'gnus-article-sort-by-number))) + #'gnus-article-sort-by-number))) (setq gnus-newsgroup-articles (gnus-sorted-nunion gnus-newsgroup-articles article-ids))) (gnus-summary-limit-include-thread id gnus-refer-thread-limit-to-thread)) @@ -12832,21 +12840,26 @@ UNREAD is a sorted list." group (delq nil (list (if add (list add 'add '(read))) (if del (list del 'del '(read)))))) (setq setmarkundo - `(gnus-request-set-mark - ,group - ',(delq nil (list - (if del (list del 'add '(read))) - (if add (list add 'del '(read)))))))))) + (delq nil (list + (if del (list del 'add '(read))) + (if add (list add 'del '(read))))))))) (set-buffer gnus-group-buffer) (gnus-undo-register - `(progn - (gnus-info-set-marks ',info ',(gnus-info-marks info) t) - (setf (gnus-info-read ',info) ',(gnus-info-read info)) - (gnus-group-jump-to-group ,group) - (gnus-get-unread-articles-in-group ',info - (gnus-active ,group)) - (gnus-group-update-group ,group t) - ,setmarkundo)))) + ;; FIXME: In `gnus-info-clear-data' we do almost the same, + ;; but we call (gnus-active group) when build the closure rather + ;; than when calling it! + (let ((gim (gnus-info-marks info)) + (gir (gnus-info-read info))) + (lambda () + (gnus-info-set-marks info gim t) + (setf (gnus-info-read info) gir) + (gnus-group-jump-to-group group) + (gnus-get-unread-articles-in-group info + (gnus-active group)) + (gnus-group-update-group group t) + (when setmarkundo + (gnus-request-set-mark + group setmarkundo))))))) ;; Enter this list into the group info. (setf (gnus-info-read info) read) ;; Set the number of unread articles in gnus-newsrc-hashtb. @@ -13030,7 +13043,7 @@ returned." (cl-merge 'list gnus-newsgroup-headers (gnus-fetch-headers articles nil t) - 'gnus-article-sort-by-number)) + #'gnus-article-sort-by-number)) (setq gnus-newsgroup-articles (gnus-sorted-nunion gnus-newsgroup-articles articles)) ;; Suppress duplicates? diff --git a/lisp/gnus/gnus-undo.el b/lisp/gnus/gnus-undo.el index 9f6c6524c32..eb88b49c2c9 100644 --- a/lisp/gnus/gnus-undo.el +++ b/lisp/gnus/gnus-undo.el @@ -127,7 +127,8 @@ "Register FORMS as something to be performed to undo a change." (when gnus-undo-mode (gnus-undo-register-1 - `(lambda () ,form)))) + (if (functionp form) form + (eval `(lambda () ,form)))))) (defun gnus-undo-register-1 (function) "Register FUNCTION as something to be performed to undo a change." commit 008a667120408ac53c9690c0c4cb12dd94ff1138 Author: Stefan Monnier Date: Sun Sep 27 14:19:52 2026 -0400 (make-autoload): Fix regression introduced by commit 085e2d33a3a3 * lisp/obsolete/autoload.el (make-autoload): Re-establish its old arg-list. * lisp/emacs-lisp/loaddefs-gen.el (loaddefs-generate--make-autoload): Handle the case where FILE is nil. Fixes bug#81906. diff --git a/lisp/emacs-lisp/loaddefs-gen.el b/lisp/emacs-lisp/loaddefs-gen.el index 2a230dd864a..6c1e60d12d0 100644 --- a/lisp/emacs-lisp/loaddefs-gen.el +++ b/lisp/emacs-lisp/loaddefs-gen.el @@ -205,22 +205,28 @@ expand)' among their `declare' forms." (when exps (cons 'progn exps))))) ;; For macros which request it, try again on their expansion. - ((progn + ((let ((file-name (or file load-name))) ;; If the car is an unknown symbol, we load the file first to ;; give packages a chance to define their macros. (unless (or (not (symbolp car)) (fboundp car) ;; Special cases handled below (memq car '(defclass defcustom deftheme defgroup nil)) - (assoc file load-history) - (member file loaddefs--load-error-files)) - (let ((load-path (cons (file-name-directory file) load-path))) + (assoc file-name load-history) + (member file-name loaddefs--load-error-files)) + ;; FIXME: We used to tweak `load-path' because we passed + ;; LOAD-NAME to `load', but now that we have the full FILE, + ;; we probably should refrain from doing it, since it can have + ;; undesirable consequences. + (let ((load-path (if file (cons (file-name-directory file) load-path) + load-path))) (message "loaddefs-gen: loading file %s (for %s)" load-name car) (condition-case err ;; Don't load the `.elc' file, in case the file wraps ;; the macro-definition in `eval-when-compile' (bug#80180). - (load file nil nil 'nosuffix) + (if file (load file nil nil 'nosuffix) + (eval-buffer)) (error - (push file loaddefs--load-error-files) ; do not attempt again + (push file-name loaddefs--load-error-files) ;Do not try again, (warn "loaddefs-gen: load error for %s:\n\t%S" load-name err))))) (and (macrop car) (eq 'expand (function-get car 'autoload-macro 'macro)) diff --git a/lisp/obsolete/autoload.el b/lisp/obsolete/autoload.el index 0ce1b98b8dd..124b0eb147b 100644 --- a/lisp/obsolete/autoload.el +++ b/lisp/obsolete/autoload.el @@ -95,7 +95,8 @@ then we use the timestamp of the output file instead. As a result: (defvar autoload-modified-buffers) ;Dynamically scoped var. -(defalias 'make-autoload #'loaddefs-generate--make-autoload) +(defun make-autoload (form load-name &optional expansion) + (loaddefs-generate--make-autoload form load-name nil expansion)) ;; Forms which have doc-strings which should be printed specially. ;; A doc-string-elt property of ELT says that (nth ELT FORM) is commit a5504ed1c1bb706b6c01326722469017e4b5ea02 Author: Basil L. Contovounesios Date: Sun May 31 11:13:36 2026 +0200 Improve some D-Bus markup and examples * doc/misc/dbus.texi (Asynchronous Methods): * lisp/net/dbus.el (dbus-call-method-asynchronously): Prefer evaluated over quoted lambdas in examples where either will do. (dbus-register-method): Consistently unquote metasyntactic object shapes in docstrings. diff --git a/doc/misc/dbus.texi b/doc/misc/dbus.texi index d63e26755d9..07b815778c8 100644 --- a/doc/misc/dbus.texi +++ b/doc/misc/dbus.texi @@ -1386,8 +1386,8 @@ The return value of @samp{org.freedesktop.portal.Settings.ReadOne} is a variant. :session "org.freedesktop.portal.Desktop" "/org/freedesktop/portal/desktop" "org.freedesktop.portal.Settings" "ReadOne" - '((lambda (msg) (message "Method handler %s" msg)) . - (lambda (err) (message "Error handler %s" err))) + (cons (lambda (msg) (message "Method handler %s" msg)) + (lambda (err) (message "Error handler %s" err))) "org.freedesktop.appearance" "color-scheme") @print{} Method handler (0) @@ -1402,8 +1402,8 @@ There does not exist a method @samp{org.freedesktop.portal.Settings.ReadTwo}. :session "org.freedesktop.portal.Desktop" "/org/freedesktop/portal/desktop" "org.freedesktop.portal.Settings" "ReadTwo" - '((lambda (msg) (message "Method handler %s" msg)) . - (lambda (err) (message "Error handler %s" err))) + (cons (lambda (msg) (message "Method handler %s" msg)) + (lambda (err) (message "Error handler %s" err))) "org.freedesktop.appearance" "color-scheme") @print{} Error handler diff --git a/lisp/net/dbus.el b/lisp/net/dbus.el index c9de9d75bbe..234b9022982 100644 --- a/lisp/net/dbus.el +++ b/lisp/net/dbus.el @@ -450,7 +450,7 @@ call as arguments. These are the same arguments which are returned when `dbus-call-method' is invoked instead. If HANDLER is nil, no return message will be expected. -HANDLER can also be the cons cell `(HANDLER . ERROR-HANDLER)'. In this +HANDLER can also be the cons cell (HANDLER . ERROR-HANDLER). In this case, ERROR-HANDLER will be called in case an error is returned from D-Bus. It uses the returned D-Bus error as argument. @@ -494,8 +494,8 @@ The return value of \"org.freedesktop.portal.Settings.ReadOne\" is a variant. :session \"org.freedesktop.portal.Desktop\" \"/org/freedesktop/portal/desktop\" \"org.freedesktop.portal.Settings\" \"ReadOne\" - \\='((lambda (msg) (message \"Method handler %s\" msg)) . - (lambda (err) (message \"Error handler %s\" err))) + (cons (lambda (msg) (message \"Method handler %s\" msg)) + (lambda (err) (message \"Error handler %s\" err))) \"org.freedesktop.appearance\" \"color-scheme\") -| Method handler (0) @@ -508,8 +508,8 @@ There does not exist a method \"org.freedesktop.portal.Settings.ReadTwo\". :session \"org.freedesktop.portal.Desktop\" \"/org/freedesktop/portal/desktop\" \"org.freedesktop.portal.Settings\" \"ReadTwo\" - \\='((lambda (msg) (message \"Method handler %s\" msg)) . - (lambda (err) (message \"Error handler %s\" err))) + (cons (lambda (msg) (message \"Method handler %s\" msg)) + (lambda (err) (message \"Error handler %s\" err))) \"org.freedesktop.appearance\" \"color-scheme\") -| Error handler @@ -949,8 +949,8 @@ message. If HANDLER returns a reply message with an empty argument list, HANDLER must return the keyword `:ignore' in order to distinguish it from nil (the boolean false). -If HANDLER detects an error, it shall return the list `(:error -ERROR-NAME ERROR-MESSAGE)'. ERROR-NAME is a namespaced string +If HANDLER detects an error, it shall return the list (:error +ERROR-NAME ERROR-MESSAGE). ERROR-NAME is a namespaced string which characterizes the error type, and ERROR-MESSAGE is a free text string. Alternatively, any Emacs signal `dbus-error' in HANDLER raises a D-Bus error message with the error name commit bde4e21f3eec3cfdab5600b0987b4f590e3997c4 Merge: 42c58524484 9e5d32b597f Author: Eli Zaretskii Date: Sun Sep 27 09:44:16 2026 -0400 Merge from origin/emacs-31 9e5d32b597f Avoid infloop in redisplay when window includes a tall image b4fdff95b3e ; Fix last change 33cb7fe6dd7 Fix speedbar window behavior when togling side windows (b... 7ea48dd0ab3 ; Minor Tramp cleanup 95d4620c07b ; FIXME. commit 9e5d32b597f4361951b7af84a5895267af48ea5a Author: Eli Zaretskii Date: Sun Sep 27 16:38:40 2026 +0300 Avoid infloop in redisplay when window includes a tall image * src/xdisp.c (try_scrolling): Patch logic when the bottom-most row is very tall. (Bug#81931) diff --git a/src/xdisp.c b/src/xdisp.c index 66ae3d84b5b..166376a6370 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -19502,6 +19502,11 @@ try_scrolling (Lisp_Object window, bool just_this_one_p, if (dy > 0) scroll_down_p = true; } + else if (scroll_margin_y < 0) + { + /* A tall row (like tall image?) at window's bottom. */ + scroll_down_p = true; + } } if (scroll_down_p) commit 42c585244848f132c896e99b163e30b1ad6f9adc Author: Basil L. Contovounesios Date: Wed May 6 22:34:55 2026 +0200 Tweak w32 tests for directory-files-recursively * test/lisp/files-tests.el (files-tests-directory-files-recursively-w32): Simplify 'butlast' call in terms of 'take' (bug#80915). diff --git a/test/lisp/files-tests.el b/test/lisp/files-tests.el index de17a4d4e9f..7f87f7c8ac7 100644 --- a/test/lisp/files-tests.el +++ b/test/lisp/files-tests.el @@ -884,8 +884,6 @@ unquoted file names." ;; Allow the directory to be deleted. (set-file-modes d-b #o700 'nofollow))))) -(defvar w32-downcase-file-names) - (ert-deftest files-tests-directory-files-recursively-w32 () "Test MS-Windows specific features of `directory-files-recursively'." (skip-unless (eq system-type 'windows-nt)) @@ -910,15 +908,16 @@ unquoted file names." ;; Run them through 'directory-file-name' because the list above ;; uses directory names, whereas 'directory-files-recursively' ;; returns their file names. - (should (equal (mapcar 'directory-file-name files-list) + (should (equal (mapcar #'directory-file-name files-list) ;; Make the returned file names relative. (mapcar fnrel (directory-files-recursively tmpdir ".*" t)))) ;; Test that 'directory-files-recursively' downcases file names it ;; returns when 'w32-downcase-file-names' is non-nil. + (defvar w32-downcase-file-names) (let ((w32-downcase-file-names t)) - (should (equal (mapcar 'downcase - (mapcar 'directory-file-name files-list)) + (should (equal (mapcar #'downcase + (mapcar #'directory-file-name files-list)) (mapcar fnrel (directory-files-recursively tmpdir ".*" t))))) ;; Test that backslashes are mirrored when files are returned. @@ -928,11 +927,10 @@ unquoted file names." (subdir (file-name-concat (concat tmpdir-with-backslashes "\\") (string-replace "/" "\\" (nth nelt files-list))))) - (should (equal (mapcar 'directory-file-name - (butlast files-list (- (length files-list) - nelt))) + (should (equal (mapcar #'directory-file-name + (take nelt files-list)) (mapcar fnrel - (directory-files-recursively subdir ".*" t)))))))) + (directory-files-recursively subdir ".*" t)))))))) (ert-deftest files-tests-file-name-non-special-dired-compress-handler () ;; `dired-compress-file' can get confused by filenames with ":" in commit cc7a4dfbf175a361aecb4c0696ded20e02d6a8aa Author: Basil L. Contovounesios Date: Tue Mar 31 22:14:42 2026 +0200 Improve directory-files-recursively This affords control over its return value's sort order, and fixes some edge cases in the default lexicographic sorting (bug#80915). It also fixes cases where the returned file names were not absolute. For some prior discussion, see: https://lists.gnu.org/r/emacs-devel/2014-12/msg01046.html * etc/NEWS (Lisp Changes in Emacs 32.1): Announce new lessp parameter of directory-files-recursively. * doc/lispref/files.texi (Contents of Directories): Consistently write 'subdirectory' without a hyphen. Clarify which part of directory names is used by different directory-files-recursively arguments. Document its new lessp parameter. * doc/lispref/sequences.texi (Sequence Functions): Add anchor for cross-referencing the sort function. * lisp/files.el (files--recursively): New auxiliary function comprising the recursive body of... (directory-files-recursively): ...this, with the directory argument already expanded as a directory name, tramp-mode already set according to file-remote-p, and completion-regexp-list overridden to avoid its influence. Ensure returned file names are absolute, as promised. Sort directories by their file name, not directory name, by default. Add optional parameter lessp which allows sorting by a custom predicate or not at all. Call file-symlink-p at most, not at least, once. Perform a single nconc traversal for subdirectories even with include-directories non-nil. Avoid altering match data. Clarify docstring. * test/lisp/files-tests.el: Remove redundant require. (files-tests-directory-files-recursively) (files-tests-directory-files-recursively-link) (files-tests-directory-files-recursively-expand) (files-tests-directory-files-recursively-error): New tests (bug#28567, bug#36490, bug#80915). diff --git a/doc/lispref/files.texi b/doc/lispref/files.texi index 28c43c08ad8..e6296d0043f 100644 --- a/doc/lispref/files.texi +++ b/doc/lispref/files.texi @@ -3113,30 +3113,36 @@ See @var{file-symlink-p} to distinguish symlinks. @end defun @cindex recursive traverse of directory tree -@defun directory-files-recursively directory regexp &optional include-directories predicate follow-symlinks +@defun directory-files-recursively directory regexp &optional include-directories predicate follow-symlinks lessp Return all files under @var{directory} whose names match @var{regexp}. This function searches the specified @var{directory} and its -sub-directories, recursively, for files whose basenames (i.e., without -the leading directories) match the specified @var{regexp}, and returns -a list of the absolute file names of the matching files -(@pxref{Relative File Names, absolute file names}). The file names -are returned in depth-first order, meaning that files in some -sub-directory are returned before the files in its parent directory. -In addition, matching files found in each subdirectory are sorted -alphabetically by their basenames. By default, directories whose -names match @var{regexp} are omitted from the list, but if the +subdirectories, recursively, for files whose basenames (i.e., without +the leading directories) match the specified @var{regexp}, and returns a +list of the absolute file names of the matching files (@pxref{Relative +File Names, absolute file names}). The file names are returned in +depth-first order, meaning that files in some subdirectory are returned +before the files in its parent directory. By default, directories whose +basenames match @var{regexp} are omitted from the list, but if the optional argument @var{include-directories} is non-@code{nil}, they are included. By default, all subdirectories are descended into. If @var{predicate} is @code{t}, errors when trying to descend into a subdirectory (for instance, if it's not readable by this user) are ignored. If it's -neither @code{nil} nor @code{t}, it should be a function that takes -one parameter (the subdirectory name) and should return non-@code{nil} +neither @code{nil} nor @code{t}, it should be a function that takes one +parameter (the subdirectory's absolute name) and returns non-@code{nil} if the directory is to be descended into. Symbolic links to subdirectories are not followed by default, but if @var{follow-symlinks} is non-@code{nil}, they are followed. + +By default, the file names returned are sorted lexicographically by +basename per subdirectory. If @var{lessp} is @code{t}, they are not +sorted and appear in arbitrary order in each subdirectory. If +@var{lessp} is neither @code{nil} nor @code{t}, it should be a function +suitable as a @code{:lessp} predicate for @code{sort} (@pxref{definition +of sort}), that takes two arguments (two file basenames) and returns +non-@code{nil} if the first should precede the second. @end defun diff --git a/doc/lispref/sequences.texi b/doc/lispref/sequences.texi index afee255346c..2467c924d0d 100644 --- a/doc/lispref/sequences.texi +++ b/doc/lispref/sequences.texi @@ -350,6 +350,7 @@ encouraged to treat strings as immutable even when they are mutable. @end defun +@anchor{definition of sort} @defun sort sequence &rest keyword-args @cindex stable sort @cindex sorting lists diff --git a/etc/NEWS b/etc/NEWS index 5cf10dbb92f..5a3248aaf94 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -573,6 +573,16 @@ alphabetic abbreviations like "CST", which are too often ambiguous. The declaration '(font-lock-keyword t)' specifies that a normal function is to be highlighted by font-lock like a macro. ++++ +** New optional LESSP parameter for 'directory-files-recursively'. +It allows control over the relative order of file names per directory +in the resulting list. The default remains sorting each directory +according to 'string-lessp'. A value of t returns files in their +arbitrary directory order without sorting, which may be faster. +The argument can also be a function of two parameters suitable as the +':lessp' argument of 'sort'. For example, 'string-collate-lessp' would +result in the locale-specific collation order one might see in Dired. + * Changes in Emacs 32.1 on Non-Free Operating Systems diff --git a/lisp/files.el b/lisp/files.el index e0d510df51b..3ed40e355ce 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -1027,64 +1027,120 @@ The path separator is colon in GNU and GNU-like systems." (error "No such directory found via CDPATH environment variable: %s" dir) (error "No such directory: %s" dir))))) +(defun files--recursively (dir regexp + &optional include-directories predicate + follow-symlinks lessp) + "Recursive body of `directory-files-recursively'. +Expects DIR to be an absolute directory (not file) name, +and `tramp-mode' and `completion-regexp-list' to be set +appropriately for `file-name-all-completions'." + (let ((result ()) ;; Subdirectories and their files, followed by... + (files ()) ;; ...the current directory's regular files. + (predfn (and (not (booleanp predicate)) predicate)) + (comps (file-name-all-completions "" dir))) + ;; By returning directory names, `file-name-all-completions' is + ;; faster than `directory-files' followed by `file-directory-p'. + ;; However, the trailing slash of directory names affects sort + ;; order, e.g., it can put "foo-bar/" before "foo/". Since + ;; `file-name-all-completions' uses `file-name-as-directory', it is + ;; safe, faster, and more predictable to use `substring' in place of + ;; `directory-file-name'. In fact, it is faster to compute the + ;; `substring' twice (first as a sortkey, then for REGEXP) than to + ;; do it once and save the result in a temporary object. + (dolist (file (if (eq lessp t) comps + (sort comps :in-place t :lessp lessp + ;; Normalize directory names as file names. + :key (lambda (f) + (if (directory-name-p f) + (substring f nil -1) + f))))) + (cond ((member file '("./" "../"))) + ;; When FILE is of the form "foo/". + ((directory-name-p file) + (let* (;; Nondir part of directory file name, + ;; of the form "foo". + (name (substring file nil -1)) + ;; Absolute directory file name, of the form + ;; "/dir/foo". Cheaper than `expand-file-name', and + ;; avoids pitfalls with files named, e.g., "~". + (full-file (concat dir name)) + ;; List of subdirectory's files. + (sub-files + (and + ;; Don't follow symlinks to other directories. + (or follow-symlinks + (not (file-symlink-p full-file))) + ;; Allow filtering subdirectories. + (or (not predfn) + (funcall predfn full-file)) + (if (eq predicate t) + (ignore-error file-error + (files--recursively + (file-name-as-directory full-file) regexp + include-directories predicate + follow-symlinks lessp)) + (files--recursively + (file-name-as-directory full-file) regexp + include-directories predicate + follow-symlinks lessp)))) + ;; Whether to list subdirectory itself. + (sub-dir (and include-directories + (string-match-p regexp name) + (list full-file)))) + ;; Skip any no-op `nconc' traversals. + (when (or sub-files sub-dir) + (setq result (nconc result sub-files sub-dir))))) + ;; When FILE is of the form "foo". + ((string-match-p regexp file) + (push (concat dir file) files)))) + (nconc result (nreverse files)))) + (defun directory-files-recursively (dir regexp &optional include-directories predicate - follow-symlinks) + follow-symlinks lessp) "Return list of all files under directory DIR whose names match REGEXP. -This function works recursively. Files are returned in \"depth -first\" order, and files from each directory are sorted in -alphabetical order. Each file name appears in the returned list -in its absolute form. +This function works recursively, with files returned in \"depth +first\" (bottom-up) order. Each file name whose non-directory part +matches REGEXP appears in the returned list in its absolute form. By default, the returned list excludes directories, but if -optional argument INCLUDE-DIRECTORIES is non-nil, they are -included. +optional argument INCLUDE-DIRECTORIES is non-nil, it means +consider them for inclusion. PREDICATE can be either nil (which means that all subdirectories of DIR are descended into), t (which means that subdirectories that can't be read are ignored), or a function (which is called with -the name of each subdirectory, and should return non-nil if the -subdirectory is to be descended into). +the absolute name of each subdirectory, and should return non-nil +if the subdirectory is to be descended into). If FOLLOW-SYMLINKS is non-nil, symbolic links that point to directories are followed. Note that this can lead to infinite -recursion." - (let* ((result nil) - (files nil) - (dir (directory-file-name dir)) - ;; When DIR is "/", remote file names like "/method:" could - ;; also be offered. We shall suppress them. - (tramp-mode (and tramp-mode (file-remote-p (expand-file-name dir))))) - (dolist (file (sort (file-name-all-completions "" dir) - 'string<)) - (unless (member file '("./" "../")) - (if (directory-name-p file) - (let* ((leaf (substring file 0 (1- (length file)))) - (full-file (concat dir "/" leaf))) - ;; Don't follow symlinks to other directories. - (when (and (or (not (file-symlink-p full-file)) - (and (file-symlink-p full-file) - follow-symlinks)) - ;; Allow filtering subdirectories. - (or (eq predicate nil) - (eq predicate t) - (funcall predicate full-file))) - (let ((sub-files - (if (eq predicate t) - (ignore-error file-error - (directory-files-recursively - full-file regexp include-directories - predicate follow-symlinks)) - (directory-files-recursively - full-file regexp include-directories - predicate follow-symlinks)))) - (setq result (nconc result sub-files)))) - (when (and include-directories - (string-match regexp leaf)) - (setq result (nconc result (list full-file))))) - (when (string-match regexp file) - (push (concat dir "/" file) files))))) - (nconc result (nreverse files)))) +recursion. + +LESSP determines the relative order of file names per directory in the +returned list. When nil, it defaults to lexicographic order as per +`string-lessp'. When t, files in each directory are not sorted, and +their order is arbitrary. Otherwise, it is a function suitable as the +`:lessp' argument of `sort', that takes two arguments (the non-directory +part of two file names) and returns non-nil if the first should precede +the second. For example, `string-collate-lessp' gives the +locale-specific collation order one might see in Dired." + ;; Set up some (potentially expensive) invariants before recursing. + (let* (;; As requested by `files--recursively'. + (dir (if (directory-name-p dir) dir (file-name-as-directory dir))) + ;; Must be absolute for the `file-remote-p' that follows, and + ;; so that the final result comprises absolute names even when + ;; the input is relative. But skip `expand-file-name' if + ;; possible, for speed and symmetry with the input. + (dir (if (file-name-absolute-p dir) dir (expand-file-name dir))) + ;; When DIR is "/", remote file names like "/method:" could + ;; also be offered. We shall suppress them. + (tramp-mode (and tramp-mode (file-remote-p dir))) + ;; Avoid its influence on `file-name-all-completions', + ;; which we use in place of `directory-files' for speed. + (completion-regexp-list ())) + (files--recursively dir regexp include-directories + predicate follow-symlinks lessp))) (defun directory-empty-p (dir) "Return t if DIR names an existing directory containing no other files. diff --git a/test/lisp/files-tests.el b/test/lisp/files-tests.el index ccfed997734..de17a4d4e9f 100644 --- a/test/lisp/files-tests.el +++ b/test/lisp/files-tests.el @@ -21,7 +21,6 @@ (require 'ert) (require 'ert-x) -(require 'nadvice) (eval-when-compile (require 'cl-lib)) (require 'bytecomp) ; `byte-compiler-base-file-name'. (require 'dired) ; `dired-uncache'. @@ -32,7 +31,7 @@ (defvar files-test-result nil) (defvar files-test-safe-result nil) -(put 'files-test-safe-result 'safe-local-variable 'booleanp) +(put 'files-test-safe-result 'safe-local-variable #'booleanp) (defun files-test-fun1 () (setq files-test-result t)) @@ -696,6 +695,194 @@ unquoted file names." (files-tests--with-temp-non-special-and-file-name-handler (tmpdir nospecial-dir t) (should-error (directory-files-and-attributes nospecial-dir)))) +(ert-deftest files-tests-directory-files-recursively () + "Test `directory-files-recursively' behavior." + (ert-with-temp-directory dir + (let* ((rel (lambda (f) (file-relative-name f dir))) + (tree '("d/0" "d/1" "d/a" "d/b" "d" + "d-a/0" "d-a/1" "d-a/a" "d-a/b" "d-a" + "d-b/0" "d-b/1" "d-b/a" "d-b/b" "d-b" + "0" "1" "a" "b")) + ;; The same but without intermediate subdirs. + (leaves (seq-difference tree '("d" "d-a" "d-b")))) + (dolist (file leaves) + (make-empty-file (expand-file-name file dir) t)) + + ;; Returned leaves are sorted and absolute. + (let ((all (directory-files-recursively dir (rx)))) + (should (all #'file-name-absolute-p all)) + (should (equal (mapcar rel all) leaves))) + + ;; Returned tree is sorted and absolute. + (let ((all (directory-files-recursively dir (rx) t))) + (should (all #'file-name-absolute-p all)) + (should (equal (mapcar rel all) tree))) + + ;; Sort order can be changed. + (let ((all (directory-files-recursively dir (rx) nil nil nil t))) + (should (seq-set-equal-p (mapcar rel all) leaves))) + (let ((all (directory-files-recursively dir (rx) t nil nil t))) + (should (seq-set-equal-p (mapcar rel all) tree))) + (let ((all (directory-files-recursively dir (rx) nil nil nil #'string>))) + (should (equal (mapcar rel all) + '("d-b/b" "d-b/a" "d-b/1" "d-b/0" + "d-a/b" "d-a/a" "d-a/1" "d-a/0" + "d/b" "d/a" "d/1" "d/0" + "b" "a" "1" "0")))) + (let ((all (directory-files-recursively dir (rx) t nil nil #'string>))) + (should (equal (mapcar rel all) + '("d-b/b" "d-b/a" "d-b/1" "d-b/0" "d-b" + "d-a/b" "d-a/a" "d-a/1" "d-a/0" "d-a" + "d/b" "d/a" "d/1" "d/0" "d" + "b" "a" "1" "0")))) + + ;; Relative subdir returns absolute file names. + (let* ((default-directory dir) + (ds (directory-files-recursively "d" (rx)))) + (should (all #'file-name-absolute-p ds)) + (should (equal (mapcar rel ds) '("d/0" "d/1" "d/a" "d/b")))) + + ;; Result is unaffected by `completion-regexp-list'. + (let* ((completion-regexp-list (list (rx unmatchable))) + (all (directory-files-recursively dir (rx)))) + (should (equal (mapcar rel all) leaves))) + + ;; Regexp matches nondir part. + (should (equal (mapcar rel (directory-files-recursively dir (rx ?a))) + '("d/a" "d-a/a" "d-b/a" "a"))) + (should (equal (mapcar rel (directory-files-recursively dir (rx ?a) t)) + '("d/a" "d-a/a" "d-a" "d-b/a" "a"))) + (should-not (directory-files-recursively dir (rx unmatchable))) + (should-not (directory-files-recursively dir (rx unmatchable) t)) + + ;; Regexp does not match current/parent dir. + (should-not (directory-files-recursively dir (rx bos ?. eos))) + (should-not (directory-files-recursively dir (rx bos ?. eos) t)) + (should-not (directory-files-recursively dir (rx bos ".." eos))) + (should-not (directory-files-recursively dir (rx bos ".." eos) t)) + + ;; Predicate only applies to subdirs. + (let ((top (directory-files-recursively dir (rx) nil #'ignore))) + (should (equal (mapcar rel top) '("0" "1" "a" "b")))) + (let ((top (directory-files-recursively dir (rx) t #'ignore))) + (should (equal (mapcar rel top) '("d" "d-a" "d-b" "0" "1" "a" "b")))) + + ;; Predicates nil, t, `always' equivalent in absence of errors. + (dolist (pred (list t #'always)) + (let ((all (directory-files-recursively dir (rx) nil pred))) + (should (equal (mapcar rel all) leaves))) + (let ((all (directory-files-recursively dir (rx) t pred))) + (should (equal (mapcar rel all) tree)))) + + ;; Predicate receives absolute file names. + (let ((pred (lambda (f) (should (file-name-absolute-p f)) t))) + (directory-files-recursively dir (rx) nil pred))))) + +(ert-deftest files-tests-directory-files-recursively-link () + "Test `directory-files-recursively' behavior with symbolic links." + (ert-with-temp-directory adir + (ert-with-temp-directory bdir + (let* ((arel (lambda (f) (file-relative-name f adir))) + (brel (lambda (f) (file-relative-name f bdir))) + (atree '("d/0" "d/1" "d" "a" "b")) + (btree '("d/a" "d/b" "d" "0" "1")) + ;; Full tree with atree linked to btree via "d-b". + (ctree '("d/0" "d/1" "d" + "d-b/d/a" "d-b/d/b" "d-b/d" "d-b/0" "d-b/1" "d-b" + "a" "b")) + ;; atree including "d-b" directory link. + (dtree '("d/0" "d/1" "d" "d-b" "a" "b")) + (aleaves (remove "d" atree)) + (bleaves (remove "d" btree)) + (cleaves (seq-difference ctree '("d" "d-b/d" "d-b")))) + ;; The assumption is that this will signal an error + ;; on systems that don't support symbolic links. + (skip-unless + (ignore-error file-error + (make-symbolic-link (directory-file-name bdir) + (expand-file-name "d-b" adir)) + t)) + (dolist (file aleaves) + (make-empty-file (expand-file-name file adir) t)) + (dolist (file bleaves) + (make-empty-file (expand-file-name file bdir) t)) + + ;; Sanity check. + (let ((all (directory-files-recursively adir (rx)))) + (should (equal (mapcar arel all) aleaves))) + (let ((all (directory-files-recursively bdir (rx)))) + (should (equal (mapcar brel all) bleaves))) + (let ((all (directory-files-recursively bdir (rx) nil nil t))) + (should (equal (mapcar brel all) bleaves))) + (let ((all (directory-files-recursively adir (rx) t))) + (should (equal (mapcar arel all) dtree))) + (let ((all (directory-files-recursively bdir (rx) t))) + (should (equal (mapcar brel all) btree))) + (let ((all (directory-files-recursively bdir (rx) t nil t))) + (should (equal (mapcar brel all) btree))) + + ;; Follow links. + (let ((all (directory-files-recursively adir (rx) nil nil t))) + (should (equal (mapcar arel all) cleaves))) + (let ((all (directory-files-recursively adir (rx) t nil t))) + (should (equal (mapcar arel all) ctree))) + (let ((bs (directory-files-recursively adir (rx ?b) nil nil t))) + (should (equal (mapcar arel bs) '("d-b/d/b" "b")))) + (let ((bs (directory-files-recursively adir (rx ?b) t nil t))) + (should (equal (mapcar arel bs) '("d-b/d/b" "d-b" "b")))) + + ;; Predicate can override links. + (let ((pred (lambda (f) (not (file-symlink-p f))))) + (let ((all (directory-files-recursively adir (rx) nil pred t))) + (should (equal (mapcar arel all) aleaves))) + (let ((all (directory-files-recursively adir (rx) t pred t))) + (should (equal (mapcar arel all) dtree)))))))) + +(ert-deftest files-tests-directory-files-recursively-expand () + "Check that files named \"~\" are not expanded (bug#36490)." + (ert-with-temp-directory dir + (let* ((rel (lambda (f) (file-relative-name f dir))) + (tree '("~/a" "~/b" "~" "a" "b")) + ;; The same but without intermediate subdirs. + (leaves (remove "~" tree)) + (home (expand-file-name "~")) + (pred (lambda (f) + ;; Check that we do not descend into the + ;; (usually /nonexistent) home directory. + (should-not (equal (directory-file-name f) home)) + t))) + (dolist (file leaves) + ;; Avoid `expand-file-name' on files named "~". + (make-empty-file (concat dir file) t)) + (let ((all (directory-files-recursively dir (rx) nil pred))) + (should (equal (mapcar rel all) leaves))) + (let ((all (directory-files-recursively dir (rx) t pred))) + (should (equal (mapcar rel all) tree)))))) + +(ert-deftest files-tests-directory-files-recursively-error () + "Check that inaccessible directories can be ignored (bug#28567)." + (ert-with-temp-directory dir + (let ((rel (lambda (f) (file-relative-name f dir))) + ;; A subdirectory that can't be descended into. + (d-b (expand-file-name "d-b" dir))) + (dolist (file '("d-a/0" "d-b/1" "a")) + (make-empty-file (expand-file-name file dir) t)) + (unwind-protect + (progn + (skip-unless + (ignore-error file-error + (set-file-modes d-b 0 'nofollow) + ;; Removing some modes doesn't work on MS-Windows, so + ;; check that they changed before proceeding (bug#80915). + (not (file-accessible-directory-p d-b)))) + (should-error (directory-files-recursively dir (rx)) + :type 'permission-denied) + (let ((all (directory-files-recursively dir (rx) nil t))) + (should (equal (mapcar rel all) '("d-a/0" "a")))) + (let ((all (directory-files-recursively dir (rx) t t))) + (should (equal (mapcar rel all) '("d-a/0" "d-a" "d-b" "a"))))) + ;; Allow the directory to be deleted. + (set-file-modes d-b #o700 'nofollow))))) (defvar w32-downcase-file-names) commit 8f784f2c4175e86c156577443556e4c42da80b2b Author: Dmitry Gutov Date: Sat Sep 26 19:15:39 2026 +0300 etags/etags-regen-mode: Work better inside directories with symlinks * lisp/progmodes/etags.el (etags-file-of-tag): Do not force the truename (bug#81911). * lisp/progmodes/etags-regen.el (etags-regen--tags-generate): Use the relative file name for TAGS as well when appropriate. diff --git a/lisp/progmodes/etags-regen.el b/lisp/progmodes/etags-regen.el index 9be4be9f92d..e749f6ab01b 100644 --- a/lisp/progmodes/etags-regen.el +++ b/lisp/progmodes/etags-regen.el @@ -332,7 +332,7 @@ is used in buffers that have no alternative completion configured." " ") ;; ctags's etags requires '-L' for stdin input. (if ctags-p "-L" "") - (shell-quote-argument (file-local-name tags-file))))) + (shell-quote-argument (funcall fun tags-file))))) (with-temp-buffer (mapc (lambda (f) (insert (funcall fun f) "\n")) diff --git a/lisp/progmodes/etags.el b/lisp/progmodes/etags.el index 395f5ed99c8..63fabf66be7 100644 --- a/lisp/progmodes/etags.el +++ b/lisp/progmodes/etags.el @@ -1278,7 +1278,7 @@ buffer-local values of tags table format variables." (buffer-substring (match-beginning 1) (match-end 1))))) (if relative str - (expand-file-name str (file-truename default-directory)))))) + (expand-file-name str default-directory))))) (defun etags-tags-completion-table () ; Doc string? commit 05b5edf19038f986f282b9cfcfec428ad46bff03 Author: Eli Zaretskii Date: Sat Sep 26 14:39:07 2026 +0300 ; * src/systhread.c (sys_thread_self): Remove type-cast (bug#81837). diff --git a/src/systhread.c b/src/systhread.c index 6d8dd7c17a4..97423f563b4 100644 --- a/src/systhread.c +++ b/src/systhread.c @@ -413,7 +413,7 @@ sys_cond_destroy (sys_cond_t *cond) sys_thread_t sys_thread_self (void) { - return (sys_thread_t) GetCurrentThreadId (); + return GetCurrentThreadId (); } bool commit b4fdff95b3e686130bd376b4eb43bb719280a4a3 Author: Eli Zaretskii Date: Sat Sep 26 14:24:06 2026 +0300 ; Fix last change * lisp/speedbar.el (speedbar-window--handle-window-buffer-change): Doc fix. diff --git a/lisp/speedbar.el b/lisp/speedbar.el index 8812da30aa7..8ad2863a06b 100644 --- a/lisp/speedbar.el +++ b/lisp/speedbar.el @@ -1164,8 +1164,9 @@ killing `speedbar-buffer', which is useful for `kill-buffer-hook'." speedbar-default-position)))) (defun speedbar-window--handle-window-buffer-change (w) - "Handle the change of the window that displays the ‘speedbar-buffer’. -If W displays the `speedbar-buffer' buffer, update `speedbar--window' if necessary." + "Handle the change of the window that displays the `speedbar-buffer'. +If W displays the `speedbar-buffer' buffer, update `speedbar--window' +if necessary." (when (and (eq (window-buffer w) speedbar-buffer) (not (eq speedbar--window w))) commit 33cb7fe6dd74554086f623b25a48f76e90c8bde9 Author: Vincenzo Pupillo Date: Fri Sep 18 17:01:37 2026 +0200 Fix speedbar window behavior when togling side windows (bug#81773). * lisp/speedbar.el (speedbar-window-mode): Persist 'no-other-window' and 'no-delete-other-windows' as additional parameters to 'window-persistent-parameters'. Add a hook to 'window-buffer-change-functions' to handle speedbar window change. (speedbar-window-close): Remove the hook from 'window-buffer-change-functions'. (speedbar-window--handle-window-buffer-change): New function. diff --git a/lisp/speedbar.el b/lisp/speedbar.el index 71f2fe80899..8812da30aa7 100644 --- a/lisp/speedbar.el +++ b/lisp/speedbar.el @@ -1086,15 +1086,21 @@ supported at a time. ;; let's create the window (setq speedbar--window - (display-buffer-in-side-window speedbar-buffer - `((side ,@speedbar-window-side) - (slot . 0) - (dedicated ,@speedbar-window-dedicated-window) - (window-width ,@speedbar--window-width)))) + (display-buffer-in-side-window speedbar-buffer + `((side ,@speedbar-window-side) + (slot . 0) + (dedicated ,@speedbar-window-dedicated-window) + (window-width ,@speedbar--window-width)))) + ;; additional window parameters (set-window-parameter speedbar--window 'no-other-window t) (set-window-parameter speedbar--window 'no-delete-other-windows t) + ;; persist these settings so that `window-toggle-side-windows' + ;; will retain them when it recreates this side window. + (add-to-list 'window-persistent-parameters '(no-other-window . t)) + (add-to-list 'window-persistent-parameters '(no-delete-other-windows . t)) + ;; `speedbar-reconfigure-keymaps' checks if the `speedbar-window' is open, so ;; should stay after the buffer and window definition. (speedbar-reconfigure-keymaps) @@ -1103,6 +1109,9 @@ supported at a time. ;; handle kill-buffer (add-hook 'kill-buffer-hook (lambda () (speedbar-window-close t)) nil t) + ;; handle the change of the window that display the `speedbar-buffer' + (add-hook 'window-buffer-change-functions + #'speedbar-window--handle-window-buffer-change nil t) ;; hscroll (setq-local auto-hscroll-mode nil) @@ -1124,6 +1133,7 @@ killing `speedbar-buffer', which is useful for `kill-buffer-hook'." speedbar-window-max-width current-width))) + (remove-hook 'window-buffer-change-functions #'speedbar-window--handle-window-buffer-change) (delete-window speedbar--window) (setq speedbar--window nil speedbar-frame nil @@ -1153,6 +1163,14 @@ killing `speedbar-buffer', which is useful for `kill-buffer-hook'." (dframe-attached-frame speedbar-frame) speedbar-default-position)))) +(defun speedbar-window--handle-window-buffer-change (w) + "Handle the change of the window that displays the ‘speedbar-buffer’. +If W displays the `speedbar-buffer' buffer, update `speedbar--window' if necessary." + (when (and + (eq (window-buffer w) speedbar-buffer) + (not (eq speedbar--window w))) + (setq speedbar--window w))) + (defun speedbar-handle-delete-frame (e) "Handle a delete-frame event E. If the deleted frame is the frame speedbar is attached to, commit 7ea48dd0ab3fbb8a62990234023f6514c596710b Author: Michael Albinus Date: Sat Sep 26 09:31:31 2026 +0200 ; Minor Tramp cleanup * doc/misc/tramp.texi (FUSE-based methods): Fix wording. (File name syntax): Mention respective regular expressions. * lisp/net/tramp.el (tramp-method-regexp): Fix docstring. (tramp-get-default-directory): Validate BUFFER. diff --git a/doc/misc/tramp.texi b/doc/misc/tramp.texi index 5cbcb810824..fe3f7ed92b3 100644 --- a/doc/misc/tramp.texi +++ b/doc/misc/tramp.texi @@ -1610,10 +1610,7 @@ option @code{tramp-sshfs-program}. All remote files are available via the local mount point. @value{tramp} aids in mounting the file system if it isn't mounted yet. The remote file name syntax is - -@example -@trampfn{sshfs,user@@host#port,/path/to/file} -@end example +@trampfn{sshfs,user@@host#port,/path/to/file}. User name and port number are optional. This method does not support password handling, the file system must either be mounted already, or @@ -3662,8 +3659,9 @@ is a feature of Emacs that may cause missed prompts when using @cindex file name syntax @cindex file name examples -@file{@trampfn{method,host,/path/to/file}} opens file @var{/path/to/file} -on the remote host @var{host}, using the method @var{method}. +@file{@trampfn{method,user@@host,/path/to/file}} opens the file +@var{/path/to/file} on the remote host @var{host} for the user +@var{user}, using the method @var{method}. @c We cannot use @trampfn{} in @item. @table @file @@ -3676,7 +3674,8 @@ For the file @file{.emacs} specified using the fully qualified domain name of the host. @item @value{prefix}ssh@value{postfixhop}melancholia@value{postfix}~/.emacs -For the file @file{.emacs} specified using the @file{~}, which is expanded. +For the file @file{.emacs} specified using the tilde @file{~}, which +is expanded. @item @value{prefix}ssh@value{postfixhop}melancholia@value{postfix}~daniel/.emacs For the file @file{.emacs} located in @samp{daniel}'s home directory @@ -3719,6 +3718,19 @@ For syntactical reasons, the default method must be indicated by the pseudo method @file{-}. @end ifset +@defvar tramp-method-regexp +@defvarx tramp-user-regexp +@defvarx tramp-host-regexp +@defvarx tramp-port-regexp +@defvarx tramp-localname-regexp + +These regular expressions define the syntax of remote file name +components. + +A method name must be defined in the association list +@code{tramp-methods}. +@end defvar + @ifset unified @node Change file name syntax diff --git a/lisp/net/tramp.el b/lisp/net/tramp.el index a134f2b51cd..67ff176985f 100644 --- a/lisp/net/tramp.el +++ b/lisp/net/tramp.el @@ -1043,7 +1043,7 @@ Should always start with \"\\\\=`\". Derived from `tramp-prefix-format'.") (defvar tramp-method-regexp nil ; Initialized when defining `tramp-syntax'! "Regexp matching method identifiers. -The `ftp' syntax does not support methods.") +The `simplified' syntax does not support methods.") (defconst tramp-postfix-method-format-alist '((default . ":") @@ -2146,7 +2146,7 @@ version, the function does nothing." (defsubst tramp-get-default-directory (buffer) "Return `default-directory' of BUFFER." - (buffer-local-value 'default-directory buffer)) + (and (bufferp buffer) (buffer-local-value 'default-directory buffer))) ;;;###tramp-autoload (defsubst tramp-get-buffer-string (&optional buffer) @@ -3181,7 +3181,7 @@ BODY is the backend specific code." (tramp-make-tramp-file-name (tramp-dissect-hop-name hop)))) (let (tramp-default-user tramp-default-user-alist - tramp-default-host tramp-default-host-alist) + tramp-default-host tramp-default-host-alist) ;; Possible completion structures. (dolist (elt (tramp-completion-dissect-file-name fullname)) commit 95d4620c07b4945e1b411670acaae9cd5a183a44 Author: Sean Whitton Date: Fri Sep 25 17:22:48 2026 +0100 ; FIXME. diff --git a/lisp/cus-edit.el b/lisp/cus-edit.el index 3b5d9121ef4..3f6ceb845e3 100644 --- a/lisp/cus-edit.el +++ b/lisp/cus-edit.el @@ -804,6 +804,9 @@ when the action is chosen.") 'modified)) custom-options))) (define-key-after map [Custom-reset-saved] + ;; FIXME: This should be "Revert This Session's Customizations" + ;; but the apostrophe causes problems: + ;; `(menu-item "Revert Customizations of This Session" Custom-reset-saved :enable (seq-some ,(lambda (option) (memq (widget-get option :custom-state)