Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix: sourceCode#isSpaceBetweenTokens() checks non-adjacent tokens #12491

Merged
merged 4 commits into from Nov 7, 2019
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
28 changes: 19 additions & 9 deletions lib/source-code/source-code.js
Expand Up @@ -411,19 +411,29 @@ class SourceCode extends TokenStore {
}

/**
* Determines if two tokens have at least one whitespace character
* between them. This completely disregards comments in making the
* determination, so comments count as zero-length substrings.
* @param {Token} first The token to check after.
* @param {Token} second The token to check before.
* @returns {boolean} True if there is only space between tokens, false
* if there is anything other than whitespace between tokens.
* Determines if two nodes or tokens have at least one whitespace character
* between them.
* @param {ASTNode|Token} first The node or token to check after.
* @param {ASTNode|Token} second The node or token to check before.
* @returns {boolean} True if there is a whitespace character between
* any of the tokens found between the two given nodes or tokens.
* @public
*/
isSpaceBetweenTokens(first, second) {
const text = this.text.slice(first.range[1], second.range[0]);
const finalToken = this.getFirstToken(second) || second;
let currentToken = this.getLastToken(first) || first;

return /\s/u.test(text.replace(/\/\*.*?\*\//gus, ""));
while (currentToken !== finalToken) {
mysticatea marked this conversation as resolved.
Show resolved Hide resolved
const nextToken = this.getTokenAfter(currentToken, { includeComments: true });

if (currentToken.range[1] !== nextToken.range[0]) {
return true;
}

currentToken = nextToken;
}

return false;
}

/**
Expand Down
217 changes: 193 additions & 24 deletions tests/lib/source-code/source-code.js
Expand Up @@ -1790,32 +1790,201 @@ describe("SourceCode", () => {
});

describe("isSpaceBetweenTokens()", () => {
describe("should return true when there is at least one whitespace character between two tokens", () => {
leche.withData([
["let foo", true],
["let foo", true],
["let /**/ foo", true],
["let/**/foo", false],
["let/*\n*/foo", false]
], (code, expected) => {
it(code, () => {
const ast = espree.parse(code, DEFAULT_CONFIG),
sourceCode = new SourceCode(code, ast);

assert.strictEqual(
sourceCode.isSpaceBetweenTokens(
sourceCode.ast.tokens[0], sourceCode.ast.tokens[sourceCode.ast.tokens.length - 1]
),
expected
);
});
});

