eslint-plugin-svelte
Version:
ESLint plugin for Svelte using AST
221 lines (220 loc) • 7.41 kB
JavaScript
import { isNotWhitespace } from './ast.js';
import { isBeginningOfLine } from './commons.js';
import { getFirstAndLastTokens } from './commons.js';
export class OffsetContext {
constructor(arg) {
this.offsets = new Map();
this.ignoreRanges = new Map();
this.sourceCode = arg.sourceCode;
this.options = arg.options;
}
/**
* Set offset to the given index.
*/
setOffsetIndex(index, offset, base) {
if (index === base) {
return;
}
this.offsets.set(index, {
type: 0 /* OffsetDataType.normal */,
base,
offset
});
}
/**
* Set align indent to the given index.
*/
setAlignIndent(index, alignIndent, base) {
if (index === base) {
return;
}
this.offsets.set(index, {
type: 1 /* OffsetDataType.align */,
base,
alignIndent
});
}
/**
* Set offset to the given tokens.
*/
setOffsetToken(token, offset, baseToken) {
if (!token) {
return;
}
if (Array.isArray(token)) {
for (const t of token) {
this.setOffsetToken(t, offset, baseToken);
}
return;
}
this.setOffsetIndex(token.range[0], offset, baseToken.range[0]);
}
/**
* Copy offset to the given index from srcIndex.
*/
copyOffset(index, srcIndex) {
const offsetData = this.offsets.get(srcIndex);
if (!offsetData) {
return;
}
if (offsetData.type === 2 /* OffsetDataType.start */) {
this.setStartOffsetIndex(index, offsetData.offset);
}
else if (offsetData.type === 1 /* OffsetDataType.align */) {
this.setAlignIndent(index, offsetData.alignIndent, offsetData.base);
}
else {
this.setOffsetIndex(index, offsetData.offset, offsetData.base);
}
}
/**
* Set start offset to the given index.
*/
setStartOffsetIndex(index, offset) {
this.offsets.set(index, {
type: 2 /* OffsetDataType.start */,
offset
});
}
/**
* Set start offset to the given token.
*/
setStartOffsetToken(token, offset) {
if (!token) {
return;
}
if (Array.isArray(token)) {
for (const t of token) {
this.setStartOffsetToken(t, offset);
}
return;
}
this.setStartOffsetIndex(token.range[0], offset);
}
setOffsetElementList(nodes, baseNodeOrToken, lastNodeOrToken, offset, align) {
let setIndent = (token, baseToken) => this.setOffsetToken(token, offset, baseToken);
if (align) {
for (const n of nodes) {
if (n) {
if (!isBeginningOfLine(this.sourceCode, n)) {
const startLoc = n.loc.start;
const alignIndent = startLoc.column - /^\s*/u.exec(this.sourceCode.lines[startLoc.line - 1])[0].length;
setIndent = (token, baseToken) => this.setAlignIndent(token.range[0], alignIndent, baseToken.range[0]);
}
break;
}
}
}
this._setOffsetElementList(nodes, baseNodeOrToken, lastNodeOrToken, setIndent);
}
_setOffsetElementList(nodes, baseNodeOrToken, lastNodeOrToken, setIndent) {
const baseToken = this.sourceCode.getFirstToken(baseNodeOrToken);
let prevToken = this.sourceCode.getLastToken(baseNodeOrToken);
for (const node of nodes) {
if (node == null) {
continue;
}
const elementTokens = getFirstAndLastTokens(this.sourceCode, node, prevToken.range[1]);
let t = prevToken;
while ((t = this.sourceCode.getTokenAfter(t, {
includeComments: true,
filter: isNotWhitespace
})) != null &&
t.range[1] <= elementTokens.firstToken.range[0]) {
setIndent(t, baseToken);
}
setIndent(elementTokens.firstToken, baseToken);
prevToken = elementTokens.lastToken;
}
if (lastNodeOrToken) {
const lastToken = this.sourceCode.getFirstToken(lastNodeOrToken);
let t = prevToken;
while ((t = this.sourceCode.getTokenAfter(t, {
includeComments: true,
filter: isNotWhitespace
})) != null &&
t.range[1] <= lastToken.range[0]) {
setIndent(t, baseToken);
}
this.setOffsetToken(lastToken, 0, baseToken);
}
}
/**
* Ignore range of the given node.
*/
ignore(node) {
const range = node.range;
const n = this.ignoreRanges.get(range[0]) ?? 0;
this.ignoreRanges.set(range[0], Math.max(n, range[1]));
}
getOffsetCalculator() {
// eslint-disable-next-line @typescript-eslint/no-use-before-define -- ignore
return new OffsetCalculator({
offsets: this.offsets,
options: this.options,
ignoreRanges: [...this.ignoreRanges]
});
}
}
export class OffsetCalculator {
constructor(arg) {
this.offsets = arg.offsets;
this.options = arg.options;
this.ignoreRanges = arg.ignoreRanges;
}
/**
* Calculate correct indentation of the given index.
*/
getExpectedIndentFromIndex(index) {
const offsetInfo = this.offsets.get(index);
if (offsetInfo == null) {
return null;
}
if (offsetInfo.expectedIndent != null) {
return offsetInfo.expectedIndent;
}
if (offsetInfo.type === 2 /* OffsetDataType.start */) {
return offsetInfo.offset * this.options.indentSize;
}
const baseIndent = this.getExpectedIndentFromIndex(offsetInfo.base);
if (baseIndent == null) {
return null;
}
if (offsetInfo.type === 1 /* OffsetDataType.align */) {
return baseIndent + offsetInfo.alignIndent;
}
return baseIndent + offsetInfo.offset * this.options.indentSize;
}
/**
* Calculate correct indentation of the given token.
*/
getExpectedIndentFromToken(token) {
return this.getExpectedIndentFromIndex(token.range[0]);
}
/**
* Calculate correct indentation of the line of the given tokens.
*/
getExpectedIndentFromTokens(tokens) {
for (const token of tokens) {
const index = token.range[0];
if (this.ignoreRanges.some(([f, t]) => f <= index && index < t)) {
return null;
}
const expectedIndent = this.getExpectedIndentFromIndex(index);
if (expectedIndent != null) {
return expectedIndent;
}
}
return null;
}
/** Save expected indent to give tokens */
saveExpectedIndent(tokens, expectedIndent) {
for (const token of tokens) {
const offsetInfo = this.offsets.get(token.range[0]);
if (offsetInfo == null) {
continue;
}
offsetInfo.expectedIndent = offsetInfo.expectedIndent ?? expectedIndent;
}
}
}