Skip to content

Commit c24cb32

Browse files
committed
fix: Multi-Line as one comment --story=1
1 parent 9a8e121 commit c24cb32

File tree

5 files changed

+203
-20
lines changed

5 files changed

+203
-20
lines changed

adapters/golang/parser.go

Lines changed: 100 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -81,27 +81,105 @@ func (a *Adapter) parseWithScopeTracking(fset *token.FileSet, f *ast.File, fileP
8181

8282
// Now iterate in order
8383
for _, cg := range f.Comments {
84-
for _, c := range cg.List {
85-
symbol := commentSymbols[c]
86-
if symbol == "" {
87-
symbol = "package." + f.Name.Name // Default to package level
88-
}
89-
comments = append(comments, a.createComment(fset, c, filePath, symbol))
84+
if len(cg.List) == 0 {
85+
continue
86+
}
87+
88+
// Use the symbol of the first comment in the group
89+
symbol := commentSymbols[cg.List[0]]
90+
if symbol == "" {
91+
symbol = "package." + f.Name.Name // Default to package level
9092
}
93+
94+
// Split group into subgroups and create comments
95+
subComments := a.processCommentGroup(fset, cg, filePath, symbol)
96+
comments = append(comments, subComments...)
9197
}
9298

9399
return comments, nil
94100
}
95101

96-
func (a *Adapter) createComment(fset *token.FileSet, c *ast.Comment, file, symbol string) *domain.Comment {
102+
// processCommentGroup handles the logic of merging or splitting comments within a group.
103+
// It ensures that only consecutive Line comments (//) are merged.
104+
// Block comments (/* */) are kept separate, even if consecutive or adjacent to line comments.
105+
func (a *Adapter) processCommentGroup(fset *token.FileSet, cg *ast.CommentGroup, file, symbol string) []*domain.Comment {
106+
var results []*domain.Comment
107+
if len(cg.List) == 0 {
108+
return results
109+
}
110+
111+
// Buffer for consecutive line comments
112+
var lineBuffer []*ast.Comment
113+
114+
flushLineBuffer := func() {
115+
if len(lineBuffer) == 0 {
116+
return
117+
}
118+
results = append(results, a.createMergedComment(fset, lineBuffer, file, symbol))
119+
lineBuffer = nil // Clear buffer
120+
}
121+
122+
for _, c := range cg.List {
123+
if strings.HasPrefix(c.Text, "//") {
124+
// It's a line comment, add to buffer
125+
lineBuffer = append(lineBuffer, c)
126+
} else {
127+
// It's a block comment (/* ... */)
128+
// 1. Flush any existing line comments
129+
flushLineBuffer()
130+
131+
// 2. Add this block comment individually
132+
results = append(results, a.createSingleComment(fset, c, file, symbol))
133+
}
134+
}
135+
// Flush remaining line comments at the end
136+
flushLineBuffer()
137+
138+
return results
139+
}
140+
141+
// createMergedComment creates a single comment from a list of Line comments
142+
func (a *Adapter) createMergedComment(fset *token.FileSet, comments []*ast.Comment, file, symbol string) *domain.Comment {
143+
if len(comments) == 0 {
144+
return nil
145+
}
146+
147+
first := comments[0]
148+
last := comments[len(comments)-1]
149+
150+
pos := fset.Position(first.Pos())
151+
end := fset.Position(last.End())
152+
153+
var sb strings.Builder
154+
for i, c := range comments {
155+
if i > 0 {
156+
sb.WriteString("\n")
157+
}
158+
sb.WriteString(c.Text)
159+
}
160+
161+
return &domain.Comment{
162+
File: file,
163+
Language: "go",
164+
Symbol: symbol,
165+
Range: domain.TextRange{
166+
StartLine: pos.Line,
167+
StartCol: pos.Column,
168+
EndLine: end.Line,
169+
EndCol: end.Column,
170+
},
171+
SourceText: sb.String(),
172+
Type: domain.CommentTypeLine,
173+
}
174+
}
175+
176+
// createSingleComment creates a comment from a single AST comment (usually Block)
177+
func (a *Adapter) createSingleComment(fset *token.FileSet, c *ast.Comment, file, symbol string) *domain.Comment {
97178
pos := fset.Position(c.Pos())
98179
end := fset.Position(c.End())
99180

100-
// Clean the text: remove // or /* */
101-
text := c.Text
102-
// Determine type
103181
cType := domain.CommentTypeLine
104-
if strings.HasPrefix(text, "/*") {
182+
if strings.HasPrefix(c.Text, "/*") {
105183
cType = domain.CommentTypeBlock
106184
}
107185

@@ -115,7 +193,17 @@ func (a *Adapter) createComment(fset *token.FileSet, c *ast.Comment, file, symbo
115193
EndLine: end.Line,
116194
EndCol: end.Column,
117195
},
118-
SourceText: text,
196+
SourceText: c.Text,
119197
Type: cType,
120198
}
121199
}
200+
201+
// createCommentFromGroup is deprecated
202+
func (a *Adapter) createCommentFromGroup(fset *token.FileSet, cg *ast.CommentGroup, file, symbol string) *domain.Comment {
203+
return nil
204+
}
205+
206+
// createComment is deprecated
207+
func (a *Adapter) createComment(fset *token.FileSet, c *ast.Comment, file, symbol string) *domain.Comment {
208+
return nil
209+
}

adapters/rust/extractor.go

Lines changed: 75 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,25 +40,94 @@ func (a *RustAdapter) extractComments(root *sitter.Node, src []byte, file string
4040
owner := FindOwnerNode(node, src)
4141
symbolPath := ResolveSymbolPath(owner, src)
4242

43+
// Calculate Range (Trimming trailing newline if present for Line/Doc comments)
44+
startRow := int(node.StartPoint().Row) + 1
45+
startCol := int(node.StartPoint().Column) + 1
46+
endRow := int(node.EndPoint().Row) + 1
47+
endCol := int(node.EndPoint().Column) + 1
48+
49+
// Fix for Tree-sitter including newline in Line/Doc comments
50+
// If it's a line/doc comment and includes newline (EndCol == 1 && EndRow > StartRow)
51+
// We want to snap it back to the same line.
52+
cType := getDomainCommentType(content)
53+
if (cType == domain.CommentTypeLine || cType == domain.CommentTypeDoc) &&
54+
strings.HasSuffix(content, "\n") {
55+
// Trim the newline from content
56+
content = strings.TrimSuffix(content, "\n")
57+
// Recalculate EndRow/EndCol based on trimmed content
58+
// Since it's a single line comment, EndRow is StartRow
59+
endRow = startRow
60+
// EndCol is StartCol + length (in bytes)
61+
endCol = startCol + len(content)
62+
}
63+
4364
comment := &domain.Comment{
4465
SourceText: content,
4566
Symbol: symbolPath,
4667
File: file,
4768
Language: "rust",
4869
Range: domain.TextRange{
49-
StartLine: int(node.StartPoint().Row) + 1,
50-
StartCol: int(node.StartPoint().Column) + 1,
51-
EndLine: int(node.EndPoint().Row) + 1,
52-
EndCol: int(node.EndPoint().Column) + 1,
70+
StartLine: startRow,
71+
StartCol: startCol,
72+
EndLine: endRow,
73+
EndCol: endCol,
5374
},
54-
Type: getDomainCommentType(content),
75+
Type: cType,
5576
}
5677
comment.ID = utils.GenerateCommentID(comment)
5778
comments = append(comments, comment)
5879
}
5980
}
6081

61-
return comments, nil
82+
return mergeComments(comments), nil
83+
}
84+
85+
// mergeComments merges consecutive comments of the same type and symbol.
86+
func mergeComments(comments []*domain.Comment) []*domain.Comment {
87+
if len(comments) == 0 {
88+
return comments
89+
}
90+
91+
var merged []*domain.Comment
92+
for _, current := range comments {
93+
if len(merged) == 0 {
94+
merged = append(merged, current)
95+
continue
96+
}
97+
98+
last := merged[len(merged)-1]
99+
100+
// Condition to merge:
101+
// 1. Same Type (Doc or Line)
102+
// 2. Same Symbol
103+
// 3. Consecutive lines:
104+
// - Since we fixed range to exclude newline, EndLine of last + 1 == StartLine of current
105+
isConsecutive := last.Range.EndLine+1 == current.Range.StartLine
106+
107+
shouldMerge := (last.Type == domain.CommentTypeDoc || last.Type == domain.CommentTypeLine) &&
108+
last.Type == current.Type &&
109+
last.Symbol == current.Symbol &&
110+
isConsecutive
111+
112+
if shouldMerge {
113+
// Append content
114+
// Ensure we have a newline between them if not present in the last one
115+
if !strings.HasSuffix(last.SourceText, "\n") {
116+
last.SourceText += "\n"
117+
}
118+
last.SourceText += current.SourceText
119+
120+
// Update range
121+
last.Range.EndLine = current.Range.EndLine
122+
last.Range.EndCol = current.Range.EndCol
123+
124+
// Regenerate ID because content changed
125+
last.ID = utils.GenerateCommentID(last)
126+
} else {
127+
merged = append(merged, current)
128+
}
129+
}
130+
return merged
62131
}
63132

