From 6f9e052edb347d6af5fc98015657928ba4300790 Mon Sep 17 00:00:00 2001 From: Brian Donovan <1938+eventualbuddha@users.noreply.github.com> Date: Mon, 14 Jul 2025 05:26:17 -0700 Subject: [PATCH] languages: Add JS/TS generator functions to outline (#34388) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Functions like `function* iterateElements() {}` would not show up in the editor's navigation outline. With this change, they do. | **Before** | **After** |-|-| |Screenshot 2025-07-13 at 4 58 22 PM|Screenshot 2025-07-13 at 4 58
55 PM| 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. --- 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(+) diff --git a/crates/languages/src/javascript/outline.scm b/crates/languages/src/javascript/outline.scm index 99aa4bdfd5ad530505ebb90dc075e5ca405a5451..026c71e1f91d323ff2370828f330e4a4944e74db 100644 --- a/crates/languages/src/javascript/outline.scm +++ b/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 diff --git a/crates/languages/src/tsx/outline.scm b/crates/languages/src/tsx/outline.scm index df6ffa5aec8aa1b23b8179d0c341231feea5c0b5..5dafe791e493d03f6a73fa7c155ebb03072dc4d5 100644 --- a/crates/languages/src/tsx/outline.scm +++ b/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 diff --git a/crates/languages/src/typescript.rs b/crates/languages/src/typescript.rs index 3c1ecdcd5c51ee0d10a886327bac488b44621f6d..34b9c3224eecf9c86dd61cf64cb0d5e33572a810 100644 --- a/crates/languages/src/typescript.rs +++ b/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::>(), + &[ + ("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| { diff --git a/crates/languages/src/typescript/outline.scm b/crates/languages/src/typescript/outline.scm index df6ffa5aec8aa1b23b8179d0c341231feea5c0b5..5dafe791e493d03f6a73fa7c155ebb03072dc4d5 100644 --- a/crates/languages/src/typescript/outline.scm +++ b/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