diff --git a/src/app.ts b/src/app.ts index 0c76dcf..1078eb5 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,6 +1,6 @@ import { Alert } from './gui/Alert' import { CreateKeyboardListeners } from './input/keyboard' -import { Wush } from './shell/Wush' +import { Wush } from './shell/wush/Wush' import { Terminal } from './terminal/Terminal' import { EventBroadcaster } from './utils/EventBroadcaster' diff --git a/src/program/Loadprg.ts b/src/program/Loadprg.ts index c9f733c..4f17714 100644 --- a/src/program/Loadprg.ts +++ b/src/program/Loadprg.ts @@ -11,18 +11,11 @@ export class Loadprg extends Program { this.shell = shell } - async Exec(_: SimpleStream, stdout: SimpleStream, __: Item, args: string[]): Promise { + async Exec(_: SimpleStream, stdout: SimpleStream, workdir: Item, args: string[]): Promise { const javascript = args.slice(2).join(' ') try { - const exec: Function = eval(javascript) - - const program = class extends Program { - async Exec(stdin: SimpleStream, stdout: SimpleStream, workdir: Item, args: string[]): Promise { - exec(stdin, stdout, workdir, args) - return 0 - } - } + const program = eval(javascript) this.shell.LoadProgram(new program(), args[1]) } catch (e) { diff --git a/src/program/Lsprg.ts b/src/program/Lsprg.ts index 4d23efa..43285c0 100644 --- a/src/program/Lsprg.ts +++ b/src/program/Lsprg.ts @@ -1,5 +1,5 @@ import type { Item } from '../fs/Item' -import type { Wush } from '../shell/Wush' +import type { Wush } from '../shell/wush/Wush' import type { SimpleStream } from '../utils/SimpleStream' import { Program } from './Program' diff --git a/src/shell/Wush.ts b/src/shell/Wush.ts deleted file mode 100644 index 753d26c..0000000 --- a/src/shell/Wush.ts +++ /dev/null @@ -1,306 +0,0 @@ -// Web-Uno Shell -// the best name I could come up with lol - -import { Clear } from '../program/Clear' -import { Eval } from '../program/Eval' -import { Loadprg } from '../program/Loadprg' -import { Lsprg } from '../program/Lsprg' -import { Info } from '../program/Info' -import { Program } from '../program/Program' -import { Terminal } from '../terminal/Terminal' -import { EventBroadcaster } from '../utils/EventBroadcaster' -import { SimpleStream } from '../utils/SimpleStream' -import { Shell } from './Shell' -import { Ls } from '../program/Ls' -import { Item } from '../fs/Item' -import { Touch } from '../program/Touch' -import { Sl } from '../program/Sl' -import { Rm } from '../program/Rm' -import { Rl } from '../program/Rl' -import { ResetIndexedDb } from '../program/ResetIndexedDb' -import { Cat } from '../program/Cat' -import { Echo } from '../program/Echo' -import { Mkdir } from '../program/Mkdir' -import { Pwd } from '../program/Pwd' -import { Cd } from '../program/Cd' - -export class Wush extends Shell { - public readonly Version = "0.1.0" - public readonly Name = "wush" - - // buffer - private buffer: string[] = [] - private bufferPos: number = 0 - - private history: string[] = [] - private historyPos: number = 0 - - // exec stuff - /** - * -1 if the exec is currently running and anything else when it's not - */ - private execExitCode: number = 0 - - // todo: workers - // private workersAllowed: boolean = false - - // streams - readonly stdin: SimpleStream - readonly stdout: SimpleStream - - private programs: { [name: string]: Program } = {} - private workingDirectory: Item = null as unknown as Item // workdir is initialized in Init so this should be safe - - constructor(broadcaster: EventBroadcaster, terminal: Terminal) { - super(broadcaster, terminal) - - // create streams - this.stdin = new SimpleStream() - this.stdout = new SimpleStream() - } - - async Init() { - this.broadcaster.on('keydown', (key: string, isCharacter: boolean) => - this.HandleKeyInput(key, isCharacter), - ) - - // load workdir - this.workingDirectory = await Item.Root() - - // load core programs - this.programs['clear'] = new Clear() - this.programs['eval'] = new Eval() - this.programs['loadprg'] = new Loadprg(this) - this.programs['lsprg'] = new Lsprg(this) - this.programs['info'] = new Info() - this.programs['ls'] = new Ls() - this.programs['touch'] = new Touch() - this.programs['sl'] = new Sl() - this.programs['rm'] = new Rm() - this.programs['rl'] = new Rl() - this.programs['rsindb'] = new ResetIndexedDb() - this.programs['cat'] = new Cat() - this.programs['echo'] = new Echo() - this.programs['mkdir'] = new Mkdir() - this.programs['pwd'] = new Pwd() - this.programs['cd'] = new Cd(this) - - this.stdout.on(data => this.WriteEscapedString(data)) - this.Prompt() - } - - GetPrograms(): { [name: string]: Program } { - return this.programs - } - - LoadProgram(program: Program, name: string) { - this.programs[name] = program - } - - UnloadProgram(name: string) { - delete this.programs[name] - } - - ExecuteProgram(name: string, args: string[]) { - this.programs[name].Exec(this.stdin, this.stdout, this.workingDirectory, args) - .then(code => { - this.execExitCode = code != -1 ? code : -2 - }) - .catch((e) => { - this.WriteEscapedString(`wush: command ${name} exited with the following exception\n`) - this.WriteEscapedString(` | ${String(e)}\n`) - }) - .finally(() => { - // check if the exec actually exited with an exit code - // and if not, set it to -2 to indicate that it didn't exit with a valid code - if (this.execExitCode === -1) - this.execExitCode = -2 - - // this.terminal.Write(`The program exited with exit code ${this.execExitCode}.`) - this.Prompt() - }) - } - - Prompt() { - this.terminal.Write(`boykisser in ${this.workingDirectory.GetPath()} -> `) - } - - /** - * Changes the working directory - * @param directory the directory to enter into - * @throws an error if the directory cannot be opened - */ - async SetWorkingDirectory(directory: Item) { - if (!(await directory.Exists()) || !directory.IsDirectory()) - throw new Error(`Directory ${directory.GetPath()} doesn't exist`) - - this.workingDirectory = directory - } - - WriteStdin(data: string) { - this.stdin.emit(data) - } - - WriteEscapedString(data: string) { - let buf = data.split('') - buf.forEach((char) => { - if (!this.ProcessSimpleControlCode(char)) - this.terminal.Write(char) - }) - } - - PushToBuffer(text: string) { - text.split('').forEach(char => { - this.buffer.splice(this.bufferPos, 0, char) - this.bufferPos++ - }) - - // console.log(this.buffer) - } - - RemoveCharFromBuffer(amount: number, index: number) { - this.buffer.splice(index - amount, amount) - this.bufferPos -= amount - - // console.log(this.buffer) - } - - MoveBufferPos(index: number, absolute: boolean = false) { - this.bufferPos = Math.max(absolute ? index : this.bufferPos + index, 0) - } - - FlushBuffer() { - this.buffer = [] - this.bufferPos = 0 - } - - ExecuteBuffer() { - const args = this.buffer.join('').split(' ') - this.FlushBuffer() - - console.log(`Executing ${args[0]} with args '${args}'`) - - this.execExitCode = -1 - - if (this.programs[args[0]] instanceof Program) { - this.ExecuteProgram(args[0], args) - } else { - this.execExitCode = 0 - - // don't print anything if there are no arguments - if (args[0].length !== 0) { - this.terminal.Write(`wush: unknown command: ${args[0]}.`) - this.terminal.MoveCursor(0, 1, { x: true, y: false }) - } - - this.Prompt() - } - } - - ProcessSimpleControlCode(code: string): boolean { - switch (code) { - case '\f': - this.terminal.NewPage() - return true - case '\n': - this.terminal.MoveCursor(0, 1, { x: true, y: false }) - return true - default: - return false - } - } - - // todo: actual processing - ProcessAdvancedControlCode(complex: string): boolean { - if (!complex.match(/\\(.*;)/gm)) - return false - - return true - // const code = matches[] - } - - HandleKeyInput(key: string, isCharacter: boolean) { - if (this.execExitCode === -1) console.log('program running') - if (!isCharacter) { - switch (key) { - case 'ArrowLeft': - if (this.bufferPos > 0) { - this.terminal.MoveCursor(-1, 0) - this.MoveBufferPos(-1) - } - - break - case 'ArrowRight': - if (this.bufferPos < this.buffer.length) { - this.terminal.MoveCursor(1, 0) - this.MoveBufferPos(1) - } - break - case 'ArrowUp': - if (this.historyPos < this.history.length) { - if (this.historyPos === 0) - this.history.splice(0, 0, this.buffer.join('')) - - this.historyPos++ - console.log(this.historyPos) - console.log(this.history[this.historyPos]) - } - break - case 'ArrowDown': - if (this.historyPos > 0) { - this.historyPos-- - - if (this.historyPos === 0) - this.history.splice(0, 1) - - console.log(this.historyPos) - console.log(this.history[this.historyPos]) - } - break - case 'Backspace': - // don't erase anything if there's nothing left in the buffer - if (this.bufferPos === 0) - break - - this.terminal.RemoveCell() - this.RemoveCharFromBuffer(1, this.bufferPos) - // this.buffer.splice(this.bufferPos, 1) - this.terminal.MoveCursor(-1, 0) - break - case 'Delete': - if (this.bufferPos >= this.buffer.length || this.buffer.length <= 0) - break - - this.bufferPos++ - this.terminal.MoveCursor(1, 0) - this.terminal.RemoveCell() - this.RemoveCharFromBuffer(1, this.bufferPos) - this.terminal.MoveCursor(-1, 0) - break - case 'Enter': - // send the buffer to stdin if an exec is running - if (this.execExitCode === -1) { - this.WriteStdin(`${this.buffer.join('')}\n`) - this.FlushBuffer() - } else { - // "execute" the buffer - this.terminal.MoveCursor(0, 1, { x: true, y: false }) - this.history.splice(0, 0, this.buffer.join('')) - this.ExecuteBuffer() - } - - break - case 'F5': - location.reload() - break - } - } else { - this.historyPos = 0 - - // push the character into the buffer - this.terminal.InsertCell(key) - this.PushToBuffer(key) - this.terminal.MoveCursor(1, 0) - } - } -}