64133
// getDomainCommentType maps raw comment content to domain.CommentType

tests/complex_parsing_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ func TestComplexGoParsing(t *testing.T) {
8080
assert.True(t, foundBlock, "Should find Block Comment")
8181

8282
// 4. Verify commented out code
83-
// Usually parsed as a line comment "commented_out_code();"
83+
// Should be strictly separated from preceding block comment
8484
_, hasCode := found["// commented_out_code();"]
85-
assert.True(t, hasCode, "Should parse commented out code as comment")
85+
assert.True(t, hasCode, "Should find commented out code as separate comment")
8686
}

tests/rust_integration_test.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,15 @@ func TestRustSupport(t *testing.T) {
3838

3939
assert.True(t, strings.HasSuffix(result.File, "lib.rs"))
4040
assert.NotEmpty(t, result.Comments)
41+
42+
// Expect 6 comments after merging consecutive lines:
43+
// 1. Merged inner doc (lines 1-2)
44+
// 2. Inner doc (line 4)
45+
// 3. Outer doc (line 6)
46+
// 4. Line comment (line 8)
47+
// 5. Block comment (lines 9-10)
48+
// 6. Merged line comment (lines 11-12)
49+
assert.Equal(t, 6, len(result.Comments), "Should have exactly 6 comments after merging")
4150

4251
// Verify Language and Types
4352
foundInnerDoc := false
@@ -71,4 +80,16 @@ func TestRustSupport(t *testing.T) {
7180

7281
assert.True(t, foundInnerDoc, "Should find inner doc comment //!")
7382
assert.True(t, foundOuterDoc, "Should find outer doc comment ///")
83+
84+
// Verify range logic for merged comments (lines 1-2)
85+
// Should be: StartLine 1, EndLine 2 (was 3 before fix)
86+
if len(result.Comments) > 0 {
87+
firstComment := result.Comments[0]
88+
rng := firstComment["range"].(map[string]interface{})
89+
startLine := int(rng["startLine"].(float64))
90+
endLine := int(rng["endLine"].(float64))
91+
92+
assert.Equal(t, 1, startLine, "First comment should start on line 1")
93+
assert.Equal(t, 2, endLine, "First comment (lines 1-2 merged) should end on line 2, not 3")
94+
}
7495
}

tests/testdata/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
1+
//! DAO Fork related constants from [EIP-779](https://eips.ethereum.org/EIPS/eip-779).
2+
//! It happened on Ethereum block 1_920_000
3+
14
//! Inner doc comment for module
25
36
/// Outer doc comment for function
47
fn main() {
58
// Line comment
69
/* Block
710
comment */
11+
// good
12+
// luck
813
}

0 commit comments

Comments
 (0)