mirror of
https://github.com/DaanVandenBosch/phantasmal-world.git
synced 2025-04-05 07:18:29 +08:00
246 lines
9.4 KiB
TypeScript
246 lines
9.4 KiB
TypeScript
import { ResizableWidget } from "../../core/gui/ResizableWidget";
|
|
import { el } from "../../core/gui/dom";
|
|
import { editor, KeyCode, KeyMod, Range } from "monaco-editor";
|
|
import { AsmEditorToolBar } from "./AsmEditorToolBar";
|
|
import { EditorHistory } from "./EditorHistory";
|
|
import "./AsmEditorView.css";
|
|
import { ListChangeType } from "../../core/observable/property/list/ListProperty";
|
|
import { GuiStore } from "../../core/stores/GuiStore";
|
|
import { AsmEditorStore } from "../stores/AsmEditorStore";
|
|
import { QuestRunner } from "../QuestRunner";
|
|
import IStandaloneCodeEditor = editor.IStandaloneCodeEditor;
|
|
|
|
editor.defineTheme("phantasmal-world", {
|
|
base: "vs-dark",
|
|
inherit: true,
|
|
rules: [
|
|
{ token: "", foreground: "e0e0e0", background: "#181818" },
|
|
{ token: "tag", foreground: "99bbff" },
|
|
{ token: "keyword", foreground: "d0a0ff", fontStyle: "bold" },
|
|
{ token: "predefined", foreground: "bbffbb" },
|
|
{ token: "number", foreground: "ffffaa" },
|
|
{ token: "number.hex", foreground: "ffffaa" },
|
|
{ token: "string", foreground: "88ffff" },
|
|
{ token: "string.escape", foreground: "8888ff" },
|
|
],
|
|
colors: {
|
|
"editor.background": "#181818",
|
|
"editor.lineHighlightBackground": "#202020",
|
|
},
|
|
});
|
|
|
|
const DUMMY_MODEL = editor.createModel("", "psoasm");
|
|
|
|
export class AsmEditorView extends ResizableWidget {
|
|
private readonly tool_bar_view: AsmEditorToolBar;
|
|
private readonly editor: IStandaloneCodeEditor;
|
|
private readonly history: EditorHistory;
|
|
private breakpoint_decoration_ids: string[] = [];
|
|
private execloc_decoration_id: string | undefined;
|
|
|
|
readonly element = el.div();
|
|
|
|
constructor(
|
|
gui_store: GuiStore,
|
|
quest_runner: QuestRunner,
|
|
private readonly asm_editor_store: AsmEditorStore,
|
|
) {
|
|
super();
|
|
|
|
this.tool_bar_view = this.disposable(new AsmEditorToolBar(asm_editor_store));
|
|
|
|
this.element.append(this.tool_bar_view.element);
|
|
|
|
this.editor = this.disposable(
|
|
editor.create(this.element, {
|
|
theme: "phantasmal-world",
|
|
scrollBeyondLastLine: false,
|
|
autoIndent: "full",
|
|
fontSize: 13,
|
|
wordWrap: "on",
|
|
wrappingIndent: "indent",
|
|
renderIndentGuides: false,
|
|
folding: false,
|
|
glyphMargin: gui_store.feature_active("vm"),
|
|
}),
|
|
);
|
|
|
|
this.history = this.disposable(new EditorHistory(this.editor));
|
|
|
|
// Commands and actions.
|
|
this.editor.addCommand(KeyMod.CtrlCmd | KeyCode.KEY_Z, () => {
|
|
// Do nothing.
|
|
});
|
|
|
|
this.editor.addCommand(KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_Z, () => {
|
|
// Do nothing.
|
|
});
|
|
|
|
const quick_command = this.editor.getAction("editor.action.quickCommand");
|
|
|
|
this.disposables(
|
|
this.editor.addAction({
|
|
id: "editor.action.quickCommand",
|
|
label: "Command Palette",
|
|
keybindings: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_P],
|
|
run: () => quick_command.run(),
|
|
}),
|
|
);
|
|
|
|
// Undo/Redo
|
|
this.disposables(
|
|
asm_editor_store.did_undo.observe(({ value: source }) => {
|
|
this.editor.trigger(source, "undo", undefined);
|
|
}),
|
|
|
|
asm_editor_store.did_redo.observe(({ value: source }) => {
|
|
this.editor.trigger(source, "redo", undefined);
|
|
}),
|
|
|
|
asm_editor_store.model.observe(
|
|
({ value: model }) => {
|
|
this.editor.updateOptions({
|
|
readOnly: !this.enabled.val || !model,
|
|
});
|
|
this.editor.setModel(model || DUMMY_MODEL);
|
|
this.history.reset();
|
|
|
|
this.breakpoint_decoration_ids = [];
|
|
this.execloc_decoration_id = "";
|
|
|
|
quest_runner.clear_breakpoints();
|
|
},
|
|
{ call_now: true },
|
|
),
|
|
|
|
asm_editor_store.breakpoints.observe_list(change => {
|
|
if (change.type === ListChangeType.ListChange) {
|
|
// remove
|
|
for (const breakpoint of change.removed) {
|
|
const cur_decos = this.editor.getLineDecorations(breakpoint.line_no);
|
|
// find decoration on line
|
|
if (cur_decos) {
|
|
for (const deco of cur_decos) {
|
|
const idx = this.breakpoint_decoration_ids.indexOf(deco.id);
|
|
|
|
if (idx > -1) {
|
|
// remove decoration
|
|
this.editor.deltaDecorations([deco.id], []);
|
|
this.breakpoint_decoration_ids.splice(idx, 1);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// add
|
|
for (const breakpoint of change.inserted) {
|
|
const cur_decos = this.editor.getLineDecorations(breakpoint.line_no);
|
|
// don't allow duplicates
|
|
if (
|
|
!cur_decos?.some(deco =>
|
|
this.breakpoint_decoration_ids.includes(deco.id),
|
|
)
|
|
) {
|
|
// add new decoration, don't overwrite anything, save decoration id
|
|
this.breakpoint_decoration_ids.push(
|
|
this.editor.deltaDecorations(
|
|
[],
|
|
[
|
|
{
|
|
range: new Range(
|
|
breakpoint.line_no,
|
|
0,
|
|
breakpoint.line_no,
|
|
0,
|
|
),
|
|
options: {
|
|
glyphMarginClassName:
|
|
"quest_editor_AsmEditorView_breakpoint-enabled",
|
|
glyphMarginHoverMessage: {
|
|
value: "Breakpoint",
|
|
},
|
|
},
|
|
},
|
|
],
|
|
)[0],
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}),
|
|
|
|
asm_editor_store.execution_location.observe(e => {
|
|
const old_line_num = e.old_value;
|
|
const new_line_num = e.value;
|
|
|
|
// remove old
|
|
if (old_line_num !== undefined && this.execloc_decoration_id !== undefined) {
|
|
const old_line_decos = this.editor.getLineDecorations(old_line_num);
|
|
|
|
if (old_line_decos) {
|
|
this.editor.deltaDecorations([this.execloc_decoration_id], []);
|
|
}
|
|
}
|
|
|
|
// add new
|
|
if (new_line_num !== undefined) {
|
|
this.execloc_decoration_id = this.editor.deltaDecorations(
|
|
[],
|
|
[
|
|
{
|
|
range: new Range(new_line_num, 0, new_line_num, 0),
|
|
options: {
|
|
className: "quest_editor_AsmEditorView_execution-location",
|
|
isWholeLine: true,
|
|
},
|
|
},
|
|
],
|
|
)[0];
|
|
|
|
this.editor.revealLineInCenterIfOutsideViewport(new_line_num);
|
|
}
|
|
}),
|
|
|
|
this.editor.onDidFocusEditorWidget(() => asm_editor_store.undo.make_current()),
|
|
|
|
this.editor.onMouseDown(e => {
|
|
switch (e.target.type) {
|
|
case editor.MouseTargetType.GUTTER_GLYPH_MARGIN:
|
|
{
|
|
const pos = e.target.position;
|
|
if (!pos) {
|
|
return;
|
|
}
|
|
quest_runner.toggle_breakpoint(pos.lineNumber);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}),
|
|
|
|
this.enabled.bind_to(quest_runner.running.map(r => !r)),
|
|
);
|
|
|
|
this.finalize_construction();
|
|
}
|
|
|
|
focus(): void {
|
|
this.editor.focus();
|
|
}
|
|
|
|
resize(width: number, height: number): this {
|
|
const editor_height = Math.max(0, height - this.tool_bar_view.height);
|
|
this.editor.layout({ width, height: editor_height });
|
|
return this;
|
|
}
|
|
|
|
protected set_enabled(enabled: boolean): void {
|
|
super.set_enabled(enabled);
|
|
|
|
this.tool_bar_view.enabled.val = enabled;
|
|
this.editor.updateOptions({ readOnly: !enabled || !this.asm_editor_store.model.val });
|
|
}
|
|
}
|