mirror of
https://git.savannah.gnu.org/git/emacs.git
synced 2026-09-10 07:46:51 -04:00
Fix JSX tag angle-bracket pairing in tsx-ts-mode
* lisp/progmodes/typescript-ts-mode.el (tsx-ts--s-p-query): Capture 'jsx_self_closing_element' as well. (tsx-ts--syntax-propertize-captures): For JSX tag elements, give only the leading '<' and trailing '>' matching-pair syntax and leave the interior untouched; for 'jsx_text', neutralize balanced pair characters to punctuation (bug#81460). * test/lisp/progmodes/typescript-ts-mode-tests.el (tsx-ts-mode-test-jsx-tag-syntax-propertize): New test.
This commit is contained in:
parent
8b521b98ed
commit
cc210dbf14
|
|
@ -824,7 +824,8 @@ at least 3 (which is the default value)."
|
|||
'(((regex pattern: (regex_pattern) @regexp))
|
||||
((jsx_text) @jsx)
|
||||
((jsx_opening_element) @jsx)
|
||||
((jsx_closing_element) @jsx)))))
|
||||
((jsx_closing_element) @jsx)
|
||||
((jsx_self_closing_element) @jsx)))))
|
||||
|
||||
(defun typescript-ts--syntax-propertize (beg end)
|
||||
(let ((captures (treesit-query-capture 'typescript typescript-ts--s-p-query beg end)))
|
||||
|
|
@ -845,24 +846,35 @@ at least 3 (which is the default value)."
|
|||
(incf ne)
|
||||
(put-text-property ns (1+ ns) 'syntax-table syntax)
|
||||
(put-text-property (1- ne) ne 'syntax-table syntax)))
|
||||
;; We put punctuation syntax on all the balanced pair
|
||||
;; characters so they don't mess up syntax-ppss. We can't put
|
||||
;; string syntax on the whole thing because a) it doesn't work
|
||||
;; if the text is one character long, and b) it interferes
|
||||
;; forward/backward-sexp.
|
||||
|
||||
('jsx
|
||||
(save-excursion
|
||||
(goto-char ns)
|
||||
(while (re-search-forward (rx (or "{" "}" "[" "]"
|
||||
"(" ")" "<" ">"))
|
||||
ne t)
|
||||
(put-text-property
|
||||
(match-beginning 0) (match-end 0)
|
||||
'syntax-table (string-to-syntax
|
||||
(cond
|
||||
((equal (match-string 0) "<") "(>")
|
||||
((equal (match-string 0) ">") ")<")
|
||||
(t ".")))))))))))
|
||||
(if (member (treesit-node-type node)
|
||||
'("jsx_opening_element"
|
||||
"jsx_closing_element"
|
||||
"jsx_self_closing_element"))
|
||||
;; A JSX tag's only real delimiters are its own outermost
|
||||
;; '<' and '>'. Mark just those two as a matching pair and
|
||||
;; leave the interior alone: attribute values are '{...}'
|
||||
;; expressions of ordinary code (possibly with nested JSX)
|
||||
;; whose brackets must keep their normal syntax so they
|
||||
;; nest, match and highlight correctly.
|
||||
(progn
|
||||
(put-text-property ns (1+ ns)
|
||||
'syntax-table (string-to-syntax "(>"))
|
||||
(put-text-property (1- ne) ne
|
||||
'syntax-table (string-to-syntax ")<")))
|
||||
;; jsx_text is raw text with no nesting to preserve, so
|
||||
;; neutralize its balanced-pair characters to punctuation;
|
||||
;; otherwise stray or unbalanced brackets in the text would
|
||||
;; confuse syntax-ppss.
|
||||
(save-excursion
|
||||
(goto-char ns)
|
||||
(while (re-search-forward (rx (or "{" "}" "[" "]"
|
||||
"(" ")" "<" ">"))
|
||||
ne t)
|
||||
(put-text-property
|
||||
(match-beginning 0) (match-end 0)
|
||||
'syntax-table (string-to-syntax "."))))))))))
|
||||
|
||||
;;;###autoload
|
||||
(defun tsx-ts-mode-maybe ()
|
||||
|
|
|
|||
|
|
@ -28,5 +28,53 @@
|
|||
(treesit-ready-p 'tsx)))
|
||||
(ert-test-erts-file (ert-resource-file "indent.erts")))
|
||||
|
||||
(ert-deftest tsx-ts-mode-test-jsx-tag-syntax-propertize ()
|
||||
"Every JSX tag's angle brackets must pair with that tag's own bounds.
|
||||
|
||||
Make sure jsx element's `<' pair with the closing `>' correctly instead
|
||||
of the wrong `>' from its attributes contained an arrow function `=>'."
|
||||
(skip-unless (treesit-ready-p 'tsx))
|
||||
(dolist (src '(;; A fragment, arrow-function attributes, and an arrow
|
||||
;; that returns a nested `<div></div>' element -- all
|
||||
;; inside the opening tag's range.
|
||||
"const A = () => (
|
||||
<>
|
||||
<Component
|
||||
onClick={() => {}}
|
||||
panel={() => {
|
||||
return <div></div>;
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Component>
|
||||
</>
|
||||
);
|
||||
"
|
||||
;; A self-closing element nested inside an attribute
|
||||
;; expression.
|
||||
"const B = () => (
|
||||
<Component slot={<Panel onClick={() => cb()}/>}>
|
||||
{children}
|
||||
</Component>
|
||||
);
|
||||
"))
|
||||
(with-temp-buffer
|
||||
(insert src)
|
||||
(tsx-ts-mode)
|
||||
(syntax-propertize (point-max))
|
||||
(pcase-dolist (`(,_ . ,node)
|
||||
(treesit-query-capture
|
||||
'tsx '(((jsx_opening_element) @el)
|
||||
((jsx_closing_element) @el)
|
||||
((jsx_self_closing_element) @el))))
|
||||
(let ((ns (treesit-node-start node))
|
||||
(ne (treesit-node-end node)))
|
||||
(should (eq (char-after ns) ?<))
|
||||
(should (eq (char-before ne) ?>))
|
||||
;; Forward from the tag's `<' lands exactly after its own
|
||||
;; `>', and backward round-trips to the `<'.
|
||||
(should (= (scan-sexps ns 1) ne))
|
||||
(should (= (scan-sexps ne -1) ns)))))))
|
||||
|
||||
(provide 'typescript-ts-mode-tests)
|
||||
;;; typescript-ts-mode-tests.el ends here
|
||||
|
|
|
|||
Loading…
Reference in a new issue