tree-sitter / tree-sitter-typescript

TypeScript grammar for tree-sitter
MIT License
341 stars 104 forks source link

TSX Attribute Interpolation On Newline Causes Parse Issue #155

Closed stangerjm closed 3 years ago

stangerjm commented 3 years ago

When writing a React component using TSX if you have an attribute on a newline and use interpolation it seems to cause issues with the parser. I have found that backticks without interpolation are fine, as is using interpolation on an inline attribute. The variable inside the interpolated string is not highlighted correctly as is the rest of the file.

This style is common especially if using ESLint rules that force line length limitations and consistent attribute formatting. Furthermore interpolation in an attribute like this can be correctly compiled so it should be highlighted correctly.

Let me know if you need more information about this issue happy to provide any info you like.

Attribute on newline: image

Inline attribute image

mjambon commented 3 years ago

Thanks for the report! Can you please provide a text version of the examples so we can test them easily?

stangerjm commented 3 years ago

Totally

Newline

import * as React from 'react';

const id = 'some-id';
export default function Test() {
  return <div
    className={`test${id}`}
  >
    Hello there!
  </div>;
}

Inline

import * as React from 'react';

const id = 'some-id';
export default function Test() {
  return <div className={`test${id}`}>
    Hello there!
  </div>;
}
stangerjm commented 3 years ago

Also any guides for contributing? I'd love to contribute if I have some spare time

mjambon commented 3 years ago

Also any guides for contributing?

I started something last week but didn't go very far: https://github.com/returntocorp/tree-sitter-guidelines I think it should cover:

Some problems are easier than others, and it's best to start with something easy, such as an extension of the grammar that doesn't introduce conflicts.

mjambon commented 3 years ago

Back to this issue:

Input:

import * as React from 'react';

const id = 'some-id';
export default function Test() {
  return <div
    className={`test${id}`}
  >
    Hello there!
  </div>;
}

Output:

~/tree-sitter-typescript/tsx $ ../node_modules/.bin/tree-sitter parse toto.tsx
(program [0, 0] - [10, 0]
  (import_statement [0, 0] - [0, 31]
    (import_clause [0, 7] - [0, 17]
      (namespace_import [0, 7] - [0, 17]
        (identifier [0, 12] - [0, 17])))
    source: (string [0, 23] - [0, 30]))
  (lexical_declaration [2, 0] - [2, 21]
    (variable_declarator [2, 6] - [2, 20]
      name: (identifier [2, 6] - [2, 8])
      value: (string [2, 11] - [2, 20])))
  (export_statement [3, 0] - [9, 1]
    declaration: (function_declaration [3, 15] - [9, 1]
      name: (identifier [3, 24] - [3, 28])
      parameters: (formal_parameters [3, 28] - [3, 30])
      body: (statement_block [3, 31] - [9, 1]
        (return_statement [4, 2] - [8, 9]
          (jsx_element [4, 9] - [8, 8]
            open_tag: (jsx_opening_element [4, 9] - [6, 3]
              name: (identifier [4, 10] - [4, 13])
              attribute: (jsx_attribute [5, 4] - [5, 27]
                (property_identifier [5, 4] - [5, 13])
                (jsx_expression [5, 14] - [5, 27]
                  (template_string [5, 15] - [5, 26]
                    (template_substitution [5, 20] - [5, 25]
                      (identifier [5, 22] - [5, 24]))))))
            (jsx_text [6, 3] - [8, 2])
            close_tag: (jsx_closing_element [8, 2] - [8, 8]
              name: (identifier [8, 4] - [8, 7]))))))))

The other input tsx produces the same parse tree except for positions.

You can see that the tree-sitter parser does return the details for the different pieces of the template:

                    (template_substitution [5, 20] - [5, 25]
                      (identifier [5, 22] - [5, 24]))))))

So it's up to the editor to render these with different colors, if desired.