From 51e77b71a15fdc2a2bf75f88bc93d1819aa30baf Mon Sep 17 00:00:00 2001 From: Garrett Robinson Date: Thu, 7 May 2026 21:12:06 +0000 Subject: [PATCH 1/2] Fix LSP documentSymbol marking every symbol as deprecated DocumentSymbol responses unconditionally set Tags to [SymbolTagDeprecated], so every symbol in every .proto file rendered with a strikethrough in editors that honor symbol tags (VS Code's Outline panel, breadcrumbs, sticky scroll, and Go to Symbol). Per the LSP spec, clients prefer Tags over the legacy Deprecated boolean, so the unconditional tag won even when Deprecated was false. Only include SymbolTagDeprecated in Tags when the symbol is actually deprecated. Also fix the deprecation check itself: it previously discarded the value returned by AsBool() and only checked the ok return, treating an explicit `option deprecated = false;` as deprecated. Use the boolean value directly instead. Fixes #4530 --- CHANGELOG.md | 1 + private/buf/buflsp/symbol.go | 11 +++++------ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 17df23e0b1..09b954d412 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## [Unreleased] - Add LSP completion for `buf.gen.yaml`, `buf.yaml`, and `buf.policy.yaml` files. +- Fix LSP `textDocument/documentSymbol` marking every symbol as deprecated. ## [v1.69.0] - 2026-04-29 diff --git a/private/buf/buflsp/symbol.go b/private/buf/buflsp/symbol.go index 61c9ba744e..018942ebab 100644 --- a/private/buf/buflsp/symbol.go +++ b/private/buf/buflsp/symbol.go @@ -377,9 +377,10 @@ func (s *symbol) GetSymbolInformation() protocol.SymbolInformation { default: kind = protocol.SymbolKindVariable } - var isDeprecated bool - if _, ok := s.ir.Deprecated().AsBool(); ok { - isDeprecated = true + isDeprecated, _ := s.ir.Deprecated().AsBool() + var tags []protocol.SymbolTag + if isDeprecated { + tags = []protocol.SymbolTag{protocol.SymbolTagDeprecated} } return protocol.SymbolInformation{ Name: string(name), @@ -387,9 +388,7 @@ func (s *symbol) GetSymbolInformation() protocol.SymbolInformation { Location: location, ContainerName: containerName, Deprecated: isDeprecated, - Tags: []protocol.SymbolTag{ - protocol.SymbolTagDeprecated, - }, + Tags: tags, } } From 7600461e4cae5fda1152dba6f5e3d0916289d3f9 Mon Sep 17 00:00:00 2001 From: Garrett Robinson Date: Fri, 8 May 2026 23:11:42 +0000 Subject: [PATCH 2/2] Add regression test for documentSymbol Tags Extend TestDocumentSymbol to assert on the Tags field of every returned symbol, not just the legacy Deprecated boolean. Tags must only carry SymbolTagDeprecated for symbols that are actually deprecated; the existing test passed against the buggy unconditional-tag code because it never inspected Tags. Also add an ExplicitlyNotDeprecated message with `option deprecated = false;` to the test fixture so both the unset and explicitly-false cases are pinned down. --- private/buf/buflsp/document_symbol_test.go | 11 +++++++++++ private/buf/buflsp/testdata/symbols/symbols.proto | 5 +++++ 2 files changed, 16 insertions(+) diff --git a/private/buf/buflsp/document_symbol_test.go b/private/buf/buflsp/document_symbol_test.go index 26087a0585..7fced93eeb 100644 --- a/private/buf/buflsp/document_symbol_test.go +++ b/private/buf/buflsp/document_symbol_test.go @@ -72,6 +72,8 @@ func TestDocumentSymbol(t *testing.T) { {name: "symbols.v1.CreateDocumentResponse.document", kind: protocol.SymbolKindField, line: 39}, // Document document {name: "symbols.v1.LegacyDocument", kind: protocol.SymbolKindClass, line: 42, deprecated: true}, // message LegacyDocument (deprecated) {name: "symbols.v1.LegacyDocument.id", kind: protocol.SymbolKindField, line: 44}, // string id + {name: "symbols.v1.ExplicitlyNotDeprecated", kind: protocol.SymbolKindClass, line: 47}, // message ExplicitlyNotDeprecated (option deprecated = false) + {name: "symbols.v1.ExplicitlyNotDeprecated.id", kind: protocol.SymbolKindField, line: 49}, // string id }, }, } @@ -100,6 +102,15 @@ func TestDocumentSymbol(t *testing.T) { assert.Equal(t, testURI, found.Location.URI, "symbol %s has wrong URI", expectedSymbol.name) assert.Equal(t, expectedSymbol.line, found.Location.Range.Start.Line, "symbol %s has wrong line number", expectedSymbol.name) assert.Equal(t, expectedSymbol.deprecated, found.Deprecated, "symbol %s has wrong deprecated status", expectedSymbol.name) + // Tags must only carry SymbolTagDeprecated for symbols that are + // actually deprecated. Clients prefer Tags over the legacy + // Deprecated field, so an unconditional tag would render every + // symbol with a strikethrough. + var expectedTags []protocol.SymbolTag + if expectedSymbol.deprecated { + expectedTags = []protocol.SymbolTag{protocol.SymbolTagDeprecated} + } + assert.Equal(t, expectedTags, found.Tags, "symbol %s has wrong tags", expectedSymbol.name) } }) } diff --git a/private/buf/buflsp/testdata/symbols/symbols.proto b/private/buf/buflsp/testdata/symbols/symbols.proto index 1af9a100ea..11cbde4eed 100644 --- a/private/buf/buflsp/testdata/symbols/symbols.proto +++ b/private/buf/buflsp/testdata/symbols/symbols.proto @@ -44,3 +44,8 @@ message LegacyDocument { option deprecated = true; string id = 1; } + +message ExplicitlyNotDeprecated { + option deprecated = false; + string id = 1; +}