From f2b201926879cebacb8c6df99b439acba58fa0e0 Mon Sep 17 00:00:00 2001 From: hirasawayuki Date: Sun, 20 Oct 2024 11:54:44 +0900 Subject: [PATCH] feat: add comment formatter and unit tests --- private/buf/buflsp/symbol.go | 49 +++++++++++++++++------- private/buf/buflsp/symbol_test.go | 63 +++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+), 13 deletions(-) create mode 100644 private/buf/buflsp/symbol_test.go diff --git a/private/buf/buflsp/symbol.go b/private/buf/buflsp/symbol.go index 8ea0da97f1..53fd4421de 100644 --- a/private/buf/buflsp/symbol.go +++ b/private/buf/buflsp/symbol.go @@ -498,24 +498,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 := formatComment(comments.Index(i).RawText()) if comment != "" { printed = true } - // No need to process Markdown in comment; this Just Works! fmt.Fprintln(&tooltip, comment) } @@ -528,6 +516,41 @@ func (s *symbol) FormatDocs(ctx context.Context) string { return tooltip.String() } +// formatComment takes a raw comment string and formats it by removing comment +// delimiters and unnecessary whitespace. It handles both single-line (//) and +// multi-line (/* */) comments. +func formatComment(comment string) string { + comment = strings.TrimSpace(comment) + + 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.HasSuffix(comment, "*/") { + comment = strings.Trim(comment, "/*") + // single-line + if !strings.Contains(comment, "\n") { + return strings.TrimSpace(comment) + } + + lines := strings.Split(strings.TrimSpace(comment), "\n") + for i, line := range lines { + line = strings.TrimSpace(line) + line = strings.TrimLeft(line, "*") + line = strings.TrimPrefix(line, " ") + lines[i] = line + } + + return strings.Join(lines, "\n") + } + + return 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..dbfe2a74a7 --- /dev/null +++ b/private/buf/buflsp/symbol_test.go @@ -0,0 +1,63 @@ +package buflsp + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_formatComment(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: "/*\n * This is a\n * multi-line comment\n */", + expected: "This is a\nmulti-line comment", + }, + { + name: "Multi-line comment with mixed indentation", + input: "/*\n * First line\n * - Second line\n * - Third line\n */", + expected: "First line\n- Second line\n - Third line", + }, + { + name: "Multi-line comment with multi-asterisks", + input: "/*** This is a\n *** multi-line comment\n *** with multi-asterisks ***/", + expected: "This is a\nmulti-line comment\nwith multi-asterisks", + }, + { + name: "Multi-line comment without asterisks", + input: "/* This is a\n multi-line comment\n without asterisks */", + expected: "This is a\nmulti-line comment\nwithout asterisks", + }, + { + name: "Single-line multi-line comment", + input: "/* Single-line multi-line comment */", + expected: "Single-line multi-line comment", + }, + { + name: "Empty comment", + input: "/**/", + expected: "", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + result := formatComment(tt.input) + if result != tt.expected { + assert.Equal(t, tt.input, result) + } + }) + } +}