mirror of
https://github.com/zed-industries/zed.git
synced 2026-06-01 03:14:56 +07:00
Improve TS and JS symbol outline (#39797)
Added more granular symbols for ts and js in outline panel. This is a bit closer to what vscode offers. <details><summary>Screenshots of current vs new</summary> <p> New: <img width="1723" height="1221" alt="image" src="https://github.com/user-attachments/assets/796d3b59-fffa-4a66-9986-f7c75e618103" /> Current: <img width="1714" height="1347" alt="image" src="https://github.com/user-attachments/assets/f7cff463-de2a-4d86-b1a6-e19f4fd8dc6e" /> Current vscode (cursor): <img width="1710" height="1177" alt="image" src="https://github.com/user-attachments/assets/31902d52-becf-4d3f-960d-7e054e00e32d" /> </p> </details> I have never touched scheme before, and pair-programmed this with ai, so please let me know if there's any glaring issues with the implementation. I just miss the outline panel in vscode very much, and would love to see this land. Happy to help with tsx/jsx as well if this is the direction you guys were thinking of taking the outline impl. Doesn't fully close https://github.com/zed-industries/zed/issues/20964 as there is no support for chained class method callbacks or `Class.prototype.method = ...` as mentioned in https://github.com/zed-industries/zed/issues/21243, but this is a step forward. Release Notes: - Improved typescript and javascript symbol outline panel
This commit is contained in:
parent
36210e72af
commit
94c28ba14a
3 changed files with 468 additions and 43 deletions
|
|
@ -31,38 +31,103 @@
|
|||
(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
|
||||
(program
|
||||
(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
|
||||
(program
|
||||
(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
|
||||
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
|
||||
[
|
||||
|
|
@ -116,4 +181,43 @@
|
|||
)
|
||||
) @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
|
||||
|
|
|
|||
|
|
@ -1110,7 +1110,7 @@ mod tests {
|
|||
|
||||
let text = r#"
|
||||
function a() {
|
||||
// local variables are omitted
|
||||
// local variables are included
|
||||
let a1 = 1;
|
||||
// all functions are included
|
||||
async function a2() {}
|
||||
|
|
@ -1133,6 +1133,7 @@ mod tests {
|
|||
.collect::<Vec<_>>(),
|
||||
&[
|
||||
("function a()", 0),
|
||||
("let a1", 1),
|
||||
("async function a2()", 1),
|
||||
("let b", 0),
|
||||
("function getB()", 0),
|
||||
|
|
@ -1141,6 +1142,223 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
#[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#"
|
||||
// Top-level destructuring
|
||||
const { a1, a2 } = a;
|
||||
const [b1, b2] = b;
|
||||
|
||||
// Defaults and rest
|
||||
const [c1 = 1, , c2, ...rest1] = c;
|
||||
const { d1, d2: e1, f1 = 2, g1: h1 = 3, ...rest2 } = d;
|
||||
|
||||
function processData() {
|
||||
// Nested object destructuring
|
||||
const { c1, c2 } = c;
|
||||
// Nested array destructuring
|
||||
const [d1, d2, d3] = d;
|
||||
// Destructuring with renaming
|
||||
const { f1: g1 } = f;
|
||||
// With defaults
|
||||
const [x = 10, y] = xy;
|
||||
}
|
||||
|
||||
class DataHandler {
|
||||
method() {
|
||||
// Destructuring in class method
|
||||
const { a1, a2 } = a;
|
||||
const [b1, ...b2] = b;
|
||||
}
|
||||
}
|
||||
"#
|
||||
.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),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[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#"
|
||||
// Object with function properties
|
||||
const o = { m() {}, async n() {}, g: function* () {}, h: () => {}, k: function () {} };
|
||||
|
||||
// Object with primitive properties
|
||||
const p = { p1: 1, p2: "hello", p3: true };
|
||||
|
||||
// Nested objects
|
||||
const q = {
|
||||
r: {
|
||||
// won't be included due to one-level depth limit
|
||||
s: 1
|
||||
},
|
||||
t: 2
|
||||
};
|
||||
|
||||
function getData() {
|
||||
const local = { x: 1, y: 2 };
|
||||
return local;
|
||||
}
|
||||
"#
|
||||
.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),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[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#"
|
||||
// Symbols as object keys
|
||||
const sym = Symbol("test");
|
||||
const obj1 = {
|
||||
[sym]: 1,
|
||||
[Symbol("inline")]: 2,
|
||||
normalKey: 3
|
||||
};
|
||||
|
||||
// Enums as object keys
|
||||
enum Color { Red, Blue, Green }
|
||||
|
||||
const obj2 = {
|
||||
[Color.Red]: "red value",
|
||||
[Color.Blue]: "blue value",
|
||||
regularProp: "normal"
|
||||
};
|
||||
|
||||
// Mixed computed properties
|
||||
const key = "dynamic";
|
||||
const obj3 = {
|
||||
[key]: 1,
|
||||
["string" + "concat"]: 2,
|
||||
[1 + 1]: 3,
|
||||
static: 4
|
||||
};
|
||||
|
||||
// Nested objects with computed properties
|
||||
const obj4 = {
|
||||
[sym]: {
|
||||
nested: 1
|
||||
},
|
||||
regular: {
|
||||
[key]: 2
|
||||
}
|
||||
};
|
||||
"#
|
||||
.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),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_generator_function_outline(cx: &mut TestAppContext) {
|
||||
let language = crate::language("javascript", tree_sitter_typescript::LANGUAGE_TSX.into());
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue