Skip to content

Commit 4967db7

Browse files
committed
rewrite isSpaceBetween()
1 parent a719672 commit 4967db7

File tree

1 file changed

+51
-38
lines changed

1 file changed

+51
-38
lines changed

apps/oxlint/src-js/plugins/tokens.ts

Lines changed: 51 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
*/
44

55
import { createRequire } from "node:module";
6-
import { sourceText, initSourceText } from "./source_code.js";
6+
import { sourceText } from "./source_code.js";
77
import { debugAssert, debugAssertIsNonNull } from "../utils/asserts.js";
88

99
import type { Comment, Node, NodeOrToken } from "./types.ts";
@@ -1699,10 +1699,6 @@ export function getTokenByRangeStart(
16991699
return null;
17001700
}
17011701

1702-
// Regex that tests for whitespace.
1703-
// TODO: Is this too liberal? Should it be a more constrained set of whitespace characters?
1704-
const WHITESPACE_REGEXP = /\s/;
1705-
17061702
/**
17071703
* Determine if two nodes or tokens have at least one whitespace character between them.
17081704
* Order does not matter.
@@ -1717,47 +1713,64 @@ const WHITESPACE_REGEXP = /\s/;
17171713
* Note: `checkInsideOfJSXText === false` in ESLint's implementation of `sourceCode.isSpaceBetween`. hmmmmmm
17181714
* https://github.com/eslint/eslint/blob/523c076866400670fb2192a3f55dbf7ad3469247/lib/languages/js/source-code/source-code.js#L182-L230
17191715
*
1720-
* @param nodeOrToken1 - The first node or token to check between.
1721-
* @param nodeOrToken2 - The second node or token to check between.
1716+
* @param first - The first node or token to check between.
1717+
* @param second - The second node or token to check between.
17221718
* @returns `true` if there is a whitespace character between
17231719
* any of the tokens found between the two given nodes or tokens.
17241720
*/
1725-
export function isSpaceBetween(nodeOrToken1: NodeOrToken, nodeOrToken2: NodeOrToken): boolean {
1726-
const range1 = nodeOrToken1.range,
1727-
range2 = nodeOrToken2.range,
1728-
start1 = range1[0],
1729-
start2 = range2[0];
1730-
1731-
// Find the gap between the two nodes/tokens.
1732-
//
1733-
// 1 node/token can completely enclose another, but they can't *partially* overlap.
1734-
// ```
1735-
// Possible:
1736-
// |------------|
1737-
// |------|
1738-
//
1739-
// Impossible:
1740-
// |------------|
1741-
// |------------|
1742-
// ```
1743-
// We use that invariant to reduce this to a single branch.
1744-
let gapStart, gapEnd;
1745-
if (start1 < start2) {
1746-
gapStart = range1[1]; // end1
1747-
gapEnd = start2;
1721+
export function isSpaceBetween(first: NodeOrToken, second: NodeOrToken): boolean {
1722+
if (tokens === null) initTokens();
1723+
if (tokensWithComments === null) initTokensWithComments();
1724+
debugAssertIsNonNull(tokensWithComments);
1725+
1726+
const tokensWithCommentsLength = tokensWithComments.length,
1727+
range1 = first.range,
1728+
range2 = second.range;
1729+
1730+
// The "between" range0
1731+
let rangeStart: number, rangeEnd: number;
1732+
1733+
// Unlike other methods which require the user to pass the nodes in order of appearance,
1734+
// `isSpaceBetween()` is invariant over the sequence of the of the two nodes
1735+
if (range1[0] < range2[0]) {
1736+
rangeStart = range1[1];
1737+
rangeEnd = range2[0];
17481738
} else {
1749-
gapStart = range2[1]; // end2;
1750-
gapEnd = start1;
1739+
rangeStart = range2[1];
1740+
rangeEnd = range1[0];
17511741
}
17521742

1753-
// If `gapStart >= gapEnd`, one node encloses the other, or the two are directly adjacent
1754-
if (gapStart >= gapEnd) return false;
1743+
// Binary search for the first token past `rangeStart`
1744+
// Unless `first` and `second` are adjacent or overlapping,
1745+
// the token will be the first token between the two nodes
1746+
let tokenBetweenIndex = tokensWithCommentsLength;
1747+
for (let lo = 0; lo < tokenBetweenIndex; ) {
1748+
const mid = (lo + tokenBetweenIndex) >> 1;
1749+
if (tokensWithComments[mid].range[0] < rangeStart) {
1750+
lo = mid + 1;
1751+
} else {
1752+
tokenBetweenIndex = mid;
1753+
}
1754+
}
17551755

1756-
// Check if there's any whitespace in the gap
1757-
if (sourceText === null) initSourceText();
1758-
debugAssertIsNonNull(sourceText);
1756+
for (
1757+
let lastTokenEnd = rangeStart;
1758+
tokenBetweenIndex < tokensWithCommentsLength;
1759+
tokenBetweenIndex++
1760+
) {
1761+
const tokenRange = tokensWithComments[tokenBetweenIndex].range;
1762+
const tokenStart = tokenRange[0];
1763+
// The first token of the later node should undergo the check in the second branch
1764+
if (tokenStart > rangeEnd) {
1765+
break;
1766+
} else if (tokenStart !== lastTokenEnd) {
1767+
return true;
1768+
} else {
1769+
lastTokenEnd = tokenRange[1];
1770+
}
1771+
}
17591772

1760-
return WHITESPACE_REGEXP.test(sourceText.slice(gapStart, gapEnd));
1773+
return false;
17611774
}
17621775

17631776
/**

0 commit comments

Comments
 (0)