Embed elixir in heex as well as elixir->heex->elixir (bug#76788).

* lisp/progmodes/elixir-ts-mode.el (elixir-ts--range-rules):
Rename to a shorter name from 'elixir-ts--treesit-range-rules'.
(elixir-ts--font-lock-feature-list, elixir-ts--thing-settings)
(elixir-ts--range-rules): New variables with default values
extracted from 'elixir-ts-mode'.
(elixir-ts-mode): Use 'elixir-ts--font-lock-feature-list',
'elixir-ts--thing-settings', 'elixir-ts--range-rules'
and 'heex-ts--range-rules'.
Use 'treesit-merge-font-lock-feature-list' to merge
'heex-ts--font-lock-feature-list'.

* lisp/progmodes/heex-ts-mode.el
(heex-ts--font-lock-feature-list, heex-ts--range-rules):
New variables.
(heex-ts-mode): Use 'heex-ts--font-lock-feature-list',
'heex-ts--range-rules'.  Merge 'elixir-ts--font-lock-settings',
'elixir-ts--font-lock-feature-list', 'elixir-ts--thing-settings'
for embedding elixir in heex.  Enable the 'sexp' navigation
by default with 'treesit-cycle-sexp-type'.

* lisp/progmodes/c-ts-mode.el (c-ts-mode): Append
'treesit-range-rules' to possibly already existing list in
'treesit-range-settings'.

* lisp/treesit.el (treesit-language-at-point-default):
Optimize to use 'when-let*'.
This commit is contained in:
Juri Linkov 2025-04-18 19:52:19 +03:00
parent 3d3be6dd0e
commit d56e37c83c
4 changed files with 116 additions and 65 deletions

View file

@ -1516,13 +1516,14 @@ in your init files."
treesit-font-lock-settings
c-ts-mode-doxygen-comment-font-lock-settings))
(setq-local treesit-range-settings
(treesit-range-rules
:embed 'doxygen
:host 'c
:local t
`(((comment) @cap
(:match
,c-ts-mode--doxygen-comment-regex @cap)))))))))
(append treesit-range-settings
(treesit-range-rules
:embed 'doxygen
:host 'c
:local t
`(((comment) @cap
(:match
,c-ts-mode--doxygen-comment-regex @cap))))))))))
(derived-mode-add-parents 'c-ts-mode '(c-mode))

View file

