bitburner-src/src/ScriptEditor/ScriptEditor.ts

71 lines
3.3 KiB
TypeScript

import type { ContentFilePath } from "../Paths/ContentFile";
import { EventEmitter } from "../utils/EventEmitter";
import * as monaco from "monaco-editor";
import { loadThemes, makeTheme, sanitizeTheme } from "./ui/themes";
import libSource from "!!raw-loader!./NetscriptDefinitions.d.ts";
import { Settings } from "../Settings/Settings";
import { NetscriptExtra } from "../NetscriptFunctions/Extra";
import * as enums from "../Enums";
import { ns } from "../NetscriptFunctions";
/** Event emitter used for tracking when changes have been made to a content file. */
export const fileEditEvents = new EventEmitter<[hostname: string, filename: ContentFilePath]>();
export class ScriptEditor {
// TODO: This will store info about currently open scripts.
// Among other things, this will allow informing the script editor of changes made elsewhere, even if the script editor is not being rendered.
// openScripts: OpenScript[] = [];
// Currently, this object is only used for initialization.
isInitialized = false;
initialize() {
if (this.isInitialized) return;
this.isInitialized = true;
// populate API keys for adding tokenization
const apiKeys: string[] = [];
const api = { args: [], pid: 1, enums, ...ns };
const hiddenAPI = NetscriptExtra();
function populate(apiLayer: object = api) {
for (const [apiKey, apiValue] of Object.entries(apiLayer)) {
if (apiLayer === api && apiKey in hiddenAPI) continue;
apiKeys.push(apiKey);
if (typeof apiValue === "object") populate(apiValue);
}
}
populate();
// Add api keys to tokenization
(async function () {
// We have to improve the default js language otherwise theme sucks
const jsLanguage = monaco.languages.getLanguages().find((l) => l.id === "javascript");
// Unsupported function is not exposed in monaco public API.
const l = await (jsLanguage as any).loader();
// replaced the bare tokens with regexes surrounded by \b, e.g. \b{token}\b which matches a word-break on either side
// this prevents the highlighter from highlighting pieces of variables that start with a reserved token name
l.language.tokenizer.root.unshift([new RegExp("\\bns\\b"), { token: "ns" }]);
for (const symbol of apiKeys)
l.language.tokenizer.root.unshift([new RegExp(`\\b${symbol}\\b`), { token: "netscriptfunction" }]);
const otherKeywords = ["let", "const", "var", "function"];
const otherKeyvars = ["true", "false", "null", "undefined"];
otherKeywords.forEach((k) =>
l.language.tokenizer.root.unshift([new RegExp(`\\b${k}\\b`), { token: "otherkeywords" }]),
);
otherKeyvars.forEach((k) =>
l.language.tokenizer.root.unshift([new RegExp(`\\b${k}\\b`), { token: "otherkeyvars" }]),
);
l.language.tokenizer.root.unshift([new RegExp("\\bthis\\b"), { token: "this" }]);
})();
// Add ts definitions for API
const source = (libSource + "").replace(/export /g, "");
monaco.languages.typescript.javascriptDefaults.addExtraLib(source, "netscript.d.ts");
monaco.languages.typescript.typescriptDefaults.addExtraLib(source, "netscript.d.ts");
// Load themes
loadThemes(monaco.editor.defineTheme);
sanitizeTheme(Settings.EditorTheme);
monaco.editor.defineTheme("customTheme", makeTheme(Settings.EditorTheme));
}
}
export const scriptEditor = new ScriptEditor();