commit d99354720be8655a58c8a09c5ae110f86ac9fb5d (HEAD, refs/remotes/origin/master) Author: Juri Linkov Date: Thu Aug 14 20:59:15 2025 +0300 ; * test/infra/Dockerfile.emba: Fix warning about casing not matching. diff --git a/test/infra/Dockerfile.emba b/test/infra/Dockerfile.emba index 7301c3ea2e7..539bc42efb1 100644 --- a/test/infra/Dockerfile.emba +++ b/test/infra/Dockerfile.emba @@ -24,7 +24,7 @@ # Maintainer: Michael Albinus # URL: https://emba.gnu.org/emacs/emacs -FROM debian:bookworm as emacs-base +FROM debian:bookworm AS emacs-base RUN apt-get update && \ apt-get install -y --no-install-recommends -o=Dpkg::Use-Pty=0 \ @@ -32,7 +32,7 @@ RUN apt-get update && \ libxml2-dev libdbus-1-dev libacl1-dev acl git man-db texinfo gdb python3 \ && rm -rf /var/lib/apt/lists/* -FROM emacs-base as emacs-inotify +FROM emacs-base AS emacs-inotify RUN apt-get update && \ apt-get install -y --no-install-recommends -o=Dpkg::Use-Pty=0 \ @@ -46,7 +46,7 @@ RUN ./configure # 'make -j4 bootstrap' does not work reliably. RUN make -j `nproc` bootstrap -FROM emacs-base as emacs-filenotify-gio +FROM emacs-base AS emacs-filenotify-gio RUN apt-get update && \ apt-get install -y --no-install-recommends -o=Dpkg::Use-Pty=0 \ @@ -59,7 +59,7 @@ RUN ./autogen.sh autoconf RUN ./configure --with-file-notification=gfile RUN make -j `nproc` bootstrap -FROM debian:trixie as emacs-eglot +FROM debian:trixie AS emacs-eglot # This mimics emacs-base. RUN apt-get update && \ @@ -109,7 +109,7 @@ RUN make -j `nproc` bootstrap # --eval '(package-install (quote company))' \ # --eval '(package-install (quote yasnippet))' -FROM emacs-base as emacs-tree-sitter +FROM emacs-base AS emacs-tree-sitter # Install tree-sitter library. RUN apt-get update && \ @@ -157,7 +157,7 @@ RUN src/emacs -Q --batch \ (list (expand-file-name "src/emacs")) treesit-admin--builtin-modes \ (expand-file-name "compatibility-report.html"))' -FROM emacs-base as emacs-gnustep +FROM emacs-base AS emacs-gnustep RUN apt-get update && \ apt-get install -y --no-install-recommends -o=Dpkg::Use-Pty=0 \ @@ -170,7 +170,7 @@ RUN ./autogen.sh autoconf RUN ./configure --with-ns RUN make -j `nproc` bootstrap -FROM emacs-base as emacs-native-comp +FROM emacs-base AS emacs-native-comp # The libgccjit version must correspond to the gcc version. RUN apt-get update && \ @@ -178,7 +178,7 @@ RUN apt-get update && \ libgccjit-12-dev zlib1g-dev \ && rm -rf /var/lib/apt/lists/* -FROM emacs-native-comp as emacs-native-comp-speed0 +FROM emacs-native-comp AS emacs-native-comp-speed0 COPY . /checkout WORKDIR /checkout @@ -187,7 +187,7 @@ RUN ./configure --with-native-compilation RUN make -j `nproc` bootstrap \ NATIVE_FULL_AOT=1 BYTE_COMPILE_EXTRA_FLAGS='--eval "(setq comp-speed 0)"' -FROM emacs-native-comp as emacs-native-comp-speed1 +FROM emacs-native-comp AS emacs-native-comp-speed1 COPY . /checkout WORKDIR /checkout @@ -196,7 +196,7 @@ RUN ./configure --with-native-compilation RUN make -j `nproc` bootstrap \ BYTE_COMPILE_EXTRA_FLAGS='--eval "(setq comp-speed 1)"' -FROM emacs-native-comp as emacs-native-comp-speed2 +FROM emacs-native-comp AS emacs-native-comp-speed2 COPY . /checkout WORKDIR /checkout commit 0ac3a1f26c6c02d0a81290dba4d30d57e5bd92f1 Author: Juri Linkov Date: Thu Aug 14 19:40:08 2025 +0300 New user option 'treesit-enabled-modes' (bug#79180) * lisp/treesit.el (treesit-enabled-modes): New user option. * src/treesit.c (treesit-major-mode-remap-alist): New variable. * lisp/progmodes/c-ts-mode.el: * lisp/progmodes/csharp-mode.el: * lisp/progmodes/java-ts-mode.el: * lisp/progmodes/js.el: * lisp/progmodes/json-ts-mode.el: * lisp/progmodes/ruby-ts-mode.el: * lisp/progmodes/sh-script.el: * lisp/textmodes/css-mode.el: * lisp/textmodes/mhtml-ts-mode.el: * lisp/textmodes/toml-ts-mode.el: Add ts-mode mapping to 'treesit-major-mode-remap-alist' for ts-modes that already have the corresponding non-ts mode association in 'auto-mode-alist'. * lisp/progmodes/cmake-ts-mode.el (cmake-ts-mode-maybe): * lisp/progmodes/dockerfile-ts-mode.el (dockerfile-ts-mode-maybe): * lisp/progmodes/elixir-ts-mode.el (elixir-ts-mode-maybe): * lisp/progmodes/go-ts-mode.el (go-ts-mode-maybe) (go-mod-ts-mode-maybe, go-work-ts-mode-maybe): * lisp/progmodes/heex-ts-mode.el (heex-ts-mode-maybe): * lisp/progmodes/lua-ts-mode.el (lua-ts-mode-maybe): * lisp/progmodes/php-ts-mode.el (php-ts-mode-maybe): * lisp/progmodes/rust-ts-mode.el (rust-ts-mode-maybe): * lisp/progmodes/typescript-ts-mode.el (typescript-ts-mode-maybe) (tsx-ts-mode-maybe): * lisp/textmodes/markdown-ts-mode.el (markdown-ts-mode-maybe): * lisp/textmodes/yaml-ts-mode.el (yaml-ts-mode-maybe): Add a wrapper function to 'auto-mode-alist' for ts-modes that have no corresponding non-ts mode. Also add a mapping to 'treesit-major-mode-remap-alist' for the case when a non-ts mode is installed from an external source to be able to customize it with 'treesit-enabled-modes'. diff --git a/etc/NEWS b/etc/NEWS index f8d4b8f2f67..5b3be259196 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -722,6 +722,12 @@ the default UI you get, i.e., when 'register-use-preview' is 'traditional'. ** Tree-sitter +*** New user option 'treesit-enabled-modes'. +You can customize it either to t to enable all available ts-modes, +or to select a list of ts-modes to enable. Depending on customization, +it modifies the variable 'major-mode-remap-alist' from the corresponding +variable 'treesit-major-mode-remap-alist' prepared by ts-mode packages. + *** New user option 'treesit-auto-install-grammar'. It controls the automatic installation of tree-sitter grammar libraries needed for tree-sitter based modes, if these grammar libraries are not diff --git a/lisp/progmodes/c-ts-mode.el b/lisp/progmodes/c-ts-mode.el index 5155193c603..55240c3869a 100644 --- a/lisp/progmodes/c-ts-mode.el +++ b/lisp/progmodes/c-ts-mode.el @@ -65,11 +65,9 @@ ;; ;; will turn on the c++-ts-mode for C++ source files. ;; -;; - If you have both C and C++ grammars installed, add -;; -;; (load "c-ts-mode") -;; -;; to your init file. +;; - If you have both C and C++ grammars installed, customize +;; 'treesit-enabled-modes' and select 'c-ts-mode', +;; 'c++-mode', 'c-or-c++-mode'. ;; ;; You can also turn on these modes manually in a buffer. Doing so ;; will set up Emacs to use the C/C++ modes defined here for other @@ -1675,21 +1673,14 @@ the code is C or C++, and based on that chooses whether to enable 'c-ts-mode))) (funcall (major-mode-remap mode)))) -(when (treesit-ready-p 'cpp) - (setq major-mode-remap-defaults - (assq-delete-all 'c++-mode major-mode-remap-defaults)) - (add-to-list 'major-mode-remap-defaults '(c++-mode . c++-ts-mode))) - -(when (treesit-ready-p 'c) - (setq major-mode-remap-defaults - (assq-delete-all 'c-mode major-mode-remap-defaults)) - (add-to-list 'major-mode-remap-defaults '(c-mode . c-ts-mode))) - -(when (and (treesit-ready-p 'cpp) - (treesit-ready-p 'c)) - (setq major-mode-remap-defaults - (assq-delete-all 'c-or-c++-mode major-mode-remap-defaults)) - (add-to-list 'major-mode-remap-defaults '(c-or-c++-mode . c-or-c++-ts-mode))) +;;;###autoload +(when (treesit-available-p) + (add-to-list 'treesit-major-mode-remap-alist + '(c-mode . c-ts-mode)) + (add-to-list 'treesit-major-mode-remap-alist + '(c++-mode . c++-ts-mode)) + (add-to-list 'treesit-major-mode-remap-alist + '(c-or-c++-mode . c-or-c++-ts-mode))) (provide 'c-ts-mode) (provide 'c++-ts-mode) diff --git a/lisp/progmodes/cmake-ts-mode.el b/lisp/progmodes/cmake-ts-mode.el index 2f2d1b6e2a0..3f879e37ba2 100644 --- a/lisp/progmodes/cmake-ts-mode.el +++ b/lisp/progmodes/cmake-ts-mode.el @@ -255,9 +255,22 @@ Return nil if there is no name or if NODE is not a defun node." (derived-mode-add-parents 'cmake-ts-mode '(cmake-mode)) -(if (treesit-ready-p 'cmake) - (add-to-list 'auto-mode-alist - '("\\(?:CMakeLists\\.txt\\|\\.cmake\\)\\'" . cmake-ts-mode))) +;;;###autoload +(defun cmake-ts-mode-maybe () + "Enable `cmake-ts-mode' when its grammar is available." + (if (or (treesit-language-available-p 'cmake) + (eq treesit-enabled-modes t) + (memq 'cmake-ts-mode treesit-enabled-modes)) + (cmake-ts-mode) + (fundamental-mode))) + +;;;###autoload +(when (treesit-available-p) + (add-to-list 'auto-mode-alist + '("\\(?:CMakeLists\\.txt\\|\\.cmake\\)\\'" . cmake-ts-mode-maybe)) + ;; To be able to toggle between an external package and core ts-mode: + (add-to-list 'treesit-major-mode-remap-alist + '(cmake-mode . cmake-ts-mode))) (provide 'cmake-ts-mode) diff --git a/lisp/progmodes/csharp-mode.el b/lisp/progmodes/csharp-mode.el index 958ccf05672..fb05389ba91 100644 --- a/lisp/progmodes/csharp-mode.el +++ b/lisp/progmodes/csharp-mode.el @@ -1219,12 +1219,15 @@ Key bindings: "local_function_statement") eos)) - (treesit-major-mode-setup) - - (add-to-list 'auto-mode-alist '("\\.cs\\'" . csharp-ts-mode))) + (treesit-major-mode-setup)) (derived-mode-add-parents 'csharp-ts-mode '(csharp-mode)) +;;;###autoload +(when (treesit-available-p) + (add-to-list 'treesit-major-mode-remap-alist + '(csharp-mode . csharp-ts-mode))) + (provide 'csharp-mode) ;;; csharp-mode.el ends here diff --git a/lisp/progmodes/dockerfile-ts-mode.el b/lisp/progmodes/dockerfile-ts-mode.el index fe0c9e23acc..79a2197c078 100644 --- a/lisp/progmodes/dockerfile-ts-mode.el +++ b/lisp/progmodes/dockerfile-ts-mode.el @@ -202,11 +202,24 @@ Return nil if there is no name or if NODE is not a stage node." (derived-mode-add-parents 'dockerfile-ts-mode '(dockerfile-mode)) -(if (treesit-ready-p 'dockerfile) - (add-to-list 'auto-mode-alist - ;; NOTE: We can't use `rx' here, as it breaks bootstrap. - '("\\(?:Dockerfile\\(?:\\..*\\)?\\|\\.[Dd]ockerfile\\)\\'" - . dockerfile-ts-mode))) +;;;###autoload +(defun dockerfile-ts-mode-maybe () + "Enable `dockerfile-ts-mode' when its grammar is available." + (if (or (treesit-language-available-p 'dockerfile) + (eq treesit-enabled-modes t) + (memq 'dockerfile-ts-mode treesit-enabled-modes)) + (dockerfile-ts-mode) + (fundamental-mode))) + +;;;###autoload +(when (treesit-available-p) + (add-to-list 'auto-mode-alist + ;; NOTE: We can't use `rx' here, as it breaks bootstrap. + '("\\(?:Dockerfile\\(?:\\..*\\)?\\|\\.[Dd]ockerfile\\)\\'" + . dockerfile-ts-mode-maybe)) + ;; To be able to toggle between an external package and core ts-mode: + (add-to-list 'treesit-major-mode-remap-alist + '(dockerfile-mode . dockerfile-ts-mode))) (provide 'dockerfile-ts-mode) diff --git a/lisp/progmodes/elixir-ts-mode.el b/lisp/progmodes/elixir-ts-mode.el index 8b43c032424..05ad76d100f 100644 --- a/lisp/progmodes/elixir-ts-mode.el +++ b/lisp/progmodes/elixir-ts-mode.el @@ -806,12 +806,24 @@ Return nil if NODE is not a defun node or doesn't have a name." (derived-mode-add-parents 'elixir-ts-mode '(elixir-mode)) -(if (treesit-ready-p 'elixir) - (progn - (add-to-list 'auto-mode-alist '("\\.elixir\\'" . elixir-ts-mode)) - (add-to-list 'auto-mode-alist '("\\.ex\\'" . elixir-ts-mode)) - (add-to-list 'auto-mode-alist '("\\.exs\\'" . elixir-ts-mode)) - (add-to-list 'auto-mode-alist '("mix\\.lock" . elixir-ts-mode)))) +;;;###autoload +(defun elixir-ts-mode-maybe () + "Enable `elixir-ts-mode' when its grammar is available." + (if (or (treesit-language-available-p 'elixir) + (eq treesit-enabled-modes t) + (memq 'elixir-ts-mode treesit-enabled-modes)) + (elixir-ts-mode) + (fundamental-mode))) + +;;;###autoload +(when (treesit-available-p) + (add-to-list 'auto-mode-alist '("\\.elixir\\'" . elixir-ts-mode-maybe)) + (add-to-list 'auto-mode-alist '("\\.ex\\'" . elixir-ts-mode-maybe)) + (add-to-list 'auto-mode-alist '("\\.exs\\'" . elixir-ts-mode-maybe)) + (add-to-list 'auto-mode-alist '("mix\\.lock" . elixir-ts-mode-maybe)) + ;; To be able to toggle between an external package and core ts-mode: + (add-to-list 'treesit-major-mode-remap-alist + '(elixir-mode . elixir-ts-mode))) (provide 'elixir-ts-mode) diff --git a/lisp/progmodes/go-ts-mode.el b/lisp/progmodes/go-ts-mode.el index e8e4c9af29d..40f3de0bc15 100644 --- a/lisp/progmodes/go-ts-mode.el +++ b/lisp/progmodes/go-ts-mode.el @@ -359,10 +359,21 @@ (derived-mode-add-parents 'go-ts-mode '(go-mode)) -(if (treesit-ready-p 'go) - ;; FIXME: Should we instead put `go-mode' in `auto-mode-alist' - ;; and then use `major-mode-remap-defaults' to map it to `go-ts-mode'? - (add-to-list 'auto-mode-alist '("\\.go\\'" . go-ts-mode))) +;;;###autoload +(defun go-ts-mode-maybe () + "Enable `go-ts-mode' when its grammar is available." + (if (or (treesit-language-available-p 'go) + (eq treesit-enabled-modes t) + (memq 'go-ts-mode treesit-enabled-modes)) + (go-ts-mode) + (fundamental-mode))) + +;;;###autoload +(when (treesit-available-p) + (add-to-list 'auto-mode-alist '("\\.go\\'" . go-ts-mode-maybe)) + ;; To be able to toggle between an external package and core ts-mode: + (add-to-list 'treesit-major-mode-remap-alist + '(go-mode . go-ts-mode))) (defun go-ts-mode--defun-name (node &optional skip-prefix) "Return the defun name of NODE. @@ -622,8 +633,21 @@ what the parent of the node would be if it were a node." (derived-mode-add-parents 'go-mod-ts-mode '(go-mod-mode)) -(if (treesit-ready-p 'gomod t) - (add-to-list 'auto-mode-alist '("/go\\.mod\\'" . go-mod-ts-mode))) +;;;###autoload +(defun go-mod-ts-mode-maybe () + "Enable `go-mod-ts-mode' when its grammar is available." + (if (or (treesit-language-available-p 'gomod) + (eq treesit-enabled-modes t) + (memq 'go-mod-ts-mode treesit-enabled-modes)) + (go-mod-ts-mode) + (fundamental-mode))) + +;;;###autoload +(when (treesit-available-p) + (add-to-list 'auto-mode-alist '("/go\\.mod\\'" . go-mod-ts-mode-maybe)) + ;; To be able to toggle between an external package and core ts-mode: + (add-to-list 'treesit-major-mode-remap-alist + '(go-mod-mode . go-mod-ts-mode))) ;;;; go.work support. @@ -711,7 +735,20 @@ what the parent of the node would be if it were a node." (treesit-major-mode-setup))) ;;;###autoload -(add-to-list 'auto-mode-alist '("/go\\.work\\'" . go-work-ts-mode)) +(defun go-work-ts-mode-maybe () + "Enable `go-work-ts-mode' when its grammar is available." + (if (or (treesit-language-available-p 'gowork) + (eq treesit-enabled-modes t) + (memq 'go-work-ts-mode treesit-enabled-modes)) + (go-work-ts-mode) + (fundamental-mode))) + +;;;###autoload +(when (treesit-available-p) + (add-to-list 'auto-mode-alist '("/go\\.work\\'" . go-work-ts-mode-maybe)) + ;; To be able to toggle between an external package and core ts-mode: + (add-to-list 'treesit-major-mode-remap-alist + '(go-work-mode . go-work-ts-mode))) (provide 'go-ts-mode) diff --git a/lisp/progmodes/heex-ts-mode.el b/lisp/progmodes/heex-ts-mode.el index c478750a73e..41634d0e6a4 100644 --- a/lisp/progmodes/heex-ts-mode.el +++ b/lisp/progmodes/heex-ts-mode.el @@ -265,10 +265,23 @@ Return nil if NODE is not a defun node or doesn't have a name." (derived-mode-add-parents 'heex-ts-mode '(heex-mode)) -(if (treesit-ready-p 'heex) - ;; Both .heex and the deprecated .leex files should work - ;; with the tree-sitter-heex grammar. - (add-to-list 'auto-mode-alist '("\\.[hl]?eex\\'" . heex-ts-mode))) +;;;###autoload +(defun heex-ts-mode-maybe () + "Enable `heex-ts-mode' when its grammar is available." + (if (or (treesit-language-available-p 'heex) + (eq treesit-enabled-modes t) + (memq 'heex-ts-mode treesit-enabled-modes)) + (heex-ts-mode) + (fundamental-mode))) + +;;;###autoload +(when (treesit-available-p) + ;; Both .heex and the deprecated .leex files should work + ;; with the tree-sitter-heex grammar. + (add-to-list 'auto-mode-alist '("\\.[hl]?eex\\'" . heex-ts-mode-maybe)) + ;; To be able to toggle between an external package and core ts-mode: + (add-to-list 'treesit-major-mode-remap-alist + '(heex-mode . heex-ts-mode))) (provide 'heex-ts-mode) ;;; heex-ts-mode.el ends here diff --git a/lisp/progmodes/java-ts-mode.el b/lisp/progmodes/java-ts-mode.el index 0fe35a4df8b..e989d1b3f5d 100644 --- a/lisp/progmodes/java-ts-mode.el +++ b/lisp/progmodes/java-ts-mode.el @@ -524,8 +524,10 @@ Return nil if there is no name or if NODE is not a defun node." (derived-mode-add-parents 'java-ts-mode '(java-mode)) -(if (treesit-ready-p 'java) - (add-to-list 'auto-mode-alist '("\\.java\\'" . java-ts-mode))) +;;;###autoload +(when (treesit-available-p) + (add-to-list 'treesit-major-mode-remap-alist + '(java-mode . java-ts-mode))) (provide 'java-ts-mode) diff --git a/lisp/progmodes/js.el b/lisp/progmodes/js.el index 5254e0d7089..c44b2adf146 100644 --- a/lisp/progmodes/js.el +++ b/lisp/progmodes/js.el @@ -4105,13 +4105,15 @@ See `treesit-thing-settings' for more information.") ;; Outline minor mode (setq-local treesit-outline-predicate js-ts-mode--outline-predicate) - (treesit-major-mode-setup) - - (add-to-list 'auto-mode-alist - '("\\(\\.js[mx]?\\|\\.har\\)\\'" . js-ts-mode)))) + (treesit-major-mode-setup))) (derived-mode-add-parents 'js-ts-mode '(js-mode)) +;;;###autoload +(when (treesit-available-p) + (add-to-list 'treesit-major-mode-remap-alist + '(javascript-mode . js-ts-mode))) + (defvar js-ts--s-p-query (when (treesit-available-p) (treesit-query-compile 'javascript diff --git a/lisp/progmodes/json-ts-mode.el b/lisp/progmodes/json-ts-mode.el index a18f3c342c0..b0db0a12210 100644 --- a/lisp/progmodes/json-ts-mode.el +++ b/lisp/progmodes/json-ts-mode.el @@ -181,9 +181,10 @@ Return nil if there is no name or if NODE is not a defun node." (derived-mode-add-parents 'json-ts-mode '(json-mode)) -(if (treesit-ready-p 'json) - (add-to-list 'auto-mode-alist - '("\\.json\\'" . json-ts-mode))) +;;;###autoload +(when (treesit-available-p) + (add-to-list 'treesit-major-mode-remap-alist + '(js-json-mode . json-ts-mode))) (provide 'json-ts-mode) diff --git a/lisp/progmodes/lua-ts-mode.el b/lisp/progmodes/lua-ts-mode.el index 3df36e329b3..07a8f0aef55 100644 --- a/lisp/progmodes/lua-ts-mode.el +++ b/lisp/progmodes/lua-ts-mode.el @@ -769,9 +769,22 @@ Calls REPORT-FN directly." (derived-mode-add-parents 'lua-ts-mode '(lua-mode)) -(when (treesit-ready-p 'lua) - (add-to-list 'auto-mode-alist '("\\.lua\\'" . lua-ts-mode)) - (add-to-list 'interpreter-mode-alist '("\\") ")>") (t "."))))))))))) -(if (treesit-ready-p 'tsx) - (add-to-list 'auto-mode-alist '("\\.tsx\\'" . tsx-ts-mode))) +;;;###autoload +(defun tsx-ts-mode-maybe () + "Enable `tsx-ts-mode' when its grammar is available." + (if (or (treesit-language-available-p 'tsx) + (eq treesit-enabled-modes t) + (memq 'tsx-ts-mode treesit-enabled-modes)) + (tsx-ts-mode) + (fundamental-mode))) + +;;;###autoload +(when (treesit-available-p) + (add-to-list 'auto-mode-alist '("\\.tsx\\'" . tsx-ts-mode-maybe)) + ;; To be able to toggle between an external package and core ts-mode: + (add-to-list 'treesit-major-mode-remap-alist + '(tsx-mode . tsx-ts-mode))) (provide 'typescript-ts-mode) diff --git a/lisp/textmodes/css-mode.el b/lisp/textmodes/css-mode.el index 45053ac6f23..0a02aedfa4f 100644 --- a/lisp/textmodes/css-mode.el +++ b/lisp/textmodes/css-mode.el @@ -1911,12 +1911,15 @@ can also be used to fill comments. (setq-local treesit-outline-predicate css-ts-mode--outline-predicate) (setq-local treesit-thing-settings css--treesit-thing-settings) - (treesit-major-mode-setup) - - (add-to-list 'auto-mode-alist '("\\.css\\'" . css-ts-mode)))) + (treesit-major-mode-setup))) (derived-mode-add-parents 'css-ts-mode '(css-mode)) +;;;###autoload +(when (treesit-available-p) + (add-to-list 'treesit-major-mode-remap-alist + '(css-mode . css-ts-mode))) + ;;;###autoload (define-derived-mode css-mode css-base-mode "CSS" "Major mode to edit Cascading Style Sheets (CSS). diff --git a/lisp/textmodes/html-ts-mode.el b/lisp/textmodes/html-ts-mode.el index 1bf89ee8f4e..12ab735171d 100644 --- a/lisp/textmodes/html-ts-mode.el +++ b/lisp/textmodes/html-ts-mode.el @@ -186,8 +186,8 @@ Return nil if there is no name or if NODE is not a defun node." (derived-mode-add-parents 'html-ts-mode '(html-mode)) -(if (treesit-ready-p 'html t) - (add-to-list 'auto-mode-alist '("\\.html\\'" . html-ts-mode))) +;; No `auto-mode-alist' associations are defined here +;; to give preference to `mhtml-ts-mode'. (provide 'html-ts-mode) diff --git a/lisp/textmodes/markdown-ts-mode.el b/lisp/textmodes/markdown-ts-mode.el index a94d1590fa0..4af80f80b5f 100644 --- a/lisp/textmodes/markdown-ts-mode.el +++ b/lisp/textmodes/markdown-ts-mode.el @@ -403,8 +403,21 @@ the same features enabled in MODE." (derived-mode-add-parents 'markdown-ts-mode '(markdown-mode)) -(if (treesit-ready-p 'markdown) - (add-to-list 'auto-mode-alist '("\\.md\\'" . markdown-ts-mode))) +;;;###autoload +(defun markdown-ts-mode-maybe () + "Enable `markdown-ts-mode' when its grammar is available." + (if (or (treesit-language-available-p 'markdown) + (eq treesit-enabled-modes t) + (memq 'markdown-ts-mode treesit-enabled-modes)) + (markdown-ts-mode) + (fundamental-mode))) + +;;;###autoload +(when (treesit-available-p) + (add-to-list 'auto-mode-alist '("\\.md\\'" . markdown-ts-mode-maybe)) + ;; To be able to toggle between an external package and core ts-mode: + (add-to-list 'treesit-major-mode-remap-alist + '(markdown-mode . markdown-ts-mode))) (provide 'markdown-ts-mode) ;;; markdown-ts-mode.el ends here diff --git a/lisp/textmodes/mhtml-ts-mode.el b/lisp/textmodes/mhtml-ts-mode.el index bf50148b84e..be3873e848a 100644 --- a/lisp/textmodes/mhtml-ts-mode.el +++ b/lisp/textmodes/mhtml-ts-mode.el @@ -587,9 +587,10 @@ Powered by tree-sitter." ;; Add some extra parents. (derived-mode-add-parents 'mhtml-ts-mode '(css-mode js-mode)) -(when (and (treesit-ready-p 'html t) (treesit-ready-p 'javascript t) (treesit-ready-p 'css t)) - (add-to-list - 'auto-mode-alist '("\\.[sx]?html?\\(\\.[a-zA-Z_]+\\)?\\'" . mhtml-ts-mode))) +;;;###autoload +(when (treesit-available-p) + (add-to-list 'treesit-major-mode-remap-alist + '(mhtml-mode . mhtml-ts-mode))) (provide 'mhtml-ts-mode) ;;; mhtml-ts-mode.el ends here diff --git a/lisp/textmodes/toml-ts-mode.el b/lisp/textmodes/toml-ts-mode.el index c1c5dea2bd9..511a6b5f8ed 100644 --- a/lisp/textmodes/toml-ts-mode.el +++ b/lisp/textmodes/toml-ts-mode.el @@ -172,8 +172,10 @@ Return nil if there is no name or if NODE is not a defun node." (derived-mode-add-parents 'toml-ts-mode '(toml-mode)) -(if (treesit-ready-p 'toml) - (add-to-list 'auto-mode-alist '("\\.toml\\'" . toml-ts-mode))) +;;;###autoload +(when (treesit-available-p) + (add-to-list 'treesit-major-mode-remap-alist + '(conf-toml-mode . toml-ts-mode))) (provide 'toml-ts-mode) diff --git a/lisp/textmodes/yaml-ts-mode.el b/lisp/textmodes/yaml-ts-mode.el index cadae19af1e..95c9c1abd7d 100644 --- a/lisp/textmodes/yaml-ts-mode.el +++ b/lisp/textmodes/yaml-ts-mode.el @@ -227,8 +227,21 @@ Return nil if there is no name or if NODE is not a defun node." (derived-mode-add-parents 'yaml-ts-mode '(yaml-mode)) -(if (treesit-ready-p 'yaml) - (add-to-list 'auto-mode-alist '("\\.ya?ml\\'" . yaml-ts-mode))) +;;;###autoload +(defun yaml-ts-mode-maybe () + "Enable `yaml-ts-mode' when its grammar is available." + (if (or (treesit-language-available-p 'yaml) + (eq treesit-enabled-modes t) + (memq 'yaml-ts-mode treesit-enabled-modes)) + (yaml-ts-mode) + (fundamental-mode))) + +;;;###autoload +(when (treesit-available-p) + (add-to-list 'auto-mode-alist '("\\.ya?ml\\'" . yaml-ts-mode-maybe)) + ;; To be able to toggle between an external package and core ts-mode: + (add-to-list 'treesit-major-mode-remap-alist + '(yaml-mode . yaml-ts-mode))) (provide 'yaml-ts-mode) diff --git a/lisp/treesit.el b/lisp/treesit.el index 0d30efb3782..ecdcf0b5551 100644 --- a/lisp/treesit.el +++ b/lisp/treesit.el @@ -125,7 +125,8 @@ in a Emacs not built with tree-sitter library." (declare-function treesit-available-p "treesit.c") - (defvar treesit-thing-settings))) + (defvar treesit-thing-settings) + (defvar treesit-major-mode-remap-alist))) (treesit-declare-unavailable-functions) @@ -5399,6 +5400,31 @@ Tree-sitter grammar for `%s' is missing; install it?" ;; Check that the grammar was installed successfully (treesit-ready-p lang))))) +;;; Treesit enabled modes + +;;;###autoload +(defcustom treesit-enabled-modes nil + "Specify what treesit modes to enable by default. +The value can be either a list of ts-modes to enable, +or t to enable all ts-modes." + :type `(choice + (const :tag "Disable all automatic associations" nil) + (const :tag "Enable all available ts-modes" t) + (set :tag "List of enabled ts-modes" + ,@(when (treesit-available-p) + (sort (mapcar (lambda (m) `(function-item ,m)) + (seq-uniq (mapcar #'cdr treesit-major-mode-remap-alist))))))) + :initialize #'custom-initialize-default + :set (lambda (sym val) + (set-default sym val) + (when (treesit-available-p) + (dolist (m treesit-major-mode-remap-alist) + (setq major-mode-remap-alist + (if (or (eq val t) (memq (cdr m) val)) + (cons m major-mode-remap-alist) + (delete m major-mode-remap-alist)))))) + :version "31.1") + ;;; Shortdocs (defun treesit--generate-shortdoc-examples () diff --git a/src/treesit.c b/src/treesit.c index bb720589c85..bf982de580b 100644 --- a/src/treesit.c +++ b/src/treesit.c @@ -5299,6 +5299,16 @@ language is in this list, Emacs enables line-column tracking for the buffer. */); Vtreesit_languages_require_line_column_tracking = Qnil; + DEFVAR_LISP ("treesit-major-mode-remap-alist", + Vtreesit_major_mode_remap_alist, + doc: + /* Alist mapping file-specified modes to ts-modes. + +The value should be an alist of (MODE . TS-MODE). +This alist is used to modify the value of `major-mode-remap-alist' +depending on customization of `treesit-enabled-modes'. */); + Vtreesit_major_mode_remap_alist = Qnil; + staticpro (&Vtreesit_str_libtree_sitter); Vtreesit_str_libtree_sitter = build_string ("libtree-sitter-"); staticpro (&Vtreesit_str_tree_sitter); commit 97e2e90519ad60ce3920d89752cf9fd5375db7b0 Author: Elijah Gabe Pérez Date: Thu Aug 14 16:29:45 2025 +0200 Fix lua-ts-mode-tests.el * test/lisp/progmodes/lua-ts-mode-tests.el (lua-ts-test-auto-close-block-comments): Skip when needed. diff --git a/test/lisp/progmodes/lua-ts-mode-tests.el b/test/lisp/progmodes/lua-ts-mode-tests.el index b3267367e90..3364f6750cc 100644 --- a/test/lisp/progmodes/lua-ts-mode-tests.el +++ b/test/lisp/progmodes/lua-ts-mode-tests.el @@ -83,6 +83,7 @@ `(call-with-saved-electric-modes (lambda () ,@body))) (ert-deftest lua-ts-test-auto-close-block-comments () + (skip-unless (treesit-ready-p 'lua t)) (save-electric-modes (with-temp-buffer (dlet ((lua-ts-auto-close-block-comments t)) commit fb5e6c6bb7bd4296b794a3b510968cac08962e70 Author: Sean Whitton Date: Thu Aug 14 14:56:07 2025 +0100 vc-git-modify-change-comment: Bind c-s-f-w for command arguments too * lisp/vc/vc-git.el (vc-git-modify-change-comment): Bind coding-system-for-write to locale-coding-system around the call to vc-git-command on MS-Windows. The temporary file name could contain non-ASCII, e.g. the user's username. diff --git a/lisp/vc/vc-git.el b/lisp/vc/vc-git.el index d2727e8c367..f01807e0113 100644 --- a/lisp/vc/vc-git.el +++ b/lisp/vc/vc-git.el @@ -2081,10 +2081,6 @@ Will not rewrite likely-public history; see option `vc-allow-rewriting-published ;; On MS-Windows, pass the message through a file, to work ;; around how command line arguments must be in the system ;; codepage, and therefore might not support non-ASCII. - ;; - ;; As our other arguments are static, we need not be concerned - ;; about the encoding of command line arguments in general. - ;; See `vc-git-checkin' for the more complex case. (and (eq system-type 'windows-nt) (let ((default-directory (or (file-name-directory (or (car files) @@ -2122,12 +2118,18 @@ Rebase may --autosquash your other squash!/fixup!/amend!; proceed?"))) (write-region message nil msg-file))) ;; Regardless of the state of the index and working tree, this ;; will always create an empty commit, thanks to --only. - (apply #'vc-git-command nil 0 nil - "commit" "--only" "--allow-empty" - (nconc (if msg-file - (list "-F" (file-local-name msg-file)) - (list "-m" message)) - args))) + (let ((coding-system-for-write + ;; On MS-Windows, we must encode command-line arguments in + ;; the system codepage. + (if (eq system-type 'windows-nt) + locale-coding-system + coding-system-for-write))) + (apply #'vc-git-command nil 0 nil + "commit" "--only" "--allow-empty" + (nconc (if msg-file + (list "-F" (file-local-name msg-file)) + (list "-m" message)) + args)))) (when (and msg-file (file-exists-p msg-file)) (delete-file msg-file)))) (with-environment-variables (("GIT_SEQUENCE_EDITOR" "true")) commit ea7aee894dd113d92a2f800d40b665b239376f5d Author: Sean Whitton Date: Thu Aug 14 14:50:09 2025 +0100 vc-buffer-sync-fileset: Reorder some checks for speed (bug#79137) * lisp/vc/vc.el (vc-buffer-sync-fileset): Check whether a buffer's file is in one of our directories before checking whether it exists on disk. This should mean far fewer system calls overall (bug#79137). diff --git a/lisp/vc/vc.el b/lisp/vc/vc.el index f2972f10e6a..5d8c3f1eeb8 100644 --- a/lisp/vc/vc.el +++ b/lisp/vc/vc.el @@ -2506,14 +2506,15 @@ buffers whose files exist on disk. Otherwise it syncs all of them." (lambda (buf) (and-let* ((file (buffer-local-value 'buffer-file-name buf)) - ((or missing-in-dirs (file-exists-p file))) ((cl-some (if not-essential (lambda (dir) ;; For speed (bug#79137). (string-prefix-p dir file)) (lambda (dir) (file-in-directory-p file dir))) - dirs))))))))) + dirs)) + ((or missing-in-dirs + (file-exists-p file)))))))))) (dolist (buf buffers) (with-current-buffer buf (vc-buffer-sync not-essential))))) commit e9800cabffa73018a83108de2bb4e1686f1e8385 Author: Po Lu Date: Thu Aug 14 19:33:09 2025 +0800 Fix generation of defvars defined by easy-mmode and company in loaddefs.el * lisp/emacs-lisp/loaddefs-gen.el (loaddefs-generate--make-autoload): Restore previously existing conditions for recursing on macroexpansions when processing easy-mmode and related macros. diff --git a/lisp/emacs-lisp/loaddefs-gen.el b/lisp/emacs-lisp/loaddefs-gen.el index c8e05921fc1..9e91a11204d 100644 --- a/lisp/emacs-lisp/loaddefs-gen.el +++ b/lisp/emacs-lisp/loaddefs-gen.el @@ -244,7 +244,25 @@ expand)' among their `declare' forms." (setq expand (let ((load-true-file-name file) (load-file-name file)) (macroexpand form))) - (not (eq car (car expand))))) + (or (and + ;; Previously, macros defined in this list would not + ;; see their expansions processed in place of + ;; themselves if such an expansion did not yield a + ;; `progn', `prog1' or `defalias' form. Not + ;; reproducing these conditions results in the + ;; omission of minor mode variables and suchlike in + ;; loaddefs.el when only the defuns in the + ;; macroexpansions are autoloaded. + (not (memq car '( define-globalized-minor-mode defun defmacro + define-minor-mode define-inline + cl-defun cl-defmacro cl-defgeneric + cl-defstruct pcase-defmacro iter-defun cl-iter-defun + ;; Obsolete; keep until the alias is removed. + easy-mmode-define-global-mode + easy-mmode-define-minor-mode + define-global-minor-mode))) + (not (eq car (car expand)))) + (memq (car expand) '(progn prog1 defalias))))) ;; Recurse on the expansion. (loaddefs-generate--make-autoload expand file 'expansion)) commit 02f5a075dc20e713eed5505d68217c88bb74d9b6 Author: Sean Whitton Date: Thu Aug 14 11:54:33 2025 +0100 vc-hg-checkin-patch: Fix several issues * lisp/vc/vc-hg.el (vc-hg-checkin-patch): Use make-nearby-temp-file. Protect the async process with vc-wait-for-process-before-save. Don't call vc-checkin-hook here, instead return a (async . #) list so that vc-finish-logentry does it. diff --git a/lisp/vc/vc-hg.el b/lisp/vc/vc-hg.el index 5bb826d555f..3a6580c16f4 100644 --- a/lisp/vc/vc-hg.el +++ b/lisp/vc/vc-hg.el @@ -1231,8 +1231,7 @@ REV is ignored." (apply #'vc-hg-command nil 0 files args)))) (defun vc-hg-checkin-patch (patch-string comment) - (let ((parent (current-buffer)) - (patch-file (make-temp-file "hg-patch"))) + (let ((patch-file (make-nearby-temp-file "hg-patch"))) (write-region patch-string nil patch-file) (unwind-protect (let ((args (list "update" @@ -1243,14 +1242,14 @@ REV is ignored." (vc-hg--extract-headers comment))) (if vc-async-checkin (let ((buffer (vc-hg--async-buffer))) - (apply #'vc-hg--async-command buffer args) + (vc-wait-for-process-before-save + (apply #'vc-hg--async-command buffer args) + "Finishing checking in patch....") (with-current-buffer buffer (vc-run-delayed - (vc-compilation-mode 'hg) - (when (buffer-live-p parent) - (with-current-buffer parent - (run-hooks 'vc-checkin-hook))))) - (vc-set-async-update buffer)) + (vc-compilation-mode 'hg))) + (vc-set-async-update buffer) + (list 'async (get-buffer-process buffer))) (apply #'vc-hg-command nil 0 nil args))) (delete-file patch-file)))) commit 9351fad650e4952f9ff1d8fc951957a283802f5e Author: Eshel Yaron Date: Wed Aug 13 21:48:18 2025 +0200 ; * lisp/completion-preview.el: Fix race condition. * lisp/completion-preview.el (completion-preview--update): Bind 'inhibit-quit' to non-nil around completion preview overlay initialization. This binding prevents the surrounding 'while-no-input' from interrupting the overlay initialization, which yields an inconsistent state wherein the overlay is visible but completion-preview-active-mode wasn't enabled. diff --git a/lisp/completion-preview.el b/lisp/completion-preview.el index 84380ee5c54..e5953f4a513 100644 --- a/lisp/completion-preview.el +++ b/lisp/completion-preview.el @@ -521,7 +521,9 @@ candidates or if there are multiple matching completions and (run-hook-wrapped 'completion-at-point-functions #'completion-preview--capf-wrapper) - (when-let* ((suffix (car suffixes))) + (when-let* ((suffix (car suffixes)) + (inhibit-quit t)) + ;; Critical section, do not quit upon receiving input here. (set-text-properties 0 (length suffix) (list 'face (if (cdr suffixes) 'completion-preview commit 7856dd557a76a73d44c9ae13fbe13f09becf5841 Author: Michael Albinus Date: Wed Aug 13 17:33:37 2025 +0200 Limit size of Tramp debug buffer * doc/misc/tramp.texi (Traces and Profiles): Add tramp-debug-buffer-limit to index. Describe it. * lisp/net/tramp-message.el (tramp-debug-buffer-limit): New defcustom. (tramp-debug-message): Use it. (Bug#79182) diff --git a/doc/misc/tramp.texi b/doc/misc/tramp.texi index 7fbd4f898f5..1dc616918d0 100644 --- a/doc/misc/tramp.texi +++ b/doc/misc/tramp.texi @@ -6830,6 +6830,7 @@ they are kept. Example: @vindex tramp-verbose @vindex tramp-debug-to-file @vindex tramp-debug-command-messages +@vindex tramp-debug-buffer-limit @value{tramp} messages are raised with verbosity levels ranging from 0 to 10. @value{tramp} does not display all messages; only those with a @@ -6918,6 +6919,12 @@ strings), and the entry and exit messages for the function @code{tramp-file-name-handler}. This is intended for @value{tramp} maintainers, analyzing the remote commands for performance analysis. +The debug buffer can be very large, if @code{tramp-verbose} is high, +and @value{tramp} runs for a long time. If the buffer size exceeds +@code{tramp-debug-buffer-limit} (3GB by default), a warning will be +raised. This user option can be adapted to your needs; a value of 0 +means that there is no limit (no warning). + @node GNU Free Documentation License @appendix GNU Free Documentation License @@ -6945,7 +6952,7 @@ maintainers, analyzing the remote commands for performance analysis. @bye -@c TODO +@c TODO: @c @c * Say something about the .login and .profile files of the remote @c shells. @@ -6954,3 +6961,5 @@ maintainers, analyzing the remote commands for performance analysis. @c host and then send commands to it. @c @c * Consistent small or capitalized words especially in menus. +@c +@c * Starting with Emacs 29, use 'setopt' in the examples. diff --git a/lisp/net/tramp-message.el b/lisp/net/tramp-message.el index 5131e5fced9..7f66f7d8087 100644 --- a/lisp/net/tramp-message.el +++ b/lisp/net/tramp-message.el @@ -94,6 +94,15 @@ This increases `tramp-verbose' to 6 if necessary." :type 'boolean :link '(info-link :tag "Tramp manual" "(tramp) Traces and Profiles")) +(defcustom tramp-debug-buffer-limit (* 3 1024 1024 1024) ;3GB + "The upper limit of a Tramp debug buffer. +If the size of a debug buffer exceeds this limit, a warning is raised. +Set it to 0 if there is no limit." + :group 'tramp + :version "31.1" + :type 'natnum + :link '(info-link :tag "Tramp manual" "(tramp) Traces and Profiles")) + (defconst tramp-debug-outline-regexp (rx ;; Timestamp. (+ digit) ":" (+ digit) ":" (+ digit) "." (+ digit) blank @@ -281,7 +290,14 @@ ARGUMENTS to actually emit the message (if applicable)." (when tramp-debug-to-file (ignore-errors (write-region - point (point-max) (tramp-get-debug-file-name vec) 'append)))))))) + point (point-max) (tramp-get-debug-file-name vec) 'append)))) + (when (and (natnump tramp-debug-buffer-limit) + (not (zerop tramp-debug-buffer-limit)) + (> (point-max) tramp-debug-buffer-limit)) + (setq-local tramp-debug-buffer-limit nil) + (lwarn + 'tramp :warning + "Tramp debug buffer %S exceeds the limit" (current-buffer))))))) ;;;###tramp-autoload (defun tramp-message (vec-or-proc level fmt-string &rest arguments) commit 40e4394f87909f5d6c94d59cf3aced9bc65fa32a Author: Michael Albinus Date: Tue Aug 12 22:05:44 2025 +0200 Fix file notification problem on Windows. * src/inotify.c (inotify_callback): Set event.frame_or_window to Qnil. * src/keyboard.c (make_lispy_event): Make a better check for the HAVE_W32NOTIFY case when creating a Qfile_notify event. diff --git a/src/inotify.c b/src/inotify.c index c29d940c984..456a9b22a80 100644 --- a/src/inotify.c +++ b/src/inotify.c @@ -339,6 +339,7 @@ inotify_callback (int fd, void *_) struct input_event event; EVENT_INIT (event); event.kind = FILE_NOTIFY_EVENT; + event.frame_or_window = Qnil; for (ssize_t i = 0; i < n; ) { diff --git a/src/keyboard.c b/src/keyboard.c index 5feb0fe231e..a9cbd107dde 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -7220,12 +7220,11 @@ make_lispy_event (struct input_event *event) #ifdef USE_FILE_NOTIFY case FILE_NOTIFY_EVENT: -#ifdef HAVE_W32NOTIFY /* Make an event (file-notify (DESCRIPTOR ACTION FILE) CALLBACK). */ - return list3 (Qfile_notify, event->arg, event->frame_or_window); -#else - return Fcons (Qfile_notify, event->arg); -#endif + if (!NILP (event->frame_or_window)) /* HAVE_W32NOTIFY */ + return list3 (Qfile_notify, event->arg, event->frame_or_window); + else + return Fcons (Qfile_notify, event->arg); #endif /* USE_FILE_NOTIFY */ case SLEEP_EVENT: commit bb1c737531c8d2e78a77b29ddd2db5b89c9c6810 Author: Sean Whitton Date: Tue Aug 12 20:35:49 2025 +0100 vc-test--other-working-trees: Export env vars so Git finds an author * test/lisp/vc/vc-tests/vc-tests.el (vc-test--with-author-identity): New macro, factored out of vc-test--version-diff. (vc-test--version-diff, vc-test--other-working-trees): Use it. diff --git a/test/lisp/vc/vc-tests/vc-tests.el b/test/lisp/vc/vc-tests/vc-tests.el index ba131502b9b..81789814350 100644 --- a/test/lisp/vc/vc-tests/vc-tests.el +++ b/test/lisp/vc/vc-tests/vc-tests.el @@ -584,6 +584,22 @@ This checks also `vc-backend' and `vc-responsible-backend'." (ignore-errors (run-hooks 'vc-test--cleanup-hook)))))) +(defmacro vc-test--with-author-identity (backend &rest body) + (declare (indent 1) (debug t)) + `(let ((process-environment process-environment)) + ;; git tries various approaches to guess a user name and email, + ;; which can fail depending on how the system is configured. + ;; Eg if the user account has no GECOS, git commit can fail with + ;; status 128 "fatal: empty ident name". + (when (memq ,backend '(Bzr Git)) + (setq process-environment (cons "EMAIL=john@doe.ee" + process-environment))) + (when (eq ,backend 'Git) + (setq process-environment (append '("GIT_AUTHOR_NAME=A" + "GIT_COMMITTER_NAME=C") + process-environment))) + ,@body)) + (declare-function log-edit-done "vc/log-edit") (defun vc-test--version-diff (backend) @@ -595,72 +611,62 @@ This checks also `vc-backend' and `vc-responsible-backend'." (file-truename (expand-file-name (make-temp-name "vc-test") temporary-file-directory)))) - (process-environment process-environment) vc-test--cleanup-hook) (vc--fix-home-for-bzr tempdir) - ;; git tries various approaches to guess a user name and email, - ;; which can fail depending on how the system is configured. - ;; Eg if the user account has no GECOS, git commit can fail with - ;; status 128 "fatal: empty ident name". - (when (memq backend '(Bzr Git)) - (setq process-environment (cons "EMAIL=john@doe.ee" - process-environment))) - (if (eq backend 'Git) - (setq process-environment (append '("GIT_AUTHOR_NAME=A" - "GIT_COMMITTER_NAME=C") - process-environment))) - (unwind-protect - (progn - ;; Cleanup. - (add-hook - 'vc-test--cleanup-hook - (let ((dir default-directory)) - (lambda () (delete-directory dir 'recursive)))) - - ;; Create empty repository. Check repository checkout model. - (make-directory default-directory) - (vc-test--create-repo-function backend) - - (let* ((tmp-name (expand-file-name "foo" default-directory)) - (files (list (file-name-nondirectory tmp-name)))) - ;; Write and register a new file. - (write-region "originaltext" nil tmp-name nil 'nomessage) - (vc-register (list backend files)) - - (let ((buff (find-file tmp-name))) - (with-current-buffer buff + (vc-test--with-author-identity backend + + (unwind-protect + (progn + ;; Cleanup. + (add-hook + 'vc-test--cleanup-hook + (let ((dir default-directory)) + (lambda () (delete-directory dir 'recursive)))) + + ;; Create empty repository. Check repository checkout model. + (make-directory default-directory) + (vc-test--create-repo-function backend) + + (let* ((tmp-name (expand-file-name "foo" default-directory)) + (files (list (file-name-nondirectory tmp-name)))) + ;; Write and register a new file. + (write-region "originaltext" nil tmp-name nil 'nomessage) + (vc-register (list backend files)) + + (let ((buff (find-file tmp-name))) + (with-current-buffer buff + (progn + ;; Optionally checkout file. + (when (memq backend '(RCS CVS SCCS)) + (vc-checkout tmp-name)) + + ;; Checkin file. + (vc-checkin files backend) + (insert "Testing vc-version-diff") + (let (vc-async-checkin) + (log-edit-done))))) + + ;; Modify file content. + (when (memq backend '(RCS CVS SCCS)) + (vc-checkout tmp-name)) + (write-region "updatedtext" nil tmp-name nil 'nomessage) + + ;; Check version diff. + (vc-version-diff files nil nil) + (if (eq backend 'Bzr) + (sleep-for 1)) + (should (bufferp (get-buffer "*vc-diff*"))) + + (with-current-buffer "*vc-diff*" (progn - ;; Optionally checkout file. - (when (memq backend '(RCS CVS SCCS)) - (vc-checkout tmp-name)) - - ;; Checkin file. - (vc-checkin files backend) - (insert "Testing vc-version-diff") - (let (vc-async-checkin) - (log-edit-done))))) - - ;; Modify file content. - (when (memq backend '(RCS CVS SCCS)) - (vc-checkout tmp-name)) - (write-region "updatedtext" nil tmp-name nil 'nomessage) - - ;; Check version diff. - (vc-version-diff files nil nil) - (if (eq backend 'Bzr) - (sleep-for 1)) - (should (bufferp (get-buffer "*vc-diff*"))) - - (with-current-buffer "*vc-diff*" - (progn - (let ((rawtext (buffer-substring-no-properties (point-min) - (point-max)))) - (should (string-search "-originaltext" rawtext)) - (should (string-search "+updatedtext" rawtext))))))) + (let ((rawtext (buffer-substring-no-properties (point-min) + (point-max)))) + (should (string-search "-originaltext" rawtext)) + (should (string-search "+updatedtext" rawtext))))))) - ;; Save exit. - (ignore-errors - (run-hooks 'vc-test--cleanup-hook)))))) + ;; Save exit. + (ignore-errors + (run-hooks 'vc-test--cleanup-hook))))))) (declare-function vc-git--program-version "vc-git") @@ -672,104 +678,104 @@ This checks also `vc-backend' and `vc-responsible-backend'." (file-name-as-directory (expand-file-name (make-temp-name "vc-test") temporary-file-directory))) - (process-environment process-environment) vc-test--cleanup-hook) - (unwind-protect - (progn - ;; Cleanup. - (add-hook - 'vc-test--cleanup-hook - (let ((dir default-directory)) - (lambda () - (delete-directory dir 'recursive) - (dolist (name '("first" "second" "first")) - (project-forget-project - (expand-file-name name default-directory)))))) - - (let* ((first (file-truename - (file-name-as-directory - (expand-file-name "first" default-directory)))) - (second (file-truename - (file-name-as-directory - (expand-file-name "second" default-directory)))) - (third (file-truename - (file-name-as-directory - (expand-file-name "third" default-directory)))) - (tmp-name (expand-file-name "foo" first)) - (project-list-file - (expand-file-name "projects.eld" default-directory))) - - ;; Set up the first working tree. - (make-directory first t) - (let ((default-directory first)) - (vc-test--create-repo-function backend) - (write-region "foo" nil tmp-name nil 'nomessage) - (vc-register `(,backend (,(file-name-nondirectory tmp-name))))) - (with-current-buffer (find-file-noselect tmp-name) - (vc-checkin (list (file-name-nondirectory tmp-name)) backend) - (insert "Testing other working trees") - (let (vc-async-checkin) - (log-edit-done)) - - ;; Set up the second working tree. - ;; Stub out `vc-dir' so that it doesn't start a - ;; background update process which won't like it when we - ;; start moving directories around. - ;; For the backends which do additional prompting (as - ;; specified in the API for this backend function) we - ;; need to stub that out. - (cl-letf (((symbol-function 'vc-dir) #'ignore)) - (cl-ecase backend - (Git (cl-letf (((symbol-function 'completing-read) - (lambda (&rest _ignore) ""))) - (vc-add-working-tree backend second))) - (Hg (vc-add-working-tree backend second))))) - - ;; Test `known-other-working-trees'. - (with-current-buffer (find-file-noselect tmp-name) - (should - (equal (list second) - (vc-call-backend backend 'known-other-working-trees))) - (let ((default-directory second)) - (should - (equal (list first) - (vc-call-backend backend 'known-other-working-trees)))) - - ;; Test `move-working-tree'. - (vc-move-working-tree backend second third) - (should - (equal (list third) - (vc-call-backend backend 'known-other-working-trees))) - (should-not (file-directory-p second)) - (should (file-directory-p third)) - ;; Moving the first working tree is only supported - ;; for some backends. - (cond ((and (eq backend 'Git) - (version<= "2.29" (vc-git--program-version))) - (let ((default-directory third)) - (vc-move-working-tree backend first second)) - (let ((default-directory third)) - (should - (equal (list second) - (vc-call-backend backend - 'known-other-working-trees)))) - (should-not (file-directory-p first)) - (should (file-directory-p second)) - (vc-move-working-tree backend second first)) - ((eq backend 'Hg) - (let ((default-directory third)) - (should-error (vc-move-working-tree backend - first second))))) - (vc-move-working-tree backend third second) - - ;; Test `delete-working-tree'. + (vc-test--with-author-identity backend + (unwind-protect + (progn + ;; Cleanup. + (add-hook + 'vc-test--cleanup-hook + (let ((dir default-directory)) + (lambda () + (delete-directory dir 'recursive) + (dolist (name '("first" "second" "first")) + (project-forget-project + (expand-file-name name default-directory)))))) + + (let* ((first (file-truename + (file-name-as-directory + (expand-file-name "first" default-directory)))) + (second (file-truename + (file-name-as-directory + (expand-file-name "second" default-directory)))) + (third (file-truename + (file-name-as-directory + (expand-file-name "third" default-directory)))) + (tmp-name (expand-file-name "foo" first)) + (project-list-file + (expand-file-name "projects.eld" default-directory))) + + ;; Set up the first working tree. + (make-directory first t) (let ((default-directory first)) - (vc-delete-working-tree backend second) - (should-not (file-directory-p second)))))) - - ;; Save exit. - (ignore-errors - (run-hooks 'vc-test--cleanup-hook)))))) + (vc-test--create-repo-function backend) + (write-region "foo" nil tmp-name nil 'nomessage) + (vc-register `(,backend (,(file-name-nondirectory tmp-name))))) + (with-current-buffer (find-file-noselect tmp-name) + (vc-checkin (list (file-name-nondirectory tmp-name)) backend) + (insert "Testing other working trees") + (let (vc-async-checkin) + (log-edit-done)) + + ;; Set up the second working tree. + ;; Stub out `vc-dir' so that it doesn't start a + ;; background update process which won't like it when we + ;; start moving directories around. + ;; For the backends which do additional prompting (as + ;; specified in the API for this backend function) we + ;; need to stub that out. + (cl-letf (((symbol-function 'vc-dir) #'ignore)) + (cl-ecase backend + (Git (cl-letf (((symbol-function 'completing-read) + (lambda (&rest _ignore) ""))) + (vc-add-working-tree backend second))) + (Hg (vc-add-working-tree backend second))))) + + ;; Test `known-other-working-trees'. + (with-current-buffer (find-file-noselect tmp-name) + (should + (equal (list second) + (vc-call-backend backend 'known-other-working-trees))) + (let ((default-directory second)) + (should + (equal (list first) + (vc-call-backend backend 'known-other-working-trees)))) + + ;; Test `move-working-tree'. + (vc-move-working-tree backend second third) + (should + (equal (list third) + (vc-call-backend backend 'known-other-working-trees))) + (should-not (file-directory-p second)) + (should (file-directory-p third)) + ;; Moving the first working tree is only supported + ;; for some backends. + (cond ((and (eq backend 'Git) + (version<= "2.29" (vc-git--program-version))) + (let ((default-directory third)) + (vc-move-working-tree backend first second)) + (let ((default-directory third)) + (should + (equal (list second) + (vc-call-backend backend + 'known-other-working-trees)))) + (should-not (file-directory-p first)) + (should (file-directory-p second)) + (vc-move-working-tree backend second first)) + ((eq backend 'Hg) + (let ((default-directory third)) + (should-error (vc-move-working-tree backend + first second))))) + (vc-move-working-tree backend third second) + + ;; Test `delete-working-tree'. + (let ((default-directory first)) + (vc-delete-working-tree backend second) + (should-not (file-directory-p second)))))) + + ;; Save exit. + (ignore-errors + (run-hooks 'vc-test--cleanup-hook))))))) ;; Create the test cases. commit e48592ef3bc0734bcc4655cc3863f35a30e14437 Author: Sean Whitton Date: Tue Aug 12 20:26:18 2025 +0100 * lisp/vc/vc-hg.el (vc-hg-checkin): Pass '-A' to 'hg commit'. diff --git a/lisp/vc/vc-hg.el b/lisp/vc/vc-hg.el index b39a8c6928a..5bb826d555f 100644 --- a/lisp/vc/vc-hg.el +++ b/lisp/vc/vc-hg.el @@ -1216,7 +1216,7 @@ It is based on `log-edit-mode', and has Hg-specific extensions.") (defun vc-hg-checkin (files comment &optional _rev) "Hg-specific version of `vc-BACKEND-checkin'. REV is ignored." - (let ((args (nconc (list "commit" "-m") + (let ((args (nconc (list "commit" "-A" "-m") (vc-hg--extract-headers comment)))) (if vc-async-checkin (let ((buffer (vc-hg--async-buffer))) diff --git a/lisp/vc/vc.el b/lisp/vc/vc.el index 135762fc806..f2972f10e6a 100644 --- a/lisp/vc/vc.el +++ b/lisp/vc/vc.el @@ -1604,7 +1604,7 @@ from which to check out the file(s)." ;; If committing a mix of removed and edited files, the ;; fileset has state = 'edited. Rather than checking the ;; state of each individual file in the fileset, it seems - ;; simplest to just check if the file exists. Bug#9781. + ;; simplest to just check if the file exists. Bug#9781. (when (and (file-exists-p file) (not (file-writable-p file))) ;; Make the file-buffer read-write. (unless (y-or-n-p (format "%s is edited but read-only; make it writable and continue? " file)) commit 56bfd63bb8dd73d3cd552f3a663bd7e9c33f3ab9 Author: Eli Zaretskii Date: Tue Aug 12 21:25:56 2025 +0300 ; Fix documentation of a recent commit * lisp/tab-line.el (tab-line-tabs-window-buffers-filter-function): Fix wording of option value :tags. (tab-line-tabs-non-excluded): Doc fix. (Bug#79159) diff --git a/lisp/tab-line.el b/lisp/tab-line.el index 2952f3cc116..7caae8bc2c1 100644 --- a/lisp/tab-line.el +++ b/lisp/tab-line.el @@ -573,8 +573,8 @@ generate the group name." #'identity "Filter which buffers should be displayed in the tab line." :type '(choice function - (const :tag "No filter the buffers" identity) - (const :tag "Show non-excluded buffers only" tab-line-tabs-non-excluded)) + (const :tag "Show all buffers" identity) + (const :tag "Omit excluded buffers" tab-line-tabs-non-excluded)) :group 'tab-line :version "31.1") @@ -582,7 +582,7 @@ generate the group name." (defvar tab-line-exclude-modes) (defun tab-line-tabs-non-excluded (buffers) - "Filter BUFFERS and return non-excluded buffers list. + "Filter BUFFERS to remove excluded buffers from the list. Intended to be used in `tab-line-tabs-window-buffers-filter-function'." (seq-remove (lambda (b) commit d487591edb9652e1ae5effe384f46b6485993761 Author: Juri Linkov Date: Tue Aug 12 20:49:02 2025 +0300 * lisp/tab-bar.el: Revert caching in 'tab-bar-format-align-right'. (tab-bar--align-right-cache): Remove variable. (tab-bar-format-align-right): Remove uses of 'tab-bar--align-right-cache' (bug#79141). diff --git a/lisp/tab-bar.el b/lisp/tab-bar.el index 396658af640..4c585985fb6 100644 --- a/lisp/tab-bar.el +++ b/lisp/tab-bar.el @@ -1194,9 +1194,6 @@ when the tab is current. Return the result as a keymap." `((add-tab menu-item ,tab-bar-new-button tab-bar-new-tab :help "New tab")))) -(defvar tab-bar--align-right-cache nil - "Optimization for `tab-bar-format-align-right'.") - (defun tab-bar-format-align-right (&optional rest) "Align the rest of tab bar items to the right. The argument `rest' is used for special handling of this item @@ -1209,13 +1206,7 @@ This prevents calling other non-idempotent items like (rest (mapconcat (lambda (item) (nth 2 item)) rest "")) (hpos (progn (add-face-text-property 0 (length rest) 'tab-bar t rest) - (or (and tab-bar--align-right-cache - (equal-including-properties - (car tab-bar--align-right-cache) rest) - (cdr tab-bar--align-right-cache)) - (let ((width (string-pixel-width rest))) - (setq tab-bar--align-right-cache (cons rest width)) - width)))) + (string-pixel-width rest))) (str (propertize " " 'display ;; The `right' spec doesn't work on TTY frames ;; when windows are split horizontally (bug#59620) commit 8f08cb386efa989d75d10b95b26999b8ff38cb72 Author: Elías Gabriel Pérez Date: Fri Aug 1 20:14:59 2025 -0600 tab-line: Allow to hide excluded buffers in the tab line. (Bug#79159) * etc/NEWS: Announce changes. * lisp/tab-line.el (tab-line-tabs-window-buffers-filter-function): New user option. (tab-line-tabs-non-excluded): New function. (tab-line-tabs-window-buffers): Update. diff --git a/etc/NEWS b/etc/NEWS index 79d124bd585..f8d4b8f2f67 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -436,6 +436,11 @@ a buffer. The value must be a condition which is passed to With this user option, if non-nil (the default), the tab close button will change its appearance if the tab buffer has been modified. +--- +*** New user option 'tab-line-tabs-window-buffers-filter-function'. +This user option controls which buffers should appear in the tab line. +By default, this is set to not filter the buffers. + ** Project --- diff --git a/lisp/tab-line.el b/lisp/tab-line.el index 2d8c75574bb..2952f3cc116 100644 --- a/lisp/tab-line.el +++ b/lisp/tab-line.el @@ -569,6 +569,30 @@ generate the group name." sorted-buffers))) (cons group-tab tabs)))) +(defcustom tab-line-tabs-window-buffers-filter-function + #'identity + "Filter which buffers should be displayed in the tab line." + :type '(choice function + (const :tag "No filter the buffers" identity) + (const :tag "Show non-excluded buffers only" tab-line-tabs-non-excluded)) + :group 'tab-line + :version "31.1") + +(defvar tab-line-exclude-buffers) +(defvar tab-line-exclude-modes) + +(defun tab-line-tabs-non-excluded (buffers) + "Filter BUFFERS and return non-excluded buffers list. +Intended to be used in `tab-line-tabs-window-buffers-filter-function'." + (seq-remove + (lambda (b) + (or (memq (buffer-local-value 'major-mode b) + tab-line-exclude-modes) + (buffer-match-p tab-line-exclude-buffers b) + (get (buffer-local-value 'major-mode b) 'tab-line-exclude) + (buffer-local-value 'tab-line-exclude b))) + buffers)) + (defun tab-line-tabs-window-buffers () "Return a list of tabs that should be displayed in the tab line. By default returns a list of window buffers, i.e. buffers previously @@ -585,9 +609,11 @@ variable `tab-line-tabs-function'." (prev-buffers (seq-filter #'buffer-live-p prev-buffers)) ;; Remove next-buffers from prev-buffers (prev-buffers (seq-difference prev-buffers next-buffers))) - (append (reverse prev-buffers) - (list buffer) - next-buffers))) + (funcall + tab-line-tabs-window-buffers-filter-function + (append (reverse prev-buffers) + (list buffer) + next-buffers)))) (defun tab-line-tabs-fixed-window-buffers () "Like `tab-line-tabs-window-buffers' but keep stable sorting order. commit c75c95ad58a0d20130407a254d43c165eb1ab6a8 Author: Sean Whitton Date: Tue Aug 12 16:01:12 2025 +0100 vc-buffer-sync-fileset: Optimization when NOT-ESSENTIAL is non-nil * lisp/vc/vc.el (vc-buffer-sync-fileset): When NOT-ESSENTIAL is non-nil, use string-prefix-p instead of file-in-directory-p (bug#79137). diff --git a/lisp/vc/vc.el b/lisp/vc/vc.el index 558054e7338..135762fc806 100644 --- a/lisp/vc/vc.el +++ b/lisp/vc/vc.el @@ -2496,7 +2496,7 @@ buffers whose files exist on disk. Otherwise it syncs all of them." dirs buffers) (dolist (name (cadr fileset)) (if (file-directory-p name) - (push name dirs) + (push (file-name-as-directory name) dirs) (when-let* ((buf (find-buffer-visiting name))) (push buf buffers)))) (when dirs @@ -2507,8 +2507,12 @@ buffers whose files exist on disk. Otherwise it syncs all of them." (and-let* ((file (buffer-local-value 'buffer-file-name buf)) ((or missing-in-dirs (file-exists-p file))) - ((cl-some (lambda (dir) - (file-in-directory-p file dir)) + ((cl-some (if not-essential + (lambda (dir) + ;; For speed (bug#79137). + (string-prefix-p dir file)) + (lambda (dir) + (file-in-directory-p file dir))) dirs))))))))) (dolist (buf buffers) (with-current-buffer buf commit 2923d244a332f73fc7e058a3aa6e9bff8f72f1c5 Author: Sean Whitton Date: Tue Aug 12 14:36:15 2025 +0100 vc-test--other-working-trees: Fix a skip condition * test/lisp/vc/vc-tests/vc-tests.el (vc-test--other-working-trees): For Git, don't try moving the first working tree unless we have Git>=2.29. diff --git a/test/lisp/vc/vc-tests/vc-tests.el b/test/lisp/vc/vc-tests/vc-tests.el index a213014e1b5..ba131502b9b 100644 --- a/test/lisp/vc/vc-tests/vc-tests.el +++ b/test/lisp/vc/vc-tests/vc-tests.el @@ -744,22 +744,22 @@ This checks also `vc-backend' and `vc-responsible-backend'." (should (file-directory-p third)) ;; Moving the first working tree is only supported ;; for some backends. - (cl-ecase backend - (Git - (let ((default-directory third)) - (vc-move-working-tree backend first second)) - (let ((default-directory third)) - (should - (equal (list second) - (vc-call-backend backend - 'known-other-working-trees)))) - (should-not (file-directory-p first)) - (should (file-directory-p second)) - (vc-move-working-tree backend second first)) - (Hg - (let ((default-directory third)) - (should-error (vc-move-working-tree backend - first second))))) + (cond ((and (eq backend 'Git) + (version<= "2.29" (vc-git--program-version))) + (let ((default-directory third)) + (vc-move-working-tree backend first second)) + (let ((default-directory third)) + (should + (equal (list second) + (vc-call-backend backend + 'known-other-working-trees)))) + (should-not (file-directory-p first)) + (should (file-directory-p second)) + (vc-move-working-tree backend second first)) + ((eq backend 'Hg) + (let ((default-directory third)) + (should-error (vc-move-working-tree backend + first second))))) (vc-move-working-tree backend third second) ;; Test `delete-working-tree'. commit 81aa814e59358b8ba00832ac3259eea6ff56800d Author: Sean Whitton Date: Tue Aug 12 14:34:45 2025 +0100 vc-do-command: In batch mode, print the output of failed commands * lisp/vc/vc-dispatcher.el (vc-do-command): In batch mode, when the command fails, print its output. diff --git a/lisp/vc/vc-dispatcher.el b/lisp/vc/vc-dispatcher.el index 139ba5aa70a..c4ad148bdf1 100644 --- a/lisp/vc/vc-dispatcher.el +++ b/lisp/vc/vc-dispatcher.el @@ -469,6 +469,9 @@ case, and the process object in the asynchronous case." (pop-to-buffer (current-buffer)) (goto-char (point-min)) (shrink-window-if-larger-than-buffer)) + (when noninteractive + (with-current-buffer buffer + (message (string-trim (buffer-string))))) (error "Failed (%s): %s" (if (integerp status) (format "status %d" status) commit c687756ce20bd98cb8af91bb555f638ba2132cfd Author: Sean Whitton Date: Tue Aug 12 14:11:13 2025 +0100 vc-test-git07-other-working-trees: Widen scope of skip on Git<=2.17 * test/lisp/vc/vc-tests/vc-tests.el (vc-test--other-working-trees): Delete condition to skip 'move-working-tree' tests on Git<=2.17. (vc-test-git07-other-working-trees): Skip this whole test on Git<=2.17 (discussion in bug#79024). diff --git a/test/lisp/vc/vc-tests/vc-tests.el b/test/lisp/vc/vc-tests/vc-tests.el index 2c2d97aaf4a..a213014e1b5 100644 --- a/test/lisp/vc/vc-tests/vc-tests.el +++ b/test/lisp/vc/vc-tests/vc-tests.el @@ -736,33 +736,31 @@ This checks also `vc-backend' and `vc-responsible-backend'." (vc-call-backend backend 'known-other-working-trees)))) ;; Test `move-working-tree'. - (unless (and (eq backend 'Git) - (version< (vc-git--program-version) "2.17")) - (vc-move-working-tree backend second third) - (should - (equal (list third) - (vc-call-backend backend 'known-other-working-trees))) - (should-not (file-directory-p second)) - (should (file-directory-p third)) - ;; Moving the first working tree is only supported - ;; for some backends. - (cl-ecase backend - (Git - (let ((default-directory third)) - (vc-move-working-tree backend first second)) - (let ((default-directory third)) - (should - (equal (list second) - (vc-call-backend backend - 'known-other-working-trees)))) - (should-not (file-directory-p first)) - (should (file-directory-p second)) - (vc-move-working-tree backend second first)) - (Hg - (let ((default-directory third)) - (should-error (vc-move-working-tree backend - first second))))) - (vc-move-working-tree backend third second)) + (vc-move-working-tree backend second third) + (should + (equal (list third) + (vc-call-backend backend 'known-other-working-trees))) + (should-not (file-directory-p second)) + (should (file-directory-p third)) + ;; Moving the first working tree is only supported + ;; for some backends. + (cl-ecase backend + (Git + (let ((default-directory third)) + (vc-move-working-tree backend first second)) + (let ((default-directory third)) + (should + (equal (list second) + (vc-call-backend backend + 'known-other-working-trees)))) + (should-not (file-directory-p first)) + (should (file-directory-p second)) + (vc-move-working-tree backend second first)) + (Hg + (let ((default-directory third)) + (should-error (vc-move-working-tree backend + first second))))) + (vc-move-working-tree backend third second) ;; Test `delete-working-tree'. (let ((default-directory first)) @@ -929,6 +927,9 @@ This checks also `vc-backend' and `vc-responsible-backend'." (car (process-lines-ignore-status "hg" "--config=extensions.share=" "share"))))) + (skip-when + (and (eq ',backend 'Git) + (version< (vc-git--program-version) "2.17"))) (let ((vc-hg-global-switches (cons "--config=extensions.share=" vc-hg-global-switches))) (vc-test--other-working-trees ',backend))))))) commit ae5287c37b7d02910860386e22ae788a25a85f27 Author: Eshel Yaron Date: Tue Aug 12 13:29:19 2025 +0200 ; Fix bug#79212. * lisp/help-fns.el (describe-mode): Fix output when we do not find the definition source. diff --git a/lisp/help-fns.el b/lisp/help-fns.el index 9cd30107002..e9601d52e33 100644 --- a/lisp/help-fns.el +++ b/lisp/help-fns.el @@ -2253,13 +2253,14 @@ is enabled in the Help buffer." (describe-function major)))) (insert " mode") (when-let* ((file-name (find-lisp-object-file-name major nil))) - (insert (format " defined in %s:\n\n" + (insert (format " defined in %s" (buttonize (help-fns-short-filename file-name) (lambda (_) (help-function-def--button-function major file-name)))))) - (insert (help-split-fundoc (documentation major) nil 'doc) + (insert ":\n\n" + (help-split-fundoc (documentation major) nil 'doc) (with-current-buffer buffer (help-fns--list-local-commands))) (ensure-empty-lines 1) commit 498b2b6d2a3c860daaa0a5901a49442c0c6c501f Author: Po Lu Date: Tue Aug 12 09:34:57 2025 +0800 Fix build without tree-sitter * lisp/textmodes/mhtml-ts-mode.el (mhtml-ts-mode--range-settings): Don't invoke `treesit-language-available-p' unless bound. diff --git a/lisp/textmodes/mhtml-ts-mode.el b/lisp/textmodes/mhtml-ts-mode.el index 4182eca0f4e..bf50148b84e 100644 --- a/lisp/textmodes/mhtml-ts-mode.el +++ b/lisp/textmodes/mhtml-ts-mode.el @@ -452,7 +452,8 @@ Calls REPORT-FN directly. Requires tidy." ;; jsdoc is not mandatory for js-ts-mode, so we respect this by ;; adding jsdoc range rules only when jsdoc is available. - (when (treesit-language-available-p 'jsdoc t) + (when (and (fboundp 'treesit-language-available-p) + (treesit-language-available-p 'jsdoc t)) (treesit-range-rules :embed 'jsdoc :host 'javascript commit 5b9f4ee76e2acdd56fa6a36d5a6a29d27287c427 Author: Dr.Sc.KAWAMOTO,Takuji Date: Sat Aug 2 13:15:06 2025 +0900 Fix NS frame position update after resize/move Fixes bug#74074, bug#79164. * src/nsterm.m ([EmacsView windowDidEndLiveResize:]): ([EmacsView updateFramePosition]): New functions. ([EmacsView windowDidMove:]): Move contents of this function into updateFramePosition and call it. Copyright-paperwork-exempt: yes diff --git a/src/nsterm.m b/src/nsterm.m index b006b4d5dd0..5127739e2d9 100644 --- a/src/nsterm.m +++ b/src/nsterm.m @@ -6818,6 +6818,11 @@ - (BOOL)fulfillService: (NSString *)name withArg: (NSString *)arg @implementation EmacsView +- (void)windowDidEndLiveResize:(NSNotification *)notification +{ + [self updateFramePosition]; +} + /* Needed to inform when window closed from lisp. */ - (void) setWindowClosing: (BOOL)closing { @@ -7970,6 +7975,37 @@ - (BOOL)windowShouldClose: (id)sender } +- (void)updateFramePosition +{ + NSWindow *win = [self window]; + NSRect r = [win frame]; + NSArray *screens = [NSScreen screens]; + NSScreen *screen = [screens objectAtIndex: 0]; + + if (!emacsframe->output_data.ns) + return; + + if (screen != nil) + { + emacsframe->left_pos = (NSMinX (r) + - NS_PARENT_WINDOW_LEFT_POS (emacsframe)); + emacsframe->top_pos = (NS_PARENT_WINDOW_TOP_POS (emacsframe) + - NSMaxY (r)); + + if (emacs_event) + { + struct input_event ie; + EVENT_INIT (ie); + ie.kind = MOVE_FRAME_EVENT; + XSETFRAME (ie.frame_or_window, emacsframe); + XSETINT (ie.x, emacsframe->left_pos); + XSETINT (ie.y, emacsframe->top_pos); + kbd_buffer_store_event (&ie); + } + } +} + + - (NSSize)windowWillResize: (NSWindow *)sender toSize: (NSSize)frameSize /* Normalize frame to gridded text size. */ { @@ -8304,34 +8340,9 @@ - (instancetype) initFrameFromEmacs: (struct frame *)f - (void)windowDidMove: sender { - NSWindow *win = [self window]; - NSRect r = [win frame]; - NSArray *screens = [NSScreen screens]; - NSScreen *screen = [screens objectAtIndex: 0]; - NSTRACE ("[EmacsView windowDidMove:]"); - if (!emacsframe->output_data.ns) - return; - - if (screen != nil) - { - emacsframe->left_pos = (NSMinX (r) - - NS_PARENT_WINDOW_LEFT_POS (emacsframe)); - emacsframe->top_pos = (NS_PARENT_WINDOW_TOP_POS (emacsframe) - - NSMaxY (r)); - - if (emacs_event) - { - struct input_event ie; - EVENT_INIT (ie); - ie.kind = MOVE_FRAME_EVENT; - XSETFRAME (ie.frame_or_window, emacsframe); - XSETINT (ie.x, emacsframe->left_pos); - XSETINT (ie.y, emacsframe->top_pos); - kbd_buffer_store_event (&ie); - } - } + [self updateFramePosition]; } commit 4293a7678b0bf5180a27093c7d65e325b8753354 Author: Eli Zaretskii Date: Mon Aug 11 21:44:26 2025 +0300 ; Fix last change in mhtml-ts-mode.el * lisp/textmodes/mhtml-ts-mode.el (mhtml-ts-mode--range-settings): Avoid asking about grammar installation when loading. (Bug#79215) diff --git a/lisp/textmodes/mhtml-ts-mode.el b/lisp/textmodes/mhtml-ts-mode.el index 66bb27ced10..4182eca0f4e 100644 --- a/lisp/textmodes/mhtml-ts-mode.el +++ b/lisp/textmodes/mhtml-ts-mode.el @@ -452,7 +452,7 @@ Calls REPORT-FN directly. Requires tidy." ;; jsdoc is not mandatory for js-ts-mode, so we respect this by ;; adding jsdoc range rules only when jsdoc is available. - (when (treesit-ensure-installed 'jsdoc) + (when (treesit-language-available-p 'jsdoc t) (treesit-range-rules :embed 'jsdoc :host 'javascript commit fd5866cae4ae500928965516160a979cd5cd87d6 Author: Dmitry Gutov Date: Mon Aug 11 02:33:19 2025 +0300 ; Coding style fix diff --git a/src/thread.c b/src/thread.c index 3d29f6c591a..f48392261f6 100644 --- a/src/thread.c +++ b/src/thread.c @@ -1200,8 +1200,8 @@ void thread_all_before_buffer_killed (Lisp_Object current) { struct thread_state *iter; - struct buffer * other = NULL; - struct buffer * b = XBUFFER (current); + struct buffer *other = NULL; + struct buffer *b = XBUFFER (current); struct thread_state *caller_thread = current_thread; for (iter = all_threads; iter; iter = iter->next_thread) commit a15a4fa94b77ed875e324c47bf4968e58a50a9f8 Author: JD Smith <93749+jdtsmith@users.noreply.github.com> Date: Sat Jul 26 20:56:13 2025 -0400 Do not include interactive args in autoload files (Bug#78995) * lisp/emacs-lisp/loaddefs-gen.el (loaddefs-generate--make-autoload): Do not include `interactive' ARG-DESCRIPTOR in generated autoload forms for function-defining macros, but only modes. diff --git a/lisp/emacs-lisp/loaddefs-gen.el b/lisp/emacs-lisp/loaddefs-gen.el index fc05e8b9ec1..c8e05921fc1 100644 --- a/lisp/emacs-lisp/loaddefs-gen.el +++ b/lisp/emacs-lisp/loaddefs-gen.el @@ -291,8 +291,8 @@ expand)' among their `declare' forms." t) (and (eq (car-safe (car body)) 'interactive) ;; List of modes or just t. - (or (if (nthcdr 1 (car body)) - (list 'quote (nthcdr 1 (car body))) + (or (if (nthcdr 2 (car body)) + (list 'quote (nthcdr 2 (car body))) t)))) ,(if macrop ''macro nil))))) commit 7486e5c3680b41cb58cb6eb1fa5aa3620d4a50bd Author: JD Smith <93749+jdtsmith@users.noreply.github.com> Date: Thu Jul 24 15:42:10 2025 -0400 Implement new autoload macro expansion declare form Currently, a hard-coded set of macros is automatically expanded during generation of autoloads. To allow user macros to request such expansion, this implements a new declare form `autoload-macro' (Bug#78995), with supported value `expand'. For example, macros which wrap `define-minor-mode', can declare `(autoload-macro expand)' to request that ;;;###autoload-adorned calls to the macro are expanded during generation, such that an autoload for the resulting function is created. * lisp/emacs-lisp/byte-run.el (byte-run--set-autoload-macro): Handle autoload-macro declare forms. (macro-declarations-alist) Add handler for 'autoload-macro declare forms. (defmacro, defun): * lisp/emacs-lisp/cl-generic.el (cl-defgeneric, cl-defun) (cl-iter-defun, cl-defmacro, cl-defstruct): * lisp/emacs-lisp/easy-mmode.el (define-minor-mode, define-globalized-minor-mode, iter-defun): * lisp/emacs-lisp/inline.el (define-inline): * lisp/emacs-lisp/pcase.el (pcase-defmacro): Declare (autoload-macro expand) to request expansion of the macro during autoload generation. * lisp/emacs-lisp/loaddefs-gen.el (loaddefs-generate--make-autoload): Handle the `autoload-macro=expand' property for macros. Load the ;;;###autoload-containing file if an unknown symbol is encountered in the car of the following form, to give packages a chance to define their macros and request expansion. Factor list of special function-defining macros out as a constant variable: `loaddefs--defining-macros'. * doc/lispref/functions.texi (Declare Form): * doc/lispref/loading.texi (Autoload): Document `autoload-macro'. diff --git a/doc/lispref/functions.texi b/doc/lispref/functions.texi index ea6e07e05fb..1b2a21e160c 100644 --- a/doc/lispref/functions.texi +++ b/doc/lispref/functions.texi @@ -2742,6 +2742,14 @@ Indent calls to this function or macro according to @var{indent-spec}. This is typically used for macros, though it works for functions too. @xref{Indenting Macros}. +@cindex @code{autoload-macro} (@code{declare} spec) +@item (autoload-macro @var{value}) +This is used when defining a macro. If @var{value} is @samp{expand}, +any calls to the macro which follow an autoload comment will first be +expanded during generation of the autoloads. This declaration is used +as an alternative to hard-coding lists of macros to expand in +@code{loaddefs-generate--make-autoload}. @xref{Autoload}. + @item (interactive-only @var{value}) Set the function's @code{interactive-only} property to @var{value}. @xref{The interactive-only property}. diff --git a/doc/lispref/loading.texi b/doc/lispref/loading.texi index 7fa64b72999..836d980ff0d 100644 --- a/doc/lispref/loading.texi +++ b/doc/lispref/loading.texi @@ -665,10 +665,12 @@ Building Emacs loads @file{loaddefs.el} and thus calls @code{autoload}. The same magic comment can copy any kind of form into @file{loaddefs.el}. The form following the magic comment is copied -verbatim, @emph{except} if it is one of the forms which the autoload -facility handles specially (e.g., by conversion into an -@code{autoload} call). The forms which are not copied verbatim are -the following: +verbatim, unless it is a form which the autoload facility handles +specially, by conversion directly into an @code{autoload} call, or by +recursively expanding the macro. Any macro can request expansion of +autoloads which call it during generation; @xref{autoload-macro expand}. + +The following forms are handled specially: @table @asis @item Definitions for function or function-like objects: @@ -684,8 +686,12 @@ and @code{define-overloadable-function} (see the commentary in @item Other definition types: @code{defcustom}, @code{defgroup}, @code{deftheme}, @code{defclass} -(@pxref{Top,EIEIO,,eieio,EIEIO}), and @code{define-skeleton} -(@pxref{Top,Autotyping,,autotype,Autotyping}). +(@pxref{Top,EIEIO,,eieio,EIEIO}), @code{define-skeleton} +(@pxref{Top,Autotyping,,autotype,Autotyping}), +@code{transient-define-prefix}, @code{transient-define-suffix}, +@code{transient-define-infix}, @code{transient-define-argument}, and +@code{transient-define-group} (@pxref{TOP,Transient,,transient,Transient +User and Developer Manual}). @end table You can also use a magic comment to execute a form at build time @@ -727,11 +733,50 @@ keep the line length down. @samp{(fn)} in the usage part of the documentation string is replaced with the function's name when the various help functions (@pxref{Help Functions}) display it. +@anchor{autoload-macro expand} If you write a function definition with an unusual macro that is not -one of the known and recognized function definition methods, use of an -ordinary magic autoload comment would copy the whole definition into -@code{loaddefs.el}. That is not desirable. You can put the desired -@code{autoload} call into @code{loaddefs.el} instead by writing this: +one of the known and recognized function definition methods, using an +ordinary magic autoload comment with a call to the macro would require +autoloading the macro definition itself to work. Doing so copies the +whole macro definition into the autoload file. If that is not desired, +you can use the special declare form @code{(autoload-macro expand)} in +your macro definition (@pxref{Declare Form}), instead of autoloading it. +This indicates to the autoload system that calls to your macro following +@samp{;;;###autoload} should be expanded, similar to how the special +functions listed above are handled. For example, a macro which wraps +@code{define-minor-mode} can request expansion, so that proper +@code{autoload} calls for the function it defines are generated. + +@cindex @code{:autoload-end} +The keyword symbol @code{:autoload-end} can be used in the expansion of +a macro to prevent including unwanted forms in the autoload output. Its +presence causes any further elements within the form where it appears to +be silently skipped. For example, if during autoload generation, a +macro's expansion includes: + +@example +(progn + (put my-mode 'visible-prop t) + :autoload-end + (put my-mode 'hidden-prop nil)) +@end example + +@noindent +the final form (@w{@code{(put my-mode 'hidden-prop nil)}}) will not be +copied into the autoload file. + +Note that, if a symbol in the @code{car} of an autoloaded form is found +to be undefined during autoload generation, the file in which the +associated @samp{;;;###autoload} appears will itself be loaded, to give +the macro a chance to be defined. Packages which use special loading +mechanisms, including loading their own @file{@var{package}-loaddefs.el} +file, should therefore gracefully handle load errors, since these files +may not yet exist during autoload generation. This can be done, e.g., +by setting the @var{no-error} argument of @code{require} non-@code{nil} +(@pxref{Named Features})). + +Alternatively, instead of expansion, you can put the desired +@code{autoload} call into @file{loaddefs.el} by writing this: @example ;;;###autoload (autoload 'foo "myfile") diff --git a/lisp/emacs-lisp/byte-run.el b/lisp/emacs-lisp/byte-run.el index 6412c8cde22..49fd6084693 100644 --- a/lisp/emacs-lisp/byte-run.el +++ b/lisp/emacs-lisp/byte-run.el @@ -286,6 +286,12 @@ This is used by `declare'.") (list 'put (list 'quote name) ''edebug-form-spec (list 'quote spec))))) +(defalias 'byte-run--set-autoload-macro + #'(lambda (name _args spec) + (list 'function-put (list 'quote name) + ''autoload-macro (list 'quote spec))) + "Handle autoload-macro declarations") + (defalias 'byte-run--set-no-font-lock-keyword #'(lambda (name _args val) (list 'function-put (list 'quote name) @@ -365,8 +371,13 @@ This is used by `declare'.") (cons (list 'debug #'byte-run--set-debug) (cons - (list 'no-font-lock-keyword #'byte-run--set-no-font-lock-keyword) - defun-declarations-alist)) + ;; macros can declare (autoload-macro expand) to request expansion + ;; during autoload generation of forms calling them. See + ;; `loaddefs-generate--make-autoload'. + (list 'autoload-macro #'byte-run--set-autoload-macro) + (cons + (list 'no-font-lock-keyword #'byte-run--set-no-font-lock-keyword) + defun-declarations-alist))) "List associating properties of macros to their macro expansion. Each element of the list takes the form (PROP FUN) where FUN is a function. For each (PROP . VALUES) in a macro's declaration, the FUN corresponding @@ -412,6 +423,8 @@ The return value is undefined. (if declarations (cons 'prog1 (cons def (car declarations))) def)))))) +;; Expand to defalias and related forms on autoload gen +(function-put 'defmacro 'autoload-macro 'expand) ; Since we cannot `declare' it ;; Now that we defined defmacro we can use it! (defmacro defun (name arglist &rest body) @@ -424,7 +437,9 @@ INTERACTIVE is an optional `interactive' specification. The return value is undefined. \(fn NAME ARGLIST [DOCSTRING] [DECL] [INTERACTIVE] BODY...)" - (declare (doc-string 3) (indent 2)) + (declare (doc-string 3) (indent 2) + ;; Expand to defalias on autoload gen + (autoload-macro expand)) (or name (error "Cannot define '%s' as a function" name)) (if (null (and (listp arglist) diff --git a/lisp/emacs-lisp/cl-generic.el b/lisp/emacs-lisp/cl-generic.el index 4c6c6a0007c..d02d0d6c2a4 100644 --- a/lisp/emacs-lisp/cl-generic.el +++ b/lisp/emacs-lisp/cl-generic.el @@ -258,7 +258,9 @@ DEFAULT-BODY, if present, is used as the body of a default method. cl--generic-edebug-make-name in:method] lambda-doc def-body)]] - def-body))) + def-body)) + ;; Expand to defun and related forms on autoload definition + (autoload-macro expand)) (let* ((doc (if (stringp (car-safe options-and-methods)) (pop options-and-methods))) (declarations nil) diff --git a/lisp/emacs-lisp/cl-macs.el b/lisp/emacs-lisp/cl-macs.el index a076012cd30..08a6cbf209d 100644 --- a/lisp/emacs-lisp/cl-macs.el +++ b/lisp/emacs-lisp/cl-macs.el @@ -396,7 +396,9 @@ more details. [&optional ("interactive" interactive)] def-body)) (doc-string 3) - (indent 2)) + (indent 2) + ;; expand to function definition on autoload gen + (autoload-macro expand)) `(defun ,name ,@(cl--transform-lambda (cons args body) name))) ;;;###autoload @@ -414,7 +416,9 @@ and BODY is implicitly surrounded by (cl-block NAME ...). [&optional ("interactive" interactive)] def-body)) (doc-string 3) - (indent 2)) + (indent 2) + ;; expand (eventually) to function definition on autoload gen + (autoload-macro expand)) (require 'generator) `(iter-defun ,name ,@(cl--transform-lambda (cons args body) name))) @@ -473,7 +477,8 @@ more details. (declare (debug (&define name cl-macro-list cl-declarations-or-string def-body)) (doc-string 3) - (indent 2)) + (indent 2) + (autoload-macro expand)) ; expand to defmacro on autoload gen `(defmacro ,name ,@(cl--transform-lambda (cons args body) name))) (def-edebug-elem-spec 'cl-lambda-expr @@ -3087,7 +3092,9 @@ To see the documentation for a defined struct type, use sexp])] [&optional stringp] ;; All the above is for the following def-form. - &rest &or symbolp (symbolp &optional def-form &rest sexp)))) + &rest &or symbolp (symbolp &optional def-form &rest sexp))) + ;; expand to function definitions and related forms on autoload gen + (autoload-macro expand)) (let* ((name (if (consp struct) (car struct) struct)) (warning nil) (opts (cdr-safe struct)) diff --git a/lisp/emacs-lisp/easy-mmode.el b/lisp/emacs-lisp/easy-mmode.el index b5831c314e1..ca921308877 100644 --- a/lisp/emacs-lisp/easy-mmode.el +++ b/lisp/emacs-lisp/easy-mmode.el @@ -222,10 +222,12 @@ INIT-VALUE LIGHTER KEYMAP. (indent defun) (debug (&define name string-or-null-p [&optional [¬ keywordp] sexp - &optional [¬ keywordp] sexp - &optional [¬ keywordp] sexp] + &optional [¬ keywordp] sexp + &optional [¬ keywordp] sexp] [&rest [keywordp sexp]] - def-body))) + def-body)) + ;; expand to the command definition on autoload gen + (autoload-macro expand)) (let* ((last-message (make-symbol "last-message")) (mode-name (symbol-name mode)) @@ -488,7 +490,9 @@ after running the major mode's hook. However, MODE is not turned on if the hook has explicitly disabled it. \(fn GLOBAL-MODE MODE TURN-ON [KEY VALUE]... BODY...)" - (declare (doc-string 2) (indent defun)) + (declare (doc-string 2) (indent defun) + ;; expand to the minor-mode definition on autoload gen + (autoload-macro expand)) (let* ((global-mode-name (symbol-name global-mode)) (mode-name (symbol-name mode)) (pretty-name (easy-mmode-pretty-mode-name mode)) diff --git a/lisp/emacs-lisp/generator.el b/lisp/emacs-lisp/generator.el index 344e11e245e..c01e559fd5c 100644 --- a/lisp/emacs-lisp/generator.el +++ b/lisp/emacs-lisp/generator.el @@ -675,7 +675,8 @@ encapsulates the state of a computation that produces a sequence of values. Callers can retrieve each value using `iter-next'." (declare (indent defun) (debug (&define name lambda-list lambda-doc &rest sexp)) - (doc-string 3)) + (doc-string 3) + (autoload-macro expand)) ; expand to the defun on autoload gen (cl-assert lexical-binding) (let* ((parsed-body (macroexp-parse-body body)) (declarations (car parsed-body)) diff --git a/lisp/emacs-lisp/inline.el b/lisp/emacs-lisp/inline.el index c015e2b57d0..027c24c465b 100644 --- a/lisp/emacs-lisp/inline.el +++ b/lisp/emacs-lisp/inline.el @@ -135,7 +135,8 @@ After VARS is handled, BODY is evaluated in the new environment." This is like `defmacro', but has several advantages. See Info node `(elisp)Defining Functions' for more details." ;; FIXME: How can this work with CL arglists? - (declare (indent defun) (debug defun) (doc-string 3)) + (declare (indent defun) (debug defun) (doc-string 3) + (autoload-macro expand)) ; expand to the defun on autoload gen (let ((doc (if (stringp (car-safe body)) (list (pop body)))) (declares (if (eq (car-safe (car-safe body)) 'declare) (pop body))) (cm-name (intern (format "%s--inliner" name))) diff --git a/lisp/emacs-lisp/loaddefs-gen.el b/lisp/emacs-lisp/loaddefs-gen.el index a8500759cf8..fc05e8b9ec1 100644 --- a/lisp/emacs-lisp/loaddefs-gen.el +++ b/lisp/emacs-lisp/loaddefs-gen.el @@ -143,12 +143,37 @@ scanning for autoloads and will be in the `load-path'." 3) form)) +;; The following macros are known to define functions, and are treated +;; specially when encountered during autoload generation, translating +;; calls to them directly into appropriate (autoload function ...) +;; forms. +;; +;; An alternative to appearing on this list is for a macro to declare +;; (autoload-macro expand), so calls to it get expanded into more basic +;; forms during generation. Macros may be removed from this list once +;; they request such expansion and produce suitable output (e.g. by +;; employing :autoload-end to omit unneeded forms). +(defconst loaddefs--defining-macros + '( define-skeleton define-derived-mode define-compilation-mode + define-generic-mode define-globalized-minor-mode define-minor-mode + cl-defun defun* cl-defmacro defmacro* define-overloadable-function + transient-define-prefix transient-define-suffix transient-define-infix + transient-define-argument transient-define-group + ;; Obsolete; keep until the alias is removed. + easy-mmode-define-global-mode + easy-mmode-define-minor-mode + define-global-minor-mode)) + +(defvar loaddefs--load-error-files nil) (defun loaddefs-generate--make-autoload (form file &optional expansion) "Turn FORM into an autoload or defvar for source file FILE. Returns nil if FORM is not a special autoload form (i.e. a function definition or macro definition or a defcustom). If EXPANSION is non-nil, we're processing the macro expansion of an -expression, in which case we want to handle forms differently." +expression, in which case we want to handle forms differently. + +Note that macros can request expansion by including `(autoload-macro +expand)' among their `declare' forms." (let ((car (car-safe form)) expand) (cond ((and expansion (eq car 'defalias)) @@ -192,42 +217,40 @@ expression, in which case we want to handle forms differently." (setq form (copy-sequence form)) (setcdr (memq :autoload-end form) nil)) (let ((exps (delq nil (mapcar (lambda (form) - (loaddefs-generate--make-autoload - form file expansion)) + (unless (eq form :autoload-end) + (loaddefs-generate--make-autoload + form file expansion))) (cdr form))))) (when exps (cons 'progn exps))))) - ;; For complex cases, try again on the macro-expansion. - ((and (memq car '( define-globalized-minor-mode defun defmacro - define-minor-mode define-inline - cl-defun cl-defmacro cl-defgeneric - cl-defstruct pcase-defmacro iter-defun cl-iter-defun - ;; Obsolete; keep until the alias is removed. - easy-mmode-define-global-mode - easy-mmode-define-minor-mode - define-global-minor-mode)) - (macrop car) - (setq expand (let ((load-true-file-name file) - (load-file-name file)) - (macroexpand form))) - (memq (car expand) '(progn prog1 defalias))) + ;; For macros which request it, try again on their expansion. + ((progn + ;; 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 loaddefs--defining-macros) + (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))) + (message "loaddefs-gen: loading file %s (for %s)" file car) + (condition-case e (load file) + (error + (push file loaddefs--load-error-files) ; do not attempt again + (warn "loaddefs-gen: load error\n\t%s" e))))) + (and (macrop car) + (eq 'expand (function-get car 'autoload-macro)) + (setq expand (let ((load-true-file-name file) + (load-file-name file)) + (macroexpand form))) + (not (eq car (car expand))))) ;; Recurse on the expansion. (loaddefs-generate--make-autoload expand file 'expansion)) - ;; For special function-like operators, use the `autoload' function. - ((memq car '( define-skeleton define-derived-mode - define-compilation-mode define-generic-mode - define-globalized-minor-mode - define-minor-mode - cl-defun defun* cl-defmacro defmacro* - define-overloadable-function - transient-define-prefix transient-define-suffix - transient-define-infix transient-define-argument - transient-define-group - ;; Obsolete; keep until the alias is removed. - easy-mmode-define-global-mode - easy-mmode-define-minor-mode - define-global-minor-mode)) + ;; For known special macros which define functions, use `autoload' + ;; directly. + ((memq car loaddefs--defining-macros) (let* ((macrop (memq car '(defmacro cl-defmacro defmacro*))) (name (nth 1 form)) (args (pcase car diff --git a/lisp/emacs-lisp/pcase.el b/lisp/emacs-lisp/pcase.el index c68b8961ee3..cd6b5e42771 100644 --- a/lisp/emacs-lisp/pcase.el +++ b/lisp/emacs-lisp/pcase.el @@ -544,7 +544,9 @@ to this macro. By convention, DOC should use \"EXPVAL\" to stand for the result of evaluating EXP (first arg to `pcase'). \n(fn NAME ARGS [DOC] &rest BODY...)" - (declare (indent 2) (debug defun) (doc-string 3)) + (declare (indent 2) (debug defun) (doc-string 3) + ;; Expand to defun and related forms on autoload gen + (autoload-macro expand)) ;; Add the function via `fsym', so that an autoload cookie placed ;; on a pcase-defmacro will cause the macro to be loaded on demand. (let ((fsym (intern (format "%s--pcase-macroexpander" name))) commit 3d7f51d872194ebf17fe4eb5cd0bb9411e196726 Author: Juri Linkov Date: Sun Aug 10 21:08:29 2025 +0300 * lisp/man.el (man): Use active region for the default value. While reading a manual name, provide text from the selected region as another default value for the second 'M-n' in the minibuffer. Also replace spaces with dashes in the name. diff --git a/lisp/man.el b/lisp/man.el index 9e1d294b1ec..49873c5a87b 100644 --- a/lisp/man.el +++ b/lisp/man.el @@ -1123,7 +1123,14 @@ for the current invocation." (and (not (equal default-entry "")) default-entry)) #'Man-completion-table - nil nil nil 'Man-topic-history default-entry))) + nil nil nil 'Man-topic-history + `(,default-entry + ,@(when (use-region-p) + (list (string-replace + " " "-" + (buffer-substring-no-properties + (region-beginning) + (region-end))))))))) (if Man-cache-completion-results-flag (read) (let ((Man-completion-cache)) (read)))))) commit e28ac4eca6a6125c95ae1f1f8511fd1522a0dca3 Author: Juri Linkov Date: Sun Aug 10 20:50:05 2025 +0300 Allow code outside Isearch delete overlays in 'isearch-opened-overlays' * lisp/isearch.el (isearch-open-necessary-overlays) (isearch-close-unnecessary-overlays): Use 'overlay-buffer' to check if overlays in 'isearch-opened-overlays' are still not deleted (bug#79115). (with-isearch-suspended): Call 'isearch-clean-overlays' after 'isearch-done'. Otherwise 'isearch-mode' called later just resets 'isearch-opened-overlays' to nil, causing "overlay leak". diff --git a/lisp/isearch.el b/lisp/isearch.el index 68519b7a820..5a79076c63d 100644 --- a/lisp/isearch.el +++ b/lisp/isearch.el @@ -1751,7 +1751,9 @@ You can update the global isearch variables by setting new values to ;; This is so that the user can do anything without failure, ;; like switch buffers and start another isearch, and return. (condition-case nil - (isearch-done t t) + (progn + (isearch-done t t) + (isearch-clean-overlays)) (exit nil)) ; was recursive editing ;; Save old point and isearch-other-end before reading from minibuffer @@ -1824,6 +1826,7 @@ You can update the global isearch variables by setting new values to (progn ;; (sit-for 1) ;; needed if isearch-done does: (message "") (isearch-done) + (isearch-clean-overlays) ;; The search done message is confusing when the string ;; is empty, so erase it. (if (equal isearch-string "") @@ -3790,19 +3793,20 @@ Optional third argument, if t, means if fail just return nil (no error). ;; point returns to the original location which surely is not contain ;; in any of these overlays, se we are safe in this case too. (defun isearch-open-necessary-overlays (ov) - (let ((inside-overlay (and (> (point) (overlay-start ov)) - (<= (point) (overlay-end ov)))) - ;; If this exists it means that the overlay was opened using - ;; this function, not by us tweaking the overlay properties. - (fct-temp (overlay-get ov 'isearch-open-invisible-temporary))) - (when (or inside-overlay (not fct-temp)) - ;; restore the values for the `invisible' properties. - (overlay-put ov 'invisible (overlay-get ov 'isearch-invisible)) - (overlay-put ov 'isearch-invisible nil)) - (if inside-overlay - (funcall (overlay-get ov 'isearch-open-invisible) ov) - (if fct-temp - (funcall fct-temp ov t))))) + (when (overlay-buffer ov) + (let ((inside-overlay (and (> (point) (overlay-start ov)) + (<= (point) (overlay-end ov)))) + ;; If this exists it means that the overlay was opened using + ;; this function, not by us tweaking the overlay properties. + (fct-temp (overlay-get ov 'isearch-open-invisible-temporary))) + (when (or inside-overlay (not fct-temp)) + ;; restore the values for the `invisible' properties. + (overlay-put ov 'invisible (overlay-get ov 'isearch-invisible)) + (overlay-put ov 'isearch-invisible nil)) + (if inside-overlay + (funcall (overlay-get ov 'isearch-open-invisible) ov) + (if fct-temp + (funcall fct-temp ov t)))))) ;; This is called when exiting isearch. It closes the temporary ;; opened overlays, except the ones that contain the latest match. @@ -3827,16 +3831,17 @@ Optional third argument, if t, means if fail just return nil (no error). (let ((overlays isearch-opened-overlays)) (setq isearch-opened-overlays nil) (dolist (ov overlays) - (if (isearch-intersects-p beg end (overlay-start ov) (overlay-end ov)) - (push ov isearch-opened-overlays) - (let ((fct-temp (overlay-get ov 'isearch-open-invisible-temporary))) - (if fct-temp - ;; If this exists it means that the overlay was opened - ;; using this function, not by us tweaking the overlay - ;; properties. - (funcall fct-temp ov t) - (overlay-put ov 'invisible (overlay-get ov 'isearch-invisible)) - (overlay-put ov 'isearch-invisible nil))))))) + (when (overlay-buffer ov) + (if (isearch-intersects-p beg end (overlay-start ov) (overlay-end ov)) + (push ov isearch-opened-overlays) + (let ((fct-temp (overlay-get ov 'isearch-open-invisible-temporary))) + (if fct-temp + ;; If this exists it means that the overlay was opened + ;; using this function, not by us tweaking the overlay + ;; properties. + (funcall fct-temp ov t) + (overlay-put ov 'invisible (overlay-get ov 'isearch-invisible)) + (overlay-put ov 'isearch-invisible nil)))))))) (defun isearch-range-invisible (beg end) commit bd62f57efd3206fc5764e9e592206be820002500 Author: Vincenzo Pupillo Date: Thu Jul 10 17:20:34 2025 +0200 'php-ts-mode' depends on 'mhtml-ts-mode' instead of JS,CSS and HTML The direct dependence on 'js-ts-mode', 'css-ts-mode' and html-ts-mode has now been replaced by 'mhtml-ts-mode'. Additional benefits are: 1. Imenu now exposes symbols for all of all languages, 2. navigation now works correctly for all languages, 3. outline works for all languages. Additional new features are: 1. indentation of PHP in mixed buffers with HTML now works much better and allows three different behaviors, an option allows you to choose the behavior. 2. a new feature shows where PHP ini files are both locally and remotely, if the buffer is associated with a remote PHP file. * etc/NEWS: Mention the rationale for those changes, the new command, and the new options. * admin/tree-sitter/treesit-admin.el (treesit-admin--builtin-modes): Added 'mhtml-ts-mode'. (treesit-admin--builtin-features): Added 'mhtml-ts-mode'. * lisp/textmodes/mhtml-ts-mode.el (mhtml-ts-mode--range-settings): New variable that store range settings. (mhtml-ts-mode--treesit-aggregated-outline-predicate): New variable that stores outline predicates. (mhtml-ts-mode): Use those two new variables. * lisp/progmodes/php-ts-mode.el: Update doc string. Removed old dependecies. (php-ts-mode--language-source-alist): Updated parsers version. (php-ts-mode-install-parsers): Removed old dependencies, now relies on mhtml-ts-mode-install-parsers'. Update doc string. (php-ts-mode-js-css-indent-offset): Update doc string. (php-ts-mode-css-fontify-colors): Removed option, now relies on 'php-ts-mode-css-fontify-colors'. (php-ts-mode-html-indent-offset): New user option. (php-ts-mode-html-relative-indent): New user option. (php-ts-mode-php-default-executable): Assume "php" instead of "/usr/bin/php" if 'executable-find' fails. (php-ts-mode-php-config): Fix tag. (php-ts-mode-find-sibling-rules): New user option with the rules for finding siblings of a file. (php-ts-mode-phpdoc-highlight-errors): New user option, if non-nil highlight unkown PHPDOC nodes. (php-ts-mode--indent-style-setter): Indentation fix. (php-ts-mode-indent-style): Indentation fix. (php-ts-mode-flymake-php): Indentation fix. (php-ts-mode--executable): Indentation fix. (php-ts-mode-show-ini): New command that show the locations of PHP ini files. (php-ts-mode--get-indent-style): Indentation fix. (php-ts-mode--set-indent-property): Indentation fix. (php-ts-mode-set-style): Indentation fix. (php-ts-mode--get-parser-ranges): Indentation fix. (php-ts-mode--possibly-braceless-keyword-re): Removed no longer used constant. (php-ts-mode--open-statement-group-heuristic): Removed no longer used function. (php-ts-mode--else-heuristic): Indentation fix. (php-ts-mode--first-sibling): Indentation fix. (php-ts-mode--js-css-tag-bol): Function removed, now using 'mhtml-ts-mode--js-css-tag-bol'. (php-ts-mode--parent-html-bol): Removed no longer used function. (php-ts-mode--parent-html-heuristic): Now more reliably handles the mixed indentation of PHP and HTML. The new option 'php-ts-mode-html-relative-indent' changes the behavior of the function. When 'php-ts-mode-html-relative-indent' is 't' the new option 'php-ts-mode-html-indent-offset' controls the offset of PHP code relative to HTML tags. (php-ts-mode--array-element-heuristic): Indentation fix. (php-ts-mode--anchor-prev-sibling): Indentation fix.. (php-ts-mode--indent-styles): The indentation rules for 'program' and text_interpolation' are now common to all indentation styles, thanks to the new version of 'php-ts-mode-arent-html-heuristic'. New rules for 'namespace_use_clause', 'use_declaration', 'use_list', 'attribute_group' and 'string_content'. (php-ts-mode--prettify-symbols-alist): New variable to support 'prettify-symbols-alist'. (php-ts-mode--test-visibility-modifier-operation-clause-p): Rename ... (php-ts-mode--test-visibility-modifier-operation-p): ... to this more correct name. (php-ts-mode--test-property-hook-clause-p): Rename ... (php-ts-mode--test-property-hook-p): ... to this more correct name. (php-ts-mode--test-relative-name-p): New function to test if 'relative_name' is supported by the PHP parser. (php-ts-mode--font-lock-settings): Use the new function for highlight 'relative_name'. Highlight 'error_suppression_expression'. Refactoring using the renamed functions. Indentation fix. (php-ts-mode--custom-html-font-lock-settings): Replace the rules with 'mhtml-ts-mode--treesit-font-lock-settings'. Fix docs string. (php-ts-mode--phpdoc-font-lock-settings): Added support for "array" and "list" array_type. New "phpdoc-error" feature to highlight unknown nodes via new 'php-ts-mode--phpdoc-fontify-error' function. (php-ts-mode--colorize-css-value): Removed function no longer used, now highlighting is handled directly by 'mhtml-ts-mode'. (php-ts-mode--phpdoc-fontify-error): New function used by 'php-ts-mode--phpdoc-font-lock-settings'. (php-ts-mode--parent-object): Indentation fix. (php-ts-mode--defun-name-separator): Indentation fix. (php-ts-mode--defun-object-name): Indentation fix. (php-ts-mode--defun-name): The function now also returns the defun name of 'mhtml-ts-mode'. (php-ts-mode--treesit-defun-type-regexp): New regexp for 'treesit-defun-type-regexp' support. (php-ts-mode--indent-defun): Indentation fix. (php-ts-mode--defun-valid-p): Fix indentation (php-ts-mode--comment-indent-new-line): Indentation fix. (php-ts-mode--comment-current-plist): New local variable that stores the state of the PHP comment style. (php-ts-mode--comment-setup): The function now handles changing, for different languages, comment styles, using "php-ts-mode--comment-current-plist" to store and retrieve the comment style for the PHP language. (php-ts-mode-comment-setup): Now store the PHP comment style in 'php-ts-mode--comment-current-plist'. (php-ts-mode-menu): Indentation fix. (php-ts-mode): Replace dependency from JS, CSS and HTML width 'mhtml-ts-mode'. Navigation, Imenu and Outline now support PHP, HTML, Javascript and CSS. Added support for 'prettify-symbol-mode'. Added support for 'electric-layout-rules'. (php-ts-mode-run-php-webserver, php-ts-mode--webserver-read-args) (php-ts-mode--webserver-read-args, inferior-php-ts-mode, run-php) (inferior-php-ts-mode-startup, php-ts-mode-inferior--write-history) (php-ts-mode-send-region): Indentation fixes. * lisp/treesit.el (treesit-range-rules): Check if treesit is available before compiling a query. (treesit-ensure-installed): Check if treesit is available before trying to ensure if a grammar library is installed. (Bug#78994) diff --git a/admin/tree-sitter/treesit-admin.el b/admin/tree-sitter/treesit-admin.el index 2be6fe42f27..2e85d6b0d8c 100644 --- a/admin/tree-sitter/treesit-admin.el +++ b/admin/tree-sitter/treesit-admin.el @@ -83,6 +83,7 @@ go-ts-mode heex-ts-mode html-ts-mode + mhtml-ts-mode java-ts-mode js-ts-mode json-ts-mode @@ -107,6 +108,7 @@ go-ts-mode heex-ts-mode html-ts-mode + mhtml-ts-mode java-ts-mode js json-ts-mode diff --git a/etc/NEWS b/etc/NEWS index c06be2301d4..79d124bd585 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -931,6 +931,12 @@ option controls how much is indented for method chaining. ** PHP-ts mode +--- +*** 'php-ts-mode' now depends on 'mhtml-ts-mode'. +The direct dependency on js-ts-mode, css-ts-mode and html-ts-mode has +now been replaced by ‘mhtml-ts-mode’. Navigation, Outline and Imenu +work for all languages and code maintenance is easier. + --- *** 'php-ts-mode-run-php-webserver' can now accept a custom "php.ini" file. You can use the new optional argument CONFIG when calling @@ -939,6 +945,34 @@ the built-in Web server. Interactively, when invoked with a prefix argument, 'php-ts-mode-run-php-webserver' prompts for the config file as well as for other connection parameters. +--- +*** The option 'php-ts-mode-css-fontify-colors' has been removed. +'mhtml-ts-mode-css-fontify-colors' replaces this option. + +--- +*** New user option 'php-ts-mode-html-relative-indent'. +In buffers containing both PHP and HTML, this option allows you to +define how the PHP code should be indented relative to the position of +the HTML tags. + +--- +*** New user option 'php-ts-mode-html-indent-offset'. +Offset of PHP code block relative to HTML tags. + +--- +*** New user option 'php-ts-mode-find-sibling-rules'. +Rules for finding siblings of a PHP file. + +--- +*** New user option 'php-ts-mode-phpdoc-highlight-errors'. +When non nil, it highlights unknown PHPDOC tags using +'font-lock-warning-face' so that the user can identify them more easily. + +--- +*** New command 'php-ts-mode-show-ini'. +Show the location of the PHP ini files, if the buffer is associated to a remote +PHP file show the remote PHP ini files. + ** Rust-ts mode --- diff --git a/lisp/progmodes/php-ts-mode.el b/lisp/progmodes/php-ts-mode.el index a01e4d66fba..d45226ad5e9 100644 --- a/lisp/progmodes/php-ts-mode.el +++ b/lisp/progmodes/php-ts-mode.el @@ -25,12 +25,12 @@ ;;; Tree-sitter language versions ;; ;; php-ts-mode has been tested with the following grammars and version: -;; - tree-sitter-phpdoc: v0.1.5 +;; - tree-sitter-phpdoc: v0.1.6 ;; - tree-sitter-css: v0.23.1-1-g6a442a3 ;; - tree-sitter-jsdoc: v0.23.2 ;; - tree-sitter-javascript: v0.23.1-2-g108b2d4 ;; - tree-sitter-html: v0.23.2-1-gd9219ad -;; - tree-sitter-php: v0.23.11 +;; - tree-sitter-php: v0.23.12 ;; ;; We try our best to make builtin modes work with latest grammar ;; versions, so a more recent grammar has a good chance to work too. @@ -42,7 +42,7 @@ ;; for editing PHP files with embedded HTML, JavaScript, CSS and phpdoc. ;; Tree Sitter is used to parse each of these languages. ;; -;; Please note that this package requires `html-ts-mode', which +;; Please note that this package requires `mhtml-ts-mode', which ;; registers itself as the major mode for editing HTML. ;; ;; This package is compatible and has been tested with the following @@ -69,9 +69,7 @@ (require 'treesit) (require 'c-ts-common) ;; For comment indent and filling. -(require 'html-ts-mode) ;; for embed html -(require 'css-mode) ;; for embed css into html -(require 'js) ;; for embed javascript into html +(require 'mhtml-ts-mode) ;; For embed html, css and js. (require 'comint) (treesit-declare-unavailable-functions) @@ -83,10 +81,10 @@ ;;; Install treesitter language parsers (defvar php-ts-mode--language-source-alist '((php "https://github.com/tree-sitter/tree-sitter-php" - :commit "43aad2b9a98aa8e603ea0cf5bb630728a5591ad8" - :source-dir "php/src") + :commit "f7cf7348737d8cff1b13407a0bfedce02ee7b046" + :source-dir "php/src") (phpdoc "https://github.com/claytonrcarter/tree-sitter-phpdoc" - :commit "fe3202e468bc17332bec8969f2b50ff1f1da3a46")) + :commit "03bb10330704b0b371b044e937d5cc7cd40b4999")) "Treesitter language parsers required by `php-ts-mode'. You can customize `treesit-language-source-alist' if you want to stick to a specific commit and/or use different parsers.") @@ -97,9 +95,10 @@ to stick to a specific commit and/or use different parsers.") (defun php-ts-mode-install-parsers () "Install all the required treesitter parsers. `treesit-language-source-alist' defines which parsers to install. -It's pre-filled by loading \"html-ts-mode\", \"css-mode\", \"js\"." +It's pre-filled by loading \"mhtml-ts-mode\"." (interactive) - (dolist (lang '(php phpdoc html css javascript jsdoc)) + (mhtml-ts-mode-install-parsers) + (dolist (lang '(php phpdoc)) (treesit-install-language-grammar lang))) ;;; Custom variables @@ -118,25 +117,63 @@ It's pre-filled by loading \"html-ts-mode\", \"css-mode\", \"js\"." (defcustom php-ts-mode-js-css-indent-offset 2 "JavaScript and CSS indent spaces related to the