orzechowskid / tree-sitter-css-in-js

tree-sitter grammar for CSS-in-JS
Other
10 stars 5 forks source link

Emacs 29 Treesitter support #21

Closed stevemolitor closed 1 year ago

stevemolitor commented 1 year ago

Any idea what it would take to adapt tree-sitter-css-in-js for use with Emacs 29 built-in tree-sitter support?

I started playing around with a hacked version of the typescript-ts-mode.el that comes with Emacs 29. I got it to use css-in-js.dylib built from this repo, and using the new treesit-explore-mode I could see tree-sitter correctly parsing embedded CSS as CSS nodes, and TSX as TSX nodes.

I couldn't quite get font-lock for CSS to work though (it still highlighted as plain TypeScript strings). I didn't spend too much time on it however.

Happy to help out or spike stuff - was just wondering if you had any pointers or had played around with this.

Thanks!

orzechowskid commented 1 year ago

tsx-ts-mode is really nice, isn't it? I've spent a little time working on a CSS-in-JS minor mode compatible with it. indentation and syntax highlighting seem to work well; capf for the CSS bits is not quite there. I do seem to be causing emacs to segfault a lot though, so that's exciting :)

I have an .el file as a gist here and I'd be curious to hear your thoughts on it: https://gist.github.com/orzechowskid/15c2e45e3ce390bfa4a56f516d788d31

the plan is to polish that up and eventually release it as a minor-mode. there doesn't appear to be a seamless way to get grammar binaries onto your machine (though this article on leveraging straight.el seems really useful), so I think this minor-mode should expose a function to fetch the correct binary from this repository and write it to the correct place on disk for your OS:

(use-package 'css-in-js-mode
  :config
  (css-in-js-fetch-and-store-dylib))

I think some stuff from tree-sitter-css-in-js.el in this repo can be re-used here, and I'd love the help if you have the bandwidth!

stevemolitor commented 1 year ago

Yeah tsx-ts-mode is nice. It fixes a few niggling edge case TypeScript indentation issues with tsx-mode that I'd been meaning to fix but hadn't gotten around to. Then I tried tsx-ts-mode and it handled them perfectly!

I'd be happy to help when I can. I'm out for the next 2 weeks.

I tried your gist. It highlighted CSS correctly, but not TypeScript:

EDIT - I didn't realize css-in-js-mode was a minor mode at first. Here's what I get running tsx-ts-mode and css-in-js-mode:

I lose much of the TypeScript highlighting. Without css-in-js mode I see this:

Initial indentation in a styled section was 2 spaces to the left of where it should be - flush fully to the left. That might be an easy hack to fix.

CSS completion did work for me at least sometimes:

stevemolitor commented 1 year ago

I used a css-in-js.dylib that I built on August 23. I copied it to libtree-sitter-css-in-js.dylib in my treesit-extra-load-path. Not sure if I need to rebuild to pick up the latest changes.

orzechowskid commented 1 year ago

thanks for the quick feedback.

I lose much of the TypeScript highlighting. Without css-in-js mode I see this:

probably due to the value of treesit-font-lock-feature-list. that variable stores a list of lists, each corresponding to a different level of font-locking. I've currently set it to only the css-in-js bits.

Initial indentation in a styled section was 2 spaces to the left of where it should be - flush fully to the left.

and here I was thinking a default indent amount of css-indent-offset would be nice :) I think this should be made a configurable option.

Not sure if I need to rebuild to pick up the latest changes.

the latest change to the grammar was made in this commit from September 13, so it might be worth pulling and rebuilding.

stevemolitor commented 1 year ago

probably due to the value of treesit-font-lock-feature-list

Oh ok. I tried slapping the lists from tsx-ts-mode in their as well and got different but still incorrect results. ;) Not sure how to fiddle with that yet.

the latest change to the grammar was made in this commit from September 13, so it might be worth pulling and rebuilding.

Thanks. I rebuilt from latest and now I too can make emacs segfault. ;) Not sure if that's related or coincidental.

orzechowskid commented 1 year ago

I've added a css-in-js-mode.el on the next branch of this repo. it's the gist from earlier plus some stuff we've noticed didn't work quite right.

image

stevemolitor commented 1 year ago

I've added a css-in-js-mode.el on the next branch of this repo

Is it in the main branch now?

I tried the latest css-in-js-mode.el from the main branch but it consistently segfaults for me when I turn on css-in-js-mode. :(

I have an M1 machine and am using libtree-sitter-css-in-js.dylib that I build locally.

orzechowskid commented 1 year ago

Is it in the main branch now?

it is, yep

I tried the latest css-in-js-mode.el from the main branch but it consistently segfaults for me when I turn on css-in-js-mode. :(

oof, really? that's a rude surprise. I'm not immediately sure what would cause that to happen; I don't think the elisp code is using any features implemented in C whch it wasn't using prior to now. I was able to cross-compile an arm64 dylib inside of a Github Action; want to give that a try and see if you get different results?

https://github.com/orzechowskid/tree-sitter-css-in-js/releases/tag/latest

stevemolitor commented 1 year ago

@orzechowskid using the arm64 dylib you built did the trick! Actually I put both the macos .so and the .dylib file in my treesit-extra-load-path. It's working great now!

I haven't quite figured out the indentation. When I use newline-and-indent / C-j, the new line is flush all the way to the left. If however I use indent-for-tab-command / TAB, it works as expected. By default it indents 2 spaces, the same as my typescript-ts-mode-indent-offset. If I set css-in-js-mode-leading-indentation to 2, it indents 4 spaces. I get it now, but sorry for the confusion or any extra work there. My problem is actually with C-j. Fwiw, C-j does work as expected for me in a .css file in css-ts-mode.

This is minor and maybe outside of your control, but re css-in-js-mode-show-color-values I noticed that color names like red are colorized, but hex values like #FF0000 are not, as you can see from the screenshot above.

But anyways this is looking fantastic!

stevemolitor commented 1 year ago

Ok now I'm getting greedy ;) :

In a .css file using css-ts-mode and lsp-mode I can get CSS documentation:

Any thoughts on how do get that working in css-in-js-mode? This might be outside the scope of this project! It relates to this tsx-mode issue.

stevemolitor commented 1 year ago

Can you point me in the direction of how to extend the grammar to support other CSS-in-JS flavors? My current project is using emotion which looks like this, not supported yet by tree-sitter-in-js yet:

import { css } from '@emotion/react';

const myCss = css`
  display: flex;
  flex-direction: column;
  gap: 40px;
`;
orzechowskid commented 1 year ago

using the arm64 dylib you built did the trick!

oh, great news! saves me from having to spend $600 on hardware to test a free side-project :)

My problem is actually with C-j. Fwiw, C-j does work as expected for me in a .css file in css-ts-mode.

since this minor mode uses its own indentation rules, I'm not surprised that it works elsewhere but not here. I don't fully understand treesit-based indentation just yet but I believe it should be a matter of adding a new indentation rule to this minor mode which uses the no-node matcher to check for empty lines and apply an offset of css-indent-offset.

I noticed that color names like red are colorized, but hex values like #FF0000 are not

oh, whoops - I was only applying that font-locking function to treesit nodes of type plain_value, not color_value (a distinct node type recognized by the base CSS treesit grammar). should be fixed next time I push.

Any thoughts on how do get [css-in-js documentation] working in css-in-js-mode?

I'm pretty sure that's the responsibility of the LSP server (and possibly client) you're using. you'll need to configure your server to understand CSS-in-JS syntax, or you might be able to run multiple LSP servers and send CSS-in-JS buffers or substrings to a CSS server? not sure.

Can you point me in the direction of how to extend the grammar to support other CSS-in-JS flavors?

just FYI the CSS-in-JS grammar and the emacs support for CSS-in-JS libraries are two different things. if you need to apply this minor mode to more regions of a .js file then all you should have to do is extend css-in-js-mode--region-queries to match more stuff. if you need to update the actual grammar because you're seeing error nodes in your treesit tree then that's a little more involved.

stevemolitor commented 1 year ago

extend css-in-js-mode--region-queries to match more stuff

Thanks, this worked perfectly:

(add-to-list css-in-js-mode--region-queries
               '((call_expression ((identifier) @id) (:match "css" @id) (template_string) @ts)))

There are more variations in using emotion which I'll play with, but it should be easy enough to adjust if needed.

I did notice that styled objects with props did not work:

export const Div = styled.div<{ isRed: boolean }>`
  background-color: ${({ isRed }) => (isRed ? 'red' : 'green')};
`;

Completion worked but highlighting did not. Maybe more adjustments to css-in-js-mode--region-queries will fix? I'm not using styled in my current project so it may be a while before I exercise it fully with this mode. I will be using emotion with this mode a lot though.

This is a great mode, very flexible! Thanks for working on it!

orzechowskid commented 1 year ago

I think I'm going to close this issue as there's been no activity for a while and things do seem to be working with treesit in emacs 29. if there are any enhancement requests to this library or minor mode specifically then definitely open a new issue!