-
Notifications
You must be signed in to change notification settings - Fork 78
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support JSX/TSX #4
Comments
See the linked web-mode bug for an alternative solution approach. |
PR welcome to add support for indent |
Is it possible to use |
@yqrashawn (rjsx author here) No, the |
Separate comment -- what would it take to get JSX support in typescript mode? Would be interested to hear if anyone has given any thoughts in regards to the following features:
It looks like this mode does some sort parsing into "p-items" but not sure if that could be extended to handle JSX. I'm guessing some of the editing facilities are more likely to exist in tide than here, so happy to ignore that point for now. |
Quite recently, How it's solved in core may be relevant to how it's best solved in Thread for those interested: https://lists.gnu.org/archive/html/emacs-devel/2019-02/msg00430.html |
Emacs 27 now supports JSX in |
I'm sure However, the main obstacle to getting TSX support is that nobody who needs it is stepping up to implement it. I'm going to quote @josteink:
|
@lddubeau there are 3 (including me) Emacs users at our company who can help with the testing. I have some experience with elisp, but have never implemented a major mode. I will take a look at the code and see I can can help. |
I currently use As far as I can tell, I would consider |
@tam5 I'm using
|
typescript-mode can't collectly indent attribute of element in tsx. Cf. emacs-typescript/typescript.el#4 On the other hand, js-mode (also rjsx-mode) can't collectly indent some cases as the following. The line including "isPlaying" can't be collectly indented. type GameState = { cellsBuffer: RingBuffer<Cells>; isPlaying: boolean; } So, for a temproal solution, toggle each mode suitably.
For day to day web development, what I'd put forward is that to be competitive with VSCode, we'd need:
Beyond that, I think all the advanced language comprehension stuff we'd want is taken care of by the TS language server. I use Tide for this, but I hear good things from people using FWIW, it seems to be that the approach that |
Also: the |
General remark: I think we're all on board with having support for tsx files, but the situation has not changed since I wrote the following in an earlier comment on this issue:
And now specific reactions to @geoffp's comments:
Every... single... time... I've tried using one of those "multiple major modes on the same buffer" modes, and I've tried many times, it has never worked right. I sank tons of my time, for nothing. It is easy to come up with a proof-of-concept recipe without realizing that, as the data to be edited becomes more complex, the whole thing falls apart. Been there, done that. (I've tried multiple different modes. This being said, if changes are needed for (By the way, for those looking for something that already works for editing
Yes,
I agree with this. I've commented many a time that to get absolutely perfect handling of highlighting and indentation in |
Seconding that. We may have a chicken and egg problem: TSX-users are not using Emacs/typescript-mode because it has inadequate TSX-support, and since we don't have users with a burning passion for TSX, nobody steps up to improve the support.
I've tried a few times, but never understood how it was supposed to work. Granted I didn't try too hard, but I guess we can assume a fair amount of average users will suffer the same kind of issues we did.
As Eric S. Raymond so famously said: Show me the code. That still applies today.
I find it usable in day to day work nowadays, but that wont solve or fix TSX-support :)
Still agreed. And still: Show me the code ;)
To put it a bit simplistic: Typescript and Javascript is mostly the same language except for a dash of With Javascript being complex and getting more complex by the day, at what point do we find these duplicate major-modes to be more work than they are worth? Has anyone seriously considered looking at current |
Author of rjsx-mode here. I haven't used 27 yet, but from a couple of threads I was on, I got the sense that there was more to do on the JSX front still. From what I saw of the js-mode process, parsing JSX with regexes was a monster effort when it was Javascript alone, and even then it relied on certain conventions (multiline JSX has to be enclosed in parens, with the root tag on its own line) to simplify things. In TS, where there are generics, the situation is going to be even tougher. There were some attempts at extending js2/rjsx to support flow, but the conclusion was you'd basically have to modify most of the parsing functions to accommodate type declarations and assertions. I imagine doing that on a lot of enormous regexes will be even tougher. Ultimately I think you need a proper parser with error recovery to handle TSX well. My dream was to build an incremental parser with automatic error recovery and grammar composition (see my dormant mole repo; lezer is another cool approach). I think even with tide/lsp, it is really nice to have a proper AST to build on top of. Unfortunately, I'm just another talking head with no code to show! |
please allow me to interject: what if we wait for https://github.com/ubolonton/emacs-tree-sitter to be somewhat usable and relay on it for parsing instead? EDIT: silly me, I noticed that syntax highlight is currently available in emacs-tree-sitter emacs-tree-sitter/elisp-tree-sitter#36 |
That looks amazing. I also guess it would be possible to do an incremental rewrite of typescript-mode using that package, as opposed to the full rewrite we all fear (and which almost never work). Are there any examples of any major-modes built using that package yet? |
no so far, the author is working on the documentation for syntax highlight still. |
Emacs 27 JSX author here. Also, non-TypeScript user here. I too wondered why typescript-mode was a standalone mode, as opposed to being a derived mode of js-mode. After all, TypeScript is a superset of JavaScript, right? Wouldn’t it be awesome if you guys could inherit support for the latest language features that are happening in Emacs core?
Indeed, parsing JSX was quite difficult. But it no longer relies on conventions like wrapping it in parens. You should be able to write a JSX expression just about anywhere in a buffer and it will font-lock and indent properly now. And indeed, since the generics syntax in TypeScript looks similar to JSX, the current parsing logic in js-mode would need to be extended to determine if the angle brackets are being used in a TS context instead of a JSX context. Here were my initial thoughts on that; I suggested that js-mode could offer some hooks into its JSX parser for deriving modes—like typescript-mode, in theory—to tweak the logic. You’d bail out when discovering a generic in an array/object initialization, in an arrow/traditional function declaration/expression/call, in a class declaration/expression, or anywhere else they’re used. |
Historical reasons, mostly. I guess this was started as a fork, when js-mode was far from as feature-rich as it is today. Also, as the (somewhat dormant) maintainer of
Well now, that does look very familiar! 😄 Thanks for reaching out back then. While I've not done any work on this issue myself, I just wanted to at least try to let the developers in Emacs core to know about possible interesting extension-points, so that if someone on our end would be willing to step up to the task, they would have something to work with. Maybe I at least succeeded in that? |
Just in case this is useful for somebody, my workaround (when possible) is to not use TSX but instead: const $ = React.createElement
const _ = null
const Title = (p: { label: string, children?: ReactNode }) =>
$("div", { className : "mt-4 mb-8" },
$("span", { className : "text-gray-400" },
p.label,
$(OtherComponent, _, p.children),
) Now I find it better than JSX/TSX. Indenting and typing works like a charm. And with some font-lock love: Comparison with TSX (js-mode): |
I made Here is my configuration (use-package typescript-mode
:ensure t
:mode "\\.tsx?$")
(use-package emmet-mode :ensure t :hook typescript-mode)
(use-package tree-sitter :ensure t)
(use-package tree-sitter-langs :ensure t)
(use-package prettier-js :ensure t :hook (typescript-mode))
;; couldn't make it work with `use-package`, plain elisp instead
(require 'tree-sitter)
(require 'tree-sitter-langs)
(add-hook 'typescript-mode-hook #'tree-sitter-hl-mode) I use it with lsp integration |
@achempion That's great stuff! If you could create a PR with that information included in the README (and possibly for plain users not using |
@josteink will send it soon |
I ended up extending the above typescript-mode + tree-sitter configuration like so: First, I defined a derived mode specifically for (use-package typescript-mode
:mode (rx ".ts" string-end)
:init
(define-derived-mode typescript-tsx-mode typescript-mode "typescript-tsx")
(add-to-list 'auto-mode-alist (cons (rx ".tsx" string-end) #'typescript-tsx-mode))) then I tweaked my tree-sitter configuration to take advantage of the new mode: (use-package tree-sitter
:hook (typescript-mode . tree-sitter-hl-mode)
:config
(setf (alist-get 'typescript-tsx-mode tree-sitter-major-mode-language-alist) 'tsx)) which uses the tsx grammar to provide better syntax highlighting for JSX, in particular this situation: const SomeComponent: React.FC<{}> = () => {
return (
<div>
{"any evaluated expression of only a string literal"}
</div> // <-- without the tsx grammar, this line is highlighted as a string
);
}; |
Does my config above have this issue?
On 27 May 2021, at 09:12, Ashlynn Levenick ***@***.***> wrote:
which uses the tsx grammar to provide better syntax highlight for JSX, in particular this situation:
const SomeComponent: React.FC<{}> = () => {
return (
<div>
{"any evaluated expression of only a string literal"}
</div> // <-- without the tsx grammar, this line is highlighted as a string
);
};
Following the tree-sitter documentation theirs idea is to have major modes integrate with it natively and the provided solution with tree-sitter-langs are quite temporary.
|
So far as I'm aware, yes: you configure |
@lynlevenick I've tried your config and have this error
|
@achempion That's really strange! I can't help but say that it works for me 😅. I didn't include that I'm still using |
@lynlevenick's config didn't work for me. This is a slightly different setup that did the trick: (use-package typescript-mode
:ensure t
:init
(define-derived-mode typescript-tsx-mode typescript-mode "tsx")
:config
(setq typescript-indent-level 2)
(add-hook 'typescript-mode #'subword-mode)
(add-to-list 'auto-mode-alist '("\\.tsx?\\'" . typescript-tsx-mode)))
(use-package tree-sitter
:ensure t
:hook ((typescript-mode . tree-sitter-hl-mode)
(typescript-tsx-mode . tree-sitter-hl-mode)))
(use-package tree-sitter-langs
:ensure t
:after tree-sitter
:config
(tree-sitter-require 'tsx)
(add-to-list 'tree-sitter-major-mode-language-alist '(typescript-tsx-mode . tsx)))
|
Thanks @josemiguelo this resolved some frustrations I had doing TSX work. I'm glad to see development of treesitter in emacs which I had gotten used to in my vim setup |
@lynlevenick This worked BEAUTIFULLY for me. I'm honestly shocked, your setup creates a perfect tsx editing experience, easily on par with vscode's. All I had to do was add one line to reduce indentation from 4 spaces to 2. I can't believe how well it worked. Thank you so much for posting, even spacemacs doesn't have anything half as good as what you put together (spacemacs user for the past three years who just jumped back to his old emacs config to see if I could get a better experience for writing tsx components). |
Implement support using recipe suggested by @josemiguelo in this comment: #4 (comment)
Thanks for the suggested solution @josemiguelo! That does IMO look simple and clean enough to be included mainline, since we are outsourcing all the "hard" work to tree-sitter. I've therefore created a PR where I attempt to mainline your solution. Feedback welcome! |
can confirm that with some slight modification to @josemiguelo 's post, it can also works with an (note that i use (use-package typescript-mode
:defer t
:mode "\\.ts\\'")
(define-derived-mode typescript-tsx-mode typescript-mode "tsx")
(add-to-list 'auto-mode-alist '("\\.tsx\\'" . typescript-tsx-mode))
(use-package eglot
:defer t
:after (:all yasnippet jsonrpc flymake project xref eldoc)
:hook
(typescript-mode . eglot-ensure))
(use-package tree-sitter
:straight (:type git :host github
:repo "emacs-tree-sitter/elisp-tree-sitter"
:branch "release")
:defer t
:after tree-sitter-langs
:hook
(eglot--managed-mode . (lambda ()
(tree-sitter-mode)
(tree-sitter-hl-mode))))
(use-package tree-sitter-langs
:straight (:type git :host github
:repo "emacs-tree-sitter/tree-sitter-langs"
:branch "release")
:config
(tree-sitter-require 'tsx)
(add-to-list 'tree-sitter-major-mode-language-alist '(typescript-tsx-mode . tsx))) |
The configurations shared above don't seem to take care of indenting in my case, is that to be expected? I see First screencast shows indenting the whole buffer in Screen.Recording.2021-10-03.at.12.52.31.AM.movScreen.Recording.2021-10-03.at.12.51.10.AM.movAdditionally, the left and right chevrons receive the operator face when they are obviously part of the tag element. Is that too the expected behavior? |
I had the same issue, ended up using
|
I, too, use web-mode, but with a derived mode: (use-package web-mode
:hook ((web-mode . lsp)
(typescript-tsx-mode . lsp))
:mode (("\\.html\\'" . web-mode)
("\\.html\\.eex\\'" . web-mode)
("\\.html\\.tera\\'" . web-mode)
("\\.tsx\\'" . typescript-tsx-mode))
:init
(define-derived-mode typescript-tsx-mode typescript-mode "TypeScript-tsx")
:config
(setq web-mode-markup-indent-offset 2
web-mode-css-indent-offset 2
web-mode-code-indent-offset 2))
(use-package prettier
:hook ((typescript-tsx-mode . prettier-mode)
(typescript-mode . prettier-mode)
(js-mode . prettier-mode)
(json-mode . prettier-mode)
(css-mode . prettier-mode)
(scss-mode . prettier-mode))) This way I can enable LSP and Prettier for the derived TSX mode only, and not for web-mode in general. |
ttps://github.com/emacs-typescript/typescript.el/issues/4#issuecomment-849355222
Will need to keep an eye out for a proper solution. This is just copy-pasta from emacs-typescript/typescript.el#4 (comment)
Implement support using recipe suggested by @josemiguelo in this comment: #4 (comment)
Implement support using recipe suggested by @josemiguelo in this comment: #4 (comment)
Typescript-support has been added to Emacs core, and it also supports TSX. Closing this issue. If you still have problems, open a new issue on the GNU Emacs bug-tracker 😄 |
TypeScript added support for JSX:
microsoft/TypeScript#3203
Briefly, this adds support for HTML-like literals in code:
typescript-mode currently doesn't indent such literals properly, in contexts like this:
Currently I use web-mode (which understands JSX) for typescript files, but that doesn't work with tide (which wants to work with typescript-mode, not web-mode).
The text was updated successfully, but these errors were encountered: