Skip to content

Commit 133b9c4

Browse files
authored
Improve lsp hover definition comment formatting (#3411)
1 parent 0560ffe commit 133b9c4

File tree

2 files changed

+137
-13
lines changed

2 files changed

+137
-13
lines changed

private/buf/buflsp/symbol.go

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -566,24 +566,12 @@ func (s *symbol) FormatDocs(ctx context.Context) string {
566566
var printed bool
567567
for _, comments := range allComments {
568568
for i := 0; i < comments.Len(); i++ {
569-
comment := comments.Index(i).RawText()
570-
571569
// The compiler does not currently provide comments without their
572570
// delimited removed, so we have to do this ourselves.
573-
if strings.HasPrefix(comment, "//") {
574-
// NOTE: We do not trim the space here, because indentation is
575-
// significant for Markdown code fences, and if every line
576-
// starts with a space, Markdown will trim it for us, even off
577-
// of code blocks.
578-
comment = strings.TrimPrefix(comment, "//")
579-
} else {
580-
comment = strings.TrimSuffix(strings.TrimPrefix(comment, "/*"), "*/")
581-
}
582-
571+
comment := commentToMarkdown(comments.Index(i).RawText())
583572
if comment != "" {
584573
printed = true
585574
}
586-
587575
// No need to process Markdown in comment; this Just Works!
588576
fmt.Fprintln(&tooltip, comment)
589577
}
@@ -596,6 +584,41 @@ func (s *symbol) FormatDocs(ctx context.Context) string {
596584
return tooltip.String()
597585
}
598586

587+
// commentToMarkdown processes comment strings and formats them for markdown display.
588+
func commentToMarkdown(comment string) string {
589+
if strings.HasPrefix(comment, "//") {
590+
// NOTE: We do not trim the space here, because indentation is
591+
// significant for Markdown code fences, and if every line
592+
// starts with a space, Markdown will trim it for us, even off
593+
// of code blocks.
594+
return strings.TrimPrefix(comment, "//")
595+
}
596+
597+
if strings.HasPrefix(comment, "/**") && !strings.HasPrefix(comment, "/**/") {
598+
// NOTE: Doxygen-style comments (/** ... */) to Markdown format
599+
// by removing comment delimiters and formatting the content.
600+
//
601+
// Example:
602+
// /**
603+
// * This is a Doxygen comment
604+
// * with multiple lines
605+
// */
606+
comment = strings.TrimSuffix(strings.TrimPrefix(comment, "/**"), "*/")
607+
608+
lines := strings.Split(strings.TrimSpace(comment), "\n")
609+
for i, line := range lines {
610+
line = strings.TrimSpace(line)
611+
line = strings.TrimPrefix(line, "*")
612+
lines[i] = line
613+
}
614+
615+
return strings.Join(lines, "\n")
616+
}
617+
618+
// Handle standard multi-line comments (/* ... */)
619+
return strings.TrimSuffix(strings.TrimPrefix(comment, "/*"), "*/")
620+
}
621+
599622
// symbolWalker is an AST walker that generates the symbol table for a file in IndexSymbols().
600623
type symbolWalker struct {
601624
file *file

private/buf/buflsp/symbol_test.go

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
// Copyright 2020-2024 Buf Technologies, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
// This file defines all of the message handlers that involve symbols.
16+
//
17+
// In particular, this file handles semantic information in fileManager that have been
18+
// *opened by the editor*, and thus do not need references to Buf modules to find.
19+
// See imports.go for that part of the LSP.
20+
package buflsp
21+
22+
import (
23+
"testing"
24+
25+
"github.com/stretchr/testify/assert"
26+
)
27+
28+
func TestCommentToMarkdown(t *testing.T) {
29+
t.Parallel()
30+
31+
tests := []struct {
32+
name string
33+
input string
34+
expected string
35+
}{
36+
{
37+
name: "single-line-comment",
38+
input: "// this is a single-line comment",
39+
expected: " this is a single-line comment",
40+
},
41+
{
42+
name: "multi-line-comment",
43+
input: `/*
44+
this is a
45+
multi-line comment
46+
*/`,
47+
expected: `
48+
this is a
49+
multi-line comment
50+
`,
51+
},
52+
{
53+
name: "doxygen-style-comment",
54+
input: `/**
55+
* Documentation comment
56+
* with asterisks
57+
*/`,
58+
expected: ` Documentation comment
59+
with asterisks`,
60+
},
61+
{
62+
name: "doxygen-mixed-indentation",
63+
input: `/**
64+
* First line
65+
* - Second line
66+
* - Third line
67+
*/`,
68+
expected: ` First line
69+
- Second line
70+
- Third line`,
71+
},
72+
{
73+
name: "markdown-emphasis",
74+
input: "/*This is *important**/",
75+
expected: "This is *important*",
76+
},
77+
{
78+
name: "single-line-doxygen",
79+
input: "/** Single line doc comment */",
80+
expected: "Single line doc comment",
81+
},
82+
{
83+
name: "empty-comment",
84+
input: "/**/",
85+
expected: "",
86+
},
87+
{
88+
name: "only-space",
89+
input: "/* */",
90+
expected: " ",
91+
},
92+
}
93+
94+
for _, test := range tests {
95+
test := test
96+
t.Run(test.name, func(t *testing.T) {
97+
t.Parallel()
98+
assert.Equal(t, test.expected, commentToMarkdown(test.input))
99+
})
100+
}
101+
}

0 commit comments

Comments
 (0)