From 463685ffa1dc513cfc15f7b0287c873a83ba036f Mon Sep 17 00:00:00 2001 From: jtuu Date: Sat, 14 Sep 2019 23:25:21 +0300 Subject: [PATCH 1/4] Added a toolbar to the script view. --- src/quest_editor/gui/AsmEditorToolBar.ts | 12 ++++++++++++ src/quest_editor/gui/AsmEditorView.ts | 5 +++++ 2 files changed, 17 insertions(+) create mode 100644 src/quest_editor/gui/AsmEditorToolBar.ts diff --git a/src/quest_editor/gui/AsmEditorToolBar.ts b/src/quest_editor/gui/AsmEditorToolBar.ts new file mode 100644 index 00000000..b9113392 --- /dev/null +++ b/src/quest_editor/gui/AsmEditorToolBar.ts @@ -0,0 +1,12 @@ +import { ToolBar } from "../../core/gui/ToolBar"; +import { Button } from "../../core/gui/Button"; + +export class AsmEditorToolBar extends ToolBar { + constructor() { + + super({ + children: [], + }); + this.finalize_construction(AsmEditorToolBar.prototype); + } +} diff --git a/src/quest_editor/gui/AsmEditorView.ts b/src/quest_editor/gui/AsmEditorView.ts index ed936ba4..7ac4727e 100644 --- a/src/quest_editor/gui/AsmEditorView.ts +++ b/src/quest_editor/gui/AsmEditorView.ts @@ -3,6 +3,7 @@ import { el } from "../../core/gui/dom"; import { editor } from "monaco-editor"; import { asm_editor_store } from "../stores/AsmEditorStore"; import IStandaloneCodeEditor = editor.IStandaloneCodeEditor; +import { AsmEditorToolBar } from "./AsmEditorToolBar"; editor.defineTheme("phantasmal-world", { base: "vs-dark", @@ -26,11 +27,15 @@ editor.defineTheme("phantasmal-world", { const DUMMY_MODEL = editor.createModel("", "psoasm"); export class AsmEditorView extends ResizableWidget { + private readonly tool_bar_view = this.disposable(new AsmEditorToolBar()); + private readonly editor: IStandaloneCodeEditor; constructor() { super(el.div()); + this.element.append(this.tool_bar_view.element); + this.editor = this.disposable( editor.create(this.element, { theme: "phantasmal-world", From 0618ca0b87e04edd54f3ab48177d9dc574f03256 Mon Sep 17 00:00:00 2001 From: jtuu Date: Sun, 15 Sep 2019 20:59:51 +0300 Subject: [PATCH 2/4] Added a checkbox for enabling inline arguments transformation mode to the script editor toolbar. --- src/quest_editor/gui/AsmEditorToolBar.ts | 27 +++++++++++++++++-- .../scripting/AssemblyAnalyser.ts | 11 +++++++- src/quest_editor/scripting/assembly.ts | 4 +++ src/quest_editor/scripting/assembly_worker.ts | 21 +++++++++++++-- .../scripting/assembly_worker_messages.ts | 10 +++++-- src/quest_editor/stores/AsmEditorStore.ts | 20 ++++++++++++++ 6 files changed, 86 insertions(+), 7 deletions(-) diff --git a/src/quest_editor/gui/AsmEditorToolBar.ts b/src/quest_editor/gui/AsmEditorToolBar.ts index b9113392..e7a28a1b 100644 --- a/src/quest_editor/gui/AsmEditorToolBar.ts +++ b/src/quest_editor/gui/AsmEditorToolBar.ts @@ -1,12 +1,35 @@ import { ToolBar } from "../../core/gui/ToolBar"; -import { Button } from "../../core/gui/Button"; +import { CheckBox } from "../../core/gui/CheckBox"; +import { asm_editor_store } from "../stores/AsmEditorStore"; export class AsmEditorToolBar extends ToolBar { constructor() { + const inline_args_mode_checkbox = new CheckBox(true, { + label: "Inline args mode", + tooltip: asm_editor_store.has_issues.map((has_issues) => { + let text = "Transform arg_push* opcodes to be inline with the opcode the arguments are given to."; + + if (has_issues) { + text += "\nThis mode cannot be toggled because there are issues in the script."; + } + + return text; + }), + }); super({ - children: [], + children: [ + inline_args_mode_checkbox + ], }); + + this.disposables( + asm_editor_store.inline_args_mode.bind_to(inline_args_mode_checkbox.checked), + + inline_args_mode_checkbox.enabled.bind_to(asm_editor_store.has_issues.map(b => !b)), + ); + + this.finalize_construction(AsmEditorToolBar.prototype); } } diff --git a/src/quest_editor/scripting/AssemblyAnalyser.ts b/src/quest_editor/scripting/AssemblyAnalyser.ts index 3a0127f2..4c055e9d 100644 --- a/src/quest_editor/scripting/AssemblyAnalyser.ts +++ b/src/quest_editor/scripting/AssemblyAnalyser.ts @@ -7,8 +7,9 @@ import { NewAssemblyInput, OutputMessageType, SignatureHelpInput, + AssemblySettingsChangeInput, } from "./assembly_worker_messages"; -import { AssemblyError, AssemblyWarning } from "./assembly"; +import { AssemblyError, AssemblyWarning, AssemblySettings } from "./assembly"; import { disassemble } from "./disassembly"; import { QuestModel } from "../model/QuestModel"; import { Kind, OPCODES } from "./opcodes"; @@ -130,6 +131,14 @@ export class AssemblyAnalyser implements Disposable { }); } + update_settings(changed_settings: Partial): void { + const message: AssemblySettingsChangeInput = { + type: InputMessageType.SettingsChange, + settings: changed_settings + }; + this.worker.postMessage(message); + } + dispose(): void { this.worker.terminate(); } diff --git a/src/quest_editor/scripting/assembly.ts b/src/quest_editor/scripting/assembly.ts index 27bea91e..aabdd4d2 100644 --- a/src/quest_editor/scripting/assembly.ts +++ b/src/quest_editor/scripting/assembly.ts @@ -35,6 +35,10 @@ export type AssemblyWarning = { export type AssemblyError = AssemblyWarning; +export type AssemblySettings = { + manual_stack: boolean +}; + export function assemble( assembly: string[], manual_stack: boolean = false, diff --git a/src/quest_editor/scripting/assembly_worker.ts b/src/quest_editor/scripting/assembly_worker.ts index 57881415..f428dcb0 100644 --- a/src/quest_editor/scripting/assembly_worker.ts +++ b/src/quest_editor/scripting/assembly_worker.ts @@ -6,8 +6,9 @@ import { OutputMessageType, SignatureHelpInput, SignatureHelpOutput, + AssemblySettingsChangeInput, } from "./assembly_worker_messages"; -import { assemble } from "./assembly"; +import { assemble, AssemblySettings } from "./assembly"; import Logger from "js-logger"; import { SegmentType } from "./instructions"; import { Opcode, OPCODES_BY_MNEMONIC } from "./opcodes"; @@ -24,6 +25,10 @@ let lines: string[] = []; const messages: AssemblyWorkerInput[] = []; let timeout: any; +const assembly_settings: AssemblySettings = { + manual_stack: false +}; + ctx.onmessage = (e: MessageEvent) => { messages.push(e.data); @@ -52,6 +57,9 @@ function process_messages(): void { case InputMessageType.SignatureHelp: signature_help(message); break; + case InputMessageType.SettingsChange: + settings_change(message); + break; } } } @@ -130,8 +138,17 @@ function signature_help(message: SignatureHelpInput): void { ctx.postMessage(response); } +/** + * Apply changes to settings. + */ +function settings_change(message: AssemblySettingsChangeInput): void { + if (message.settings.hasOwnProperty("manual_stack")) { + assembly_settings.manual_stack = Boolean(message.settings.manual_stack); + } +} + function assemble_and_send(): void { - const assembler_result = assemble(lines); + const assembler_result = assemble(lines, assembly_settings.manual_stack); const map_designations = new Map(); for (const segment of assembler_result.object_code) { diff --git a/src/quest_editor/scripting/assembly_worker_messages.ts b/src/quest_editor/scripting/assembly_worker_messages.ts index c1718b09..fb82b52e 100644 --- a/src/quest_editor/scripting/assembly_worker_messages.ts +++ b/src/quest_editor/scripting/assembly_worker_messages.ts @@ -1,4 +1,4 @@ -import { AssemblyError, AssemblyWarning } from "./assembly"; +import { AssemblyError, AssemblyWarning, AssemblySettings } from "./assembly"; import { Segment } from "./instructions"; import { Opcode } from "./opcodes"; @@ -6,9 +6,10 @@ export enum InputMessageType { NewAssembly, AssemblyChange, SignatureHelp, + SettingsChange, } -export type AssemblyWorkerInput = NewAssemblyInput | AssemblyChangeInput | SignatureHelpInput; +export type AssemblyWorkerInput = NewAssemblyInput | AssemblyChangeInput | SignatureHelpInput | AssemblySettingsChangeInput; export type NewAssemblyInput = { readonly type: InputMessageType.NewAssembly; @@ -33,6 +34,11 @@ export type SignatureHelpInput = { readonly col: number; }; +export type AssemblySettingsChangeInput = { + readonly type: InputMessageType.SettingsChange; + readonly settings: Partial; +}; + export enum OutputMessageType { NewObjectCode, SignatureHelp, diff --git a/src/quest_editor/stores/AsmEditorStore.ts b/src/quest_editor/stores/AsmEditorStore.ts index 98836211..0c5e3cc3 100644 --- a/src/quest_editor/stores/AsmEditorStore.ts +++ b/src/quest_editor/stores/AsmEditorStore.ts @@ -69,6 +69,9 @@ export class AsmEditorStore implements Disposable { () => this._did_redo.emit({ value: "asm undo" }), ); + readonly inline_args_mode: WritableProperty = property(true); + readonly has_issues: WritableProperty = property(false); + private readonly disposer = new Disposer(); private readonly model_disposer = this.disposer.add(new Disposer()); private readonly _model: WritableProperty = property(undefined); @@ -88,6 +91,17 @@ export class AsmEditorStore implements Disposable { assembly_analyser.issues.observe(({ value }) => this.update_model_markers(value), { call_now: true, }), + + this.inline_args_mode.observe(() => { + // don't allow changing inline args mode if there are issues + if (!this.has_issues.val) { + this.update_assembly_settings(); + } + }), + + assembly_analyser.issues.observe(({ value }) => { + this.has_issues.val = Boolean(value.warnings.length) || Boolean(value.errors.length); + }) ); } @@ -188,6 +202,12 @@ export class AsmEditorStore implements Disposable { ), ); } + + private update_assembly_settings(): void { + assembly_analyser.update_settings({ + manual_stack: !this.inline_args_mode.val + }); + } } export const asm_editor_store = new AsmEditorStore(); From 3155cdb1839838c7f75e12925eaa1012d57d05e3 Mon Sep 17 00:00:00 2001 From: jtuu Date: Sun, 15 Sep 2019 21:38:30 +0300 Subject: [PATCH 3/4] Transform existing quest script when inline args mode is toggled. --- .../scripting/AssemblyAnalyser.ts | 4 +- src/quest_editor/stores/AsmEditorStore.ts | 109 +++++++++++------- 2 files changed, 72 insertions(+), 41 deletions(-) diff --git a/src/quest_editor/scripting/AssemblyAnalyser.ts b/src/quest_editor/scripting/AssemblyAnalyser.ts index 4c055e9d..39087048 100644 --- a/src/quest_editor/scripting/AssemblyAnalyser.ts +++ b/src/quest_editor/scripting/AssemblyAnalyser.ts @@ -75,9 +75,9 @@ export class AssemblyAnalyser implements Disposable { this.worker.onmessage = this.process_worker_message; } - disassemble(quest: QuestModel): string[] { + disassemble(quest: QuestModel, manual_stack?: boolean): string[] { this.quest = quest; - const assembly = disassemble(quest.object_code); + const assembly = disassemble(quest.object_code, manual_stack); const message: NewAssemblyInput = { type: InputMessageType.NewAssembly, assembly }; this.worker.postMessage(message); return assembly; diff --git a/src/quest_editor/stores/AsmEditorStore.ts b/src/quest_editor/stores/AsmEditorStore.ts index 0c5e3cc3..e4a40b32 100644 --- a/src/quest_editor/stores/AsmEditorStore.ts +++ b/src/quest_editor/stores/AsmEditorStore.ts @@ -95,7 +95,7 @@ export class AsmEditorStore implements Disposable { this.inline_args_mode.observe(() => { // don't allow changing inline args mode if there are issues if (!this.has_issues.val) { - this.update_assembly_settings(); + this.change_inline_args_mode(); } }), @@ -109,53 +109,63 @@ export class AsmEditorStore implements Disposable { this.disposer.dispose(); } + /** + * Setup features for a given editor model. + * Features include undo/redo history and reassembling on change. + */ + private setup_editor_model_features(model: editor.ITextModel): void { + let initial_version = model.getAlternativeVersionId(); + let current_version = initial_version; + let last_version = initial_version; + + this.model_disposer.add( + model.onDidChangeContent(e => { + const version = model.getAlternativeVersionId(); + + if (version < current_version) { + // Undoing. + this.undo.can_redo.val = true; + + if (version === initial_version) { + this.undo.can_undo.val = false; + } + } else { + // Redoing. + if (version <= last_version) { + if (version === last_version) { + this.undo.can_redo.val = false; + } + } else { + this.undo.can_redo.val = false; + + if (current_version > last_version) { + last_version = current_version; + } + } + + this.undo.can_undo.val = true; + } + + current_version = version; + + assembly_analyser.update_assembly(e.changes); + }), + ); + } + private quest_changed(quest?: QuestModel): void { this.undo.reset(); this.model_disposer.dispose_all(); if (quest) { - const assembly = assembly_analyser.disassemble(quest); + const manual_stack = !this.inline_args_mode.val; + + const assembly = assembly_analyser.disassemble(quest, manual_stack); const model = this.model_disposer.add( editor.createModel(assembly.join("\n"), "psoasm"), ); - let initial_version = model.getAlternativeVersionId(); - let current_version = initial_version; - let last_version = initial_version; - - this.model_disposer.add( - model.onDidChangeContent(e => { - const version = model.getAlternativeVersionId(); - - if (version < current_version) { - // Undoing. - this.undo.can_redo.val = true; - - if (version === initial_version) { - this.undo.can_undo.val = false; - } - } else { - // Redoing. - if (version <= last_version) { - if (version === last_version) { - this.undo.can_redo.val = false; - } - } else { - this.undo.can_redo.val = false; - - if (current_version > last_version) { - last_version = current_version; - } - } - - this.undo.can_undo.val = true; - } - - current_version = version; - - assembly_analyser.update_assembly(e.changes); - }), - ); + this.setup_editor_model_features(model); this._model.val = model; } else { @@ -203,6 +213,27 @@ export class AsmEditorStore implements Disposable { ); } + private change_inline_args_mode(): void { + this.update_assembly_settings(); + + const quest = quest_editor_store.current_quest.val; + + if (!quest) { + return; + } + + const manual_stack = !this.inline_args_mode.val; + + const assembly = assembly_analyser.disassemble(quest, manual_stack); + const model = this.model_disposer.add( + editor.createModel(assembly.join("\n"), "psoasm"), + ); + + this.setup_editor_model_features(model); + + this._model.val = model; + } + private update_assembly_settings(): void { assembly_analyser.update_settings({ manual_stack: !this.inline_args_mode.val From fa78f5f95bb40053652f5107a69dc5afe9ae4ad1 Mon Sep 17 00:00:00 2001 From: jtuu Date: Sun, 15 Sep 2019 21:47:13 +0300 Subject: [PATCH 4/4] Lint --- src/core/stores/GuiStore.ts | 6 +++++- src/quest_editor/gui/AsmEditorToolBar.ts | 10 ++++------ src/quest_editor/scripting/AssemblyAnalyser.ts | 2 +- src/quest_editor/scripting/assembly.ts | 2 +- src/quest_editor/scripting/assembly_worker.ts | 2 +- .../scripting/assembly_worker_messages.ts | 6 +++++- src/quest_editor/stores/AsmEditorStore.ts | 13 ++++++------- 7 files changed, 23 insertions(+), 18 deletions(-) diff --git a/src/core/stores/GuiStore.ts b/src/core/stores/GuiStore.ts index 1924d1f9..8f1cacbf 100644 --- a/src/core/stores/GuiStore.ts +++ b/src/core/stores/GuiStore.ts @@ -43,7 +43,11 @@ class GuiStore implements Disposable { window.removeEventListener("keydown", this.dispatch_global_keydown); } - on_global_keydown(tool: GuiTool, binding: string, handler: (e: KeyboardEvent) => void): Disposable { + on_global_keydown( + tool: GuiTool, + binding: string, + handler: (e: KeyboardEvent) => void, + ): Disposable { const key = this.handler_key(tool, binding); this.global_keydown_handlers.set(key, handler); diff --git a/src/quest_editor/gui/AsmEditorToolBar.ts b/src/quest_editor/gui/AsmEditorToolBar.ts index e7a28a1b..be3df717 100644 --- a/src/quest_editor/gui/AsmEditorToolBar.ts +++ b/src/quest_editor/gui/AsmEditorToolBar.ts @@ -6,8 +6,9 @@ export class AsmEditorToolBar extends ToolBar { constructor() { const inline_args_mode_checkbox = new CheckBox(true, { label: "Inline args mode", - tooltip: asm_editor_store.has_issues.map((has_issues) => { - let text = "Transform arg_push* opcodes to be inline with the opcode the arguments are given to."; + tooltip: asm_editor_store.has_issues.map(has_issues => { + let text = + "Transform arg_push* opcodes to be inline with the opcode the arguments are given to."; if (has_issues) { text += "\nThis mode cannot be toggled because there are issues in the script."; @@ -18,9 +19,7 @@ export class AsmEditorToolBar extends ToolBar { }); super({ - children: [ - inline_args_mode_checkbox - ], + children: [inline_args_mode_checkbox], }); this.disposables( @@ -29,7 +28,6 @@ export class AsmEditorToolBar extends ToolBar { inline_args_mode_checkbox.enabled.bind_to(asm_editor_store.has_issues.map(b => !b)), ); - this.finalize_construction(AsmEditorToolBar.prototype); } } diff --git a/src/quest_editor/scripting/AssemblyAnalyser.ts b/src/quest_editor/scripting/AssemblyAnalyser.ts index 39087048..24c2b439 100644 --- a/src/quest_editor/scripting/AssemblyAnalyser.ts +++ b/src/quest_editor/scripting/AssemblyAnalyser.ts @@ -134,7 +134,7 @@ export class AssemblyAnalyser implements Disposable { update_settings(changed_settings: Partial): void { const message: AssemblySettingsChangeInput = { type: InputMessageType.SettingsChange, - settings: changed_settings + settings: changed_settings, }; this.worker.postMessage(message); } diff --git a/src/quest_editor/scripting/assembly.ts b/src/quest_editor/scripting/assembly.ts index aabdd4d2..bf05a205 100644 --- a/src/quest_editor/scripting/assembly.ts +++ b/src/quest_editor/scripting/assembly.ts @@ -36,7 +36,7 @@ export type AssemblyWarning = { export type AssemblyError = AssemblyWarning; export type AssemblySettings = { - manual_stack: boolean + manual_stack: boolean; }; export function assemble( diff --git a/src/quest_editor/scripting/assembly_worker.ts b/src/quest_editor/scripting/assembly_worker.ts index f428dcb0..f3d274e9 100644 --- a/src/quest_editor/scripting/assembly_worker.ts +++ b/src/quest_editor/scripting/assembly_worker.ts @@ -26,7 +26,7 @@ const messages: AssemblyWorkerInput[] = []; let timeout: any; const assembly_settings: AssemblySettings = { - manual_stack: false + manual_stack: false, }; ctx.onmessage = (e: MessageEvent) => { diff --git a/src/quest_editor/scripting/assembly_worker_messages.ts b/src/quest_editor/scripting/assembly_worker_messages.ts index fb82b52e..bb6936dd 100644 --- a/src/quest_editor/scripting/assembly_worker_messages.ts +++ b/src/quest_editor/scripting/assembly_worker_messages.ts @@ -9,7 +9,11 @@ export enum InputMessageType { SettingsChange, } -export type AssemblyWorkerInput = NewAssemblyInput | AssemblyChangeInput | SignatureHelpInput | AssemblySettingsChangeInput; +export type AssemblyWorkerInput = + | NewAssemblyInput + | AssemblyChangeInput + | SignatureHelpInput + | AssemblySettingsChangeInput; export type NewAssemblyInput = { readonly type: InputMessageType.NewAssembly; diff --git a/src/quest_editor/stores/AsmEditorStore.ts b/src/quest_editor/stores/AsmEditorStore.ts index e4a40b32..5b663983 100644 --- a/src/quest_editor/stores/AsmEditorStore.ts +++ b/src/quest_editor/stores/AsmEditorStore.ts @@ -100,8 +100,9 @@ export class AsmEditorStore implements Disposable { }), assembly_analyser.issues.observe(({ value }) => { - this.has_issues.val = Boolean(value.warnings.length) || Boolean(value.errors.length); - }) + this.has_issues.val = + Boolean(value.warnings.length) || Boolean(value.errors.length); + }), ); } @@ -225,10 +226,8 @@ export class AsmEditorStore implements Disposable { const manual_stack = !this.inline_args_mode.val; const assembly = assembly_analyser.disassemble(quest, manual_stack); - const model = this.model_disposer.add( - editor.createModel(assembly.join("\n"), "psoasm"), - ); - + const model = this.model_disposer.add(editor.createModel(assembly.join("\n"), "psoasm")); + this.setup_editor_model_features(model); this._model.val = model; @@ -236,7 +235,7 @@ export class AsmEditorStore implements Disposable { private update_assembly_settings(): void { assembly_analyser.update_settings({ - manual_stack: !this.inline_args_mode.val + manual_stack: !this.inline_args_mode.val, }); } }