mirror of
https://github.com/doomemacs/doomemacs
synced 2025-09-02 14:53:38 -05:00
This approach simplifies the hacks and uses fewer moving parts to be less error prone (or prone to potentially overwriting user/module configuration).
167 lines
8.5 KiB
EmacsLisp
167 lines
8.5 KiB
EmacsLisp
;;; tools/tree-sitter/config.el -*- lexical-binding: t; -*-
|
|
|
|
;;
|
|
;;; Packages
|
|
|
|
(use-package! treesit
|
|
:when (fboundp 'treesit-available-p)
|
|
:when (treesit-available-p)
|
|
:defer t
|
|
:preface
|
|
(setq treesit-enabled-modes t)
|
|
|
|
;; HACK: The *-ts-mode major modes are inconsistent about how they treat
|
|
;; missing language grammars (some error out, some respect
|
|
;; `treesit-auto-install-grammar', some fall back to `fundamental-mode').
|
|
;; I'd like to address this poor UX using `major-mode-remap-alist' entries
|
|
;; created by `set-tree-sitter!' (which will fall back to the non-treesit
|
|
;; modes), but most *-ts-mode's clobber `auto-mode-alist' and/or
|
|
;; `interpreter-mode-alist' each time the major mode is activated, so those
|
|
;; must be undone too so they don't overwrite user config.
|
|
;; TODO: Handle this during the 'doom sync' process instead.
|
|
(save-match-data
|
|
(dolist (sym '(auto-mode-alist interpreter-mode-alist))
|
|
(set
|
|
sym (cl-loop for (src . fn) in (symbol-value sym)
|
|
unless (and (functionp fn)
|
|
(string-match "-ts-mode\\(?:-maybe\\)?$" (symbol-name fn)))
|
|
collect (cons src fn)))))
|
|
|
|
;; HACK: These *-ts-modes change `auto-mode-alist' and/or
|
|
;; `interpreter-mode-alist' every time they are activated, running the risk
|
|
;; of overwriting user (or Doom) config.
|
|
;; REVIEW: Should be addressed upstream.
|
|
(dolist (mode '(csharp-ts-mode
|
|
python-ts-mode))
|
|
(advice-add mode :around #'+tree-sitter-ts-mode-inhibit-side-effects-a))
|
|
|
|
;; 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 ((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: 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)
|
|
"Write grammars to `doom-profile-data-dir'."
|
|
:around #'treesit-install-language-grammar
|
|
:around #'treesit--build-grammar
|
|
(let ((user-emacs-directory doom-profile-data-dir))
|
|
(apply fn args)))
|
|
|
|
;; TODO: Move most of these out to modules
|
|
(dolist (map '((awk "https://github.com/Beaglefoot/tree-sitter-awk" nil nil nil nil)
|
|
(bibtex "https://github.com/latex-lsp/tree-sitter-bibtex" nil nil nil nil)
|
|
(blueprint "https://github.com/huanie/tree-sitter-blueprint" nil nil nil nil)
|
|
(commonlisp "https://github.com/tree-sitter-grammars/tree-sitter-commonlisp" nil nil nil nil)
|
|
(latex "https://github.com/latex-lsp/tree-sitter-latex" nil nil nil nil)
|
|
(make "https://github.com/tree-sitter-grammars/tree-sitter-make" nil nil nil nil)
|
|
(nu "https://github.com/nushell/tree-sitter-nu" nil nil nil nil)
|
|
(org "https://github.com/milisims/tree-sitter-org" nil nil nil nil)
|
|
(perl "https://github.com/ganezdragon/tree-sitter-perl" nil nil nil nil)
|
|
(proto "https://github.com/mitchellh/tree-sitter-proto" nil nil nil nil)
|
|
(r "https://github.com/r-lib/tree-sitter-r" nil nil nil nil)
|
|
(sql "https://github.com/DerekStride/tree-sitter-sql" "gh-pages" nil nil nil)
|
|
(surface "https://github.com/connorlay/tree-sitter-surface" nil nil nil nil)
|
|
(toml "https://github.com/tree-sitter/tree-sitter-toml" nil nil nil nil)
|
|
(typst "https://github.com/uben0/tree-sitter-typst" "master" "src" nil nil)
|
|
(verilog "https://github.com/gmlarumbe/tree-sitter-verilog" nil nil nil nil)
|
|
(vhdl "https://github.com/alemuller/tree-sitter-vhdl" nil nil nil nil)
|
|
(vue "https://github.com/tree-sitter-grammars/tree-sitter-vue" nil nil nil nil)
|
|
(wast "https://github.com/wasm-lsp/tree-sitter-wasm" nil "wast/src" nil nil)
|
|
(wat "https://github.com/wasm-lsp/tree-sitter-wasm" nil "wat/src" nil nil)
|
|
(wgsl "https://github.com/mehmetoguzderin/tree-sitter-wgsl" nil nil nil nil)))
|
|
(cl-pushnew map treesit-language-source-alist :test #'eq :key #'car)))
|
|
|
|
|
|
;; TODO: combobulate or evil-textobj-tree-sitter
|
|
|
|
|
|
;; (use-package! combobulate
|
|
;; :commands combobulate-query-builder
|
|
;; :hook (prog-mode . combobulate-mode))
|
|
|
|
|
|
;; (use-package! evil-textobj-tree-sitter
|
|
;; :when (modulep! :editor evil +everywhere)
|
|
;; :defer t
|
|
;; :init (after! tree-sitter (require 'evil-textobj-tree-sitter))
|
|
;; :after-call doom-first-input-hook
|
|
;; :config
|
|
;; (defvar +tree-sitter-inner-text-objects-map (make-sparse-keymap))
|
|
;; (defvar +tree-sitter-outer-text-objects-map (make-sparse-keymap))
|
|
;; (defvar +tree-sitter-goto-previous-map (make-sparse-keymap))
|
|
;; (defvar +tree-sitter-goto-next-map (make-sparse-keymap))
|
|
|
|
;; (evil-define-key '(visual operator) 'tree-sitter-mode
|
|
;; "i" +tree-sitter-inner-text-objects-map
|
|
;; "a" +tree-sitter-outer-text-objects-map)
|
|
;; (evil-define-key 'normal 'tree-sitter-mode
|
|
;; "[g" +tree-sitter-goto-previous-map
|
|
;; "]g" +tree-sitter-goto-next-map)
|
|
|
|
;; (map! (:map +tree-sitter-inner-text-objects-map
|
|
;; "A" (+tree-sitter-get-textobj '("parameter.inner" "call.inner"))
|
|
;; "f" (+tree-sitter-get-textobj "function.inner")
|
|
;; "F" (+tree-sitter-get-textobj "call.inner")
|
|
;; "C" (+tree-sitter-get-textobj "class.inner")
|
|
;; "v" (+tree-sitter-get-textobj "conditional.inner")
|
|
;; "l" (+tree-sitter-get-textobj "loop.inner"))
|
|
;; (:map +tree-sitter-outer-text-objects-map
|
|
;; "A" (+tree-sitter-get-textobj '("parameter.outer" "call.outer"))
|
|
;; "f" (+tree-sitter-get-textobj "function.outer")
|
|
;; "F" (+tree-sitter-get-textobj "call.outer")
|
|
;; "C" (+tree-sitter-get-textobj "class.outer")
|
|
;; "c" (+tree-sitter-get-textobj "comment.outer")
|
|
;; "v" (+tree-sitter-get-textobj "conditional.outer")
|
|
;; "l" (+tree-sitter-get-textobj "loop.outer"))
|
|
|
|
;; (:map +tree-sitter-goto-previous-map
|
|
;; "a" (+tree-sitter-goto-textobj "parameter.outer" t)
|
|
;; "f" (+tree-sitter-goto-textobj "function.outer" t)
|
|
;; "F" (+tree-sitter-goto-textobj "call.outer" t)
|
|
;; "C" (+tree-sitter-goto-textobj "class.outer" t)
|
|
;; "c" (+tree-sitter-goto-textobj "comment.outer" t)
|
|
;; "v" (+tree-sitter-goto-textobj "conditional.outer" t)
|
|
;; "l" (+tree-sitter-goto-textobj "loop.outer" t))
|
|
;; (:map +tree-sitter-goto-next-map
|
|
;; "a" (+tree-sitter-goto-textobj "parameter.outer")
|
|
;; "f" (+tree-sitter-goto-textobj "function.outer")
|
|
;; "F" (+tree-sitter-goto-textobj "call.outer")
|
|
;; "C" (+tree-sitter-goto-textobj "class.outer")
|
|
;; "c" (+tree-sitter-goto-textobj "comment.outer")
|
|
;; "v" (+tree-sitter-goto-textobj "conditional.outer")
|
|
;; "l" (+tree-sitter-goto-textobj "loop.outer")))
|
|
|
|
;; (after! which-key
|
|
;; (setq which-key-allow-multiple-replacements t)
|
|
;; (pushnew!
|
|
;; which-key-replacement-alist
|
|
;; '(("" . "\\`+?evil-textobj-tree-sitter-function--\\(.*\\)\\(?:.inner\\|.outer\\)") . (nil . "\\1")))))
|