A command-line prompt that looks normal, but can show results as users type.
Bubbletea can do this and much more. It wasn't as popular when I wrote rtprompt
or I wasn't aware of it.
Writing bubbletea
programs is great fun, but leaving this code around as sometimes you just want the most basic thing.
I wanted a way to have auto-updated results in the terminal as a user typed -- without the experience that curses brings. I'm not a fan of how terminal UIs feel like opening a new window.
Turns out this didn't exist, and wasn't nearly as straight forward as I had hoped. We have to put the terminal into raw mode to get unbuffered input, but because it's a text prompt we need to recreate the keybindings that Linux's terminal discipline uses. Fortunately when we're done prompting, we can put everything back the way it was!
Here's the code for the example above. It's a silly thing that counts the words that have been typed. But it demonstrates that we can have output update in real-time without doing a full-screen terminal UI.
Navigation shortcuts are demonstrated too.
package main
import (
"fmt"
"github.com/coxley/rtprompt"
)
func callback(inp string, tab bool, enter bool) string {
return fmt.Sprintf("Words typed: %d", len(strings.Fields(inp)))
}
func main() {
prompt := rtprompt.New("Input: ", callback)
prompt.Wait()
fmt.Println("Glad we're done with that")
}
This one is more involved. It's also why I wrote this lib. I wanted users, when reporting a bug, have a list of open tasks shown that get updated as they type.
We use https://github.com/schollz/closestmatch for ranking, support tab to select items, colors for selected items, and a function to invoke when the user presses enter.
package main
import (
"fmt"
"github.com/coxley/rtprompt"
)
func main() {
issues := map[string]string{
"[#1011] LSPs too optimized": "",
"[#1112] Dry runs too dry": "",
"[#1213] All hands on deck": "",
"[#1314] Leak in the Enterprise": "",
"[#1415] Exception thrown during cardio": "",
"[#1516] Panic when frying eggs": "",
"[#1617] Frying eggs when panicking": "",
"[#1618] Nothing to report, just lonely": "",
"[#1619] Bloody onions when expecting uncontaminated ones": "",
}
var selected string
cm := rtprompt.ClosestMatch{
Data: issues,
OnSelect: func(s string) { selected = s },
MaxShown: 7,
}
prompt := rtprompt.New("Summary: ", cm.CB())
prompt.Wait()
fmt.Printf("Woohoo! You selected: %s\n", selected)
}
These should cover most use-cases but happy to expand them.
- ALT+B: Go back a word
- ALT+F: Go forward a word
- CTRL+A: Beginning of line
- CTRL+B: Move left
- CTRL+D: Delete forward
- CTRL+E: End of line
- CTRL+F: Move right
- CTRL+K: Remove all text after cursor
- CTRL+U: Remove all text before cursor
- CTRL+W: Remove the previous word
- CTRL-C: SIGINT
- END: End of line
- Enter: Finish input
- HOME: Beginning of line
- Left arrow: Move left
- Right arrow: Move right