@ -567,18 +567,59 @@
"Tree-sitter font-lock settings.")
(defvar elixir-ts--treesit-range-rules
(when (treesit-available-p)
(treesit-range-rules
:embed 'heex
:host 'elixir
'((sigil (sigil_name) @_name
(:match "^[HF]$" @_name)
(quoted_content) @heex)))))
(defvar elixir-ts--font-lock-feature-list
'(( elixir-comment elixir-doc elixir-definition)
( elixir-string elixir-keyword elixir-data-type)
( elixir-sigil elixir-builtin elixir-string-escape)
( elixir-function-call elixir-variable elixir-operator elixir-number ))
"Tree-sitter font-lock feature list.")
(defvar elixir-ts--thing-settings
`((sexp (not (or (and named
,(rx bos (or "source" "comment") eos))
(and anonymous
,(rx (or "{" "}" "[" "]" "(" ")"
"do" "end"))))))
(list
(or (and "\\`arguments\\'" ,#'elixir-ts--with-parens-0-p)
(and "\\`unary_operator\\'" ,#'elixir-ts--with-parens-1-p)
,(rx bos (or "block"
"quoted_atom"
"string"
"interpolation"
"sigil"
"quoted_keyword"
"list"
"tuple"
"bitstring"
"map"
"do_block"
"anonymous_function")
eos)))
(sexp-default
;; For `C-M-f' in "&|(a)"
("(" . ,(lambda (node)
(equal (treesit-node-type (treesit-node-parent node))
"unary_operator"))))
(sentence
,(rx bos (or "call") eos))
(text
,(rx bos (or "string" "sigil" "comment") eos)))
"`treesit-thing-settings' for Elixir.")
(defvar elixir-ts--range-rules
(treesit-range-rules
:embed 'heex
:host 'elixir
'((sigil (sigil_name) @_name
(:match "^[HF]$" @_name)
(quoted_content) @heex))))
(defvar heex-ts--range-rules)
(defvar heex-ts--thing-settings)
(defvar heex-ts--indent-rules)
(defvar heex-ts--font-lock-settings)
(defvar heex-ts--font-lock-feature-list)
(defun elixir-ts--treesit-anchor-grand-parent-bol (_n parent &rest _)
"Return the beginning of non-space characters for the parent node of PARENT."
@ -693,11 +734,7 @@ Return nil if NODE is not a defun node or doesn't have a name."
;; Font-lock.
(setq-local treesit-font-lock-settings elixir-ts--font-lock-settings)
(setq-local treesit-font-lock-feature-list
'(( elixir-comment elixir-doc elixir-definition)
( elixir-string elixir-keyword elixir-data-type)
( elixir-sigil elixir-builtin elixir-string-escape)
( elixir-function-call elixir-variable elixir-operator elixir-number )))
elixir-ts--font-lock-feature-list)
;; Imenu.
(setq-local treesit-simple-imenu-settings
@ -708,37 +745,7 @@ Return nil if NODE is not a defun node or doesn't have a name."
;; Navigation.
(setq-local treesit-thing-settings
`((elixir
(sexp (not (or (and named
,(rx bos (or "source" "comment") eos))
(and anonymous
,(rx (or "{" "}" "[" "]" "(" ")"
"do" "end"))))))
(list
(or (and "\\`arguments\\'" ,#'elixir-ts--with-parens-0-p)
(and "\\`unary_operator\\'" ,#'elixir-ts--with-parens-1-p)
,(rx bos (or "block"
"quoted_atom"
"string"
"interpolation"
"sigil"
"quoted_keyword"
"list"
"tuple"
"bitstring"
"map"
"do_block"
"anonymous_function")
eos)))
(sexp-default
;; For `C-M-f' in "&|(a)"
("(" . ,(lambda (node)
(equal (treesit-node-type (treesit-node-parent node))
"unary_operator"))))
(sentence
,(rx bos (or "call") eos))
(text
,(rx bos (or "string" "sigil" "comment") eos)))
`((elixir ,@elixir-ts--thing-settings)
(heex ,@heex-ts--thing-settings)))
(setq-local treesit-defun-type-regexp
'("call" . elixir-ts--defun-p))
@ -747,7 +754,12 @@ Return nil if NODE is not a defun node or doesn't have a name."
;; Embedded Heex.
(when (treesit-ensure-installed 'heex)
(setq-local treesit-range-settings elixir-ts--treesit-range-rules)
(setq-local treesit-range-settings
(append elixir-ts--range-rules
;; Leave only local parsers from heex
;; for elixir->heex->elixir embedding.
(seq-filter (lambda (r) (nth 2 r))
heex-ts--range-rules)))
(setq-local treesit-font-lock-settings
(append treesit-font-lock-settings
@ -758,12 +770,9 @@ Return nil if NODE is not a defun node or doesn't have a name."
heex-ts--indent-rules))
(setq-local treesit-font-lock-feature-list
'(( elixir-comment elixir-doc elixir-definition
heex-comment heex-keyword heex-doctype )
( elixir-string elixir-keyword elixir-data-type
heex-component heex-tag heex-attribute heex-string )
( elixir-sigil elixir-builtin elixir-string-escape)
( elixir-function-call elixir-variable elixir-operator elixir-number ))))
(treesit-merge-font-lock-feature-list
treesit-font-lock-feature-list
heex-ts--font-lock-feature-list)))
(treesit-major-mode-setup)
(setq-local syntax-propertize-function #'elixir-ts--syntax-propertize)

View file

@ -132,6 +132,12 @@
])))
"Tree-sitter font-lock settings.")
(defvar heex-ts--font-lock-feature-list
'(( heex-comment heex-keyword heex-doctype )
( heex-component heex-tag heex-attribute heex-string )
() ())
"Tree-sitter font-lock feature list.")
(defun heex-ts--defun-name (node)
"Return the name of the defun NODE.
Return nil if NODE is not a defun node or doesn't have a name."
@ -166,6 +172,24 @@ Return nil if NODE is not a defun node or doesn't have a name."
,(rx bos (or "comment" "text") eos)))
"`treesit-thing-settings' for HEEx.")
(defvar heex-ts--range-rules
(treesit-range-rules
:embed 'elixir
:host 'heex
'((directive [(partial_expression_value)
(ending_expression_value)]
@cap))
:embed 'elixir
:host 'heex
:local t
'((directive (expression_value) @cap)
(expression (expression_value) @cap))))
(defvar elixir-ts--font-lock-settings)
(defvar elixir-ts--font-lock-feature-list)
(defvar elixir-ts--thing-settings)
;;;###autoload
(define-derived-mode heex-ts-mode html-mode "HEEx"
"Major mode for editing HEEx, powered by tree-sitter."
@ -204,11 +228,29 @@ Return nil if NODE is not a defun node or doesn't have a name."
(setq-local treesit-simple-indent-rules heex-ts--indent-rules)
(setq-local treesit-font-lock-feature-list
'(( heex-comment heex-keyword heex-doctype )
( heex-component heex-tag heex-attribute heex-string )
() ()))
heex-ts--font-lock-feature-list)
(treesit-major-mode-setup)))
(when (treesit-ready-p 'elixir)
(require 'elixir-ts-mode)
(treesit-parser-create 'elixir)
(setq-local treesit-range-settings heex-ts--range-rules)
(setq-local treesit-font-lock-settings
(append treesit-font-lock-settings
elixir-ts--font-lock-settings))
(setq-local treesit-font-lock-feature-list
(treesit-merge-font-lock-feature-list
treesit-font-lock-feature-list
elixir-ts--font-lock-feature-list))
(setq-local treesit-thing-settings
(append treesit-thing-settings
`((elixir ,@elixir-ts--thing-settings)))))
(treesit-major-mode-setup)
;; Enable the 'sexp' navigation by default
(treesit-cycle-sexp-type)))
(derived-mode-add-parents 'heex-ts-mode '(heex-mode))

View file

@ -195,9 +195,8 @@ Returns the language at POSITION, or nil if there's no parser in the
buffer. When there are multiple parsers that cover POSITION, use the
parser with the deepest embed level as it's the \"most relevant\" parser
at POSITION."
(let ((parser (car (treesit-parsers-at position))))
(when parser
(treesit-parser-language parser))))
(when-let* ((parser (car (treesit-parsers-at position))))
(treesit-parser-language parser)))
;;; Node API supplement