mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2025-04-04 01:12:50 +02:00
320 lines
13 KiB
JavaScript
320 lines
13 KiB
JavaScript
import { ScriptEditor } from "./ScriptEditor";
|
|
|
|
const ace = require('brace');
|
|
|
|
require('brace/mode/javascript');
|
|
require('./AceNetscriptMode');
|
|
require('brace/theme/chaos');
|
|
require('brace/theme/chrome');
|
|
require('brace/theme/monokai');
|
|
require('brace/theme/solarized_dark');
|
|
require('brace/theme/solarized_light');
|
|
require('brace/theme/terminal');
|
|
require('brace/theme/twilight');
|
|
require('brace/theme/xcode');
|
|
require("brace/keybinding/vim");
|
|
require("brace/keybinding/emacs");
|
|
require("brace/ext/language_tools");
|
|
|
|
import { NetscriptFunctions } from "../NetscriptFunctions";
|
|
import { Settings } from "../Settings/Settings";
|
|
import { AceKeybindingSetting } from "../Settings/SettingEnums";
|
|
|
|
import { clearEventListeners } from "../../utils/uiHelpers/clearEventListeners";
|
|
import { createElement } from "../../utils/uiHelpers/createElement";
|
|
import { createOptionElement } from "../../utils/uiHelpers/createOptionElement";
|
|
import { getSelectText,
|
|
getSelectValue } from "../../utils/uiHelpers/getSelectData";
|
|
import { removeChildrenFromElement } from "../../utils/uiHelpers/removeChildrenFromElement";
|
|
|
|
// Wrapper for Ace editor
|
|
const Keybindings = {
|
|
ace: null,
|
|
vim: "ace/keyboard/vim",
|
|
emacs: "ace/keyboard/emacs",
|
|
};
|
|
|
|
function validateInitializationParamters(params) {
|
|
if (params.saveAndCloseFn == null) { return false; } // Save & close button function
|
|
if (params.quitFn == null) { return false; } // Quitting editor, aka Engine.loadTerminalContent
|
|
|
|
return true;
|
|
}
|
|
|
|
class AceEditorWrapper extends ScriptEditor {
|
|
constructor() {
|
|
super();
|
|
this.vimCommandDisplayWrapper = null;
|
|
}
|
|
|
|
init(params) {
|
|
if (this.editor != null) {
|
|
console.error(`AceEditor.init() called when it's already initialized`);
|
|
return false;
|
|
}
|
|
|
|
// Validate/Sanitize input
|
|
if (!validateInitializationParamters(params)) {
|
|
console.error(`'params' argument passed into initAceEditor() does not have proper properties`);
|
|
return false;
|
|
}
|
|
|
|
// Store the filename input
|
|
this.filenameInput = document.getElementById("script-editor-filename");
|
|
if (this.filenameInput == null) {
|
|
console.error(`Could not get Script Editor filename element (id=script-editor-filename)`);
|
|
return false;
|
|
}
|
|
|
|
// Initialize ACE Script editor
|
|
this.editor = ace.edit('ace-editor');
|
|
this.editor.getSession().setMode('ace/mode/netscript');
|
|
this.editor.setTheme('ace/theme/monokai');
|
|
const editorElement = document.getElementById('ace-editor');
|
|
if (editorElement == null) { return false; }
|
|
editorElement.style.fontSize = '16px';
|
|
this.editor.setOption("showPrintMargin", false);
|
|
|
|
// Configure some of the VIM keybindings
|
|
ace.config.loadModule('ace/keyboard/vim', function(module) {
|
|
var VimApi = module.CodeMirror.Vim;
|
|
VimApi.defineEx('write', 'w', function(cm, input) {
|
|
params.saveAndCloseFn();
|
|
});
|
|
VimApi.defineEx('quit', 'q', function(cm, input) {
|
|
params.quitFn();
|
|
});
|
|
VimApi.defineEx('xwritequit', 'x', function(cm, input) {
|
|
params.saveAndCloseFn();
|
|
});
|
|
VimApi.defineEx('wqwritequit', 'wq', function(cm, input) {
|
|
params.saveAndCloseFn();
|
|
});
|
|
});
|
|
|
|
// Store a reference to the VIM command display
|
|
this.vimCommandDisplayWrapper = document.getElementById("codemirror-vim-command-display-wrapper");
|
|
if (this.vimCommandDisplayWrapper == null) {
|
|
console.error(`Could not get Vim Command Display element (id=codemirror-vim-command-display-wrapper)`);
|
|
return false;
|
|
}
|
|
|
|
//Function autocompleter
|
|
this.editor.setOption("enableBasicAutocompletion", true);
|
|
var autocompleter = {
|
|
getCompletions: function(editor, session, pos, prefix, callback) {
|
|
if (prefix.length === 0) {callback(null, []); return;}
|
|
var words = [];
|
|
var fns = NetscriptFunctions(null);
|
|
for (let name in fns) {
|
|
if (fns.hasOwnProperty(name)) {
|
|
words.push({
|
|
name: name,
|
|
value: name,
|
|
});
|
|
|
|
//Get functions from namespaces
|
|
const namespaces = ["bladeburner", "hacknet", "codingcontract", "gang", "sleeve"];
|
|
if (namespaces.includes(name)) {
|
|
let namespace = fns[name];
|
|
if (typeof namespace !== "object") {continue;}
|
|
let namespaceFns = Object.keys(namespace);
|
|
for (let i = 0; i < namespaceFns.length; ++i) {
|
|
words.push({
|
|
name: namespaceFns[i],
|
|
value: namespaceFns[i],
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
callback(null, words);
|
|
},
|
|
}
|
|
this.editor.completers = [autocompleter];
|
|
|
|
return true;
|
|
}
|
|
|
|
initialized() {
|
|
return (this.editor != null);
|
|
}
|
|
|
|
// Create the configurable Options for this Editor
|
|
create() {
|
|
function safeGetElementById(id, whatFor="") {
|
|
const elem = document.getElementById(id);
|
|
if (elem == null) {
|
|
throw new Error(`Could not find ${whatFor} DOM element(id=${id})`);
|
|
}
|
|
|
|
return elem;
|
|
}
|
|
|
|
function safeClearEventListeners(id, whatFor="") {
|
|
const elem = clearEventListeners(id);
|
|
if (elem == null) {
|
|
throw new Error(`Could not find ${whatFor} DOM element(id=${id})`);
|
|
}
|
|
|
|
return elem;
|
|
}
|
|
|
|
try {
|
|
const optionsPanel = safeGetElementById("script-editor-options-panel", "Script Editor Options Panel");
|
|
|
|
// Set editor to visible
|
|
const elem = document.getElementById("ace-editor");
|
|
if (elem instanceof HTMLElement) {
|
|
elem.style.display = "block";
|
|
}
|
|
|
|
// Make sure the Vim command display from CodeMirror is invisible
|
|
if (this.vimCommandDisplayWrapper instanceof HTMLElement) {
|
|
this.vimCommandDisplayWrapper.style.display = "none";
|
|
}
|
|
|
|
// Theme
|
|
const themeDropdown = safeClearEventListeners("script-editor-option-theme", "Theme Selector");
|
|
removeChildrenFromElement(themeDropdown);
|
|
themeDropdown.add(createOptionElement("Chaos"));
|
|
themeDropdown.add(createOptionElement("Chrome"));
|
|
themeDropdown.add(createOptionElement("Monokai"));
|
|
themeDropdown.add(createOptionElement("Solarized Dark", "Solarized_Dark"));
|
|
themeDropdown.add(createOptionElement("Solarized Light", "Solarized_Light"));
|
|
themeDropdown.add(createOptionElement("Terminal"));
|
|
themeDropdown.add(createOptionElement("Twilight"));
|
|
themeDropdown.add(createOptionElement("XCode"));
|
|
if (Settings.EditorTheme) {
|
|
var initialIndex = 2;
|
|
for (var i = 0; i < themeDropdown.options.length; ++i) {
|
|
if (themeDropdown.options[i].value === Settings.EditorTheme) {
|
|
initialIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
themeDropdown.selectedIndex = initialIndex;
|
|
} else {
|
|
themeDropdown.selectedIndex = 2;
|
|
}
|
|
|
|
themeDropdown.onchange = () => {
|
|
const val = themeDropdown.value;
|
|
Settings.EditorTheme = val;
|
|
const themePath = "ace/theme/" + val.toLowerCase();
|
|
this.editor.setTheme(themePath);
|
|
};
|
|
themeDropdown.onchange();
|
|
|
|
// Keybinding
|
|
const keybindingDropdown = safeClearEventListeners("script-editor-option-keybinding", "Keybinding Selector");
|
|
removeChildrenFromElement(keybindingDropdown);
|
|
keybindingDropdown.add(createOptionElement("Ace", AceKeybindingSetting.Ace));
|
|
keybindingDropdown.add(createOptionElement("Vim", AceKeybindingSetting.Vim));
|
|
keybindingDropdown.add(createOptionElement("Emacs", AceKeybindingSetting.Emacs));
|
|
if (Settings.EditorKeybinding) {
|
|
// Sanitize the Keybinding setting
|
|
if (!(Object.values(AceKeybindingSetting).includes(Settings.EditorKeybinding))) {
|
|
Settings.EditorKeybinding = AceKeybindingSetting.Ace;
|
|
}
|
|
var initialIndex = 0;
|
|
for (var i = 0; i < keybindingDropdown.options.length; ++i) {
|
|
if (keybindingDropdown.options[i].value === Settings.EditorKeybinding) {
|
|
initialIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
keybindingDropdown.selectedIndex = initialIndex;
|
|
} else {
|
|
keybindingDropdown.selectedIndex = 0;
|
|
}
|
|
keybindingDropdown.onchange = () => {
|
|
var val = keybindingDropdown.value;
|
|
Settings.EditorKeybinding = val;
|
|
this.editor.setKeyboardHandler(Keybindings[val.toLowerCase()]);
|
|
};
|
|
keybindingDropdown.onchange();
|
|
|
|
// Highlight Active line
|
|
const highlightActiveChkBox = safeClearEventListeners("script-editor-option-highlightactiveline", "Active Line Checkbox");
|
|
highlightActiveChkBox.onchange = () => {
|
|
this.editor.setHighlightActiveLine(highlightActiveChkBox.checked);
|
|
};
|
|
|
|
// Show Invisibles
|
|
const showInvisiblesChkBox = safeClearEventListeners("script-editor-option-showinvisibles", "Show Invisible Checkbox");
|
|
showInvisiblesChkBox.onchange = () => {
|
|
this.editor.setShowInvisibles(showInvisiblesChkBox.checked);
|
|
};
|
|
|
|
// Use Soft Tab
|
|
const softTabChkBox = safeClearEventListeners("script-editor-option-usesofttab", "Soft Tab Checkbox");
|
|
softTabChkBox.onchange = () => {
|
|
this.editor.getSession().setUseSoftTabs(softTabChkBox.checked);
|
|
};
|
|
|
|
// Some helper functions for dealing with flexible options
|
|
function resetFlexibleOption(id) {
|
|
const fieldset = safeGetElementById(id);
|
|
removeChildrenFromElement(fieldset);
|
|
fieldset.style.display = "block";
|
|
return fieldset;
|
|
}
|
|
|
|
function removeFlexibleOption(id) {
|
|
// This doesn't really remove it, just sets it to invisible
|
|
const fieldset = resetFlexibleOption(id);
|
|
fieldset.style.display = "none";
|
|
return fieldset;
|
|
}
|
|
|
|
// Jshint Maxerr (Flex 1)
|
|
const flex1Fieldset = resetFlexibleOption("script-editor-option-flex1-fieldset");
|
|
const flex1Id = "script-editor-option-maxerr";
|
|
const flex1ValueLabel = createElement("em", { innerText: "200" });
|
|
flex1Fieldset.appendChild(createElement("label", {
|
|
for: flex1Id,
|
|
innerText: "Max Error Count",
|
|
}));
|
|
const flex1Input = createElement("input", {
|
|
id: flex1Id,
|
|
max: "1000",
|
|
min: "50",
|
|
name: flex1Id,
|
|
step: "1",
|
|
type: "range",
|
|
value: "200",
|
|
changeListener: () => {
|
|
this.editor.getSession().$worker.send("changeOptions", [{maxerr:flex1Input.value}]);
|
|
flex1ValueLabel.innerText = flex1Input.value;
|
|
}
|
|
});
|
|
flex1Fieldset.appendChild(flex1Input);
|
|
flex1Fieldset.appendChild(flex1ValueLabel);
|
|
|
|
// Nothing for Flex Options 2-4
|
|
removeFlexibleOption("script-editor-option-flex2-fieldset");
|
|
removeFlexibleOption("script-editor-option-flex3-fieldset");
|
|
removeFlexibleOption("script-editor-option-flex4-fieldset");
|
|
} catch(e) {
|
|
console.error(`Exception caught: ${e}`);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
isFocused() {
|
|
if (this.editor == null) { return false; }
|
|
return this.editor.isFocused();
|
|
}
|
|
|
|
// Sets the editor to be invisible. Does not require this class to be initialized
|
|
setInvisible() {
|
|
const elem = document.getElementById("ace-editor");
|
|
if (elem instanceof HTMLElement) {
|
|
elem.style.display = "none";
|
|
}
|
|
}
|
|
}
|
|
|
|
export const AceEditor = new AceEditorWrapper();
|