mirror of
https://github.com/microsoft/vscode.git
synced 2026-05-02 06:21:50 +01:00
117 lines
3.9 KiB
TypeScript
117 lines
3.9 KiB
TypeScript
/*---------------------------------------------------------------------------------------------
|
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
*--------------------------------------------------------------------------------------------*/
|
|
'use strict';
|
|
|
|
import { TextDocument, Position, CancellationToken } from 'vscode-languageserver';
|
|
import { createScanner, SyntaxKind, ScanError } from 'jsonc-parser';
|
|
import { FoldingRangeType, FoldingRange, FoldingRangeList } from 'vscode-languageserver-protocol-foldingprovider';
|
|
|
|
export function getFoldingRegions(document: TextDocument, maxRanges: number | undefined, cancellationToken: CancellationToken | null) {
|
|
let ranges: FoldingRange[] = [];
|
|
let nestingLevels: number[] = [];
|
|
let stack: FoldingRange[] = [];
|
|
let prevStart = -1;
|
|
let scanner = createScanner(document.getText(), false);
|
|
let token = scanner.scan();
|
|
|
|
function addRange(range: FoldingRange) {
|
|
ranges.push(range);
|
|
nestingLevels.push(stack.length);
|
|
}
|
|
|
|
while (token !== SyntaxKind.EOF) {
|
|
if (cancellationToken && cancellationToken.isCancellationRequested) {
|
|
return null;
|
|
}
|
|
switch (token) {
|
|
case SyntaxKind.OpenBraceToken:
|
|
case SyntaxKind.OpenBracketToken: {
|
|
let startLine = document.positionAt(scanner.getTokenOffset()).line;
|
|
let range = { startLine, endLine: startLine, type: token === SyntaxKind.OpenBraceToken ? 'object' : 'array' };
|
|
stack.push(range);
|
|
break;
|
|
}
|
|
case SyntaxKind.CloseBraceToken:
|
|
case SyntaxKind.CloseBracketToken: {
|
|
let type = token === SyntaxKind.CloseBraceToken ? 'object' : 'array';
|
|
if (stack.length > 0 && stack[stack.length - 1].type === type) {
|
|
let range = stack.pop();
|
|
let line = document.positionAt(scanner.getTokenOffset()).line;
|
|
if (range && line > range.startLine + 1 && prevStart !== range.startLine) {
|
|
range.endLine = line - 1;
|
|
addRange(range);
|
|
prevStart = range.startLine;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case SyntaxKind.BlockCommentTrivia: {
|
|
let startLine = document.positionAt(scanner.getTokenOffset()).line;
|
|
let endLine = document.positionAt(scanner.getTokenOffset() + scanner.getTokenLength()).line;
|
|
if (scanner.getTokenError() === ScanError.UnexpectedEndOfComment && startLine + 1 < document.lineCount) {
|
|
scanner.setPosition(document.offsetAt(Position.create(startLine + 1, 0)));
|
|
} else {
|
|
if (startLine < endLine) {
|
|
addRange({ startLine, endLine, type: FoldingRangeType.Comment });
|
|
prevStart = startLine;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case SyntaxKind.LineCommentTrivia: {
|
|
let text = document.getText().substr(scanner.getTokenOffset(), scanner.getTokenLength());
|
|
let m = text.match(/^\/\/\s*#(region\b)|(endregion\b)/);
|
|
if (m) {
|
|
let line = document.positionAt(scanner.getTokenOffset()).line;
|
|
if (m[1]) { // start pattern match
|
|
let range = { startLine: line, endLine: line, type: FoldingRangeType.Region };
|
|
stack.push(range);
|
|
} else {
|
|
let i = stack.length - 1;
|
|
while (i >= 0 && stack[i].type !== FoldingRangeType.Region) {
|
|
i--;
|
|
}
|
|
if (i >= 0) {
|
|
let range = stack[i];
|
|
stack.length = i;
|
|
if (line > range.startLine && prevStart !== range.startLine) {
|
|
range.endLine = line;
|
|
addRange(range);
|
|
prevStart = range.startLine;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
}
|
|
token = scanner.scan();
|
|
}
|
|
if (maxRanges && ranges.length > maxRanges) {
|
|
let counts: number[] = [];
|
|
for (let level of nestingLevels) {
|
|
if (level < 30) {
|
|
counts[level] = (counts[level] || 0) + 1;
|
|
}
|
|
}
|
|
let entries = 0;
|
|
let maxLevel = 0;
|
|
for (let i = 0; i < counts.length; i++) {
|
|
let n = counts[i];
|
|
if (n) {
|
|
if (n + entries > maxRanges) {
|
|
maxLevel = i;
|
|
break;
|
|
}
|
|
entries += n;
|
|
}
|
|
}
|
|
ranges = ranges.filter((r, index) => nestingLevels[index] < maxLevel);
|
|
}
|
|
return <FoldingRangeList>{ ranges };
|
|
} |