Skip to content

Syntax highlighting issue in TextAreaΒ #6011

@TomJGooding

Description

@TomJGooding

Unfortunately I found a few problems with the TextArea syntax highlighting while working on #5977!

After fixing an issue in the highlghts .scm files, I was struggling to understand a change in the snapshot tests where Python's 'range' function had different highlighting.

This turned out to be an existing issue with the syntax highlighting. I've copied my investigations from that PR below.


Consider this Python code:

print("Hello world")
x = range(10)

The tree-sitter query captures (on this branch) look something like this.1 Notice that function.call is before type.builtin, since 'print' was matched first.

[('variable', ['print', 'range', 'x']),
 ('function.call', ['print', 'range']),
 ('function.builtin', ['print', 'range']),
 ('punctuation.bracket', ['(', '(', ')', ')']),
 ('string', ['"Hello world"']),
 ('operator', ['=']),
 ('type.builtin', ['range']),
 ('number', ['10'])]

After removing the print function:

x = range(10)

Notice how the order of the captures has changed, where now function.call is after type.builtin

[('variable', ['x', 'range']),
 ('operator', ['=']),
 ('type.builtin', ['range']),
 ('function.call', ['range']),
 ('function.builtin', ['range']),
 ('punctuation.bracket', ['(', ')']),
 ('number', ['10'])]

The TextArea loops over the captures to build the 'highlight map':

captures = self.document.query_syntax_tree(self._highlight_query)
for highlight_name, nodes in captures.items():
for node in nodes:
node_start_row, node_start_column = node.start_point
node_end_row, node_end_column = node.end_point
if node_start_row == node_end_row:
highlight = (node_start_column, node_end_column, highlight_name)
highlights[node_start_row].append(highlight)

When the TextArea lines are rendered, it loops over the line highlights to apply the syntax highlighting. This explains why 'range' will be highlighted differently depending on whether there's another function call before it!

line_highlights = highlights[line_index]
for highlight_start, highlight_end, highlight_name in line_highlights:
node_style = get_highlight_from_theme(highlight_name)
if node_style is not None:
line.stylize(
node_style,
byte_to_codepoint.get(highlight_start, 0),
byte_to_codepoint.get(highlight_end) if highlight_end else None,
)

Footnotes

  1. QueryCursor.captures actually returns "A dict where the keys are the names of the captures and the values are lists of the captured nodes". I've formatted the captures to make them simpler to understand. ↩

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions