From 7dcedc16b5c2c561f89b6a6e2fd8847bc7f6b64c Mon Sep 17 00:00:00 2001 From: Henrik Lissner Date: Sat, 30 Aug 2025 03:01:44 +0200 Subject: [PATCH] refactor(tree-sitter): major mode remapping This approach simplifies the hacks and uses fewer moving parts to be less error prone (or prone to potentially overwriting user/module configuration). --- .../tools/tree-sitter/autoload/tree-sitter.el | 8 +-- modules/tools/tree-sitter/config.el | 68 ++++++++----------- 2 files changed, 29 insertions(+), 47 deletions(-) diff --git a/modules/tools/tree-sitter/autoload/tree-sitter.el b/modules/tools/tree-sitter/autoload/tree-sitter.el index 69dfeaabe..f9953bbbd 100644 --- a/modules/tools/tree-sitter/autoload/tree-sitter.el +++ b/modules/tools/tree-sitter/autoload/tree-sitter.el @@ -1,8 +1,5 @@ ;;; tools/tree-sitter/autoload/tree-sitter.el -*- lexical-binding: t; -*- -;;;###autoload -(defvar +tree-sitter--major-mode-remaps-alist nil) - ;;;###autodef (fset 'tree-sitter! #'ignore) (defun tree-sitter! () (message "Old tree-sitter.el support is deprecated!")) @@ -25,9 +22,8 @@ pre-Emacs 31." (cl-check-type ts-mode symbol) (setq recipes (mapcar #'ensure-list (ensure-list recipes))) (dolist (m (ensure-list mode)) - (add-to-list - '+tree-sitter--major-mode-remaps-alist - (list m ts-mode (mapcar #'car recipes) nil))) + (setf (alist-get m major-mode-remap-defaults) ts-mode) + (put ts-mode '+tree-sitter (cons m (mapcar #'car recipes)))) (when (setq recipes (cl-remove-if-not #'cdr recipes)) (with-eval-after-load 'treesit (dolist (recipe recipes) diff --git a/modules/tools/tree-sitter/config.el b/modules/tools/tree-sitter/config.el index 3770b15a5..486f9db06 100644 --- a/modules/tools/tree-sitter/config.el +++ b/modules/tools/tree-sitter/config.el @@ -35,51 +35,37 @@ python-ts-mode)) (advice-add mode :around #'+tree-sitter-ts-mode-inhibit-side-effects-a)) - ;; HACK: Some *-ts-mode packages modify `major-mode-remap-defaults' - ;; inconsistently. Playing whack-a-mole to undo those changes is more hassle - ;; then simply ignoring them (by overriding `major-mode-remap-defaults' for - ;; any modes remapped with `set-tree-sitter!'). The user shouldn't touch - ;; `major-mode-remap-defaults' anyway; `major-mode-remap-alist' will always - ;; have precedence. + ;; HACK: Intercept all ts-mode major mode remappings so grammars can be + ;; dynamically checked and `treesit-auto-install-grammar' can be + ;; consistently respected (which isn't currently the case with the majority + ;; of ts-modes, even the built-in ones). (defadvice! +tree-sitter--maybe-remap-major-mode-a (fn mode) :around #'major-mode-remap - (let ((major-mode-remap-defaults - ;; Because standard major-mode remapping doesn't offer graceful - ;; failure in some cases, I implement it myself: - (cons (when-let* ((spec (assq mode +tree-sitter--major-mode-remaps-alist)) - (ts-mode (nth 1 spec)) - (langs (nth 2 spec))) - (if (not (fboundp ts-mode)) - (ignore (message "Couldn't find %S, falling back to %S" ts-mode mode)) - (prog1 (and (or (eq treesit-enabled-modes t) - (memq ts-mode treesit-enabled-modes)) - ;; Lazily load autoload so - ;; `treesit-language-source-alist' is - ;; initialized. - (let ((fn (symbol-function ts-mode))) - (or (not (autoloadp fn)) - (autoload-do-load fn ts-mode))) - ;; Only prompt once, and log other times. - (cl-every (if (get ts-mode 'ensured?) - (doom-rpartial #'treesit-ready-p 'message) - #'treesit-ensure-installed) - langs) - `((,mode . ,ts-mode))) - (put ts-mode 'ensured? t)))) - major-mode-remap-defaults))) - (funcall fn mode))) + (let ((mode (funcall fn mode))) + (if-let* ((ts (get mode '+tree-sitter)) + (fallback-mode (car ts))) + (cond ((not (fboundp mode)) + (message "Couldn't find %S, falling back to %S" mode fallback-mode) + fallback-mode) + ((and (or (eq treesit-enabled-modes t) + (memq fallback-mode treesit-enabled-modes)) + ;; Lazily load autoloaded `treesit-language-source-alist' + ;; entries. + (let ((fn (symbol-function mode))) + (or (not (autoloadp fn)) + (autoload-do-load fn mode))) + ;; Only prompt once, and log other times. + (or (null (cdr ts)) + (cl-every (if (get mode '+tree-sitter-ensured) + (doom-rpartial #'treesit-ready-p 'message) + #'treesit-ensure-installed) + (cdr ts)))) + (put mode '+tree-sitter-ensured t) + mode) + (fallback-mode)) + mode))) :config - ;; HACK: The implementation of `treesit-enabled-modes's setter and - ;; `treesit-major-mode-remap-alist' is intrusively opinionated, so disable - ;; it ato avoid untimely (and overriding) modifications of - ;; `major-mode-remap-alist' at runtime. What's more, this was only - ;; introduced in 31, so ignoring them is more consistent for pre-31 users. - (when major-mode-remap-alist - (dolist (m treesit-major-mode-remap-alist) - (setq major-mode-remap-alist (delete m major-mode-remap-alist)))) - (setq treesit-major-mode-remap-alist nil) - ;; HACK: Keep $EMACSDIR clean by installing grammars to the active profile. (add-to-list 'treesit-extra-load-path (concat doom-profile-data-dir "tree-sitter")) (defadvice! +tree-sitter--install-grammar-to-local-dir-a (fn &rest args)