Skip to content

Commit 0781883

Browse files
Fix description extraction to respect blank line separation (#16)
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
1 parent 25d3ba6 commit 0781883

File tree

4 files changed

+54
-15
lines changed

4 files changed

+54
-15
lines changed

.changeset/puny-places-change.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"urlspec-vscode-extension": patch
3+
"@urlspec/language": patch
4+
"@urlspec/builder": patch
5+
---
6+
7+
fix: seperate page description when new line inserted between

packages/language/src/cst-utils.ts

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22
* CST utilities for extracting metadata from Concrete Syntax Tree nodes
33
*/
44

5-
import type { AstNode } from "langium";
5+
import type { AstNode, LeafCstNode } from "langium";
66

77
/**
8-
* Extract description from comments preceding an AST node
8+
* Extract description from comments preceding an AST node.
9+
* Stops at a blank line between comment blocks.
910
*/
1011
export function extractDescription(node: AstNode): string | undefined {
1112
const cstNode = node.$cstNode;
@@ -16,24 +17,35 @@ export function extractDescription(node: AstNode): string | undefined {
1617
const currentIndex = children.indexOf(cstNode);
1718
const comments: string[] = [];
1819

19-
// Look at previous siblings only at the immediate level
20-
let foundNonWhitespace = false;
20+
// nextCommentLine tracks the start line of the most recently collected
21+
// comment (the one closest to the node). We use it to detect blank lines
22+
// between comment groups: if the gap between two adjacent SL_COMMENTs is
23+
// more than 1 line, there is a blank line between them and we stop.
24+
let nextCommentLine: number | undefined;
25+
2126
for (let i = currentIndex - 1; i >= 0; i--) {
22-
const sibling = children[i];
23-
if (sibling.tokenType?.name === "SL_COMMENT") {
24-
const commentText = sibling.text.replace(/^\/\/\s*/, "").trim();
27+
const sibling = children[i] as LeafCstNode | { tokenType: undefined };
28+
if (!("tokenType" in sibling) || sibling.tokenType === undefined) {
29+
// Composite node (another declaration) — stop
30+
break;
31+
}
32+
if (sibling.tokenType.name === "SL_COMMENT") {
33+
const leaf = sibling as LeafCstNode;
34+
// If there is a blank line between this comment and the next one
35+
// we already collected, stop before including this comment.
36+
if (
37+
nextCommentLine !== undefined &&
38+
nextCommentLine - leaf.range.end.line > 1
39+
) {
40+
break;
41+
}
42+
const commentText = leaf.text.replace(/^\/\/\s*/, "").trim();
2543
if (commentText) {
2644
comments.unshift(commentText);
2745
}
28-
foundNonWhitespace = true;
29-
} else if (sibling.tokenType?.name === "WS") {
30-
const newlineCount = (sibling.text.match(/\n/g) || []).length;
31-
if (newlineCount > 1 && foundNonWhitespace) {
32-
break;
33-
}
34-
} else {
35-
break;
46+
nextCommentLine = leaf.range.start.line;
3647
}
48+
// WS tokens are not present in container.content, so no else branch needed
3749
}
3850

3951
return comments.length > 0 ? comments.join("\n") : undefined;
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// =============================================
2+
// 에스크로 (Escrow)
3+
// =============================================
4+
5+
// 에스크로 결제 주문 페이지
6+
page escrowPaymentOrder = /escrow/payment {
7+
channelId: string;
8+
amount: string;
9+
}
10+
11+
page noDescription = /no-desc {
12+
channelId: string;
13+
}

packages/language/test/resolver.test.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,4 +164,11 @@ describe("URLSpec Resolver", () => {
164164
"Status of a job posting",
165165
);
166166
});
167+
168+
it("should not include section header comment separated by blank line", async () => {
169+
const doc = await parseFile(fixture("section-comment-separation.urlspec"));
170+
const spec = resolve(doc);
171+
expect(spec.pages[0]?.description).toBe("에스크로 결제 주문 페이지");
172+
expect(spec.pages[1]?.description).toBeUndefined();
173+
});
167174
});

0 commit comments

Comments
 (0)