Positive comment offset syntax: +01:00
This commit is contained in:
parent
ba92dd50d1
commit
574272602f
|
|
@ -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(`
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
Loading…
Reference in New Issue