From d30edc7f59958f9074cb2d801c1308551aaf8fa0 Mon Sep 17 00:00:00 2001 From: Billy Vong Date: Fri, 17 Dec 2021 12:48:34 -0500 Subject: [PATCH] feat: Add `vim` terminal command --- src/ScriptEditor/ui/ScriptEditorRoot.tsx | 2 +- src/Terminal/HelpText.ts | 8 +++ src/Terminal/Terminal.ts | 2 + src/Terminal/commands/common/editor.ts | 65 +++++++++++++++++++ src/Terminal/commands/nano.ts | 46 +------------ src/Terminal/commands/vim.ts | 16 +++++ ...termineAllPossibilitiesForTabCompletion.ts | 3 +- src/ui/GameRoot.tsx | 7 +- src/ui/Router.ts | 6 +- 9 files changed, 107 insertions(+), 48 deletions(-) create mode 100644 src/Terminal/commands/common/editor.ts create mode 100644 src/Terminal/commands/vim.ts diff --git a/src/ScriptEditor/ui/ScriptEditorRoot.tsx b/src/ScriptEditor/ui/ScriptEditorRoot.tsx index a5456c1bb..b19ac4d72 100644 --- a/src/ScriptEditor/ui/ScriptEditorRoot.tsx +++ b/src/ScriptEditor/ui/ScriptEditorRoot.tsx @@ -44,7 +44,7 @@ interface IProps { hostname: string; player: IPlayer; router: IRouter; - vim?: boolean; + vim: boolean; } // TODO: try to removve global symbols diff --git a/src/Terminal/HelpText.ts b/src/Terminal/HelpText.ts index 18de1a121..8ebb92c11 100644 --- a/src/Terminal/HelpText.ts +++ b/src/Terminal/HelpText.ts @@ -40,6 +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", "weaken [server] Reduce the security of a server", "wget [url] [target file] Retrieves code/text from a web server", ]; @@ -402,6 +403,13 @@ export const HelpTexts: IMap = { " ", "It is not necessary to differentiate between global and non-global aliases when using 'unalias'", ], + vim: [ + "vim [file name]", + " ", + "Opens up the specified file 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", + ], weaken: [ "weaken", "", diff --git a/src/Terminal/Terminal.ts b/src/Terminal/Terminal.ts index 25ed5264f..4f5f276a7 100644 --- a/src/Terminal/Terminal.ts +++ b/src/Terminal/Terminal.ts @@ -67,6 +67,7 @@ import { sudov } from "./commands/sudov"; import { tail } from "./commands/tail"; import { top } from "./commands/top"; import { unalias } from "./commands/unalias"; +import { vim } from "./commands/vim"; import { weaken } from "./commands/weaken"; import { wget } from "./commands/wget"; import { hash } from "../hash/hash"; @@ -789,6 +790,7 @@ export class Terminal implements ITerminal { tail: tail, top: top, unalias: unalias, + vim: vim, weaken: weaken, wget: wget, }; diff --git a/src/Terminal/commands/common/editor.ts b/src/Terminal/commands/common/editor.ts new file mode 100644 index 000000000..cc551e9b5 --- /dev/null +++ b/src/Terminal/commands/common/editor.ts @@ -0,0 +1,65 @@ +import { ITerminal } from "../../ITerminal"; +import { IRouter, ScriptEditorRouteOptions} from "../../../ui/Router"; +import { IPlayer } from "../../../PersonObjects/IPlayer"; +import { BaseServer } from "../../../Server/BaseServer"; +import { isScriptFilename } from "../../../Script/isScriptFilename"; +import { CursorPositions } from "../../../ScriptEditor/CursorPositions"; + +interface EditorParameters { + terminal: ITerminal; + router: IRouter; + player: IPlayer; + server: BaseServer; + args: (string | number | boolean)[]; +} + + +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] + ""; + 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) { + +}`; + } + CursorPositions.saveCursor(filename, { + row: 3, + column: 5, + }); + router.toScriptEditor(filepath, code, scriptEditorRouteOptions); + } else { + router.toScriptEditor(filepath, script.code, scriptEditorRouteOptions); + } + } else 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; + } + } catch (e) { + terminal.error(e + ""); + } +} diff --git a/src/Terminal/commands/nano.ts b/src/Terminal/commands/nano.ts index 18ad8a6eb..476fbebe6 100644 --- a/src/Terminal/commands/nano.ts +++ b/src/Terminal/commands/nano.ts @@ -2,8 +2,8 @@ import { ITerminal } from "../ITerminal"; import { IRouter } from "../../ui/Router"; import { IPlayer } from "../../PersonObjects/IPlayer"; import { BaseServer } from "../../Server/BaseServer"; -import { isScriptFilename } from "../../Script/isScriptFilename"; -import { CursorPositions } from "../../ScriptEditor/CursorPositions"; + +import {commonEditor} from './common/editor'; export function nano( terminal: ITerminal, @@ -12,45 +12,5 @@ export function nano( server: BaseServer, args: (string | number | boolean)[], ): void { - if (args.length !== 1) { - terminal.error("Incorrect usage of nano command. Usage: nano [scriptname]"); - return; - } - - try { - const filename = args[0] + ""; - 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) { - -}`; - } - CursorPositions.saveCursor(filename, { - row: 3, - column: 5, - }); - router.toScriptEditor(filepath, code); - } else { - router.toScriptEditor(filepath, script.code); - } - } else if (filename.endsWith(".txt")) { - const filepath = terminal.getFilepath(filename); - const txt = terminal.getTextFile(player, filename); - if (txt == null) { - router.toScriptEditor(filepath); - } else { - router.toScriptEditor(filepath, txt.text); - } - } else { - terminal.error("Invalid file. Only scripts (.script, .ns, .js), or text files (.txt) can be edited with nano"); - return; - } - } catch (e) { - terminal.error(e + ""); - } + return commonEditor('nano', {terminal, router, player, server, args}); } diff --git a/src/Terminal/commands/vim.ts b/src/Terminal/commands/vim.ts new file mode 100644 index 000000000..d469abf19 --- /dev/null +++ b/src/Terminal/commands/vim.ts @@ -0,0 +1,16 @@ +import { ITerminal } from "../ITerminal"; +import { IRouter } from "../../ui/Router"; +import { IPlayer } from "../../PersonObjects/IPlayer"; +import { BaseServer } from "../../Server/BaseServer"; + +import {commonEditor} from './common/editor'; + +export function vim( + terminal: ITerminal, + router: IRouter, + player: IPlayer, + server: BaseServer, + args: (string | number | boolean)[], +): void { + return commonEditor('vim', {terminal, router, player, server, args}, {vim: true}); +} diff --git a/src/Terminal/determineAllPossibilitiesForTabCompletion.ts b/src/Terminal/determineAllPossibilitiesForTabCompletion.ts index 5b07c0287..7e8cb07dd 100644 --- a/src/Terminal/determineAllPossibilitiesForTabCompletion.ts +++ b/src/Terminal/determineAllPossibilitiesForTabCompletion.ts @@ -49,6 +49,7 @@ const commands = [ "tail", "theme", "top", + "vim", "weaken", ]; @@ -265,7 +266,7 @@ export async function determineAllPossibilitiesForTabCompletion( return allPos; } - if (isCommand("nano")) { + if (isCommand("nano") || isCommand("vim")) { addAllScripts(); addAllTextFiles(); addAllDirectories(); diff --git a/src/ui/GameRoot.tsx b/src/ui/GameRoot.tsx index 494dfeea9..7362da33e 100644 --- a/src/ui/GameRoot.tsx +++ b/src/ui/GameRoot.tsx @@ -33,7 +33,7 @@ import createStyles from "@mui/styles/createStyles"; import Box from "@mui/material/Box"; import Typography from "@mui/material/Typography"; -import { Page, IRouter } from "./Router"; +import { Page, IRouter, ScriptEditorRouteOptions } from "./Router"; import { Overview } from "./React/Overview"; import { SidebarRoot } from "../Sidebar/ui/SidebarRoot"; import { AugmentationsRoot } from "../Augmentation/ui/AugmentationsRoot"; @@ -97,6 +97,7 @@ const useStyles = makeStyles((theme: Theme) => let filename = ""; let code = ""; +let vim = false; export let Router: IRouter = { page: () => { @@ -246,9 +247,10 @@ 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) => { + toScriptEditor: (fn: string, c: string, options?: ScriptEditorRouteOptions) => { filename = fn; code = c; + vim = !!options?.vim; setPage(Page.ScriptEditor); }, toSleeves: () => setPage(Page.Sleeves), @@ -335,6 +337,7 @@ export function GameRoot({ player, engine, terminal }: IProps): React.ReactEleme hostname={player.getCurrentServer().hostname} player={player} router={Router} + vim={vim} /> ) : page === Page.ActiveScripts ? ( diff --git a/src/ui/Router.ts b/src/ui/Router.ts index 6f7cb78e9..1540fc5c4 100644 --- a/src/ui/Router.ts +++ b/src/ui/Router.ts @@ -38,6 +38,10 @@ export enum Page { Recovery, } +export interface ScriptEditorRouteOptions { + vim: boolean; +} + /** * This class keeps track of player navigation/routing within the game. */ @@ -66,7 +70,7 @@ export interface IRouter { toJob(): void; toMilestones(): void; toResleeves(): void; - toScriptEditor(filename?: string, code?: string): void; + toScriptEditor(filename?: string, code?: string, options?: ScriptEditorRouteOptions): void; toSleeves(): void; toStockMarket(): void; toTerminal(): void;