Skip to content

Commit feaaa4d

Browse files
glennsartiTylerLeonhardt
authored andcommitted
(GH-1437) Fix detecting contiguous comment blocks and regions (#1438)
* (maint) Refactor region folding detection Previously the region comment detection used a little convoluted method to detect regions in a document. This commit simplifies the detection by extracting the line from the document and using regex's similar to that used by the PowerShell language configuration. This also removes the need for the emptyline and subsequentText method calls. While the performance of the folder is pretty quick, this should in theory make it faster on larger documents by doing less calls to the VSCode Document API. * (GH-1437) Fix detecting contiguous comment blocks and regions Previously the syntax folding feature would not correctly identify comment blocks and comment regions if they appeared all together. This commit changes the comment block detection to ignore line comments that start with region and endregion, i.e. region block start/end directives. This commit also adds test for this scenario.
1 parent b5e8379 commit feaaa4d

File tree

4 files changed

+58
-43
lines changed

4 files changed

+58
-43
lines changed

Diff for: src/features/Folding.ts

+31-43
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,22 @@ interface ILineNumberRangeList extends Array<LineNumberRange> { }
173173
export class FoldingProvider implements vscode.FoldingRangeProvider {
174174
private powershellGrammar: IGrammar;
175175

176+
/**
177+
* These regular expressions are used to match lines which mark the start and end of region comment in a PowerShell
178+
* script. They are based on the defaults in the VS Code Language Configuration at;
179+
* https://github.com/Microsoft/vscode/blob/64186b0a26/extensions/powershell/language-configuration.json#L26-L31
180+
*/
181+
private readonly startRegionText = /^\s*#region\b/i;
182+
private readonly endRegionText = /^\s*#endregion\b/i;
183+
/**
184+
* This regular expressions is used to detect a line comment (as opposed to an inline comment), that is not a region
185+
* block directive i.e.
186+
* - No text between the beginning of the line and `#`
187+
* - Comment does start with region
188+
* - Comment does start with endregion
189+
*/
190+
private readonly lineCommentText = /\s*#(?!region\b|endregion\b)/i;
191+
176192
constructor(
177193
powershellGrammar: IGrammar,
178194
) {
@@ -310,34 +326,16 @@ export class FoldingProvider implements vscode.FoldingRangeProvider {
310326
}
311327

312328
/**
313-
* Given a zero based offset, find the line text preceeding it in the document
329+
* Given a zero based offset, find the line in the document
314330
* @param offset Zero based offset in the document
315331
* @param document The source text document
316-
* @returns The line text preceeding the offset, not including the preceeding Line Feed
332+
* @returns The line at the offset
317333
*/
318-
private preceedingText(
334+
private lineAtOffset(
319335
offset: number,
320336
document: vscode.TextDocument,
321-
): string {
322-
const endPos = document.positionAt(offset);
323-
const startPos = endPos.translate(0, -endPos.character);
324-
325-
return document.getText(new vscode.Range(startPos, endPos));
326-
}
327-
328-
/**
329-
* Given a zero based offset, find the line text after it in the document
330-
* @param offset Zero based offset in the document
331-
* @param document The source text document
332-
* @returns The line text after the offset, not including the subsequent Line Feed
333-
*/
334-
private subsequentText(
335-
offset: number,
336-
document: vscode.TextDocument,
337-
): string {
338-
const startPos: vscode.Position = document.positionAt(offset);
339-
const endPos: vscode.Position = document.lineAt(document.positionAt(offset)).range.end;
340-
return document.getText(new vscode.Range(startPos, endPos));
337+
): vscode.TextLine {
338+
return document.lineAt(document.positionAt(offset));
341339
}
342340

343341
/**
@@ -353,19 +351,16 @@ export class FoldingProvider implements vscode.FoldingRangeProvider {
353351
document: vscode.TextDocument,
354352
): ILineNumberRangeList {
355353
const result = [];
356-
357-
const emptyLine = /^\s*$/;
358-
359354
let startLine: number = -1;
360355
let nextLine: number = -1;
361356

362357
tokens.forEach((token) => {
363358
if (token.scopes.indexOf("punctuation.definition.comment.powershell") !== -1) {
359+
const line: vscode.TextLine = this.lineAtOffset(token.startIndex, document);
364360
// The punctuation.definition.comment.powershell token matches new-line comments
365-
// and inline comments e.g. `$x = 'foo' # inline comment`. We are only interested
366-
// in comments which begin the line i.e. no preceeding text
367-
if (emptyLine.test(this.preceedingText(token.startIndex, document))) {
368-
const lineNum = document.positionAt(token.startIndex).line;
361+
// and inline comments e.g. `$x = 'foo' # inline comment`.
362+
if (this.lineCommentText.test(line.text)) {
363+
const lineNum = line.lineNumber;
369364
// A simple pattern for keeping track of contiguous numbers in a known sorted array
370365
if (startLine === -1) {
371366
startLine = lineNum;
@@ -420,21 +415,14 @@ export class FoldingProvider implements vscode.FoldingRangeProvider {
420415
document: vscode.TextDocument,
421416
): ITokenList {
422417
const result = [];
423-
424-
const emptyLine = /^\s*$/;
425-
const startRegionText = /^#region\b/i;
426-
const endRegionText = /^#endregion\b/i;
427-
428418
tokens.forEach((token) => {
429419
if (token.scopes.indexOf("punctuation.definition.comment.powershell") !== -1) {
430-
if (emptyLine.test(this.preceedingText(token.startIndex, document))) {
431-
const commentText = this.subsequentText(token.startIndex, document);
432-
if (startRegionText.test(commentText)) {
433-
result.push(this.addTokenScope(token, "custom.start.region"));
434-
}
435-
if (endRegionText.test(commentText)) {
436-
result.push(this.addTokenScope(token, "custom.end.region"));
437-
}
420+
const line: string = this.lineAtOffset(token.startIndex, document).text;
421+
if (this.startRegionText.test(line)) {
422+
result.push(this.addTokenScope(token, "custom.start.region"));
423+
}
424+
if (this.endRegionText.test(line)) {
425+
result.push(this.addTokenScope(token, "custom.end.region"));
438426
}
439427
}
440428
});

Diff for: test/features/folding.test.ts

+3
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@ suite("Features", () => {
5050
{ start: 41, end: 45, kind: 3 },
5151
{ start: 51, end: 53, kind: 3 },
5252
{ start: 56, end: 59, kind: 3 },
53+
{ start: 64, end: 66, kind: 1 },
54+
{ start: 67, end: 72, kind: 3 },
55+
{ start: 68, end: 70, kind: 1 },
5356
];
5457

5558
test("Can detect all of the foldable regions in a document with CRLF line endings", async () => {

Diff for: test/fixtures/folding-crlf.ps1

+12
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,15 @@ double quoted herestrings should also fold
5959
'should fold2'
6060
)
6161
}
62+
63+
# Make sure contiguous comment blocks can be folded properly
64+
65+
# Comment Block 1
66+
# Comment Block 1
67+
# Comment Block 1
68+
#region Comment Block 3
69+
# Comment Block 2
70+
# Comment Block 2
71+
# Comment Block 2
72+
$something = $true
73+
#endregion Comment Block 3

Diff for: test/fixtures/folding-lf.ps1

+12
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,15 @@ double quoted herestrings should also fold
5959
'should fold2'
6060
)
6161
}
62+
63+
# Make sure contiguous comment blocks can be folded properly
64+
65+
# Comment Block 1
66+
# Comment Block 1
67+
# Comment Block 1
68+
#region Comment Block 3
69+
# Comment Block 2
70+
# Comment Block 2
71+
# Comment Block 2
72+
$something = $true
73+
#endregion Comment Block 3

0 commit comments

Comments
 (0)