From 0ac3a1f26c6c02d0a81290dba4d30d57e5bd92f1 Mon Sep 17 00:00:00 2001 From: Juri Linkov Date: Thu, 14 Aug 2025 19:40:08 +0300 Subject: [PATCH] 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'. --- etc/NEWS | 6 ++++ lisp/progmodes/c-ts-mode.el | 31 ++++++----------- lisp/progmodes/cmake-ts-mode.el | 19 +++++++++-- lisp/progmodes/csharp-mode.el | 9 +++-- lisp/progmodes/dockerfile-ts-mode.el | 23 ++++++++++--- lisp/progmodes/elixir-ts-mode.el | 24 +++++++++---- lisp/progmodes/go-ts-mode.el | 51 ++++++++++++++++++++++++---- lisp/progmodes/heex-ts-mode.el | 21 +++++++++--- lisp/progmodes/java-ts-mode.el | 6 ++-- lisp/progmodes/js.el | 10 +++--- lisp/progmodes/json-ts-mode.el | 7 ++-- lisp/progmodes/lua-ts-mode.el | 19 +++++++++-- lisp/progmodes/php-ts-mode.el | 23 ++++++++++--- lisp/progmodes/ruby-mode.el | 4 --- lisp/progmodes/ruby-ts-mode.el | 16 ++++----- lisp/progmodes/rust-ts-mode.el | 17 ++++++++-- lisp/progmodes/sh-script.el | 5 +++ lisp/progmodes/typescript-ts-mode.el | 34 ++++++++++++++++--- lisp/textmodes/css-mode.el | 9 +++-- lisp/textmodes/html-ts-mode.el | 4 +-- lisp/textmodes/markdown-ts-mode.el | 17 ++++++++-- lisp/textmodes/mhtml-ts-mode.el | 7 ++-- lisp/textmodes/toml-ts-mode.el | 6 ++-- lisp/textmodes/yaml-ts-mode.el | 17 ++++++++-- lisp/treesit.el | 28 ++++++++++++++- src/treesit.c | 10 ++++++ 26 files changed, 323 insertions(+), 100 deletions(-) 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);