focus works

This commit is contained in:
Olivier Gagnon 2021-08-20 01:57:32 -04:00
parent 73ec97db87
commit 258716388e
5 changed files with 57 additions and 1887 deletions

1602
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -35,6 +35,7 @@
"loader-runner": "^2.3.0",
"loader-utils": "^1.1.0",
"memory-fs": "~0.4.1",
"monaco-editor": "^0.27.0",
"node-sass": "^6.0.1",
"normalize.css": "^8.0.0",
"numeral": "2.0.6",

@ -1,6 +1,8 @@
import React, { useState, useEffect } from 'react';
import React, { useState, useEffect, useRef } from 'react';
import { StdButton } from "../../ui/React/StdButton";
import Editor from "@monaco-editor/react";
import * as monaco from "monaco-editor";
import IStandaloneCodeEditor = monaco.editor.IStandaloneCodeEditor;
import { createPopup } from "../../ui/React/createPopup";
import { ConfigPopup } from "./ConfigPopup";
import { Config } from "./Config";
@ -16,6 +18,7 @@ import { TextFile } from "../../TextFile";
import { calculateRamUsage } from "../../Script/RamCalculations";
import { RamCalculationErrorCode } from "../../Script/RamCalculationErrorCodes";
import { numeralWrapper } from "../../ui/numeralFormat";
import { CursorPositions } from "../../ScriptEditor/CursorPositions";
interface IProps {
filename: string;
@ -30,13 +33,22 @@ interface IProps {
// https://www.npmjs.com/package/@monaco-editor/react#development-playground
export function Root(props: IProps): React.ReactElement {
const editorRef = useRef<IStandaloneCodeEditor | null>(null);
const [filename, setFilename] = useState(props.filename);
const [code, setCode] = useState<string>(props.code);
const [ram, setRAM] = useState('');
const [config, setConfig] = useState<Config>({theme: 'vs-dark'});
function save(): void {
//CursorPositions.saveCursor(filename, cursor);
if(editorRef.current !== null) {
const position = editorRef.current.getPosition();
if(position !== null) {
CursorPositions.saveCursor(filename, {
row: position.lineNumber,
column: position.column,
});
}
}
// TODO(hydroflame): re-enable the tutorial.
// if (ITutorial.isRunning && ITutorial.currStep === iTutorialSteps.TerminalTypeScript) {
@ -141,42 +153,46 @@ export function Root(props: IProps): React.ReactElement {
function updateCode(newCode?: string): void {
if(newCode === undefined) return;
setCode(newCode);
}
useEffect(() => {
async function updateRAM() {
const codeCopy = code.repeat(1);
const ramUsage = await calculateRamUsage(codeCopy, props.player.getCurrentServer().scripts);
if (ramUsage > 0) {
console.log(ramUsage);
setRAM("RAM: " + numeralWrapper.formatRAM(ramUsage));
return;
}
switch (ramUsage) {
case RamCalculationErrorCode.ImportError: {
setRAM("RAM: Import Error");
break;
}
case RamCalculationErrorCode.URLImportError: {
setRAM("RAM: HTTP Import Error");
break;
}
case RamCalculationErrorCode.SyntaxError:
default: {
setRAM("RAM: Syntax Error");
break;
}
}
return new Promise<void>(() => undefined);
async function updateRAM(): Promise<void> {
const codeCopy = code+"";
const ramUsage = await calculateRamUsage(codeCopy, props.player.getCurrentServer().scripts);
if (ramUsage > 0) {
setRAM("RAM: " + numeralWrapper.formatRAM(ramUsage));
return;
}
const id = setInterval(() => {
console.log(code);
updateRAM();
}, 10000);
switch (ramUsage) {
case RamCalculationErrorCode.ImportError: {
setRAM("RAM: Import Error");
break;
}
case RamCalculationErrorCode.URLImportError: {
setRAM("RAM: HTTP Import Error");
break;
}
case RamCalculationErrorCode.SyntaxError:
default: {
setRAM("RAM: Syntax Error");
break;
}
}
return new Promise<void>(() => undefined);
}
useEffect(() => {
const id = setInterval(updateRAM, 1000);
return () => clearInterval(id);
}, []);
}, [code]);
function onMount(editor: IStandaloneCodeEditor): void {
editorRef.current = editor;
if(editorRef.current === null) return;
const position = CursorPositions.getCursor(filename);
editorRef.current.setPosition({lineNumber: position.row, column: position.column});
editorRef.current.focus();
}
return (<div id="script-editor-wrapper">
<div id="script-editor-filename-wrapper">
@ -185,6 +201,7 @@ export function Root(props: IProps): React.ReactElement {
<StdButton text={"config"} onClick={openConfig} />
</div>
<Editor
onMount={onMount}
loading={<p>Loading script editor!</p>}
height="80%"
defaultLanguage="javascript"

@ -31,111 +31,6 @@ import { compareArrays } from "../../utils/helpers/compareArrays";
import { createElement } from "../../utils/uiHelpers/createElement";
var scriptEditorRamText = null;
export function scriptEditorInit() {
// Wrapper container that holds all the buttons below the script editor
const wrapper = document.getElementById("script-editor-buttons-wrapper");
if (wrapper == null) {
console.error("Could not find 'script-editor-buttons-wrapper'");
return false;
}
// Beautify button
const beautifyButton = createElement("button", {
class: "std-button",
display: "inline-block",
innerText: "Beautify",
clickListener:()=>{
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",
});
// Link to Netscript documentation
const documentationButton = createElement("a", {
class: "std-button",
display: "inline-block",
href:"https://bitburner.readthedocs.io/en/latest/index.html",
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);
wrapper.appendChild(documentationButton);
// Initialize editors
const initParams = {
saveAndCloseFn: saveAndCloseScriptEditor,
quitFn: Engine.loadTerminalContent,
}
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;
}
}
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;
}
Settings.Editor = opt;
}
editorSelector.onchange(); // Trigger the onchange event handler
}
export function getCurrentEditor() {
switch (Settings.Editor) {
@ -149,43 +44,7 @@ export function getCurrentEditor() {
}
}
//Updates RAM usage in script
export async function updateScriptEditorContent() {
var filename = document.getElementById("script-editor-filename").value;
if (!isScriptFilename(filename)) {
scriptEditorRamText.innerText = "RAM: N/A";
return;
}
let code;
try {
code = getCurrentEditor().getCode();
} catch(e) {
scriptEditorRamText.innerText = "RAM: ERROR";
return;
}
var codeCopy = code.repeat(1);
var ramUsage = await calculateRamUsage(codeCopy, Player.getCurrentServer().scripts);
if (ramUsage > 0) {
scriptEditorRamText.innerText = "RAM: " + numeralWrapper.formatRAM(ramUsage);
} else {
switch (ramUsage) {
case RamCalculationErrorCode.ImportError:
scriptEditorRamText.innerText = "RAM: Import Error";
break;
case RamCalculationErrorCode.URLImportError:
scriptEditorRamText.innerText = "RAM: HTTP Import Error";
break;
case RamCalculationErrorCode.SyntaxError:
default:
scriptEditorRamText.innerText = "RAM: Syntax Error";
break;
}
}
}
// TODO(hydroflame): move to Monaco mount/unmount
//Define key commands in script editor (ctrl o to save + close, etc.)
$(document).keydown(function(e) {
if (Settings.DisableHotkeys === true) {return;}
@ -193,104 +52,11 @@ $(document).keydown(function(e) {
//Ctrl + b
if (e.keyCode == 66 && (e.ctrlKey || e.metaKey)) {
e.preventDefault();
saveAndCloseScriptEditor();
saveAndCloseScriptEditor(); // deleted function
}
}
});
function saveAndCloseScriptEditor() {
var filename = document.getElementById("script-editor-filename").value;
let code, cursor;
try {
code = getCurrentEditor().getCode();
cursor = getCurrentEditor().getCursor();
CursorPositions.saveCursor(filename, cursor);
} catch(e) {
dialogBoxCreate("Something went wrong when trying to save (getCurrentEditor().getCode() or getCurrentEditor().getCursor()). Please report to game developer with details");
return;
}
if (ITutorial.isRunning && ITutorial.currStep === iTutorialSteps.TerminalTypeScript) {
//Make sure filename + code properly follow tutorial
if (filename !== "n00dles.script") {
dialogBoxCreate("Leave the script name as 'n00dles'!");
return;
}
code = code.replace(/\s/g, "");
if (code.indexOf("while(true){hack('n00dles');}") == -1) {
dialogBoxCreate("Please copy and paste the code from the tutorial!");
return;
}
//Save the script
let s = Player.getCurrentServer();
for (var i = 0; i < s.scripts.length; i++) {
if (filename == s.scripts[i].filename) {
s.scripts[i].saveScript(getCurrentEditor().getCode(), Player.currentServer, Player.getCurrentServer().scripts);
Engine.loadTerminalContent();
return iTutorialNextStep();
}
}
// If the current script does NOT exist, create a new one
let script = new Script();
script.saveScript(getCurrentEditor().getCode(), Player.currentServer, Player.getCurrentServer().scripts);
s.scripts.push(script);
return iTutorialNextStep();
}
if (filename == "") {
dialogBoxCreate("You must specify a filename!");
return;
}
if (filename !== ".fconf" && !isValidFilePath(filename)) {
dialogBoxCreate("Script filename can contain only alphanumerics, hyphens, and underscores, and must end with an extension.");
return;
}
var s = Player.getCurrentServer();
if (filename === ".fconf") {
try {
parseFconfSettings(code);
} catch(e) {
dialogBoxCreate(`Invalid .fconf file: ${e}`);
return;
}
} else if (isScriptFilename(filename)) {
//If the current script already exists on the server, overwrite it
for (let i = 0; i < s.scripts.length; i++) {
if (filename == s.scripts[i].filename) {
s.scripts[i].saveScript(getCurrentEditor().getCode(), Player.currentServer, Player.getCurrentServer().scripts);
Engine.loadTerminalContent();
return;
}
}
//If the current script does NOT exist, create a new one
const script = new Script();
script.saveScript(getCurrentEditor().getCode(), Player.currentServer, Player.getCurrentServer().scripts);
s.scripts.push(script);
} else if (filename.endsWith(".txt")) {
for (let i = 0; i < s.textFiles.length; ++i) {
if (s.textFiles[i].fn === filename) {
s.textFiles[i].write(code);
Engine.loadTerminalContent();
return;
}
}
const textFile = new TextFile(filename, code);
s.textFiles.push(textFile);
} else {
dialogBoxCreate("Invalid filename. Must be either a script (.script) or " +
" or text file (.txt)")
return;
}
Engine.loadTerminalContent();
}
export function scriptCalculateOfflineProduction(runningScriptObj) {
//The Player object stores the last update time from when we were online
const thisUpdate = new Date().getTime();

@ -66,11 +66,6 @@ import {
} from "./Programs/ProgramHelpers";
import { redPillFlag } from "./RedPill";
import { saveObject, loadGame } from "./SaveObject";
import {
getCurrentEditor,
scriptEditorInit,
updateScriptEditorContent,
} from "./Script/ScriptHelpers";
import { Root as ScriptEditorRoot } from "./Monaco/ui/Root";
import { initForeignServers, AllServers } from "./Server/AllServers";
import { Settings } from "./Settings/Settings";
@ -136,13 +131,6 @@ import ReactDOM from "react-dom";
$(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(error) {}
if (!Player.isWorking && !redPillFlag && !inMission && !cinematicTextFlag) {
if (e.keyCode == KEY.T && e.altKey) {
e.preventDefault();
@ -797,13 +785,6 @@ const Engine = {
Engine.Counters.updateDisplaysMed = 9;
}
if (Engine.Counters.updateDisplaysLong <= 0) {
if (routing.isOn(Page.ScriptEditor)) {
updateScriptEditorContent();
}
Engine.Counters.updateDisplaysLong = 15;
}
if (Engine.Counters.createProgramNotifications <= 0) {
var num = getNumAvailableCreateProgram();
var elem = document.getElementById("create-program-notification");
@ -1207,7 +1188,6 @@ const Engine = {
}
// Initialize labels on game settings
setSettingsLabels();
scriptEditorInit();
Terminal.resetTerminalInput();
},