text editor improvements

This commit is contained in:
Olivier Gagnon 2021-12-29 02:04:24 -05:00
parent ed86577d6c
commit 0fc95e6215
4 changed files with 85 additions and 96 deletions

@ -745,7 +745,7 @@ export function initBitNodeMultipliers(p: IPlayer): void {
BitNodeMultipliers.StaneksGiftPowerMultiplier = 0.5; BitNodeMultipliers.StaneksGiftPowerMultiplier = 0.5;
BitNodeMultipliers.StaneksGiftExtraSize = 2; BitNodeMultipliers.StaneksGiftExtraSize = 2;
BitNodeMultipliers.GangSoftcap = 0.8; BitNodeMultipliers.GangSoftcap = 0.8;
BitNodeMultipliers.CorporationSoftCap = 0.9; BitNodeMultipliers.CorporationSoftCap = 0.7;
BitNodeMultipliers.WorldDaemonDifficulty = 2; BitNodeMultipliers.WorldDaemonDifficulty = 2;
break; break;
case 10: // Digital Carbon case 10: // Digital Carbon

@ -64,7 +64,7 @@ import { NetscriptSleeve } from "./NetscriptFunctions/Sleeve";
import { NetscriptExtra } from "./NetscriptFunctions/Extra"; import { NetscriptExtra } from "./NetscriptFunctions/Extra";
import { NetscriptHacknet } from "./NetscriptFunctions/Hacknet"; import { NetscriptHacknet } from "./NetscriptFunctions/Hacknet";
import { NetscriptStanek } from "./NetscriptFunctions/Stanek"; import { NetscriptStanek } from "./NetscriptFunctions/Stanek";
import { NetscriptUserInterface } from './NetscriptFunctions/UserInterface'; import { NetscriptUserInterface } from "./NetscriptFunctions/UserInterface";
import { NetscriptBladeburner } from "./NetscriptFunctions/Bladeburner"; import { NetscriptBladeburner } from "./NetscriptFunctions/Bladeburner";
import { NetscriptCodingContract } from "./NetscriptFunctions/CodingContract"; import { NetscriptCodingContract } from "./NetscriptFunctions/CodingContract";
import { NetscriptCorporation } from "./NetscriptFunctions/Corporation"; import { NetscriptCorporation } from "./NetscriptFunctions/Corporation";
@ -2318,10 +2318,12 @@ export function NetscriptFunctions(workerScript: WorkerScript): NS {
...base, ...base,
...extra, ...extra,
}; };
function getFunctionNames(obj: NS, prefix: string): string[] { function getFunctionNames(obj: any, prefix: string): string[] {
const functionNames: string[] = []; const functionNames: string[] = [];
for (const [key, value] of Object.entries(obj)) { for (const [key, value] of Object.entries(obj)) {
if (typeof value == "function") { if (key === "args") {
continue;
} else if (typeof value == "function") {
functionNames.push(prefix + key); functionNames.push(prefix + key);
} else if (typeof value == "object") { } else if (typeof value == "object") {
functionNames.push(...getFunctionNames(value, key + ".")); functionNames.push(...getFunctionNames(value, key + "."));

@ -109,7 +109,6 @@ async function parseOnlyRamCalculate(
for (const s of otherScripts) { for (const s of otherScripts) {
if (areImportsEquals(s.filename, fn)) { if (areImportsEquals(s.filename, fn)) {
script = s; script = s;
console.log(`${s.filename} ${fn}`);
break; break;
} }
} }

@ -93,26 +93,21 @@ class OpenScript {
} }
} }
let openScripts: OpenScript[] = [];
let currentScript: OpenScript | null = null;
// Called every time script editor is opened // Called every time script editor is opened
export function Root(props: IProps): React.ReactElement { export function Root(props: IProps): React.ReactElement {
const setRerender = useState(false)[1];
function rerender(): void {
setRerender((o) => !o);
}
const editorRef = useRef<IStandaloneCodeEditor | null>(null); const editorRef = useRef<IStandaloneCodeEditor | null>(null);
const monacoRef = useRef<Monaco | null>(null); const monacoRef = useRef<Monaco | null>(null);
const vimStatusRef = useRef<HTMLElement>(null); const vimStatusRef = useRef<HTMLElement>(null);
const [vimEditor, setVimEditor] = useState<any>(null); const [vimEditor, setVimEditor] = useState<any>(null);
const [editor, setEditor] = useState<IStandaloneCodeEditor | null>(null); const [editor, setEditor] = useState<IStandaloneCodeEditor | null>(null);
const [openScripts, setOpenScripts] = useState<OpenScript[]>(
window.localStorage.getItem("scriptEditorOpenScripts") !== null
? JSON.parse(window.localStorage.getItem("scriptEditorOpenScripts")!)
: [],
);
const [currentScript, setCurrentScript] = useState<OpenScript | null>(
window.localStorage.getItem("scriptEditorCurrentScript") !== null
? JSON.parse(window.localStorage.getItem("scriptEditorCurrentScript")!)
: null,
);
const [ram, setRAM] = useState("RAM: ???"); const [ram, setRAM] = useState("RAM: ???");
const [updatingRam, setUpdatingRam] = useState(false); const [updatingRam, setUpdatingRam] = useState(false);
const [decorations, setDecorations] = useState<string[]>([]); const [decorations, setDecorations] = useState<string[]>([]);
@ -144,26 +139,6 @@ export function Root(props: IProps): React.ReactElement {
}; };
}, []); }, []);
useEffect(() => {
// Save currentScript
window.localStorage.setItem(
"scriptEditorCurrentScript",
JSON.stringify(currentScript, (key, value) => {
if (key == "model") return undefined;
return value;
}),
);
// Save openScripts
window.localStorage.setItem(
"scriptEditorOpenScripts",
JSON.stringify(openScripts, (key, value) => {
if (key == "model") return undefined;
return value;
}),
);
}, [currentScript, openScripts]);
useEffect(() => { useEffect(() => {
if (currentScript !== null) { if (currentScript !== null) {
updateRAM(currentScript.code); updateRAM(currentScript.code);
@ -171,23 +146,23 @@ export function Root(props: IProps): React.ReactElement {
}, []); }, []);
useEffect(() => { useEffect(() => {
function maybeSave(event: KeyboardEvent): void { function keydown(event: KeyboardEvent): void {
if (Settings.DisableHotkeys) return; if (Settings.DisableHotkeys) return;
//Ctrl + b //Ctrl + b
if (event.keyCode == 66 && (event.ctrlKey || event.metaKey)) { if (event.code == "KeyB" && (event.ctrlKey || event.metaKey)) {
event.preventDefault(); event.preventDefault();
save(); props.router.toTerminal();
} }
// CTRL/CMD + S // CTRL/CMD + S
if (event.code == `KeyS` && (event.ctrlKey || event.metaKey)) { if (event.code == "KeyS" && (event.ctrlKey || event.metaKey)) {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
save(); save();
} }
} }
document.addEventListener("keydown", maybeSave); document.addEventListener("keydown", keydown);
return () => document.removeEventListener("keydown", maybeSave); return () => document.removeEventListener("keydown", keydown);
}); });
useEffect(() => { useEffect(() => {
@ -362,7 +337,7 @@ export function Root(props: IProps): React.ReactElement {
regenerateModel(openScript); regenerateModel(openScript);
} }
setCurrentScript(openScript); currentScript = openScript;
editorRef.current.setModel(openScript.model); editorRef.current.setModel(openScript.model);
editorRef.current.setPosition(openScript.lastPosition); editorRef.current.setPosition(openScript.lastPosition);
editorRef.current.revealLineInCenter(openScript.lastPosition.lineNumber); editorRef.current.revealLineInCenter(openScript.lastPosition.lineNumber);
@ -376,14 +351,12 @@ export function Root(props: IProps): React.ReactElement {
new monacoRef.current.Position(0, 0), new monacoRef.current.Position(0, 0),
monacoRef.current.editor.createModel(code, filename.endsWith(".txt") ? "plaintext" : "javascript"), monacoRef.current.editor.createModel(code, filename.endsWith(".txt") ? "plaintext" : "javascript"),
); );
setOpenScripts((oldArray) => [...oldArray, newScript]); openScripts.push(newScript);
setCurrentScript({ ...newScript }); currentScript = { ...newScript };
editorRef.current.setModel(newScript.model); editorRef.current.setModel(newScript.model);
updateRAM(newScript.code); updateRAM(newScript.code);
} }
} }
} else {
console.log("here we need to load something if we can");
} }
editorRef.current.focus(); editorRef.current.focus();
@ -422,24 +395,26 @@ export function Root(props: IProps): React.ReactElement {
function updateCode(newCode?: string): void { function updateCode(newCode?: string): void {
if (newCode === undefined) return; if (newCode === undefined) return;
updateRAM(newCode); updateRAM(newCode);
if (editorRef.current !== null) { if (editorRef.current === null) return;
const newPos = editorRef.current.getPosition(); const newPos = editorRef.current.getPosition();
if (newPos === null) return; if (newPos === null) return;
setCurrentScript((oldScript) => ({ ...oldScript!, code: newCode, lastPosition: newPos! })); if (currentScript !== null) {
if (currentScript !== null) { currentScript = { ...currentScript, code: newCode, lastPosition: newPos };
const curIndex = openScripts.findIndex( const curIndex = openScripts.findIndex(
(script) => script.fileName === currentScript.fileName && script.hostname === currentScript.hostname, (script) =>
); currentScript !== null &&
const newArr = [...openScripts]; script.fileName === currentScript.fileName &&
const tempScript = currentScript; script.hostname === currentScript.hostname,
tempScript.code = newCode; );
newArr[curIndex] = tempScript; const newArr = [...openScripts];
setOpenScripts([...newArr]); const tempScript = currentScript;
} tempScript.code = newCode;
try { newArr[curIndex] = tempScript;
infLoop(newCode); openScripts = [...newArr];
} catch (err) {}
} }
try {
infLoop(newCode);
} catch (err) {}
} }
function saveScript(scriptToSave: OpenScript): void { function saveScript(scriptToSave: OpenScript): void {
@ -487,7 +462,7 @@ export function Root(props: IProps): React.ReactElement {
function save(): void { function save(): void {
if (currentScript === null) { if (currentScript === null) {
console.log("currentScript is null when it shouldn't be. Unabel to save script"); console.error("currentScript is null when it shouldn't be. Unable to save script");
return; return;
} }
// this is duplicate code with saving later. // this is duplicate code with saving later.
@ -507,7 +482,6 @@ export function Root(props: IProps): React.ReactElement {
iTutorialNextStep(); iTutorialNextStep();
props.router.toTerminal();
return; return;
} }
@ -536,7 +510,6 @@ export function Root(props: IProps): React.ReactElement {
server.scripts, server.scripts,
); );
if (Settings.SaveGameOnFileSave) saveObject.saveGame(); if (Settings.SaveGameOnFileSave) saveObject.saveGame();
props.router.toTerminal();
return; return;
} }
} }
@ -550,7 +523,6 @@ export function Root(props: IProps): React.ReactElement {
if (server.textFiles[i].fn === currentScript.fileName) { if (server.textFiles[i].fn === currentScript.fileName) {
server.textFiles[i].write(currentScript.code); server.textFiles[i].write(currentScript.code);
if (Settings.SaveGameOnFileSave) saveObject.saveGame(); if (Settings.SaveGameOnFileSave) saveObject.saveGame();
props.router.toTerminal();
return; return;
} }
} }
@ -562,7 +534,6 @@ export function Root(props: IProps): React.ReactElement {
} }
if (Settings.SaveGameOnFileSave) saveObject.saveGame(); if (Settings.SaveGameOnFileSave) saveObject.saveGame();
props.router.toTerminal();
} }
function reorder(list: Array<OpenScript>, startIndex: number, endIndex: number): OpenScript[] { function reorder(list: Array<OpenScript>, startIndex: number, endIndex: number): OpenScript[] {
@ -582,19 +553,22 @@ export function Root(props: IProps): React.ReactElement {
const items = reorder(openScripts, result.source.index, result.destination.index); const items = reorder(openScripts, result.source.index, result.destination.index);
setOpenScripts(items); openScripts = items;
} }
function onTabClick(index: number): void { function onTabClick(index: number): void {
if (currentScript !== null) { if (currentScript !== null) {
// Save currentScript to openScripts // Save currentScript to openScripts
const curIndex = openScripts.findIndex( const curIndex = openScripts.findIndex(
(script) => script.fileName === currentScript.fileName && script.hostname === currentScript.hostname, (script) =>
currentScript !== null &&
script.fileName === currentScript.fileName &&
script.hostname === currentScript.hostname,
); );
openScripts[curIndex] = currentScript; openScripts[curIndex] = currentScript;
} }
setCurrentScript({ ...openScripts[index] }); currentScript = { ...openScripts[index] };
if (editorRef.current !== null && openScripts[index] !== null) { if (editorRef.current !== null && openScripts[index] !== null) {
if (openScripts[index].model === undefined || openScripts[index].model.isDisposed()) { if (openScripts[index].model === undefined || openScripts[index].model.isDisposed()) {
@ -609,25 +583,21 @@ export function Root(props: IProps): React.ReactElement {
} }
} }
async function onTabClose(index: number): Promise<void> { function onTabClose(index: number): void {
// See if the script on the server is up to date // See if the script on the server is up to date
const closingScript = openScripts[index]; const closingScript = openScripts[index];
const savedOpenScripts: Array<OpenScript> = JSON.parse(window.localStorage.getItem("scriptEditorOpenScripts")!); const savedScriptIndex = openScripts.findIndex(
const savedScriptIndex = savedOpenScripts.findIndex(
(script) => script.fileName === closingScript.fileName && script.hostname === closingScript.hostname, (script) => script.fileName === closingScript.fileName && script.hostname === closingScript.hostname,
); );
let savedScriptCode = ""; let savedScriptCode = "";
if (savedScriptIndex !== -1) { if (savedScriptIndex !== -1) {
savedScriptCode = savedOpenScripts[savedScriptIndex].code; savedScriptCode = openScripts[savedScriptIndex].code;
} }
const server = GetServer(closingScript.hostname);
if (server === null) throw new Error(`Server '${closingScript.hostname}' should not be null, but it is.`);
const serverScriptIndex = GetServer(closingScript.hostname)?.scripts.findIndex( const serverScriptIndex = server.scripts.findIndex((script) => script.filename === closingScript.fileName);
(script) => script.filename === closingScript.fileName, if (serverScriptIndex === -1 || savedScriptCode !== server.scripts[serverScriptIndex as number].code) {
);
if (
serverScriptIndex === -1 ||
savedScriptCode !== GetServer(closingScript.hostname)?.scripts[serverScriptIndex as number].code
) {
PromptEvent.emit({ PromptEvent.emit({
txt: "Do you want to save changes to " + closingScript.fileName + "?", txt: "Do you want to save changes to " + closingScript.fileName + "?",
resolve: (result: boolean) => { resolve: (result: boolean) => {
@ -641,15 +611,18 @@ export function Root(props: IProps): React.ReactElement {
} }
if (openScripts.length > 1) { if (openScripts.length > 1) {
setOpenScripts((oldScripts) => oldScripts.filter((value, i) => i !== index)); openScripts = openScripts.filter((value, i) => i !== index);
let indexOffset = -1; let indexOffset = -1;
if (openScripts[index + indexOffset] === undefined) { if (openScripts[index + indexOffset] === undefined) {
indexOffset = 1; indexOffset = 1;
if (openScripts[index + indexOffset] === undefined) {
indexOffset = 0;
}
} }
// Change current script if we closed it // Change current script if we closed it
setCurrentScript(openScripts[index + indexOffset]); currentScript = openScripts[index + indexOffset];
if (editorRef.current !== null) { if (editorRef.current !== null) {
if ( if (
openScripts[index + indexOffset].model === undefined || openScripts[index + indexOffset].model === undefined ||
@ -664,14 +637,26 @@ export function Root(props: IProps): React.ReactElement {
editorRef.current.revealLineInCenter(openScripts[index + indexOffset].lastPosition.lineNumber); editorRef.current.revealLineInCenter(openScripts[index + indexOffset].lastPosition.lineNumber);
editorRef.current.focus(); editorRef.current.focus();
} }
rerender();
} else { } else {
// No more scripts are open // No more scripts are open
setOpenScripts([]); openScripts = [];
setCurrentScript(null); currentScript = null;
props.router.toTerminal(); props.router.toTerminal();
} }
} }
function dirty(index: number): string {
const openScript = openScripts[index];
const server = GetServer(openScript.hostname);
if (server === null) throw new Error(`Server '${openScript.hostname}' should not be null, but it is.`);
const serverScript = server.scripts.find((s) => s.filename === openScript.fileName);
if (serverScript === undefined) return " *";
return serverScript.code !== openScript.code ? " *" : "";
}
// Toolbars are roughly 112px: // Toolbars are roughly 112px:
// 8px body margin top // 8px body margin top
// 38.5px filename tabs // 38.5px filename tabs
@ -718,7 +703,6 @@ export function Root(props: IProps): React.ReactElement {
}} }}
> >
<Button <Button
id={"tabButton" + fileName + hostname}
onClick={() => onTabClick(index)} onClick={() => onTabClick(index)}
style={{ style={{
background: background:
@ -727,10 +711,9 @@ export function Root(props: IProps): React.ReactElement {
: "", : "",
}} }}
> >
{hostname}:~/{fileName} {hostname}:~/{fileName} {dirty(index)}
</Button> </Button>
<Button <Button
id={"tabCloseButton" + fileName + hostname}
onClick={() => onTabClose(index)} onClick={() => onTabClose(index)}
style={{ style={{
maxWidth: "20px", maxWidth: "20px",
@ -779,7 +762,8 @@ export function Root(props: IProps): React.ReactElement {
<Typography color={updatingRam ? "secondary" : "primary"} sx={{ mx: 1 }}> <Typography color={updatingRam ? "secondary" : "primary"} sx={{ mx: 1 }}>
{ram} {ram}
</Typography> </Typography>
<Button onClick={save}>Save & Close (Ctrl/Cmd + s)</Button> <Button onClick={save}>Save All (Ctrl/Cmd + s)</Button>
<Button onClick={props.router.toTerminal}>Close (Ctrl/Cmd + b)</Button>
<Typography sx={{ mx: 1 }}> <Typography sx={{ mx: 1 }}>
{" "} {" "}
Documentation:{" "} Documentation:{" "}
@ -825,10 +809,14 @@ export function Root(props: IProps): React.ReactElement {
alignItems: "center", alignItems: "center",
}} }}
> >
<p style={{ color: Settings.theme.primary, fontSize: "20px", textAlign: "center" }}> <span style={{ color: Settings.theme.primary, fontSize: "20px", textAlign: "center" }}>
<h1>No open files</h1> <Typography variant="h4">No open files</Typography>
<h5>Use "nano [File Name]" in the terminal to open files</h5> <Typography variant="h5">
</p> Use `nano FILENAME` in
<br />
the terminal to open files
</Typography>
</span>
</div> </div>
</> </>
); );