leche.withData([
["let foo = bar;", true],
["let foo = bar;", true],
["let /**/ foo = bar;", true],
["let/**/foo = bar;", false],
["let/*\n*/foo = bar", false],
["a+b", false],
["a/**/+b", false],
["a/* */+b", false],
["a/**/ +b", true],
["a/**/ /**/+b", true],
["a/**/\n/**/+b", true],
["a +b", true]
], (code, expected) => {

it("should return true when there is one space between tokens", () => {
const ast = espree.parse(code, DEFAULT_CONFIG),
sourceCode = new SourceCode(code, ast);
leche.withData([
["a+b", false],
["a +b", true],
["a/**/+b", false],
["a/* */+b", false],
["a/**/ +b", true],
["a/**/ /**/+b", true],
["a/* */ /* */+b", true],
["a/**/\n/**/+b", true],
["a/* */\n/* */+b", true],
["a/**/+b/**/+c", false],
["a/* */+b/* */+c", false],
["a/**/+b /**/+c", true],
["a/* */+b /* */+c", true],
["a/**/ +b/**/+c", true],
["a/* */ +b/* */+c", true],
["a/**/+b\t/**/+c", true],
["a/* */+b\t/* */+c", true],
["a/**/\t+b/**/+c", true],
["a/* */\t+b/* */+c", true],
["a/**/+b\n/**/+c", true],
["a/* */+b\n/* */+c", true],
["a/**/\n+b/**/+c", true],
["a/* */\n+b/* */+c", true],
["a/* */+' /**/ '/* */+c", false],
["a/* */+ ' /**/ '/* */+c", true],
["a/* */+' /**/ ' /* */+c", true],
["a/* */+ ' /**/ ' /* */+c", true],
["a/* */+` /*\n*/ `/* */+c", false],
["a/* */+ ` /*\n*/ `/* */+c", true],
["a/* */+` /*\n*/ ` /* */+c", true],
["a/* */+ ` /*\n*/ ` /* */+c", true]
], (code, expected) => {
it(code, () => {
const ast = espree.parse(code, DEFAULT_CONFIG),
sourceCode = new SourceCode(code, ast);

assert.strictEqual(
sourceCode.isSpaceBetweenTokens(
sourceCode.ast.tokens[0], sourceCode.ast.tokens[sourceCode.ast.tokens.length - 2]
),
expected
);
});
});
});

assert.strictEqual(
sourceCode.isSpaceBetweenTokens(
sourceCode.ast.tokens[0], sourceCode.ast.tokens[1]
),
expected
);
describe("should return true when there is at least one whitespace character between a token and a node", () => {
leche.withData([
[";let foo = bar", false],
[";/**/let foo = bar", false],
[";/* */let foo = bar", false],
["; let foo = bar", true],
["; let foo = bar", true],
["; /**/let foo = bar", true],
["; /* */let foo = bar", true],
[";/**/ let foo = bar", true],
[";/* */ let foo = bar", true],
["; /**/ let foo = bar", true],
["; /* */ let foo = bar", true],
[";\tlet foo = bar", true],
[";\tlet foo = bar", true],
[";\t/**/let foo = bar", true],
[";\t/* */let foo = bar", true],
[";/**/\tlet foo = bar", true],
[";/* */\tlet foo = bar", true],
[";\t/**/\tlet foo = bar", true],
[";\t/* */\tlet foo = bar", true],
[";\nlet foo = bar", true],
[";\nlet foo = bar", true],
[";\n/**/let foo = bar", true],
[";\n/* */let foo = bar", true],
[";/**/\nlet foo = bar", true],
[";/* */\nlet foo = bar", true],
[";\n/**/\nlet foo = bar", true],
[";\n/* */\nlet foo = bar", true]
], (code, expected) => {
it(code, () => {
const ast = espree.parse(code, DEFAULT_CONFIG),
sourceCode = new SourceCode(code, ast);

assert.strictEqual(
sourceCode.isSpaceBetweenTokens(
sourceCode.ast.tokens[0], sourceCode.ast.body[sourceCode.ast.body.length - 1]
),
expected
);
});
});
});

describe("should return true when there is at least one whitespace character between a node and a token", () => {
leche.withData([
["let foo = bar;;", false],
["let foo = bar;;;", false],
["let foo = 1; let bar = 2;;", true],
["let foo = bar;/**/;", false],
["let foo = bar;/* */;", false],
["let foo = bar;;;", false],
["let foo = bar; ;", true],
["let foo = bar; /**/;", true],
["let foo = bar; /* */;", true],
["let foo = bar;/**/ ;", true],
["let foo = bar;/* */ ;", true],
["let foo = bar; /**/ ;", true],
["let foo = bar; /* */ ;", true],
["let foo = bar;\t;", true],
["let foo = bar;\t/**/;", true],
["let foo = bar;\t/* */;", true],
["let foo = bar;/**/\t;", true],
["let foo = bar;/* */\t;", true],
["let foo = bar;\t/**/\t;", true],
["let foo = bar;\t/* */\t;", true],
["let foo = bar;\n;", true],
["let foo = bar;\n/**/;", true],
["let foo = bar;\n/* */;", true],
["let foo = bar;/**/\n;", true],
["let foo = bar;/* */\n;", true],
["let foo = bar;\n/**/\n;", true],
["let foo = bar;\n/* */\n;", true]
], (code, expected) => {
it(code, () => {
const ast = espree.parse(code, DEFAULT_CONFIG),
sourceCode = new SourceCode(code, ast);

assert.strictEqual(
sourceCode.isSpaceBetweenTokens(
sourceCode.ast.body[0], sourceCode.ast.tokens[sourceCode.ast.tokens.length - 1]
),
expected
);
});
});
});

describe("should return true when there is at least one whitespace character between two nodes", () => {
leche.withData([
["let foo = bar;let baz = qux;", false],
["let foo = bar;/**/let baz = qux;", false],
["let foo = bar;/* */let baz = qux;", false],
["let foo = bar; let baz = qux;", true],
["let foo = bar; /**/let baz = qux;", true],
["let foo = bar; /* */let baz = qux;", true],
["let foo = bar;/**/ let baz = qux;", true],
["let foo = bar;/* */ let baz = qux;", true],
["let foo = bar; /**/ let baz = qux;", true],
["let foo = bar; /* */ let baz = qux;", true],
["let foo = bar;\tlet baz = qux;", true],
["let foo = bar;\t/**/let baz = qux;", true],
["let foo = bar;\t/* */let baz = qux;", true],
["let foo = bar;/**/\tlet baz = qux;", true],
["let foo = bar;/* */\tlet baz = qux;", true],
["let foo = bar;\t/**/\tlet baz = qux;", true],
["let foo = bar;\t/* */\tlet baz = qux;", true],
["let foo = bar;\nlet baz = qux;", true],
["let foo = bar;\n/**/let baz = qux;", true],
["let foo = bar;\n/* */let baz = qux;", true],
["let foo = bar;/**/\nlet baz = qux;", true],
["let foo = bar;/* */\nlet baz = qux;", true],
["let foo = bar;\n/**/\nlet baz = qux;", true],
["let foo = bar;\n/* */\nlet baz = qux;", true],
["let foo = 1;let foo2 = 2; let foo3 = 3;", true]
], (code, expected) => {
it(code, () => {
const ast = espree.parse(code, DEFAULT_CONFIG),
sourceCode = new SourceCode(code, ast);

assert.strictEqual(
sourceCode.isSpaceBetweenTokens(
sourceCode.ast.body[0], sourceCode.ast.body[sourceCode.ast.body.length - 1]
),
expected
);
});
});
});
});
Expand Down