SourceTermAnalysisSystem_vue/node_modules/eslint-plugin-toml/lib/rules/indent.js
2026-05-15 10:22:44 +08:00

449 lines
18 KiB
JavaScript

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const toml_eslint_parser_1 = require("toml-eslint-parser");
const utils_1 = require("../utils");
const ast_utils_1 = require("../utils/ast-utils");
const compat_1 = require("../utils/compat");
const ITERATION_OPTS = Object.freeze({
includeComments: true,
});
function buildIndentUtility(optionValue) {
const indent = optionValue ?? 2;
const textIndent = typeof indent === "number" ? " ".repeat(indent) : "\t";
return {
getIndentText: (offset) => offset === 1 ? textIndent : textIndent.repeat(offset),
outdent(indent) {
return indent.slice(0, -textIndent.length);
},
};
}
exports.default = (0, utils_1.createRule)("indent", {
meta: {
docs: {
description: "enforce consistent indentation",
categories: ["standard"],
extensionRule: false,
},
fixable: "whitespace",
schema: [
{
oneOf: [
{
enum: ["tab"],
},
{
type: "integer",
minimum: 0,
},
],
},
{
type: "object",
properties: {
subTables: { type: "integer", minimum: 0 },
keyValuePairs: { type: "integer", minimum: 0 },
},
additionalProperties: false,
},
],
messages: {
wrongIndentation: "Expected indentation of {{expected}} but found {{actual}}.",
},
type: "layout",
},
create(context) {
const sourceCode = (0, compat_1.getSourceCode)(context);
if (!sourceCode.parserServices?.isTOML) {
return {};
}
const { getIndentText, outdent } = buildIndentUtility(context.options[0]);
const subTablesOffset = context.options[1]?.subTables ?? 0;
const keyValuePairsOffset = context.options[1]?.keyValuePairs ?? 0;
const offsets = new Map();
function setOffset(token, offset, baseToken) {
if (token == null) {
return;
}
if (Array.isArray(token)) {
for (const t of token) {
setOffset(t, offset, baseToken);
}
}
else {
offsets.set(token, {
baseToken,
offset,
});
}
}
function processNodeList(nodeList, left, right, offset) {
let lastToken = left;
const alignTokens = new Set();
for (const node of nodeList) {
if (node == null) {
continue;
}
const elementTokens = {
firstToken: sourceCode.getFirstToken(node),
lastToken: sourceCode.getLastToken(node),
};
let t = lastToken;
while ((t = sourceCode.getTokenAfter(t, ITERATION_OPTS)) != null &&
t.range[1] <= elementTokens.firstToken.range[0]) {
alignTokens.add(t);
}
alignTokens.add(elementTokens.firstToken);
lastToken = elementTokens.lastToken;
}
if (right != null) {
let t = lastToken;
while ((t = sourceCode.getTokenAfter(t, ITERATION_OPTS)) != null &&
t.range[1] <= right.range[0]) {
alignTokens.add(t);
}
}
alignTokens.delete(left);
setOffset([...alignTokens], offset, left);
if (right != null) {
setOffset(right, 0, left);
}
}
return {
TOMLTopLevelTable(node) {
const first = sourceCode.getFirstToken(node, ITERATION_OPTS);
if (!first) {
return;
}
const beforeTokens = sourceCode.getTokensBefore(first, ITERATION_OPTS);
if (beforeTokens.length) {
const firstOfAllTokens = beforeTokens[0];
offsets.set(firstOfAllTokens, {
baseToken: null,
offset: 0,
expectedIndent: "",
});
setOffset(beforeTokens.slice(1), 0, firstOfAllTokens);
setOffset(first, 0, firstOfAllTokens);
}
else {
offsets.set(first, {
baseToken: null,
offset: 0,
expectedIndent: "",
});
}
let tableKeyStack = [];
function getTableOffset(keys) {
let last = tableKeyStack.pop();
while (last) {
if (last.keys.length &&
last.keys.length <= keys.length &&
last.keys.every((k, i) => k === keys[i])) {
if (last.keys.length < keys.length) {
tableKeyStack.push(last);
return last.offset + subTablesOffset;
}
return last.offset;
}
last = tableKeyStack.pop();
}
return 0;
}
for (const body of node.body) {
const bodyFirstToken = sourceCode.getFirstToken(body);
if (body.type === "TOMLKeyValue") {
if (bodyFirstToken !== first) {
setOffset(bodyFirstToken, 0, first);
}
}
if (body.type === "TOMLTable") {
const keys = (0, toml_eslint_parser_1.getStaticTOMLValue)(body.key);
const offset = getTableOffset(keys);
tableKeyStack.push({ keys, offset });
if (bodyFirstToken !== first) {
setOffset(bodyFirstToken, offset, first);
}
}
else {
tableKeyStack = [];
}
}
},
TOMLTable(node) {
const openBracket = sourceCode.getFirstToken(node);
if (node.kind === "array") {
const openBracketNext = sourceCode.getTokenAfter(openBracket);
setOffset(openBracketNext, 0, openBracket);
}
const key = sourceCode.getFirstToken(node.key);
setOffset(key, 1, openBracket);
const closeBracket = sourceCode.getTokenAfter(node.key);
setOffset(closeBracket, 0, openBracket);
if (node.kind === "array") {
const closeBracketNext = sourceCode.getTokenAfter(closeBracket);
setOffset(closeBracketNext, 0, closeBracket);
}
processNodeList(node.body, openBracket, null, keyValuePairsOffset);
},
TOMLKeyValue(node) {
const keyToken = sourceCode.getFirstToken(node.key);
const valueToken = sourceCode.getFirstToken(node.value);
const eqToken = sourceCode.getTokenBefore(node.value, ast_utils_1.isEqualSign);
setOffset(eqToken, 1, keyToken);
setOffset(valueToken, 1, eqToken);
},
TOMLKey(node) {
const first = sourceCode.getFirstToken(node, ITERATION_OPTS);
processNodeList(node.keys, first, null, 1);
},
TOMLValue() {
},
TOMLBare() {
},
TOMLQuoted() {
},
TOMLArray(node) {
const openBracket = sourceCode.getFirstToken(node);
const closeBracket = sourceCode.getLastToken(node);
processNodeList(node.elements, openBracket, closeBracket, 1);
},
TOMLInlineTable(node) {
const openBrace = sourceCode.getFirstToken(node);
const closeBrace = sourceCode.getLastToken(node);
processNodeList(node.body, openBrace, closeBrace, 1);
},
"Program:exit"(node) {
const lineIndentsStep1 = [];
let tokensOnSameLine = [];
for (const token of sourceCode.getTokens(node, ITERATION_OPTS)) {
if (tokensOnSameLine.length === 0 ||
tokensOnSameLine[0].loc.start.line === token.loc.start.line) {
tokensOnSameLine.push(token);
}
else {
const lineIndent = processExpectedIndent(tokensOnSameLine);
lineIndentsStep1[lineIndent.line] = lineIndent;
tokensOnSameLine = [token];
}
}
if (tokensOnSameLine.length >= 1) {
const lineIndent = processExpectedIndent(tokensOnSameLine);
lineIndentsStep1[lineIndent.line] = lineIndent;
}
const lineIndents = processMissingLines(lineIndentsStep1);
validateLines(lineIndents);
},
};
function processExpectedIndent(lineTokens) {
const firstToken = lineTokens.shift();
let token = firstToken;
const expectedIndent = getExpectedIndent(token);
let lineExpectedIndent = expectedIndent;
if (lineExpectedIndent == null) {
while ((token = lineTokens.shift()) != null) {
lineExpectedIndent = getExpectedIndent(token);
if (lineExpectedIndent != null) {
break;
}
}
}
if (expectedIndent != null) {
while ((token = lineTokens.shift()) != null) {
const offset = offsets.get(token);
if (offset) {
offset.expectedIndent = expectedIndent;
}
}
}
const { line, column } = firstToken.loc.start;
return {
expectedIndent: lineExpectedIndent,
actualIndent: sourceCode.lines[line - 1].slice(0, column),
firstToken,
line,
};
}
function getExpectedIndent(token) {
const offset = offsets.get(token);
if (!offset) {
return null;
}
if (offset.expectedIndent != null) {
return offset.expectedIndent;
}
if (offset.baseToken == null) {
return null;
}
const baseIndent = getExpectedIndent(offset.baseToken);
if (baseIndent == null) {
return null;
}
const offsetIndent = offset.offset;
return (offset.expectedIndent = baseIndent + getIndentText(offsetIndent));
}
function processMissingLines(lineIndents) {
const results = [];
const commentLines = [];
for (const lineIndent of lineIndents) {
if (!lineIndent) {
continue;
}
const line = lineIndent.line;
if ((0, ast_utils_1.isCommentToken)(lineIndent.firstToken)) {
const last = commentLines[commentLines.length - 1];
if (last && last.range[1] === line - 1) {
last.range[1] = line;
last.commentLineIndents.push(lineIndent);
}
else {
commentLines.push({
range: [line, line],
commentLineIndents: [lineIndent],
});
}
}
else if (lineIndent.expectedIndent != null) {
const indent = {
line,
expectedIndent: lineIndent.expectedIndent,
actualIndent: lineIndent.actualIndent,
firstToken: lineIndent.firstToken,
};
if (!results[line]) {
results[line] = indent;
}
}
}
processComments(commentLines);
return results;
function processComments(commentLines) {
for (const { range, commentLineIndents } of commentLines) {
const prev = results
.slice(0, range[0])
.filter((data) => data)
.pop();
const next = results
.slice(range[1] + 1)
.filter((data) => data)
.shift();
const expectedIndents = [];
let either;
if (prev && next) {
expectedIndents.unshift(next.expectedIndent);
if (next.expectedIndent < prev.expectedIndent) {
let indent = next.expectedIndent + getIndentText(1);
while (indent.length <= prev.expectedIndent.length) {
expectedIndents.unshift(indent);
indent += getIndentText(1);
}
}
}
else if ((either = prev || next)) {
expectedIndents.unshift(either.expectedIndent);
if (!next) {
let indent = outdent(either.expectedIndent);
while (indent.length > 0) {
expectedIndents.push(indent);
indent = outdent(indent);
if (indent.length <= 0) {
expectedIndents.push(indent);
break;
}
}
}
}
if (!expectedIndents.length) {
continue;
}
let expectedIndent = expectedIndents[0];
for (const commentLineIndent of commentLineIndents) {
if (results[commentLineIndent.line]) {
continue;
}
const indentCandidate = expectedIndents.find((indent, index) => {
if (indent.length <= commentLineIndent.actualIndent.length) {
return true;
}
const prev = expectedIndents[index + 1]?.length ?? -1;
return (prev < commentLineIndent.actualIndent.length &&
commentLineIndent.actualIndent.length < indent.length);
});
if (indentCandidate != null &&
indentCandidate.length < expectedIndent.length) {
expectedIndent = indentCandidate;
}
results[commentLineIndent.line] = {
line: commentLineIndent.line,
expectedIndent,
actualIndent: commentLineIndent.actualIndent,
firstToken: commentLineIndent.firstToken,
};
}
}
}
}
function validateLines(lineIndents) {
for (const lineIndent of lineIndents) {
if (!lineIndent) {
continue;
}
if (lineIndent.actualIndent !== lineIndent.expectedIndent) {
const startLoc = {
line: lineIndent.line,
column: 0,
};
context.report({
loc: {
start: startLoc,
end: lineIndent.firstToken.loc.start,
},
messageId: "wrongIndentation",
data: getIndentData(lineIndent),
fix(fixer) {
return fixer.replaceTextRange([
sourceCode.getIndexFromLoc(startLoc),
lineIndent.firstToken.range[0],
], lineIndent.expectedIndent);
},
});
}
}
}
function getIndentData(lineIndent) {
return {
expected: toDisplayText(lineIndent.expectedIndent),
actual: toDisplayText(lineIndent.actualIndent),
};
function toDisplayText(indent) {
if (indent.length === 0) {
return "0 spaces";
}
const char = indent[0];
if (char === " " || char === "\t") {
let uni = true;
for (const c of indent) {
if (c !== char) {
uni = false;
}
}
if (uni) {
const unit = char === " " ? "spaces" : "tabs";
return `${indent.length} ${unit}`;
}
}
return `"${replaceToDisplay(indent)}"`;
}
function replaceToDisplay(indent) {
return indent.replace(/\s/gu, (c) => {
if (c === "\t")
return "\\t";
if (c === " ")
return " ";
const hex = c.codePointAt(0).toString(16);
return `\\u${`000${hex}`.slice(-4)}`;
});
}
}
},
});