Skip to content

Commit

Permalink
Improved documentation further and the SelectWithAdd example
Browse files Browse the repository at this point in the history
  • Loading branch information
minivera committed Jul 19, 2018
1 parent 170b792 commit 5ff80d1
Show file tree
Hide file tree
Showing 14 changed files with 296 additions and 171 deletions.
23 changes: 17 additions & 6 deletions _examples/select_add/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,24 @@ import (
)

func main() {
prompt := promptui.SelectWithAdd{
Label: "What's your text editor",
Items: []string{"Vim", "Emacs", "Sublime", "VSCode", "Atom"},
AddLabel: "Other",
}
items := []string{"Vim", "Emacs", "Sublime", "VSCode", "Atom"}
index := -1
var result string
var err error

for index < 0 {
prompt := promptui.SelectWithAdd{
Label: "What's your text editor",
Items: items,
AddLabel: "Other",
}

_, result, err := prompt.Run()
index, result, err = prompt.Run()

if index == -1 {
items = append(items, result)
}
}

if err != nil {
fmt.Printf("Prompt failed %v\n", err)
Expand Down
26 changes: 11 additions & 15 deletions codes.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,9 @@ const esc = "\033["

type attribute int

// Define the possible state of text inside the application, either Bold, faint, italic or underline.
// The possible state of text inside the application, either Bold, faint, italic or underline.
//
// By using an identifier in templates similar to `"{{ . | underline }}"`, one of these state will be given to the
// text through the use of the Styler function.
// These constants are called through the use of the Styler function.
const (
reset attribute = iota

Expand All @@ -24,10 +23,9 @@ const (
FGUnderline
)

// Define the possible colors of text inside the application.
// The possible colors of text inside the application.
//
// By using an identifier in templates similar to `"{{ . | cyan }}"`, one of these colors will be given to the
// text through the use of the Styler function.
// These constants are called through the use of the Styler function.
const (
FGBlack attribute = iota + 30
FGRed
Expand All @@ -39,10 +37,9 @@ const (
FGWhite
)

// Define the possible background colors of text inside the application.
// The possible background colors of text inside the application.
//
// By using an identifier in templates similar to `"{{ . | red | cyan }}"`, where the second color is
// the background color, one of these colors will be given to the text through the use of the Styler function.
// These constants are called through the use of the Styler function.
const (
BGBlack attribute = iota + 40
BGRed
Expand All @@ -63,11 +60,10 @@ const (
clearLine = esc + "2K"
)

// FuncMap defines template helpers for the output. It can be extended as a
// regular map.
// FuncMap defines template helpers for the output. It can be extended as a regular map.
//
// The function maps the state, color and background colors constants to string inside the promptui
// templates. This allows the link between the string "black" in a template and the constant FGBlack.
// The functions inside the map link the state, color and background colors strings detected in templates to a Styler
// function that applies the given style using the corresponding constant.
var FuncMap = template.FuncMap{
"black": Styler(FGBlack),
"red": Styler(FGRed),
Expand Down Expand Up @@ -99,12 +95,12 @@ func movementCode(n uint, code rune) string {
return esc + strconv.FormatUint(uint64(n), 10) + string(code)
}

// Styler is a closure that accepts multiple possible styling transforms from the state,
// Styler is a function that accepts multiple possible styling transforms from the state,
// color and background colors constants and transforms them into a templated string
// to apply those styles in the CLI.
//
// The returned styling function accepts a string that will be extended with
// the original function's styling attributes.
// the wrapping function's styling attributes.
func Styler(attrs ...attribute) func(interface{}) string {
attrstrs := make([]string, len(attrs))
for i, v := range attrs {
Expand Down
54 changes: 54 additions & 0 deletions example_main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package promptui

import (
"errors"
"fmt"
"strconv"
)

// This is an example for the Prompt mode of promptui. In this example, a prompt is created
// with a validator function that validates the given value to make sure its a number.
// If successful, it will output the chosen number in a formatted message.
func Example() {
validate := func(input string) error {
_, err := strconv.ParseFloat(input, 64)
if err != nil {
return errors.New("Invalid number")
}
return nil
}

prompt := Prompt{
Label: "Number",
Validate: validate,
}

result, err := prompt.Run()

if err != nil {
fmt.Printf("Prompt failed %v\n", err)
return
}

fmt.Printf("You choose %q\n", result)
}

// This is an example for the Select mode of promptui. In this example, a select is created with
// the days of the week as its items. When an item is selected, the selected day will be displayed
// in a formatted message.
func Example_aux() {
prompt := Select{
Label: "Select Day",
Items: []string{"Monday", "Tuesday", "Wednesday", "Thursday", "Friday",
"Saturday", "Sunday"},
}

_, result, err := prompt.Run()

if err != nil {
fmt.Printf("Prompt failed %v\n", err)
return
}

fmt.Printf("You choose %q\n", result)
}
42 changes: 42 additions & 0 deletions example_prompt_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package promptui

import (
"strconv"
"fmt"
)

// This example shows how to use the prompt validator and templates to create a stylized prompt.
// The validator will make sure the value entered is a parseable float while the templates will
// color the value to show validity.
func ExamplePrompt() {
// The validate function follows the required validator signature.
validate := func(input string) error {
_, err := strconv.ParseFloat(input, 64)
return err
}

// Each template displays the data received from the prompt with some formatting.
templates := &PromptTemplates{
Prompt: "{{ . }} ",
Valid: "{{ . | green }} ",
Invalid: "{{ . | red }} ",
Success: "{{ . | bold }} ",
}

prompt := Prompt{
Label: "Spicy Level",
Templates: templates,
Validate: validate,
}

result, err := prompt.Run()

if err != nil {
fmt.Printf("Prompt failed %v\n", err)
return
}

// The result of the prompt, if valid, is displayed in a formatted message.
fmt.Printf("You answered %s\n", result)
}

75 changes: 75 additions & 0 deletions example_select_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package promptui

import (
"fmt"
"strings"
)

// Any type can be given to the select's item as long as the templates properly implement the dot notation
// to display it.
type pepper struct {
Name string
HeatUnit int
Peppers int
}

// This examples shows a complex and customized select.
func ExampleSelect() {
// The select will show a series of peppers stored inside a slice of structs. To display the content of the struct,
// the usual dot notation is used inside the templates to select the fields and color them.
peppers := []pepper{
{Name: "Bell Pepper", HeatUnit: 0, Peppers: 0},
{Name: "Banana Pepper", HeatUnit: 100, Peppers: 1},
{Name: "Poblano", HeatUnit: 1000, Peppers: 2},
{Name: "Jalapeño", HeatUnit: 3500, Peppers: 3},
{Name: "Aleppo", HeatUnit: 10000, Peppers: 4},
{Name: "Tabasco", HeatUnit: 30000, Peppers: 5},
{Name: "Malagueta", HeatUnit: 50000, Peppers: 6},
{Name: "Habanero", HeatUnit: 100000, Peppers: 7},
{Name: "Red Savina Habanero", HeatUnit: 350000, Peppers: 8},
{Name: "Dragon’s Breath", HeatUnit: 855000, Peppers: 9},
}

// The Active and Selected templates set a small pepper icon next to the name colored and the heat unit for the
// active template. The details template is show at the bottom of the select's list and displays the full info
// for that pepper in a multi-line template.
templates := &SelectTemplates{
Label: "{{ . }}?",
Active: "\U0001F336 {{ .Name | cyan }} ({{ .HeatUnit | red }})",
Inactive: " {{ .Name | cyan }} ({{ .HeatUnit | red }})",
Selected: "\U0001F336 {{ .Name | red | cyan }}",
Details: `
--------- Pepper ----------
{{ "Name:" | faint }} {{ .Name }}
{{ "Heat Unit:" | faint }} {{ .HeatUnit }}
{{ "Peppers:" | faint }} {{ .Peppers }}`,
}

// A searcher function is implemented which enabled the search mode for the select. The function follows
// the required searcher signature and finds any pepper whose name contains the searched string.
searcher := func(input string, index int) bool {
pepper := peppers[index]
name := strings.Replace(strings.ToLower(pepper.Name), " ", "", -1)
input = strings.Replace(strings.ToLower(input), " ", "", -1)

return strings.Contains(name, input)
}

prompt := Select{
Label: "Spicy Level",
Items: peppers,
Templates: templates,
Size: 4,
Searcher: searcher,
}

i, _, err := prompt.Run()

if err != nil {
fmt.Printf("Prompt failed %v\n", err)
return
}

// The selected pepper will be displayed with its name and index in a formatted message.
fmt.Printf("You choose number %d: %s\n", i+1, peppers[i].Name)
}
33 changes: 33 additions & 0 deletions example_selectwithadd_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package promptui

import "fmt"

// This example shows how to create a SelectWithAdd that will add each new item it is given to the
// list of items until one is chosen.
func ExampleSelectWithAdd() {
items := []string{"Vim", "Emacs", "Sublime", "VSCode", "Atom"}
index := -1
var result string
var err error

for index < 0 {
prompt := SelectWithAdd{
Label: "What's your text editor",
Items: items,
AddLabel: "Add your own",
}

index, result, err = prompt.Run()

if index == -1 {
items = append(items, result)
}
}

if err != nil {
fmt.Printf("Prompt failed %v\n", err)
return
}

fmt.Printf("You choose %s\n", result)
}
14 changes: 7 additions & 7 deletions keycodes.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,23 @@ package promptui
import "github.com/chzyer/readline"

// These runes are used to identity the commands entered by the user in the command prompt. They map
// to specific actions of prompt-ui and can be remapped if necessary.
// to specific actions of promptui in prompt mode and can be remapped if necessary.
var (
// KeyEnter is the default key for submission/selection inside a command line prompt.
// KeyEnter is the default key for submission/selection.
KeyEnter rune = readline.CharEnter

// KeyBackspace is the default key for deleting input text inside a command line prompt.
// KeyBackspace is the default key for deleting input text.
KeyBackspace rune = readline.CharBackspace

// KeyPrev is the default key to go up during selection inside a command line prompt.
// KeyPrev is the default key to go up during selection.
KeyPrev rune = readline.CharPrev

// KeyNext is the default key to go down during selection inside a command line prompt.
// KeyNext is the default key to go down during selection.
KeyNext rune = readline.CharNext

// KeyBackward is the default key to page up during selection inside a command line prompt.
// KeyBackward is the default key to page up during selection.
KeyBackward rune = readline.CharBackward

// KeyForward is the default key to page down during selection inside a command line prompt.
// KeyForward is the default key to page down during selection.
KeyForward rune = readline.CharForward
)
4 changes: 2 additions & 2 deletions list/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ type List struct {
Searcher Searcher
}

// New creates and initializes a list of searchable items. The items attribute must be a slice of items with a
// New creates and initializes a list of searchable items. The items attribute must be a slice type with a
// size greater than 0. Error will be returned if those two conditions are not met.
func New(items interface{}, size int) (*List, error) {
if size < 1 {
Expand Down Expand Up @@ -162,7 +162,7 @@ func (l *List) CanPageUp() bool {
}

// Index returns the index of the item currently selected inside the searched list. If no item is selected,
// the NotFound (-1) index will be returned instead.
// the NotFound (-1) index is returned.
func (l *List) Index() int {
selected := l.scope[l.cursor]

Expand Down
Loading

0 comments on commit 5ff80d1

Please sign in to comment.