From 20f7a11502abe9ca63ea04fab17eb014431b0036 Mon Sep 17 00:00:00 2001 From: hirasawayuki Date: Sat, 26 Oct 2024 22:04:51 +0900 Subject: [PATCH 1/3] Improve lsp hover definition comment formatting --- private/buf/buflsp/symbol.go | 49 +++++++++++++++++++------- private/buf/buflsp/symbol_test.go | 58 +++++++++++++++++++++++++++++++ 2 files changed, 94 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 1d73107849..a85c890b4e 100644 --- a/private/buf/buflsp/symbol.go +++ b/private/buf/buflsp/symbol.go @@ -493,24 +493,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) } @@ -523,6 +511,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..25696c8cd7 --- /dev/null +++ b/private/buf/buflsp/symbol_test.go @@ -0,0 +1,58 @@ +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 JavaDoc convention", + input: "/** This is a\n * multi-line comment\n * with multi-asterisks */", + expected: "This is a\nmulti-line comment\nwith multi-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) + } + }) + } +} From bc268c6a54e96294f882ae39b05d2909f20e4f33 Mon Sep 17 00:00:00 2001 From: hirasawayuki Date: Wed, 30 Oct 2024 23:54:23 +0900 Subject: [PATCH 2/3] rename 'formatComment' to 'commentToMarkdown' and add comment --- private/buf/buflsp/symbol.go | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/private/buf/buflsp/symbol.go b/private/buf/buflsp/symbol.go index a85c890b4e..90425584d1 100644 --- a/private/buf/buflsp/symbol.go +++ b/private/buf/buflsp/symbol.go @@ -495,7 +495,7 @@ func (s *symbol) FormatDocs(ctx context.Context) string { for i := 0; i < comments.Len(); i++ { // The compiler does not currently provide comments without their // delimited removed, so we have to do this ourselves. - comment := formatComment(comments.Index(i).RawText()) + comment := commentToMarkdown(comments.Index(i).RawText()) if comment != "" { printed = true } @@ -511,12 +511,8 @@ 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) - +// 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 @@ -525,25 +521,29 @@ func formatComment(comment string) string { 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) - } + 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.TrimLeft(line, "*") - line = strings.TrimPrefix(line, " ") + line = strings.TrimPrefix(line, "*") lines[i] = line } return strings.Join(lines, "\n") } - return comment + // 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(). From 605b66d571869ce37a18651b8edf118bdd84646a Mon Sep 17 00:00:00 2001 From: hirasawayuki Date: Wed, 30 Oct 2024 23:54:48 +0900 Subject: [PATCH 3/3] fix test case --- private/buf/buflsp/symbol_test.go | 89 +++++++++++++++++++++++-------- 1 file changed, 66 insertions(+), 23 deletions(-) diff --git a/private/buf/buflsp/symbol_test.go b/private/buf/buflsp/symbol_test.go index 25696c8cd7..c4a0ae7825 100644 --- a/private/buf/buflsp/symbol_test.go +++ b/private/buf/buflsp/symbol_test.go @@ -1,3 +1,22 @@ +// 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 ( @@ -6,7 +25,7 @@ import ( "github.com/stretchr/testify/assert" ) -func Test_formatComment(t *testing.T) { +func TestCommentToMarkdown(t *testing.T) { t.Parallel() tests := []struct { @@ -15,44 +34,68 @@ func Test_formatComment(t *testing.T) { expected string }{ { - name: "Single-line comment", - input: "// This is a single-line comment", - expected: " This is a single-line comment", + 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: "Multi-line comment", - input: "/*\n This is a\n multi-line comment\n */", - expected: "This is a\nmulti-line comment", + name: "doxygen-style-comment", + input: `/** + * Documentation comment + * with asterisks + */`, + expected: ` Documentation comment + with asterisks`, }, { - 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: "doxygen-mixed-indentation", + input: `/** + * First line + * - Second line + * - Third line + */`, + expected: ` First line + - Second line + - Third line`, }, { - name: "Multi-line comment with JavaDoc convention", - input: "/** This is a\n * multi-line comment\n * with multi-asterisks */", - expected: "This is a\nmulti-line comment\nwith multi-asterisks", + name: "markdown-emphasis", + input: "/*This is *important**/", + expected: "This is *important*", }, { - name: "Single-line multi-line comment", - input: "/* Single-line multi-line comment */", - expected: "Single-line multi-line comment", + name: "single-line-doxygen", + input: "/** Single line doc comment */", + expected: "Single line doc comment", }, { - name: "Empty comment", + name: "empty-comment", input: "/**/", expected: "", }, + { + name: "only-space", + input: "/* */", + expected: " ", + }, } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { + for _, test := range tests { + test := test + t.Run(test.name, func(t *testing.T) { t.Parallel() - result := formatComment(tt.input) - if result != tt.expected { - assert.Equal(t, tt.input, result) - } + assert.Equal(t, test.expected, commentToMarkdown(test.input)) }) } }