Skip to content

Commit edbe775

Browse files
authored
Merge pull request #31 from coderoad/feature/hints
Feature/hints
2 parents 811b701 + d6cd924 commit edbe775

File tree

6 files changed

+960
-627
lines changed

6 files changed

+960
-627
lines changed

Diff for: README.md

+6
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,12 @@ This level has two steps...
5656

5757
The first step with id L1S1. The Step id should start with the level id.
5858

59+
#### HINTS
60+
61+
- The first hint available when a user requests a hint
62+
- The second hint that will show
63+
- The third and final hint, as it is last in order
64+
5965
### L1S2 The second step
6066

6167
The second step...

Diff for: src/schema/tutorial.ts

+10
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,16 @@ export default {
201201
],
202202
},
203203
},
204+
hints: {
205+
type: "array",
206+
description:
207+
"An optional array of hints to provide helpful feedback to users",
208+
items: {
209+
type: "string",
210+
description: "A hint to provide to the user",
211+
examples: ["Have you tried doing X?"],
212+
},
213+
},
204214
required: ["content", "setup", "solution"],
205215
},
206216
},

Diff for: src/templates/TUTORIAL.md

+6
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,9 @@ Short description of the step's purpose. Should be short and fit in one line
3636
### L1S2 Another step
3737

3838
Step's short description.
39+
40+
#### Hints
41+
42+
- If a hint exists, the user can request a hint
43+
- Hints will show up in the order they are written
44+
- When all hints are added, the hint option will become disabled

Diff for: src/utils/parse.ts

+54-30
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,11 @@ export function parseMdContent(md: string): TutorialFrame | never {
4949
mdContent.summary.description = summaryMatch.groups.tutorialDescription.trim();
5050
}
5151

52+
let current = { level: "0", step: "0" };
5253
// Identify each part of the content
5354
parts.forEach((section: string) => {
5455
// match level
55-
const levelRegex = /^(##\s(?<levelId>L\d+)\s(?<levelTitle>.*)[\n\r]*(>\s*(?<levelSummary>.*))?[\n\r]+(?<levelContent>[^]*))/;
56+
const levelRegex = /^(#{2}\s(?<levelId>L\d+)\s(?<levelTitle>.*)[\n\r]*(>\s*(?<levelSummary>.*))?[\n\r]+(?<levelContent>[^]*))/;
5657
const levelMatch: RegExpMatchArray | null = section.match(levelRegex);
5758
if (levelMatch && levelMatch.groups) {
5859
const {
@@ -71,16 +72,33 @@ export function parseMdContent(md: string): TutorialFrame | never {
7172
: truncate(levelContent.trim(), { length: 80, omission: "..." }),
7273
content: levelContent.trim(),
7374
};
75+
current = { level: levelId, step: "0" };
7476
} else {
7577
// match step
76-
const stepRegex = /^(###\s(?<stepId>(?<levelId>L\d+)S\d+)\s(?<stepTitle>.*)[\n\r]+(?<stepContent>[^]*))/;
78+
const stepRegex = /^(#{3}\s(?<stepId>(?<levelId>L\d+)S\d+)\s(?<stepTitle>.*)[\n\r]+(?<stepContent>[^]*))/;
7779
const stepMatch: RegExpMatchArray | null = section.match(stepRegex);
7880
if (stepMatch && stepMatch.groups) {
7981
const { stepId, stepContent } = stepMatch.groups;
82+
8083
mdContent.steps[stepId] = {
8184
id: stepId,
8285
content: stepContent.trim(),
8386
};
87+
current = { ...current, step: stepId };
88+
} else {
89+
// parse hints from stepContent
90+
const hintDetectRegex = /^(#{4}\sHINTS[\n\r]+(\*\s(?<hintContent>[^]*))[\n\r]+)+/;
91+
const hintMatch = section.match(hintDetectRegex);
92+
if (!!hintMatch) {
93+
const hintItemRegex = /[\n\r]+\*\s/;
94+
const hints = section
95+
.split(hintItemRegex)
96+
.slice(1) // remove #### HINTS
97+
.map((h) => h.trim());
98+
if (hints.length) {
99+
mdContent.steps[current.step].hints = hints;
100+
}
101+
}
84102
}
85103
}
86104
});
@@ -135,39 +153,45 @@ export function parse(params: ParseParams): any {
135153
}
136154

137155
// add level step commits
138-
level.steps = (level.steps || []).map(
139-
(step: T.Step, stepIndex: number) => {
140-
const stepKey = `${levelSetupKey}S${stepIndex + 1}`;
141-
const stepSetupKey = `${stepKey}Q`;
142-
if (params.commits[stepSetupKey]) {
143-
if (!step.setup) {
144-
step.setup = {
145-
commits: [],
146-
};
156+
try {
157+
level.steps = (level.steps || []).map(
158+
(step: T.Step, stepIndex: number) => {
159+
const stepKey = `${levelSetupKey}S${stepIndex + 1}`;
160+
const stepSetupKey = `${stepKey}Q`;
161+
if (params.commits[stepSetupKey]) {
162+
if (!step.setup) {
163+
step.setup = {
164+
commits: [],
165+
};
166+
}
167+
step.setup.commits = params.commits[stepSetupKey];
147168
}
148-
step.setup.commits = params.commits[stepSetupKey];
149-
}
150169

151-
const stepSolutionKey = `${stepKey}A`;
152-
if (params.commits[stepSolutionKey]) {
153-
if (!step.solution) {
154-
step.solution = {
155-
commits: [],
156-
};
170+
const stepSolutionKey = `${stepKey}A`;
171+
if (params.commits[stepSolutionKey]) {
172+
if (!step.solution) {
173+
step.solution = {
174+
commits: [],
175+
};
176+
}
177+
step.solution.commits = params.commits[stepSolutionKey];
157178
}
158-
step.solution.commits = params.commits[stepSolutionKey];
159-
}
160179

161-
// add markdown
162-
const stepMarkdown: Partial<T.Step> = mdContent.steps[step.id];
163-
if (stepMarkdown) {
164-
step = { ...step, ...stepMarkdown };
165-
}
180+
// add markdown
181+
const stepMarkdown: Partial<T.Step> = mdContent.steps[step.id];
182+
if (stepMarkdown) {
183+
step = { ...step, ...stepMarkdown };
184+
}
166185

167-
step.id = `${stepKey}`;
168-
return step;
169-
}
170-
);
186+
step.id = `${stepKey}`;
187+
return step;
188+
}
189+
);
190+
} catch (error) {
191+
console.log(JSON.stringify(level.steps));
192+
console.error("Error parsing level steps");
193+
console.error(error.message);
194+
}
171195

172196
return level;
173197
})

0 commit comments

Comments
 (0)