diff --git a/private/buf/buflsp/symbol.go b/private/buf/buflsp/symbol.go index f9c07f577b..704aac075d 100644 --- a/private/buf/buflsp/symbol.go +++ b/private/buf/buflsp/symbol.go @@ -566,24 +566,12 @@ func (s *symbol) FormatDocs(ctx context.Context) string { var printed bool for _, comments := range allComments { for i := 0; i < comments.Len(); i++ { - comment := comments.Index(i).RawText() - // The compiler does not currently provide comments without their // delimited removed, so we have to do this ourselves. - if strings.HasPrefix(comment, "//") { - // NOTE: We do not trim the space here, because indentation is - // significant for Markdown code fences, and if every line - // starts with a space, Markdown will trim it for us, even off - // of code blocks. - comment = strings.TrimPrefix(comment, "//") - } else { - comment = strings.TrimSuffix(strings.TrimPrefix(comment, "/*"), "*/") - } - + comment := commentToMarkdown(comments.Index(i).RawText()) if comment != "" { printed = true } - // No need to process Markdown in comment; this Just Works! fmt.Fprintln(&tooltip, comment) } @@ -596,6 +584,41 @@ func (s *symbol) FormatDocs(ctx context.Context) string { return tooltip.String() } +// commentToMarkdown processes comment strings and formats them for markdown display. +func commentToMarkdown(comment string) string { + if strings.HasPrefix(comment, "//") { + // NOTE: We do not trim the space here, because indentation is + // significant for Markdown code fences, and if every line + // starts with a space, Markdown will trim it for us, even off + // of code blocks. + return strings.TrimPrefix(comment, "//") + } + + if strings.HasPrefix(comment, "/**") && !strings.HasPrefix(comment, "/**/") { + // NOTE: Doxygen-style comments (/** ... */) to Markdown format + // by removing comment delimiters and formatting the content. + // + // Example: + // /** + // * This is a Doxygen comment + // * with multiple lines + // */ + comment = strings.TrimSuffix(strings.TrimPrefix(comment, "/**"), "*/") + + lines := strings.Split(strings.TrimSpace(comment), "\n") + for i, line := range lines { + line = strings.TrimSpace(line) + line = strings.TrimPrefix(line, "*") + lines[i] = line + } + + return strings.Join(lines, "\n") + } + + // Handle standard multi-line comments (/* ... */) + return strings.TrimSuffix(strings.TrimPrefix(comment, "/*"), "*/") +} + // symbolWalker is an AST walker that generates the symbol table for a file in IndexSymbols(). type symbolWalker struct { file *file diff --git a/private/buf/buflsp/symbol_test.go b/private/buf/buflsp/symbol_test.go new file mode 100644 index 0000000000..c4a0ae7825 --- /dev/null +++ b/private/buf/buflsp/symbol_test.go @@ -0,0 +1,101 @@ +// Copyright 2020-2024 Buf Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file defines all of the message handlers that involve symbols. +// +// In particular, this file handles semantic information in fileManager that have been +// *opened by the editor*, and thus do not need references to Buf modules to find. +// See imports.go for that part of the LSP. +package buflsp + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestCommentToMarkdown(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + input string + expected string + }{ + { + name: "single-line-comment", + input: "// this is a single-line comment", + expected: " this is a single-line comment", + }, + { + name: "multi-line-comment", + input: `/* + this is a + multi-line comment +*/`, + expected: ` + this is a + multi-line comment +`, + }, + { + name: "doxygen-style-comment", + input: `/** + * Documentation comment + * with asterisks + */`, + expected: ` Documentation comment + with asterisks`, + }, + { + name: "doxygen-mixed-indentation", + input: `/** + * First line + * - Second line + * - Third line + */`, + expected: ` First line + - Second line + - Third line`, + }, + { + name: "markdown-emphasis", + input: "/*This is *important**/", + expected: "This is *important*", + }, + { + name: "single-line-doxygen", + input: "/** Single line doc comment */", + expected: "Single line doc comment", + }, + { + name: "empty-comment", + input: "/**/", + expected: "", + }, + { + name: "only-space", + input: "/* */", + expected: " ", + }, + } + + for _, test := range tests { + test := test + t.Run(test.name, func(t *testing.T) { + t.Parallel() + assert.Equal(t, test.expected, commentToMarkdown(test.input)) + }) + } +}