mirror of
git://git.sv.gnu.org/emacs.git
synced 2025-12-12 09:00:40 -08:00
Revise the toggle scheme of tree-sitter (again)
Now instead of a toggle function (major-mode-backend-function), we let major mode set local variables like treesit-font-lock-settings, treesit-imenu-function, then treesit-mode takes care of activating those things (clearing font-lock-keywords, setting imenu-create-index-function to treesit-imenu-function, etc). js.el and python.el: I've returned js-mode and python-mode to exactly what they were before tree-sitter change, plus lines at the end setting up tree-sitter variables. Sorry about all these fuss :-D * lisp/treesit.el (treesit-mode-inhibit-message): Remove option. (major-mode-backend-function) (treesit-remapped-major-mode-alist): Remove variables. (treesit-mode): Move down to the end of buffer. Do more things. (global-treesit-mode): Move down to the end of buffer. Don't handle major-mode-remap-alist anymore. (global-treesit-mode--turn-on): Move down to the end of buffer. (treesit-ready-p): Move down to the end of buffer. Changed signature. (treesit-font-lock-enable): Remove function. (treesit-defun-type-regexp): New variable. (treesit-beginning-of-defun) (treesit-end-of-defun): New function. (treesit-imenu-function): New variable. (treesit-mode-supported) (treesit-required-languages) (treesit--local-variable-backup): New variables. (treesit--backup-local-variable): New function * lisp/progmodes/js.el (js-use-tree-sitter): Remove option. (js--treesit-defun-type-regexp): Remove variable. (Now set inline in js-mode.) (js--treesit-beginning-of-defun) (js--treesit-end-of-defun): Remove functions. (Now use treesit-beginning/end-of-defun.) (js--backend-toggle) (js--json-backend-toggle): Remove function. (js-mode) (js-json-mode): Restore back to before tree-sitter changes. Add tree-sitter setup at the end. * lisp/progmodes/python.el (python--backend-toggle): Remove function. (python-mode): Restore back to before tree-sitter changes. Add tree-sitter setup at the end. * lisp/progmodes/ts-mode.el (ts-mode--font-lock-settings): Use js--fontify-template-string. (ts-mode--fontify-template-string): Remove function (because we can just use js--fontify-template-string). (ts-mode--defun-type-regexp): Remove variable (now set inline in ts-mode). (ts-mode--beginning-of-defun) (ts-mode--end-of-defun): Remove functions (now using treesit-beginning/end-of-defun). (ts-mode): Setup tree-sitter variables and then turn on treesit-mode or move to js-mode.
This commit is contained in:
parent
4c328daf01
commit
5159789e55
4 changed files with 253 additions and 327 deletions
|
|
@ -3403,13 +3403,6 @@ This function is intended for use in `after-change-functions'."
|
||||||
js-mode "\\(@[[:alpha:]]+\\>\\|$\\)")
|
js-mode "\\(@[[:alpha:]]+\\>\\|$\\)")
|
||||||
|
|
||||||
;;; Tree sitter integration
|
;;; Tree sitter integration
|
||||||
(defcustom js-use-tree-sitter nil
|
|
||||||
"If non-nil, `js-mode' tries to use tree-sitter.
|
|
||||||
Currently `js-mode' uses tree-sitter for font-locking,
|
|
||||||
indentation, which-function and movement functions."
|
|
||||||
:version "29.1"
|
|
||||||
:type 'boolean
|
|
||||||
:safe 'booleanp)
|
|
||||||
|
|
||||||
(defun js--treesit-backward-up-list ()
|
(defun js--treesit-backward-up-list ()
|
||||||
(lambda (_node _parent _bol &rest _)
|
(lambda (_node _parent _bol &rest _)
|
||||||
|
|
@ -3608,79 +3601,6 @@ This function can be used as a value in `which-func-functions'"
|
||||||
do (setq node (treesit-node-parent node))
|
do (setq node (treesit-node-parent node))
|
||||||
finally return (string-join name-list "."))))
|
finally return (string-join name-list "."))))
|
||||||
|
|
||||||
(defvar js--treesit-defun-type-regexp
|
|
||||||
(rx (or "class_declaration"
|
|
||||||
"method_definition"
|
|
||||||
"function_declaration"
|
|
||||||
"lexical_declaration"))
|
|
||||||
"Regular expression that matches type of defun nodes.
|
|
||||||
Used in `js--treesit-beginning-of-defun' and friends.")
|
|
||||||
|
|
||||||
(defun js--treesit-beginning-of-defun (&optional arg)
|
|
||||||
"Tree-sitter `beginning-of-defun' function.
|
|
||||||
ARG is the same as in `beginning-of-defun."
|
|
||||||
(let ((arg (or arg 1)))
|
|
||||||
(if (> arg 0)
|
|
||||||
;; Go backward.
|
|
||||||
(while (and (> arg 0)
|
|
||||||
(treesit-search-forward-goto
|
|
||||||
js--treesit-defun-type-regexp 'start nil t))
|
|
||||||
(setq arg (1- arg)))
|
|
||||||
;; Go forward.
|
|
||||||
(while (and (< arg 0)
|
|
||||||
(treesit-search-forward-goto
|
|
||||||
js--treesit-defun-type-regexp 'start))
|
|
||||||
(setq arg (1+ arg))))))
|
|
||||||
|
|
||||||
(defun js--treesit-end-of-defun (&optional arg)
|
|
||||||
"Tree-sitter `end-of-defun' function.
|
|
||||||
ARG is the same as in `end-of-defun."
|
|
||||||
(let ((arg (or arg 1)))
|
|
||||||
(if (< arg 0)
|
|
||||||
;; Go backward.
|
|
||||||
(while (and (< arg 0)
|
|
||||||
(treesit-search-forward-goto
|
|
||||||
js--treesit-defun-type-regexp 'end nil t))
|
|
||||||
(setq arg (1+ arg)))
|
|
||||||
;; Go forward.
|
|
||||||
(while (and (> arg 0)
|
|
||||||
(treesit-search-forward-goto
|
|
||||||
js--treesit-defun-type-regexp 'end))
|
|
||||||
(setq arg (1- arg))))))
|
|
||||||
|
|
||||||
(defun js--backend-toggle (backend warn)
|
|
||||||
"Toggle backend for `js-mode'.
|
|
||||||
For BACKEND and WARN see `treesit-mode-function'."
|
|
||||||
(cond
|
|
||||||
;; Tree-sitter.
|
|
||||||
((and (eq backend 'treesit) (treesit-ready-p warn 'javascript))
|
|
||||||
(setq-local treesit-simple-indent-rules js--treesit-indent-rules)
|
|
||||||
(setq-local indent-line-function #'treesit-indent)
|
|
||||||
|
|
||||||
(setq-local beginning-of-defun-function #'js--treesit-beginning-of-defun)
|
|
||||||
(setq-local end-of-defun-function #'js--treesit-end-of-defun)
|
|
||||||
|
|
||||||
(add-hook 'which-func-functions #'js-treesit-current-defun nil t)
|
|
||||||
|
|
||||||
(setq-local font-lock-keywords-only t)
|
|
||||||
(setq-local treesit-font-lock-settings js--treesit-font-lock-settings)
|
|
||||||
(setq-local treesit-font-lock-feature-list '((minimal) (moderate) (full)))
|
|
||||||
(treesit-font-lock-enable))
|
|
||||||
;; Elisp.
|
|
||||||
((eq backend 'elisp)
|
|
||||||
(setq-local indent-line-function #'js-indent-line)
|
|
||||||
(setq-local beginning-of-defun-function #'js-beginning-of-defun)
|
|
||||||
(setq-local end-of-defun-function #'js-end-of-defun)
|
|
||||||
|
|
||||||
(setq-local font-lock-defaults
|
|
||||||
(list js--font-lock-keywords nil nil nil nil
|
|
||||||
'(font-lock-syntactic-face-function
|
|
||||||
. js-font-lock-syntactic-face-function)))
|
|
||||||
|
|
||||||
;; Imenu
|
|
||||||
(setq imenu-case-fold-search nil)
|
|
||||||
(setq imenu-create-index-function #'js--imenu-create-index))))
|
|
||||||
|
|
||||||
;;; Main Function
|
;;; Main Function
|
||||||
|
|
||||||
;;;###autoload
|
;;;###autoload
|
||||||
|
|
@ -3689,7 +3609,14 @@ For BACKEND and WARN see `treesit-mode-function'."
|
||||||
:group 'js
|
:group 'js
|
||||||
;; Ensure all CC Mode "lang variables" are set to valid values.
|
;; Ensure all CC Mode "lang variables" are set to valid values.
|
||||||
(c-init-language-vars js-mode)
|
(c-init-language-vars js-mode)
|
||||||
|
(setq-local indent-line-function #'js-indent-line)
|
||||||
|
(setq-local beginning-of-defun-function #'js-beginning-of-defun)
|
||||||
|
(setq-local end-of-defun-function #'js-end-of-defun)
|
||||||
(setq-local open-paren-in-column-0-is-defun-start nil)
|
(setq-local open-paren-in-column-0-is-defun-start nil)
|
||||||
|
(setq-local font-lock-defaults
|
||||||
|
(list js--font-lock-keywords nil nil nil nil
|
||||||
|
'(font-lock-syntactic-face-function
|
||||||
|
. js-font-lock-syntactic-face-function)))
|
||||||
(setq-local syntax-propertize-function #'js-syntax-propertize)
|
(setq-local syntax-propertize-function #'js-syntax-propertize)
|
||||||
(add-hook 'syntax-propertize-extend-region-functions
|
(add-hook 'syntax-propertize-extend-region-functions
|
||||||
#'syntax-propertize-multiline 'append 'local)
|
#'syntax-propertize-multiline 'append 'local)
|
||||||
|
|
@ -3704,6 +3631,8 @@ For BACKEND and WARN see `treesit-mode-function'."
|
||||||
(setq-local comment-start "// ")
|
(setq-local comment-start "// ")
|
||||||
(setq-local comment-start-skip "\\(?://+\\|/\\*+\\)\\s *")
|
(setq-local comment-start-skip "\\(?://+\\|/\\*+\\)\\s *")
|
||||||
(setq-local comment-end "")
|
(setq-local comment-end "")
|
||||||
|
(setq-local fill-paragraph-function #'js-fill-paragraph)
|
||||||
|
(setq-local normal-auto-fill-function #'js-do-auto-fill)
|
||||||
|
|
||||||
;; Parse cache
|
;; Parse cache
|
||||||
(add-hook 'before-change-functions #'js--flush-caches t t)
|
(add-hook 'before-change-functions #'js--flush-caches t t)
|
||||||
|
|
@ -3716,6 +3645,10 @@ For BACKEND and WARN see `treesit-mode-function'."
|
||||||
(add-hook 'after-change-functions #'js-jsx--detect-after-change nil t))
|
(add-hook 'after-change-functions #'js-jsx--detect-after-change nil t))
|
||||||
(js-use-syntactic-mode-name)
|
(js-use-syntactic-mode-name)
|
||||||
|
|
||||||
|
;; Imenu
|
||||||
|
(setq imenu-case-fold-search nil)
|
||||||
|
(setq imenu-create-index-function #'js--imenu-create-index)
|
||||||
|
|
||||||
;; for filling, pretend we're cc-mode
|
;; for filling, pretend we're cc-mode
|
||||||
(c-foreign-init-lit-pos-cache)
|
(c-foreign-init-lit-pos-cache)
|
||||||
(add-hook 'before-change-functions #'c-foreign-truncate-lit-pos-cache nil t)
|
(add-hook 'before-change-functions #'c-foreign-truncate-lit-pos-cache nil t)
|
||||||
|
|
@ -3726,9 +3659,6 @@ For BACKEND and WARN see `treesit-mode-function'."
|
||||||
(setq-local electric-layout-rules
|
(setq-local electric-layout-rules
|
||||||
'((?\; . after) (?\{ . after) (?\} . before)))
|
'((?\; . after) (?\{ . after) (?\} . before)))
|
||||||
|
|
||||||
(setq-local fill-paragraph-function #'js-fill-paragraph)
|
|
||||||
(setq-local normal-auto-fill-function #'js-do-auto-fill)
|
|
||||||
|
|
||||||
(let ((c-buffer-is-cc-mode t))
|
(let ((c-buffer-is-cc-mode t))
|
||||||
;; FIXME: These are normally set by `c-basic-common-init'. Should
|
;; FIXME: These are normally set by `c-basic-common-init'. Should
|
||||||
;; we call it instead? (Bug#6071)
|
;; we call it instead? (Bug#6071)
|
||||||
|
|
@ -3756,10 +3686,19 @@ For BACKEND and WARN see `treesit-mode-function'."
|
||||||
;; will mysteriously disappear.
|
;; will mysteriously disappear.
|
||||||
;; FIXME: We should instead do this fontification lazily by adding
|
;; FIXME: We should instead do this fontification lazily by adding
|
||||||
;; calls to syntax-propertize wherever it's really needed.
|
;; calls to syntax-propertize wherever it's really needed.
|
||||||
;; (syntax-propertize (point-max))
|
;;(syntax-propertize (point-max))
|
||||||
|
|
||||||
(js--backend-toggle 'elisp nil)
|
;; Tree-sitter support.
|
||||||
(setq-local major-mode-backend-function #'js--backend-toggle))
|
(setq-local treesit-mode-supported t)
|
||||||
|
(setq-local treesit-required-languages '(javascript))
|
||||||
|
(setq-local treesit-simple-indent-rules js--treesit-indent-rules)
|
||||||
|
(setq-local treesit-defun-type-regexp
|
||||||
|
(rx (or "class_declaration"
|
||||||
|
"method_definition"
|
||||||
|
"function_declaration"
|
||||||
|
"lexical_declaration")))
|
||||||
|
(setq-local treesit-font-lock-settings js--treesit-font-lock-settings)
|
||||||
|
(setq-local treesit-font-lock-feature-list '((minimal) (moderate) (full))))
|
||||||
|
|
||||||
(defvar js-json--treesit-font-lock-settings
|
(defvar js-json--treesit-font-lock-settings
|
||||||
(treesit-font-lock-rules
|
(treesit-font-lock-rules
|
||||||
|
|
@ -3791,29 +3730,18 @@ For BACKEND and WARN see `treesit-mode-function'."
|
||||||
((parent-is "object") parent-bol ,js-indent-level)
|
((parent-is "object") parent-bol ,js-indent-level)
|
||||||
)))
|
)))
|
||||||
|
|
||||||
(defun js--json-backend-toggle (backend warn)
|
|
||||||
"Toggle backend for `json-mode'.
|
|
||||||
For BACKEND and WARN see `treesit-mode-function'."
|
|
||||||
(cond
|
|
||||||
;; Tree-sitter.
|
|
||||||
((and (eq backend 'treesit) (treesit-ready-p warn 'json))
|
|
||||||
(setq-local treesit-simple-indent-rules js--json-treesit-indent-rules)
|
|
||||||
(setq-local indent-line-function #'treesit-indent)
|
|
||||||
|
|
||||||
(setq-local font-lock-keywords-only t)
|
|
||||||
(setq-local treesit-font-lock-settings js-json--treesit-font-lock-settings)
|
|
||||||
(treesit-font-lock-enable))
|
|
||||||
;; Elisp.
|
|
||||||
((eq backend 'elisp)
|
|
||||||
(js--backend-toggle 'elisp nil))))
|
|
||||||
|
|
||||||
;;;###autoload
|
;;;###autoload
|
||||||
(define-derived-mode js-json-mode js-mode "JSON"
|
(define-derived-mode js-json-mode js-mode "JSON"
|
||||||
(setq-local js-enabled-frameworks nil)
|
(setq-local js-enabled-frameworks nil)
|
||||||
;; Speed up `syntax-ppss': JSON files can be big but can't hold
|
;; Speed up `syntax-ppss': JSON files can be big but can't hold
|
||||||
;; regexp matchers nor #! thingies (and `js-enabled-frameworks' is nil).
|
;; regexp matchers nor #! thingies (and `js-enabled-frameworks' is nil).
|
||||||
(setq-local syntax-propertize-function #'ignore)
|
(setq-local syntax-propertize-function #'ignore)
|
||||||
(setq-local major-mode-backend-function #'js--json-backend-toggle))
|
|
||||||
|
;; Tree-sitter support.
|
||||||
|
(setq-local treesit-mode-supported t)
|
||||||
|
(setq-local treesit-required-languages '(json))
|
||||||
|
(setq-local treesit-simple-indent-rules js--json-treesit-indent-rules)
|
||||||
|
(setq-local treesit-font-lock-settings js-json--treesit-font-lock-settings))
|
||||||
|
|
||||||
;; Since we made JSX support available and automatically-enabled in
|
;; Since we made JSX support available and automatically-enabled in
|
||||||
;; the base `js-mode' (for ease of use), now `js-jsx-mode' simply
|
;; the base `js-mode' (for ease of use), now `js-jsx-mode' simply
|
||||||
|
|
|
||||||
|
|
@ -6375,35 +6375,6 @@ Add import for undefined name `%s' (empty to skip): "
|
||||||
(defvar electric-indent-inhibit)
|
(defvar electric-indent-inhibit)
|
||||||
(defvar prettify-symbols-alist)
|
(defvar prettify-symbols-alist)
|
||||||
|
|
||||||
(defun python--backend-toggle (backend warn)
|
|
||||||
"Toggle backend for `python-mode'.
|
|
||||||
BACKEND and WARN are explained in `treesit-mode-function'."
|
|
||||||
(cond
|
|
||||||
;; Tree-sitter.
|
|
||||||
((and (eq backend 'treesit) (treesit-ready-p warn 'python))
|
|
||||||
(setq-local font-lock-keywords-only t)
|
|
||||||
(setq-local treesit-font-lock-feature-list
|
|
||||||
'((basic) (moderate) (elaborate)))
|
|
||||||
(setq-local treesit-font-lock-settings
|
|
||||||
python--treesit-settings)
|
|
||||||
(treesit-font-lock-enable)
|
|
||||||
(setq-local imenu-create-index-function
|
|
||||||
#'python-imenu-treesit-create-index)
|
|
||||||
(add-hook 'which-func-functions
|
|
||||||
#'python-info-treesit-current-defun nil t))
|
|
||||||
;; Elisp.
|
|
||||||
((eq backend 'elisp)
|
|
||||||
(setq-local font-lock-defaults
|
|
||||||
`(,python-font-lock-keywords
|
|
||||||
nil nil nil nil
|
|
||||||
(font-lock-syntactic-face-function
|
|
||||||
. python-font-lock-syntactic-face-function)
|
|
||||||
(font-lock-extend-after-change-region-function
|
|
||||||
. python-font-lock-extend-region)))
|
|
||||||
(setq-local imenu-create-index-function
|
|
||||||
#'python-imenu-create-index)
|
|
||||||
(add-hook 'which-func-functions #'python-info-current-defun nil t))))
|
|
||||||
|
|
||||||
;;;###autoload
|
;;;###autoload
|
||||||
(define-derived-mode python-mode prog-mode "Python"
|
(define-derived-mode python-mode prog-mode "Python"
|
||||||
"Major mode for editing Python files.
|
"Major mode for editing Python files.
|
||||||
|
|
@ -6420,9 +6391,11 @@ BACKEND and WARN are explained in `treesit-mode-function'."
|
||||||
|
|
||||||
(setq-local forward-sexp-function python-forward-sexp-function)
|
(setq-local forward-sexp-function python-forward-sexp-function)
|
||||||
|
|
||||||
(python--backend-toggle 'elisp nil)
|
(setq-local font-lock-defaults
|
||||||
|
`(,python-font-lock-keywords
|
||||||
(setq-local major-mode-backend-function #'python--backend-toggle)
|
nil nil nil nil
|
||||||
|
(font-lock-syntactic-face-function
|
||||||
|
. python-font-lock-syntactic-face-function)))
|
||||||
|
|
||||||
(setq-local syntax-propertize-function
|
(setq-local syntax-propertize-function
|
||||||
python-syntax-propertize-function)
|
python-syntax-propertize-function)
|
||||||
|
|
@ -6451,9 +6424,14 @@ BACKEND and WARN are explained in `treesit-mode-function'."
|
||||||
(add-hook 'post-self-insert-hook
|
(add-hook 'post-self-insert-hook
|
||||||
#'python-indent-post-self-insert-function 'append 'local)
|
#'python-indent-post-self-insert-function 'append 'local)
|
||||||
|
|
||||||
|
(setq-local imenu-create-index-function
|
||||||
|
#'python-imenu-create-index)
|
||||||
|
|
||||||
(setq-local add-log-current-defun-function
|
(setq-local add-log-current-defun-function
|
||||||
#'python-info-current-defun)
|
#'python-info-current-defun)
|
||||||
|
|
||||||
|
(add-hook 'which-func-functions #'python-info-current-defun nil t)
|
||||||
|
|
||||||
(setq-local skeleton-further-elements
|
(setq-local skeleton-further-elements
|
||||||
'((abbrev-mode nil)
|
'((abbrev-mode nil)
|
||||||
(< '(backward-delete-char-untabify (min python-indent-offset
|
(< '(backward-delete-char-untabify (min python-indent-offset
|
||||||
|
|
@ -6462,13 +6440,13 @@ BACKEND and WARN are explained in `treesit-mode-function'."
|
||||||
|
|
||||||
(with-no-warnings
|
(with-no-warnings
|
||||||
;; suppress warnings about eldoc-documentation-function being obsolete
|
;; suppress warnings about eldoc-documentation-function being obsolete
|
||||||
(if (null eldoc-documentation-function)
|
(if (null eldoc-documentation-function)
|
||||||
;; Emacs<25
|
;; Emacs<25
|
||||||
(setq-local eldoc-documentation-function #'python-eldoc-function)
|
(setq-local eldoc-documentation-function #'python-eldoc-function)
|
||||||
(if (boundp 'eldoc-documentation-functions)
|
(if (boundp 'eldoc-documentation-functions)
|
||||||
(add-hook 'eldoc-documentation-functions #'python-eldoc-function nil t)
|
(add-hook 'eldoc-documentation-functions #'python-eldoc-function nil t)
|
||||||
(add-function :before-until (local 'eldoc-documentation-function)
|
(add-function :before-until (local 'eldoc-documentation-function)
|
||||||
#'python-eldoc-function))))
|
#'python-eldoc-function))))
|
||||||
|
|
||||||
(add-to-list
|
(add-to-list
|
||||||
'hs-special-modes-alist
|
'hs-special-modes-alist
|
||||||
|
|
@ -6499,7 +6477,15 @@ BACKEND and WARN are explained in `treesit-mode-function'."
|
||||||
(when python-indent-guess-indent-offset
|
(when python-indent-guess-indent-offset
|
||||||
(python-indent-guess-indent-offset))
|
(python-indent-guess-indent-offset))
|
||||||
|
|
||||||
(add-hook 'flymake-diagnostic-functions #'python-flymake nil t))
|
(add-hook 'flymake-diagnostic-functions #'python-flymake nil t)
|
||||||
|
|
||||||
|
(setq-local treesit-mode-supported t)
|
||||||
|
(setq-local treesit-required-languages '(python))
|
||||||
|
(setq-local treesit-font-lock-feature-list
|
||||||
|
'((basic) (moderate) (elaborate)))
|
||||||
|
(setq-local treesit-font-lock-settings python--treesit-settings)
|
||||||
|
(setq-local treesit-imenu-function
|
||||||
|
#'python-imenu-treesit-create-index))
|
||||||
|
|
||||||
;;; Completion predicates for M-x
|
;;; Completion predicates for M-x
|
||||||
;; Commands that only make sense when editing Python code
|
;; Commands that only make sense when editing Python code
|
||||||
|
|
|
||||||
|
|
@ -134,7 +134,7 @@
|
||||||
|
|
||||||
(string) @font-lock-string-face
|
(string) @font-lock-string-face
|
||||||
|
|
||||||
(template_string) @ts-mode--fontify-template-string
|
(template_string) @js--fontify-template-string
|
||||||
(template_substitution ["${" "}"] @font-lock-builtin-face)
|
(template_substitution ["${" "}"] @font-lock-builtin-face)
|
||||||
|
|
||||||
(comment) @font-lock-comment-face)
|
(comment) @font-lock-comment-face)
|
||||||
|
|
@ -248,61 +248,6 @@
|
||||||
(jsx_attribute (property_identifier) @font-lock-constant-face)))
|
(jsx_attribute (property_identifier) @font-lock-constant-face)))
|
||||||
"Tree-sitter font-lock settings.")
|
"Tree-sitter font-lock settings.")
|
||||||
|
|
||||||
(defun ts-mode--fontify-template-string (beg end node)
|
|
||||||
"Fontify template string but not substitution inside it.
|
|
||||||
BEG, END, NODE refers to the template_string node."
|
|
||||||
(ignore end)
|
|
||||||
;; Stolen from `js--fontify-template-string'
|
|
||||||
(let ((child (treesit-node-child node 0)))
|
|
||||||
(while child
|
|
||||||
(if (equal (treesit-node-type child) "template_substitution")
|
|
||||||
(put-text-property beg (treesit-node-start child)
|
|
||||||
'face 'font-lock-string-face)
|
|
||||||
(put-text-property beg (treesit-node-end child)
|
|
||||||
'face 'font-lock-string-face))
|
|
||||||
(setq beg (treesit-node-end child)
|
|
||||||
child (treesit-node-next-sibling child)))))
|
|
||||||
|
|
||||||
(defvar ts-mode--defun-type-regexp
|
|
||||||
(rx (or "class_declaration"
|
|
||||||
"method_definition"
|
|
||||||
"function_declaration"
|
|
||||||
"lexical_declaration"))
|
|
||||||
"Regular expression that matches type of defun nodes.
|
|
||||||
Used in `ts-mode--beginning-of-defun' and friends.")
|
|
||||||
|
|
||||||
(defun ts-mode--beginning-of-defun (&optional arg)
|
|
||||||
"Tree-sitter `beginning-of-defun' function.
|
|
||||||
ARG is the same as in `beginning-of-defun."
|
|
||||||
(let ((arg (or arg 1)))
|
|
||||||
(if (> arg 0)
|
|
||||||
;; Go backward.
|
|
||||||
(while (and (> arg 0)
|
|
||||||
(treesit-search-forward-goto
|
|
||||||
ts-mode--defun-type-regexp 'start nil t))
|
|
||||||
(setq arg (1- arg)))
|
|
||||||
;; Go forward.
|
|
||||||
(while (and (< arg 0)
|
|
||||||
(treesit-search-forward-goto
|
|
||||||
ts-mode--defun-type-regexp 'start))
|
|
||||||
(setq arg (1+ arg))))))
|
|
||||||
|
|
||||||
(defun ts-mode--end-of-defun (&optional arg)
|
|
||||||
"Tree-sitter `end-of-defun' function.
|
|
||||||
ARG is the same as in `end-of-defun."
|
|
||||||
(let ((arg (or arg 1)))
|
|
||||||
(if (< arg 0)
|
|
||||||
;; Go backward.
|
|
||||||
(while (and (< arg 0)
|
|
||||||
(treesit-search-forward-goto
|
|
||||||
ts-mode--defun-type-regexp 'end nil t))
|
|
||||||
(setq arg (1+ arg)))
|
|
||||||
;; Go forward.
|
|
||||||
(while (and (> arg 0)
|
|
||||||
(treesit-search-forward-goto
|
|
||||||
ts-mode--defun-type-regexp 'end))
|
|
||||||
(setq arg (1- arg))))))
|
|
||||||
|
|
||||||
;;;###autoload
|
;;;###autoload
|
||||||
(add-to-list 'auto-mode-alist '("\\.ts\\'" . ts-mode))
|
(add-to-list 'auto-mode-alist '("\\.ts\\'" . ts-mode))
|
||||||
|
|
||||||
|
|
@ -315,26 +260,31 @@ ARG is the same as in `end-of-defun."
|
||||||
:group 'typescript
|
:group 'typescript
|
||||||
:syntax-table ts-mode--syntax-table
|
:syntax-table ts-mode--syntax-table
|
||||||
|
|
||||||
|
;; Treesit-mode.
|
||||||
|
(setq-local treesit-mode-supported t)
|
||||||
|
(setq-local treesit-required-languages '(tsx))
|
||||||
|
;; Comments.
|
||||||
|
(setq-local comment-start "// ")
|
||||||
|
(setq-local comment-start-skip "\\(?://+\\|/\\*+\\)\\s *")
|
||||||
|
(setq-local comment-end "")
|
||||||
|
;; Indent.
|
||||||
|
(setq-local treesit-simple-indent-rules ts-mode--indent-rules)
|
||||||
|
;; Navigation.
|
||||||
|
(setq-local treesit-defun-type-regexp
|
||||||
|
(rx (or "class_declaration"
|
||||||
|
"method_definition"
|
||||||
|
"function_declaration"
|
||||||
|
"lexical_declaration")))
|
||||||
|
;; Font-lock.
|
||||||
|
(setq-local treesit-font-lock-settings ts-mode--font-lock-settings)
|
||||||
|
(setq-local treesit-font-lock-feature-list '((minimal) (moderate) (full)))
|
||||||
|
|
||||||
(cond
|
(cond
|
||||||
((treesit-ready-p nil 'tsx)
|
((treesit-ready-p '(tsx))
|
||||||
;; Comments
|
(treesit-mode))
|
||||||
(setq-local comment-start "// ")
|
|
||||||
(setq-local comment-start-skip "\\(?://+\\|/\\*+\\)\\s *")
|
|
||||||
(setq-local comment-end "")
|
|
||||||
|
|
||||||
(setq-local treesit-simple-indent-rules ts-mode--indent-rules)
|
|
||||||
(setq-local indent-line-function #'treesit-indent)
|
|
||||||
|
|
||||||
(setq-local beginning-of-defun-function #'ts-mode--beginning-of-defun)
|
|
||||||
(setq-local end-of-defun-function #'ts-mode--end-of-defun)
|
|
||||||
|
|
||||||
(setq font-lock-keywords-only t)
|
|
||||||
(setq-local treesit-font-lock-settings ts-mode--font-lock-settings)
|
|
||||||
(setq treesit-font-lock-feature-list '((minimal) (moderate) (full)))
|
|
||||||
(treesit-font-lock-enable))
|
|
||||||
(t
|
(t
|
||||||
(message "Tree-sitter for TypeScript isn't available, falling back to `js-mode'")
|
(js-mode)
|
||||||
(js-mode))))
|
(message "Tree-sitter for TypeScript isn't available, falling back to `js-mode'"))))
|
||||||
|
|
||||||
(provide 'ts-mode)
|
(provide 'ts-mode)
|
||||||
|
|
||||||
|
|
|
||||||
282
lisp/treesit.el
282
lisp/treesit.el
|
|
@ -33,7 +33,7 @@
|
||||||
(require 'cl-seq)
|
(require 'cl-seq)
|
||||||
(require 'font-lock)
|
(require 'font-lock)
|
||||||
|
|
||||||
;;; Activating tree-sitter
|
;;; Custom options
|
||||||
|
|
||||||
;; Tree-sitter always appear as treesit in symbols.
|
;; Tree-sitter always appear as treesit in symbols.
|
||||||
(defgroup treesit nil
|
(defgroup treesit nil
|
||||||
|
|
@ -48,105 +48,8 @@ indent, imenu, etc."
|
||||||
:type 'integer
|
:type 'integer
|
||||||
:version "29.1")
|
:version "29.1")
|
||||||
|
|
||||||
(defcustom treesit-mode-inhibit-message nil
|
|
||||||
"If non-nil, don't print message when tree-sitter can't activate."
|
|
||||||
:type 'boolean
|
|
||||||
:version "29.1")
|
|
||||||
|
|
||||||
(declare-function treesit-available-p "treesit.c")
|
(declare-function treesit-available-p "treesit.c")
|
||||||
|
|
||||||
(defvar-local major-mode-backend-function nil
|
|
||||||
"A function that toggles the \"backend\" for a major mode.
|
|
||||||
|
|
||||||
The function is passed two argument BACKEND and WARN. BACKEND is
|
|
||||||
a symbol representing the backend we want to activate. Currently
|
|
||||||
it can be `treesit' or `elisp'.
|
|
||||||
|
|
||||||
If WARN is non-nil, display a warning if tree-sitter can't
|
|
||||||
activate, if WARN is nil, just print an message and don't display
|
|
||||||
warning.")
|
|
||||||
|
|
||||||
;;;###autoload
|
|
||||||
(define-minor-mode treesit-mode
|
|
||||||
"Activate tree-sitter to power major-mode features.
|
|
||||||
|
|
||||||
This mode is merely a SUGGESTION of turning on tree-sitter,
|
|
||||||
actual activation of tree-sitter functionalities depends on
|
|
||||||
whether the major mode supports tree-sitter, availability of
|
|
||||||
specific tree-sitter language definition, etc."
|
|
||||||
:version "29.1"
|
|
||||||
:group 'treesit
|
|
||||||
(when major-mode-backend-function
|
|
||||||
(funcall major-mode-backend-function
|
|
||||||
(if treesit-mode 'treesit 'elisp)
|
|
||||||
(eq this-command 'treesit-mode))))
|
|
||||||
|
|
||||||
(defvar treesit-remapped-major-mode-alist nil
|
|
||||||
"A list like `major-mode-remap-alist'.
|
|
||||||
|
|
||||||
When major modes activate tree-sitter by switching to another
|
|
||||||
major mode and `global-treesit-mode' is on, they add an
|
|
||||||
entry (MODE . NEW-MODE) to `major-mode-remap-alist', so next time
|
|
||||||
Emacs uses NEW-MODE directly.
|
|
||||||
|
|
||||||
These remaps should be removed when `global-treesit-mode' is
|
|
||||||
turned off, so we record each (MODE . NEW-MODE) in this variable.")
|
|
||||||
|
|
||||||
;;;###autoload
|
|
||||||
(define-globalized-minor-mode global-treesit-mode treesit-mode
|
|
||||||
global-treesit-mode--turn-on
|
|
||||||
:version "29.1"
|
|
||||||
:group 'treesit
|
|
||||||
:predicate t
|
|
||||||
(when (not global-treesit-mode)
|
|
||||||
(dolist (map treesit-remapped-major-mode-alist)
|
|
||||||
(setq major-mode-remap-alist
|
|
||||||
(remove map major-mode-remap-alist)))))
|
|
||||||
|
|
||||||
(defun global-treesit-mode--turn-on ()
|
|
||||||
"Function used to determine whether to turn on `treesit-mode'.
|
|
||||||
Called in every buffer if `global-treesit-mode' is on."
|
|
||||||
(treesit-mode))
|
|
||||||
|
|
||||||
(defun treesit-ready-p (warn &rest languages)
|
|
||||||
"Check that tree-sitter is ready to be used.
|
|
||||||
|
|
||||||
If tree-sitter is not ready and WARN is nil-nil, emit a warning;
|
|
||||||
if WARN is nil, print a message. But if
|
|
||||||
`treesit-mode-inhibit-message' is non-nil, don't even print the
|
|
||||||
message.
|
|
||||||
|
|
||||||
LANGUAGES are languages we want check for availability."
|
|
||||||
(let (msg)
|
|
||||||
;; Check for each condition and set MSG.
|
|
||||||
(catch 'term
|
|
||||||
(when (not (treesit-available-p))
|
|
||||||
(setq msg "tree-sitter library is not built with Emacs")
|
|
||||||
(throw 'term nil))
|
|
||||||
(when (> (buffer-size) treesit-max-buffer-size)
|
|
||||||
(setq msg "buffer larger than `treesit-max-buffer-size'")
|
|
||||||
(throw 'term nil))
|
|
||||||
(dolist (lang languages)
|
|
||||||
(pcase-let ((`(,available . ,err)
|
|
||||||
(treesit-language-available-p lang t)))
|
|
||||||
(when (not available)
|
|
||||||
(setq msg (format "language definition for %s is unavailable (%s): %s"
|
|
||||||
lang (nth 0 err)
|
|
||||||
(string-join
|
|
||||||
(mapcar (lambda (x) (format "%s" x))
|
|
||||||
(cdr err))
|
|
||||||
" ")))
|
|
||||||
(throw 'term nil)))))
|
|
||||||
;; Decide if all conditions met and whether emit a warning.
|
|
||||||
(if (not msg)
|
|
||||||
t
|
|
||||||
(setq msg (concat "Cannot activate tree-sitter, because " msg))
|
|
||||||
(if warn
|
|
||||||
(display-warning 'treesit msg)
|
|
||||||
(unless treesit-mode-inhibit-message
|
|
||||||
(message "%s" msg)))
|
|
||||||
nil)))
|
|
||||||
|
|
||||||
;;; Parser API supplement
|
;;; Parser API supplement
|
||||||
|
|
||||||
(defun treesit-parse-string (string language)
|
(defun treesit-parse-string (string language)
|
||||||
|
|
@ -605,18 +508,6 @@ If LOUDLY is non-nil, message some debugging information."
|
||||||
(let ((font-lock-unfontify-region-function #'ignore))
|
(let ((font-lock-unfontify-region-function #'ignore))
|
||||||
(funcall #'font-lock-default-fontify-region start end loudly)))
|
(funcall #'font-lock-default-fontify-region start end loudly)))
|
||||||
|
|
||||||
(defun treesit-font-lock-enable ()
|
|
||||||
"Enable tree-sitter font-locking for the current buffer."
|
|
||||||
(treesit-font-lock-recompute-features)
|
|
||||||
(setq-local font-lock-fontify-region-function
|
|
||||||
#'treesit-font-lock-fontify-region)
|
|
||||||
;; If we don't set `font-lock-defaults' to some non-nil value,
|
|
||||||
;; font-lock doesn't enable properly (`font-lock-mode-internal'
|
|
||||||
;; doesn't run). See `font-lock-specified-p'.
|
|
||||||
(when (null font-lock-defaults)
|
|
||||||
(setq font-lock-defaults '(nil)))
|
|
||||||
(font-lock-mode 1))
|
|
||||||
|
|
||||||
;;; Indent
|
;;; Indent
|
||||||
|
|
||||||
(defvar treesit--indent-verbose nil
|
(defvar treesit--indent-verbose nil
|
||||||
|
|
@ -945,6 +836,177 @@ ALL, BACKWARD, and UP are the same as in `treesit-search-forward'."
|
||||||
(goto-char start))
|
(goto-char start))
|
||||||
node))
|
node))
|
||||||
|
|
||||||
|
;;; Navigation
|
||||||
|
|
||||||
|
(defvar-local treesit-defun-type-regexp nil
|
||||||
|
"A regexp that matches the node type of defun nodes.
|
||||||
|
For example, \"(function|class)_definition\".
|
||||||
|
|
||||||
|
This is used by `treesit-beginning-of-defun' and friends. Bind
|
||||||
|
it buffer-locally and `treesit-mode' will use it for navigating
|
||||||
|
defun's.")
|
||||||
|
|
||||||
|
(defun treesit-beginning-of-defun (&optional arg)
|
||||||
|
"Tree-sitter `beginning-of-defun' function.
|
||||||
|
ARG is the same as in `beginning-of-defun."
|
||||||
|
(let ((arg (or arg 1)))
|
||||||
|
(if (> arg 0)
|
||||||
|
;; Go backward.
|
||||||
|
(while (and (> arg 0)
|
||||||
|
(treesit-search-forward-goto
|
||||||
|
treesit-defun-type-regexp 'start nil t))
|
||||||
|
(setq arg (1- arg)))
|
||||||
|
;; Go forward.
|
||||||
|
(while (and (< arg 0)
|
||||||
|
(treesit-search-forward-goto
|
||||||
|
treesit-defun-type-regexp 'start))
|
||||||
|
(setq arg (1+ arg))))))
|
||||||
|
|
||||||
|
(defun treesit-end-of-defun ()
|
||||||
|
"Tree-sitter `end-of-defun' function."
|
||||||
|
(treesit-search-forward-goto treesit-defun-type-regexp 'end))
|
||||||
|
|
||||||
|
;;; Imenu
|
||||||
|
|
||||||
|
(defvar-local treesit-imenu-function nil
|
||||||
|
"Tree-sitter version of `imenu-create-index-function'.
|
||||||
|
|
||||||
|
Set this variable to a function and `treesit-mode' will bind it
|
||||||
|
to `imenu-create-index-function'.")
|
||||||
|
|
||||||
|
;;; Activating tree-sitter
|
||||||
|
|
||||||
|
(defvar-local treesit-mode-supported nil
|
||||||
|
"Set this variable to t to indicate support for `treesit-mode'.")
|
||||||
|
|
||||||
|
(defvar-local treesit-required-languages nil
|
||||||
|
"A list of languages this major mode need for tree-sitter.")
|
||||||
|
|
||||||
|
(defun treesit-ready-p (languages &optional report)
|
||||||
|
"Check that tree-sitter is ready to be used.
|
||||||
|
|
||||||
|
If tree-sitter is not ready and REPORT non-nil, emit a warning or
|
||||||
|
message. Emit a warning if REPORT is `warn', message if REPORT
|
||||||
|
is `message'.
|
||||||
|
|
||||||
|
LANGUAGES is a list of languages we want check for availability."
|
||||||
|
(let (msg)
|
||||||
|
;; Check for each condition and set MSG.
|
||||||
|
(catch 'term
|
||||||
|
(when (not treesit-mode-supported)
|
||||||
|
(setq msg "this major mode doesn't support it")
|
||||||
|
(throw 'term nil))
|
||||||
|
(when (not (treesit-available-p))
|
||||||
|
(setq msg "tree-sitter library is not built with Emacs")
|
||||||
|
(throw 'term nil))
|
||||||
|
(when (> (buffer-size) treesit-max-buffer-size)
|
||||||
|
(setq msg "buffer larger than `treesit-max-buffer-size'")
|
||||||
|
(throw 'term nil))
|
||||||
|
(dolist (lang languages)
|
||||||
|
(pcase-let ((`(,available . ,err)
|
||||||
|
(treesit-language-available-p lang t)))
|
||||||
|
(when (not available)
|
||||||
|
(setq msg (format "language definition for %s is unavailable (%s): %s"
|
||||||
|
lang (nth 0 err)
|
||||||
|
(string-join
|
||||||
|
(mapcar (lambda (x) (format "%s" x))
|
||||||
|
(cdr err))
|
||||||
|
" ")))
|
||||||
|
(throw 'term nil)))))
|
||||||
|
;; Decide if all conditions met and whether emit a warning.
|
||||||
|
(if (not msg)
|
||||||
|
t
|
||||||
|
(setq msg (concat "Cannot activate tree-sitter, because " msg))
|
||||||
|
(pcase report
|
||||||
|
('warn (display-warning 'treesit msg))
|
||||||
|
('message (message "%s" msg)))
|
||||||
|
nil)))
|
||||||
|
|
||||||
|
(defvar-local treesit--local-variable-backup nil
|
||||||
|
"An alist of (VAR . VALUE) that backs up pre-treesit-mode values.")
|
||||||
|
|
||||||
|
(defun treesit--backup-local-variable (variable value &optional restore)
|
||||||
|
"Backup VARIABLE's value and set it to VALUE.
|
||||||
|
If RESTORE is non-nil, ignore VALUE and restore the backup (if
|
||||||
|
there is any)."
|
||||||
|
(if restore
|
||||||
|
(when-let ((value (alist-get variable
|
||||||
|
treesit--local-variable-backup)))
|
||||||
|
(set variable value))
|
||||||
|
;; Set.
|
||||||
|
(make-variable-buffer-local variable)
|
||||||
|
(setf (alist-get variable treesit--local-variable-backup)
|
||||||
|
(symbol-value variable))
|
||||||
|
(set variable value)))
|
||||||
|
|
||||||
|
;;;###autoload
|
||||||
|
(define-minor-mode treesit-mode
|
||||||
|
"Activate tree-sitter to power major-mode features.
|
||||||
|
|
||||||
|
This mode is merely a SUGGESTION of turning on tree-sitter,
|
||||||
|
actual activation of tree-sitter functionalities depends on
|
||||||
|
whether the major mode supports tree-sitter, availability of
|
||||||
|
specific tree-sitter language definition, etc."
|
||||||
|
:version "29.1"
|
||||||
|
:group 'treesit
|
||||||
|
(if treesit-mode
|
||||||
|
(when (treesit-ready-p treesit-required-languages 'warn)
|
||||||
|
;; Font-lock.
|
||||||
|
(setq-local treesit--local-variable-backup nil)
|
||||||
|
;; NOTE: If anyone other than the major mode added stuff to
|
||||||
|
;; `font-lock-keywords', they would need to re-add them after
|
||||||
|
;; `treesit-mode' turns on.
|
||||||
|
(when treesit-font-lock-settings
|
||||||
|
(treesit--backup-local-variable 'font-lock-keywords-only t)
|
||||||
|
(treesit--backup-local-variable 'font-lock-keywords nil)
|
||||||
|
(treesit--backup-local-variable
|
||||||
|
'font-lock-fontify-region-function
|
||||||
|
#'treesit-font-lock-fontify-region)
|
||||||
|
(treesit-font-lock-recompute-features))
|
||||||
|
;; Indent.
|
||||||
|
(when treesit-simple-indent-rules
|
||||||
|
(treesit--backup-local-variable
|
||||||
|
'indent-line-function #'treesit-indent))
|
||||||
|
;; Navigation.
|
||||||
|
(when treesit-defun-type-regexp
|
||||||
|
(treesit--backup-local-variable
|
||||||
|
'beginning-of-defun-function #'treesit-beginning-of-defun)
|
||||||
|
(treesit--backup-local-variable
|
||||||
|
'end-of-defun-function #'treesit-end-of-defun))
|
||||||
|
;; Imenu.
|
||||||
|
(when treesit-imenu-function
|
||||||
|
(treesit--backup-local-variable
|
||||||
|
'imenu-create-index-function treesit-imenu-function)))
|
||||||
|
;; Font-lock.
|
||||||
|
(treesit--backup-local-variable 'font-lock-keywords nil t)
|
||||||
|
(treesit--backup-local-variable 'font-lock-keywords-only nil t)
|
||||||
|
(treesit--backup-local-variable
|
||||||
|
'font-lock-fontify-region-function nil t)
|
||||||
|
;; Indent.
|
||||||
|
(treesit--backup-local-variable 'indent-line-function nil t)
|
||||||
|
;; Navigation.
|
||||||
|
(treesit--backup-local-variable 'beginning-of-defun-function nil t)
|
||||||
|
(treesit--backup-local-variable 'end-of-defun-function nil t)
|
||||||
|
;; Imenu.
|
||||||
|
(treesit--backup-local-variable 'imenu-create-index-function nil t)
|
||||||
|
))
|
||||||
|
|
||||||
|
(defun global-treesit-mode--turn-on ()
|
||||||
|
"Function used to determine whether to turn on `treesit-mode'.
|
||||||
|
Called in every buffer if `global-treesit-mode' is on."
|
||||||
|
;; Check tree-sitter readiness and don't emit warnings.
|
||||||
|
(when (and treesit-mode-supported
|
||||||
|
(treesit-ready-p treesit-required-languages))
|
||||||
|
(treesit-mode)))
|
||||||
|
|
||||||
|
;;;###autoload
|
||||||
|
(define-globalized-minor-mode global-treesit-mode treesit-mode
|
||||||
|
global-treesit-mode--turn-on
|
||||||
|
:version "29.1"
|
||||||
|
:group 'treesit
|
||||||
|
:predicate t
|
||||||
|
nil)
|
||||||
|
|
||||||
;;; Debugging
|
;;; Debugging
|
||||||
|
|
||||||
(defvar-local treesit--inspect-name nil
|
(defvar-local treesit--inspect-name nil
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue