MISC: Various small fixes (#574)

* ns.ls filter can include leading slash in filename
* scp from terminal accepts multiple filenames
* terminal displays root / instead of ~ as base
* cd with no args returns to root
This commit is contained in:
Snarling 2023-06-06 08:46:07 -04:00 committed by GitHub
parent ed93fea141
commit 40b89baca1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 77 additions and 50 deletions

@ -902,7 +902,7 @@ export const ns: InternalAPI<NSFull> = {
];
if (!substring) return allFilenames.sort();
return allFilenames.filter((filename) => filename.includes(substring)).sort();
return allFilenames.filter((filename) => ("/" + filename).includes(substring)).sort();
},
getRecentScripts: () => (): RecentScript[] => {
return recentScripts.map((rs) => ({

@ -4,8 +4,8 @@ import { directoryExistsOnServer, resolveDirectory } from "../../Paths/Directory
export function cd(args: (string | number | boolean)[], server: BaseServer): void {
if (args.length > 1) return Terminal.error("Incorrect number of arguments. Usage: cd [dir]");
// If no arg was provided, just use "".
const userInput = String(args[0] ?? "");
// If no arg was provided, just use "/".
const userInput = String(args[0] ?? "/");
const targetDir = resolveDirectory(userInput, Terminal.currDir);
// Explicitly checking null due to root being ""
if (targetDir === null) return Terminal.error(`Could not resolve directory ${userInput}`);

@ -5,36 +5,62 @@ import { hasScriptExtension } from "../../Paths/ScriptFilePath";
import { hasTextExtension } from "../../Paths/TextFilePath";
import { checkEnum } from "../../utils/helpers/enum";
import { LiteratureName } from "../../Literature/data/LiteratureNames";
import { ContentFile } from "../../Paths/ContentFile";
export function scp(args: (string | number | boolean)[], server: BaseServer): void {
if (args.length !== 2) {
if (args.length < 2) {
return Terminal.error("Incorrect usage of scp command. Usage: scp [source filename] [destination hostname]");
}
const [scriptname, destHostname] = args.map((arg) => arg + "");
const path = Terminal.getFilepath(scriptname);
if (!path) return Terminal.error(`Invalid file path: ${scriptname}`);
// Validate destination server
const destHostname = String(args.pop());
const destServer = GetServer(destHostname);
if (!destServer) return Terminal.error(`Invalid destination server: ${args[1]}`);
if (!destServer) return Terminal.error(`Invalid destination server: ${destHostname}`);
// Lit files
if (path.endsWith(".lit")) {
if (!checkEnum(LiteratureName, path) || !server.messages.includes(path)) {
return Terminal.error(`No file at path ${path}`);
// Validate filepaths
const filenames = args.map(String);
const files: (LiteratureName | ContentFile)[] = [];
// File validation loop, handle all errors before copying any files
for (const filename of filenames) {
const path = Terminal.getFilepath(filename);
if (!path) return Terminal.error(`Invalid file path: ${filename}`);
// Validate .lit files
if (path.endsWith(".lit")) {
if (!checkEnum(LiteratureName, path) || !server.messages.includes(path)) {
return Terminal.error(`scp failed: ${path} does not exist on server ${server.hostname}`);
}
files.push(path);
continue;
}
if (destServer.messages.includes(path)) return Terminal.print(`${path} was already on ${destHostname}`);
destServer.messages.push(path);
return Terminal.print(`Copied ${path} to ${destHostname}`);
// Error for invalid filetype
if (!hasScriptExtension(path) && !hasTextExtension(path)) {
return Terminal.error(
`scp failed: ${path} has invalid extension. scp only works for scripts (.js or .script), text files (.txt), and literature files (.lit)`,
);
}
const sourceContentFile = server.getContentFile(path);
if (!sourceContentFile) return Terminal.error(`scp failed: ${path} does not exist on server ${server.hostname}`);
files.push(sourceContentFile);
}
if (!hasScriptExtension(path) && !hasTextExtension(path)) {
return Terminal.error("scp only works for scripts, text files (.txt), and literature files (.lit)");
// Actually copy the files (no more errors possible)
for (const file of files) {
// Lit files, entire "file" is just the name
if (checkEnum(LiteratureName, file)) {
if (destServer.messages.includes(file)) {
Terminal.print(`${file} was already on ${destHostname}, file skipped`);
continue;
}
destServer.messages.push(file);
Terminal.print(`${file} copied to ${destHostname}`);
continue;
}
// Content files (script and txt)
const { filename, content } = file;
const { overwritten } = destServer.writeToContentFile(filename, content);
if (overwritten) Terminal.warn(`${filename} already existed on ${destHostname} and was overwritten`);
else Terminal.print(`${filename} copied to ${destHostname}`);
}
// Text or script
const source = server.getContentFile(path);
if (!source) return Terminal.error(`No file at path ${path}`);
const { overwritten } = destServer.writeToContentFile(path, source.content);
if (overwritten) Terminal.warn(`${path} already exists on ${destHostname} and will be overwritten`);
Terminal.print(`${path} copied to ${destHostname}`);
}

@ -163,7 +163,7 @@ export async function getTabCompletionPossibilities(terminalText: string, baseDi
// Just some booleans so the mismatch between command length and arg number are not as confusing.
const onCommand = commandLength === 1;
const onFirstCommandArg = commandLength === 2;
const onSecondCommandArg = commandLength === 3;
// const onSecondCommandArg = commandLength === 3; // unused
// These are always added.
addGlobalAliases();
@ -239,11 +239,12 @@ export async function getTabCompletionPossibilities(terminalText: string, baseDi
return possibilities;
case "scp":
if (onFirstCommandArg) {
addScripts();
addTextFiles();
addLiterature();
} else if (onSecondCommandArg) addServerNames();
if (!onFirstCommandArg) {
addServerNames();
}
addScripts();
addTextFiles();
addLiterature();
return possibilities;
case "rm":

@ -197,7 +197,7 @@ export function TerminalInput(): React.ReactElement {
// Run command.
if (event.key === KEY.ENTER && value !== "") {
event.preventDefault();
Terminal.print(`[${Player.getCurrentServer().hostname} ~${Terminal.cwd()}]> ${value}`);
Terminal.print(`[${Player.getCurrentServer().hostname} /${Terminal.cwd()}]> ${value}`);
Terminal.executeCommands(value);
saveValue("");
return;
@ -282,7 +282,7 @@ export function TerminalInput(): React.ReactElement {
if (Settings.EnableBashHotkeys) {
if (event.code === KEYCODE.C && event.ctrlKey && ref && ref.selectionStart === ref.selectionEnd) {
event.preventDefault();
Terminal.print(`[${Player.getCurrentServer().hostname} ~${Terminal.cwd()}]> ${value}`);
Terminal.print(`[${Player.getCurrentServer().hostname} /${Terminal.cwd()}]> ${value}`);
modifyInput("clearall");
}
@ -361,7 +361,7 @@ export function TerminalInput(): React.ReactElement {
className: classes.input,
startAdornment: (
<Typography color={Terminal.action === null ? "primary" : "secondary"} flexShrink={0}>
[{Player.getCurrentServer().hostname}&nbsp;~{Terminal.cwd()}]&gt;&nbsp;
[{Player.getCurrentServer().hostname}&nbsp;/{Terminal.cwd()}]&gt;&nbsp;
</Typography>
),
spellCheck: false,

@ -13,6 +13,7 @@ import { hasScriptExtension } from "../../../src/Paths/ScriptFilePath";
import { LiteratureName } from "../../../src/Literature/data/LiteratureNames";
import { MessageFilename } from "../../../src/Message/MessageHelpers";
import { Terminal } from "../../../src/Terminal";
import { IPAddress } from "../../../src/Types/strings";
describe("getTabCompletionPossibilities", function () {
let closeServer: Server;
@ -23,7 +24,7 @@ describe("getTabCompletionPossibilities", function () {
Player.init();
closeServer = new Server({
ip: "8.8.8.8",
ip: "8.8.8.8" as IPAddress,
hostname: "near",
hackDifficulty: 1,
moneyAvailable: 70000,
@ -33,7 +34,7 @@ describe("getTabCompletionPossibilities", function () {
serverGrowth: 3000,
});
farServer = new Server({
ip: "4.4.4.4",
ip: "4.4.4.4" as IPAddress,
hostname: "far",
hackDifficulty: 1,
moneyAvailable: 70000,
@ -87,23 +88,22 @@ describe("getTabCompletionPossibilities", function () {
it("completes the scp command", async () => {
writeFiles();
let options = await getTabCompletionPossibilities("scp ", root);
expect(options.sort()).toEqual(
[
"note.txt",
"folder1/text.txt",
"folder1/text2.txt",
"hack.js",
"weaken.js",
"grow.js",
"old.script",
"folder1/test.js",
"anotherFolder/win.js",
LiteratureName.AGreenTomorrow,
].sort(),
);
const filesToMatch = [
"note.txt",
"folder1/text.txt",
"folder1/text2.txt",
"hack.js",
"weaken.js",
"grow.js",
"old.script",
"folder1/test.js",
"anotherFolder/win.js",
LiteratureName.AGreenTomorrow,
];
expect(options.sort()).toEqual(filesToMatch.sort());
// Test the second command argument (server name)
options = await getTabCompletionPossibilities("scp note.txt ", root);
expect(options).toEqual(["home", "near", "far"]);
expect(options.sort()).toEqual(["home", "near", "far", ...filesToMatch].sort());
});
it("completes the kill, tail, mem, and check commands", async () => {