phantasmal-world/src/scripting/assembly_worker_init.ts

121 lines
4.1 KiB
TypeScript
Raw Normal View History

2019-07-23 17:03:35 +08:00
import { editor } from "monaco-editor";
import { assemble, AssemblyError } from "./assembly";
interface ScriptWorkerInput {}
class NewModelInput implements ScriptWorkerInput {
constructor(readonly value: string) {}
}
class ModelChangeInput implements ScriptWorkerInput {
constructor(readonly changes: editor.IModelContentChange[]) {}
}
interface ScriptWorkerOutput {}
class NewErrorsOutput implements ScriptWorkerOutput {
constructor(readonly errors: AssemblyError[]) {}
}
let lines: string[] = [];
function replace_line_part(
line_no: number,
start_col: number,
end_col: number,
new_line_parts: string[]
): void {
const line = lines[line_no - 1];
// We keep the parts of the line that weren't affected by the edit.
const line_start = line.slice(0, start_col - 1);
const line_end = line.slice(end_col);
if (new_line_parts.length === 1) {
lines.splice(line_no - 1, 1, line_start + new_line_parts[0] + line_end);
} else {
lines.splice(
line_no - 1,
1,
line_start + new_line_parts[0],
...new_line_parts.slice(1, new_line_parts.length - 2),
new_line_parts[new_line_parts.length - 1] + line_end
);
}
}
function replace_line_part_left(line_no: number, end_col: number, new_line_part: string): void {
lines.splice(line_no - 1, 1, new_line_part + lines[line_no - 1].slice(end_col));
}
function replace_line_part_right(line_no: number, start_col: number, new_line_part: string): void {
lines.splice(line_no - 1, 1, lines[line_no - 1].slice(0, start_col - 1) + new_line_part);
}
function replace_lines(start_line_no: number, end_line_no: number, new_lines: string[]): void {
lines.splice(start_line_no - 1, end_line_no - start_line_no + 1, ...new_lines);
}
function replace_lines_and_merge_line_parts(
start_line_no: number,
end_line_no: number,
start_col: number,
end_col: number,
new_line_part: string
): void {
const start_line = lines[start_line_no - 1];
const end_line = lines[end_line_no - 1];
// We keep the parts of the lines that weren't affected by the edit.
const start_line_start = start_line.slice(0, start_col - 1);
const end_line_end = end_line.slice(end_col);
lines.splice(
start_line_no - 1,
end_line_no - start_line_no + 1,
start_line_start + new_line_part + end_line_end
);
}
window.onmessage = (e: MessageEvent) => {
const message: ScriptWorkerInput = e.data;
if (message instanceof NewModelInput) {
lines = message.value.split("\n");
window.postMessage(assemble(lines).errors, window.origin);
} else if (message instanceof ModelChangeInput) {
for (const change of message.changes) {
const { startLineNumber, endLineNumber, startColumn, endColumn } = change.range;
const lines_changed = endLineNumber - startLineNumber + 1;
const new_lines = change.text.split("\n");
if (lines_changed === 1) {
replace_line_part(startLineNumber, startColumn, endColumn, new_lines);
} else if (new_lines.length === 1) {
replace_lines_and_merge_line_parts(
startLineNumber,
endLineNumber,
startColumn,
endColumn,
new_lines[0]
);
} else {
// Keep the left part of the first changed line.
replace_line_part_right(startLineNumber, startColumn, new_lines[0]);
// Replace all the lines in between.
replace_lines(
startLineNumber + 1,
endLineNumber - 1,
new_lines.slice(1, new_lines.length - 2)
);
// Keep the right part of the last changed line.
replace_line_part_left(startLineNumber, endColumn, new_lines[new_lines.length - 1]);
}
}
window.postMessage(assemble(lines).errors, window.origin);
} else {
throw new Error("Couldn't process message.");
}
};