chore: offload user input to InputManager

This commit is contained in:
binekrasik
2026-05-20 22:46:59 +02:00
parent abe52acaa7
commit 0649843821
7 changed files with 285 additions and 212 deletions

View File

@@ -25,20 +25,21 @@ import { Mkdir } from '../../program/Mkdir'
import { Cd } from '../../program/Cd'
import { Printf } from '../../program/Printf'
import { Pwd } from '../../program/Pwd'
import { ShellEnvironment } from './ShellEnvironment'
import { Environment } from './Environment'
import { InputManager } from './InputManager'
export class Wush extends Shell {
public readonly Version = "0.3.1"
public readonly Name = "wush"
// buffer
private buffer: string[] = []
private bufferPos: number = 0
private promptStart: CursorPosition = { col: 0, row: 0 }
_buffer: string[] = []
_bufferPos: number = 0
_promptStart: CursorPosition = { col: 0, row: 0 }
private history: string[] = []
private historyIndex: number | null = null
private historyDraft: string = ''
private inputManager: InputManager
// @ts-ignore unused for now
private environment: Environment
// exec stuff
/**
@@ -46,8 +47,6 @@ export class Wush extends Shell {
*/
private execExitCode: number = 0
readonly environment: ShellEnvironment = new ShellEnvironment()
// streams
readonly stdin: SimpleStream<string>
readonly stdout: SimpleStream<string>
@@ -61,13 +60,14 @@ export class Wush extends Shell {
// create streams
this.stdin = new SimpleStream<string>()
this.stdout = new SimpleStream<string>()
this.inputManager = new InputManager(this, broadcaster)
this.environment = new Environment()
}
async Init() {
// initialize keyboard listener
this.broadcaster.on('keydown', (key: string, isCharacter: boolean) =>
this.HandleKeyInput(key, isCharacter),
)
this.inputManager.RegisterEvents()
// load workdir
this.workingDirectory = await Item.Root()
@@ -98,6 +98,10 @@ export class Wush extends Shell {
this.Prompt()
}
HasRunningProgram(): boolean {
return this.execExitCode === -1
}
GetPrograms(): { [name: string]: Program } {
return this.programs
}
@@ -110,6 +114,10 @@ export class Wush extends Shell {
delete this.programs[name]
}
GetExecExitCode(): number {
return this.execExitCode
}
async ExecuteProgram(name: string, args: string[]): Promise<void> {
return new Promise<void>(resolve => {
this.programs[name].Exec(this.stdin, this.stdout, this.workingDirectory, args)
@@ -136,11 +144,12 @@ export class Wush extends Shell {
Prompt() {
this.terminal.Write(`user in ${this.workingDirectory.GetPath()} -> `)
this.promptStart = this.terminal.GetCursorPosition()
this.buffer = []
this.bufferPos = 0
this.historyIndex = null
this.historyDraft = ''
this._promptStart = this.terminal.GetCursorPosition()
this._buffer = []
this._bufferPos = 0
this.inputManager.historyIndex = null
this.inputManager.historyDraft = ''
}
/**
@@ -206,23 +215,23 @@ export class Wush extends Shell {
PushToBuffer(text: string) {
text.split('').forEach(char => {
this.buffer.splice(this.bufferPos, 0, char)
this.bufferPos++
this._buffer.splice(this._bufferPos, 0, char)
this._bufferPos++
})
}
RemoveCharFromBuffer(amount: number, index: number) {
this.buffer.splice(index - amount, amount)
this.bufferPos -= amount
this._buffer.splice(index - amount, amount)
this._bufferPos -= amount
}
MoveBufferPos(index: number, absolute: boolean = false) {
this.bufferPos = Math.max(absolute ? index : this.bufferPos + index, 0)
this._bufferPos = Math.max(absolute ? index : this._bufferPos + index, 0)
}
FlushBuffer() {
this.buffer = []
this.bufferPos = 0
this._buffer = []
this._bufferPos = 0
}
// takes the prompt buffer, parses it and executes the contents
@@ -453,12 +462,12 @@ export class Wush extends Shell {
let line: string
if (data) {
// data overrides the buffer
this.buffer = data.split('')
this.bufferPos = 0
this._buffer = data.split('')
this._bufferPos = 0
line = data
} else {
// use shell buffer if no arbitrary string was provided
line = this.buffer.join('')
line = this._buffer.join('')
this.FlushBuffer()
}
@@ -585,83 +594,47 @@ export class Wush extends Shell {
private GetCursorPositionForBufferPos(pos: number): CursorPosition {
const width = this.GetWrapWidth()
const absoluteIndex = this.promptStart.col + Math.max(pos, 0)
const absoluteIndex = this._promptStart.col + Math.max(pos, 0)
const rowOffset = Math.floor(absoluteIndex / width)
const col = absoluteIndex % width
return { row: this.promptStart.row + rowOffset, col }
return { row: this._promptStart.row + rowOffset, col }
}
private SyncCursorToBuffer() {
const position = this.GetCursorPositionForBufferPos(this.bufferPos)
_SyncCursorToBuffer() {
const position = this.GetCursorPositionForBufferPos(this._bufferPos)
this.terminal.SetCursorPosition(position.col, position.row)
}
private ClearBuffer() {
if (this.buffer.length === 0)
if (this._buffer.length === 0)
return
if (this.bufferPos < this.buffer.length) {
this.bufferPos = this.buffer.length
this.SyncCursorToBuffer()
if (this._bufferPos < this._buffer.length) {
this._bufferPos = this._buffer.length
this._SyncCursorToBuffer()
}
while (this.bufferPos > 0) {
while (this._bufferPos > 0) {
this.terminal.RemoveCell()
this.RemoveCharFromBuffer(1, this.bufferPos)
this.SyncCursorToBuffer()
this.RemoveCharFromBuffer(1, this._bufferPos)
this._SyncCursorToBuffer()
}
}
private InsertText(text: string) {
_InsertText(text: string) {
if (text.length === 0)
return
for (const char of text) {
this.terminal.InsertCell(char)
this.PushToBuffer(char)
this.SyncCursorToBuffer()
this._SyncCursorToBuffer()
}
}
private SetBuffer(text: string) {
_SetBuffer(text: string) {
this.ClearBuffer()
this.InsertText(text)
}
private ExitHistoryNavigation() {
this.historyIndex = null
this.historyDraft = ''
}
private NavigateHistory(direction: -1 | 1) {
if (this.history.length === 0)
return
if (direction === -1) {
if (this.historyIndex === null) {
this.historyDraft = this.buffer.join('')
this.historyIndex = this.history.length - 1
} else if (this.historyIndex > 0) {
this.historyIndex -= 1
} else {
return
}
this.SetBuffer(this.history[this.historyIndex])
return
}
if (this.historyIndex === null)
return
if (this.historyIndex < this.history.length - 1) {
this.historyIndex += 1
this.SetBuffer(this.history[this.historyIndex])
return
}
this.historyIndex = null
this.SetBuffer(this.historyDraft)
this._InsertText(text)
}
}