diff --git a/src/ansi-shell/readline.js b/src/ansi-shell/readline.js index e72b88d..e25d345 100644 --- a/src/ansi-shell/readline.js +++ b/src/ansi-shell/readline.js @@ -20,15 +20,18 @@ const ReadlineProcessorBuilder = builder => builder result: { value: '' }, cursor: { value: 0 }, }, + // TODO: dormant configuration; waiting on ContextSignature imports: { out: {}, - in_: {} + in_: {}, + history: {} } })) .variable('result', { getDefaultValue: () => '' }) .variable('cursor', { getDefaultValue: () => 0 }) .external('out', { required: true }) .external('in_', { required: true }) + .external('history', { required: true }) .beforeAll('get-byte', async ctx => { const { locals, externs } = ctx; @@ -162,7 +165,7 @@ const ReadlineProcessorBuilder = builder => builder const finalByte = locals.byte; const controlSequence = vars.controlSequence.toArray(); - // Log.log('controlSequence', controlSequence); + // Log.log('controlSequence', finalByte, controlSequence); if ( ! CSI_HANDLERS.hasOwnProperty(finalByte) ) { return; @@ -187,10 +190,27 @@ const ReadlineProcessor = ReadlineProcessorBuilder( new StatefulProcessorBuilder() ); +class HistoryManager { + constructor () { + this.items = []; + this.index = 0; + } + + get () { + return this.items[this.index]; + } + + save (data) { + this.items[this.index] = data; + } +} + class Readline { constructor (params) { this.internal_ = {}; for ( const k in params ) this.internal_[k] = params[k]; + + this.history = new HistoryManager(); } async readline (prompt) { @@ -201,39 +221,16 @@ class Readline { const { result - } = await ReadlineProcessor.run({ out, in_ }); + } = await ReadlineProcessor.run({ + out, in_, + history: this.history + }); + + this.history.save(result); + this.history.index++; return result; } - - // async readline (prompt) { - // const out = this.internal_.out; - // const in_ = this.internal_.in; - - // out.write(prompt); - - // let text = ''; - - // const decoder = new TextDecoder(); - - // while ( true ) { - // const byteBuffer = new Uint8Array(1); - // await in_.read(byteBuffer); - - // const byte = byteBuffer[0]; - // if ( byte === CHAR_LF ) { - // out.write('\n'); - // break; - // } - - // out.write(byteBuffer); - // const part = decoder.decode(byteBuffer); - // text += part; - // } - - // // const text = await in_.readLine({ stream: out }); - // return text; - // } } export default class ReadlineLib { diff --git a/src/ansi-shell/rl_csi_handlers.js b/src/ansi-shell/rl_csi_handlers.js index 610e05a..c0a1616 100644 --- a/src/ansi-shell/rl_csi_handlers.js +++ b/src/ansi-shell/rl_csi_handlers.js @@ -59,8 +59,71 @@ export const PC_FN_HANDLERS = { } }; +const save_history = ctx => { + const { history } = ctx.externs; + history.save(ctx.vars.result); +}; + +const correct_cursor = (ctx, oldCursor) => { + // TODO: make this work differently if oldCursor is not defined + + const amount = ctx.vars.cursor - oldCursor; + ctx.vars.cursor = ctx.vars.result.length; + const L = amount < 0 ? 'D' : 'C'; + if ( amount === 0 ) return; + const moveSequence = new Uint8Array([ + consts.CHAR_ESC, consts.CHAR_CSI, + ...(new TextEncoder().encode('' + Math.abs(amount))), + cc(L) + ]); + ctx.externs.out.write(moveSequence); +}; + +const home = ctx => { + const amount = ctx.vars.cursor; + ctx.vars.cursor = 0; + const moveSequence = new Uint8Array([ + consts.CHAR_ESC, consts.CHAR_CSI, + ...(new TextEncoder().encode('' + amount)), + cc('D') + ]); + if ( amount !== 0 ) ctx.externs.out.write(moveSequence); +}; + +const select_current_history = ctx => { + const { history } = ctx.externs; + home(ctx); + ctx.vars.result = history.get(); + ctx.vars.cursor = ctx.vars.result.length; + const clearToEndSequence = new Uint8Array([ + consts.CHAR_ESC, consts.CHAR_CSI, + ...(new TextEncoder().encode('0')), + cc('K') + ]); + ctx.externs.out.write(clearToEndSequence); + ctx.externs.out.write(history.get()); +}; + // --- CSI handlers: this is the last definition in this file --- export const CSI_HANDLERS = { + [cc('A')]: CSI_INT_ARG(ctx => { + save_history(ctx); + const { history } = ctx.externs; + + if ( history.index === 0 ) return; + + history.index--; + select_current_history(ctx); + }), + [cc('B')]: CSI_INT_ARG(ctx => { + save_history(ctx); + const { history } = ctx.externs; + + if ( history.index === history.items.length - 1 ) return; + + history.index++; + select_current_history(ctx); + }), // cursor back [cc('D')]: CSI_INT_ARG(ctx => { if ( ctx.vars.cursor === 0 ) { @@ -115,14 +178,7 @@ export const CSI_HANDLERS = { }), // Home [cc('H')]: ctx => { - const amount = ctx.vars.cursor; - ctx.vars.cursor = 0; - const moveSequence = new Uint8Array([ - consts.CHAR_ESC, consts.CHAR_CSI, - ...(new TextEncoder().encode('' + amount)), - cc('D') - ]); - ctx.externs.out.write(moveSequence); + home(ctx); }, // End [cc('F')]: ctx => { @@ -133,6 +189,6 @@ export const CSI_HANDLERS = { ...(new TextEncoder().encode('' + amount)), cc('C') ]); - ctx.externs.out.write(moveSequence); + if ( amount !== 0 ) ctx.externs.out.write(moveSequence); }, };