go: Fix outline for methods with unnamed receivers (#58656)

Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

This fixes Go outline extraction for methods whose receiver has no name,
such as `func (v2) Method()`. These methods are now included in outline
symbols, which also feed breadcrumbs and sticky
scroll.

Tested with:

- `cargo test -p languages`
- `cargo test -p grammars`
- `./script/clippy -p languages`
- `cargo fmt --check --package languages`

Before:

[before_cut.webm](https://github.com/user-attachments/assets/91eb5cb0-703a-4496-b0dd-5369c4c219fc)

After:

[after_cut.webm](https://github.com/user-attachments/assets/76d13d88-3671-4118-99fc-c073a6e64727)

Release Notes:

- Fixed Go methods with unnamed receivers not appearing in breadcrumbs
and sticky scroll.

Co-authored-by: Kirill Bulatov <kirill@zed.dev>
This commit is contained in:
olegator888 2026-07-08 15:21:46 +03:00 committed by GitHub
parent 2243c13b9b
commit 35ddcb2ecd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 85 additions and 1 deletions

View file

@ -23,7 +23,7 @@
receiver: (parameter_list
"(" @context
(parameter_declaration
name: (_) @context
name: (_)? @context
type: (_) @context)
")" @context)
name: (field_identifier) @name

View file

@ -945,6 +945,7 @@ mod tests {
use gpui::{AppContext, Hsla, TestAppContext};
use task::TaskContext;
use theme::SyntaxTheme;
use unindent::Unindent as _;
fn go_language() -> Arc<Language> {
let language = language("go", tree_sitter_go::LANGUAGE.into());
@ -2017,6 +2018,89 @@ mod tests {
);
}
#[gpui::test]
fn test_go_outline_includes_methods_with_receiver_forms(cx: &mut TestAppContext) {
let language = go_language();
let source = r#"
package main
type v2 struct{}
func (v2) BrokenMethod() {
println("start")
}
func (_ v2) UnderscoreReceiverMethod() {
println("start")
}
func (v v2) NamedReceiverMethod() {
println("start")
}
func (v *v2) PointerReceiverMethod() {
println("start")
}
func WorkingFunction() {
println("start")
}
"#
.unindent();
let buffer =
cx.new(|cx| crate::Buffer::local(source.clone(), cx).with_language(language, cx));
let snapshot = buffer.read_with(cx, |buffer, _| buffer.snapshot());
let outline = snapshot.outline(None);
assert_eq!(
outline
.items
.iter()
.map(|item| item.text.as_str())
.collect::<Vec<_>>(),
&[
"type v2",
"func (v2) BrokenMethod",
"func (_ v2) UnderscoreReceiverMethod",
"func (v v2) NamedReceiverMethod",
"func (v *v2) PointerReceiverMethod",
"func WorkingFunction",
]
);
for (method_name, expected_symbol) in [
("BrokenMethod", "func (v2) BrokenMethod"),
(
"UnderscoreReceiverMethod",
"func (_ v2) UnderscoreReceiverMethod",
),
("NamedReceiverMethod", "func (v v2) NamedReceiverMethod"),
(
"PointerReceiverMethod",
"func (v *v2) PointerReceiverMethod",
),
("WorkingFunction", "func WorkingFunction"),
] {
let method_position = source
.find(&format!("{method_name}()"))
.expect("method should exist in source");
let body_position = source[method_position..]
.find("println")
.map(|body_offset| method_position + body_offset)
.expect("method should contain a body");
let symbols = snapshot.symbols_containing(body_position, None);
assert_eq!(
symbols
.last()
.map(|item| item.text.as_str())
.expect("method should have an outline symbol"),
expected_symbol
);
}
}
#[test]
fn test_extract_subtest_name() {
// Interpreted string literal