summaryrefslogtreecommitdiff
path: root/vendor/github.com/manifoldco/promptui/cursor.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/manifoldco/promptui/cursor.go')
-rw-r--r--vendor/github.com/manifoldco/promptui/cursor.go232
1 files changed, 232 insertions, 0 deletions
diff --git a/vendor/github.com/manifoldco/promptui/cursor.go b/vendor/github.com/manifoldco/promptui/cursor.go
new file mode 100644
index 000000000..7f0961bdb
--- /dev/null
+++ b/vendor/github.com/manifoldco/promptui/cursor.go
@@ -0,0 +1,232 @@
+package promptui
+
+import (
+ "fmt"
+ "strings"
+)
+
+// Pointer is A specific type that translates a given set of runes into a given
+// set of runes pointed at by the cursor.
+type Pointer func(to []rune) []rune
+
+func defaultCursor(ignored []rune) []rune {
+ return []rune("\u2588")
+}
+
+func blockCursor(input []rune) []rune {
+ return []rune(fmt.Sprintf("\\e[7m%s\\e[0m", string(input)))
+}
+
+func pipeCursor(input []rune) []rune {
+ marker := []rune("|")
+ out := []rune{}
+ out = append(out, marker...)
+ out = append(out, input...)
+ return out
+}
+
+var (
+ // DefaultCursor is a big square block character. Obscures whatever was
+ // input.
+ DefaultCursor Pointer = defaultCursor
+ // BlockCursor is a cursor which highlights a character by inverting colors
+ // on it.
+ BlockCursor Pointer = blockCursor
+ // PipeCursor is a pipe character "|" which appears before the input
+ // character.
+ PipeCursor Pointer = pipeCursor
+)
+
+// Cursor tracks the state associated with the movable cursor
+// The strategy is to keep the prompt, input pristine except for requested
+// modifications. The insertion of the cursor happens during a `format` call
+// and we read in new input via an `Update` call
+type Cursor struct {
+ // shows where the user inserts/updates text
+ Cursor Pointer
+ // what the user entered, and what we will echo back to them, after
+ // insertion of the cursor and prefixing with the prompt
+ input []rune
+ // Put the cursor before this slice
+ Position int
+ erase bool
+}
+
+// NewCursor create a new cursor, with the DefaultCursor, the specified input,
+// and position at the end of the specified starting input.
+func NewCursor(startinginput string, pointer Pointer, eraseDefault bool) Cursor {
+ if pointer == nil {
+ pointer = defaultCursor
+ }
+ cur := Cursor{Cursor: pointer, Position: len(startinginput), input: []rune(startinginput), erase: eraseDefault}
+ if eraseDefault {
+ cur.Start()
+ } else {
+ cur.End()
+ }
+ return cur
+}
+
+func (c *Cursor) String() string {
+ return fmt.Sprintf(
+ "Cursor: %s, input %s, Position %d",
+ string(c.Cursor([]rune(""))), string(c.input), c.Position)
+}
+
+// End is a convenience for c.Place(len(c.input)) so you don't have to know how I
+// indexed.
+func (c *Cursor) End() {
+ c.Place(len(c.input))
+}
+
+// Start is convenience for c.Place(0) so you don't have to know how I
+// indexed.
+func (c *Cursor) Start() {
+ c.Place(0)
+}
+
+// ensures we are in bounds.
+func (c *Cursor) correctPosition() {
+ if c.Position > len(c.input) {
+ c.Position = len(c.input)
+ }
+
+ if c.Position < 0 {
+ c.Position = 0
+ }
+}
+
+// insert the cursor rune array into r before the provided index
+func format(a []rune, c *Cursor) string {
+ i := c.Position
+ var b []rune
+
+ out := make([]rune, 0)
+ if i < len(a) {
+ b = c.Cursor(a[i : i+1])
+ out = append(out, a[:i]...) // does not include i
+ out = append(out, b...) // add the cursor
+ out = append(out, a[i+1:]...) // add the rest after i
+ } else {
+ b = c.Cursor([]rune{})
+ out = append(out, a...)
+ out = append(out, b...)
+ }
+ return string(out)
+}
+
+// Format renders the input with the Cursor appropriately positioned.
+func (c *Cursor) Format() string {
+ r := c.input
+ // insert the cursor
+ return format(r, c)
+}
+
+// FormatMask replaces all input runes with the mask rune.
+func (c *Cursor) FormatMask(mask rune) string {
+ if mask == ' ' {
+ return format([]rune{}, c)
+ }
+
+ r := make([]rune, len(c.input))
+ for i := range r {
+ r[i] = mask
+ }
+ return format(r, c)
+}
+
+// Update inserts newinput into the input []rune in the appropriate place.
+// The cursor is moved to the end of the inputed sequence.
+func (c *Cursor) Update(newinput string) {
+ a := c.input
+ b := []rune(newinput)
+ i := c.Position
+ a = append(a[:i], append(b, a[i:]...)...)
+ c.input = a
+ c.Move(len(b))
+}
+
+// Get returns a copy of the input
+func (c *Cursor) Get() string {
+ return string(c.input)
+}
+
+// GetMask returns a mask string with length equal to the input
+func (c *Cursor) GetMask(mask rune) string {
+ return strings.Repeat(string(mask), len(c.input))
+}
+
+// Replace replaces the previous input with whatever is specified, and moves the
+// cursor to the end position
+func (c *Cursor) Replace(input string) {
+ c.input = []rune(input)
+ c.End()
+}
+
+// Place moves the cursor to the absolute array index specified by position
+func (c *Cursor) Place(position int) {
+ c.Position = position
+ c.correctPosition()
+}
+
+// Move moves the cursor over in relative terms, by shift indices.
+func (c *Cursor) Move(shift int) {
+ // delete the current cursor
+ c.Position = c.Position + shift
+ c.correctPosition()
+}
+
+// Backspace removes the rune that precedes the cursor
+//
+// It handles being at the beginning or end of the row, and moves the cursor to
+// the appropriate position.
+func (c *Cursor) Backspace() {
+ a := c.input
+ i := c.Position
+ if i == 0 {
+ // Shrug
+ return
+ }
+ if i == len(a) {
+ c.input = a[:i-1]
+ } else {
+ c.input = append(a[:i-1], a[i:]...)
+ }
+ // now it's pointing to the i+1th element
+ c.Move(-1)
+}
+
+// Listen is a readline Listener that updates internal cursor state appropriately.
+func (c *Cursor) Listen(line []rune, pos int, key rune) ([]rune, int, bool) {
+ if line != nil {
+ // no matter what, update our internal representation.
+ c.Update(string(line))
+ }
+
+ switch key {
+ case 0: // empty
+ case KeyEnter:
+ return []rune(c.Get()), c.Position, false
+ case KeyBackspace, KeyCtrlH:
+ if c.erase {
+ c.erase = false
+ c.Replace("")
+ }
+ c.Backspace()
+ case KeyForward:
+ // the user wants to edit the default, despite how we set it up. Let
+ // them.
+ c.erase = false
+ c.Move(1)
+ case KeyBackward:
+ c.Move(-1)
+ default:
+ if c.erase {
+ c.erase = false
+ c.Replace("")
+ c.Update(string(key))
+ }
+ }
+
+ return []rune(c.Get()), c.Position, true
+}