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", () => {
|
it("throws error when comment offset is outside of interval length", () => {
|
||||||
expect(() =>
|
expect(() =>
|
||||||
parse(`
|
parse(`
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
|
import { last } from "ramda";
|
||||||
import { Interval, Workout, Comment } from "../ast";
|
import { Interval, Workout, Comment } from "../ast";
|
||||||
import { Duration } from "../Duration";
|
import { Duration } from "../Duration";
|
||||||
import { ConstantIntensity, FreeIntensity, RangeIntensity } from "../Intensity";
|
import { ConstantIntensity, FreeIntensity, RangeIntensity } from "../Intensity";
|
||||||
import { ParseError } from "./ParseError";
|
import { ParseError } from "./ParseError";
|
||||||
import { IntervalType, SourceLocation, Token } from "./tokenizer";
|
import { IntervalType, OffsetToken, SourceLocation, Token } from "./tokenizer";
|
||||||
|
|
||||||
type Header = Partial<Omit<Workout, "intervals">>;
|
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);
|
throw new ParseError(`Expected [comment text] instead got ${tokenToString(text)}`, text?.loc || offset.loc);
|
||||||
}
|
}
|
||||||
comments.push({
|
comments.push({
|
||||||
// when offset is negative, recalculate it based on interval length
|
offset: absoluteOffset(offset, intervalDuration, last(comments)),
|
||||||
offset: new Duration(offset.kind === "absolute" ? offset.value : intervalDuration.seconds - offset.value),
|
|
||||||
text: text.value,
|
text: text.value,
|
||||||
loc: offset.loc,
|
loc: offset.loc,
|
||||||
});
|
});
|
||||||
|
|
@ -85,6 +85,16 @@ const parseIntervalComments = (tokens: Token[], intervalDuration: Duration): [Co
|
||||||
return [comments, tokens];
|
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[]] => {
|
const parseIntervalParams = (type: IntervalType, tokens: Token[], loc: SourceLocation): [Interval, Token[]] => {
|
||||||
let duration;
|
let duration;
|
||||||
let cadence;
|
let cadence;
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,7 @@ const tokenizeParams = (text: string, loc: SourceLocation): Token[] => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const tokenizeComment = (line: string, row: number): Token[] | undefined => {
|
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) {
|
if (!commentHead) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
@ -113,7 +113,7 @@ const tokenizeComment = (line: string, row: number): Token[] | undefined => {
|
||||||
{ type: "comment-start", loc: { row, col: line.indexOf("@") } },
|
{ type: "comment-start", loc: { row, col: line.indexOf("@") } },
|
||||||
{
|
{
|
||||||
type: "offset",
|
type: "offset",
|
||||||
kind: minus ? "relative-minus" : "absolute",
|
kind: signToKind(sign),
|
||||||
value: toSeconds(offset),
|
value: toSeconds(offset),
|
||||||
loc: { row, col: commentHead.length },
|
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 tokenizeHeader = (label: HeaderType, separator: string, paramString: string, row: number): Token[] => {
|
||||||
const token: HeaderToken = {
|
const token: HeaderToken = {
|
||||||
type: "header",
|
type: "header",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue