Implemented new Editor Option: CodeMirror. (Vim mode not 100% done yet)

This commit is contained in:
danielyxie
2019-01-27 14:08:45 -08:00
parent a2237d4319
commit 8d2c007bcb
39 changed files with 69645 additions and 9328 deletions
+2 -2
View File
@@ -15,8 +15,8 @@ import { saveObject } from "../SaveObject";
import { Script,
RunningScript} from "../Script";
import { Server } from "../Server";
import { OwnedAugmentationsOrderSetting } from "../SettingEnums";
import { Settings } from "../Settings";
import { OwnedAugmentationsOrderSetting } from "../Settings/SettingEnums";
import { Settings } from "../Settings/Settings";
import { SourceFiles } from "../SourceFile";
import { dialogBoxCreate } from "../../utils/DialogBox";
+1
View File
@@ -524,6 +524,7 @@ export let CONSTANTS: IMap<any> = {
* Home Computer RAM is now capped at 2 ^ 30 GB (1073741824 GB)
* Pop-up dialog boxes are a little bit bigger
* Bug Fix: When importing scripts, "./" will now be properly ignored (e.g. import { foo } from "./lib.script" )
`
}
+2 -2
View File
@@ -10,8 +10,8 @@ import { FactionInfos } from "./FactionInfo";
import { Locations} from "../Location";
import { HackingMission, setInMission } from "../Missions";
import { Player } from "../Player";
import { PurchaseAugmentationsOrderSetting } from "../SettingEnums";
import { Settings } from "../Settings";
import { PurchaseAugmentationsOrderSetting } from "../Settings/SettingEnums";
import { Settings } from "../Settings/Settings";
import {Page, routing} from "../ui/navigationTracking";
import {numeralWrapper} from "../ui/numeralFormat";
+1 -1
View File
@@ -1,6 +1,6 @@
import {Engine} from "./engine";
import {Player} from "./Player";
import {Settings} from "./Settings";
import {Settings} from "./Settings/Settings";
import {clearEventListeners} from "../utils/uiHelpers/clearEventListeners";
import {createElement} from "../utils/uiHelpers/createElement";
import {createPopup} from "../utils/uiHelpers/createPopup";
+1 -1
View File
@@ -14,7 +14,7 @@ import {Player} from "./Player";
import {Server, AllServers, AddToAllServers} from "./Server";
import {purchaseServer,
purchaseRamForHomeComputer} from "./ServerPurchases";
import {Settings} from "./Settings";
import {Settings} from "./Settings/Settings";
import { SourceFileFlags } from "./SourceFile/SourceFileFlags";
import {SpecialServerNames, SpecialServerIps} from "./SpecialServerIps";
+1 -1
View File
@@ -6,7 +6,7 @@ import { inMission } from "./Missions";
import { Player } from "./Player";
import { redPillFlag } from "./RedPill";
import { GetServerByHostname } from "./Server";
import { Settings } from "./Settings";
import { Settings } from "./Settings/Settings";
import { dialogBoxCreate,
dialogBoxOpened} from "../utils/DialogBox";
import {Reviver, Generic_toJSON,
+1 -1
View File
@@ -4,7 +4,7 @@ import { Player } from "./Player";
import { Environment } from "./NetscriptEnvironment";
import { WorkerScript, addWorkerScript} from "./NetscriptWorker";
import { Server, getServer} from "./Server";
import { Settings } from "./Settings";
import { Settings } from "./Settings/Settings";
import { Script, findRunningScript,
RunningScript } from "./Script";
+1 -1
View File
@@ -40,7 +40,7 @@ import {Script, findRunningScript, RunningScript,
import {Server, getServer, AddToAllServers,
AllServers, processSingleServerGrowth,
GetServerByHostname, numCycleForGrowth} from "./Server";
import {Settings} from "./Settings";
import {Settings} from "./Settings/Settings";
import {SpecialServerIps} from "./SpecialServerIps";
import {Stock} from "./StockMarket/Stock";
import {StockMarket, StockSymbols, SymbolToStockMap,
+1 -1
View File
@@ -1,4 +1,4 @@
import {Settings} from "./Settings";
import {Settings} from "./Settings/Settings";
function NetscriptPort() {
this.data = [];
+1 -1
View File
@@ -12,7 +12,7 @@ import {NetscriptFunctions} from "./NetscriptFunctions";
import {executeJSScript} from "./NetscriptJSEvaluator";
import {NetscriptPort} from "./NetscriptPort";
import {AllServers} from "./Server";
import {Settings} from "./Settings";
import {Settings} from "./Settings/Settings";
import {generate} from 'escodegen';
+1 -1
View File
@@ -25,7 +25,7 @@ import {Locations} from "./Locations";
import {hasBn11SF, hasWallStreetSF,hasAISF} from "./NetscriptFunctions";
import { Sleeve } from "./PersonObjects/Sleeve/Sleeve";
import {AllServers, Server, AddToAllServers} from "./Server";
import {Settings} from "./Settings";
import {Settings} from "./Settings/Settings";
import {SpecialServerIps, SpecialServerNames} from "./SpecialServerIps";
import {SourceFiles, applySourceFile} from "./SourceFile";
import { SourceFileFlags } from "./SourceFile/SourceFileFlags";
+1 -1
View File
@@ -14,7 +14,7 @@ import {loadMessages, initMessages, Messages} from "./Message";
import {Player, loadPlayer} from "./Player";
import {loadAllRunningScripts} from "./Script";
import {AllServers, loadAllServers} from "./Server";
import {Settings} from "./Settings";
import {Settings} from "./Settings/Settings";
import {loadSpecialServerIps, SpecialServerIps} from "./SpecialServerIps";
import {loadStockMarket, StockMarket} from "./StockMarket/StockMarket";
import {dialogBoxCreate} from "../utils/DialogBox";
+113 -180
View File
@@ -1,19 +1,3 @@
var ace = require('brace');
var beautify = require('js-beautify').js_beautify;
require('brace/mode/javascript');
require('../netscript');
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");
// Importing this doesn't work for some reason.
const walk = require("acorn/dist/walk");
@@ -26,8 +10,11 @@ import {evaluateImport} from "./NetscriptEvaluator";
import {NetscriptFunctions} from "./NetscriptFunctions";
import {addWorkerScript, WorkerScript} from "./NetscriptWorker";
import {Player} from "./Player";
import { AceEditor } from "./ScriptEditor/Ace";
import { CodeMirrorEditor } from "./ScriptEditor/CodeMirror";
import {AllServers, processSingleServerGrowth} from "./Server";
import {Settings} from "./Settings";
import { Settings } from "./Settings/Settings";
import { EditorSetting } from "./Settings/SettingEnums";
import {post} from "./ui/postToTerminal";
import {TextFile} from "./TextFile";
import {parse, Node} from "../utils/acorn";
@@ -41,47 +28,42 @@ import {createElement} from "../utils/uiHelpers/createE
import {getTimestamp} from "../utils/helpers/getTimestamp";
import {roundToTwo} from "../utils/helpers/roundToTwo";
var keybindings = {
ace: null,
vim: "ace/keyboard/vim",
emacs: "ace/keyboard/emacs",
};
function isScriptFilename(f) {
return f.endsWith(".js") || f.endsWith(".script") || f.endsWith(".ns");
}
var scriptEditorRamCheck = null, scriptEditorRamText = null;
function scriptEditorInit() {
//Create buttons at the bottom of script editor
var wrapper = document.getElementById("script-editor-buttons-wrapper");
// Wrapper container that holds all the buttons below the script editor
const wrapper = document.getElementById("script-editor-buttons-wrapper");
if (wrapper == null) {
console.log("Error finding 'script-editor-buttons-wrapper'");
return;
console.error("Could not find 'script-editor-buttons-wrapper'");
return false;
}
var beautifyButton = createElement("a", {
class:"a-link-button", display:"inline-block",
innerText:"Beautify",
// Beautify button
const beautifyButton = createElement("button", {
class: "std-button",
display: "inline-block",
innerText: "Beautify",
clickListener:()=>{
beautifyScript();
return false;
}
});
var closeButton = createElement("a", {
class:"a-link-button", display:"inline-block",
innerText:"Save & Close (Ctrl/Cmd + b)",
clickListener:()=>{
saveAndCloseScriptEditor();
let editor = getCurrentEditor();
if (editor != null) {
editor.beautifyScript();
}
return false;
}
});
// Text that displays RAM calculation
scriptEditorRamText = createElement("p", {
display:"inline-block", margin:"10px", id:"script-editor-status-text"
});
var checkboxLabel = createElement("label", {
// Label for checkbox (defined below)
const checkboxLabel = createElement("label", {
for:"script-editor-ram-check", margin:"4px", marginTop: "8px",
innerText:"Dynamic RAM Usage Checker", color:"white",
tooltip:"Enable/Disable the dynamic RAM Usage display. You may " +
@@ -89,18 +71,34 @@ function scriptEditorInit() {
"performance issues"
});
// Checkbox for enabling/disabling dynamic RAM calculation
scriptEditorRamCheck = createElement("input", {
type:"checkbox", name:"script-editor-ram-check", id:"script-editor-ram-check",
margin:"4px", marginTop: "8px",
});
scriptEditorRamCheck.checked = true;
var documentationButton = createElement("a", {
display:"inline-block", class:"a-link-button", innerText:"Netscript Documentation",
// Link to Netscript documentation
const documentationButton = createElement("a", {
class: "std-button",
display: "inline-block",
href:"https://bitburner.readthedocs.io/en/latest/index.html",
target:"_blank"
innerText:"Netscript Documentation",
target:"_blank",
});
// Save and Close button
const closeButton = createElement("button", {
class: "std-button",
display: "inline-block",
innerText: "Save & Close (Ctrl/Cmd + b)",
clickListener:()=>{
saveAndCloseScriptEditor();
return false;
}
});
// Add all buttons to the UI
wrapper.appendChild(beautifyButton);
wrapper.appendChild(closeButton);
wrapper.appendChild(scriptEditorRamText);
@@ -108,146 +106,86 @@ function scriptEditorInit() {
wrapper.appendChild(checkboxLabel);
wrapper.appendChild(documentationButton);
//Initialize ACE Script editor
var editor = ace.edit('javascript-editor');
editor.getSession().setMode('ace/mode/netscript');
editor.setTheme('ace/theme/monokai');
document.getElementById('javascript-editor').style.fontSize='16px';
editor.setOption("showPrintMargin", false);
// Initialize editors
const initParams = {
saveAndCloseFn: saveAndCloseScriptEditor,
quitFn: Engine.loadTerminalContent,
}
/* Script editor options */
//Theme
var themeDropdown = document.getElementById("script-editor-option-theme");
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;
}
AceEditor.init(initParams);
CodeMirrorEditor.init(initParams);
// Setup the selector for which Editor to use
const editorSelector = document.getElementById("script-editor-option-editor");
if (editorSelector == null) {
console.error(`Could not find DOM Element for editor selector (id=script-editor-option-editor)`);
return false;
}
for (let i = 0; i < editorSelector.options.length; ++i) {
if (editorSelector.options[i].value === Settings.Editor) {
editorSelector.selectedIndex = i;
break;
}
themeDropdown.selectedIndex = initialIndex;
} else {
themeDropdown.selectedIndex = 2;
}
themeDropdown.onchange = function() {
var val = themeDropdown.value;
Settings.EditorTheme = val;
var themePath = "ace/theme/" + val.toLowerCase();
editor.setTheme(themePath);
};
themeDropdown.onchange();
//Keybinding
var keybindingDropdown = document.getElementById("script-editor-option-keybinding");
if (Settings.EditorKeybinding) {
var initialIndex = 0;
for (var i = 0; i < keybindingDropdown.options.length; ++i) {
if (keybindingDropdown.options[i].value === Settings.EditorKeybinding) {
initialIndex = i;
editorSelector.onchange = () => {
const opt = editorSelector.value;
switch (opt) {
case EditorSetting.Ace:
const codeMirrorCode = CodeMirrorEditor.getCode();
const codeMirrorFn = CodeMirrorEditor.getFilename();
AceEditor.create();
CodeMirrorEditor.setInvisible();
AceEditor.openScript(codeMirrorFn, codeMirrorCode);
break;
}
case EditorSetting.CodeMirror:
const aceCode = AceEditor.getCode();
const aceFn = AceEditor.getFilename();
CodeMirrorEditor.create();
AceEditor.setInvisible();
CodeMirrorEditor.openScript(aceFn, aceCode);
break;
default:
console.error(`Unrecognized Editor Setting: ${opt}`);
return;
}
keybindingDropdown.selectedIndex = initialIndex;
} else {
keybindingDropdown.selectedIndex = 0;
}
keybindingDropdown.onchange = function() {
var val = keybindingDropdown.value;
Settings.EditorKeybinding = val;
editor.setKeyboardHandler(keybindings[val.toLowerCase()]);
};
keybindingDropdown.onchange();
//Highlight Active line
var highlightActiveChkBox = document.getElementById("script-editor-option-highlightactiveline");
highlightActiveChkBox.onchange = function() {
editor.setHighlightActiveLine(highlightActiveChkBox.checked);
};
//Show Invisibles
var showInvisiblesChkBox = document.getElementById("script-editor-option-showinvisibles");
showInvisiblesChkBox.onchange = function() {
editor.setShowInvisibles(showInvisiblesChkBox.checked);
};
//Use Soft Tab
var softTabChkBox = document.getElementById("script-editor-option-usesofttab");
softTabChkBox.onchange = function() {
editor.getSession().setUseSoftTabs(softTabChkBox.checked);
};
//Jshint Maxerr
var maxerr = document.getElementById("script-editor-option-maxerr");
var maxerrLabel = document.getElementById("script-editor-option-maxerror-value-label");
maxerrLabel.innerHTML = maxerr.value;
maxerr.onchange = function() {
editor.getSession().$worker.send("changeOptions", [{maxerr:maxerr.value}]);
maxerrLabel.innerHTML = maxerr.value;
Settings.Editor = opt;
}
//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) {
saveAndCloseScriptEditor();
});
VimApi.defineEx('quit', 'q', function(cm, input) {
Engine.loadTerminalContent();
});
VimApi.defineEx('xwritequit', 'x', function(cm, input) {
saveAndCloseScriptEditor();
});
VimApi.defineEx('wqwritequit', 'wq', function(cm, input) {
saveAndCloseScriptEditor();
});
});
editorSelector.onchange(); // Trigger the onchange event handler
}
//Function autocompleter
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);
},
export function getCurrentEditor() {
switch (Settings.Editor) {
case EditorSetting.Ace:
return AceEditor;
case EditorSetting.CodeMirror:
return CodeMirrorEditor;
default:
console.log(`Invalid Editor Setting: ${Settings.Editor}`);
throw new Error(`Invalid Editor Setting: ${Settings.Editor}`);
return null;
}
editor.completers = [autocompleter];
}
//Updates RAM usage in script
async function updateScriptEditorContent() {
export async function updateScriptEditorContent() {
var filename = document.getElementById("script-editor-filename").value;
if (scriptEditorRamCheck == null || !scriptEditorRamCheck.checked || !isScriptFilename(filename)) {
scriptEditorRamText.innerText = "N/A";
return;
}
var editor = ace.edit('javascript-editor');
var code = editor.getValue();
let code;
try {
code = getCurrentEditor().getCode();
} catch(e) {
scriptEditorRamText.innerText = "RAM: ERROR";
return;
}
var codeCopy = code.repeat(1);
var ramUsage = await calculateRamUsage(codeCopy);
if (ramUsage !== -1) {
@@ -269,20 +207,16 @@ $(document).keydown(function(e) {
}
});
function beautifyScript() {
var editor = ace.edit('javascript-editor');
var code = editor.getValue();
code = beautify(code, {
indent_size: 4,
brace_style: "preserve-inline",
});
editor.setValue(code);
}
function saveAndCloseScriptEditor() {
var filename = document.getElementById("script-editor-filename").value;
var editor = ace.edit('javascript-editor');
var code = editor.getValue();
try {
let code = getCurrentEditor().getCode();
} catch(e) {
dialogBoxCreate("Something went wrong when trying to save (getCurrentEditor().getCode()). Please report to game developer with details");
return;
}
if (ITutorial.isRunning && ITutorial.currStep === iTutorialSteps.TerminalTypeScript) {
//Make sure filename + code properly follow tutorial
if (filename !== "foodnstuff.script") {
@@ -387,8 +321,7 @@ function Script(fn = "", code = "", server = "") {
Script.prototype.saveScript = function() {
if (routing.isOn(Page.ScriptEditor)) {
//Update code and filename
var editor = ace.edit('javascript-editor');
var code = editor.getValue();
const code = getCurrentEditor().getCode();
this.code = code.replace(/^\s+|\s+$/g, '');
var filename = document.getElementById("script-editor-filename").value;
@@ -1099,5 +1032,5 @@ AllServersMap.fromJSON = function(value) {
Reviver.constructors.AllServersMap = AllServersMap;
export {updateScriptEditorContent, loadAllRunningScripts, findRunningScript,
export {loadAllRunningScripts, findRunningScript,
RunningScript, Script, AllServersMap, scriptEditorInit, isScriptFilename};
+301
View File
@@ -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();
+857
View File
@@ -0,0 +1,857 @@
//This file should be copied into brace/mode/netscript.js
ace.define("ace/mode/doc_comment_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"], function(acequire, exports, module) {
"use strict";
var oop = acequire("../lib/oop");
var TextHighlightRules = acequire("./text_highlight_rules").TextHighlightRules;
var DocCommentHighlightRules = function() {
this.$rules = {
"start" : [ {
token : "comment.doc.tag",
regex : "@[\\w\\d_]+" // TODO: fix email addresses
},
DocCommentHighlightRules.getTagRule(),
{
defaultToken : "comment.doc",
caseInsensitive: true
}]
};
};
oop.inherits(DocCommentHighlightRules, TextHighlightRules);
DocCommentHighlightRules.getTagRule = function(start) {
return {
token : "comment.doc.tag.storage.type",
regex : "\\b(?:TODO|FIXME|XXX|HACK)\\b"
};
}
DocCommentHighlightRules.getStartRule = function(start) {
return {
token : "comment.doc", // doc comment
regex : "\\/\\*(?=\\*)",
next : start
};
};
DocCommentHighlightRules.getEndRule = function (start) {
return {
token : "comment.doc", // closing comment
regex : "\\*\\/",
next : start
};
};
exports.DocCommentHighlightRules = DocCommentHighlightRules;
});
ace.define("ace/mode/netscript_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/doc_comment_highlight_rules","ace/mode/text_highlight_rules"], function(acequire, exports, module) {
"use strict";
var oop = acequire("../lib/oop");
var DocCommentHighlightRules = acequire("./doc_comment_highlight_rules").DocCommentHighlightRules;
var TextHighlightRules = acequire("./text_highlight_rules").TextHighlightRules;
var identifierRe = "[a-zA-Z\\$_\u00a1-\uffff][a-zA-Z\\d\\$_\u00a1-\uffff]*";
let NetscriptFunctions =
"hack|hackAnalyzeThreads|hackAnalyzePercent|hackChance|" +
"sleep|grow|weaken|growthAnalyze|print|tprint|scan|nuke|brutessh|" +
"ftpcrack|" +
"clearLog|disableLog|enableLog|isLogEnabled|getScriptLogs|" +
"relaysmtp|httpworm|sqlinject|run|exec|spawn|kill|killall|exit|" +
"scp|ls|ps|hasRootAccess|" +
"getIp|getHackingMultipliers|getBitNodeMultipliers|getStats|isBusy|" +
"getHacknetMultipliers|" +
"getHostname|getHackingLevel|getServerMoneyAvailable|getServerMaxMoney|" +
"getServerGrowth|getServerSecurityLevel|getServerBaseSecurityLevel|" +
"getServerMinSecurityLevel|" +
"getServerRequiredHackingLevel|getServerNumPortsRequired|getServerRam|" +
"serverExists|fileExists|isRunning|" +
"deleteServer|getPurchasedServers|" +
"getPurchasedServerLimit|getPurchasedServerMaxRam|" +
"getPurchasedServerCost|" +
"purchaseServer|round|write|tryWrite|read|peek|clear|rm|getPortHandle|" +
"scriptRunning|scriptKill|getScriptName|getScriptRam|" +
"getHackTime|getGrowTime|getWeakenTime|getScriptIncome|getScriptExpGain|" +
"getTimeSinceLastAug|prompt|" +
// Singularity Functions
"universityCourse|getCharacterInformation|" +
"gymWorkout|travelToCity|purchaseTor|purchaseProgram|upgradeHomeRam|" +
"getUpgradeHomeRamCost|workForCompany|applyToCompany|getCompanyRep|" +
"getCompanyFavor|stopAction|getFactionFavor|" +
"getFavorToDonate|getFactionFavorGain|getCompanyFavorGain|" +
"checkFactionInvitations|joinFaction|workForFaction|getFactionRep|" +
"donateToFaction|" +
"createProgram|commitCrime|getCrimeChance|getOwnedAugmentations|" +
"getOwnedSourceFiles|getAugmentationsFromFaction|" +
"getAugmentationPrereq|getAugmentationCost|purchaseAugmentation|" +
"installAugmentations|" +
// TIX API
"getStockPrice|getStockPosition|getStockSymbols|getStockMaxShares|" +
"buyStock|sellStock|shortStock|sellShort|" +
"placeOrder|cancelOrder|getOrders|getStockVolatility|getStockForecast|" +
"purchase4SMarketData|purchase4SMarketDataTixApi|" +
// Hacknet Node API
"hacknet|numNodes|purchaseNode|getPurchaseNodeCost|getNodeStats|" +
"upgradeLevel|upgradeRam|upgradeCore|getLevelUpgradeCost|" +
"getRamUpgradeCost|getCoreUpgradeCost|" +
// Gang API
"gang|" +
"getMemberNames|getGangInformation|getMemberInformation|canRecruitMember|" +
"recruitMember|getTaskNames|setMemberTask|getEquipmentNames|" +
"getEquipmentCost|getEquipmentType|purchaseEquipment|ascendMember|" +
"setTerritoryWarfare|" +
"getChanceToWinClash|getBonusTime|" +
// Bladeburner API
"bladeburner|getContractNames|getOperationNames|getBlackOpNames|" +
"getGeneralActionNames|getSkillNames|startAction|stopBladeburnerAction|" +
"getActionTime|getActionEstimatedSuccessChance|getActionCountRemaining|" +
"getActionMaxLevel|getActionCurrentLevel|getActionAutolevel|" +
"getActionRepGain|setActionAutolevel|setActionLevel|" +
"getRank|getSkillPoints|getSkillLevel|getSkillUpgradeCost|" +
"upgradeSkill|getTeamSize|getCity|" +
"setTeamSize|getCityEstimatedPopulation|getCityEstimatedCommunities|" +
"getCityChaos|switchCity|getStamina|joinBladeburnerFaction|getBonusTime|" +
// Coding Contract API
"codingcontract|attempt|getContractType|getData|getDescription|" +
"getNumTriesRemaining";
var NetscriptHighlightRules = function(options) {
var keywordMapper = this.createKeywordMapper({
"variable.language":
"Array|Boolean|Date|Function|Iterator|Number|Object|RegExp|String|Proxy|" + // Constructors
"Namespace|QName|XML|XMLList|" + // E4X
"ArrayBuffer|Float32Array|Float64Array|Int16Array|Int32Array|Int8Array|" +
"Uint16Array|Uint32Array|Uint8Array|Uint8ClampedArray|" +
"Error|EvalError|InternalError|RangeError|ReferenceError|StopIteration|" + // Errors
"SyntaxError|TypeError|URIError|" +
"decodeURI|decodeURIComponent|encodeURI|encodeURIComponent|eval|isFinite|" + // Non-constructor functions
"isNaN|parseFloat|parseInt|" +
"ns|" + NetscriptFunctions +"|" +
"JSON|Math|" + // Other
"this|arguments|prototype|window|document" , // Pseudo
"keyword":
"const|yield|import|get|set|async|await|" +
"break|case|catch|continue|default|delete|do|else|finally|for|function|" +
"if|in|of|instanceof|new|return|switch|throw|try|typeof|let|var|while|with|debugger|" +
"__parent__|__count__|escape|unescape|with|__proto__|" +
"class|enum|extends|super|export|implements|private|public|interface|package|protected|static",
"storage.type":
"const|let|var|function",
"constant.language":
"null|Infinity|NaN|undefined",
"support.function":
"alert",
"constant.language.boolean": "true|false"
}, "identifier");
var kwBeforeRe = "case|do|else|finally|in|instanceof|return|throw|try|typeof|yield|void";
var escapedRe = "\\\\(?:x[0-9a-fA-F]{2}|" + // hex
"u[0-9a-fA-F]{4}|" + // unicode
"u{[0-9a-fA-F]{1,6}}|" + // es6 unicode
"[0-2][0-7]{0,2}|" + // oct
"3[0-7][0-7]?|" + // oct
"[4-7][0-7]?|" + //oct
".)";
this.$rules = {
"no_regex" : [
DocCommentHighlightRules.getStartRule("doc-start"),
comments("no_regex"),
{
token : "string",
regex : "'(?=.)",
next : "qstring"
}, {
token : "string",
regex : '"(?=.)',
next : "qqstring"
}, {
token : "constant.numeric", // hex
regex : /0(?:[xX][0-9a-fA-F]+|[bB][01]+)\b/
}, {
token : "constant.numeric", // float
regex : /[+-]?\d[\d_]*(?:(?:\.\d*)?(?:[eE][+-]?\d+)?)?\b/
}, {
token : [
"storage.type", "punctuation.operator", "support.function",
"punctuation.operator", "entity.name.function", "text","keyword.operator"
],
regex : "(" + identifierRe + ")(\\.)(prototype)(\\.)(" + identifierRe +")(\\s*)(=)",
next: "function_arguments"
}, {
token : [
"storage.type", "punctuation.operator", "entity.name.function", "text",
"keyword.operator", "text", "storage.type", "text", "paren.lparen"
],
regex : "(" + identifierRe + ")(\\.)(" + identifierRe +")(\\s*)(=)(\\s*)(function)(\\s*)(\\()",
next: "function_arguments"
}, {
token : [
"entity.name.function", "text", "keyword.operator", "text", "storage.type",
"text", "paren.lparen"
],
regex : "(" + identifierRe +")(\\s*)(=)(\\s*)(function)(\\s*)(\\()",
next: "function_arguments"
}, {
token : [
"storage.type", "punctuation.operator", "entity.name.function", "text",
"keyword.operator", "text",
"storage.type", "text", "entity.name.function", "text", "paren.lparen"
],
regex : "(" + identifierRe + ")(\\.)(" + identifierRe +")(\\s*)(=)(\\s*)(function)(\\s+)(\\w+)(\\s*)(\\()",
next: "function_arguments"
}, {
token : [
"storage.type", "text", "entity.name.function", "text", "paren.lparen"
],
regex : "(function)(\\s+)(" + identifierRe + ")(\\s*)(\\()",
next: "function_arguments"
}, {
token : [
"entity.name.function", "text", "punctuation.operator",
"text", "storage.type", "text", "paren.lparen"
],
regex : "(" + identifierRe + ")(\\s*)(:)(\\s*)(function)(\\s*)(\\()",
next: "function_arguments"
}, {
token : [
"text", "text", "storage.type", "text", "paren.lparen"
],
regex : "(:)(\\s*)(function)(\\s*)(\\()",
next: "function_arguments"
}, {
token : "keyword",
regex : "(?:" + kwBeforeRe + ")\\b",
next : "start"
}, {
token : ["support.constant"],
regex : /that\b/
}, {
token : ["storage.type", "punctuation.operator", "support.function.firebug"],
regex : /(console)(\.)(warn|info|log|error|time|trace|timeEnd|assert)\b/
}, {
token : keywordMapper,
regex : identifierRe
}, {
token : "punctuation.operator",
regex : /[.](?![.])/,
next : "property"
}, {
token : "keyword.operator",
regex : /--|\+\+|\.{3}|===|==|=|!=|!==|<+=?|>+=?|!|&&|\|\||\?:|[!$%&*+\-~\/^]=?/,
next : "start"
}, {
token : "punctuation.operator",
regex : /[?:,;.]/,
next : "start"
}, {
token : "paren.lparen",
regex : /[\[({]/,
next : "start"
}, {
token : "paren.rparen",
regex : /[\])}]/
}, {
token: "comment",
regex: /^#!.*$/
}
],
property: [{
token : "text",
regex : "\\s+"
}, {
token : [
"storage.type", "punctuation.operator", "entity.name.function", "text",
"keyword.operator", "text",
"storage.type", "text", "entity.name.function", "text", "paren.lparen"
],
regex : "(" + identifierRe + ")(\\.)(" + identifierRe +")(\\s*)(=)(\\s*)(function)(?:(\\s+)(\\w+))?(\\s*)(\\()",
next: "function_arguments"
}, {
token : "punctuation.operator",
regex : /[.](?![.])/
}, {
token : "support.function",
regex : "/|" + NetscriptFunctions + "|/"
}, {
token : "support.function",
regex : /(s(?:h(?:ift|ow(?:Mod(?:elessDialog|alDialog)|Help))|croll(?:X|By(?:Pages|Lines)?|Y|To)?|t(?:op|rike)|i(?:n|zeToContent|debar|gnText)|ort|u(?:p|b(?:str(?:ing)?)?)|pli(?:ce|t)|e(?:nd|t(?:Re(?:sizable|questHeader)|M(?:i(?:nutes|lliseconds)|onth)|Seconds|Ho(?:tKeys|urs)|Year|Cursor|Time(?:out)?|Interval|ZOptions|Date|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Date|FullYear)|FullYear|Active)|arch)|qrt|lice|avePreferences|mall)|h(?:ome|andleEvent)|navigate|c(?:har(?:CodeAt|At)|o(?:s|n(?:cat|textual|firm)|mpile)|eil|lear(?:Timeout|Interval)?|a(?:ptureEvents|ll)|reate(?:StyleSheet|Popup|EventObject))|t(?:o(?:GMTString|S(?:tring|ource)|U(?:TCString|pperCase)|Lo(?:caleString|werCase))|est|a(?:n|int(?:Enabled)?))|i(?:s(?:NaN|Finite)|ndexOf|talics)|d(?:isableExternalCapture|ump|etachEvent)|u(?:n(?:shift|taint|escape|watch)|pdateCommands)|j(?:oin|avaEnabled)|p(?:o(?:p|w)|ush|lugins.refresh|a(?:ddings|rse(?:Int|Float)?)|r(?:int|ompt|eference))|e(?:scape|nableExternalCapture|val|lementFromPoint|x(?:p|ec(?:Script|Command)?))|valueOf|UTC|queryCommand(?:State|Indeterm|Enabled|Value)|f(?:i(?:nd|le(?:ModifiedDate|Size|CreatedDate|UpdatedDate)|xed)|o(?:nt(?:size|color)|rward)|loor|romCharCode)|watch|l(?:ink|o(?:ad|g)|astIndexOf)|a(?:sin|nchor|cos|t(?:tachEvent|ob|an(?:2)?)|pply|lert|b(?:s|ort))|r(?:ou(?:nd|teEvents)|e(?:size(?:By|To)|calc|turnValue|place|verse|l(?:oad|ease(?:Capture|Events)))|andom)|g(?:o|et(?:ResponseHeader|M(?:i(?:nutes|lliseconds)|onth)|Se(?:conds|lection)|Hours|Year|Time(?:zoneOffset)?|Da(?:y|te)|UTC(?:M(?:i(?:nutes|lliseconds)|onth)|Seconds|Hours|Da(?:y|te)|FullYear)|FullYear|A(?:ttention|llResponseHeaders)))|m(?:in|ove(?:B(?:y|elow)|To(?:Absolute)?|Above)|ergeAttributes|a(?:tch|rgins|x))|b(?:toa|ig|o(?:ld|rderWidths)|link|ack))\b(?=\()/
}, {
token : "support.function.dom",
regex : /(s(?:ub(?:stringData|mit)|plitText|e(?:t(?:NamedItem|Attribute(?:Node)?)|lect))|has(?:ChildNodes|Feature)|namedItem|c(?:l(?:ick|o(?:se|neNode))|reate(?:C(?:omment|DATASection|aption)|T(?:Head|extNode|Foot)|DocumentFragment|ProcessingInstruction|E(?:ntityReference|lement)|Attribute))|tabIndex|i(?:nsert(?:Row|Before|Cell|Data)|tem)|open|delete(?:Row|C(?:ell|aption)|T(?:Head|Foot)|Data)|focus|write(?:ln)?|a(?:dd|ppend(?:Child|Data))|re(?:set|place(?:Child|Data)|move(?:NamedItem|Child|Attribute(?:Node)?)?)|get(?:NamedItem|Element(?:sBy(?:Name|TagName|ClassName)|ById)|Attribute(?:Node)?)|blur)\b(?=\()/
}, {
token : "support.constant",
regex : /(s(?:ystemLanguage|cr(?:ipts|ollbars|een(?:X|Y|Top|Left))|t(?:yle(?:Sheets)?|atus(?:Text|bar)?)|ibling(?:Below|Above)|ource|uffixes|e(?:curity(?:Policy)?|l(?:ection|f)))|h(?:istory|ost(?:name)?|as(?:h|Focus))|y|X(?:MLDocument|SLDocument)|n(?:ext|ame(?:space(?:s|URI)|Prop))|M(?:IN_VALUE|AX_VALUE)|c(?:haracterSet|o(?:n(?:structor|trollers)|okieEnabled|lorDepth|mp(?:onents|lete))|urrent|puClass|l(?:i(?:p(?:boardData)?|entInformation)|osed|asses)|alle(?:e|r)|rypto)|t(?:o(?:olbar|p)|ext(?:Transform|Indent|Decoration|Align)|ags)|SQRT(?:1_2|2)|i(?:n(?:ner(?:Height|Width)|put)|ds|gnoreCase)|zIndex|o(?:scpu|n(?:readystatechange|Line)|uter(?:Height|Width)|p(?:sProfile|ener)|ffscreenBuffering)|NEGATIVE_INFINITY|d(?:i(?:splay|alog(?:Height|Top|Width|Left|Arguments)|rectories)|e(?:scription|fault(?:Status|Ch(?:ecked|arset)|View)))|u(?:ser(?:Profile|Language|Agent)|n(?:iqueID|defined)|pdateInterval)|_content|p(?:ixelDepth|ort|ersonalbar|kcs11|l(?:ugins|atform)|a(?:thname|dding(?:Right|Bottom|Top|Left)|rent(?:Window|Layer)?|ge(?:X(?:Offset)?|Y(?:Offset)?))|r(?:o(?:to(?:col|type)|duct(?:Sub)?|mpter)|e(?:vious|fix)))|e(?:n(?:coding|abledPlugin)|x(?:ternal|pando)|mbeds)|v(?:isibility|endor(?:Sub)?|Linkcolor)|URLUnencoded|P(?:I|OSITIVE_INFINITY)|f(?:ilename|o(?:nt(?:Size|Family|Weight)|rmName)|rame(?:s|Element)|gColor)|E|whiteSpace|l(?:i(?:stStyleType|n(?:eHeight|kColor))|o(?:ca(?:tion(?:bar)?|lName)|wsrc)|e(?:ngth|ft(?:Context)?)|a(?:st(?:M(?:odified|atch)|Index|Paren)|yer(?:s|X)|nguage))|a(?:pp(?:MinorVersion|Name|Co(?:deName|re)|Version)|vail(?:Height|Top|Width|Left)|ll|r(?:ity|guments)|Linkcolor|bove)|r(?:ight(?:Context)?|e(?:sponse(?:XML|Text)|adyState))|global|x|m(?:imeTypes|ultiline|enubar|argin(?:Right|Bottom|Top|Left))|L(?:N(?:10|2)|OG(?:10E|2E))|b(?:o(?:ttom|rder(?:Width|RightWidth|BottomWidth|Style|Color|TopWidth|LeftWidth))|ufferDepth|elow|ackground(?:Color|Image)))\b/
}, {
token : "identifier",
regex : identifierRe
}, {
regex: "",
token: "empty",
next: "no_regex"
}
],
"start": [
DocCommentHighlightRules.getStartRule("doc-start"),
comments("start"),
{
token: "string.regexp",
regex: "\\/",
next: "regex"
}, {
token : "text",
regex : "\\s+|^$",
next : "start"
}, {
token: "empty",
regex: "",
next: "no_regex"
}
],
"regex": [
{
token: "regexp.keyword.operator",
regex: "\\\\(?:u[\\da-fA-F]{4}|x[\\da-fA-F]{2}|.)"
}, {
token: "string.regexp",
regex: "/[sxngimy]*",
next: "no_regex"
}, {
token : "invalid",
regex: /\{\d+\b,?\d*\}[+*]|[+*$^?][+*]|[$^][?]|\?{3,}/
}, {
token : "constant.language.escape",
regex: /\(\?[:=!]|\)|\{\d+\b,?\d*\}|[+*]\?|[()$^+*?.]/
}, {
token : "constant.language.delimiter",
regex: /\|/
}, {
token: "constant.language.escape",
regex: /\[\^?/,
next: "regex_character_class"
}, {
token: "empty",
regex: "$",
next: "no_regex"
}, {
defaultToken: "string.regexp"
}
],
"regex_character_class": [
{
token: "regexp.charclass.keyword.operator",
regex: "\\\\(?:u[\\da-fA-F]{4}|x[\\da-fA-F]{2}|.)"
}, {
token: "constant.language.escape",
regex: "]",
next: "regex"
}, {
token: "constant.language.escape",
regex: "-"
}, {
token: "empty",
regex: "$",
next: "no_regex"
}, {
defaultToken: "string.regexp.charachterclass"
}
],
"function_arguments": [
{
token: "variable.parameter",
regex: identifierRe
}, {
token: "punctuation.operator",
regex: "[, ]+"
}, {
token: "punctuation.operator",
regex: "$"
}, {
token: "empty",
regex: "",
next: "no_regex"
}
],
"qqstring" : [
{
token : "constant.language.escape",
regex : escapedRe
}, {
token : "string",
regex : "\\\\$",
next : "qqstring"
}, {
token : "string",
regex : '"|$',
next : "no_regex"
}, {
defaultToken: "string"
}
],
"qstring" : [
{
token : "constant.language.escape",
regex : escapedRe
}, {
token : "string",
regex : "\\\\$",
next : "qstring"
}, {
token : "string",
regex : "'|$",
next : "no_regex"
}, {
defaultToken: "string"
}
]
};
if (!options || !options.noES6) {
this.$rules.no_regex.unshift({
regex: "[{}]", onMatch: function(val, state, stack) {
this.next = val == "{" ? this.nextState : "";
if (val == "{" && stack.length) {
stack.unshift("start", state);
}
else if (val == "}" && stack.length) {
stack.shift();
this.next = stack.shift();
if (this.next.indexOf("string") != -1 || this.next.indexOf("jsx") != -1)
return "paren.quasi.end";
}
return val == "{" ? "paren.lparen" : "paren.rparen";
},
nextState: "start"
}, {
token : "string.quasi.start",
regex : /`/,
push : [{
token : "constant.language.escape",
regex : escapedRe
}, {
token : "paren.quasi.start",
regex : /\${/,
push : "start"
}, {
token : "string.quasi.end",
regex : /`/,
next : "pop"
}, {
defaultToken: "string.quasi"
}]
});
if (!options || options.jsx != false)
JSX.call(this);
}
this.embedRules(DocCommentHighlightRules, "doc-",
[ DocCommentHighlightRules.getEndRule("no_regex") ]);
this.normalizeRules();
};
oop.inherits(NetscriptHighlightRules, TextHighlightRules);
function JSX() {
var tagRegex = identifierRe.replace("\\d", "\\d\\-");
var jsxTag = {
onMatch : function(val, state, stack) {
var offset = val.charAt(1) == "/" ? 2 : 1;
if (offset == 1) {
if (state != this.nextState)
stack.unshift(this.next, this.nextState, 0);
else
stack.unshift(this.next);
stack[2]++;
} else if (offset == 2) {
if (state == this.nextState) {
stack[1]--;
if (!stack[1] || stack[1] < 0) {
stack.shift();
stack.shift();
}
}
}
return [{
type: "meta.tag.punctuation." + (offset == 1 ? "" : "end-") + "tag-open.xml",
value: val.slice(0, offset)
}, {
type: "meta.tag.tag-name.xml",
value: val.substr(offset)
}];
},
regex : "</?" + tagRegex + "",
next: "jsxAttributes",
nextState: "jsx"
};
this.$rules.start.unshift(jsxTag);
var jsxJsRule = {
regex: "{",
token: "paren.quasi.start",
push: "start"
};
this.$rules.jsx = [
jsxJsRule,
jsxTag,
{include : "reference"},
{defaultToken: "string"}
];
this.$rules.jsxAttributes = [{
token : "meta.tag.punctuation.tag-close.xml",
regex : "/?>",
onMatch : function(value, currentState, stack) {
if (currentState == stack[0])
stack.shift();
if (value.length == 2) {
if (stack[0] == this.nextState)
stack[1]--;
if (!stack[1] || stack[1] < 0) {
stack.splice(0, 2);
}
}
this.next = stack[0] || "start";
return [{type: this.token, value: value}];
},
nextState: "jsx"
},
jsxJsRule,
comments("jsxAttributes"),
{
token : "entity.other.attribute-name.xml",
regex : tagRegex
}, {
token : "keyword.operator.attribute-equals.xml",
regex : "="
}, {
token : "text.tag-whitespace.xml",
regex : "\\s+"
}, {
token : "string.attribute-value.xml",
regex : "'",
stateName : "jsx_attr_q",
push : [
{token : "string.attribute-value.xml", regex: "'", next: "pop"},
{include : "reference"},
{defaultToken : "string.attribute-value.xml"}
]
}, {
token : "string.attribute-value.xml",
regex : '"',
stateName : "jsx_attr_qq",
push : [
{token : "string.attribute-value.xml", regex: '"', next: "pop"},
{include : "reference"},
{defaultToken : "string.attribute-value.xml"}
]
},
jsxTag
];
this.$rules.reference = [{
token : "constant.language.escape.reference.xml",
regex : "(?:&#[0-9]+;)|(?:&#x[0-9a-fA-F]+;)|(?:&[a-zA-Z0-9_:\\.-]+;)"
}];
}
function comments(next) {
return [
{
token : "comment", // multi line comment
regex : /\/\*/,
next: [
DocCommentHighlightRules.getTagRule(),
{token : "comment", regex : "\\*\\/", next : next || "pop"},
{defaultToken : "comment", caseInsensitive: true}
]
}, {
token : "comment",
regex : "\\/\\/",
next: [
DocCommentHighlightRules.getTagRule(),
{token : "comment", regex : "$|^", next : next || "pop"},
{defaultToken : "comment", caseInsensitive: true}
]
}
];
}
exports.NetscriptHighlightRules = NetscriptHighlightRules;
});
ace.define("ace/mode/matching_brace_outdent",["require","exports","module","ace/range"], function(acequire, exports, module) {
"use strict";
var Range = acequire("../range").Range;
var MatchingBraceOutdent = function() {};
(function() {
this.checkOutdent = function(line, input) {
if (! /^\s+$/.test(line))
return false;
return /^\s*\}/.test(input);
};
this.autoOutdent = function(doc, row) {
var line = doc.getLine(row);
var match = line.match(/^(\s*\})/);
if (!match) return 0;
var column = match[1].length;
var openBracePos = doc.findMatchingBracket({row: row, column: column});
if (!openBracePos || openBracePos.row == row) return 0;
var indent = this.$getIndent(doc.getLine(openBracePos.row));
doc.replace(new Range(row, 0, row, column-1), indent);
};
this.$getIndent = function(line) {
return line.match(/^\s*/)[0];
};
}).call(MatchingBraceOutdent.prototype);
exports.MatchingBraceOutdent = MatchingBraceOutdent;
});
ace.define("ace/mode/folding/cstyle",["require","exports","module","ace/lib/oop","ace/range","ace/mode/folding/fold_mode"], function(acequire, exports, module) {
"use strict";
var oop = acequire("../../lib/oop");
var Range = acequire("../../range").Range;
var BaseFoldMode = acequire("./fold_mode").FoldMode;
var FoldMode = exports.FoldMode = function(commentRegex) {
if (commentRegex) {
this.foldingStartMarker = new RegExp(
this.foldingStartMarker.source.replace(/\|[^|]*?$/, "|" + commentRegex.start)
);
this.foldingStopMarker = new RegExp(
this.foldingStopMarker.source.replace(/\|[^|]*?$/, "|" + commentRegex.end)
);
}
};
oop.inherits(FoldMode, BaseFoldMode);
(function() {
this.foldingStartMarker = /(\{|\[)[^\}\]]*$|^\s*(\/\*)/;
this.foldingStopMarker = /^[^\[\{]*(\}|\])|^[\s\*]*(\*\/)/;
this.singleLineBlockCommentRe= /^\s*(\/\*).*\*\/\s*$/;
this.tripleStarBlockCommentRe = /^\s*(\/\*\*\*).*\*\/\s*$/;
this.startRegionRe = /^\s*(\/\*|\/\/)#?region\b/;
this._getFoldWidgetBase = this.getFoldWidget;
this.getFoldWidget = function(session, foldStyle, row) {
var line = session.getLine(row);
if (this.singleLineBlockCommentRe.test(line)) {
if (!this.startRegionRe.test(line) && !this.tripleStarBlockCommentRe.test(line))
return "";
}
var fw = this._getFoldWidgetBase(session, foldStyle, row);
if (!fw && this.startRegionRe.test(line))
return "start"; // lineCommentRegionStart
return fw;
};
this.getFoldWidgetRange = function(session, foldStyle, row, forceMultiline) {
var line = session.getLine(row);
if (this.startRegionRe.test(line))
return this.getCommentRegionBlock(session, line, row);
var match = line.match(this.foldingStartMarker);
if (match) {
var i = match.index;
if (match[1])
return this.openingBracketBlock(session, match[1], row, i);
var range = session.getCommentFoldRange(row, i + match[0].length, 1);
if (range && !range.isMultiLine()) {
if (forceMultiline) {
range = this.getSectionRange(session, row);
} else if (foldStyle != "all")
range = null;
}
return range;
}
if (foldStyle === "markbegin")
return;
var match = line.match(this.foldingStopMarker);
if (match) {
var i = match.index + match[0].length;
if (match[1])
return this.closingBracketBlock(session, match[1], row, i);
return session.getCommentFoldRange(row, i, -1);
}
};
this.getSectionRange = function(session, row) {
var line = session.getLine(row);
var startIndent = line.search(/\S/);
var startRow = row;
var startColumn = line.length;
row = row + 1;
var endRow = row;
var maxRow = session.getLength();
while (++row < maxRow) {
line = session.getLine(row);
var indent = line.search(/\S/);
if (indent === -1)
continue;
if (startIndent > indent)
break;
var subRange = this.getFoldWidgetRange(session, "all", row);
if (subRange) {
if (subRange.start.row <= startRow) {
break;
} else if (subRange.isMultiLine()) {
row = subRange.end.row;
} else if (startIndent == indent) {
break;
}
}
endRow = row;
}
return new Range(startRow, startColumn, endRow, session.getLine(endRow).length);
};
this.getCommentRegionBlock = function(session, line, row) {
var startColumn = line.search(/\s*$/);
var maxRow = session.getLength();
var startRow = row;
var re = /^\s*(?:\/\*|\/\/|--)#?(end)?region\b/;
var depth = 1;
while (++row < maxRow) {
line = session.getLine(row);
var m = re.exec(line);
if (!m) continue;
if (m[1]) depth--;
else depth++;
if (!depth) break;
}
var endRow = row;
if (endRow > startRow) {
return new Range(startRow, startColumn, endRow, line.length);
}
};
}).call(FoldMode.prototype);
});
ace.define("ace/mode/netscript",["require","exports","module","ace/lib/oop","ace/mode/text","ace/mode/netscript_highlight_rules","ace/mode/matching_brace_outdent","ace/worker/worker_client","ace/mode/behaviour/cstyle","ace/mode/folding/cstyle"], function(acequire, exports, module) {
"use strict";
var oop = acequire("../lib/oop");
var TextMode = acequire("./text").Mode;
var NetscriptHighlightRules = acequire("./netscript_highlight_rules").NetscriptHighlightRules;
var MatchingBraceOutdent = acequire("./matching_brace_outdent").MatchingBraceOutdent;
var WorkerClient = acequire("../worker/worker_client").WorkerClient;
var CstyleBehaviour = acequire("./behaviour/cstyle").CstyleBehaviour;
var CStyleFoldMode = acequire("./folding/cstyle").FoldMode;
var Mode = function() {
this.HighlightRules = NetscriptHighlightRules;
this.$outdent = new MatchingBraceOutdent();
this.$behaviour = new CstyleBehaviour();
this.foldingRules = new CStyleFoldMode();
};
oop.inherits(Mode, TextMode);
(function() {
this.lineCommentStart = "//";
this.blockComment = {start: "/*", end: "*/"};
this.getNextLineIndent = function(state, line, tab) {
var indent = this.$getIndent(line);
var tokenizedLine = this.getTokenizer().getLineTokens(line, state);
var tokens = tokenizedLine.tokens;
var endState = tokenizedLine.state;
if (tokens.length && tokens[tokens.length-1].type == "comment") {
return indent;
}
if (state == "start" || state == "no_regex") {
var match = line.match(/^.*(?:\bcase\b.*:|[\{\(\[])\s*$/);
if (match) {
indent += tab;
}
} else if (state == "doc-start") {
if (endState == "start" || endState == "no_regex") {
return "";
}
var match = line.match(/^\s*(\/?)\*/);
if (match) {
if (match[1]) {
indent += " ";
}
indent += "* ";
}
}
return indent;
};
this.checkOutdent = function(state, line, input) {
return this.$outdent.checkOutdent(line, input);
};
this.autoOutdent = function(state, doc, row) {
this.$outdent.autoOutdent(doc, row);
};
this.createWorker = function(session) {
var worker = new WorkerClient(["ace"], require("brace/worker/javascript"), "JavaScriptWorker");
worker.attachToDocument(session.getDocument());
worker.on("annotate", function(results) {
session.setAnnotations(results.data);
});
worker.on("terminate", function() {
session.clearAnnotations();
});
return worker;
};
this.$id = "ace/mode/netscript";
}).call(Mode.prototype);
exports.Mode = Mode;
});
+523
View File
@@ -0,0 +1,523 @@
// Wrapper for CodeMirror editor
// https://github.com/codemirror/codemirror
import { ScriptEditor } from "./ScriptEditor";
import 'codemirror/lib/codemirror.css';
import 'codemirror/theme/monokai.css';
import 'codemirror/theme/3024-day.css';
import 'codemirror/theme/3024-night.css';
import 'codemirror/theme/abcdef.css';
import 'codemirror/theme/ambiance-mobile.css';
import 'codemirror/theme/ambiance.css';
import 'codemirror/theme/base16-dark.css';
import 'codemirror/theme/base16-light.css';
import 'codemirror/theme/bespin.css';
import 'codemirror/theme/blackboard.css';
import 'codemirror/theme/cobalt.css';
import 'codemirror/theme/colorforth.css';
import 'codemirror/theme/darcula.css';
import 'codemirror/theme/dracula.css';
import 'codemirror/theme/duotone-dark.css';
import 'codemirror/theme/duotone-light.css';
import 'codemirror/theme/eclipse.css';
import 'codemirror/theme/elegant.css';
import 'codemirror/theme/erlang-dark.css';
import 'codemirror/theme/gruvbox-dark.css';
import 'codemirror/theme/hopscotch.css';
import 'codemirror/theme/icecoder.css';
import 'codemirror/theme/idea.css';
import 'codemirror/theme/isotope.css';
import 'codemirror/theme/lesser-dark.css';
import 'codemirror/theme/liquibyte.css';
import 'codemirror/theme/lucario.css';
import 'codemirror/theme/material.css';
import 'codemirror/theme/mbo.css';
import 'codemirror/theme/mdn-like.css';
import 'codemirror/theme/midnight.css';
import 'codemirror/theme/neat.css';
import 'codemirror/theme/neo.css';
import 'codemirror/theme/night.css';
import 'codemirror/theme/oceanic-next.css';
import 'codemirror/theme/panda-syntax.css';
import 'codemirror/theme/paraiso-dark.css';
import 'codemirror/theme/paraiso-light.css';
import 'codemirror/theme/pastel-on-dark.css';
import 'codemirror/theme/railscasts.css';
import 'codemirror/theme/rubyblue.css';
import 'codemirror/theme/seti.css';
import 'codemirror/theme/shadowfox.css';
import 'codemirror/theme/solarized.css';
import 'codemirror/theme/ssms.css';
import 'codemirror/theme/the-matrix.css';
import 'codemirror/theme/tomorrow-night-bright.css';
import 'codemirror/theme/tomorrow-night-eighties.css';
import 'codemirror/theme/ttcn.css';
import 'codemirror/theme/twilight.css';
import 'codemirror/theme/vibrant-ink.css';
import 'codemirror/theme/xq-dark.css';
import 'codemirror/theme/xq-light.css';
import 'codemirror/theme/yeti.css';
import 'codemirror/theme/zenburn.css';
import "../../css/codemirror-overrides.scss";
import CodeMirror from "codemirror/lib/codemirror.js";
import "codemirror/mode/javascript/javascript.js";
import "./CodeMirrorNetscriptMode";
import 'codemirror/keymap/sublime.js';
import 'codemirror/keymap/vim.js';
import 'codemirror/keymap/emacs.js';
import 'codemirror/addon/comment/continuecomment.js';
import 'codemirror/addon/edit/closebrackets.js';
import 'codemirror/addon/edit/matchbrackets.js';
import 'codemirror/addon/fold/foldcode.js';
import 'codemirror/addon/fold/foldgutter.js';
import 'codemirror/addon/fold/foldgutter.css';
import 'codemirror/addon/fold/brace-fold.js';
import 'codemirror/addon/fold/indent-fold.js';
import 'codemirror/addon/fold/comment-fold.js';
import 'codemirror/addon/hint/show-hint.js';
import 'codemirror/addon/hint/show-hint.css';
import 'codemirror/addon/lint/lint.js';
import 'codemirror/addon/lint/lint.css';
import 'codemirror/addon/search/match-highlighter.js';
import 'codemirror/addon/selection/active-line.js';
window.JSHINT = require('jshint').JSHINT;
import './CodeMirrorNetscriptHint.js';
import { NetscriptFunctions } from "../NetscriptFunctions";
import { CodeMirrorThemeSetting } from "../Settings/SettingEnums";
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";
// Max number of invisibles to be shown in a group if the "Show Invisibles" option
// is marked
const MaxInvisibles = 20;
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 CodeMirrorEditorWrapper extends ScriptEditor {
constructor() {
super();
this.tabsStyleElement = null;
}
init(params) {
if (this.editor != null) {
console.error(`CodeMirrorEditor.init() called when it's already initialized`);
return false;
}
// Validate/Sanitize input
if (!validateInitializationParamters(params)) {
console.error(`'params' argument passed into CodeMirrorEditor.init() 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;
}
// Add styling for the "Show Invisibles" option for spaces
const classBase = '.CodeMirror .cm-whitespace-';
const spaceChar = '·';
const style = document.createElement('style');
style.setAttribute('data-name', 'js-show-invisibles');
let rules = '';
let spaceChars = '';
for (let i = 1; i <= MaxInvisibles; ++i) {
spaceChars += spaceChar;
const rule = classBase + i + '::before { content: "' + spaceChars + '";}\n';
rules += rule;
}
style.textContent = rules;
document.head.appendChild(style);
// Add an element for the "Show Invisible" option for tabs
this.tabsStyleElement = document.createElement('style');
document.head.appendChild(this.tabsStyleElement);
// Define a "Save" command for CodeMirror so shortcuts like Ctrl + s
// will save in-game
CodeMirror.commands.save = function() { params.saveAndCloseFn(); }
// Add Netscript Functions to the autocompleter
const netscriptFns = [];
var fnsObj = NetscriptFunctions(null);
for (let name in fnsObj) {
if (fnsObj.hasOwnProperty(name)) {
netscriptFns.push(name);
//Get functions from namespaces
const namespaces = ["bladeburner", "hacknet", "codingcontract", "gang"];
if (namespaces.includes(name)) {
let namespace = fnsObj[name];
if (typeof namespace !== "object") {continue;}
let namespaceFns = Object.keys(namespace);
for (let i = 0; i < namespaceFns.length; ++i) {
netscriptFns.push(namespaceFns[i]);
}
}
}
}
CodeMirror.hint.netscript = function(editor) {
const origList = CodeMirror.hint.javascript(editor) || {from: editor.getCursor(), to: editor.getCursor(), list: []};
origList.list.push(...netscriptFns);
let list = origList.list || [];
let cursor = editor.getCursor();
let currentLine = editor.getLine(cursor.line);
let start = cursor.ch;
let end = start;
while (end < currentLine.length && /[\w$]+/.test(currentLine.charAt(end))) ++end;
while (start && /[\w$]+/.test(currentLine.charAt(start - 1))) --start;
let curWord = start != end && currentLine.slice(start, end);
let regex = new RegExp('^' + curWord, 'i');
let result = {
list: (!curWord ? list : list.filter(function (item) {
return item.match(regex);
})).sort(),
from: CodeMirror.Pos(cursor.line, start),
to: CodeMirror.Pos(cursor.line, end)
};
return result;
};
}
initialized() {
return (this.filenameInput != null);
}
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 {
if (!this.initialized()) {
console.warn(`CodeMirrorEditor.create() called when editor was not initialized`);
return;
}
// Initialize CodeMirror Editor
const textAreaElement = safeGetElementById("codemirror-editor", "CodeMirror Textarea");
const formElement = safeGetElementById("codemirror-form-wrapper", "CodeMirror Form Wrapper");
formElement.style.display = "block";
this.editor = CodeMirror.fromTextArea(textAreaElement, {
autofocus: true,
extraKeys: { "Ctrl-Space": "autocomplete" },
foldGutter: true,
gutters: ["CodeMirror-lint-markers", "CodeMirror-linenumbers", "CodeMirror-foldgutter"],
highlightSelectionMatches: true,
hintOptions: { hint: CodeMirror.hint.netscript },
indentUnit: 4,
keyMap: "default",
lineNumbers: true,
matchBrackets: true,
maxInvisibles: 32,
mode: "netscript",
theme: Settings.EditorTheme,
});
// Setup Theme Option
const themeDropdown = safeClearEventListeners("script-editor-option-theme", "Theme Selector");
removeChildrenFromElement(themeDropdown);
const themeOptions = Object.keys(CodeMirrorThemeSetting);
for (let i = 0; i < themeOptions.length; ++i) {
const themeKey = themeOptions[i];
const themeValue = CodeMirrorThemeSetting[themeKey];
themeDropdown.add(createOptionElement(themeKey, themeValue));
}
if (Settings.EditorTheme) {
var initialIndex = 0;
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 = 0;
}
themeDropdown.onchange = () => {
const val = themeDropdown.value;
Settings.EditorTheme = val;
this.editor.setOption("theme", val);
};
themeDropdown.onchange();
// Setup Keymap Option
const keybindingDropdown = safeClearEventListeners("script-editor-option-keybinding", "Keymap Selector");
if (keybindingDropdown == null) {
console.error(`Could not find Script Editor's keybinding selector element (id="script-editor-option-keybinding")`);
return false;
}
removeChildrenFromElement(keybindingDropdown);
keybindingDropdown.add(createOptionElement("Default", "default"));
keybindingDropdown.add(createOptionElement("Sublime", "sublime"));
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 = () => {
const val = keybindingDropdown.value;
Settings.EditorKeybinding = val;
this.editor.removeKeyMap("sublime");
this.editor.removeKeyMap("emacs");
this.editor.removeKeyMap("vim");
this.editor.addKeyMap(val);
this.editor.setOption("keyMap", val);
console.log(`Set keymap to ${val} for CodeMirror`);
};
keybindingDropdown.onchange();
// Highlight Active line
const highlightActiveChkBox = safeClearEventListeners("script-editor-option-highlightactiveline", "Active Line Checkbox");
highlightActiveChkBox.onchange = () => {
this.editor.setOption("styleActiveLine", highlightActiveChkBox.checked);
};
highlightActiveChkBox.onchange();
// Show Invisibles
const showInvisiblesChkBox = safeClearEventListeners("script-editor-option-showinvisibles", "Show Invisible Checkbox");
showInvisiblesChkBox.onchange = () => {
const overlayMode = {
name: 'invisibles',
token: function nextToken(stream) {
var ret,
spaces = 0,
space = stream.peek() === ' ';
if (space) {
while (space && spaces < MaxInvisibles) {
++spaces;
stream.next();
space = stream.peek() === ' ';
}
ret = 'whitespace whitespace-' + spaces;
} else {
while (!stream.eol() && !space) {
stream.next();
space = stream.peek() === ' ';
}
ret = 'cm-eol';
}
return ret;
}
};
if (showInvisiblesChkBox.checked) {
// Spaces
this.editor.addOverlay(overlayMode);
// Tabs
this.tabsStyleElement.innerHTML = ".cm-tab {background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAMCAYAAAAkuj5RAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAABDSURBVEhLYxgFo2BkAGYH/9r/QFoAxIGyhx6AORzZA4xD1TcHNjYzQplDB2CLgaECYHkADIZqqgGBoZdsRsHgBgwMAB8iFHF42AERAAAAAElFTkSuQmCC);background-position: right;background-repeat: no-repeat;}";
} else {
this.editor.removeOverlay("invisibles");
this.tabsStyleElement.innerHTML = "";
}
};
showInvisiblesChkBox.onchange();
//Use Soft Tab
const softTabChkBox = safeClearEventListeners("script-editor-option-usesofttab", "Soft Tab Checkbox");
softTabChkBox.onchange = () => {
this.editor.setOption("indentWithTabs", !softTabChkBox.checked);
if (softTabChkBox.checked) {
this.editor.addKeyMap({
name: "soft-tabs-keymap",
"Tab": function (cm) {
if (cm.somethingSelected()) {
var sel = cm.getSelection("\n");
// Indent only if there are multiple lines selected, or if the selection spans a full line
if (sel.length > 0 && (sel.indexOf("\n") > -1 || sel.length === cm.getLine(cm.getCursor().line).length)) {
cm.indentSelection("add");
return;
}
}
if (cm.options.indentWithTabs)
cm.execCommand("insertTab");
else
cm.execCommand("insertSoftTab");
},
"Shift-Tab": function (cm) {
cm.indentSelection("subtract");
}
});
} else {
this.editor.removeKeyMap("soft-tabs-keymap");
}
};
softTabChkBox.onchange();
// 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;
}
// Flex 1: Automatically Close Brackets and Quotes
const flex1Fieldset = resetFlexibleOption("script-editor-option-flex1-fieldset");
const flex1Id = "script-editor-option-flex1";
flex1Fieldset.appendChild(createElement("label", {
for: flex1Id,
innerText: "Auto-Close Brackets/Quotes",
}));
const flex1Checkbox = createElement("input", {
checked: true,
id: flex1Id,
name: flex1Id,
type: "checkbox",
});
flex1Fieldset.appendChild(flex1Checkbox);
flex1Checkbox.onchange = () => {
this.editor.setOption("autoCloseBrackets", flex1Checkbox.checked);
};
flex1Checkbox.onchange();
// Flex 2: Disable/Enable Linting
const flex2Fieldset = resetFlexibleOption("script-editor-option-flex2-fieldset");
const flex2Id = "script-editor-option-flex2";
flex2Fieldset.appendChild(createElement("label", {
for: flex2Id,
innerText: "Enable Linting",
}));
const flex2Checkbox = createElement("input", {
checked: true,
id: flex2Id,
name: flex2Id,
type: "checkbox",
});
flex2Fieldset.appendChild(flex2Checkbox);
flex2Checkbox.onchange = () => {
if (flex2Checkbox.checked) {
this.editor.setOption("lint", CodeMirror.lint.netscript);
} else {
this.editor.setOption("lint", false);
}
}
flex2Checkbox.onchange();
// Flex 3: Continue Comments
const flex3Fieldset = resetFlexibleOption("script-editor-option-flex3-fieldset");
const flex3Id = "script-editor-option-flex3";
flex3Fieldset.appendChild(createElement("label", {
for: flex3Id,
innerText: "Continue Comments",
}));
const flex3Checkbox = createElement("input", {
checked: true,
id: flex3Id,
name: flex3Id,
type: "checkbox",
});
flex3Fieldset.appendChild(flex3Checkbox);
flex3Checkbox.onchange = () => {
this.editor.setOption("continueComments", flex3Checkbox.checked);
}
flex3Checkbox.onchange();
removeFlexibleOption("script-editor-option-flex4-fieldset");
this.editor.refresh();
console.log(this.editor.options);
} catch(e) {
console.error(`Exception caught: ${e}`);
return false;
}
}
isFocused() {
if (this.editor == null) { return false; }
return this.editor.hasFocus();
}
// Sets the editor to be invisible
setInvisible() {
if (!this.initialized()) {
console.warn(`CodeMirrorEditor.setInvisible() called when editor was not initialized`);
return;
}
if (this.editor != null) {
this.editor.toTextArea();
this.editor = null;
}
const elem = document.getElementById("codemirror-form-wrapper");
if (elem instanceof HTMLElement) {
elem.style.display = "none";
}
}
}
export const CodeMirrorEditor = new CodeMirrorEditorWrapper();
@@ -0,0 +1,78 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/LICENSE
(function(mod) {
if (typeof exports == "object" && typeof module == "object") // CommonJS
mod(require("codemirror/lib/codemirror"));
else if (typeof define == "function" && define.amd) // AMD
define(["codemirror/lib/codemirror"], mod);
else // Plain browser env
mod(CodeMirror);
})(function(CodeMirror) {
"use strict";
// declare global: JSHINT
function validator(text, options) {
if (!window.JSHINT) {
if (window.console) {
window.console.error("Error: window.JSHINT not defined, CodeMirror JavaScript linting cannot run.");
}
return [];
}
// To ignore the 'async/await' errors, we'll manually edit the code ('text')
// that gets processed by JSHINT to include the ignore directory
const splitText = text.split("\n");
const ignoreDirective = " // jshint ignore:line";
for (let i = 0; i < splitText.length; ++i) {
if (splitText[i].match(/.*async function.+{/g)) {
splitText[i] += ignoreDirective;
} else if (splitText[i].match(/.*await.+;/g)) {
splitText[i] += ignoreDirective;
}
}
const sanitizedText = splitText.join("\n");
if (!options.indent) // JSHint error.character actually is a column index, this fixes underlining on lines using tabs for indentation
options.indent = 1; // JSHint default value is 4
JSHINT(sanitizedText, options, options.globals);
var errors = JSHINT.data().errors, result = [];
if (errors) parseErrors(errors, result);
return result;
}
CodeMirror.registerHelper("lint", "netscript", validator);
function parseErrors(errors, output) {
for ( var i = 0; i < errors.length; i++) {
var error = errors[i];
if (error) {
if (error.line == 0) { continue; }
if (error.line < 0) {
if (window.console) {
window.console.warn("Cannot display JSHint error (invalid line " + error.line + ")", error);
}
continue;
}
var start = error.character - 1, end = start + 1;
if (error.evidence) {
var index = error.evidence.substring(start).search(/.\b/);
if (index > -1) {
end += index;
}
}
// Convert to format expected by validation service
var hint = {
message: error.reason,
severity: error.code ? (error.code.startsWith('W') ? "warning" : "error") : "error",
from: CodeMirror.Pos(error.line - 1, start),
to: CodeMirror.Pos(error.line - 1, end)
};
output.push(hint);
}
}
}
});
File diff suppressed because it is too large Load Diff
+58
View File
@@ -0,0 +1,58 @@
// Base Script Editor class for the Ace/CodeMirror/etc. wrappers
const beautify = require('js-beautify').js_beautify;
export class ScriptEditor {
constructor() {
this.editor = null; // Stores the CodeMirror editor reference
this.filenameInput = null; // Stores the filename input DOM element
}
init() {
throw new Error(`Tried to initialize base ScriptEditor class`);
}
beautifyScript() {
if (this.editor == null) {
console.warn(`ScriptEditor.beautifyScript() called when editor was not initialized`);
return;
}
let code = this.editor.getValue();
code = beautify(code, {
indent_size: 4,
brace_style: "preserve-inline",
});
this.editor.setValue(code);
}
openScript(filename="", code="") {
if (this.editor == null || this.filenameInput == null) {
console.warn(`ScriptEditor.openScript() called when editor was not initialized`);
return;
}
if (filename != "") {
this.filenameInput.value = filename;
this.editor.setValue(code);
}
this.editor.focus();
}
getCode() {
if (this.editor == null) {
console.warn(`ScriptEditor.getCode() called when editor was not initialized`);
return "";
}
return this.editor.getValue();
}
getFilename() {
if (this.filenameInput == null) {
console.warn(`ScriptEditor.getFilename() called when editor was not initialized`);
return "";
}
return this.filenameInput.value;
}
}
-16
View File
@@ -1,16 +0,0 @@
/**
* Enum Of allowed values for the 'OwnedAugmentationsOrder' setting
*/
export enum OwnedAugmentationsOrderSetting {
Alphabetically,
AcquirementTime,
}
/**
* Enum Of allowed values for the 'OwnedAugmentationsOrder' setting
*/
export enum PurchaseAugmentationsOrderSetting {
Cost,
Default,
Reputation,
}
+83
View File
@@ -0,0 +1,83 @@
// Enums that defined allowed values for setting configuration
export enum CodeMirrorThemeSetting {
Monokai = "monokai",
Day_3024 = "3024-day",
Night_3024 = "3024-night",
abcdef = "abcdef",
Ambiance_mobile = "ambiance-mobile",
Ambiance = "ambiance",
Base16_dark = "base16-dark",
Base16_light = "base16-light",
Bespin = "bespin",
Blackboard = "blackboard",
Cobalt = "cobalt",
Colorforth = "colorforth",
Darcula = "darcula",
Dracula = "dracula",
Duotone_dark = "duotone-dark",
Duotone_light = "duotone-light",
Eclipse = "eclipse",
Elegant = "elegant",
Erlang_dark = "erlang-dark",
Gruvbox_dark = "gruvbox-dark",
Hopscotch = "hopscotch",
Icecoder = "icecoder",
Idea = "idea",
Isotope = "isotope",
Lesser_dark = "lesser-dark",
Liquibyte = "liquibyte",
Lucario = "lucario",
Material = "material",
Mbo = "mbo",
Mdn_like = "mdn-like",
Midnight = "midnight",
Neat = "neat",
Neo = "neo",
Night = "night",
Oceanic_next = "oceanic-next",
Panda_syntax = "panda-syntax",
Paraiso_dark = "paraiso-dark",
Paraiso_light = "paraiso-light",
Pastel_on_dark = "pastel-on-dark",
Railscasts = "railscasts",
Rubyblue = "rubyblue",
Seti = "seti",
Shadowfox = "shadowfox",
Solarized = "solarized",
ssms = "ssms",
The_matrix = "the-matrix",
Tomorrow_night_bright = "tomorrow-night-bright",
Tomorrow_night_eighties = "tomorrow-night-eighties",
Ttcn = "ttcn",
Twilight = "twilight",
Vibrant_ink = "vibrant-ink",
xq_dark = "xq-dark",
xq_light = "xq-light",
Yeti = "yeti",
Zenburn = "zenburn",
}
/**
* Allowed values for the "Editor" setting
*/
export enum EditorSetting {
Ace = "Ace",
CodeMirror = "CodeMirror",
}
/**
* Allowed values for the 'OwnedAugmentationsOrder' setting
*/
export enum PurchaseAugmentationsOrderSetting {
Cost,
Default,
Reputation,
}
/**
* Allowed values for the 'OwnedAugmentationsOrder' setting
*/
export enum OwnedAugmentationsOrderSetting {
Alphabetically,
AcquirementTime,
}
+12 -3
View File
@@ -1,5 +1,8 @@
import { ISelfInitializer, ISelfLoading } from "./types";
import { OwnedAugmentationsOrderSetting, PurchaseAugmentationsOrderSetting } from "./SettingEnums";
import { ISelfInitializer, ISelfLoading } from "../types";
import { CodeMirrorThemeSetting,
EditorSetting,
OwnedAugmentationsOrderSetting,
PurchaseAugmentationsOrderSetting } from "./SettingEnums";
/**
* Represents the default settings the player could customize.
@@ -65,6 +68,11 @@ interface IDefaultSettings {
* Represents all possible settings the player wants to customize to their play style.
*/
interface ISettings extends IDefaultSettings {
/**
* Which editor should be used (CodeMirror or Ace)?
*/
Editor: EditorSetting;
/**
* The keybinding to use in the script editor.
* TODO: This should really be an enum of allowed values.
@@ -75,7 +83,7 @@ interface ISettings extends IDefaultSettings {
* The theme used in the script editor.
* TODO: This should really be an enum of allowed values.
*/
EditorTheme: string;
EditorTheme: string | CodeMirrorThemeSetting;
/**
* What order the player's owned Augmentations/Source Files should be displayed in
@@ -110,6 +118,7 @@ export const Settings: ISettings & ISelfInitializer & ISelfLoading = {
AutosaveInterval: defaultSettings.AutosaveInterval,
CodeInstructionRunTime: 25,
DisableHotkeys: defaultSettings.DisableHotkeys,
Editor: EditorSetting.Ace,
EditorKeybinding: "ace",
EditorTheme: "Monokai",
Locale: "en",
+1 -1
View File
@@ -30,7 +30,7 @@ import {findRunningScript, RunningScript,
AllServersMap, isScriptFilename} from "./Script";
import {AllServers, GetServerByHostname,
getServer, Server} from "./Server";
import {Settings} from "./Settings";
import {Settings} from "./Settings/Settings";
import {SpecialServerIps,
SpecialServerNames} from "./SpecialServerIps";
import {getTextFile} from "./TextFile";
+19 -8
View File
@@ -51,10 +51,12 @@ import { displayCreateProgramContent,
initCreateProgramButtons } from "./Programs/ProgramHelpers";
import {redPillFlag, hackWorldDaemon} from "./RedPill";
import {saveObject, loadGame} from "./SaveObject";
import {loadAllRunningScripts, scriptEditorInit,
updateScriptEditorContent} from "./Script";
import { getCurrentEditor,
loadAllRunningScripts,
scriptEditorInit,
updateScriptEditorContent } from "./Script";
import {AllServers, Server, initForeignServers} from "./Server";
import {Settings} from "./Settings";
import {Settings} from "./Settings/Settings";
import { initSourceFiles, SourceFiles } from "./SourceFile";
import { updateSourceFileFlags } from "./SourceFile/SourceFileFlags";
@@ -103,6 +105,7 @@ import "../css/buttons.scss";
import "../css/mainmenu.scss";
import "../css/characteroverview.scss";
import "../css/terminal.scss";
import "../css/scripteditor.scss";
import "../css/menupages.scss";
import "../css/redpill.scss";
import "../css/stockmarket.scss";
@@ -137,6 +140,14 @@ import "../css/treant.css";
*/
$(document).keydown(function(e) {
if (Settings.DisableHotkeys === true) {return;}
// These hotkeys should be disabled if the player is writing scripts
try {
if (getCurrentEditor().isFocused()) {
return;
}
} catch(e) {}
if (!Player.isWorking && !redPillFlag && !inMission && !cinematicTextFlag) {
if (e.keyCode == 84 && e.altKey) {
e.preventDefault();
@@ -268,12 +279,12 @@ const Engine = {
loadScriptEditorContent: function(filename = "", code = "") {
Engine.hideAllContent();
Engine.Display.scriptEditorContent.style.display = "block";
var editor = ace.edit('javascript-editor');
if (filename != "") {
document.getElementById("script-editor-filename").value = filename;
editor.setValue(code);
try {
getCurrentEditor().openScript(filename, code);
} catch(e) {
exceptionAlert(e);
}
editor.focus();
updateScriptEditorContent();
routing.navigateTo(Page.ScriptEditor);
MainMenuLinks.ScriptEditor.classList.add("active");
+17 -21
View File
@@ -120,7 +120,8 @@ if (htmlWebpackPlugin.options.googleAnalytics.trackingId) { %>
<input id="script-editor-filename" type="text" maxlength="30" tabindex="1" />
</div>
<div id="javascript-editor"></div>
<div id="ace-editor"></div>
<form id="codemirror-form-wrapper"><textarea id="codemirror-editor"></textarea></form>
<div id="script-editor-buttons-wrapper"></div> <!-- Buttons below script editor -->
</div> <!-- End wrapper -->
@@ -128,26 +129,21 @@ if (htmlWebpackPlugin.options.googleAnalytics.trackingId) { %>
<div id="script-editor-options-panel">
<h1 style="color:white;"> Script Editor Options </h1>
<fieldset>
<label for="script-editor-option-theme">Theme</label>
<select id="script-editor-option-theme">
<option value="Chaos">Chaos</option>
<option value="Chrome">Chrome</option>
<option value="Monokai">Monokai</option>
<option value="Solarized_Dark">Solarized Dark</option>
<option value="Solarized_Light">Solarized Light</option>
<option value="Terminal">Terminal</option>
<option value="Twilight">Twilight</option>
<option value="XCode">XCode</option>
<label for="script-editor-option-editor">Editor</label>
<select id="script-editor-option-editor">
<option value="Ace">Ace</option>
<option value="CodeMirror">CodeMirror</option>
</select>
</fieldset>
<fieldset>
<label for="script-editor-option-theme">Theme</label>
<select id="script-editor-option-theme"></select>
</fieldset>
<fieldset>
<label for="script-editor-option-keybinding">Key Binding</label>
<select id="script-editor-option-keybinding">
<option value="ace">Ace</option>
<option value="vim">Vim</option>
<option value="emacs">Emacs</option>
</select>
<select id="script-editor-option-keybinding"></select>
</fieldset>
<fieldset>
@@ -165,11 +161,11 @@ if (htmlWebpackPlugin.options.googleAnalytics.trackingId) { %>
<input type="checkbox" name="script-editor-option-usesofttab" id="script-editor-option-usesofttab" checked>
</fieldset>
<fieldset>
<label for="script-editor-option-maxerr" class="tooltip">Max Error Count</label>
<input type="range" max="1000" min="50" value="200" step="1" name="script-editor-option-maxerr" id="script-editor-option-maxerr" />
<em id="script-editor-option-maxerror-value-label" style="font-style: normal;"></em>
</fieldset>
<fieldset id="script-editor-option-flex1-fieldset"></fieldset>
<fieldset id="script-editor-option-flex2-fieldset"></fieldset>
<fieldset id="script-editor-option-flex3-fieldset"></fieldset>
<fieldset id="script-editor-option-flex4-fieldset"></fieldset>
</div> <!-- End script editor options panel -->
</div>
+1 -1
View File
@@ -1,5 +1,5 @@
import {Engine} from "../engine";
import {Settings} from "../Settings";
import {Settings} from "../Settings/Settings";
import {numeralWrapper} from "./numeralFormat";