rtprompt

package module
v0.0.0-...-6472287 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 18, 2021 License: MIT Imports: 7 Imported by: 0

README

Realtime Prompt (rtprompt)

GoDoc

A command-line prompt that looks normal, but can show results as users type.

Basic Example

Why?

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!

Example (Basic)

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")
}

Example (Closest Match)

Closest Match Example

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)
}

Supported Keybinds

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

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Callback

type Callback func(s string, tab bool, enter bool) string

Callback to notify when input changes

Receives the input value and whether a tab was just pressed. Called in-line with each key press so heavy processing should be done outside of the callback path.

Callback is initialized with zero-values before keypresses are read. This is gives you a chance to show full output up front, and let user input reduce it.

Tab and Enter keypresses will set the respective booleans. The input text will be passed as well.

It's possible to split Callback into onKeypress, onTab, and onEnter. If this is something you want please submit an issue on the repo. :)

type ClosestMatch

type ClosestMatch struct {
	Data     map[string]string
	OnSelect func(string)
	MaxShown int

	// default: "Use <TAB> and <ENTER> to select from below. Otherwise press <ENTER> when ready"
	Instructions     string
	ShowInstructions bool

	// default: FgBlue
	SelectedColor *color.Color
	// default: FgHiBlack
	InstructionColor *color.Color
}

ClosestMatch configures a callback to let users select from a set of options

Data's keys are used as titles, and shown on-screen. The values can be used to give additional ranking. For example, you might want to show ticket names but use their summaries for more accurate matching.

onSelect will be called when user presses Enter. If they made a selection, the string arg will be equal to one of the keys in Data. Otherwise it's the raw value of the prompt.

func (*ClosestMatch) CB

func (c *ClosestMatch) CB() Callback

CB returns a configured callback to use with Prompt

type Prompt

type Prompt struct {
	// What the user sees in front of their input
	Prefix   string
	Callback Callback

	// Lines between the prompt and output from callback (default: 2)
	Padding int

	// Keep the value of text and pos variables on screen
	Debug bool
	// contains filtered or unexported fields
}

Prompt lets a user type input that is sent to a callback in realtime

Callback is invoked when the input changes or Tab or Enter are pressed. The return value is shown below the prompt and rewritten when changed.

This allows for a reactive CLI that isn't full-on curses/TUI. It should feel like a normal tool.

func New

func New(pfx string, callback Callback) *Prompt

New prompt instance w/ sane defaults

func (*Prompt) Wait

func (p *Prompt) Wait()

Wait begins the prompt and feeds updates into the callback until Enter is pressed.

The terminal is put into raw mode until this returns. Avoid writing to stdout/stderr until then else it will throw off the formatting. Badly.

Most of the logic making it feel like a normal prompt is from careful repositioning of the ANSI cursor.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL