Skip to content

Commit 3312114

Browse files
authored
Line up comments due to different LSP client behaviours (#4165)
1 parent f2ab229 commit 3312114

File tree

1 file changed

+42
-19
lines changed

1 file changed

+42
-19
lines changed

private/buf/buflsp/symbol.go

Lines changed: 42 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"log/slog"
2626
"slices"
2727
"strings"
28+
"unicode"
2829

2930
"github.com/bufbuild/buf/private/bufpkg/bufmodule"
3031
"github.com/bufbuild/protocompile/experimental/ast"
@@ -351,27 +352,11 @@ func (s *symbol) getDocsFromComments() string {
351352
// traversing backwards for leading comemnts only.
352353
_, start := def.Context().Stream().Around(def.Span().Start)
353354
cursor := token.NewCursorAt(start)
354-
t := cursor.PrevSkippable()
355-
for !t.IsZero() {
356-
switch t.Kind() {
357-
case token.Comment:
358-
comments = append(comments, commentToMarkdown(t.Text()))
359-
}
360-
prev := cursor.PeekPrevSkippable()
361-
if !prev.Kind().IsSkippable() {
362-
break
363-
}
364-
if prev.Kind() == token.Space {
365-
// Check if the whitespace contains a newline. If so, then we break. This is to prevent
366-
// picking up comments that are not contiguous to the declaration.
367-
if strings.Contains(prev.Text(), "\n") {
368-
break
369-
}
370-
}
371-
t = cursor.PrevSkippable()
355+
for t := cursor.PrevSkippable(); t.Kind() == token.Comment; t = cursor.PrevSkippable() {
356+
comments = append(comments, commentToMarkdown(t.Text()))
372357
}
373358
// Reverse the list and return joined.
374-
slices.Reverse(comments)
359+
slices.Reverse(lineUpComments(comments))
375360

376361
var docs strings.Builder
377362
for _, comment := range comments {
@@ -450,6 +435,44 @@ func commentToMarkdown(comment string) string {
450435
return strings.TrimSuffix(strings.TrimPrefix(comment, "/*"), "*/")
451436
}
452437

438+
// lineUpComments is a helper function for lining up the comments for docs, since some users
439+
// may start their comments with spaces, e.g.
440+
//
441+
// // Foo is a ...
442+
// //
443+
// // Foo is used for ...
444+
// message Foo { ... }
445+
//
446+
// vs.
447+
//
448+
// //Foo is a ...
449+
// //
450+
// //Foo is used for ...
451+
// message Foo { ... }
452+
//
453+
// When different LSP clients render these docs, they treat leading spaces differently. To
454+
// help mitigate this, we use the following heuristic to line up the comments:
455+
//
456+
// If all lines containing one or more non-space characters all start with a single space
457+
// character, we trim the space character for each of these lines. Otherwise, do nothing.
458+
func lineUpComments(comments []string) []string {
459+
linedUp := make([]string, len(comments))
460+
for i, comment := range comments {
461+
if strings.ContainsFunc(comment, func(r rune) bool {
462+
return !unicode.IsSpace(r)
463+
}) {
464+
// We are only checking for " " and do not count nbsp's.
465+
if !strings.HasPrefix(comment, " ") {
466+
return comments
467+
}
468+
linedUp[i] = strings.TrimPrefix(comment, " ")
469+
} else {
470+
linedUp[i] = comment
471+
}
472+
}
473+
return linedUp
474+
}
475+
453476
// irMemberDoc returns the documentation for a message field, enum value or extension field.
454477
func irMemberDoc(irMember ir.Member) string {
455478
number := irMember.Number()

0 commit comments

Comments
 (0)