Support negative comment offsets

This commit is contained in:
Rene Saarsoo 2020-11-21 21:22:29 +02:00
parent ea00d550fe
commit 90e1664ed6
3 changed files with 95 additions and 5 deletions

View File

@ -555,6 +555,94 @@ Rest: 5:00 50%
`);
});
it("parses intervals with negative comment offsets", () => {
expect(
parse(`
Name: My Workout
Interval: 10:00 90%
@ 0:10 Find your rythm.
@ -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(`
Object {
"author": "",
"description": "",
"intervals": Array [
Object {
"cadence": undefined,
"comments": Array [
Object {
"loc": Object {
"col": 4,
"row": 3,
},
"offset": Duration {
"seconds": 10,
},
"text": "Find your rythm.",
},
Object {
"loc": Object {
"col": 4,
"row": 4,
},
"offset": Duration {
"seconds": 590,
},
"text": "Final push. YOU GOT IT!",
},
],
"duration": Duration {
"seconds": 600,
},
"intensity": ConstantIntensity {
"_value": 0.9,
},
"type": "Interval",
},
Object {
"cadence": undefined,
"comments": Array [
Object {
"loc": Object {
"col": 4,
"row": 7,
},
"offset": Duration {
"seconds": 30,
},
"text": "Great effort!",
},
Object {
"loc": Object {
"col": 4,
"row": 8,
},
"offset": Duration {
"seconds": 180,
},
"text": "Cool down well after all of this.",
},
],
"duration": Duration {
"seconds": 300,
},
"intensity": ConstantIntensity {
"_value": 0.5,
},
"type": "Rest",
},
],
"name": "My Workout",
"tags": Array [],
}
`);
});
it("throws error when comment offset is outside of interval length", () => {
expect(() =>
parse(`

View File

@ -57,7 +57,7 @@ const parseHeader = (tokens: Token[]): [Header, Token[]] => {
return [header, tokens];
};
const parseIntervalComments = (tokens: Token[]): [Comment[], Token[]] => {
const parseIntervalComments = (tokens: Token[], intervalDuration: Duration): [Comment[], Token[]] => {
const comments: Comment[] = [];
while (tokens[0]) {
const [start, offset, text, ...rest] = tokens;
@ -72,7 +72,8 @@ const parseIntervalComments = (tokens: Token[]): [Comment[], Token[]] => {
throw new ParseError(`Expected [comment text] instead got ${tokenToString(text)}`, text?.loc || offset.loc);
}
comments.push({
offset: new Duration(offset.value),
// when offset is negative, recalculate it based on interval length
offset: new Duration(offset.value >= 0 ? offset.value : intervalDuration.seconds + offset.value),
text: text.value,
loc: offset.loc,
});
@ -115,7 +116,7 @@ const parseIntervalParams = (type: IntervalType, tokens: Token[], loc: SourceLoc
intensity = new FreeIntensity();
}
const [comments, rest] = parseIntervalComments(tokens);
const [comments, rest] = parseIntervalComments(tokens, duration);
return [{ type, duration, intensity, cadence, comments }, rest];
};

View File

@ -89,16 +89,17 @@ const tokenizeParams = (text: string, loc: SourceLocation): Token[] => {
};
const tokenizeComment = (line: string, row: number): Token[] | undefined => {
const [, commentHead, offset, commentText] = line.match(/^(\s*@\s*)([0-9:]+)(.*?)$/) || [];
const [, commentHead, minus, offset, commentText] = line.match(/^(\s*@\s*)(-?)([0-9:]+)(.*?)$/) || [];
if (!commentHead) {
return undefined;
}
const sign = minus ? -1 : 1;
if (!DURATION_REGEX.test(offset)) {
throw new ParseError("Invalid comment offset", { row, col: commentHead.length });
}
return [
{ type: "comment-start", loc: { row, col: line.indexOf("@") } },
{ type: "duration", value: toSeconds(offset), loc: { row, col: commentHead.length } },
{ type: "duration", value: sign * toSeconds(offset), loc: { row, col: commentHead.length } },
{ type: "text", value: commentText.trim(), loc: { row, col: commentHead.length + offset.length } },
];
};