mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-11-22 23:53:48 +01:00
Merge pull request #2527 from smolgumball/add-simple-globs-nano-vim
feat: add simple glob support to open multiple files with `nano` & `vim` commands
This commit is contained in:
commit
c791e8264a
@ -7,7 +7,7 @@ export const TerminalHelpText: string[] = [
|
||||
'alias [-g] [name="value"] Create or display Terminal aliases',
|
||||
"analyze Get information about the current machine ",
|
||||
"backdoor Install a backdoor on the current machine ",
|
||||
" buy [-l/-a/program] Purchase a program through the Dark Web",
|
||||
"buy [-l/program] Purchase a program through the Dark Web",
|
||||
"cat [file] Display a .msg, .lit, or .txt file",
|
||||
"cd [dir] Change to a new directory",
|
||||
"check [script] [args...] Print a script's logs to Terminal",
|
||||
@ -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 one or more scripts or text files",
|
||||
"nano [file ...] | [glob] 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,12 +40,33 @@ 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 one or more scripts or text files in vim mode",
|
||||
"vim [file ...] | [glob] Text editor - Open up and edit one or more scripts or text files in vim mode",
|
||||
"weaken Reduce the security of the current machine",
|
||||
"wget [url] [target file] Retrieves code/text from a web server",
|
||||
" ",
|
||||
];
|
||||
|
||||
const TemplatedHelpTexts: IMap<(command: string) => string[]> = {
|
||||
scriptEditor: (command) => {
|
||||
return [
|
||||
`${command} [file ...] | [glob]`,
|
||||
` `,
|
||||
`Opens up the specified file(s) in the Script Editor. Only scripts (.js, .ns, .script) or text files (.txt) `,
|
||||
`can be edited using the Script Editor. If a file does not exist a new one will be created`,
|
||||
` `,
|
||||
`If provided a glob as the only argument, ${command} can spider directories and open all matching `,
|
||||
`files at once. ${command} cannot create files using globs, so your scripts must already exist.`,
|
||||
` `,
|
||||
`Examples:`,
|
||||
` `,
|
||||
`${command} test.js`,
|
||||
`${command} test.js test2.js`,
|
||||
` `,
|
||||
`${command} test.*`,
|
||||
`${command} /my-dir/*.js`,
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
export const HelpTexts: IMap<string[]> = {
|
||||
alias: [
|
||||
'Usage: alias [-g] [name="value"] ',
|
||||
@ -322,16 +343,8 @@ export const HelpTexts: IMap<string[]> = {
|
||||
" mv myScript.js myOldScript.js",
|
||||
" ",
|
||||
],
|
||||
nano: [
|
||||
"Usage: nano [file ...]",
|
||||
" ",
|
||||
"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",
|
||||
" ",
|
||||
],
|
||||
nano: TemplatedHelpTexts.scriptEditor('nano'),
|
||||
ps: ["Usage: ps", " ", "Prints all scripts that are running on the current server", " "],
|
||||
|
||||
rm: [
|
||||
"Usage: rm [file]",
|
||||
" ",
|
||||
@ -433,14 +446,7 @@ export const HelpTexts: IMap<string[]> = {
|
||||
"It is not necessary to differentiate between global and non-global aliases when using 'unalias'",
|
||||
" ",
|
||||
],
|
||||
vim: [
|
||||
"Usage: vim [file ...]",
|
||||
" ",
|
||||
"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",
|
||||
" ",
|
||||
],
|
||||
vim: TemplatedHelpTexts.scriptEditor('vim'),
|
||||
weaken: [
|
||||
"Usage: weaken",
|
||||
" ",
|
||||
|
@ -1,9 +1,12 @@
|
||||
import { ITerminal } from "../../ITerminal";
|
||||
import { removeLeadingSlash, removeTrailingSlash } from '../../DirectoryHelpers'
|
||||
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";
|
||||
import { Script } from "../../../Script/Script";
|
||||
import { isEmpty } from "lodash";
|
||||
|
||||
interface EditorParameters {
|
||||
terminal: ITerminal;
|
||||
@ -22,6 +25,74 @@ export async function main(ns) {
|
||||
|
||||
}`;
|
||||
|
||||
interface ISimpleScriptGlob {
|
||||
glob: string;
|
||||
preGlob: string;
|
||||
postGlob: string;
|
||||
globError: string;
|
||||
globMatches: string[];
|
||||
globAgainst: Script[];
|
||||
}
|
||||
|
||||
function containsSimpleGlob(filename: string): boolean {
|
||||
return filename.includes("*");
|
||||
}
|
||||
|
||||
function detectSimpleScriptGlob(
|
||||
args: EditorParameters["args"],
|
||||
player: IPlayer,
|
||||
terminal: ITerminal,
|
||||
): ISimpleScriptGlob | null {
|
||||
if (args.length == 1 && containsSimpleGlob(`${args[0]}`)) {
|
||||
const filename = `${args[0]}`;
|
||||
const scripts = player.getCurrentServer().scripts;
|
||||
const parsedGlob = parseSimpleScriptGlob(filename, scripts, terminal);
|
||||
return parsedGlob;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function parseSimpleScriptGlob(globString: string, globDatabase: Script[], terminal: ITerminal): ISimpleScriptGlob {
|
||||
const parsedGlob: ISimpleScriptGlob = {
|
||||
glob: globString,
|
||||
preGlob: "",
|
||||
postGlob: "",
|
||||
globError: "",
|
||||
globMatches: [],
|
||||
globAgainst: globDatabase,
|
||||
};
|
||||
|
||||
// Ensure deep globs are minified to simple globs, which act as deep globs in this impl
|
||||
globString = globString.replace("**", "*");
|
||||
|
||||
// Ensure only a single glob is present
|
||||
if (globString.split("").filter((c) => c == "*").length !== 1) {
|
||||
parsedGlob.globError = "Only a single glob is supported per command.\nexample: `nano my-dir/*.js`";
|
||||
return parsedGlob;
|
||||
}
|
||||
|
||||
// Split arg around glob, normalize preGlob path
|
||||
[parsedGlob.preGlob, parsedGlob.postGlob] = globString.split("*");
|
||||
parsedGlob.preGlob = removeLeadingSlash(parsedGlob.preGlob);
|
||||
|
||||
// Add CWD to preGlob path
|
||||
const cwd = removeTrailingSlash(terminal.cwd())
|
||||
parsedGlob.preGlob = `${cwd}/${parsedGlob.preGlob}`
|
||||
|
||||
// For every script on the current server, filter matched scripts per glob values & persist
|
||||
globDatabase.forEach((script) => {
|
||||
const filename = script.filename.startsWith('/') ? script.filename : `/${script.filename}`
|
||||
if (filename.startsWith(parsedGlob.preGlob) && filename.endsWith(parsedGlob.postGlob)) {
|
||||
parsedGlob.globMatches.push(filename);
|
||||
}
|
||||
});
|
||||
|
||||
// Rebuild glob for potential error reporting
|
||||
parsedGlob.glob = `${parsedGlob.preGlob}*${parsedGlob.postGlob}`
|
||||
|
||||
return parsedGlob;
|
||||
}
|
||||
|
||||
export function commonEditor(
|
||||
command: string,
|
||||
{ terminal, router, player, args }: EditorParameters,
|
||||
@ -32,8 +103,15 @@ export function commonEditor(
|
||||
return;
|
||||
}
|
||||
|
||||
let filesToLoadOrCreate = args;
|
||||
try {
|
||||
const files = args.map((arg) => {
|
||||
const globSearch = detectSimpleScriptGlob(args, player, terminal);
|
||||
if (globSearch) {
|
||||
if (isEmpty(globSearch.globError) === false) throw new Error(globSearch.globError);
|
||||
filesToLoadOrCreate = globSearch.globMatches;
|
||||
}
|
||||
|
||||
const files = filesToLoadOrCreate.map((arg) => {
|
||||
const filename = `${arg}`;
|
||||
|
||||
if (isScriptFilename(filename)) {
|
||||
@ -55,12 +133,18 @@ export function commonEditor(
|
||||
if (filename.endsWith(".txt")) {
|
||||
const filepath = terminal.getFilepath(filename);
|
||||
const txt = terminal.getTextFile(player, filename);
|
||||
return [filepath, txt == null ? "" : txt.text];
|
||||
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}`);
|
||||
throw new Error(
|
||||
`Invalid file. Only scripts (.script, .ns, .js), or text files (.txt) can be edited with ${command}`,
|
||||
);
|
||||
});
|
||||
|
||||
if (globSearch && files.length === 0) {
|
||||
throw new Error(`Could not find any valid files to open with ${command} using glob: \`${globSearch.glob}\``)
|
||||
}
|
||||
|
||||
router.toScriptEditor(Object.fromEntries(files), scriptEditorRouteOptions);
|
||||
} catch (e) {
|
||||
terminal.error(`${e}`);
|
||||
|
Loading…
Reference in New Issue
Block a user