449 lines
14 KiB
TypeScript
449 lines
14 KiB
TypeScript
namespace JSXCommands {
|
||
|
||
/**
|
||
* Echoes the given arguments
|
||
*/
|
||
export class Echo implements JSTerminal.Program {
|
||
create(terminal: JSTerminal.Terminal, stdio: Std.IO, args?: string[]) {
|
||
if (args.filter((a) => { return a != ""; }).length == 0) {
|
||
stdio.println("Nothing echoes... nothing");
|
||
terminal.closeProgram(this);
|
||
return;
|
||
}
|
||
stdio.println(args.join(" "));
|
||
terminal.closeProgram(this);
|
||
}
|
||
enable() { }
|
||
disable() { }
|
||
onClose() { return true; }
|
||
|
||
getManual() {
|
||
return "Echoes the given arguments" +
|
||
"\n\nUsage: echo [anything to echo]";
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Lists all files in the given directory or this directory, if path not specified.
|
||
*/
|
||
export class List implements JSTerminal.Program {
|
||
jsx: JavaScriptX.JSX;
|
||
|
||
constructor(jsx: JavaScriptX.JSX) {
|
||
this.jsx = jsx;
|
||
}
|
||
|
||
create(terminal: JSTerminal.Terminal, stdio: Std.IO, args?: string[]) {
|
||
let folder = this.jsx.currentFolder;
|
||
if (args.length > 0) {
|
||
let pathStr = args.slice(-1)[0];
|
||
let path = this.jsx.findPath(pathStr);
|
||
if (path instanceof JavaScriptX.Folder) {
|
||
args.splice(-1);
|
||
folder = path;
|
||
}
|
||
}
|
||
let argObj = parseArgsToObject(args);
|
||
|
||
let a = "a" in argObj;
|
||
|
||
if (a) {
|
||
stdio.println(".", "blue");
|
||
if (folder.parentFolder != null) {
|
||
stdio.println("..", "blue");
|
||
}
|
||
}
|
||
let files = folder.files.filter(f => (!f.hidden || a));
|
||
for (let i = 0; i < files.length; i++) {
|
||
let f = files[i];
|
||
stdio.println(JavaScriptX.formatToLength(f.name, 20),
|
||
((f.hidden) ? "blue" : (f instanceof JavaScriptX.Folder) ? "red" : "yellow")
|
||
);
|
||
stdio.print(" " +
|
||
((f instanceof JavaScriptX.Folder) ? "DIR" : ((f instanceof JavaScriptX.BinaryFile) ? "BIN" : "TXT")));
|
||
stdio.print(" " + new Date(f.created).toLocaleString());
|
||
}
|
||
|
||
terminal.closeProgram(this);
|
||
}
|
||
enable() { }
|
||
disable() { }
|
||
onClose() { return true; }
|
||
|
||
getManual() {
|
||
return "Lists all files in the given directory\nor this directory, if path not specified." +
|
||
"\n\nUsage: ls [options] [path]" +
|
||
"\n\nOptions:\n " + JavaScriptX.formatToLength("-a", 10) + "List all files (incl. hidden)";
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Changes directory to the specified directory.
|
||
*/
|
||
export class ChangeDirectory implements JSTerminal.Program {
|
||
jsx: JavaScriptX.JSX;
|
||
|
||
constructor(jsx: JavaScriptX.JSX) {
|
||
this.jsx = jsx;
|
||
}
|
||
|
||
create(terminal: JSTerminal.Terminal, stdio: Std.IO, args?: string[]) {
|
||
if (args.length == 0) {
|
||
stdio.println("Insufficent parameters, usage:");
|
||
stdio.println("cd [path]");
|
||
terminal.closeProgram(this);
|
||
return;
|
||
}
|
||
let path = this.jsx.findPath(args[0]);
|
||
let folder = false;
|
||
if (path) {
|
||
if (path instanceof JavaScriptX.Folder) {
|
||
folder = true;
|
||
} else {
|
||
stdio.println(`'${args[0]}' is not a folder.`);
|
||
}
|
||
} else {
|
||
stdio.println(`'${args[0]}' not found.`);
|
||
}
|
||
if (folder && path instanceof JavaScriptX.Folder) { // second part only to satisfy TypeScript compiler
|
||
this.jsx.currentFolder = path;
|
||
}
|
||
|
||
terminal.closeProgram(this);
|
||
}
|
||
enable() { }
|
||
disable() { }
|
||
onClose() { return true; }
|
||
|
||
getManual() {
|
||
return "Changes directory to the specified directory." +
|
||
"\n\nUsage: cd [path]";
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Creates a directory at specified location.
|
||
*/
|
||
export class MakeDirectory implements JSTerminal.Program, JavaScriptX.Manual {
|
||
jsx: JavaScriptX.JSX;
|
||
|
||
constructor(jsx: JavaScriptX.JSX) {
|
||
this.jsx = jsx;
|
||
}
|
||
|
||
create(terminal: JSTerminal.Terminal, stdio: Std.IO, args?: string[]) {
|
||
if (args.length == 0) {
|
||
stdio.println("Insufficent parameters, usage:");
|
||
stdio.println("mkdir [path]");
|
||
terminal.closeProgram(this);
|
||
return;
|
||
}
|
||
let pathArg = args.splice(-1)[0];
|
||
let argObj = parseArgsToObject(args);
|
||
|
||
let h = "h" in argObj;
|
||
|
||
let origPathpieces = pathArg.split("/");
|
||
let curr = [origPathpieces[0]];
|
||
let i = 0;
|
||
let createFolder = false;
|
||
do {
|
||
let path = this.jsx.findPath(curr.join("/"));
|
||
if (!path) {
|
||
if (origPathpieces.length == curr.length) {
|
||
createFolder = true;
|
||
} else {
|
||
stdio.println(`${curr.join("/")} not found!`);
|
||
}
|
||
break;
|
||
} else if (path && origPathpieces.length == curr.length) {
|
||
stdio.println(`${curr.join("/")} already exists!`);
|
||
} else if (!(path instanceof JavaScriptX.Folder)) {
|
||
stdio.println(`${curr.join("/")} is not a folder!`);
|
||
}
|
||
|
||
if (i < origPathpieces.length) {
|
||
i += 1;
|
||
curr.push(origPathpieces[i]);
|
||
}
|
||
} while (curr.length < origPathpieces.length + 1)
|
||
|
||
if (createFolder) {
|
||
let path = origPathpieces.slice(0, -1).join("/");
|
||
let name = origPathpieces.slice(-1)[0];
|
||
let relative = this.jsx.findPath(path);
|
||
if (relative instanceof JavaScriptX.Folder) {
|
||
relative.addFile(new JavaScriptX.Folder(name, h));
|
||
} else {
|
||
stdio.println("An error occoured creating the folder.");
|
||
}
|
||
}
|
||
|
||
terminal.closeProgram(this);
|
||
}
|
||
enable() { }
|
||
disable() { }
|
||
onClose() { return true; }
|
||
|
||
getManual() {
|
||
return "Creates a directory at specified location." +
|
||
"\n\nUsage: mkdir [options] [path]" +
|
||
"\n\nOptions:\n " + JavaScriptX.formatToLength("-h", 10) + "Makes the folder hidden.";
|
||
}
|
||
}
|
||
|
||
/**
|
||
* The Woman in the JSX equivalent to the UNIX Man.
|
||
* It's called Woman (not Man) because of the funny wording,
|
||
* where 'man' sounds like the gender 'man'
|
||
*/
|
||
export class Woman implements JSTerminal.Program, JavaScriptX.Manual {
|
||
|
||
jsx: JavaScriptX.JSX;
|
||
|
||
constructor(jsx: JavaScriptX.JSX) {
|
||
this.jsx = jsx;
|
||
}
|
||
|
||
create(terminal: JSTerminal.Terminal, stdio: Std.IO, args?: string[]) {
|
||
if (args.length == 0) {
|
||
this.readManual(stdio, this);
|
||
terminal.closeProgram(this);
|
||
return;
|
||
}
|
||
let filePath = args.slice(-1)[0];
|
||
for (let env in this.jsx.environmentVariables) {
|
||
if (filePath == env) {
|
||
filePath = env;
|
||
break;
|
||
}
|
||
let envFolder = this.jsx.findPath(this.jsx.environmentVariables[env]);
|
||
if (envFolder instanceof JavaScriptX.Folder) {
|
||
for (let i = 0; i < envFolder.files.length; i++) {
|
||
if (envFolder.files[i].name == filePath) {
|
||
filePath = envFolder.files[i].getPath();
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
let f = this.jsx.findPath(filePath);
|
||
if (f && f instanceof JavaScriptX.BinaryFile && manualGuard(f.program)) {
|
||
this.readManual(stdio, f.program);
|
||
} else {
|
||
stdio.println("Nothing to womansplain.");
|
||
}
|
||
|
||
terminal.closeProgram(this);
|
||
}
|
||
enable() { }
|
||
disable() { }
|
||
onClose() { return true; }
|
||
|
||
getManual() {
|
||
return "The Woman in the JSX equivalent to the UNIX Man. " +
|
||
"It's called Woman (not Man) because of the funny wording, " +
|
||
"where 'man' sounds like the gender 'man'" +
|
||
"\n\nUsage: woman [path]";
|
||
}
|
||
|
||
readManual(stdio: Std.IO, manual: JavaScriptX.Manual) {
|
||
stdio.println(manual.getManual());
|
||
}
|
||
}
|
||
|
||
export class Caternate implements JSTerminal.Program, JavaScriptX.Manual {
|
||
|
||
jsx: JavaScriptX.JSX;
|
||
|
||
constructor(jsx: JavaScriptX.JSX) {
|
||
this.jsx = jsx;
|
||
}
|
||
|
||
create(terminal: JSTerminal.Terminal, stdio: Std.IO, args?: string[]) {
|
||
if (args.length == 0) {
|
||
stdio.println("Insufficent arguments.");
|
||
stdio.println("\nUsage: cat [path]");
|
||
terminal.closeProgram(this);
|
||
return;
|
||
}
|
||
let path = args.splice(-1)[0];
|
||
let file = this.jsx.findPath(path);
|
||
if (file instanceof JavaScriptX.BinaryFile) {
|
||
stdio.println("Unable to read binary files (yet).");
|
||
} else if (file instanceof JavaScriptX.Folder) {
|
||
stdio.println(file.name, "blue");
|
||
stdio.print(" is a DIRectory containing ")
|
||
stdio.print(file.files.length + "", "red");
|
||
stdio.print(" files.");
|
||
} else if (file instanceof JavaScriptX.TextFile) {
|
||
let contents = file.readContent();
|
||
stdio.println(contents);
|
||
}
|
||
terminal.closeProgram(this);
|
||
}
|
||
enable() { }
|
||
disable() { }
|
||
onClose() { return true; }
|
||
|
||
getManual() {
|
||
return "[cat]ernate reads data from given file and outputs it." +
|
||
"\n\nUsage: cat [path]";
|
||
}
|
||
|
||
readManual(stdio: Std.IO, manual: JavaScriptX.Manual) {
|
||
stdio.println(manual.getManual());
|
||
}
|
||
}
|
||
|
||
export class Touch implements JSTerminal.Program, JavaScriptX.Manual {
|
||
jsx: JavaScriptX.JSX;
|
||
|
||
constructor(jsx: JavaScriptX.JSX) {
|
||
this.jsx = jsx;
|
||
}
|
||
|
||
create(terminal: JSTerminal.Terminal, stdio: Std.IO, args?: string[]) {
|
||
if (args.length == 0) {
|
||
stdio.println("Insufficent arguments.");
|
||
stdio.println("\nUsage: touch [path]");
|
||
terminal.closeProgram(this);
|
||
return;
|
||
}
|
||
let path = args.splice(-1)[0];
|
||
let file = this.jsx.findPath(path);
|
||
if (file) {
|
||
file.created = new Date().getTime();
|
||
} else {
|
||
let origPathparts = path.split("/");
|
||
let curr = [origPathparts[0]];
|
||
for (let i = 1; i < origPathparts.length; i++) {
|
||
let currFile = this.jsx.findPath(curr.join("/"));
|
||
if (!currFile) {
|
||
stdio.println(`${curr.join("/")} not found.`);
|
||
terminal.closeProgram(this);
|
||
return;
|
||
}
|
||
curr.push(origPathparts[i]);
|
||
}
|
||
let filename = curr.splice(-1);
|
||
let folder = this.jsx.findPath(curr.join("/"));
|
||
if (folder instanceof JavaScriptX.Folder) {
|
||
folder.addFile(new JavaScriptX.TextFile(filename[0]));
|
||
} else {
|
||
stdio.println(`${curr.join("/")} is not a folder.`);
|
||
}
|
||
}
|
||
terminal.closeProgram(this);
|
||
}
|
||
enable() { }
|
||
disable() { }
|
||
onClose() { return true; }
|
||
|
||
getManual() {
|
||
return "Touch touches the file specified updating it's timestamp. If the file doesn't exist, it's created." +
|
||
"\n\nUsage: touch [path]";
|
||
}
|
||
|
||
readManual(stdio: Std.IO, manual: JavaScriptX.Manual) {
|
||
stdio.println(manual.getManual());
|
||
}
|
||
}
|
||
export class JEdi implements JSTerminal.Program, JavaScriptX.Manual {
|
||
jsx: JavaScriptX.JSX;
|
||
stdio: Std.IO;
|
||
terminal: JSTerminal.Terminal;
|
||
text: string[] = [""];
|
||
row = 0
|
||
|
||
constructor(jsx: JavaScriptX.JSX) {
|
||
this.jsx = jsx;
|
||
}
|
||
|
||
create(terminal: JSTerminal.Terminal, stdio: Std.IO, args?: string[]) {
|
||
this.stdio = stdio;
|
||
this.terminal = terminal;
|
||
|
||
this.doCommand();
|
||
}
|
||
enable() { }
|
||
disable() { }
|
||
onClose() { return true; }
|
||
|
||
getManual() {
|
||
return "(J)avascript (Edi)tor is a custom version of the UNIX editor 'ed'." +
|
||
"\nIt's quite simpler in the way that all of it's commands are whole words" +
|
||
"\n\nUsage: jedi [(optional) path]";
|
||
}
|
||
|
||
readManual(stdio: Std.IO, manual: JavaScriptX.Manual) {
|
||
stdio.println(manual.getManual());
|
||
}
|
||
|
||
doCommand() {
|
||
let readline = this.stdio.readline({
|
||
callback: c => this.exec(c),
|
||
prefix: "\n> ",
|
||
printAfterDone: true
|
||
})
|
||
}
|
||
|
||
exec(command: string) {
|
||
if (command == "exit" || command == "quit") {
|
||
this.terminal.closeProgram(this);
|
||
return;
|
||
} else if (command.split(" ")[0] == "append") {
|
||
let appended = command.split(" ").slice(1).join(" ");
|
||
debugger;
|
||
console.log(appended)
|
||
let rows = appended.split("\n");
|
||
console.log(rows)
|
||
for (let i = 0; i < rows.length; i++) {
|
||
if (this.text.length <= (this.row + i)) {
|
||
this.text.push("");
|
||
}
|
||
this.text[this.row + i] += rows[i];
|
||
}
|
||
this.row += rows.length - 1;
|
||
this.stdio.println(`${this.text[this.row]}`);
|
||
|
||
}
|
||
|
||
this.doCommand();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Parses all given args from '--arg val' into format {arg: val}
|
||
*/
|
||
function parseArgsToObject(args: string[], defaults: { [args: string]: string } = {}) {
|
||
let returned: { [arg: string]: string } = {};
|
||
for (let key in defaults) {
|
||
returned[key] = defaults[key];
|
||
}
|
||
let currArg: string = null;
|
||
args.forEach(arg => {
|
||
if (arg.startsWith("-")) {
|
||
if (arg.startsWith("--")) {
|
||
if (currArg) { returned[currArg] = null };
|
||
currArg = arg.substr(2);
|
||
return;
|
||
}
|
||
if (currArg) { returned[currArg] = null };
|
||
currArg = arg.substr(1);
|
||
} else {
|
||
if (currArg) {
|
||
returned[currArg] = arg;
|
||
currArg = null;
|
||
}
|
||
}
|
||
});
|
||
if (currArg) { returned[currArg] = null };
|
||
return returned;
|
||
}
|
||
|
||
function manualGuard(arg: any): arg is JavaScriptX.Manual {
|
||
return arg.getManual !== undefined
|
||
}
|
||
}
|