Skip to content

Commit b15f8a3

Browse files
committed
refactor: Replace HTMLGenerator to closure-based factory function
1 parent beb1eb3 commit b15f8a3

File tree

1 file changed

+66
-92
lines changed

1 file changed

+66
-92
lines changed
Lines changed: 66 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { createHighlighter } from "./createHighlighter";
22
import type { BundledLanguage } from "shiki";
3-
import type { Lang, StepNameType, ExtendedTestCaseSnapshot, DataFixture } from "./types";
4-
import type { Command, CommandLatest, TestCaseFixture } from "@cursorless/common";
3+
import type { StepNameType, ExtendedTestCaseSnapshot, DataFixture } from "./types";
54

65
import { createDecorations } from "./helpers";
76

@@ -12,143 +11,118 @@ import { createDecorations } from "./helpers";
1211
* @returns {Promise<{ before: string; during: string; after: string }>} A promise that resolves to the generated HTML content for each step.
1312
*/
1413
export async function generateHtml(data: DataFixture) {
15-
return new HTMLGenerator(data).generateAll()
14+
return createHtmlGenerator(data).generateAll();
1615
}
1716

1817
const highlighter = createHighlighter();
1918

20-
class HTMLGenerator {
21-
private testCaseStates: {
22-
before: ExtendedTestCaseSnapshot | undefined;
23-
during: ExtendedTestCaseSnapshot | undefined;
24-
after: ExtendedTestCaseSnapshot | undefined;
25-
}
26-
private lang: Lang;
27-
private command?: CommandLatest | Command;
28-
private raw: TestCaseFixture;
29-
30-
constructor(data: DataFixture) {
31-
const { languageId, command } = data;
32-
this.lang = languageId as BundledLanguage;
33-
this.command = command; // Optional command parameter
34-
this.raw = data
35-
this.testCaseStates = {
36-
before: data.initialState,
37-
during: {
38-
...(
39-
/**
40-
* Spread the document state with more lines (finalState vs initialState),
41-
* so Shiki decorations stay in bounds and don't go out of range.
42-
*/
43-
data.finalState &&
44-
(data.finalState.documentContents?.split("\n").length > data.initialState.documentContents?.split("\n").length)
45-
? data.finalState
46-
: data.initialState
47-
),
48-
...data.ide,
49-
finalStateMarkHelpers: {
50-
thatMark: data?.finalState?.thatMark,
51-
sourceMark: data?.finalState?.sourceMark
52-
}
53-
},
54-
after: data.finalState
55-
}
56-
}
57-
58-
async generate(stepName: StepNameType) {
59-
const state = this.testCaseStates[stepName]
19+
function createHtmlGenerator(data: DataFixture) {
20+
const lang = data.languageId as BundledLanguage;
21+
const command = data.command;
22+
const raw = data;
23+
const testCaseStates = {
24+
before: data.initialState,
25+
during: {
26+
...(
27+
/**
28+
* Spread the document state with more lines (finalState vs initialState),
29+
* so Shiki decorations stay in bounds and don't go out of range.
30+
*/
31+
data.finalState &&
32+
(data.finalState.documentContents?.split("\n").length > data.initialState.documentContents?.split("\n").length)
33+
? data.finalState
34+
: data.initialState
35+
),
36+
...data.ide,
37+
finalStateMarkHelpers: {
38+
thatMark: data?.finalState?.thatMark,
39+
sourceMark: data?.finalState?.sourceMark
40+
}
41+
},
42+
after: data.finalState
43+
};
6044

45+
async function generate(stepName: StepNameType) {
46+
const state = testCaseStates[stepName];
6147
if (!state) {
62-
console.error(`Error in ${stepName} ${this.raw.command.spokenForm}`)
63-
return "Error"
48+
console.error(`Error in ${stepName} ${raw.command.spokenForm}`);
49+
return "Error";
6450
}
65-
66-
const decorations = await this.getDecorations(state);
67-
68-
const { documentContents } = state
69-
70-
const htmlArray: string[] = []
51+
const decorations = await getDecorations(state);
52+
const { documentContents } = state;
53+
const htmlArray: string[] = [];
7154
let codeBody;
72-
7355
const errorLevels = [
7456
"excludes thatMarks sourceMarks selectionRanges ideFlashes",
7557
"excludes thatMarks sourceMarks selectionRanges",
7658
"excludes thatMarks sourceMarks",
7759
"excludes thatMarks",
7860
"success",
79-
]
80-
81-
let errorLevel = errorLevels.length - 1
82-
61+
];
62+
let errorLevel = errorLevels.length - 1;
8363
for (let i = decorations.length - 1; i >= 0; i--) {
8464
const fallbackDecoration = decorations.slice(0, i).flat();
85-
errorLevel = i
65+
errorLevel = i;
8666
try {
8767
const marker = await highlighter;
8868
const options = {
8969
theme: "css-variables",
90-
lang: this.lang,
70+
lang,
9171
decorations: fallbackDecoration
9272
};
9373
codeBody = marker.codeToHtml(documentContents, options);
94-
htmlArray.push(codeBody)
95-
break; // Exit loop if successful
74+
htmlArray.push(codeBody);
75+
break;
9676
} catch (error) {
97-
console.warn(`"Failed with decorations level ${i}:"`, this.command);
77+
console.warn(`"Failed with decorations level ${i}:"`, command);
9878
console.warn(fallbackDecoration, error);
99-
// Continue to the next fallback level
10079
}
10180
}
102-
10381
if (!codeBody) {
10482
console.error("All fallback levels failed. Unable to generate code body.");
105-
codeBody = ""; // Provide a default empty string or handle as needed
83+
codeBody = "";
10684
}
107-
108-
let clipboardRendered = ""
85+
let clipboardRendered = "";
10986
if (state.clipboard) {
110-
clipboardRendered = `<pre><code>clipboard: ${state.clipboard}</pre></code>`
87+
clipboardRendered = `<pre><code>clipboard: ${state.clipboard}</pre></code>`;
11188
if (clipboardRendered !== "") {
112-
htmlArray.push(clipboardRendered)
89+
htmlArray.push(clipboardRendered);
11390
}
11491
}
115-
116-
let error = ""
92+
let error = "";
11793
if (errorLevel !== errorLevels.length - 1) {
118-
error = errorLevels[errorLevel]
119-
const errorRendered = `<pre><code>Omitted due to errors: ${error}</pre></code>`
120-
htmlArray.push(errorRendered)
94+
error = errorLevels[errorLevel];
95+
const errorRendered = `<pre><code>Omitted due to errors: ${error}</pre></code>`;
96+
htmlArray.push(errorRendered);
12197
}
122-
return { html: htmlArray.join(""), data: [decorations] }
98+
return { html: htmlArray.join(""), data: [decorations] };
12399
}
124100

125-
async generateAll() {
126-
127-
const output = {
128-
before: await this.generate("before"),
129-
during: await this.generate("during"),
130-
after: await this.generate("after"),
131-
}
132-
return output
101+
async function generateAll() {
102+
return {
103+
before: await generate("before"),
104+
during: await generate("during"),
105+
after: await generate("after"),
106+
};
133107
}
134108

135-
async getDecorations(testCaseState: ExtendedTestCaseSnapshot) {
136-
const { messages, flashes, highlights, finalStateMarkHelpers } = testCaseState
137-
138-
const potentialMarks = testCaseState.marks || {}
139-
const lines = testCaseState.documentContents.split("\n")
109+
async function getDecorations(testCaseState: ExtendedTestCaseSnapshot) {
110+
const { messages, flashes, highlights, finalStateMarkHelpers } = testCaseState;
111+
const potentialMarks = testCaseState.marks || {};
112+
const lines = testCaseState.documentContents.split("\n");
140113
const obj = {
141114
marks: potentialMarks,
142115
ide: { messages, flashes, highlights },
143-
command: this.command,
116+
command,
144117
lines,
145118
selections: testCaseState.selections,
146119
thatMark: testCaseState.thatMark,
147120
sourceMark: testCaseState.sourceMark,
148121
finalStateMarkHelpers
149-
}
150-
151-
const decorations = createDecorations(obj)
152-
return decorations
122+
};
123+
const decorations = createDecorations(obj);
124+
return decorations;
153125
}
126+
127+
return { generate, generateAll, getDecorations };
154128
}

0 commit comments

Comments
 (0)