185 lines
8.3 KiB
JavaScript
185 lines
8.3 KiB
JavaScript
![]() |
/*---------------------------------------------------------------------------------------------
|
||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||
|
*--------------------------------------------------------------------------------------------*/
|
||
|
(function (factory) {
|
||
|
if (typeof module === "object" && typeof module.exports === "object") {
|
||
|
var v = factory(require, exports);
|
||
|
if (v !== undefined) module.exports = v;
|
||
|
}
|
||
|
else if (typeof define === "function" && define.amd) {
|
||
|
define(["require", "exports", "../htmlLanguageTypes", "../parser/htmlScanner"], factory);
|
||
|
}
|
||
|
})(function (require, exports) {
|
||
|
"use strict";
|
||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||
|
exports.HTMLFolding = void 0;
|
||
|
const htmlLanguageTypes_1 = require("../htmlLanguageTypes");
|
||
|
const htmlScanner_1 = require("../parser/htmlScanner");
|
||
|
class HTMLFolding {
|
||
|
constructor(dataManager) {
|
||
|
this.dataManager = dataManager;
|
||
|
}
|
||
|
limitRanges(ranges, rangeLimit) {
|
||
|
ranges = ranges.sort((r1, r2) => {
|
||
|
let diff = r1.startLine - r2.startLine;
|
||
|
if (diff === 0) {
|
||
|
diff = r1.endLine - r2.endLine;
|
||
|
}
|
||
|
return diff;
|
||
|
});
|
||
|
// compute each range's nesting level in 'nestingLevels'.
|
||
|
// count the number of ranges for each level in 'nestingLevelCounts'
|
||
|
let top = void 0;
|
||
|
const previous = [];
|
||
|
const nestingLevels = [];
|
||
|
const nestingLevelCounts = [];
|
||
|
const setNestingLevel = (index, level) => {
|
||
|
nestingLevels[index] = level;
|
||
|
if (level < 30) {
|
||
|
nestingLevelCounts[level] = (nestingLevelCounts[level] || 0) + 1;
|
||
|
}
|
||
|
};
|
||
|
// compute nesting levels and sanitize
|
||
|
for (let i = 0; i < ranges.length; i++) {
|
||
|
const entry = ranges[i];
|
||
|
if (!top) {
|
||
|
top = entry;
|
||
|
setNestingLevel(i, 0);
|
||
|
}
|
||
|
else {
|
||
|
if (entry.startLine > top.startLine) {
|
||
|
if (entry.endLine <= top.endLine) {
|
||
|
previous.push(top);
|
||
|
top = entry;
|
||
|
setNestingLevel(i, previous.length);
|
||
|
}
|
||
|
else if (entry.startLine > top.endLine) {
|
||
|
do {
|
||
|
top = previous.pop();
|
||
|
} while (top && entry.startLine > top.endLine);
|
||
|
if (top) {
|
||
|
previous.push(top);
|
||
|
}
|
||
|
top = entry;
|
||
|
setNestingLevel(i, previous.length);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
let entries = 0;
|
||
|
let maxLevel = 0;
|
||
|
for (let i = 0; i < nestingLevelCounts.length; i++) {
|
||
|
const n = nestingLevelCounts[i];
|
||
|
if (n) {
|
||
|
if (n + entries > rangeLimit) {
|
||
|
maxLevel = i;
|
||
|
break;
|
||
|
}
|
||
|
entries += n;
|
||
|
}
|
||
|
}
|
||
|
const result = [];
|
||
|
for (let i = 0; i < ranges.length; i++) {
|
||
|
const level = nestingLevels[i];
|
||
|
if (typeof level === 'number') {
|
||
|
if (level < maxLevel || (level === maxLevel && entries++ < rangeLimit)) {
|
||
|
result.push(ranges[i]);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
getFoldingRanges(document, context) {
|
||
|
const voidElements = this.dataManager.getVoidElements(document.languageId);
|
||
|
const scanner = (0, htmlScanner_1.createScanner)(document.getText());
|
||
|
let token = scanner.scan();
|
||
|
const ranges = [];
|
||
|
const stack = [];
|
||
|
let lastTagName = null;
|
||
|
let prevStart = -1;
|
||
|
function addRange(range) {
|
||
|
ranges.push(range);
|
||
|
prevStart = range.startLine;
|
||
|
}
|
||
|
while (token !== htmlLanguageTypes_1.TokenType.EOS) {
|
||
|
switch (token) {
|
||
|
case htmlLanguageTypes_1.TokenType.StartTag: {
|
||
|
const tagName = scanner.getTokenText();
|
||
|
const startLine = document.positionAt(scanner.getTokenOffset()).line;
|
||
|
stack.push({ startLine, tagName });
|
||
|
lastTagName = tagName;
|
||
|
break;
|
||
|
}
|
||
|
case htmlLanguageTypes_1.TokenType.EndTag: {
|
||
|
lastTagName = scanner.getTokenText();
|
||
|
break;
|
||
|
}
|
||
|
case htmlLanguageTypes_1.TokenType.StartTagClose:
|
||
|
if (!lastTagName || !this.dataManager.isVoidElement(lastTagName, voidElements)) {
|
||
|
break;
|
||
|
}
|
||
|
// fallthrough
|
||
|
case htmlLanguageTypes_1.TokenType.EndTagClose:
|
||
|
case htmlLanguageTypes_1.TokenType.StartTagSelfClose: {
|
||
|
let i = stack.length - 1;
|
||
|
while (i >= 0 && stack[i].tagName !== lastTagName) {
|
||
|
i--;
|
||
|
}
|
||
|
if (i >= 0) {
|
||
|
const stackElement = stack[i];
|
||
|
stack.length = i;
|
||
|
const line = document.positionAt(scanner.getTokenOffset()).line;
|
||
|
const startLine = stackElement.startLine;
|
||
|
const endLine = line - 1;
|
||
|
if (endLine > startLine && prevStart !== startLine) {
|
||
|
addRange({ startLine, endLine });
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
case htmlLanguageTypes_1.TokenType.Comment: {
|
||
|
let startLine = document.positionAt(scanner.getTokenOffset()).line;
|
||
|
const text = scanner.getTokenText();
|
||
|
const m = text.match(/^\s*#(region\b)|(endregion\b)/);
|
||
|
if (m) {
|
||
|
if (m[1]) { // start pattern match
|
||
|
stack.push({ startLine, tagName: '' }); // empty tagName marks region
|
||
|
}
|
||
|
else {
|
||
|
let i = stack.length - 1;
|
||
|
while (i >= 0 && stack[i].tagName.length) {
|
||
|
i--;
|
||
|
}
|
||
|
if (i >= 0) {
|
||
|
const stackElement = stack[i];
|
||
|
stack.length = i;
|
||
|
const endLine = startLine;
|
||
|
startLine = stackElement.startLine;
|
||
|
if (endLine > startLine && prevStart !== startLine) {
|
||
|
addRange({ startLine, endLine, kind: htmlLanguageTypes_1.FoldingRangeKind.Region });
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
const endLine = document.positionAt(scanner.getTokenOffset() + scanner.getTokenLength()).line;
|
||
|
if (startLine < endLine) {
|
||
|
addRange({ startLine, endLine, kind: htmlLanguageTypes_1.FoldingRangeKind.Comment });
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
token = scanner.scan();
|
||
|
}
|
||
|
const rangeLimit = context && context.rangeLimit || Number.MAX_VALUE;
|
||
|
if (ranges.length > rangeLimit) {
|
||
|
return this.limitRanges(ranges, rangeLimit);
|
||
|
}
|
||
|
return ranges;
|
||
|
}
|
||
|
}
|
||
|
exports.HTMLFolding = HTMLFolding;
|
||
|
});
|