-
-
Notifications
You must be signed in to change notification settings - Fork 1k
Description
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':
textual/src/textual/widgets/_text_area.py
Lines 665 to 673 in c9439ff
| 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!
textual/src/textual/widgets/_text_area.py
Lines 1296 to 1304 in c9439ff
| 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
-
QueryCursor.capturesactually 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. β©