Merge pull request #1 from jtuu/script-editor-settings

Script editor toolbar and inline args mode toggle
This commit is contained in:
Daan Vanden Bosch 2019-09-15 22:37:24 +02:00 committed by GitHub
commit 7fe508716c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 177 additions and 46 deletions

View File

@ -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);

View File

@ -0,0 +1,33 @@
import { ToolBar } from "../../core/gui/ToolBar";
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: [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);
}
}

View File

@ -3,6 +3,7 @@ import { el } from "../../core/gui/dom";
import { editor, KeyCode, KeyMod } 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,6 +27,7 @@ editor.defineTheme("phantasmal-world", {
const DUMMY_MODEL = editor.createModel("", "psoasm");
export class AsmEditorView extends ResizableWidget {
private readonly tool_bar_view = this.disposable(new AsmEditorToolBar());
readonly element = el.div();
private readonly editor: IStandaloneCodeEditor;
@ -33,6 +35,8 @@ export class AsmEditorView extends ResizableWidget {
constructor() {
super();
this.element.append(this.tool_bar_view.element);
this.editor = this.disposable(
editor.create(this.element, {
theme: "phantasmal-world",

View File

@ -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";
@ -74,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;
@ -130,6 +131,14 @@ export class AssemblyAnalyser implements Disposable {
});
}
update_settings(changed_settings: Partial<AssemblySettings>): void {
const message: AssemblySettingsChangeInput = {
type: InputMessageType.SettingsChange,
settings: changed_settings,
};
this.worker.postMessage(message);
}
dispose(): void {
this.worker.terminate();
}

View File

@ -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,

View File

@ -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<number, number>();
for (const segment of assembler_result.object_code) {

View File

@ -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,14 @@ 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 +38,11 @@ export type SignatureHelpInput = {
readonly col: number;
};
export type AssemblySettingsChangeInput = {
readonly type: InputMessageType.SettingsChange;
readonly settings: Partial<AssemblySettings>;
};
export enum OutputMessageType {
NewObjectCode,
SignatureHelp,

View File

@ -69,6 +69,9 @@ export class AsmEditorStore implements Disposable {
() => this._did_redo.emit({ value: "asm undo" }),
);
readonly inline_args_mode: WritableProperty<boolean> = property(true);
readonly has_issues: WritableProperty<boolean> = property(false);
private readonly disposer = new Disposer();
private readonly model_disposer = this.disposer.add(new Disposer());
private readonly _model: WritableProperty<ITextModel | undefined> = property(undefined);
@ -88,6 +91,18 @@ 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.change_inline_args_mode();
}
}),
assembly_analyser.issues.observe(({ value }) => {
this.has_issues.val =
Boolean(value.warnings.length) || Boolean(value.errors.length);
}),
);
}
@ -95,53 +110,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 {
@ -188,6 +213,31 @@ 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,
});
}
}
export const asm_editor_store = new AsmEditorStore();