Positive comment offset syntax: +01:00

This commit is contained in:
Rene Saarsoo 2021-03-02 21:58:34 +02:00
parent ba92dd50d1
commit 574272602f
3 changed files with 163 additions and 5 deletions

View File

@ -643,6 +643,143 @@ Rest: 5:00 50%
`);
});
it("parses intervals with positive comment offsets", () => {
expect(
parse(`
Name: My Workout
Interval: 10:00 90%
@ 0:50 First comment
@ +0:10 Comment #2 10 seconds later
@ +0:10 Comment #3 another 10 seconds later
@ 5:00 Half way!
@ +0:10 Comment #5 10 seconds later
@ +0:10 Comment #6 another 10 seconds later
`),
).toMatchInlineSnapshot(`
Object {
"author": "",
"description": "",
"intervals": Array [
Object {
"cadence": undefined,
"comments": Array [
Object {
"loc": Object {
"col": 4,
"row": 3,
},
"offset": Duration {
"seconds": 50,
},
"text": "First comment",
},
Object {
"loc": Object {
"col": 4,
"row": 4,
},
"offset": Duration {
"seconds": 60,
},
"text": "Comment #2 10 seconds later",
},
Object {
"loc": Object {
"col": 4,
"row": 5,
},
"offset": Duration {
"seconds": 70,
},
"text": "Comment #3 another 10 seconds later",
},
Object {
"loc": Object {
"col": 4,
"row": 6,
},
"offset": Duration {
"seconds": 300,
},
"text": "Half way!",
},
Object {
"loc": Object {
"col": 4,
"row": 7,
},
"offset": Duration {
"seconds": 310,
},
"text": "Comment #5 10 seconds later",
},
Object {
"loc": Object {
"col": 4,
"row": 8,
},
"offset": Duration {
"seconds": 320,
},
"text": "Comment #6 another 10 seconds later",
},
],
"duration": Duration {
"seconds": 600,
},
"intensity": ConstantIntensity {
"_value": 0.9,
},
"type": "Interval",
},
],
"name": "My Workout",
"tags": Array [],
}
`);
});
it("treats positive comment offset as relative to interval start when there's no previous comment", () => {
expect(
parse(`
Name: My Workout
Interval: 10:00 90%
@ +1:00 First comment
`),
).toMatchInlineSnapshot(`
Object {
"author": "",
"description": "",
"intervals": Array [
Object {
"cadence": undefined,
"comments": Array [
Object {
"loc": Object {
"col": 4,
"row": 3,
},
"offset": Duration {
"seconds": 60,
},
"text": "First comment",
},
],
"duration": Duration {
"seconds": 600,
},
"intensity": ConstantIntensity {
"_value": 0.9,
},
"type": "Interval",
},
],
"name": "My Workout",
"tags": Array [],
}
`);
});
it("throws error when comment offset is outside of interval length", () => {
expect(() =>
parse(`

View File

@ -1,8 +1,9 @@
import { last } from "ramda";
import { Interval, Workout, Comment } from "../ast";
import { Duration } from "../Duration";
import { ConstantIntensity, FreeIntensity, RangeIntensity } from "../Intensity";
import { ParseError } from "./ParseError";
import { IntervalType, SourceLocation, Token } from "./tokenizer";
import { IntervalType, OffsetToken, SourceLocation, Token } from "./tokenizer";
type Header = Partial<Omit<Workout, "intervals">>;
@ -72,8 +73,7 @@ const parseIntervalComments = (tokens: Token[], intervalDuration: Duration): [Co
throw new ParseError(`Expected [comment text] instead got ${tokenToString(text)}`, text?.loc || offset.loc);
}
comments.push({
// when offset is negative, recalculate it based on interval length
offset: new Duration(offset.kind === "absolute" ? offset.value : intervalDuration.seconds - offset.value),
offset: absoluteOffset(offset, intervalDuration, last(comments)),
text: text.value,
loc: offset.loc,
});
@ -85,6 +85,16 @@ const parseIntervalComments = (tokens: Token[], intervalDuration: Duration): [Co
return [comments, tokens];
};
const absoluteOffset = (offset: OffsetToken, intervalDuration: Duration, previousComment?: Comment): Duration => {
if (offset.kind === "relative-minus") {
return new Duration(intervalDuration.seconds - offset.value);
} else if (offset.kind === "relative-plus" && previousComment) {
return new Duration(previousComment.offset.seconds + offset.value);
} else {
return new Duration(offset.value);
}
};
const parseIntervalParams = (type: IntervalType, tokens: Token[], loc: SourceLocation): [Interval, Token[]] => {
let duration;
let cadence;

View File

@ -102,7 +102,7 @@ const tokenizeParams = (text: string, loc: SourceLocation): Token[] => {
};
const tokenizeComment = (line: string, row: number): Token[] | undefined => {
const [, commentHead, minus, offset, commentText] = line.match(/^(\s*@\s*)(-?)([0-9:]+)(.*?)$/) || [];
const [, commentHead, sign, offset, commentText] = line.match(/^(\s*@\s*)([-+]?)([0-9:]+)(.*?)$/) || [];
if (!commentHead) {
return undefined;
}
@ -113,7 +113,7 @@ const tokenizeComment = (line: string, row: number): Token[] | undefined => {
{ type: "comment-start", loc: { row, col: line.indexOf("@") } },
{
type: "offset",
kind: minus ? "relative-minus" : "absolute",
kind: signToKind(sign),
value: toSeconds(offset),
loc: { row, col: commentHead.length },
},
@ -121,6 +121,17 @@ const tokenizeComment = (line: string, row: number): Token[] | undefined => {
];
};
const signToKind = (sign: string) => {
switch (sign) {
case "-":
return "relative-minus";
case "+":
return "relative-plus";
default:
return "absolute";
}
};
const tokenizeHeader = (label: HeaderType, separator: string, paramString: string, row: number): Token[] => {
const token: HeaderToken = {
type: "header",