added terminal app with example hello world

This commit is contained in:
Djeeberjr 2022-12-15 14:21:26 +01:00
parent 1863462b44
commit 2b8aa06117
4 changed files with 203 additions and 0 deletions

17
src/bin/HelloWorld.hx Normal file
View File

@ -0,0 +1,17 @@
package bin;
import lib.TermHandle;
import kernel.fs.FileHandler.WriteHandle;
import lib.CLIBase;
class HelloWorld extends CLIBase {
public function invoke(handle: TermHandle):Bool {
var world:String = "world";
if (handle.args.length > 0) {
world = handle.args[0];
}
handle.write('Hello, $world!');
return true;
}
}

151
src/bin/Terminal.hx Normal file
View File

@ -0,0 +1,151 @@
package bin;
import lib.TermHandle;
import lib.CLIBase;
import util.Color;
import kernel.ui.WindowContext;
import kernel.ui.WindowManager;
class Terminal {
private var context:WindowContext;
private var input:String = "";
private var backlog: Array<String> = [];
public function new() {}
public function execute() {
this.context = WindowManager.instance.createNewContext();
this.context.onChar.handle(char -> {
this.input += char;
this.redrawInput();
});
this.context.onKey.handle(e -> {
if (e.keyCode == 259) {
this.input = this.input.substr(0, this.input.length - 1);
this.redrawInput();
} else if (e.keyCode == 257) {
this.backlog.push("> " + this.input);
var command = this.input;
this.input = "";
this.redrawBacklog();
this.redrawInput();
this.invokeCommand(command);
}
});
WindowManager.instance.focusContextToOutput(context, "main");
this.redrawInput();
}
private function redrawBacklog() {
var size = this.context.getSize();
var linesAvailable = size.y - 1;
var start:Int = this.backlog.length - linesAvailable;
for (i in 0...linesAvailable){
var line = this.backlog[start + i];
this.context.setCursorPos(0, i);
this.context.clearLine();
if (line != null) {
this.context.write(line);
}
}
this.moveCursorToInput();
}
private function redrawInput() {
var size = this.context.getSize();
this.context.setCursorPos(0, size.y - 1);
this.context.clearLine();
this.context.setTextColor(Color.Blue);
this.context.write("> ");
this.context.setTextColor(Color.White);
this.context.write(this.input);
this.context.setCursorBlink(true);
}
private function invokeCommand(command: String):Void{
var args = this.parseArgs(command);
if (args.length == 0) {
return;
}
var commandName = args[0];
// Handle built-in commands
switch (commandName) {
case "clear":
this.clear();
return;
}
var commandArgs:Array<String> = args.slice(1);
var hadInput = false;
var handle = new TermHandle(commandArgs,{
onWrite: (s:String) -> {
if (!hadInput) {
this.backlog.push("");
hadInput = true;
}
this.backlog[this.backlog.length - 1] += s;
this.redrawBacklog();
},
onNewLine: () -> {
this.backlog.push("");
this.redrawBacklog();
}
});
var prog:CLIBase = getProgByName(commandName);
if (prog == null) {
this.backlog.push("Command not found: " + commandName);
this.redrawBacklog();
return;
}
this.context.setCursorBlink(false);
prog.invoke(handle);
// Cleanup extra newline
if (this.backlog[this.backlog.length - 1] == "") {
this.backlog.pop();
}
this.redrawInput();
}
private function parseArgs(command: String): Array<String> {
// TODO: tim and quote handling
return command.split(" ");
}
private function clear() {
this.backlog = [];
this.redrawBacklog();
}
private function getProgByName(name: String): CLIBase {
switch (name) {
case "hello":
return new HelloWorld();
default:
return null;
}
}
private function moveCursorToInput() {
var size = this.context.getSize();
this.context.setCursorPos(this.input.length + 2, size.y - 1);
}
}

6
src/lib/CLIBase.hx Normal file
View File

@ -0,0 +1,6 @@
package lib;
abstract class CLIBase {
public function new() {};
public abstract function invoke(handle: TermHandle): Bool;
}

29
src/lib/TermHandle.hx Normal file
View File

@ -0,0 +1,29 @@
package lib;
import haxe.ds.ReadOnlyArray;
typedef TermHandleEvents = {
onWrite: String->Void,
onNewLine: Void->Void,
}
class TermHandle {
public final args:ReadOnlyArray<String>;
private final events:TermHandleEvents;
@:allow(bin.Terminal)
private function new(args: Array<String>, events: TermHandleEvents) {
this.args = args;
this.events = events;
}
public function write(s:String) {
this.events.onWrite(s);
}
public function writeLn(s:String = "") {
if (s != "")
this.events.onWrite(s);
this.events.onNewLine();
}
}