Skip to content

Commit 4035975

Browse files
committed
feat(linter/plugins): implement SourceCode#getTokenByRangeStart()
1 parent e2ca770 commit 4035975

File tree

2 files changed

+65
-6
lines changed

2 files changed

+65
-6
lines changed

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

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1115,9 +1115,40 @@ export function getLastTokensBetween(
11151115
* @param rangeOptions - Options object.
11161116
* @returns The token starting at index, or `null` if no such token.
11171117
*/
1118-
// oxlint-disable-next-line no-unused-vars
11191118
export function getTokenByRangeStart(index: number, rangeOptions?: RangeOptions | null): Token | null {
1120-
throw new Error('`sourceCode.getTokenByRangeStart` not implemented yet'); // TODO
1119+
if (tokens === null) initTokens();
1120+
debugAssertIsNonNull(tokens);
1121+
debugAssertIsNonNull(comments);
1122+
1123+
const includeComments =
1124+
typeof rangeOptions === 'object' &&
1125+
rangeOptions !== null &&
1126+
'includeComments' in rangeOptions &&
1127+
rangeOptions.includeComments;
1128+
1129+
let nodeTokens: Token[] | null = null;
1130+
if (includeComments) {
1131+
if (tokensWithComments === null) initTokensWithComments();
1132+
debugAssertIsNonNull(tokensWithComments);
1133+
nodeTokens = tokensWithComments;
1134+
} else {
1135+
nodeTokens = tokens;
1136+
}
1137+
1138+
// Binary search for the token that starts at the given index
1139+
for (let lo = 0, hi = nodeTokens.length; lo < hi; ) {
1140+
const mid = (lo + hi) >> 1;
1141+
const tokenStart = nodeTokens[mid].range[0];
1142+
if (tokenStart === index) {
1143+
return nodeTokens[mid];
1144+
} else if (tokenStart < index) {
1145+
lo = mid + 1;
1146+
} else {
1147+
hi = mid;
1148+
}
1149+
}
1150+
1151+
return null;
11211152
}
11221153

11231154
// Regex that tests for whitespace.

apps/oxlint/test/tokens.test.ts

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -755,11 +755,39 @@ describe('when calling getTokensBetween', () => {
755755
getTokensBetween;
756756
});
757757

758+
// https://github.com/eslint/eslint/blob/v9.39.1/tests/lib/languages/js/source-code/token-store.js#L1526-L1562
758759
describe('when calling getTokenByRangeStart', () => {
759-
/* oxlint-disable-next-line no-disabled-tests expect-expect */
760-
it('is to be implemented');
761-
/* oxlint-disable-next-line no-unused-expressions */
762-
getTokenByRangeStart;
760+
it('should return identifier token', () => {
761+
const { type, value } = getTokenByRangeStart(9)!;
762+
763+
expect(type).toEqual('Identifier');
764+
expect(value).toEqual('answer');
765+
});
766+
767+
it("should return null when token doesn't exist", () => {
768+
expect(getTokenByRangeStart(10)).toBeNull();
769+
});
770+
771+
it('should return a comment token when includeComments is true', () => {
772+
const { type, value } = getTokenByRangeStart(15, {
773+
includeComments: true,
774+
})!;
775+
776+
expect(type).toEqual('Block');
777+
expect(value).toEqual('B');
778+
});
779+
780+
it('should not return a comment token at the supplied index when includeComments is false', () => {
781+
expect(
782+
getTokenByRangeStart(15, {
783+
includeComments: false,
784+
}),
785+
).toBeNull();
786+
});
787+
788+
it('should not return comment tokens by default', () => {
789+
expect(getTokenByRangeStart(15)).toBeNull();
790+
});
763791
});
764792

765793
describe('when calling getTokenOrCommentBefore', () => {

0 commit comments

Comments
 (0)