feat: Support opening multiple files from command line

This commit is contained in:
Billy Vong 2021-12-20 12:42:47 -05:00
parent d30edc7f59
commit bb2f8e883c
5 changed files with 99 additions and 94 deletions

@ -39,8 +39,8 @@ import { PromptEvent } from "../../ui/React/PromptManager";
import libSource from "!!raw-loader!../NetscriptDefinitions.d.ts";
interface IProps {
filename: string;
code: string;
// Map of filename -> code
files: Record<string, string>;
hostname: string;
player: IPlayer;
router: IRouter;
@ -307,47 +307,54 @@ export function Root(props: IProps): React.ReactElement {
if (editorRef.current === null || monacoRef.current === null) return;
if (props.filename) {
// Check if file is already opened
const openScriptIndex = openScripts.findIndex(
(script) => script.fileName === props.filename && script.hostname === props.hostname,
);
if (openScriptIndex !== -1) {
// Script is already opened
if (
openScripts[openScriptIndex].model === undefined ||
openScripts[openScriptIndex].model === null ||
openScripts[openScriptIndex].model.isDisposed()
) {
regenerateModel(openScripts[openScriptIndex]);
}
const files = Object.entries(props.files);
setCurrentScript(openScripts[openScriptIndex]);
editorRef.current.setModel(openScripts[openScriptIndex].model);
editorRef.current.setPosition(openScripts[openScriptIndex].lastPosition);
editorRef.current.revealLineInCenter(openScripts[openScriptIndex].lastPosition.lineNumber);
updateRAM(openScripts[openScriptIndex].code);
} else {
// Open script
const newScript = new OpenScript(
props.filename,
props.code,
props.hostname,
new monacoRef.current.Position(0, 0),
monacoRef.current.editor.createModel(props.code, "javascript"),
);
setOpenScripts((oldArray) => [...oldArray, newScript]);
setCurrentScript({ ...newScript });
editorRef.current.setModel(newScript.model);
updateRAM(newScript.code);
}
} else if (currentScript !== null) {
if (!files.length && currentScript !== null) {
// Open currentscript
regenerateModel(currentScript);
editorRef.current.setModel(currentScript.model);
editorRef.current.setPosition(currentScript.lastPosition);
editorRef.current.revealLineInCenter(currentScript.lastPosition.lineNumber);
updateRAM(currentScript.code);
editorRef.current.focus();
return;
}
if (!files.length) {
editorRef.current.focus();
return;
}
for (const [filename, code] of files) {
// Check if file is already opened
const openScript = openScripts.find(
(script) => script.fileName === filename && script.hostname === props.hostname,
);
if (openScript) {
// Script is already opened
if (openScript.model === undefined || openScript.model === null || openScript.model.isDisposed()) {
regenerateModel(openScript);
}
setCurrentScript(openScript);
editorRef.current.setModel(openScript.model);
editorRef.current.setPosition(openScript.lastPosition);
editorRef.current.revealLineInCenter(openScript.lastPosition.lineNumber);
updateRAM(openScript.code);
} else {
// Open script
const newScript = new OpenScript(
filename,
code,
props.hostname,
new monacoRef.current.Position(0, 0),
monacoRef.current.editor.createModel(code, "javascript"),
);
setOpenScripts((oldArray) => [...oldArray, newScript]);
setCurrentScript({ ...newScript });
editorRef.current.setModel(newScript.model);
updateRAM(newScript.code);
}
}
editorRef.current.focus();

@ -29,7 +29,7 @@ export const TerminalHelpText: string[] = [
"lscpu Displays the number of CPU cores on the machine",
"mem [script] [-t] [n] Displays the amount of RAM required to run the script",
"mv [src] [dest] Move/rename a text or script file",
"nano [file] Text editor - Open up and edit a script or text file",
"nano [file ...] Text editor - Open up and edit one or more scripts or text files",
"ps Display all scripts that are currently running",
"rm [file] Delete a file from the server",
"run [name] [-t n] [--tail] [args...] Execute a program or script",
@ -40,7 +40,7 @@ export const TerminalHelpText: string[] = [
"tail [script] [args...] Displays dynamic logs for the specified script",
"top Displays all running scripts and their RAM usage",
"unalias [alias name] Deletes the specified alias",
"vim [file] Text editor - Open up and edit a script or text file in vim mode",
"vim [file ...] Text editor - Open up and edit one or more scripts or text files in vim mode",
"weaken [server] Reduce the security of a server",
"wget [url] [target file] Retrieves code/text from a web server",
];
@ -305,9 +305,9 @@ export const HelpTexts: IMap<string[]> = {
"mv myScript.js myOldScript.js",
],
nano: [
"nano [file name]",
"nano [file ...]",
" ",
"Opens up the specified file in the Text Editor. Only scripts (.script) or text files (.txt) can be ",
"Opens up the specified file(s) in the Text Editor. Only scripts (.script) or text files (.txt) can be ",
"edited using the Text Editor. If the file does not already exist, then a new, empty one ",
"will be created",
],
@ -404,9 +404,9 @@ export const HelpTexts: IMap<string[]> = {
"It is not necessary to differentiate between global and non-global aliases when using 'unalias'",
],
vim: [
"vim [file name]",
"vim [file ...]",
" ",
"Opens up the specified file in the Text Editor in vim mode. Only scripts (.script) or text files (.txt) can be ",
"Opens up the specified file(s) in the Text Editor in vim mode. Only scripts (.script) or text files (.txt) can be ",
"edited using the Text Editor. If the file does not already exist, then a new, empty one ",
"will be created",
],

@ -1,5 +1,5 @@
import { ITerminal } from "../../ITerminal";
import { IRouter, ScriptEditorRouteOptions} from "../../../ui/Router";
import { IRouter, ScriptEditorRouteOptions } from "../../../ui/Router";
import { IPlayer } from "../../../PersonObjects/IPlayer";
import { BaseServer } from "../../../Server/BaseServer";
import { isScriptFilename } from "../../../Script/isScriptFilename";
@ -13,53 +13,56 @@ interface EditorParameters {
args: (string | number | boolean)[];
}
function isNs2(filename: string): boolean {
return filename.endsWith(".ns") || filename.endsWith(".js");
}
export function commonEditor(command: string, {
terminal,
router,
player,
server,
args,
}: EditorParameters, scriptEditorRouteOptions?: ScriptEditorRouteOptions): void {
if (args.length !== 1) {
const newNs2Template = `/** @param {NS} ns **/
export async function main(ns) {
}`;
export function commonEditor(
command: string,
{ terminal, router, player, server, args }: EditorParameters,
scriptEditorRouteOptions?: ScriptEditorRouteOptions,
): void {
if (args.length < 1) {
terminal.error(`Incorrect usage of ${command} command. Usage: ${command} [scriptname]`);
return;
}
try {
const filename = args[0] + "";
const files = args.map((arg) => {
const filename = `${arg}`;
if (isScriptFilename(filename)) {
const filepath = terminal.getFilepath(filename);
const script = terminal.getScript(player, filename);
if (script == null) {
let code = "";
if (filename.endsWith(".ns") || filename.endsWith(".js")) {
code = `/** @param {NS} ns **/
export async function main(ns) {
const fileIsNs2 = isNs2(filename);
const code = script !== null ? script.code : fileIsNs2 ? newNs2Template : "";
}`;
}
if (code === newNs2Template) {
CursorPositions.saveCursor(filename, {
row: 3,
column: 5,
});
router.toScriptEditor(filepath, code, scriptEditorRouteOptions);
} else {
router.toScriptEditor(filepath, script.code, scriptEditorRouteOptions);
}
} else if (filename.endsWith(".txt")) {
return [filepath, code];
}
if (filename.endsWith(".txt")) {
const filepath = terminal.getFilepath(filename);
const txt = terminal.getTextFile(player, filename);
if (txt == null) {
router.toScriptEditor(filepath, "", scriptEditorRouteOptions);
} else {
router.toScriptEditor(filepath, txt.text, scriptEditorRouteOptions);
}
} else {
terminal.error("Invalid file. Only scripts (.script, .ns, .js), or text files (.txt) can be edited with vim");
return;
return [filepath, txt == null ? "" : txt.text];
}
throw new Error(`Invalid file. Only scripts (.script, .ns, .js), or text files (.txt) can be edited with ${command}`);
});
router.toScriptEditor(Object.fromEntries(files), scriptEditorRouteOptions);
} catch (e) {
terminal.error(e + "");
terminal.error(`${e}`);
}
}

@ -95,10 +95,6 @@ const useStyles = makeStyles((theme: Theme) =>
}),
);
let filename = "";
let code = "";
let vim = false;
export let Router: IRouter = {
page: () => {
throw new Error("Router called before initialization");
@ -197,6 +193,7 @@ function determineStartPage(player: IPlayer): Page {
export function GameRoot({ player, engine, terminal }: IProps): React.ReactElement {
const classes = useStyles();
const [{files, vim}, setEditorOptions] = useState({files: {}, vim: false})
const [page, setPage] = useState(determineStartPage(player));
const setRerender = useState(0)[1];
const [faction, setFaction] = useState<Faction>(
@ -247,10 +244,11 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
toHacknetNodes: () => setPage(Page.Hacknet),
toMilestones: () => setPage(Page.Milestones),
toResleeves: () => setPage(Page.Resleeves),
toScriptEditor: (fn: string, c: string, options?: ScriptEditorRouteOptions) => {
filename = fn;
code = c;
vim = !!options?.vim;
toScriptEditor: (files: Record<string, string>, options?: ScriptEditorRouteOptions) => {
setEditorOptions({
files,
vim: !!options?.vim,
});
setPage(Page.ScriptEditor);
},
toSleeves: () => setPage(Page.Sleeves),
@ -292,8 +290,6 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
};
useEffect(() => {
filename = "";
code = "";
if (page !== Page.Terminal) window.scrollTo(0, 0);
});
@ -332,8 +328,7 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
<CharacterStats />
) : page === Page.ScriptEditor ? (
<ScriptEditorRoot
filename={filename}
code={code}
files={files}
hostname={player.getCurrentServer().hostname}
player={player}
router={Router}

@ -70,7 +70,7 @@ export interface IRouter {
toJob(): void;
toMilestones(): void;
toResleeves(): void;
toScriptEditor(filename?: string, code?: string, options?: ScriptEditorRouteOptions): void;
toScriptEditor(files?: Record<string, string>, options?: ScriptEditorRouteOptions): void;
toSleeves(): void;
toStockMarket(): void;
toTerminal(): void;