Copy outline improvements from typescript over to tsx as well (#41862)

Closes #4483 

Release Notes:

- Interprets outline of tsx files with the same grammar as typescript,
including improvements from #39797
This commit is contained in:
KyleBarton
2025-11-03 14:38:05 -08:00
committed by GitHub
parent 9ecf257502
commit 5ed458497e
2 changed files with 279 additions and 164 deletions
+124 -21
View File
@@ -34,18 +34,64 @@
(export_statement
(lexical_declaration
["let" "const"] @context
; Multiple names may be exported - @item is on the declarator to keep
; ranges distinct.
(variable_declarator
name: (_) @name) @item))
name: (identifier) @name) @item))
; Exported array destructuring
(export_statement
(lexical_declaration
["let" "const"] @context
(variable_declarator
name: (array_pattern
[
(identifier) @name @item
(assignment_pattern left: (identifier) @name @item)
(rest_pattern (identifier) @name @item)
]))))
; Exported object destructuring
(export_statement
(lexical_declaration
["let" "const"] @context
(variable_declarator
name: (object_pattern
[(shorthand_property_identifier_pattern) @name @item
(pair_pattern
value: (identifier) @name @item)
(pair_pattern
value: (assignment_pattern left: (identifier) @name @item))
(rest_pattern (identifier) @name @item)]))))
(program
(lexical_declaration
["let" "const"] @context
; Multiple names may be defined - @item is on the declarator to keep
; ranges distinct.
(variable_declarator
name: (_) @name) @item))
name: (identifier) @name) @item))
; Top-level array destructuring
(program
(lexical_declaration
["let" "const"] @context
(variable_declarator
name: (array_pattern
[
(identifier) @name @item
(assignment_pattern left: (identifier) @name @item)
(rest_pattern (identifier) @name @item)
]))))
; Top-level object destructuring
(program
(lexical_declaration
["let" "const"] @context
(variable_declarator
name: (object_pattern
[(shorthand_property_identifier_pattern) @name @item
(pair_pattern
value: (identifier) @name @item)
(pair_pattern
value: (assignment_pattern left: (identifier) @name @item))
(rest_pattern (identifier) @name @item)]))))
(class_declaration
"class" @context
@@ -56,21 +102,38 @@
"class" @context
name: (_) @name) @item
(method_definition
[
"get"
"set"
"async"
"*"
"readonly"
"static"
(override_modifier)
(accessibility_modifier)
]* @context
name: (_) @name
parameters: (formal_parameters
"(" @context
")" @context)) @item
; Method definitions in classes (not in object literals)
(class_body
(method_definition
[
"get"
"set"
"async"
"*"
"readonly"
"static"
(override_modifier)
(accessibility_modifier)
]* @context
name: (_) @name
parameters: (formal_parameters
"(" @context
")" @context)) @item)
; Object literal methods
(variable_declarator
value: (object
(method_definition
[
"get"
"set"
"async"
"*"
]* @context
name: (_) @name
parameters: (formal_parameters
"(" @context
")" @context)) @item))
(public_field_definition
[
@@ -124,4 +187,44 @@
)
) @item
; Object properties
(pair
key: [
(property_identifier) @name
(string (string_fragment) @name)
(number) @name
(computed_property_name) @name
]) @item
; Nested variables in function bodies
(statement_block
(lexical_declaration
["let" "const"] @context
(variable_declarator
name: (identifier) @name) @item))
; Nested array destructuring in functions
(statement_block
(lexical_declaration
["let" "const"] @context
(variable_declarator
name: (array_pattern
[
(identifier) @name @item
(assignment_pattern left: (identifier) @name @item)
(rest_pattern (identifier) @name @item)
]))))
; Nested object destructuring in functions
(statement_block
(lexical_declaration
["let" "const"] @context
(variable_declarator
name: (object_pattern
[(shorthand_property_identifier_pattern) @name @item
(pair_pattern value: (identifier) @name @item)
(pair_pattern value: (assignment_pattern left: (identifier) @name @item))
(rest_pattern (identifier) @name @item)]))))
(comment) @annotation
+155 -143
View File
@@ -1103,12 +1103,14 @@ mod tests {
#[gpui::test]
async fn test_outline(cx: &mut TestAppContext) {
let language = crate::language(
"typescript",
tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into(),
);
let text = r#"
for language in [
crate::language(
"typescript",
tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into(),
),
crate::language("tsx", tree_sitter_typescript::LANGUAGE_TSX.into()),
] {
let text = r#"
function a() {
// local variables are included
let a1 = 1;
@@ -1121,35 +1123,38 @@ mod tests {
// exported variables are included
export const d = e;
"#
.unindent();
.unindent();
let buffer = cx.new(|cx| language::Buffer::local(text, cx).with_language(language, cx));
let outline = buffer.read_with(cx, |buffer, _| buffer.snapshot().outline(None));
assert_eq!(
outline
.items
.iter()
.map(|item| (item.text.as_str(), item.depth))
.collect::<Vec<_>>(),
&[
("function a()", 0),
("let a1", 1),
("async function a2()", 1),
("let b", 0),
("function getB()", 0),
("const d", 0),
]
);
let buffer = cx.new(|cx| language::Buffer::local(text, cx).with_language(language, cx));
let outline = buffer.read_with(cx, |buffer, _| buffer.snapshot().outline(None));
assert_eq!(
outline
.items
.iter()
.map(|item| (item.text.as_str(), item.depth))
.collect::<Vec<_>>(),
&[
("function a()", 0),
("let a1", 1),
("async function a2()", 1),
("let b", 0),
("function getB()", 0),
("const d", 0),
]
);
}
}
#[gpui::test]
async fn test_outline_with_destructuring(cx: &mut TestAppContext) {
let language = crate::language(
"typescript",
tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into(),
);
let text = r#"
for language in [
crate::language(
"typescript",
tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into(),
),
crate::language("tsx", tree_sitter_typescript::LANGUAGE_TSX.into()),
] {
let text = r#"
// Top-level destructuring
const { a1, a2 } = a;
const [b1, b2] = b;
@@ -1177,55 +1182,58 @@ mod tests {
}
}
"#
.unindent();
.unindent();
let buffer = cx.new(|cx| language::Buffer::local(text, cx).with_language(language, cx));
let outline = buffer.read_with(cx, |buffer, _| buffer.snapshot().outline(None));
assert_eq!(
outline
.items
.iter()
.map(|item| (item.text.as_str(), item.depth))
.collect::<Vec<_>>(),
&[
("const a1", 0),
("const a2", 0),
("const b1", 0),
("const b2", 0),
("const c1", 0),
("const c2", 0),
("const rest1", 0),
("const d1", 0),
("const e1", 0),
("const h1", 0),
("const rest2", 0),
("function processData()", 0),
("const c1", 1),
("const c2", 1),
("const d1", 1),
("const d2", 1),
("const d3", 1),
("const g1", 1),
("const x", 1),
("const y", 1),
("class DataHandler", 0),
("method()", 1),
("const a1", 2),
("const a2", 2),
("const b1", 2),
("const b2", 2),
]
);
let buffer = cx.new(|cx| language::Buffer::local(text, cx).with_language(language, cx));
let outline = buffer.read_with(cx, |buffer, _| buffer.snapshot().outline(None));
assert_eq!(
outline
.items
.iter()
.map(|item| (item.text.as_str(), item.depth))
.collect::<Vec<_>>(),
&[
("const a1", 0),
("const a2", 0),
("const b1", 0),
("const b2", 0),
("const c1", 0),
("const c2", 0),
("const rest1", 0),
("const d1", 0),
("const e1", 0),
("const h1", 0),
("const rest2", 0),
("function processData()", 0),
("const c1", 1),
("const c2", 1),
("const d1", 1),
("const d2", 1),
("const d3", 1),
("const g1", 1),
("const x", 1),
("const y", 1),
("class DataHandler", 0),
("method()", 1),
("const a1", 2),
("const a2", 2),
("const b1", 2),
("const b2", 2),
]
);
}
}
#[gpui::test]
async fn test_outline_with_object_properties(cx: &mut TestAppContext) {
let language = crate::language(
"typescript",
tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into(),
);
let text = r#"
for language in [
crate::language(
"typescript",
tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into(),
),
crate::language("tsx", tree_sitter_typescript::LANGUAGE_TSX.into()),
] {
let text = r#"
// Object with function properties
const o = { m() {}, async n() {}, g: function* () {}, h: () => {}, k: function () {} };
@@ -1246,47 +1254,50 @@ mod tests {
return local;
}
"#
.unindent();
.unindent();
let buffer = cx.new(|cx| language::Buffer::local(text, cx).with_language(language, cx));
let outline = buffer.read_with(cx, |buffer, _| buffer.snapshot().outline(None));
assert_eq!(
outline
.items
.iter()
.map(|item| (item.text.as_str(), item.depth))
.collect::<Vec<_>>(),
&[
("const o", 0),
("m()", 1),
("async n()", 1),
("g", 1),
("h", 1),
("k", 1),
("const p", 0),
("p1", 1),
("p2", 1),
("p3", 1),
("const q", 0),
("r", 1),
("s", 2),
("t", 1),
("function getData()", 0),
("const local", 1),
("x", 2),
("y", 2),
]
);
let buffer = cx.new(|cx| language::Buffer::local(text, cx).with_language(language, cx));
let outline = buffer.read_with(cx, |buffer, _| buffer.snapshot().outline(None));
assert_eq!(
outline
.items
.iter()
.map(|item| (item.text.as_str(), item.depth))
.collect::<Vec<_>>(),
&[
("const o", 0),
("m()", 1),
("async n()", 1),
("g", 1),
("h", 1),
("k", 1),
("const p", 0),
("p1", 1),
("p2", 1),
("p3", 1),
("const q", 0),
("r", 1),
("s", 2),
("t", 1),
("function getData()", 0),
("const local", 1),
("x", 2),
("y", 2),
]
);
}
}
#[gpui::test]
async fn test_outline_with_computed_property_names(cx: &mut TestAppContext) {
let language = crate::language(
"typescript",
tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into(),
);
let text = r#"
for language in [
crate::language(
"typescript",
tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into(),
),
crate::language("tsx", tree_sitter_typescript::LANGUAGE_TSX.into()),
] {
let text = r#"
// Symbols as object keys
const sym = Symbol("test");
const obj1 = {
@@ -1323,40 +1334,41 @@ mod tests {
}
};
"#
.unindent();
.unindent();
let buffer = cx.new(|cx| language::Buffer::local(text, cx).with_language(language, cx));
let outline = buffer.read_with(cx, |buffer, _| buffer.snapshot().outline(None));
assert_eq!(
outline
.items
.iter()
.map(|item| (item.text.as_str(), item.depth))
.collect::<Vec<_>>(),
&[
("const sym", 0),
("const obj1", 0),
("[sym]", 1),
("[Symbol(\"inline\")]", 1),
("normalKey", 1),
("enum Color", 0),
("const obj2", 0),
("[Color.Red]", 1),
("[Color.Blue]", 1),
("regularProp", 1),
("const key", 0),
("const obj3", 0),
("[key]", 1),
("[\"string\" + \"concat\"]", 1),
("[1 + 1]", 1),
("static", 1),
("const obj4", 0),
("[sym]", 1),
("nested", 2),
("regular", 1),
("[key]", 2),
]
);
let buffer = cx.new(|cx| language::Buffer::local(text, cx).with_language(language, cx));
let outline = buffer.read_with(cx, |buffer, _| buffer.snapshot().outline(None));
assert_eq!(
outline
.items
.iter()
.map(|item| (item.text.as_str(), item.depth))
.collect::<Vec<_>>(),
&[
("const sym", 0),
("const obj1", 0),
("[sym]", 1),
("[Symbol(\"inline\")]", 1),
("normalKey", 1),
("enum Color", 0),
("const obj2", 0),
("[Color.Red]", 1),
("[Color.Blue]", 1),
("regularProp", 1),
("const key", 0),
("const obj3", 0),
("[key]", 1),
("[\"string\" + \"concat\"]", 1),
("[1 + 1]", 1),
("static", 1),
("const obj4", 0),
("[sym]", 1),
("nested", 2),
("regular", 1),
("[key]", 2),
]
);
}
}
#[gpui::test]