From 47c3d83e7c803af4a086114d841af3f2c3baae1b Mon Sep 17 00:00:00 2001 From: Lars Bahner Date: Thu, 22 Feb 2024 20:51:24 +0100 Subject: [PATCH] Added command history --- ui/events.go | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++- ui/ui.go | 36 +++++++++------------------ 2 files changed, 80 insertions(+), 26 deletions(-) diff --git a/ui/events.go b/ui/events.go index 198f6d5..fdf6831 100644 --- a/ui/events.go +++ b/ui/events.go @@ -6,6 +6,8 @@ import ( "time" "github.com/bahner/go-ma" + "github.com/gdamore/tcell/v2" + "github.com/rivo/tview" ) // // displaySelfMessage writes a message from ourself to the message window, @@ -25,9 +27,76 @@ func (ui *ChatUI) displaySystemMessage(msg string) { fmt.Fprintf(ui.msgW, "%s %s\n", prompt, msg) } +func (ui *ChatUI) setupInputField(inputField *tview.InputField, app *tview.Application) { + inputField.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { + switch event.Key() { + case tcell.KeyUp: + if ui.currentHistoryIndex < len(ui.inputHistory)-1 { + ui.currentHistoryIndex++ + input := ui.inputHistory[len(ui.inputHistory)-1-ui.currentHistoryIndex] + inputField.SetText(input) + return nil // event handled + } + case tcell.KeyDown: + if ui.currentHistoryIndex > 0 { + ui.currentHistoryIndex-- + input := ui.inputHistory[len(ui.inputHistory)-1-ui.currentHistoryIndex] + inputField.SetText(input) + return nil // event handled + } else if ui.currentHistoryIndex == 0 { + ui.currentHistoryIndex = -1 + inputField.SetText("") // Clear the input field + return nil // event handled + } + } + return event // let other keys pass through + }) + + inputField.SetDoneFunc(func(key tcell.Key) { + if key == tcell.KeyEnter { + input := inputField.GetText() + if input != "" { + ui.inputHistory = append(ui.inputHistory, input) // Add to history + ui.currentHistoryIndex = -1 // Reset index + ui.chInput <- input // Send input to be handled + inputField.SetText("") // Clear the input field + } + } + }) + + // the done func is called when the user hits enter, or tabs out of the field + inputField.SetDoneFunc(func(key tcell.Key) { + if key != tcell.KeyEnter { + // we don't want to do anything if they just tabbed away + return + } + + line := inputField.GetText() + if len(line) == 0 { + // ignore blank lines + return + } + + // bail if requested + if line == "/quit" { + app.Stop() + return + } + + ui.inputHistory = append(ui.inputHistory, line) + ui.currentHistoryIndex = -1 + + // send the line onto the input chan and reset the field text + ui.chInput <- line + inputField.SetText("") + }) + +} + // handleEvents runs an event loop that sends user input to the chat room // and displays messages received from the chat room. It also periodically // refreshes the list of peers in the UI. + func (ui *ChatUI) handleEvents() { peerRefreshTicker := time.NewTicker(time.Second) defer peerRefreshTicker.Stop() @@ -46,7 +115,6 @@ func (ui *ChatUI) handleEvents() { ui.displayBroadcastMessage(m) continue } - ui.displayChatMessage(m) case <-peerRefreshTicker.C: diff --git a/ui/ui.go b/ui/ui.go index e32f29b..eca0d0a 100644 --- a/ui/ui.go +++ b/ui/ui.go @@ -43,6 +43,10 @@ type ChatUI struct { broadcastCtx context.Context broadcastCancel context.CancelFunc + // History of entries + inputHistory []string + currentHistoryIndex int + // The Topic is used for publication of messages after encryption and signing. // The names are obviously, from the corresponding DIDDocument. @@ -85,29 +89,6 @@ func NewChatUI(p *p2p.P2P, a *actor.Actor) (*ChatUI, error) { SetFieldWidth(0). SetFieldBackgroundColor(tcell.ColorBlack) - // the done func is called when the user hits enter, or tabs out of the field - input.SetDoneFunc(func(key tcell.Key) { - if key != tcell.KeyEnter { - // we don't want to do anything if they just tabbed away - return - } - line := input.GetText() - if len(line) == 0 { - // ignore blank lines - return - } - - // bail if requested - if line == "/quit" { - app.Stop() - return - } - - // send the line onto the input chan and reset the field text - chInput <- line - input.SetText("") - }) - // make a text view to hold the list of peers in the room, updated by ui.refreshPeers() peersList := tview.NewTextView() peersList.SetBorder(true) @@ -129,7 +110,7 @@ func NewChatUI(p *p2p.P2P, a *actor.Actor) (*ChatUI, error) { app.SetRoot(flex, true) - return &ChatUI{ + ui := &ChatUI{ a: a, p: p, app: app, @@ -139,7 +120,12 @@ func NewChatUI(p *p2p.P2P, a *actor.Actor) (*ChatUI, error) { chInput: chInput, chMessage: make(chan *msg.Message, UI_MESSAGES_CHANNEL_BUFFERSIZE), chDone: make(chan struct{}, 1), - }, nil + } + + // A little kludgy, but acceptable for now. + ui.setupInputField(input, app) + + return ui, nil } // Run starts the chat event loop in the background, then starts