jtsx
is an Emacs package for editing JSX
or TSX
files. It provides jtsx-jsx-mode
and jtsx-tsx-mode
major modes implemented respectively on top of js-ts-mode
and tsx-ts-mode
, benefiting thus from the new built-in Tree-sitter feature.
Summary of features and fixes:
- Fix commenting and indenting issues with
JSX
code in Emacs built-injs-ts-mode
andtsx-ts-mode
modes - Refactoring: moving, wrapping/unwrapping, deleting, renaming
JSX
elements - Jumping between
JSX
opening and closing tags - Electric
JSX
closing tag and new line - Code folding
Note that jtsx-jsx-mode
and jtsx-tsx-mode
work as well respectively for standart JS
and TS
files.
Emacs 29.1 or higher, built with tree-sitter
support (./configure --with-tree-sitter
) is required. To check if your Emacs embeds tree-sitter
, typing M-x treesit
should give you at least treesit-install-language-grammar
result.
jtsx
does not work with the deprecated tree-sitter package.
tree-sitter
support, tree-sitter
languages are not installed by default. Git
and a C/C++
compiler are necessary to install them.
The straightforward way to install jtsx
is through the Melpa package manager. You can find more informations on how to install a Melpa
package here.
Installing tree-sitter
languages is required by jtsx
(javascript
for jtsx-jsx-mode
, tsx
for jtsx-tsx-mode
). You can use M-x jtsx-install-treesit-language
command which is a convenient wrapper around treesit-install-language-grammar
for that purpose.
For more advanced usages, see M-x treesit-install-language-grammar
command, or manually compile and set up language libraries.
Here an example of configuration using use-package, to put in the Emacs
init.el
:
- attach
jtsx-jsx-mode
toJSX
andJS
files - attach
jtsx-tsx-mode
toTSX
andTS
files - bind
jtsx
functions to the same shortcuts forjtsx-jsx-mode
andjtsx-tsx-mode
- set indention offsets for
JSX
andTSX
modes (use base mode variables) - customize
jtsx
behaviour through provided variables - enable
hideshow
minor mode for code folding
(use-package jtsx
:ensure t
:mode (("\\.jsx?\\'" . jtsx-jsx-mode)
("\\.tsx?\\'" . jtsx-tsx-mode))
:commands jtsx-install-treesit-language
:hook ((jtsx-jsx-mode . hs-minor-mode)
(jtsx-tsx-mode . hs-minor-mode))
:custom
;; Optional customizations
;; (js-indent-level 2)
;; (typescript-ts-mode-indent-offset 2)
;; (jtsx-switch-indent-offset 0)
;; (jtsx-indent-statement-block-regarding-standalone-parent nil)
;; (jtsx-jsx-element-move-allow-step-out t)
;; (jtsx-enable-jsx-electric-closing-element t)
;; (jtsx-enable-electric-open-newline-between-jsx-element-tags t)
;; (jtsx-enable-all-syntax-highlighting-features t)
:config
(defun jtsx-bind-keys-to-mode-map (mode-map)
"Bind keys to MODE-MAP."
(define-key mode-map (kbd "C-c C-j") 'jtsx-jump-jsx-element-tag-dwim)
(define-key mode-map (kbd "C-c j o") 'jtsx-jump-jsx-opening-tag)
(define-key mode-map (kbd "C-c j c") 'jtsx-jump-jsx-closing-tag)
(define-key mode-map (kbd "C-c j r") 'jtsx-rename-jsx-element)
(define-key mode-map (kbd "C-c <down>") 'jtsx-move-jsx-element-tag-forward)
(define-key mode-map (kbd "C-c <up>") 'jtsx-move-jsx-element-tag-backward)
(define-key mode-map (kbd "C-c C-<down>") 'jtsx-move-jsx-element-forward)
(define-key mode-map (kbd "C-c C-<up>") 'jtsx-move-jsx-element-backward)
(define-key mode-map (kbd "C-c C-S-<down>") 'jtsx-move-jsx-element-step-in-forward)
(define-key mode-map (kbd "C-c C-S-<up>") 'jtsx-move-jsx-element-step-in-backward)
(define-key mode-map (kbd "C-c j w") 'jtsx-wrap-in-jsx-element)
(define-key mode-map (kbd "C-c j u") 'jtsx-unwrap-jsx)
(define-key mode-map (kbd "C-c j d") 'jtsx-delete-jsx-node))
(defun jtsx-bind-keys-to-jtsx-jsx-mode-map ()
(jtsx-bind-keys-to-mode-map jtsx-jsx-mode-map))
(defun jtsx-bind-keys-to-jtsx-tsx-mode-map ()
(jtsx-bind-keys-to-mode-map jtsx-tsx-mode-map))
(add-hook 'jtsx-jsx-mode-hook 'jtsx-bind-keys-to-jtsx-jsx-mode-map)
(add-hook 'jtsx-tsx-mode-hook 'jtsx-bind-keys-to-jtsx-tsx-mode-map))
M-x jtsx-jump-jsx-element-tag-dwim
jumps to the opening or closing tag of the JSX
enclosing element. The furthest tag is choosen.
M-x jtsx-jump-jsx-opening-tag
jumps to the opening tag of the JSX
enclosing element.
M-x jtsx-jump-jsx-closing-tag
jumps to the closing tag of the JSX
enclosing element.
M-x jtsx-rename-jsx-element
renames both the opening and closing tags of a JSX
element. Cursor must either be inside the opening or the closing tag.
jtsx
implements some commands to move a JSX
tag or node through the JSX
structure, re-indenting automatically the modified part of code. JSX
nodes can be an element (self-closing or not), an expression or a text line.
M-x jtsx-move-jsx-element-tag-forward
moves a JSX element tag (opening or closing) forward.
M-x jtsx-move-jsx-element-tag-backward
moves a JSX element tag (opening or closing) backward.
M-x jtsx-move-jsx-element-forward
moves a JSX element (or any JSX node) forward.
M-x jtsx-move-jsx-element-backward
moves a JSX element (or any JSX node) backward.
M-x jtsx-move-jsx-element-step-in-forward
moves a JSX element (or any JSX node) forward. Step into sibling elements if possible.
M-x jtsx-move-jsx-element-step-in-backward
moves a JSX element (or any JSX node) backward. Step into sibling elements if possible.
Stepping out when moving can be desactivated by setting jtsx-jsx-element-move-allow-step-out
to nil
.
M-x jtsx-wrap-in-jsx-element
wraps JSX nodes in a new JSX element. Nodes are selected by a region if there is an active one. Else the node at point is used.
M-x jtsx-unwrap-jsx
unwraps JSX nodes. The wrapping node to remove is the node at point.
M-x jtsx-delete-jsx-node
deletes the JSX node at point and its children.
When entering the >
of an opening tag, the pending closing tag is automatically added right after the cursor if relevant (the function tries to guess if the closing addition is expected or not).
Also, when inside an empty inline jsx element (typically right after automatic insertion of closing tag), pressing ENTER
will insert 2 lines and let the cursor ready to add children elements.
These functionalities can be desactivated by setting jtsx-enable-jsx-electric-closing-element
to nil
and/or jtsx-enable-electric-open-newline-between-jsx-element-tags
to nil
.
ℹ️ Code completion is not part of jtsx
. You can get it to work by using Eglot (built-in Emacs), or any other lsp package.
jtsx-jsx-mode
and jtsx-tsx-mode
customize built-in Hideshow
package in order to support code folding into JSX parts.
Hideshow
can be enabled with M-x hs-minor-mode
command.
Please refer to Hideshow documentation for usage informations.
Function | Description |
---|---|
jtsx-jsx-mode |
Enable jtsx-jsx-mode |
jtsx-tsx-mode |
Enable jtsx-tsx-mode |
jtsx-jump-jsx-element-tag-dwim |
Jump either to the opening or to the closing tag of the JSX element. |
jtsx-jump-jsx-opening-tag |
Jump to the opening tag of the JSX element. |
jtsx-jump-jsx-closing-tag |
Jump to the closing tag of the JSX element. |
jtsx-rename-jsx-element |
Rename a JSX element at point. Point can be in the opening or closing tag. |
jtsx-move-jsx-element-tag-forward |
Move a JSX element tag (opening or closing) forward. |
jtsx-move-jsx-element-tag-backward |
Move a JSX element tag (opening or closing) backward. |
jtsx-move-jsx-element-forward |
Move a JSX element (or any JSX node) forward. |
jtsx-move-jsx-element-backward |
Move a JSX element (or any JSX node) backward. |
jtsx-move-jsx-element-step-in-forward |
Move a JSX element (or any JSX node) forward. Step into sibling elements if possible. |
jtsx-move-jsx-element-step-in-backward |
Move a JSX element (or any JSX node) backward. Step into sibling elements if possible. |
jtsx-wrap-in-jsx-element |
Wrap JSX nodes in a JSX element. Nodes are selected by a region if there is an active one. Else the node at point is used. |
jtsx-unwrap-jsx |
Unwrap JSX nodes wrapped in the node at point. |
jtsx-delete-jsx-node |
Delete a JSX node at point and its children. |
Variables | Default | Description |
---|---|---|
jtsx-switch-indent-offset |
0 |
Offset for indenting the contents of a switch block. The value must not be negative. |
jtsx-indent-statement-block-regarding-standalone-parent |
nil |
Use standalone parent as anchor to evaluate statement block indentation. If t and if the parent of a statement block is not on its own line, the statement block will be indented relative to the beginning of the whole parent continuated expression. |
jtsx-jsx-element-move-allow-step-out |
t |
Allow to step out when moving a jsx element. |
jtsx-enable-jsx-electric-closing-element |
t |
Enable electric JSX closing element feature. |
jtsx-enable-electric-open-newline-between-jsx-element-tags |
t |
Enable electric new line between jsx element tags |
jtsx-enable-all-syntax-highlighting-features |
t |
Enable all available syntax highlighting features. |
An invalid JSX
syntax in the buffer can be the source of the problem. Tree-sitter
tries to be resilient regarding syntax validity, but sometimes the parser cannot (or is not smart enough to) guess what the errored code means exactly.
Although popular web-mode
and rjsx-mode
are very good packages for JSX
edition, they have been written when Emacs built-in JSX
support was very poor or absent. Currently, Emacs comes with a quite good JSX
/ TSX
support and even gives the possibility to use Tree-sitter
integration. jtsx
takes advantage of these new features to provide a lightweight package targetting both JSX
and TSX
with some handy functionalities.