svelte-language-server
Version:
A language server for Svelte
142 lines • 5.04 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.indentBasedFoldingRangeForTag = indentBasedFoldingRangeForTag;
exports.indentBasedFoldingRange = indentBasedFoldingRange;
exports.guessTabSize = guessTabSize;
const lodash_1 = require("lodash");
/**
*
* 1. check tab and space counts for lines
* 2. if there're mixing space and tab guess the tabSize otherwise we only need to compare the numbers of spaces or tabs between lines.
*/
function indentBasedFoldingRangeForTag(document, tag) {
if (tag.startPos.line === tag.endPos.line) {
return [];
}
const startLine = tag.startPos.line + 1;
const endLine = tag.endPos.line - 1;
if (startLine > endLine || startLine === endLine) {
return [];
}
return indentBasedFoldingRange({ document, ranges: [{ startLine, endLine }] });
}
function indentBasedFoldingRange({ document, ranges, skipFold }) {
const text = document.getText();
const lines = text.split(/\r?\n/);
const indents = lines
.map((line, index) => ({
...collectIndents(line),
index
}))
.filter((line) => !line.empty);
const tabs = (0, lodash_1.sum)(indents.map((l) => l.tabCount));
const spaces = (0, lodash_1.sum)(indents.map((l) => l.spaceCount));
const tabSize = tabs && spaces ? guessTabSize(indents) : 4;
let currentIndent;
const result = [];
const unfinishedFolds = new Map();
ranges ??= [{ startLine: 0, endLine: lines.length - 1 }];
let rangeIndex = 0;
let range = ranges[rangeIndex++];
if (!range) {
return [];
}
for (const indentInfo of indents) {
if (indentInfo.index < range.startLine || indentInfo.empty) {
continue;
}
if (indentInfo.index > range.endLine) {
for (const fold of unfinishedFolds.values()) {
fold.endLine = range.endLine;
}
range = ranges[rangeIndex++];
if (!range) {
break;
}
}
const lineIndent = indentInfo.tabCount * tabSize + indentInfo.spaceCount;
currentIndent ??= lineIndent;
if (lineIndent > currentIndent) {
const startLine = indentInfo.index - 1;
if (!skipFold?.(startLine, lines[startLine])) {
const fold = { startLine, endLine: indentInfo.index };
unfinishedFolds.set(currentIndent, fold);
result.push(fold);
}
currentIndent = lineIndent;
}
if (lineIndent < currentIndent) {
const last = unfinishedFolds.get(lineIndent);
unfinishedFolds.delete(lineIndent);
if (last) {
last.endLine = Math.max(last.endLine, indentInfo.index - 1);
}
currentIndent = lineIndent;
}
}
return result;
}
function collectIndents(line) {
let tabCount = 0;
let spaceCount = 0;
let empty = true;
for (let index = 0; index < line.length; index++) {
const char = line[index];
if (char === '\t') {
tabCount++;
}
else if (char === ' ') {
spaceCount++;
}
else {
empty = false;
break;
}
}
return { tabCount, spaceCount, empty };
}
/**
*
* The indentation guessing is based on the indentation difference between lines.
* And if the count equals, then the one used more often takes priority.
*/
function guessTabSize(nonEmptyLines) {
// simplified version of
// https://github.com/microsoft/vscode/blob/559e9beea981b47ffd76d90158ccccafef663324/src/vs/editor/common/model/indentationGuesser.ts#L106
if (nonEmptyLines.length === 1) {
return 4;
}
const guessingTabSize = [2, 4, 6, 8, 3, 5, 7];
const MAX_GUESS = 8;
const matchCounts = new Map();
for (let index = 0; index < nonEmptyLines.length; index++) {
const line = nonEmptyLines[index];
const previousLine = nonEmptyLines[index - 1] ?? { spaceCount: 0, tabCount: 0 };
const spaceDiff = Math.abs(line.spaceCount - previousLine.spaceCount);
const tabDiff = Math.abs(line.tabCount - previousLine.tabCount);
const diff = tabDiff === 0 ? spaceDiff : spaceDiff % tabDiff === 0 ? spaceDiff / tabDiff : 0;
if (diff === 0 || diff > MAX_GUESS) {
continue;
}
for (const guess of guessingTabSize) {
if (diff === guess) {
matchCounts.set(guess, (matchCounts.get(guess) ?? 0) + 1);
}
}
}
let max = 0;
let tabSize;
for (const [size, count] of matchCounts) {
max = Math.max(max, count);
if (max === count) {
tabSize = size;
}
}
const match4 = matchCounts.get(4);
const match2 = matchCounts.get(2);
if (tabSize === 4 && match4 && match4 > 0 && match2 && match2 > 0 && match2 >= match4 / 2) {
tabSize = 2;
}
return tabSize ?? 4;
}
//# sourceMappingURL=indentFolding.js.map