Merge pull request #2078 from billyvg/feat/open-multiple-files-from-cli

feat: open multiple files from cli
This commit is contained in:
hydroflame 2021-12-21 11:02:11 -05:00 committed by GitHub
commit 5021013cb7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
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"; import libSource from "!!raw-loader!../NetscriptDefinitions.d.ts";
interface IProps { interface IProps {
filename: string; // Map of filename -> code
code: string; files: Record<string, string>;
hostname: string; hostname: string;
player: IPlayer; player: IPlayer;
router: IRouter; router: IRouter;
@ -330,47 +330,54 @@ export function Root(props: IProps): React.ReactElement {
if (editorRef.current === null || monacoRef.current === null) return; if (editorRef.current === null || monacoRef.current === null) return;
if (props.filename) { const files = Object.entries(props.files);
// 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]);
}
setCurrentScript(openScripts[openScriptIndex]); if (!files.length && currentScript !== null) {
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, props.filename.endsWith(".txt") ? "plaintext" : "javascript"),
);
setOpenScripts((oldArray) => [...oldArray, newScript]);
setCurrentScript({ ...newScript });
editorRef.current.setModel(newScript.model);
updateRAM(newScript.code);
}
} else if (currentScript !== null) {
// Open currentscript // Open currentscript
regenerateModel(currentScript); regenerateModel(currentScript);
editorRef.current.setModel(currentScript.model); editorRef.current.setModel(currentScript.model);
editorRef.current.setPosition(currentScript.lastPosition); editorRef.current.setPosition(currentScript.lastPosition);
editorRef.current.revealLineInCenter(currentScript.lastPosition.lineNumber); editorRef.current.revealLineInCenter(currentScript.lastPosition.lineNumber);
updateRAM(currentScript.code); 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, filename.endsWith(".txt") ? "plaintext" : "javascript"),
);
setOpenScripts((oldArray) => [...oldArray, newScript]);
setCurrentScript({ ...newScript });
editorRef.current.setModel(newScript.model);
updateRAM(newScript.code);
}
} }
editorRef.current.focus(); editorRef.current.focus();

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

@ -13,53 +13,56 @@ interface EditorParameters {
args: (string | number | boolean)[]; args: (string | number | boolean)[];
} }
function isNs2(filename: string): boolean {
return filename.endsWith(".ns") || filename.endsWith(".js");
}
export function commonEditor(command: string, { const newNs2Template = `/** @param {NS} ns **/
terminal, export async function main(ns) {
router,
player, }`;
server,
args, export function commonEditor(
}: EditorParameters, scriptEditorRouteOptions?: ScriptEditorRouteOptions): void { command: string,
if (args.length !== 1) { { terminal, router, player, server, args }: EditorParameters,
scriptEditorRouteOptions?: ScriptEditorRouteOptions,
): void {
if (args.length < 1) {
terminal.error(`Incorrect usage of ${command} command. Usage: ${command} [scriptname]`); terminal.error(`Incorrect usage of ${command} command. Usage: ${command} [scriptname]`);
return; return;
} }
try { try {
const filename = args[0] + ""; const files = args.map((arg) => {
const filename = `${arg}`;
if (isScriptFilename(filename)) { if (isScriptFilename(filename)) {
const filepath = terminal.getFilepath(filename); const filepath = terminal.getFilepath(filename);
const script = terminal.getScript(player, filename); const script = terminal.getScript(player, filename);
if (script == null) { const fileIsNs2 = isNs2(filename);
let code = ""; const code = script !== null ? script.code : fileIsNs2 ? newNs2Template : "";
if (filename.endsWith(".ns") || filename.endsWith(".js")) {
code = `/** @param {NS} ns **/
export async function main(ns) {
}`; if (code === newNs2Template) {
}
CursorPositions.saveCursor(filename, { CursorPositions.saveCursor(filename, {
row: 3, row: 3,
column: 5, 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 filepath = terminal.getFilepath(filename);
const txt = terminal.getTextFile(player, filename); const txt = terminal.getTextFile(player, filename);
if (txt == null) { return [filepath, txt == null ? "" : txt.text];
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;
} }
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) { } 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 = { export let Router: IRouter = {
page: () => { page: () => {
throw new Error("Router called before initialization"); 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 { export function GameRoot({ player, engine, terminal }: IProps): React.ReactElement {
const classes = useStyles(); const classes = useStyles();
const [{files, vim}, setEditorOptions] = useState({files: {}, vim: false})
const [page, setPage] = useState(determineStartPage(player)); const [page, setPage] = useState(determineStartPage(player));
const setRerender = useState(0)[1]; const setRerender = useState(0)[1];
const [faction, setFaction] = useState<Faction>( const [faction, setFaction] = useState<Faction>(
@ -247,10 +244,11 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
toHacknetNodes: () => setPage(Page.Hacknet), toHacknetNodes: () => setPage(Page.Hacknet),
toMilestones: () => setPage(Page.Milestones), toMilestones: () => setPage(Page.Milestones),
toResleeves: () => setPage(Page.Resleeves), toResleeves: () => setPage(Page.Resleeves),
toScriptEditor: (fn: string, c: string, options?: ScriptEditorRouteOptions) => { toScriptEditor: (files: Record<string, string>, options?: ScriptEditorRouteOptions) => {
filename = fn; setEditorOptions({
code = c; files,
vim = !!options?.vim; vim: !!options?.vim,
});
setPage(Page.ScriptEditor); setPage(Page.ScriptEditor);
}, },
toSleeves: () => setPage(Page.Sleeves), toSleeves: () => setPage(Page.Sleeves),
@ -292,8 +290,6 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
}; };
useEffect(() => { useEffect(() => {
filename = "";
code = "";
if (page !== Page.Terminal) window.scrollTo(0, 0); if (page !== Page.Terminal) window.scrollTo(0, 0);
}); });
@ -332,8 +328,7 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme
<CharacterStats /> <CharacterStats />
) : page === Page.ScriptEditor ? ( ) : page === Page.ScriptEditor ? (
<ScriptEditorRoot <ScriptEditorRoot
filename={filename} files={files}
code={code}
hostname={player.getCurrentServer().hostname} hostname={player.getCurrentServer().hostname}
player={player} player={player}
router={Router} router={Router}

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