mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2026-04-22 09:13:07 +02:00
Implemented new Editor Option: CodeMirror. (Vim mode not 100% done yet)
This commit is contained in:
@@ -0,0 +1,301 @@
|
||||
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 { 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();
|
||||
}
|
||||
|
||||
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();
|
||||
});
|
||||
});
|
||||
|
||||
//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"];
|
||||
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";
|
||||
}
|
||||
|
||||
// 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", "ace"));
|
||||
keybindingDropdown.add(createOptionElement("Vim", "vim"));
|
||||
keybindingDropdown.add(createOptionElement("Emacs", "emacs"));
|
||||
if (Settings.EditorKeybinding) {
|
||||
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();
|
||||
Reference in New Issue
Block a user