From c577ad29e95516ef3bf9420a03d02e8d1a5543fc Mon Sep 17 00:00:00 2001 From: Kishan Patel Date: Mon, 15 Sep 2025 15:49:39 -0400 Subject: [PATCH 1/5] feat(LanguageServiceProvider): added code comment collapsing Signed-off-by: Kishan Patel --- server/src/sas/LanguageServiceProvider.ts | 58 +++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/server/src/sas/LanguageServiceProvider.ts b/server/src/sas/LanguageServiceProvider.ts index b75d937c8..96cf0a754 100644 --- a/server/src/sas/LanguageServiceProvider.ts +++ b/server/src/sas/LanguageServiceProvider.ts @@ -188,6 +188,7 @@ export class LanguageServiceProvider { getFoldingRanges(): FoldingRange[] { const lineCount = this.model.getLineCount(); const result: FoldingRange[] = []; + this.addCommentFolding(result); for (let i = 0; i < lineCount; i++) { const rootBlock = this.syntaxProvider.getFoldingBlock( @@ -210,9 +211,66 @@ export class LanguageServiceProvider { continue; } } + return result; } + private addCommentFolding(result: FoldingRange[]) { + const lineCount = this.model.getLineCount(); + + // Method 1: Try token blocks (works for comments inside code blocks) + if (this.syntaxProvider.lexer.tknBlks) { + const tokenBlocks = this.syntaxProvider.lexer.tknBlks; + for (let i = 0; i < tokenBlocks.length; i++) { + const block = tokenBlocks[i]; + const isComment = + block.blockComment === true || block.type === "comment"; + if (isComment && block.endLine > block.startLine) { + result.push({ + startLine: block.startLine, + endLine: block.endLine, + kind: "comment", + }); + } + } + } + + // Method 2: Scan for multiline comments using syntax tokens (for standalone comments) + let inBlockComment = false; + let commentStartLine = -1; + + for (let lineNum = 0; lineNum < lineCount; lineNum++) { + const line = this.model.getLine(lineNum); + + if (!inBlockComment && line.includes("/*")) { + const commentStart = line.indexOf("/*"); + const commentEnd = line.indexOf("*/", commentStart + 2); + + if (commentEnd === -1) { + inBlockComment = true; + commentStartLine = lineNum; + } + } else if (inBlockComment && line.includes("*/")) { + inBlockComment = false; + if (commentStartLine !== -1 && lineNum > commentStartLine) { + const isAlreadyCovered = result.some( + (range) => + range.startLine === commentStartLine && range.endLine === lineNum, + ); + + if (!isAlreadyCovered) { + result.push({ + startLine: commentStartLine, + endLine: lineNum, + kind: "comment", + }); + } + } + commentStartLine = -1; + } + } + } + // DFS private _flattenFoldingBlockTree(rootBlock: FoldingBlock): FoldingBlock[] { const stack: FoldingBlock[] = [rootBlock]; From cb014d2b8f64f66c41b275b2ade7ad2221e8de2d Mon Sep 17 00:00:00 2001 From: Kishan Patel Date: Wed, 17 Sep 2025 18:05:10 -0400 Subject: [PATCH 2/5] feat(LanguageServiceProvider): refactored comment detection Signed-off-by: Kishan Patel --- server/src/sas/LanguageServiceProvider.ts | 63 ++++------------------- server/src/sas/SyntaxProvider.ts | 54 +++++++++++++++++++ 2 files changed, 63 insertions(+), 54 deletions(-) diff --git a/server/src/sas/LanguageServiceProvider.ts b/server/src/sas/LanguageServiceProvider.ts index 96cf0a754..b43e7c71a 100644 --- a/server/src/sas/LanguageServiceProvider.ts +++ b/server/src/sas/LanguageServiceProvider.ts @@ -216,62 +216,17 @@ export class LanguageServiceProvider { } private addCommentFolding(result: FoldingRange[]) { - const lineCount = this.model.getLineCount(); + // Get all comment ranges from the lexer + const commentRanges = this.syntaxProvider.getAllCommentRanges(); - // Method 1: Try token blocks (works for comments inside code blocks) - if (this.syntaxProvider.lexer.tknBlks) { - const tokenBlocks = this.syntaxProvider.lexer.tknBlks; - for (let i = 0; i < tokenBlocks.length; i++) { - const block = tokenBlocks[i]; - const isComment = - block.blockComment === true || block.type === "comment"; - if (isComment && block.endLine > block.startLine) { - result.push({ - startLine: block.startLine, - endLine: block.endLine, - kind: "comment", - }); - } - } + for (const range of commentRanges) { + result.push({ + startLine: range.startLine, + endLine: range.endLine, + kind: "comment", + }); } - - // Method 2: Scan for multiline comments using syntax tokens (for standalone comments) - let inBlockComment = false; - let commentStartLine = -1; - - for (let lineNum = 0; lineNum < lineCount; lineNum++) { - const line = this.model.getLine(lineNum); - - if (!inBlockComment && line.includes("/*")) { - const commentStart = line.indexOf("/*"); - const commentEnd = line.indexOf("*/", commentStart + 2); - - if (commentEnd === -1) { - inBlockComment = true; - commentStartLine = lineNum; - } - } else if (inBlockComment && line.includes("*/")) { - inBlockComment = false; - if (commentStartLine !== -1 && lineNum > commentStartLine) { - const isAlreadyCovered = result.some( - (range) => - range.startLine === commentStartLine && range.endLine === lineNum, - ); - - if (!isAlreadyCovered) { - result.push({ - startLine: commentStartLine, - endLine: lineNum, - kind: "comment", - }); - } - } - commentStartLine = -1; - } - } - } - - // DFS + } // DFS private _flattenFoldingBlockTree(rootBlock: FoldingBlock): FoldingBlock[] { const stack: FoldingBlock[] = [rootBlock]; const resultList: FoldingBlock[] = []; diff --git a/server/src/sas/SyntaxProvider.ts b/server/src/sas/SyntaxProvider.ts index 45d6f6b01..40e2cced4 100644 --- a/server/src/sas/SyntaxProvider.ts +++ b/server/src/sas/SyntaxProvider.ts @@ -423,4 +423,58 @@ export class SyntaxProvider { } return block.name; } + getTokenBlocks(): any[] { + return this.lexer.tknBlks || []; + } + getAllCommentRanges(): Array<{ startLine: number; endLine: number }> { + const ranges: Array<{ startLine: number; endLine: number }> = []; + const lineCount = this.model.getLineCount(); + + // First, get comment blocks from lexer token blocks + const tokenBlocks = this.getTokenBlocks(); + for (const block of tokenBlocks) { + const isComment = block.blockComment === true || block.type === "comment"; + if (isComment && block.endLine > block.startLine) { + ranges.push({ + startLine: block.startLine, + endLine: block.endLine, + }); + } + } + + // Then scan for any multiline comment tokens that might not be in token blocks + for (let lineNum = 0; lineNum < lineCount; lineNum++) { + const tokens = this.getSyntax(lineNum); + + for (const token of tokens) { + if (token.style === "comment" || token.style === "macro-comment") { + // Check if this token indicates a multiline span + if ( + token.state && + typeof token.state === "object" && + token.state.line !== undefined + ) { + const endLine = token.state.line; + if (endLine > lineNum) { + // Check if this range is already covered + const isAlreadyCovered = ranges.some( + (range) => + range.startLine === lineNum && range.endLine === endLine, + ); + + if (!isAlreadyCovered) { + ranges.push({ + startLine: lineNum, + endLine: endLine, + }); + } + } + } + break; // Only need first comment token per line + } + } + } + + return ranges; + } } From 8821dd953a52f3fb6b78fd82322f21f73eda0340 Mon Sep 17 00:00:00 2001 From: Kishan Patel Date: Thu, 18 Sep 2025 14:17:55 -0400 Subject: [PATCH 3/5] feat(LanguageServiceProvider): refactored logic to be in addItem() Signed-off-by: Kishan Patel --- server/src/sas/LanguageServiceProvider.ts | 4 +- server/src/sas/SyntaxProvider.ts | 66 ++++++----------------- 2 files changed, 18 insertions(+), 52 deletions(-) diff --git a/server/src/sas/LanguageServiceProvider.ts b/server/src/sas/LanguageServiceProvider.ts index b43e7c71a..73d20c902 100644 --- a/server/src/sas/LanguageServiceProvider.ts +++ b/server/src/sas/LanguageServiceProvider.ts @@ -216,8 +216,8 @@ export class LanguageServiceProvider { } private addCommentFolding(result: FoldingRange[]) { - // Get all comment ranges from the lexer - const commentRanges = this.syntaxProvider.getAllCommentRanges(); + // Get multiline comments directly from lexer token processing + const commentRanges = this.syntaxProvider.getMultilineComments(); for (const range of commentRanges) { result.push({ diff --git a/server/src/sas/SyntaxProvider.ts b/server/src/sas/SyntaxProvider.ts index 40e2cced4..035611a95 100644 --- a/server/src/sas/SyntaxProvider.ts +++ b/server/src/sas/SyntaxProvider.ts @@ -29,6 +29,8 @@ export class SyntaxProvider { private tailUnchangedSyntaxTable: SyntaxToken[][] = []; private removedSyntaxTable: SyntaxToken[][] = []; private _tokenCallback: ((token: Token) => void) | undefined; + private _multilineComments: Array<{ startLine: number; endLine: number }> = + []; public blockComment = { start: "/*", end: "*/" }; public lexer; @@ -45,6 +47,7 @@ export class SyntaxProvider { let startLine = 0; this.currTokenIndex = 0; this.lastToken = null; + this._multilineComments = []; // Clear previous comments this.parsingState = 1; //LanguageService.ParsingState.STARTING; this.parsedRange = this.lexer.start(change); @@ -345,6 +348,17 @@ export class SyntaxProvider { end: { line: token.end.line, column: token.end.column }, }); } + + // Collect multiline comments as they're processed by the lexer + if ( + (token.type === "comment" || token.type === "macro-comment") && + token.end.line > token.start.line + ) { + this._multilineComments.push({ + startLine: token.start.line, + endLine: token.end.line, + }); + } } // public functions @@ -426,55 +440,7 @@ export class SyntaxProvider { getTokenBlocks(): any[] { return this.lexer.tknBlks || []; } - getAllCommentRanges(): Array<{ startLine: number; endLine: number }> { - const ranges: Array<{ startLine: number; endLine: number }> = []; - const lineCount = this.model.getLineCount(); - - // First, get comment blocks from lexer token blocks - const tokenBlocks = this.getTokenBlocks(); - for (const block of tokenBlocks) { - const isComment = block.blockComment === true || block.type === "comment"; - if (isComment && block.endLine > block.startLine) { - ranges.push({ - startLine: block.startLine, - endLine: block.endLine, - }); - } - } - - // Then scan for any multiline comment tokens that might not be in token blocks - for (let lineNum = 0; lineNum < lineCount; lineNum++) { - const tokens = this.getSyntax(lineNum); - - for (const token of tokens) { - if (token.style === "comment" || token.style === "macro-comment") { - // Check if this token indicates a multiline span - if ( - token.state && - typeof token.state === "object" && - token.state.line !== undefined - ) { - const endLine = token.state.line; - if (endLine > lineNum) { - // Check if this range is already covered - const isAlreadyCovered = ranges.some( - (range) => - range.startLine === lineNum && range.endLine === endLine, - ); - - if (!isAlreadyCovered) { - ranges.push({ - startLine: lineNum, - endLine: endLine, - }); - } - } - } - break; // Only need first comment token per line - } - } - } - - return ranges; + getMultilineComments(): Array<{ startLine: number; endLine: number }> { + return this._multilineComments; } } From 79444297be71cf62d1a0201e072a65cf7467f96b Mon Sep 17 00:00:00 2001 From: Kishan Patel Date: Wed, 24 Sep 2025 13:03:35 -0400 Subject: [PATCH 4/5] feat(LanguageServiceProvider): addresed nits Signed-off-by: Kishan Patel --- server/src/sas/LanguageServiceProvider.ts | 4 ++-- server/src/sas/SyntaxProvider.ts | 6 ------ 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/server/src/sas/LanguageServiceProvider.ts b/server/src/sas/LanguageServiceProvider.ts index 73d20c902..5fdf8f1e2 100644 --- a/server/src/sas/LanguageServiceProvider.ts +++ b/server/src/sas/LanguageServiceProvider.ts @@ -214,7 +214,6 @@ export class LanguageServiceProvider { return result; } - private addCommentFolding(result: FoldingRange[]) { // Get multiline comments directly from lexer token processing const commentRanges = this.syntaxProvider.getMultilineComments(); @@ -226,7 +225,8 @@ export class LanguageServiceProvider { kind: "comment", }); } - } // DFS + } + // DFS private _flattenFoldingBlockTree(rootBlock: FoldingBlock): FoldingBlock[] { const stack: FoldingBlock[] = [rootBlock]; const resultList: FoldingBlock[] = []; diff --git a/server/src/sas/SyntaxProvider.ts b/server/src/sas/SyntaxProvider.ts index 035611a95..82072b8fc 100644 --- a/server/src/sas/SyntaxProvider.ts +++ b/server/src/sas/SyntaxProvider.ts @@ -437,10 +437,4 @@ export class SyntaxProvider { } return block.name; } - getTokenBlocks(): any[] { - return this.lexer.tknBlks || []; - } - getMultilineComments(): Array<{ startLine: number; endLine: number }> { - return this._multilineComments; - } } From 8a0ceda3b2e934caa7c18cb74b97991e11d5740e Mon Sep 17 00:00:00 2001 From: Kishan Patel Date: Wed, 24 Sep 2025 13:32:47 -0400 Subject: [PATCH 5/5] feat(LanguageServiceProvider): added back method accidentally removed Signed-off-by: Kishan Patel --- server/src/sas/SyntaxProvider.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/server/src/sas/SyntaxProvider.ts b/server/src/sas/SyntaxProvider.ts index 82072b8fc..537d8929f 100644 --- a/server/src/sas/SyntaxProvider.ts +++ b/server/src/sas/SyntaxProvider.ts @@ -437,4 +437,7 @@ export class SyntaxProvider { } return block.name; } + getMultilineComments(): Array<{ startLine: number; endLine: number }> { + return this._multilineComments; + } }