languages: Add JS/TS generator functions to outline (#34388)

Brian Donovan created

Functions like `function* iterateElements() {}` would not show up in the
editor's navigation outline. With this change, they do.

| **Before** | **After**
|-|-|
|<img width="453" height="280" alt="Screenshot 2025-07-13 at 4 58 22 PM"
src="https://github.com/user-attachments/assets/822f0774-bda2-4855-a6dd-80ba82fffaf3"
/>|<img width="564" height="373" alt="Screenshot 2025-07-13 at 4 58
55 PM"
src="https://github.com/user-attachments/assets/f4f6b84f-cd26-49b7-923b-724860eb18ad"
/>|

Note that I decided to use Zed's agent assistance features to do this PR
as a sort of test run. I don't normally code with an AI assistant, but
figured it might be good in this case since I'm unfamiliar with the
codebase. I must say I was fairly impressed. All the changes in this PR
were done by Claude Sonnet 4, though I have done a manual review to
ensure the changes look sane and tested the changes by running the
re-built `zed` binary with a toy project.

Closes #21631

Release Notes:

- Fixed JS/TS outlines to show generator functions.

Change summary

crates/languages/src/javascript/outline.scm |  9 +++
crates/languages/src/tsx/outline.scm        |  9 +++
crates/languages/src/typescript.rs          | 56 +++++++++++++++++++++++
crates/languages/src/typescript/outline.scm |  9 +++
4 files changed, 83 insertions(+)

Detailed changes

crates/languages/src/javascript/outline.scm 🔗

@@ -14,6 +14,15 @@
       "(" @context
       ")" @context)) @item
 
+(generator_function_declaration
+    "async"? @context
+    "function" @context
+    "*" @context
+    name: (_) @name
+    parameters: (formal_parameters
+      "(" @context
+      ")" @context)) @item
+
 (interface_declaration
     "interface" @context
     name: (_) @name) @item

crates/languages/src/tsx/outline.scm 🔗

@@ -18,6 +18,15 @@
       "(" @context
       ")" @context)) @item
 
+(generator_function_declaration
+    "async"? @context
+    "function" @context
+    "*" @context
+    name: (_) @name
+    parameters: (formal_parameters
+      "(" @context
+      ")" @context)) @item
+
 (interface_declaration
     "interface" @context
     name: (_) @name) @item

crates/languages/src/typescript.rs 🔗

@@ -1075,6 +1075,62 @@ mod tests {
         );
     }
 
+    #[gpui::test]
+    async fn test_generator_function_outline(cx: &mut TestAppContext) {
+        let language = crate::language("javascript", tree_sitter_typescript::LANGUAGE_TSX.into());
+
+        let text = r#"
+            function normalFunction() {
+                console.log("normal");
+            }
+
+            function* simpleGenerator() {
+                yield 1;
+                yield 2;
+            }
+
+            async function* asyncGenerator() {
+                yield await Promise.resolve(1);
+            }
+
+            function* generatorWithParams(start, end) {
+                for (let i = start; i <= end; i++) {
+                    yield i;
+                }
+            }
+
+            class TestClass {
+                *methodGenerator() {
+                    yield "method";
+                }
+
+                async *asyncMethodGenerator() {
+                    yield "async method";
+                }
+            }
+        "#
+        .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).unwrap());
+        assert_eq!(
+            outline
+                .items
+                .iter()
+                .map(|item| (item.text.as_str(), item.depth))
+                .collect::<Vec<_>>(),
+            &[
+                ("function normalFunction()", 0),
+                ("function* simpleGenerator()", 0),
+                ("async function* asyncGenerator()", 0),
+                ("function* generatorWithParams( )", 0),
+                ("class TestClass", 0),
+                ("*methodGenerator()", 1),
+                ("async *asyncMethodGenerator()", 1),
+            ]
+        );
+    }
+
     #[gpui::test]
     async fn test_package_json_discovery(executor: BackgroundExecutor, cx: &mut TestAppContext) {
         cx.update(|cx| {

crates/languages/src/typescript/outline.scm 🔗

@@ -18,6 +18,15 @@
       "(" @context
       ")" @context)) @item
 
+(generator_function_declaration
+    "async"? @context
+    "function" @context
+    "*" @context
+    name: (_) @name
+    parameters: (formal_parameters
+      "(" @context
+      ")" @context)) @item
+
 (interface_declaration
     "interface" @context
     name: (_) @name) @item