DanielXMoore / Civet

A TypeScript superset that favors more types and less typing
https://civet.dev
MIT License
1.55k stars 33 forks source link

JSX attributes with unbraced indented values, code children with `>` #1381

Closed edemaine closed 2 months ago

edemaine commented 2 months ago

This PR makes two major advances toward #561, in two commits:

1. JSX attributes with unbraces indented values

561 discussed a few possibilities for a symbol to indicate that the value of an attribute should be an indented block. I realized that we don't need any symbol here; we can just have an indented block (outside coffeeJSX mode which maintains full JSX compatibility without respecting indentation). Example:

<Show
  when=
    if testing
      timer() > 10
    else
      loading()
  fallback =
    <img src="loading.gif">
    <div>Loading...</div>
>

This seems like a clear (uncontroversial) win. It matches our support for indented content in various contexts, most recently condition of an if (#1364).

2. JSX child code blocks with >

I implemented my proposal from https://github.com/DanielXMoore/Civet/issues/561#issuecomment-1806918274 to allow for a JSX code child to be prefixed with > instead of wrapping it in braces.

Examples where the content is on the same line as the > seem pretty straightforward and nice:

<div .greeting>
  Hello
  >' '
  > getUser() |> .first
  !

I also added support for indented blocks, with an indentation after the >:

<div .account>
  >
    user := getUser()
    if user?
      `Logged in as ${user.first}`
    else
      <LoginButton>

These get wrapped automatically in do, which lets you do the multiple lines of code and local declarations (which are intuitively local given the indentation). This aspect is maybe a little more controversial. It's related to https://github.com/facebook/jsx/issues/39 which we've discussed implementing but haven't: making all JSX braced blocks (probably including attribute values and code blocks) implicitly wrapped in do. It's less necessary in Civet where most statements are expressions. But it's relevant exactly when you want to do multiple lines of code.

Alternatively, I could remove the indented block feature, and we could spell this as follows:

<div .account>
  >do
    user := getUser()
    if user?
      `Logged in as ${user.first}`
    else
      <LoginButton>

On the other hand, maybe > is a nice place to introduce divergent behavior like this: a single-line expression (not wrapped in do), or a multi-line block implicitly wrapped in do. Arguably we should do the same for indented attribute blocks (Part 1 of this PR), if they want to be multiple lines as well?

So potentially a bit of discussion before this should get merged. Let me know your thoughts in particular on whether indented > blocks and/or indented attribute values should be wrapped in do. (Currently the former is, but the latter isn't.)

And to be clear, this is also meant as a step toward a flag like "civet jsxCode" where the >s aren't necessary, and instead content needs to be quoted like a string. That would complete #561.

(Also deleted an old jsx.md file which seems to have been fully incorporated into reference.md long ago.)

edemaine commented 2 months ago

Agreed — it can be a little confusing visually. At least we don't have > in the middle of a tag like I originally suggested for attributes. 😅

The other backward-compatible option is &, which would avoid this issue, but I think > is more intuitive... Maybe we could come up with a Unicode alternative that's clearer? But I guess that wouldn't be backward-compatible either.

I think we could potentially improve this issue if I finally actually delved into the syntax highlighter. It should be relatively easy to detect a > when inside JSX child mode, and highlight accordingly.