From 6767857eb7545d2ce30213d25737d2cd64cfa763 Mon Sep 17 00:00:00 2001 From: Robin Houston Date: Wed, 11 Oct 2017 07:36:14 +0100 Subject: [PATCH] Update: fix ignored nodes in indent rule when using tabs (fixes #9392) (#9393) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a node is ignored by the indent rule, it ought not to matter how it’s indented. But the ignoring of nodes was implemented in such a way that the *type* of indentation (tabs vs spaces) was being checked. For example in "tab" mode, an ignored line indented by four spaces would cause the error “Expected indentation of 4 tabs but found 4 spaces”. In particular, this is a problem with “tabs for indentation, spaces for alignment” styles, where we want to allow code like: var x = 1, y = 2; where the second line is aligned using four spaces. The implementation is taken from not-an-aardvark’s comment https://github.com/eslint/eslint/pull/9393#pullrequestreview-67356950 All tests pass. Fixes #9392. --- lib/rules/indent.js | 40 +++++++++++++++++++++------------------ tests/lib/rules/indent.js | 26 +++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 18 deletions(-) diff --git a/lib/rules/indent.js b/lib/rules/indent.js index 0f6468a7e15..d33ed1ab92b 100644 --- a/lib/rules/indent.js +++ b/lib/rules/indent.js @@ -235,10 +235,12 @@ class OffsetStorage { /** * @param {TokenInfo} tokenInfo a TokenInfo instance * @param {number} indentSize The desired size of each indentation level + * @param {string} indentType The indentation character */ - constructor(tokenInfo, indentSize) { + constructor(tokenInfo, indentSize, indentType) { this._tokenInfo = tokenInfo; this._indentSize = indentSize; + this._indentType = indentType; this._tree = new BinarySearchTree(); this._tree.insert(0, { offset: 0, from: null, force: false }); @@ -408,7 +410,7 @@ class OffsetStorage { /** * Gets the desired indent of a token * @param {Token} token The token - * @returns {number} The desired indent of the token + * @returns {string} The desired indent of the token */ getDesiredIndent(token) { if (!this._desiredIndentCache.has(token)) { @@ -417,7 +419,10 @@ class OffsetStorage { // If the token is ignored, use the actual indent of the token as the desired indent. // This ensures that no errors are reported for this token. - this._desiredIndentCache.set(token, this._tokenInfo.getTokenIndent(token).length / this._indentSize); + this._desiredIndentCache.set( + token, + this._tokenInfo.getTokenIndent(token) + ); } else if (this._lockedFirstTokens.has(token)) { const firstToken = this._lockedFirstTokens.get(token); @@ -428,7 +433,7 @@ class OffsetStorage { this.getDesiredIndent(this._tokenInfo.getFirstTokenOfLine(firstToken)) + // (space between the start of the first element's line and the first element) - (firstToken.loc.start.column - this._tokenInfo.getFirstTokenOfLine(firstToken).loc.start.column) / this._indentSize + this._indentType.repeat(firstToken.loc.start.column - this._tokenInfo.getFirstTokenOfLine(firstToken).loc.start.column) ); } else { const offsetInfo = this._getOffsetDescriptor(token); @@ -436,9 +441,12 @@ class OffsetStorage { offsetInfo.from && offsetInfo.from.loc.start.line === token.loc.start.line && !offsetInfo.force - ) ? 0 : offsetInfo.offset; + ) ? 0 : offsetInfo.offset * this._indentSize; - this._desiredIndentCache.set(token, offset + (offsetInfo.from ? this.getDesiredIndent(offsetInfo.from) : 0)); + this._desiredIndentCache.set( + token, + (offsetInfo.from ? this.getDesiredIndent(offsetInfo.from) : "") + this._indentType.repeat(offset) + ); } } return this._desiredIndentCache.get(token); @@ -655,7 +663,7 @@ module.exports = { const sourceCode = context.getSourceCode(); const tokenInfo = new TokenInfo(sourceCode); - const offsets = new OffsetStorage(tokenInfo, indentSize); + const offsets = new OffsetStorage(tokenInfo, indentSize, indentType === "space" ? " " : "\t"); const parameterParens = new WeakSet(); /** @@ -688,27 +696,24 @@ module.exports = { /** * Reports a given indent violation * @param {Token} token Token violating the indent rule - * @param {int} neededIndentLevel Expected indentation level - * @param {int} gottenSpaces Actual number of indentation spaces for the token - * @param {int} gottenTabs Actual number of indentation tabs for the token + * @param {string} neededIndent Expected indentation string * @returns {void} */ - function report(token, neededIndentLevel) { + function report(token, neededIndent) { const actualIndent = Array.from(tokenInfo.getTokenIndent(token)); const numSpaces = actualIndent.filter(char => char === " ").length; const numTabs = actualIndent.filter(char => char === "\t").length; - const neededChars = neededIndentLevel * indentSize; context.report({ node: token, - message: createErrorMessage(neededChars, numSpaces, numTabs), + message: createErrorMessage(neededIndent.length, numSpaces, numTabs), loc: { start: { line: token.loc.start.line, column: 0 }, end: { line: token.loc.start.line, column: token.loc.start.column } }, fix(fixer) { const range = [token.range[0] - token.loc.start.column, token.range[0]]; - const newText = (indentType === "space" ? " " : "\t").repeat(neededChars); + const newText = neededIndent; return fixer.replaceTextRange(range, newText); } @@ -718,14 +723,13 @@ module.exports = { /** * Checks if a token's indentation is correct * @param {Token} token Token to examine - * @param {int} desiredIndentLevel needed indent level + * @param {string} desiredIndent Desired indentation of the string * @returns {boolean} `true` if the token's indentation is correct */ - function validateTokenIndent(token, desiredIndentLevel) { + function validateTokenIndent(token, desiredIndent) { const indentation = tokenInfo.getTokenIndent(token); - const expectedChar = indentType === "space" ? " " : "\t"; - return indentation === expectedChar.repeat(desiredIndentLevel * indentSize) || + return indentation === desiredIndent || // To avoid conflicts with no-mixed-spaces-and-tabs, don't report mixed spaces and tabs. indentation.includes(" ") && indentation.includes("\t"); diff --git a/tests/lib/rules/indent.js b/tests/lib/rules/indent.js index 006d7f1dd67..0a7718492f1 100644 --- a/tests/lib/rules/indent.js +++ b/tests/lib/rules/indent.js @@ -4744,6 +4744,32 @@ ruleTester.run("indent", rule, { `, options: [4, { ignoredNodes: ["JSXOpeningElement"] }] + }, + { + code: unIndent` + { + \tvar x = 1, + \t y = 2; + } + `, + options: ["tab"] + }, + { + code: unIndent` + var x = 1, + y = 2; + var z; + `, + options: ["tab", { ignoredNodes: ["VariableDeclarator"] }] + }, + { + code: unIndent` + [ + foo(), + bar + ] + `, + options: ["tab", { ArrayExpression: "first", ignoredNodes: ["CallExpression"] }] } ],