New meaning of negative offsets (breaking change)
Instead of always being relative to interval end, negative offsets are now relative to next comment (or the interval end, when there is no next comment).
This commit is contained in:
parent
574272602f
commit
93684069f0
|
|
@ -555,17 +555,13 @@ Rest: 5:00 50%
|
||||||
`);
|
`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("parses intervals with negative comment offsets", () => {
|
it("parses last comment with negative offset", () => {
|
||||||
expect(
|
expect(
|
||||||
parse(`
|
parse(`
|
||||||
Name: My Workout
|
Name: My Workout
|
||||||
Interval: 10:00 90%
|
Interval: 10:00 90%
|
||||||
@ 0:10 Find your rythm.
|
@ 0:10 Find your rythm.
|
||||||
@ -0:10 Final push. YOU GOT IT!
|
@ -0:10 Final push. YOU GOT IT!
|
||||||
|
|
||||||
Rest: 5:00 50%
|
|
||||||
@ -4:30 Great effort!
|
|
||||||
@ -2:00 Cool down well after all of this.
|
|
||||||
`),
|
`),
|
||||||
).toMatchInlineSnapshot(`
|
).toMatchInlineSnapshot(`
|
||||||
Object {
|
Object {
|
||||||
|
|
@ -604,37 +600,120 @@ Rest: 5:00 50%
|
||||||
},
|
},
|
||||||
"type": "Interval",
|
"type": "Interval",
|
||||||
},
|
},
|
||||||
|
],
|
||||||
|
"name": "My Workout",
|
||||||
|
"tags": Array [],
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("parses comment with negative offset before absolutely offset comment", () => {
|
||||||
|
expect(
|
||||||
|
parse(`
|
||||||
|
Name: My Workout
|
||||||
|
Interval: 1:00 90%
|
||||||
|
@ -0:10 Before last
|
||||||
|
@ 0:50 Last!
|
||||||
|
`),
|
||||||
|
).toMatchInlineSnapshot(`
|
||||||
|
Object {
|
||||||
|
"author": "",
|
||||||
|
"description": "",
|
||||||
|
"intervals": Array [
|
||||||
Object {
|
Object {
|
||||||
"cadence": undefined,
|
"cadence": undefined,
|
||||||
"comments": Array [
|
"comments": Array [
|
||||||
Object {
|
Object {
|
||||||
"loc": Object {
|
"loc": Object {
|
||||||
"col": 4,
|
"col": 4,
|
||||||
"row": 7,
|
"row": 3,
|
||||||
},
|
},
|
||||||
"offset": Duration {
|
"offset": Duration {
|
||||||
"seconds": 30,
|
"seconds": 40,
|
||||||
},
|
},
|
||||||
"text": "Great effort!",
|
"text": "Before last",
|
||||||
},
|
},
|
||||||
Object {
|
Object {
|
||||||
"loc": Object {
|
"loc": Object {
|
||||||
"col": 4,
|
"col": 4,
|
||||||
"row": 8,
|
"row": 4,
|
||||||
},
|
},
|
||||||
"offset": Duration {
|
"offset": Duration {
|
||||||
"seconds": 180,
|
"seconds": 50,
|
||||||
},
|
},
|
||||||
"text": "Cool down well after all of this.",
|
"text": "Last!",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"duration": Duration {
|
"duration": Duration {
|
||||||
"seconds": 300,
|
"seconds": 60,
|
||||||
},
|
},
|
||||||
"intensity": ConstantIntensity {
|
"intensity": ConstantIntensity {
|
||||||
"_value": 0.5,
|
"_value": 0.9,
|
||||||
},
|
},
|
||||||
"type": "Rest",
|
"type": "Interval",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"name": "My Workout",
|
||||||
|
"tags": Array [],
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("parses multiple comments with negative offsets in row", () => {
|
||||||
|
expect(
|
||||||
|
parse(`
|
||||||
|
Name: My Workout
|
||||||
|
Interval: 1:00 90%
|
||||||
|
@ -0:10 One more before last
|
||||||
|
@ -0:10 Before last
|
||||||
|
@ -0:10 Last!
|
||||||
|
`),
|
||||||
|
).toMatchInlineSnapshot(`
|
||||||
|
Object {
|
||||||
|
"author": "",
|
||||||
|
"description": "",
|
||||||
|
"intervals": Array [
|
||||||
|
Object {
|
||||||
|
"cadence": undefined,
|
||||||
|
"comments": Array [
|
||||||
|
Object {
|
||||||
|
"loc": Object {
|
||||||
|
"col": 4,
|
||||||
|
"row": 3,
|
||||||
|
},
|
||||||
|
"offset": Duration {
|
||||||
|
"seconds": 30,
|
||||||
|
},
|
||||||
|
"text": "One more before last",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"loc": Object {
|
||||||
|
"col": 4,
|
||||||
|
"row": 4,
|
||||||
|
},
|
||||||
|
"offset": Duration {
|
||||||
|
"seconds": 40,
|
||||||
|
},
|
||||||
|
"text": "Before last",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"loc": Object {
|
||||||
|
"col": 4,
|
||||||
|
"row": 5,
|
||||||
|
},
|
||||||
|
"offset": Duration {
|
||||||
|
"seconds": 50,
|
||||||
|
},
|
||||||
|
"text": "Last!",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"duration": Duration {
|
||||||
|
"seconds": 60,
|
||||||
|
},
|
||||||
|
"intensity": ConstantIntensity {
|
||||||
|
"_value": 0.9,
|
||||||
|
},
|
||||||
|
"type": "Interval",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"name": "My Workout",
|
"name": "My Workout",
|
||||||
|
|
@ -780,6 +859,70 @@ Interval: 10:00 90%
|
||||||
`);
|
`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("throws error when negative offset is followed by positive offset", () => {
|
||||||
|
expect(() =>
|
||||||
|
parse(`
|
||||||
|
Name: My Workout
|
||||||
|
Interval: 2:00 90%
|
||||||
|
@ -0:10 Comment 1
|
||||||
|
@ +0:30 Comment 2
|
||||||
|
@ 1:30 Comment 3
|
||||||
|
`),
|
||||||
|
).toThrowErrorMatchingInlineSnapshot(`"Negative offset followed by positive offset at line 5 char 5"`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("works fine when positive offset is followed by negative offset", () => {
|
||||||
|
expect(
|
||||||
|
parse(`
|
||||||
|
Name: My Workout
|
||||||
|
Interval: 1:00 90%
|
||||||
|
@ +0:10 Comment 1
|
||||||
|
@ -0:10 Comment 2
|
||||||
|
`),
|
||||||
|
).toMatchInlineSnapshot(`
|
||||||
|
Object {
|
||||||
|
"author": "",
|
||||||
|
"description": "",
|
||||||
|
"intervals": Array [
|
||||||
|
Object {
|
||||||
|
"cadence": undefined,
|
||||||
|
"comments": Array [
|
||||||
|
Object {
|
||||||
|
"loc": Object {
|
||||||
|
"col": 4,
|
||||||
|
"row": 3,
|
||||||
|
},
|
||||||
|
"offset": Duration {
|
||||||
|
"seconds": 10,
|
||||||
|
},
|
||||||
|
"text": "Comment 1",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"loc": Object {
|
||||||
|
"col": 4,
|
||||||
|
"row": 4,
|
||||||
|
},
|
||||||
|
"offset": Duration {
|
||||||
|
"seconds": 50,
|
||||||
|
},
|
||||||
|
"text": "Comment 2",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"duration": Duration {
|
||||||
|
"seconds": 60,
|
||||||
|
},
|
||||||
|
"intensity": ConstantIntensity {
|
||||||
|
"_value": 0.9,
|
||||||
|
},
|
||||||
|
"type": "Interval",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"name": "My Workout",
|
||||||
|
"tags": Array [],
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
it("throws error when comment offset is outside of interval length", () => {
|
it("throws error when comment offset is outside of interval length", () => {
|
||||||
expect(() =>
|
expect(() =>
|
||||||
parse(`
|
parse(`
|
||||||
|
|
|
||||||
|
|
@ -58,8 +58,14 @@ const parseHeader = (tokens: Token[]): [Header, Token[]] => {
|
||||||
return [header, tokens];
|
return [header, tokens];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type PartialComment = {
|
||||||
|
offsetToken: OffsetToken;
|
||||||
|
text: string;
|
||||||
|
loc: SourceLocation;
|
||||||
|
};
|
||||||
|
|
||||||
const parseIntervalComments = (tokens: Token[], intervalDuration: Duration): [Comment[], Token[]] => {
|
const parseIntervalComments = (tokens: Token[], intervalDuration: Duration): [Comment[], Token[]] => {
|
||||||
const comments: Comment[] = [];
|
const comments: PartialComment[] = [];
|
||||||
while (tokens[0]) {
|
while (tokens[0]) {
|
||||||
const [start, offset, text, ...rest] = tokens;
|
const [start, offset, text, ...rest] = tokens;
|
||||||
if (start.type === "comment-start") {
|
if (start.type === "comment-start") {
|
||||||
|
|
@ -73,7 +79,7 @@ const parseIntervalComments = (tokens: Token[], intervalDuration: Duration): [Co
|
||||||
throw new ParseError(`Expected [comment text] instead got ${tokenToString(text)}`, text?.loc || offset.loc);
|
throw new ParseError(`Expected [comment text] instead got ${tokenToString(text)}`, text?.loc || offset.loc);
|
||||||
}
|
}
|
||||||
comments.push({
|
comments.push({
|
||||||
offset: absoluteOffset(offset, intervalDuration, last(comments)),
|
offsetToken: offset,
|
||||||
text: text.value,
|
text: text.value,
|
||||||
loc: offset.loc,
|
loc: offset.loc,
|
||||||
});
|
});
|
||||||
|
|
@ -82,16 +88,54 @@ const parseIntervalComments = (tokens: Token[], intervalDuration: Duration): [Co
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return [comments, tokens];
|
|
||||||
|
return [computeAbsoluteOffsets(comments, intervalDuration), tokens];
|
||||||
};
|
};
|
||||||
|
|
||||||
const absoluteOffset = (offset: OffsetToken, intervalDuration: Duration, previousComment?: Comment): Duration => {
|
const computeAbsoluteOffsets = (partialComments: PartialComment[], intervalDuration: Duration): Comment[] => {
|
||||||
if (offset.kind === "relative-minus") {
|
const comments: Comment[] = [];
|
||||||
return new Duration(intervalDuration.seconds - offset.value);
|
for (let i = 0; i < partialComments.length; i++) {
|
||||||
} else if (offset.kind === "relative-plus" && previousComment) {
|
const pComment = partialComments[i];
|
||||||
return new Duration(previousComment.offset.seconds + offset.value);
|
const offsetToken = pComment.offsetToken;
|
||||||
} else {
|
|
||||||
return new Duration(offset.value);
|
// Assume absolute offset by default
|
||||||
|
let offset: Duration = new Duration(offsetToken.value);
|
||||||
|
|
||||||
|
if (offsetToken.kind === "relative-plus") {
|
||||||
|
// Position relative to previous already-computed comment offset
|
||||||
|
const previousComment = last(comments);
|
||||||
|
if (previousComment) {
|
||||||
|
offset = new Duration(previousComment.offset.seconds + offset.seconds);
|
||||||
|
}
|
||||||
|
} else if (offsetToken.kind === "relative-minus") {
|
||||||
|
// Position relative to next comment or interval end
|
||||||
|
offset = new Duration(nextCommentOffset(partialComments, i, intervalDuration).seconds - offset.seconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
comments.push({
|
||||||
|
offset,
|
||||||
|
loc: pComment.loc,
|
||||||
|
text: pComment.text,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return comments;
|
||||||
|
};
|
||||||
|
|
||||||
|
const nextCommentOffset = (partialComments: PartialComment[], i: number, intervalDuration: Duration): Duration => {
|
||||||
|
const nextComment = partialComments[i + 1];
|
||||||
|
if (!nextComment) {
|
||||||
|
return intervalDuration;
|
||||||
|
}
|
||||||
|
switch (nextComment.offsetToken.kind) {
|
||||||
|
case "relative-minus":
|
||||||
|
return new Duration(
|
||||||
|
nextCommentOffset(partialComments, i + 1, intervalDuration).seconds - nextComment.offsetToken.value,
|
||||||
|
);
|
||||||
|
case "relative-plus":
|
||||||
|
throw new ParseError("Negative offset followed by positive offset", nextComment.offsetToken.loc);
|
||||||
|
case "absolute":
|
||||||
|
default:
|
||||||
|
return new Duration(nextComment.offsetToken.value);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue