mirror of
https://github.com/DaanVandenBosch/phantasmal-world.git
synced 2025-04-05 15:28:29 +08:00
Added preliminary support for parameter hints.
This commit is contained in:
parent
7d0d3188c2
commit
56964cb4e2
@ -55,6 +55,10 @@
|
|||||||
"type": {
|
"type": {
|
||||||
"$ref": "#/definitions/param_type"
|
"$ref": "#/definitions/param_type"
|
||||||
},
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Parameter name."
|
||||||
|
},
|
||||||
"doc": {
|
"doc": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "Parameter-specific documentation."
|
"description": "Parameter-specific documentation."
|
||||||
|
@ -1,14 +1,50 @@
|
|||||||
import { action, observable } from "mobx";
|
import { action, observable } from "mobx";
|
||||||
import { editor } from "monaco-editor";
|
import { editor, languages } from "monaco-editor";
|
||||||
import AssemblyWorker from "worker-loader!./assembly_worker";
|
import AssemblyWorker from "worker-loader!./assembly_worker";
|
||||||
import {
|
import {
|
||||||
AssemblyChangeInput,
|
AssemblyChangeInput,
|
||||||
AssemblyWorkerOutput,
|
AssemblyWorkerOutput,
|
||||||
|
InputMessageType,
|
||||||
NewAssemblyInput,
|
NewAssemblyInput,
|
||||||
|
OutputMessageType,
|
||||||
|
SignatureHelpInput,
|
||||||
} from "./assembly_worker_messages";
|
} from "./assembly_worker_messages";
|
||||||
import { AssemblyError, AssemblyWarning } from "./assembly";
|
import { AssemblyError, AssemblyWarning } from "./assembly";
|
||||||
import { disassemble } from "./disassembly";
|
import { disassemble } from "./disassembly";
|
||||||
import { ObservableQuest } from "../domain/ObservableQuest";
|
import { ObservableQuest } from "../domain/ObservableQuest";
|
||||||
|
import { Kind, OPCODES } from "./opcodes";
|
||||||
|
import CompletionList = languages.CompletionList;
|
||||||
|
import CompletionItemKind = languages.CompletionItemKind;
|
||||||
|
import CompletionItem = languages.CompletionItem;
|
||||||
|
import IModelContentChange = editor.IModelContentChange;
|
||||||
|
import SignatureHelp = languages.SignatureHelp;
|
||||||
|
import ParameterInformation = languages.ParameterInformation;
|
||||||
|
|
||||||
|
const INSTRUCTION_SUGGESTIONS = OPCODES.filter(opcode => opcode != null).map(opcode => {
|
||||||
|
return ({
|
||||||
|
label: opcode.mnemonic,
|
||||||
|
kind: CompletionItemKind.Function,
|
||||||
|
insertText: opcode.mnemonic,
|
||||||
|
} as any) as languages.CompletionItem;
|
||||||
|
});
|
||||||
|
|
||||||
|
const KEYWORD_SUGGESTIONS = [
|
||||||
|
{
|
||||||
|
label: ".code",
|
||||||
|
kind: CompletionItemKind.Keyword,
|
||||||
|
insertText: "code",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: ".data",
|
||||||
|
kind: CompletionItemKind.Keyword,
|
||||||
|
insertText: "data",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: ".string",
|
||||||
|
kind: CompletionItemKind.Keyword,
|
||||||
|
insertText: "string",
|
||||||
|
},
|
||||||
|
] as CompletionItem[];
|
||||||
|
|
||||||
export class AssemblyAnalyser {
|
export class AssemblyAnalyser {
|
||||||
@observable warnings: AssemblyWarning[] = [];
|
@observable warnings: AssemblyWarning[] = [];
|
||||||
@ -16,6 +52,11 @@ export class AssemblyAnalyser {
|
|||||||
|
|
||||||
private worker = new AssemblyWorker();
|
private worker = new AssemblyWorker();
|
||||||
private quest?: ObservableQuest;
|
private quest?: ObservableQuest;
|
||||||
|
private promises = new Map<
|
||||||
|
number,
|
||||||
|
{ resolve: (result: any) => void; reject: (error: Error) => void }
|
||||||
|
>();
|
||||||
|
private message_id = 0;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.worker.onmessage = this.process_worker_message;
|
this.worker.onmessage = this.process_worker_message;
|
||||||
@ -24,16 +65,59 @@ export class AssemblyAnalyser {
|
|||||||
disassemble(quest: ObservableQuest): string[] {
|
disassemble(quest: ObservableQuest): string[] {
|
||||||
this.quest = quest;
|
this.quest = quest;
|
||||||
const assembly = disassemble(quest.object_code);
|
const assembly = disassemble(quest.object_code);
|
||||||
const message: NewAssemblyInput = { type: "new_assembly_input", assembly };
|
const message: NewAssemblyInput = { type: InputMessageType.NewAssembly, assembly };
|
||||||
this.worker.postMessage(message);
|
this.worker.postMessage(message);
|
||||||
return assembly;
|
return assembly;
|
||||||
}
|
}
|
||||||
|
|
||||||
update_assembly(changes: editor.IModelContentChange[]): void {
|
update_assembly(changes: IModelContentChange[]): void {
|
||||||
const message: AssemblyChangeInput = { type: "assembly_change_input", changes };
|
const message: AssemblyChangeInput = {
|
||||||
|
type: InputMessageType.AssemblyChange,
|
||||||
|
changes: changes.map(change => ({
|
||||||
|
start_line_no: change.range.startLineNumber,
|
||||||
|
start_col: change.range.startColumn,
|
||||||
|
end_line_no: change.range.endLineNumber,
|
||||||
|
end_col: change.range.endColumn,
|
||||||
|
new_text: change.text,
|
||||||
|
})),
|
||||||
|
};
|
||||||
this.worker.postMessage(message);
|
this.worker.postMessage(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
provide_completion_items(text: string): CompletionList {
|
||||||
|
const suggestions = /^\s*([a-z][a-z0-9_=<>!]*)?$/.test(text)
|
||||||
|
? INSTRUCTION_SUGGESTIONS
|
||||||
|
: /^\s*\.[a-z]+$/.test(text)
|
||||||
|
? KEYWORD_SUGGESTIONS
|
||||||
|
: [];
|
||||||
|
|
||||||
|
return {
|
||||||
|
suggestions,
|
||||||
|
incomplete: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async provide_signature_help(line_no: number, col: number): Promise<SignatureHelp | undefined> {
|
||||||
|
const id = this.message_id++;
|
||||||
|
|
||||||
|
return new Promise<SignatureHelp>((resolve, reject) => {
|
||||||
|
this.promises.set(id, { resolve, reject });
|
||||||
|
const message: SignatureHelpInput = {
|
||||||
|
type: InputMessageType.SignatureHelp,
|
||||||
|
id,
|
||||||
|
line_no,
|
||||||
|
col,
|
||||||
|
};
|
||||||
|
this.worker.postMessage(message);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
if (this.promises.delete(id)) {
|
||||||
|
reject(new Error("Signature help timed out."));
|
||||||
|
}
|
||||||
|
}, 5_000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
dispose(): void {
|
dispose(): void {
|
||||||
this.worker.terminate();
|
this.worker.terminate();
|
||||||
}
|
}
|
||||||
@ -42,11 +126,66 @@ export class AssemblyAnalyser {
|
|||||||
private process_worker_message = (e: MessageEvent): void => {
|
private process_worker_message = (e: MessageEvent): void => {
|
||||||
const message: AssemblyWorkerOutput = e.data;
|
const message: AssemblyWorkerOutput = e.data;
|
||||||
|
|
||||||
if (message.type === "new_object_code_output" && this.quest) {
|
switch (message.type) {
|
||||||
this.quest.object_code.splice(0, this.quest.object_code.length, ...message.object_code);
|
case OutputMessageType.NewObjectCode:
|
||||||
this.quest.set_map_designations(message.map_designations);
|
if (this.quest) {
|
||||||
this.warnings = message.warnings;
|
this.quest.object_code.splice(
|
||||||
this.errors = message.errors;
|
0,
|
||||||
|
this.quest.object_code.length,
|
||||||
|
...message.object_code,
|
||||||
|
);
|
||||||
|
this.quest.set_map_designations(message.map_designations);
|
||||||
|
this.warnings = message.warnings;
|
||||||
|
this.errors = message.errors;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case OutputMessageType.SignatureHelp:
|
||||||
|
{
|
||||||
|
const promise = this.promises.get(message.id);
|
||||||
|
|
||||||
|
if (promise) {
|
||||||
|
this.promises.delete(message.id);
|
||||||
|
|
||||||
|
if (message.opcode) {
|
||||||
|
let signature = message.opcode.mnemonic + " ";
|
||||||
|
const parameters: ParameterInformation[] = [];
|
||||||
|
let first = true;
|
||||||
|
|
||||||
|
for (const param of message.opcode.params) {
|
||||||
|
if (first) {
|
||||||
|
first = false;
|
||||||
|
} else {
|
||||||
|
signature += ", ";
|
||||||
|
}
|
||||||
|
|
||||||
|
const param_name = Kind[param.type.kind];
|
||||||
|
|
||||||
|
parameters.push({
|
||||||
|
label: [signature.length, signature.length + param_name.length],
|
||||||
|
documentation: param.doc,
|
||||||
|
});
|
||||||
|
|
||||||
|
signature += param_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
const help: SignatureHelp = {
|
||||||
|
signatures: [
|
||||||
|
{
|
||||||
|
label: signature,
|
||||||
|
documentation: message.opcode.doc,
|
||||||
|
parameters,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
activeSignature: 0,
|
||||||
|
activeParameter: message.active_param,
|
||||||
|
};
|
||||||
|
promise.resolve(help);
|
||||||
|
} else {
|
||||||
|
promise.resolve(undefined);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,17 @@
|
|||||||
import { AssemblyWorkerInput, NewObjectCodeOutput } from "./assembly_worker_messages";
|
import {
|
||||||
|
AssemblyChangeInput,
|
||||||
|
AssemblyWorkerInput,
|
||||||
|
InputMessageType,
|
||||||
|
NewObjectCodeOutput,
|
||||||
|
OutputMessageType,
|
||||||
|
SignatureHelpInput,
|
||||||
|
SignatureHelpOutput,
|
||||||
|
} from "./assembly_worker_messages";
|
||||||
import { assemble } from "./assembly";
|
import { assemble } from "./assembly";
|
||||||
import Logger from "js-logger";
|
import Logger from "js-logger";
|
||||||
import { SegmentType } from "./instructions";
|
import { SegmentType } from "./instructions";
|
||||||
import { Opcode } from "./opcodes";
|
import { Opcode, OPCODES_BY_MNEMONIC } from "./opcodes";
|
||||||
|
import { AssemblyLexer, IdentToken, TokenType } from "./AssemblyLexer";
|
||||||
|
|
||||||
Logger.useDefaults({
|
Logger.useDefaults({
|
||||||
defaultLevel: (Logger as any)[process.env["LOG_LEVEL"] || "OFF"],
|
defaultLevel: (Logger as any)[process.env["LOG_LEVEL"] || "OFF"],
|
||||||
@ -11,6 +20,7 @@ Logger.useDefaults({
|
|||||||
const ctx: Worker = self as any;
|
const ctx: Worker = self as any;
|
||||||
|
|
||||||
let lines: string[] = [];
|
let lines: string[] = [];
|
||||||
|
|
||||||
const messages: AssemblyWorkerInput[] = [];
|
const messages: AssemblyWorkerInput[] = [];
|
||||||
let timeout: any;
|
let timeout: any;
|
||||||
|
|
||||||
@ -31,47 +41,96 @@ function process_messages(): void {
|
|||||||
if (messages.length === 0) return;
|
if (messages.length === 0) return;
|
||||||
|
|
||||||
for (const message of messages.splice(0, messages.length)) {
|
for (const message of messages.splice(0, messages.length)) {
|
||||||
if (message.type === "new_assembly_input") {
|
switch (message.type) {
|
||||||
lines = message.assembly;
|
case InputMessageType.NewAssembly:
|
||||||
} else if (message.type === "assembly_change_input") {
|
lines = message.assembly;
|
||||||
for (const change of message.changes) {
|
assemble_and_send();
|
||||||
const { startLineNumber, endLineNumber, startColumn, endColumn } = change.range;
|
break;
|
||||||
const lines_changed = endLineNumber - startLineNumber + 1;
|
case InputMessageType.AssemblyChange:
|
||||||
const new_lines = change.text.split("\n");
|
assembly_change(message);
|
||||||
|
break;
|
||||||
|
case InputMessageType.SignatureHelp:
|
||||||
|
signature_help(message);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (lines_changed === 1) {
|
function assembly_change(message: AssemblyChangeInput): void {
|
||||||
replace_line_part(startLineNumber, startColumn, endColumn, new_lines);
|
for (const change of message.changes) {
|
||||||
} else if (new_lines.length === 1) {
|
const { start_line_no, end_line_no, start_col, end_col, new_text } = change;
|
||||||
replace_lines_and_merge_line_parts(
|
const lines_changed = end_line_no - start_line_no + 1;
|
||||||
startLineNumber,
|
const new_lines = new_text.split("\n");
|
||||||
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]);
|
|
||||||
|
|
||||||
// Keep the right part of the last changed line.
|
if (lines_changed === 1) {
|
||||||
replace_line_part_left(
|
replace_line_part(start_line_no, start_col, end_col, new_lines);
|
||||||
endLineNumber,
|
} else if (new_lines.length === 1) {
|
||||||
endColumn,
|
replace_lines_and_merge_line_parts(
|
||||||
new_lines[new_lines.length - 1],
|
start_line_no,
|
||||||
);
|
end_line_no,
|
||||||
|
start_col,
|
||||||
|
end_col,
|
||||||
|
new_lines[0],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Keep the left part of the first changed line.
|
||||||
|
replace_line_part_right(start_line_no, start_col, new_lines[0]);
|
||||||
|
|
||||||
// Replace all the lines in between.
|
// Keep the right part of the last changed line.
|
||||||
// It's important that we do this last.
|
replace_line_part_left(end_line_no, end_col, new_lines[new_lines.length - 1]);
|
||||||
replace_lines(
|
|
||||||
startLineNumber + 1,
|
// Replace all the lines in between.
|
||||||
endLineNumber - 1,
|
// It's important that we do this last.
|
||||||
new_lines.slice(1, new_lines.length - 1),
|
replace_lines(
|
||||||
);
|
start_line_no + 1,
|
||||||
|
end_line_no - 1,
|
||||||
|
new_lines.slice(1, new_lines.length - 1),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assemble_and_send();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hacky way of providing parameter hints.
|
||||||
|
// We just tokenize the current line and look for the first identifier and check whether it's a valid opcode.
|
||||||
|
function signature_help(message: SignatureHelpInput): void {
|
||||||
|
let opcode: Opcode | undefined;
|
||||||
|
let active_param = -1;
|
||||||
|
|
||||||
|
if (message.line_no < lines.length) {
|
||||||
|
const line = lines[message.line_no - 1];
|
||||||
|
const lexer = new AssemblyLexer();
|
||||||
|
const tokens = lexer.tokenize_line(line);
|
||||||
|
const ident = tokens.find(t => t.type === TokenType.Ident) as IdentToken | undefined;
|
||||||
|
|
||||||
|
if (ident) {
|
||||||
|
opcode = OPCODES_BY_MNEMONIC.get(ident.value);
|
||||||
|
|
||||||
|
if (opcode) {
|
||||||
|
for (const token of tokens) {
|
||||||
|
if (token.col + token.len > message.col) {
|
||||||
|
break;
|
||||||
|
} else if (token.type === TokenType.Ident && active_param === -1) {
|
||||||
|
active_param = 0;
|
||||||
|
} else if (token.type === TokenType.ArgSeparator) {
|
||||||
|
active_param++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const response: SignatureHelpOutput = {
|
||||||
|
type: OutputMessageType.SignatureHelp,
|
||||||
|
id: message.id,
|
||||||
|
opcode,
|
||||||
|
active_param,
|
||||||
|
};
|
||||||
|
ctx.postMessage(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
function assemble_and_send(): void {
|
||||||
const assembler_result = assemble(lines);
|
const assembler_result = assemble(lines);
|
||||||
const map_designations = new Map<number, number>();
|
const map_designations = new Map<number, number>();
|
||||||
|
|
||||||
@ -90,7 +149,7 @@ function process_messages(): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const response: NewObjectCodeOutput = {
|
const response: NewObjectCodeOutput = {
|
||||||
type: "new_object_code_output",
|
type: OutputMessageType.NewObjectCode,
|
||||||
map_designations,
|
map_designations,
|
||||||
...assembler_result,
|
...assembler_result,
|
||||||
};
|
};
|
||||||
|
@ -1,25 +1,56 @@
|
|||||||
import { editor } from "monaco-editor";
|
|
||||||
import { AssemblyError, AssemblyWarning } from "./assembly";
|
import { AssemblyError, AssemblyWarning } from "./assembly";
|
||||||
import { Segment } from "./instructions";
|
import { Segment } from "./instructions";
|
||||||
|
import { Opcode } from "./opcodes";
|
||||||
|
|
||||||
export type AssemblyWorkerInput = NewAssemblyInput | AssemblyChangeInput;
|
export enum InputMessageType {
|
||||||
|
NewAssembly,
|
||||||
|
AssemblyChange,
|
||||||
|
SignatureHelp,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type AssemblyWorkerInput = NewAssemblyInput | AssemblyChangeInput | SignatureHelpInput;
|
||||||
|
|
||||||
export type NewAssemblyInput = {
|
export type NewAssemblyInput = {
|
||||||
readonly type: "new_assembly_input";
|
readonly type: InputMessageType.NewAssembly;
|
||||||
readonly assembly: string[];
|
readonly assembly: string[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type AssemblyChangeInput = {
|
export type AssemblyChangeInput = {
|
||||||
readonly type: "assembly_change_input";
|
readonly type: InputMessageType.AssemblyChange;
|
||||||
readonly changes: editor.IModelContentChange[];
|
readonly changes: {
|
||||||
|
start_line_no: number;
|
||||||
|
start_col: number;
|
||||||
|
end_line_no: number;
|
||||||
|
end_col: number;
|
||||||
|
new_text: string;
|
||||||
|
}[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type AssemblyWorkerOutput = NewObjectCodeOutput;
|
export type SignatureHelpInput = {
|
||||||
|
readonly type: InputMessageType.SignatureHelp;
|
||||||
|
readonly id: number;
|
||||||
|
readonly line_no: number;
|
||||||
|
readonly col: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export enum OutputMessageType {
|
||||||
|
NewObjectCode,
|
||||||
|
SignatureHelp,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type AssemblyWorkerOutput = NewObjectCodeOutput | SignatureHelpOutput;
|
||||||
|
|
||||||
export type NewObjectCodeOutput = {
|
export type NewObjectCodeOutput = {
|
||||||
readonly type: "new_object_code_output";
|
readonly type: OutputMessageType.NewObjectCode;
|
||||||
readonly object_code: Segment[];
|
readonly object_code: Segment[];
|
||||||
readonly map_designations: Map<number, number>;
|
readonly map_designations: Map<number, number>;
|
||||||
readonly warnings: AssemblyWarning[];
|
readonly warnings: AssemblyWarning[];
|
||||||
readonly errors: AssemblyError[];
|
readonly errors: AssemblyError[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type SignatureHelpOutput = {
|
||||||
|
readonly type: OutputMessageType.SignatureHelp;
|
||||||
|
readonly id: number;
|
||||||
|
readonly opcode?: Opcode;
|
||||||
|
readonly active_param: number;
|
||||||
|
};
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
import { autorun } from "mobx";
|
import { autorun } from "mobx";
|
||||||
import { editor, languages, MarkerSeverity } from "monaco-editor";
|
import { editor, languages, MarkerSeverity, Position } from "monaco-editor";
|
||||||
import React, { Component, createRef, ReactNode } from "react";
|
import React, { Component, createRef, ReactNode } from "react";
|
||||||
import { AutoSizer } from "react-virtualized";
|
import { AutoSizer } from "react-virtualized";
|
||||||
import { AssemblyAnalyser } from "../scripting/AssemblyAnalyser";
|
import { AssemblyAnalyser } from "../scripting/AssemblyAnalyser";
|
||||||
import { OPCODES } from "../scripting/opcodes";
|
|
||||||
import { quest_editor_store } from "../stores/QuestEditorStore";
|
import { quest_editor_store } from "../stores/QuestEditorStore";
|
||||||
import { Action } from "../../core/undo";
|
import { Action } from "../../core/undo";
|
||||||
import styles from "./AssemblyEditorComponent.css";
|
import styles from "./AssemblyEditorComponent.css";
|
||||||
|
import CompletionList = languages.CompletionList;
|
||||||
|
import ITextModel = editor.ITextModel;
|
||||||
|
import IStandaloneCodeEditor = editor.IStandaloneCodeEditor;
|
||||||
|
import SignatureHelp = languages.SignatureHelp;
|
||||||
|
|
||||||
const ASM_SYNTAX: languages.IMonarchLanguage = {
|
const ASM_SYNTAX: languages.IMonarchLanguage = {
|
||||||
defaultToken: "invalid",
|
defaultToken: "invalid",
|
||||||
@ -59,54 +62,37 @@ const ASM_SYNTAX: languages.IMonarchLanguage = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const INSTRUCTION_SUGGESTIONS = OPCODES.filter(opcode => opcode != null).map(opcode => {
|
const assembly_analyser = new AssemblyAnalyser();
|
||||||
return ({
|
|
||||||
label: opcode.mnemonic,
|
|
||||||
kind: languages.CompletionItemKind.Function,
|
|
||||||
insertText: opcode.mnemonic,
|
|
||||||
} as any) as languages.CompletionItem;
|
|
||||||
});
|
|
||||||
|
|
||||||
const KEYWORD_SUGGESTIONS = [
|
|
||||||
{
|
|
||||||
label: ".code",
|
|
||||||
kind: languages.CompletionItemKind.Keyword,
|
|
||||||
insertText: "code",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: ".data",
|
|
||||||
kind: languages.CompletionItemKind.Keyword,
|
|
||||||
insertText: "data",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: ".string",
|
|
||||||
kind: languages.CompletionItemKind.Keyword,
|
|
||||||
insertText: "string",
|
|
||||||
},
|
|
||||||
] as languages.CompletionItem[];
|
|
||||||
|
|
||||||
languages.register({ id: "psoasm" });
|
languages.register({ id: "psoasm" });
|
||||||
|
|
||||||
languages.setMonarchTokensProvider("psoasm", ASM_SYNTAX);
|
languages.setMonarchTokensProvider("psoasm", ASM_SYNTAX);
|
||||||
|
|
||||||
languages.registerCompletionItemProvider("psoasm", {
|
languages.registerCompletionItemProvider("psoasm", {
|
||||||
provideCompletionItems: (model, position) => {
|
provideCompletionItems(model, position): CompletionList {
|
||||||
const value = model.getValueInRange({
|
const text = model.getValueInRange({
|
||||||
startLineNumber: position.lineNumber,
|
startLineNumber: position.lineNumber,
|
||||||
endLineNumber: position.lineNumber,
|
endLineNumber: position.lineNumber,
|
||||||
startColumn: 1,
|
startColumn: 1,
|
||||||
endColumn: position.column,
|
endColumn: position.column,
|
||||||
});
|
});
|
||||||
const suggestions = /^\s*([a-z][a-z0-9_=<>!]*)?$/.test(value)
|
return assembly_analyser.provide_completion_items(text);
|
||||||
? INSTRUCTION_SUGGESTIONS
|
|
||||||
: /^\s*\.[a-z]+$/.test(value)
|
|
||||||
? KEYWORD_SUGGESTIONS
|
|
||||||
: [];
|
|
||||||
|
|
||||||
return {
|
|
||||||
suggestions,
|
|
||||||
incomplete: false,
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
languages.registerSignatureHelpProvider("psoasm", {
|
||||||
|
signatureHelpTriggerCharacters: [" ", ","],
|
||||||
|
|
||||||
|
signatureHelpRetriggerCharacters: [", "],
|
||||||
|
|
||||||
|
provideSignatureHelp(
|
||||||
|
_model: ITextModel,
|
||||||
|
position: Position,
|
||||||
|
): Promise<SignatureHelp | undefined> {
|
||||||
|
return assembly_analyser.provide_signature_help(position.lineNumber, position.column);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
languages.setLanguageConfiguration("psoasm", {
|
languages.setLanguageConfiguration("psoasm", {
|
||||||
indentationRules: {
|
indentationRules: {
|
||||||
increaseIndentPattern: /^\s*\d+:/,
|
increaseIndentPattern: /^\s*\d+:/,
|
||||||
@ -157,8 +143,7 @@ type MonacoProps = {
|
|||||||
|
|
||||||
class MonacoComponent extends Component<MonacoProps> {
|
class MonacoComponent extends Component<MonacoProps> {
|
||||||
private div_ref = createRef<HTMLDivElement>();
|
private div_ref = createRef<HTMLDivElement>();
|
||||||
private editor?: editor.IStandaloneCodeEditor;
|
private editor?: IStandaloneCodeEditor;
|
||||||
private assembly_analyser?: AssemblyAnalyser;
|
|
||||||
private disposers: (() => void)[] = [];
|
private disposers: (() => void)[] = [];
|
||||||
|
|
||||||
render(): ReactNode {
|
render(): ReactNode {
|
||||||
@ -177,8 +162,6 @@ class MonacoComponent extends Component<MonacoProps> {
|
|||||||
wrappingIndent: "indent",
|
wrappingIndent: "indent",
|
||||||
});
|
});
|
||||||
|
|
||||||
this.assembly_analyser = new AssemblyAnalyser();
|
|
||||||
|
|
||||||
this.disposers.push(
|
this.disposers.push(
|
||||||
this.dispose,
|
this.dispose,
|
||||||
autorun(this.update_model),
|
autorun(this.update_model),
|
||||||
@ -209,8 +192,8 @@ class MonacoComponent extends Component<MonacoProps> {
|
|||||||
private update_model = () => {
|
private update_model = () => {
|
||||||
const quest = quest_editor_store.current_quest;
|
const quest = quest_editor_store.current_quest;
|
||||||
|
|
||||||
if (quest && this.editor && this.assembly_analyser) {
|
if (quest && this.editor) {
|
||||||
const assembly = this.assembly_analyser.disassemble(quest);
|
const assembly = assembly_analyser.disassemble(quest);
|
||||||
const model = editor.createModel(assembly.join("\n"), "psoasm");
|
const model = editor.createModel(assembly.join("\n"), "psoasm");
|
||||||
|
|
||||||
quest_editor_store.script_undo.action = new Action(
|
quest_editor_store.script_undo.action = new Action(
|
||||||
@ -260,8 +243,7 @@ class MonacoComponent extends Component<MonacoProps> {
|
|||||||
|
|
||||||
current_version = version;
|
current_version = version;
|
||||||
|
|
||||||
if (!this.assembly_analyser) return;
|
assembly_analyser.update_assembly(e.changes);
|
||||||
this.assembly_analyser.update_assembly(e.changes);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.disposers.push(() => disposable.dispose());
|
this.disposers.push(() => disposable.dispose());
|
||||||
@ -273,10 +255,11 @@ class MonacoComponent extends Component<MonacoProps> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
private update_model_markers = () => {
|
private update_model_markers = () => {
|
||||||
if (!this.editor || !this.assembly_analyser) return;
|
if (!this.editor) return;
|
||||||
|
|
||||||
// Reference errors here to make sure we get mobx updates.
|
// Reference warnings and errors here to make sure we get mobx updates.
|
||||||
this.assembly_analyser.errors.length;
|
assembly_analyser.warnings.length;
|
||||||
|
assembly_analyser.errors.length;
|
||||||
|
|
||||||
const model = this.editor.getModel();
|
const model = this.editor.getModel();
|
||||||
if (!model) return;
|
if (!model) return;
|
||||||
@ -284,14 +267,25 @@ class MonacoComponent extends Component<MonacoProps> {
|
|||||||
editor.setModelMarkers(
|
editor.setModelMarkers(
|
||||||
model,
|
model,
|
||||||
"psoasm",
|
"psoasm",
|
||||||
this.assembly_analyser.errors.map(error => ({
|
assembly_analyser.warnings
|
||||||
severity: MarkerSeverity.Error,
|
.map(warning => ({
|
||||||
message: error.message,
|
severity: MarkerSeverity.Warning,
|
||||||
startLineNumber: error.line_no,
|
message: warning.message,
|
||||||
endLineNumber: error.line_no,
|
startLineNumber: warning.line_no,
|
||||||
startColumn: error.col,
|
endLineNumber: warning.line_no,
|
||||||
endColumn: error.col + error.length,
|
startColumn: warning.col,
|
||||||
})),
|
endColumn: warning.col + warning.length,
|
||||||
|
}))
|
||||||
|
.concat(
|
||||||
|
assembly_analyser.errors.map(error => ({
|
||||||
|
severity: MarkerSeverity.Error,
|
||||||
|
message: error.message,
|
||||||
|
startLineNumber: error.line_no,
|
||||||
|
endLineNumber: error.line_no,
|
||||||
|
startColumn: error.col,
|
||||||
|
endColumn: error.col + error.length,
|
||||||
|
})),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -302,9 +296,5 @@ class MonacoComponent extends Component<MonacoProps> {
|
|||||||
if (model) model.dispose();
|
if (model) model.dispose();
|
||||||
this.editor = undefined;
|
this.editor = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.assembly_analyser) {
|
|
||||||
this.assembly_analyser.dispose();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user