Commit 4f4dfe05 authored by David Sveningsson's avatar David Sveningsson
Browse files

feat(dom): `DOMTokenList` can extract location data for each token

fixes #74
parent 9895dc5e
import { Location } from "../context";
import { DOMTokenList } from "./domtokenlist";
import { DynamicValue } from "./dynamic-value";
......@@ -67,6 +68,55 @@ describe("DOMTokenList", () => {
});
});
describe("location()", () => {
const location: Location = {
filename: "mock",
line: 1,
column: 1,
offset: 0,
size: 11,
};
it("should return location by index", () => {
expect.assertions(3);
const list = new DOMTokenList("foo bar baz", location);
expect(list.location(0)).toEqual({
filename: "mock",
line: 1,
column: 1,
offset: 0,
size: 3,
});
expect(list.location(1)).toEqual({
filename: "mock",
line: 1,
column: 5,
offset: 4,
size: 3,
});
expect(list.location(2)).toEqual({
filename: "mock",
line: 1,
column: 9,
offset: 8,
size: 3,
});
});
it("should return undefined if out of range", () => {
expect.assertions(2);
const list = new DOMTokenList("foo bar baz", location);
expect(list.location(-1)).toBeUndefined();
expect(list.location(3)).toBeUndefined();
});
it("should throw error when accessing location without base location", () => {
expect.assertions(1);
const list = new DOMTokenList("foo");
expect(() => list.location(0)).toThrow();
});
});
describe("contains()", () => {
it("should return true if token exists", () => {
expect.assertions(1);
......
import { Location, sliceLocation } from "../context";
import { DynamicValue } from "./dynamic-value";
interface Result {
tokens: string[];
locations: Location[];
}
function parse(text: string, baseLocation: Location): Result {
const tokens: string[] = [];
const locations: Location[] | undefined = baseLocation ? [] : undefined;
for (let begin = 0; begin < text.length; ) {
let end = text.indexOf(" ", begin);
/* if the last space was found move the position to the last character
* in the string */
if (end === -1) {
end = text.length;
}
/* handle multiple spaces */
const size = end - begin;
if (size === 0) {
begin++;
continue;
}
/* extract token */
const token = text.substring(begin, end);
tokens.push(token);
/* extract location */
if (baseLocation) {
const location = sliceLocation(baseLocation, begin, end);
locations.push(location);
}
/* advance position to the character after the current end position */
begin += size + 1;
}
return { tokens, locations };
}
export class DOMTokenList extends Array<string> {
public readonly value: string;
private readonly locations: Location[] | undefined;
public constructor(value: string | DynamicValue) {
public constructor(value: string | DynamicValue, location?: Location) {
if (value && typeof value === "string") {
super(...value.trim().split(/ +/));
const { tokens, locations } = parse(value, location);
super(...tokens);
this.locations = locations;
} else {
super(0);
}
......@@ -17,10 +61,20 @@ export class DOMTokenList extends Array<string> {
}
}
public item(n: number): string {
public item(n: number): string | undefined {
return this[n];
}
public location(n: number): Location | undefined {
if (this.locations) {
return this.locations[n];
} else {
throw new Error(
"Trying to access DOMTokenList location when base location isn't set"
);
}
}
public contains(token: string): boolean {
return this.indexOf(token) >= 0;
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment