From fd5784ff0ebbdfac2bd319c639813cdfb05f575d Mon Sep 17 00:00:00 2001 From: Luis Davim Date: Tue, 26 Jul 2022 15:16:03 +0100 Subject: [PATCH 01/10] feat: adding interactive continue printer Signed-off-by: Luis Davim --- README.md | 31 ++- _examples/README.md | 25 +++ _examples/interactive_continue/README.md | 25 +++ _examples/interactive_continue/demo/README.md | 18 ++ .../interactive_continue/demo/animation.svg | 10 + _examples/interactive_continue/demo/ci.go | 19 ++ _examples/interactive_continue/demo/main.go | 11 ++ docs/README.md | 31 ++- interactive_continue_printer.go | 178 +++++++++++++++++ interactive_continue_printer_test.go | 183 ++++++++++++++++++ 10 files changed, 525 insertions(+), 6 deletions(-) create mode 100644 _examples/interactive_continue/README.md create mode 100644 _examples/interactive_continue/demo/README.md create mode 100644 _examples/interactive_continue/demo/animation.svg create mode 100644 _examples/interactive_continue/demo/ci.go create mode 100644 _examples/interactive_continue/demo/main.go create mode 100644 interactive_continue_printer.go create mode 100644 interactive_continue_printer_test.go diff --git a/README.md b/README.md index 7433dc04a..eb0acfee2 100644 --- a/README.md +++ b/README.md @@ -90,7 +90,7 @@ which features automatic website generation, automatic deployments, a custom CI- |✏ Documentation |To view the official documentation of the latest release, you can go to the automatically generated page of [pkg.go.dev](https://pkg.go.dev/github.com/pterm/pterm#section-documentation) This documentation is very technical and includes every method that can be used in PTerm.
**For an easy start we recommend that you take a look at the [examples section](#-examples).** Here you can see pretty much every feature of PTerm with example code. The animations of the examples are automatically updated as soon as something changes in PTerm.|
- + ### Printers (Components) |Feature|Examples| - |Feature|Examples| @@ -113,7 +113,7 @@ which features automatic website generation, automatic deployments, a custom CI- |---|---|---| |![Jens Lauterbach](https://avatars.githubusercontent.com/u/1292368?s=25)|[@jenslauterbach](https://github.com/jenslauterbach)|25$| -
+ ## 🧪 Examples @@ -1066,6 +1066,31 @@ func boolToText(b bool) string { +### interactive_continue/demo + +![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/interactive_continue/demo/animation.svg) + +
+ +SHOW SOURCE + +```go +package main + +import ( + "github.com/pterm/pterm" +) + +func main() { + result, _ := pterm.DefaultInteractiveContinue.Show() + pterm.Println() // Blank line + pterm.Info.Printfln("You answered: %s", result) +} + +``` + +
+ ### interactive_multiselect/demo ![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/interactive_multiselect/demo/animation.svg) @@ -1608,7 +1633,7 @@ func main() { - + --- > GitHub [@pterm](https://github.com/pterm)  ·  diff --git a/_examples/README.md b/_examples/README.md index 12dad6280..41cc98480 100644 --- a/_examples/README.md +++ b/_examples/README.md @@ -938,6 +938,31 @@ func boolToText(b bool) string { +### interactive_continue/demo + +![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/interactive_continue/demo/animation.svg) + +
+ +SHOW SOURCE + +```go +package main + +import ( + "github.com/pterm/pterm" +) + +func main() { + result, _ := pterm.DefaultInteractiveContinue.Show() + pterm.Println() // Blank line + pterm.Info.Printfln("You answered: %s", result) +} + +``` + +
+ ### interactive_multiselect/demo ![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/interactive_multiselect/demo/animation.svg) diff --git a/_examples/interactive_continue/README.md b/_examples/interactive_continue/README.md new file mode 100644 index 000000000..3aa4b33a1 --- /dev/null +++ b/_examples/interactive_continue/README.md @@ -0,0 +1,25 @@ +### interactive_continue/demo + +![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/interactive_continue/demo/animation.svg) + +
+ +SHOW SOURCE + +```go +package main + +import ( + "github.com/pterm/pterm" +) + +func main() { + result, _ := pterm.DefaultInteractiveContinue.Show() + pterm.Println() // Blank line + pterm.Info.Printfln("You answered: %s", result) +} + +``` + +
+ diff --git a/_examples/interactive_continue/demo/README.md b/_examples/interactive_continue/demo/README.md new file mode 100644 index 000000000..f4c20783a --- /dev/null +++ b/_examples/interactive_continue/demo/README.md @@ -0,0 +1,18 @@ +# interactive_continue/demo + +![Animation](animation.svg) + +```go +package main + +import ( + "github.com/pterm/pterm" +) + +func main() { + result, _ := pterm.DefaultInteractiveContinue.Show() + pterm.Println() // Blank line + pterm.Info.Printfln("You answered: %s", result) +} + +``` diff --git a/_examples/interactive_continue/demo/animation.svg b/_examples/interactive_continue/demo/animation.svg new file mode 100644 index 000000000..5ec0f59cd --- /dev/null +++ b/_examples/interactive_continue/demo/animation.svg @@ -0,0 +1,10 @@ +Doyouwanttocontinue[Y/n/a/s]: INFO Youanswered:yesRestartinganimation... diff --git a/_examples/interactive_continue/demo/ci.go b/_examples/interactive_continue/demo/ci.go new file mode 100644 index 000000000..2ba46081b --- /dev/null +++ b/_examples/interactive_continue/demo/ci.go @@ -0,0 +1,19 @@ +package main + +import ( + "os" + "time" + + "atomicgo.dev/keyboard" +) + +// ------ Automation for CI ------ +// You can ignore this function, it is used to automatically run the demo and generate the example animation in our CI system. +func init() { + if os.Getenv("CI") == "true" { + go func() { + time.Sleep(time.Second * 2) + keyboard.SimulateKeyPress('y') + }() + } +} diff --git a/_examples/interactive_continue/demo/main.go b/_examples/interactive_continue/demo/main.go new file mode 100644 index 000000000..6684bdf90 --- /dev/null +++ b/_examples/interactive_continue/demo/main.go @@ -0,0 +1,11 @@ +package main + +import ( + "github.com/pterm/pterm" +) + +func main() { + result, _ := pterm.DefaultInteractiveContinue.Show() + pterm.Println() // Blank line + pterm.Info.Printfln("You answered: %s", result) +} diff --git a/docs/README.md b/docs/README.md index 7433dc04a..eb0acfee2 100644 --- a/docs/README.md +++ b/docs/README.md @@ -90,7 +90,7 @@ which features automatic website generation, automatic deployments, a custom CI- |✏ Documentation |To view the official documentation of the latest release, you can go to the automatically generated page of [pkg.go.dev](https://pkg.go.dev/github.com/pterm/pterm#section-documentation) This documentation is very technical and includes every method that can be used in PTerm.
**For an easy start we recommend that you take a look at the [examples section](#-examples).** Here you can see pretty much every feature of PTerm with example code. The animations of the examples are automatically updated as soon as something changes in PTerm.|
- + ### Printers (Components) |Feature|Examples| - |Feature|Examples| @@ -113,7 +113,7 @@ which features automatic website generation, automatic deployments, a custom CI- |---|---|---| |![Jens Lauterbach](https://avatars.githubusercontent.com/u/1292368?s=25)|[@jenslauterbach](https://github.com/jenslauterbach)|25$| -
+ ## 🧪 Examples @@ -1066,6 +1066,31 @@ func boolToText(b bool) string { +### interactive_continue/demo + +![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/interactive_continue/demo/animation.svg) + +
+ +SHOW SOURCE + +```go +package main + +import ( + "github.com/pterm/pterm" +) + +func main() { + result, _ := pterm.DefaultInteractiveContinue.Show() + pterm.Println() // Blank line + pterm.Info.Printfln("You answered: %s", result) +} + +``` + +
+ ### interactive_multiselect/demo ![Animation](https://raw.githubusercontent.com/pterm/pterm/master/_examples/interactive_multiselect/demo/animation.svg) @@ -1608,7 +1633,7 @@ func main() { - + --- > GitHub [@pterm](https://github.com/pterm)  ·  diff --git a/interactive_continue_printer.go b/interactive_continue_printer.go new file mode 100644 index 000000000..994e513b6 --- /dev/null +++ b/interactive_continue_printer.go @@ -0,0 +1,178 @@ +package pterm + +import ( + "fmt" + "os" + "strings" + + "atomicgo.dev/cursor" + "atomicgo.dev/keyboard" + "atomicgo.dev/keyboard/keys" +) + +var ( + // DefaultInteractiveContinue is the default InteractiveContinue printer. + // Pressing "y" will return yes, "n" will return no, "a" returns all and "s" returns stop. + // Pressing enter without typing any letter will return the configured default value (by default set to "yes", the fisrt option). + DefaultInteractiveContinue = InteractiveContinuePrinter{ + DefaultValueIndex: 0, + DefaultText: "Do you want to continue", + TextStyle: &ThemeDefault.PrimaryStyle, + Options: []string{"yes", "no", "all", "stop"}, + OptionsStyle: &ThemeDefault.SuccessMessageStyle, + SuffixStyle: &ThemeDefault.SecondaryStyle, + } +) + +// InteractiveContinuePrinter is a printer for interactive continue prompts. +type InteractiveContinuePrinter struct { + DefaultValueIndex int + DefaultText string + TextStyle *Style + Options []string + OptionsStyle *Style + Handles []string + ShowFullHandles bool + SuffixStyle *Style +} + +// WithDefaultText sets the default text. +func (p InteractiveContinuePrinter) WithDefaultText(text string) *InteractiveContinuePrinter { + p.DefaultText = text + return &p +} + +// WithDefaultValueIndex sets the default value, which will be returned when the user presses enter without typing any letter. +func (p InteractiveContinuePrinter) WithDefaultValueIndex(value int) *InteractiveContinuePrinter { + if value >= len(p.Options) { + panic("Index out of range") + } + p.DefaultValueIndex = value + return &p +} + +// WithDefaultValue sets the default value, which will be returned when the user presses enter without typing any letter. +func (p InteractiveContinuePrinter) WithDefaultValue(value string) *InteractiveContinuePrinter { + for i, o := range p.Options { + if o == value { + p.DefaultValueIndex = i + break + } + } + return &p +} + +// WithTextStyle sets the text style. +func (p InteractiveContinuePrinter) WithTextStyle(style *Style) *InteractiveContinuePrinter { + p.TextStyle = style + return &p +} + +// WithOptions sets the options. +func (p InteractiveContinuePrinter) WithOptions(options []string) *InteractiveContinuePrinter { + p.Options = options + return &p +} + +// WithHandles allows you to customize the short handles for the answers. +func (p InteractiveContinuePrinter) WithHandles(handles []string) *InteractiveContinuePrinter { + if len(handles) != len(p.Options) { + panic("Invalid number of handles") + } + p.Handles = handles + return &p +} + +// WithFullHandles will set ShowFullHandles to true +// this makes the printer display the full options instead their shorthand version. +func (p InteractiveContinuePrinter) WithFullHandles() *InteractiveContinuePrinter { + p.ShowFullHandles = true + return &p +} + +// WithOptionsStyle sets the continue style. +func (p InteractiveContinuePrinter) WithOptionsStyle(style *Style) *InteractiveContinuePrinter { + p.OptionsStyle = style + return &p +} + +// WithSuffixStyle sets the suffix style. +func (p InteractiveContinuePrinter) WithSuffixStyle(style *Style) *InteractiveContinuePrinter { + p.SuffixStyle = style + return &p +} + +// Show shows the continue prompt. +// +// Example: +// result, _ := pterm.DefaultInteractiveContinue.Show("Do you want to apply the changes?") +// pterm.Println(result) +func (p InteractiveContinuePrinter) Show(text ...string) (string, error) { + var result string + + if len(text) == 0 || text[0] == "" { + text = []string{p.DefaultText} + } + + if p.ShowFullHandles { + p.Handles = p.Options + } + + if p.Handles == nil || len(p.Handles) == 0 { + p.Handles = p.getDefaultHandles() + } + + p.TextStyle.Print(text[0] + " " + p.getSuffix() + ": ") + + err := keyboard.Listen(func(keyInfo keys.Key) (stop bool, err error) { + if err != nil { + return false, fmt.Errorf("failed to get key: %w", err) + } + key := keyInfo.Code + char := keyInfo.String() + + switch key { + case keys.RuneKey: + for i, c := range p.Handles { + if p.ShowFullHandles { + c = string([]rune(c)[0]) + } + if char == c || (i == p.DefaultValueIndex && strings.EqualFold(c, char)) { + Println() + result = p.Options[i] + return true, nil + } + } + case keys.Enter: + Println() + result = p.Options[p.DefaultValueIndex] + return true, nil + case keys.CtrlC: + os.Exit(1) + return true, nil + } + return false, nil + }) + cursor.StartOfLine() + return result, err +} + +// getDefaultHandles returns the short hand answers for the continueation prompt +func (p InteractiveContinuePrinter) getDefaultHandles() []string { + handles := []string{} + for _, option := range p.Options { + handles = append(handles, strings.ToLower(string([]rune(option)[0]))) + } + handles[p.DefaultValueIndex] = strings.ToUpper(handles[p.DefaultValueIndex]) + + return handles +} + +// getSuffix returns the continueation prompt suffix +func (p InteractiveContinuePrinter) getSuffix() string { + if p.Handles == nil || len(p.Handles) != len(p.Options) { + panic("Handles not initialized") + } + + return p.SuffixStyle.Sprintf("[%s]", strings.Join(p.Handles, "/")) +} diff --git a/interactive_continue_printer_test.go b/interactive_continue_printer_test.go new file mode 100644 index 000000000..57e21edaf --- /dev/null +++ b/interactive_continue_printer_test.go @@ -0,0 +1,183 @@ +package pterm_test + +import ( + "testing" + + "atomicgo.dev/keyboard" + "atomicgo.dev/keyboard/keys" + "github.com/MarvinJWendt/testza" + + "github.com/pterm/pterm" +) + +func TestInteractiveContinuePrinter_Show_yes(t *testing.T) { + go func() { + keyboard.SimulateKeyPress('y') + }() + result, _ := pterm.DefaultInteractiveContinue.Show() + testza.AssertEqual(t, result, "yes") + go func() { + keyboard.SimulateKeyPress('Y') + }() + result, _ = pterm.DefaultInteractiveContinue.Show() + testza.AssertEqual(t, result, "yes") +} + +func TestInteractiveContinuePrinter_Show_no(t *testing.T) { + go func() { + keyboard.SimulateKeyPress('n') + }() + result, _ := pterm.DefaultInteractiveContinue.Show() + testza.AssertEqual(t, result, "no") +} + +func TestInteractiveContinuePrinter_WithDefaultValueIndes(t *testing.T) { + p := pterm.DefaultInteractiveContinue.WithDefaultValueIndex(1) + testza.AssertEqual(t, p.DefaultValueIndex, 1) +} + +func TestInteractiveContinuePrinter_WithDefaultValue_yes(t *testing.T) { + go func() { + keyboard.SimulateKeyPress(keys.Enter) + }() + p := pterm.DefaultInteractiveContinue.WithDefaultValue("yes") + result, _ := p.Show() + testza.AssertEqual(t, result, "yes") +} + +func TestInteractiveContinuePrinter_WithDefaultValue_no(t *testing.T) { + p := pterm.DefaultInteractiveContinue.WithDefaultValue("no") + go func() { + keyboard.SimulateKeyPress(keys.Enter) + }() + result, _ := p.Show() + testza.AssertEqual(t, result, "no") + go func() { + keyboard.SimulateKeyPress('n') + }() + result, _ = p.Show() + testza.AssertEqual(t, result, "no") + go func() { + keyboard.SimulateKeyPress('N') + }() + result, _ = p.Show() + testza.AssertEqual(t, result, "no") +} + +func TestInteractiveContinuePrinter_WithFullHandles(t *testing.T) { + p := pterm.DefaultInteractiveContinue.WithFullHandles() + testza.AssertTrue(t, p.ShowFullHandles) + go func() { + keyboard.SimulateKeyPress('n') + }() + result, _ := p.Show() + testza.AssertEqual(t, result, "no") +} + +func TestInteractiveContinuePrinter_WithOptionsStyle(t *testing.T) { + style := pterm.NewStyle(pterm.FgRed) + p := pterm.DefaultInteractiveContinue.WithOptionsStyle(style) + testza.AssertEqual(t, p.OptionsStyle, style) +} + +func TestInteractiveContinuePrinter_WithOptions(t *testing.T) { + p := pterm.DefaultInteractiveContinue.WithOptions([]string{"next", "stop", "continue"}) + testza.AssertEqual(t, p.Options, []string{"next", "stop", "continue"}) +} + +func TestInteractiveContinuePrinter_WithHandles(t *testing.T) { + p := pterm.DefaultInteractiveContinue.WithOptions([]string{"yes", "no", "always", "never"}).WithHandles([]string{"y", "n", "a", "N"}) + testza.AssertEqual(t, p.Handles, []string{"y", "n", "a", "N"}) + tests := []struct { + name string + key rune + expected string + }{ + { + name: "Yes", + key: 'y', + expected: "yes", + }, + { + name: "No", + key: 'n', + expected: "no", + }, + { + name: "Always", + key: 'a', + expected: "always", + }, + { + name: "Never", + key: 'N', + expected: "never", + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + go func() { + keyboard.SimulateKeyPress(tc.key) + }() + result, _ := p.Show() + testza.AssertEqual(t, result, tc.expected) + }) + } + p.DefaultValueIndex = 1 + go func() { + keyboard.SimulateKeyPress(keys.Enter) + }() + result, _ := p.Show() + testza.AssertEqual(t, result, "no") +} + +func TestInteractiveContinuePrinter_WithDefaultText(t *testing.T) { + p := pterm.DefaultInteractiveContinue.WithDefaultText("default") + testza.AssertEqual(t, p.DefaultText, "default") +} + +func TestInteractiveContinuePrinter_CustomAnswers(t *testing.T) { + p := pterm.DefaultInteractiveContinue.WithOptions([]string{"next", "stop", "continue"}) + tests := []struct { + name string + key rune + expected string + }{ + { + name: "Next", + key: 'n', + expected: "next", + }, + { + name: "Stop", + key: 's', + expected: "stop", + }, + { + name: "Continue", + key: 'c', + expected: "continue", + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + go func() { + keyboard.SimulateKeyPress(tc.key) + }() + result, _ := p.Show() + testza.AssertEqual(t, result, tc.expected) + }) + } +} + +func TestInteractiveContinuePrinter_WithSuffixStyle(t *testing.T) { + style := pterm.NewStyle(pterm.FgRed) + p := pterm.DefaultInteractiveContinue.WithSuffixStyle(style) + testza.AssertEqual(t, p.SuffixStyle, style) +} + +func TestInteractiveContinuePrinter_WithTextStyle(t *testing.T) { + style := pterm.NewStyle(pterm.FgRed) + p := pterm.DefaultInteractiveContinue.WithTextStyle(style) + testza.AssertEqual(t, p.TextStyle, style) +} From df3cbffd1c4dd964b761cfea435261291efb64ba Mon Sep 17 00:00:00 2001 From: Luis Davim Date: Thu, 15 Sep 2022 10:16:31 +0100 Subject: [PATCH 02/10] refactor: use a map for the options --- interactive_continue_printer.go | 109 +++++++++------------------ interactive_continue_printer_test.go | 65 ++-------------- 2 files changed, 42 insertions(+), 132 deletions(-) diff --git a/interactive_continue_printer.go b/interactive_continue_printer.go index 994e513b6..41a47e17c 100644 --- a/interactive_continue_printer.go +++ b/interactive_continue_printer.go @@ -15,25 +15,23 @@ var ( // Pressing "y" will return yes, "n" will return no, "a" returns all and "s" returns stop. // Pressing enter without typing any letter will return the configured default value (by default set to "yes", the fisrt option). DefaultInteractiveContinue = InteractiveContinuePrinter{ - DefaultValueIndex: 0, - DefaultText: "Do you want to continue", - TextStyle: &ThemeDefault.PrimaryStyle, - Options: []string{"yes", "no", "all", "stop"}, - OptionsStyle: &ThemeDefault.SuccessMessageStyle, - SuffixStyle: &ThemeDefault.SecondaryStyle, + DefaultValue: "Y", + DefaultText: "Do you want to continue", + TextStyle: &ThemeDefault.PrimaryStyle, + Options: map[string]string{"Y": "yes", "n": "no", "a": "allways", "s": "stop"}, + OptionsStyle: &ThemeDefault.SuccessMessageStyle, + SuffixStyle: &ThemeDefault.SecondaryStyle, } ) // InteractiveContinuePrinter is a printer for interactive continue prompts. type InteractiveContinuePrinter struct { - DefaultValueIndex int - DefaultText string - TextStyle *Style - Options []string - OptionsStyle *Style - Handles []string - ShowFullHandles bool - SuffixStyle *Style + DefaultValue string + DefaultText string + TextStyle *Style + Options map[string]string + OptionsStyle *Style + SuffixStyle *Style } // WithDefaultText sets the default text. @@ -43,22 +41,11 @@ func (p InteractiveContinuePrinter) WithDefaultText(text string) *InteractiveCon } // WithDefaultValueIndex sets the default value, which will be returned when the user presses enter without typing any letter. -func (p InteractiveContinuePrinter) WithDefaultValueIndex(value int) *InteractiveContinuePrinter { - if value >= len(p.Options) { - panic("Index out of range") - } - p.DefaultValueIndex = value - return &p -} - -// WithDefaultValue sets the default value, which will be returned when the user presses enter without typing any letter. func (p InteractiveContinuePrinter) WithDefaultValue(value string) *InteractiveContinuePrinter { - for i, o := range p.Options { - if o == value { - p.DefaultValueIndex = i - break - } + if _, ok := p.Options[value]; !ok { + panic("Invalid value: " + value) } + p.DefaultValue = value return &p } @@ -69,27 +56,11 @@ func (p InteractiveContinuePrinter) WithTextStyle(style *Style) *InteractiveCont } // WithOptions sets the options. -func (p InteractiveContinuePrinter) WithOptions(options []string) *InteractiveContinuePrinter { +func (p InteractiveContinuePrinter) WithOptions(options map[string]string) *InteractiveContinuePrinter { p.Options = options return &p } -// WithHandles allows you to customize the short handles for the answers. -func (p InteractiveContinuePrinter) WithHandles(handles []string) *InteractiveContinuePrinter { - if len(handles) != len(p.Options) { - panic("Invalid number of handles") - } - p.Handles = handles - return &p -} - -// WithFullHandles will set ShowFullHandles to true -// this makes the printer display the full options instead their shorthand version. -func (p InteractiveContinuePrinter) WithFullHandles() *InteractiveContinuePrinter { - p.ShowFullHandles = true - return &p -} - // WithOptionsStyle sets the continue style. func (p InteractiveContinuePrinter) WithOptionsStyle(style *Style) *InteractiveContinuePrinter { p.OptionsStyle = style @@ -105,8 +76,9 @@ func (p InteractiveContinuePrinter) WithSuffixStyle(style *Style) *InteractiveCo // Show shows the continue prompt. // // Example: -// result, _ := pterm.DefaultInteractiveContinue.Show("Do you want to apply the changes?") -// pterm.Println(result) +// +// result, _ := pterm.DefaultInteractiveContinue.Show("Do you want to apply the changes?") +// pterm.Println(result) func (p InteractiveContinuePrinter) Show(text ...string) (string, error) { var result string @@ -114,14 +86,6 @@ func (p InteractiveContinuePrinter) Show(text ...string) (string, error) { text = []string{p.DefaultText} } - if p.ShowFullHandles { - p.Handles = p.Options - } - - if p.Handles == nil || len(p.Handles) == 0 { - p.Handles = p.getDefaultHandles() - } - p.TextStyle.Print(text[0] + " " + p.getSuffix() + ": ") err := keyboard.Listen(func(keyInfo keys.Key) (stop bool, err error) { @@ -133,19 +97,17 @@ func (p InteractiveContinuePrinter) Show(text ...string) (string, error) { switch key { case keys.RuneKey: - for i, c := range p.Handles { - if p.ShowFullHandles { - c = string([]rune(c)[0]) - } - if char == c || (i == p.DefaultValueIndex && strings.EqualFold(c, char)) { + for k := range p.Options { + c := string([]rune(k)[0]) + if char == c || (k == p.DefaultValue && strings.EqualFold(c, char)) { Println() - result = p.Options[i] + result = p.Options[k] return true, nil } } case keys.Enter: Println() - result = p.Options[p.DefaultValueIndex] + result = p.Options[p.DefaultValue] return true, nil case keys.CtrlC: os.Exit(1) @@ -157,22 +119,19 @@ func (p InteractiveContinuePrinter) Show(text ...string) (string, error) { return result, err } -// getDefaultHandles returns the short hand answers for the continueation prompt -func (p InteractiveContinuePrinter) getDefaultHandles() []string { - handles := []string{} - for _, option := range p.Options { - handles = append(handles, strings.ToLower(string([]rune(option)[0]))) - } - handles[p.DefaultValueIndex] = strings.ToUpper(handles[p.DefaultValueIndex]) - - return handles -} - // getSuffix returns the continueation prompt suffix func (p InteractiveContinuePrinter) getSuffix() string { - if p.Handles == nil || len(p.Handles) != len(p.Options) { + if p.Options == nil || len(p.Options) == 0 { panic("Handles not initialized") } + var ( + builder strings.Builder + step string + ) + for k, v := range p.Options { + builder.WriteString(fmt.Sprintf("%s%s (%s)", step, k, v)) + step = " / " + } - return p.SuffixStyle.Sprintf("[%s]", strings.Join(p.Handles, "/")) + return p.SuffixStyle.Sprintf("[%s]", builder.String()) } diff --git a/interactive_continue_printer_test.go b/interactive_continue_printer_test.go index 57e21edaf..6b785c19c 100644 --- a/interactive_continue_printer_test.go +++ b/interactive_continue_printer_test.go @@ -31,22 +31,22 @@ func TestInteractiveContinuePrinter_Show_no(t *testing.T) { testza.AssertEqual(t, result, "no") } -func TestInteractiveContinuePrinter_WithDefaultValueIndes(t *testing.T) { - p := pterm.DefaultInteractiveContinue.WithDefaultValueIndex(1) - testza.AssertEqual(t, p.DefaultValueIndex, 1) +func TestInteractiveContinuePrinter_WithDefaultValue(t *testing.T) { + p := pterm.DefaultInteractiveContinue.WithDefaultValue("n") + testza.AssertEqual(t, p.DefaultValue, "n") } func TestInteractiveContinuePrinter_WithDefaultValue_yes(t *testing.T) { go func() { keyboard.SimulateKeyPress(keys.Enter) }() - p := pterm.DefaultInteractiveContinue.WithDefaultValue("yes") + p := pterm.DefaultInteractiveContinue.WithDefaultValue("Y") result, _ := p.Show() testza.AssertEqual(t, result, "yes") } func TestInteractiveContinuePrinter_WithDefaultValue_no(t *testing.T) { - p := pterm.DefaultInteractiveContinue.WithDefaultValue("no") + p := pterm.DefaultInteractiveContinue.WithDefaultValue("n") go func() { keyboard.SimulateKeyPress(keys.Enter) }() @@ -64,16 +64,6 @@ func TestInteractiveContinuePrinter_WithDefaultValue_no(t *testing.T) { testza.AssertEqual(t, result, "no") } -func TestInteractiveContinuePrinter_WithFullHandles(t *testing.T) { - p := pterm.DefaultInteractiveContinue.WithFullHandles() - testza.AssertTrue(t, p.ShowFullHandles) - go func() { - keyboard.SimulateKeyPress('n') - }() - result, _ := p.Show() - testza.AssertEqual(t, result, "no") -} - func TestInteractiveContinuePrinter_WithOptionsStyle(t *testing.T) { style := pterm.NewStyle(pterm.FgRed) p := pterm.DefaultInteractiveContinue.WithOptionsStyle(style) @@ -81,13 +71,8 @@ func TestInteractiveContinuePrinter_WithOptionsStyle(t *testing.T) { } func TestInteractiveContinuePrinter_WithOptions(t *testing.T) { - p := pterm.DefaultInteractiveContinue.WithOptions([]string{"next", "stop", "continue"}) - testza.AssertEqual(t, p.Options, []string{"next", "stop", "continue"}) -} - -func TestInteractiveContinuePrinter_WithHandles(t *testing.T) { - p := pterm.DefaultInteractiveContinue.WithOptions([]string{"yes", "no", "always", "never"}).WithHandles([]string{"y", "n", "a", "N"}) - testza.AssertEqual(t, p.Handles, []string{"y", "n", "a", "N"}) + p := pterm.DefaultInteractiveContinue.WithOptions(map[string]string{"y": "yes", "n": "no", "a": "always", "N": "never"}) + testza.AssertEqual(t, p.Options, map[string]string{"y": "yes", "n": "no", "a": "always", "N": "never"}) tests := []struct { name string key rune @@ -123,7 +108,7 @@ func TestInteractiveContinuePrinter_WithHandles(t *testing.T) { testza.AssertEqual(t, result, tc.expected) }) } - p.DefaultValueIndex = 1 + p.DefaultValue = "n" go func() { keyboard.SimulateKeyPress(keys.Enter) }() @@ -136,40 +121,6 @@ func TestInteractiveContinuePrinter_WithDefaultText(t *testing.T) { testza.AssertEqual(t, p.DefaultText, "default") } -func TestInteractiveContinuePrinter_CustomAnswers(t *testing.T) { - p := pterm.DefaultInteractiveContinue.WithOptions([]string{"next", "stop", "continue"}) - tests := []struct { - name string - key rune - expected string - }{ - { - name: "Next", - key: 'n', - expected: "next", - }, - { - name: "Stop", - key: 's', - expected: "stop", - }, - { - name: "Continue", - key: 'c', - expected: "continue", - }, - } - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - go func() { - keyboard.SimulateKeyPress(tc.key) - }() - result, _ := p.Show() - testza.AssertEqual(t, result, tc.expected) - }) - } -} - func TestInteractiveContinuePrinter_WithSuffixStyle(t *testing.T) { style := pterm.NewStyle(pterm.FgRed) p := pterm.DefaultInteractiveContinue.WithSuffixStyle(style) From 26c2e83cef2e04600bdd162f83b802069336b51b Mon Sep 17 00:00:00 2001 From: Luis Davim Date: Thu, 15 Sep 2022 13:03:40 +0100 Subject: [PATCH 03/10] Revert "refactor: use a map for the options" This reverts commit df3cbffd1c4dd964b761cfea435261291efb64ba. --- interactive_continue_printer.go | 109 ++++++++++++++++++--------- interactive_continue_printer_test.go | 65 ++++++++++++++-- 2 files changed, 132 insertions(+), 42 deletions(-) diff --git a/interactive_continue_printer.go b/interactive_continue_printer.go index 41a47e17c..994e513b6 100644 --- a/interactive_continue_printer.go +++ b/interactive_continue_printer.go @@ -15,23 +15,25 @@ var ( // Pressing "y" will return yes, "n" will return no, "a" returns all and "s" returns stop. // Pressing enter without typing any letter will return the configured default value (by default set to "yes", the fisrt option). DefaultInteractiveContinue = InteractiveContinuePrinter{ - DefaultValue: "Y", - DefaultText: "Do you want to continue", - TextStyle: &ThemeDefault.PrimaryStyle, - Options: map[string]string{"Y": "yes", "n": "no", "a": "allways", "s": "stop"}, - OptionsStyle: &ThemeDefault.SuccessMessageStyle, - SuffixStyle: &ThemeDefault.SecondaryStyle, + DefaultValueIndex: 0, + DefaultText: "Do you want to continue", + TextStyle: &ThemeDefault.PrimaryStyle, + Options: []string{"yes", "no", "all", "stop"}, + OptionsStyle: &ThemeDefault.SuccessMessageStyle, + SuffixStyle: &ThemeDefault.SecondaryStyle, } ) // InteractiveContinuePrinter is a printer for interactive continue prompts. type InteractiveContinuePrinter struct { - DefaultValue string - DefaultText string - TextStyle *Style - Options map[string]string - OptionsStyle *Style - SuffixStyle *Style + DefaultValueIndex int + DefaultText string + TextStyle *Style + Options []string + OptionsStyle *Style + Handles []string + ShowFullHandles bool + SuffixStyle *Style } // WithDefaultText sets the default text. @@ -41,11 +43,22 @@ func (p InteractiveContinuePrinter) WithDefaultText(text string) *InteractiveCon } // WithDefaultValueIndex sets the default value, which will be returned when the user presses enter without typing any letter. +func (p InteractiveContinuePrinter) WithDefaultValueIndex(value int) *InteractiveContinuePrinter { + if value >= len(p.Options) { + panic("Index out of range") + } + p.DefaultValueIndex = value + return &p +} + +// WithDefaultValue sets the default value, which will be returned when the user presses enter without typing any letter. func (p InteractiveContinuePrinter) WithDefaultValue(value string) *InteractiveContinuePrinter { - if _, ok := p.Options[value]; !ok { - panic("Invalid value: " + value) + for i, o := range p.Options { + if o == value { + p.DefaultValueIndex = i + break + } } - p.DefaultValue = value return &p } @@ -56,11 +69,27 @@ func (p InteractiveContinuePrinter) WithTextStyle(style *Style) *InteractiveCont } // WithOptions sets the options. -func (p InteractiveContinuePrinter) WithOptions(options map[string]string) *InteractiveContinuePrinter { +func (p InteractiveContinuePrinter) WithOptions(options []string) *InteractiveContinuePrinter { p.Options = options return &p } +// WithHandles allows you to customize the short handles for the answers. +func (p InteractiveContinuePrinter) WithHandles(handles []string) *InteractiveContinuePrinter { + if len(handles) != len(p.Options) { + panic("Invalid number of handles") + } + p.Handles = handles + return &p +} + +// WithFullHandles will set ShowFullHandles to true +// this makes the printer display the full options instead their shorthand version. +func (p InteractiveContinuePrinter) WithFullHandles() *InteractiveContinuePrinter { + p.ShowFullHandles = true + return &p +} + // WithOptionsStyle sets the continue style. func (p InteractiveContinuePrinter) WithOptionsStyle(style *Style) *InteractiveContinuePrinter { p.OptionsStyle = style @@ -76,9 +105,8 @@ func (p InteractiveContinuePrinter) WithSuffixStyle(style *Style) *InteractiveCo // Show shows the continue prompt. // // Example: -// -// result, _ := pterm.DefaultInteractiveContinue.Show("Do you want to apply the changes?") -// pterm.Println(result) +// result, _ := pterm.DefaultInteractiveContinue.Show("Do you want to apply the changes?") +// pterm.Println(result) func (p InteractiveContinuePrinter) Show(text ...string) (string, error) { var result string @@ -86,6 +114,14 @@ func (p InteractiveContinuePrinter) Show(text ...string) (string, error) { text = []string{p.DefaultText} } + if p.ShowFullHandles { + p.Handles = p.Options + } + + if p.Handles == nil || len(p.Handles) == 0 { + p.Handles = p.getDefaultHandles() + } + p.TextStyle.Print(text[0] + " " + p.getSuffix() + ": ") err := keyboard.Listen(func(keyInfo keys.Key) (stop bool, err error) { @@ -97,17 +133,19 @@ func (p InteractiveContinuePrinter) Show(text ...string) (string, error) { switch key { case keys.RuneKey: - for k := range p.Options { - c := string([]rune(k)[0]) - if char == c || (k == p.DefaultValue && strings.EqualFold(c, char)) { + for i, c := range p.Handles { + if p.ShowFullHandles { + c = string([]rune(c)[0]) + } + if char == c || (i == p.DefaultValueIndex && strings.EqualFold(c, char)) { Println() - result = p.Options[k] + result = p.Options[i] return true, nil } } case keys.Enter: Println() - result = p.Options[p.DefaultValue] + result = p.Options[p.DefaultValueIndex] return true, nil case keys.CtrlC: os.Exit(1) @@ -119,19 +157,22 @@ func (p InteractiveContinuePrinter) Show(text ...string) (string, error) { return result, err } +// getDefaultHandles returns the short hand answers for the continueation prompt +func (p InteractiveContinuePrinter) getDefaultHandles() []string { + handles := []string{} + for _, option := range p.Options { + handles = append(handles, strings.ToLower(string([]rune(option)[0]))) + } + handles[p.DefaultValueIndex] = strings.ToUpper(handles[p.DefaultValueIndex]) + + return handles +} + // getSuffix returns the continueation prompt suffix func (p InteractiveContinuePrinter) getSuffix() string { - if p.Options == nil || len(p.Options) == 0 { + if p.Handles == nil || len(p.Handles) != len(p.Options) { panic("Handles not initialized") } - var ( - builder strings.Builder - step string - ) - for k, v := range p.Options { - builder.WriteString(fmt.Sprintf("%s%s (%s)", step, k, v)) - step = " / " - } - return p.SuffixStyle.Sprintf("[%s]", builder.String()) + return p.SuffixStyle.Sprintf("[%s]", strings.Join(p.Handles, "/")) } diff --git a/interactive_continue_printer_test.go b/interactive_continue_printer_test.go index 6b785c19c..57e21edaf 100644 --- a/interactive_continue_printer_test.go +++ b/interactive_continue_printer_test.go @@ -31,22 +31,22 @@ func TestInteractiveContinuePrinter_Show_no(t *testing.T) { testza.AssertEqual(t, result, "no") } -func TestInteractiveContinuePrinter_WithDefaultValue(t *testing.T) { - p := pterm.DefaultInteractiveContinue.WithDefaultValue("n") - testza.AssertEqual(t, p.DefaultValue, "n") +func TestInteractiveContinuePrinter_WithDefaultValueIndes(t *testing.T) { + p := pterm.DefaultInteractiveContinue.WithDefaultValueIndex(1) + testza.AssertEqual(t, p.DefaultValueIndex, 1) } func TestInteractiveContinuePrinter_WithDefaultValue_yes(t *testing.T) { go func() { keyboard.SimulateKeyPress(keys.Enter) }() - p := pterm.DefaultInteractiveContinue.WithDefaultValue("Y") + p := pterm.DefaultInteractiveContinue.WithDefaultValue("yes") result, _ := p.Show() testza.AssertEqual(t, result, "yes") } func TestInteractiveContinuePrinter_WithDefaultValue_no(t *testing.T) { - p := pterm.DefaultInteractiveContinue.WithDefaultValue("n") + p := pterm.DefaultInteractiveContinue.WithDefaultValue("no") go func() { keyboard.SimulateKeyPress(keys.Enter) }() @@ -64,6 +64,16 @@ func TestInteractiveContinuePrinter_WithDefaultValue_no(t *testing.T) { testza.AssertEqual(t, result, "no") } +func TestInteractiveContinuePrinter_WithFullHandles(t *testing.T) { + p := pterm.DefaultInteractiveContinue.WithFullHandles() + testza.AssertTrue(t, p.ShowFullHandles) + go func() { + keyboard.SimulateKeyPress('n') + }() + result, _ := p.Show() + testza.AssertEqual(t, result, "no") +} + func TestInteractiveContinuePrinter_WithOptionsStyle(t *testing.T) { style := pterm.NewStyle(pterm.FgRed) p := pterm.DefaultInteractiveContinue.WithOptionsStyle(style) @@ -71,8 +81,13 @@ func TestInteractiveContinuePrinter_WithOptionsStyle(t *testing.T) { } func TestInteractiveContinuePrinter_WithOptions(t *testing.T) { - p := pterm.DefaultInteractiveContinue.WithOptions(map[string]string{"y": "yes", "n": "no", "a": "always", "N": "never"}) - testza.AssertEqual(t, p.Options, map[string]string{"y": "yes", "n": "no", "a": "always", "N": "never"}) + p := pterm.DefaultInteractiveContinue.WithOptions([]string{"next", "stop", "continue"}) + testza.AssertEqual(t, p.Options, []string{"next", "stop", "continue"}) +} + +func TestInteractiveContinuePrinter_WithHandles(t *testing.T) { + p := pterm.DefaultInteractiveContinue.WithOptions([]string{"yes", "no", "always", "never"}).WithHandles([]string{"y", "n", "a", "N"}) + testza.AssertEqual(t, p.Handles, []string{"y", "n", "a", "N"}) tests := []struct { name string key rune @@ -108,7 +123,7 @@ func TestInteractiveContinuePrinter_WithOptions(t *testing.T) { testza.AssertEqual(t, result, tc.expected) }) } - p.DefaultValue = "n" + p.DefaultValueIndex = 1 go func() { keyboard.SimulateKeyPress(keys.Enter) }() @@ -121,6 +136,40 @@ func TestInteractiveContinuePrinter_WithDefaultText(t *testing.T) { testza.AssertEqual(t, p.DefaultText, "default") } +func TestInteractiveContinuePrinter_CustomAnswers(t *testing.T) { + p := pterm.DefaultInteractiveContinue.WithOptions([]string{"next", "stop", "continue"}) + tests := []struct { + name string + key rune + expected string + }{ + { + name: "Next", + key: 'n', + expected: "next", + }, + { + name: "Stop", + key: 's', + expected: "stop", + }, + { + name: "Continue", + key: 'c', + expected: "continue", + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + go func() { + keyboard.SimulateKeyPress(tc.key) + }() + result, _ := p.Show() + testza.AssertEqual(t, result, tc.expected) + }) + } +} + func TestInteractiveContinuePrinter_WithSuffixStyle(t *testing.T) { style := pterm.NewStyle(pterm.FgRed) p := pterm.DefaultInteractiveContinue.WithSuffixStyle(style) From 5be8768dd1e5238ee12a25093ca7da395d1854bd Mon Sep 17 00:00:00 2001 From: Luis Davim Date: Thu, 15 Sep 2022 13:25:01 +0100 Subject: [PATCH 04/10] refactor: show full handles by default --- interactive_continue_printer.go | 31 ++++++++++++++++------------ interactive_continue_printer_test.go | 6 +++--- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/interactive_continue_printer.go b/interactive_continue_printer.go index 994e513b6..68a439dae 100644 --- a/interactive_continue_printer.go +++ b/interactive_continue_printer.go @@ -8,6 +8,8 @@ import ( "atomicgo.dev/cursor" "atomicgo.dev/keyboard" "atomicgo.dev/keyboard/keys" + "golang.org/x/text/cases" + "golang.org/x/text/language" ) var ( @@ -32,7 +34,7 @@ type InteractiveContinuePrinter struct { Options []string OptionsStyle *Style Handles []string - ShowFullHandles bool + ShowshortHandles bool SuffixStyle *Style } @@ -83,10 +85,10 @@ func (p InteractiveContinuePrinter) WithHandles(handles []string) *InteractiveCo return &p } -// WithFullHandles will set ShowFullHandles to true -// this makes the printer display the full options instead their shorthand version. -func (p InteractiveContinuePrinter) WithFullHandles() *InteractiveContinuePrinter { - p.ShowFullHandles = true +// WithShortHandles will set ShowShortHandles to true +// this makes the printer display the shorthand options instead their shorthand version. +func (p InteractiveContinuePrinter) WithShortHandles() *InteractiveContinuePrinter { + p.ShowshortHandles = true return &p } @@ -105,8 +107,9 @@ func (p InteractiveContinuePrinter) WithSuffixStyle(style *Style) *InteractiveCo // Show shows the continue prompt. // // Example: -// result, _ := pterm.DefaultInteractiveContinue.Show("Do you want to apply the changes?") -// pterm.Println(result) +// +// result, _ := pterm.DefaultInteractiveContinue.Show("Do you want to apply the changes?") +// pterm.Println(result) func (p InteractiveContinuePrinter) Show(text ...string) (string, error) { var result string @@ -114,12 +117,14 @@ func (p InteractiveContinuePrinter) Show(text ...string) (string, error) { text = []string{p.DefaultText} } - if p.ShowFullHandles { - p.Handles = p.Options + if p.ShowshortHandles { + p.Handles = p.getShortHandles() } if p.Handles == nil || len(p.Handles) == 0 { - p.Handles = p.getDefaultHandles() + p.Handles = make([]string, len(p.Options)) + copy(p.Handles, p.Options) + p.Handles[p.DefaultValueIndex] = cases.Title(language.Und, cases.Compact).String(p.Handles[p.DefaultValueIndex]) } p.TextStyle.Print(text[0] + " " + p.getSuffix() + ": ") @@ -134,7 +139,7 @@ func (p InteractiveContinuePrinter) Show(text ...string) (string, error) { switch key { case keys.RuneKey: for i, c := range p.Handles { - if p.ShowFullHandles { + if !p.ShowshortHandles { c = string([]rune(c)[0]) } if char == c || (i == p.DefaultValueIndex && strings.EqualFold(c, char)) { @@ -157,8 +162,8 @@ func (p InteractiveContinuePrinter) Show(text ...string) (string, error) { return result, err } -// getDefaultHandles returns the short hand answers for the continueation prompt -func (p InteractiveContinuePrinter) getDefaultHandles() []string { +// getShortHandles returns the short hand answers for the continueation prompt +func (p InteractiveContinuePrinter) getShortHandles() []string { handles := []string{} for _, option := range p.Options { handles = append(handles, strings.ToLower(string([]rune(option)[0]))) diff --git a/interactive_continue_printer_test.go b/interactive_continue_printer_test.go index 57e21edaf..1d3fb0758 100644 --- a/interactive_continue_printer_test.go +++ b/interactive_continue_printer_test.go @@ -64,9 +64,9 @@ func TestInteractiveContinuePrinter_WithDefaultValue_no(t *testing.T) { testza.AssertEqual(t, result, "no") } -func TestInteractiveContinuePrinter_WithFullHandles(t *testing.T) { - p := pterm.DefaultInteractiveContinue.WithFullHandles() - testza.AssertTrue(t, p.ShowFullHandles) +func TestInteractiveContinuePrinter_WithShortHandles(t *testing.T) { + p := pterm.DefaultInteractiveContinue.WithShortHandles() + testza.AssertTrue(t, p.ShowshortHandles) go func() { keyboard.SimulateKeyPress('n') }() From 2e8d9c013f21edc765be19855177ca67fda2ea37 Mon Sep 17 00:00:00 2001 From: Luis Davim Date: Thu, 15 Sep 2022 18:15:33 +0100 Subject: [PATCH 05/10] refactor: address renaming PR comments --- interactive_confirm_printer.go | 5 +++-- interactive_continue_printer.go | 20 ++++++++++---------- interactive_continue_printer_test.go | 6 +++--- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/interactive_confirm_printer.go b/interactive_confirm_printer.go index 31dbafd9f..89e60bc9f 100644 --- a/interactive_confirm_printer.go +++ b/interactive_confirm_printer.go @@ -89,8 +89,9 @@ func (p InteractiveConfirmPrinter) WithSuffixStyle(style *Style) *InteractiveCon // Show shows the confirm prompt. // // Example: -// result, _ := pterm.DefaultInteractiveConfirm.Show("Are you sure?") -// pterm.Println(result) +// +// result, _ := pterm.DefaultInteractiveConfirm.Show("Are you sure?") +// pterm.Println(result) func (p InteractiveConfirmPrinter) Show(text ...string) (bool, error) { var result bool diff --git a/interactive_continue_printer.go b/interactive_continue_printer.go index 68a439dae..9c186eda2 100644 --- a/interactive_continue_printer.go +++ b/interactive_continue_printer.go @@ -20,7 +20,7 @@ var ( DefaultValueIndex: 0, DefaultText: "Do you want to continue", TextStyle: &ThemeDefault.PrimaryStyle, - Options: []string{"yes", "no", "all", "stop"}, + Options: []string{"yes", "no", "all", "cancel"}, OptionsStyle: &ThemeDefault.SuccessMessageStyle, SuffixStyle: &ThemeDefault.SecondaryStyle, } @@ -34,7 +34,7 @@ type InteractiveContinuePrinter struct { Options []string OptionsStyle *Style Handles []string - ShowshortHandles bool + ShowShortHandles bool SuffixStyle *Style } @@ -85,10 +85,10 @@ func (p InteractiveContinuePrinter) WithHandles(handles []string) *InteractiveCo return &p } -// WithShortHandles will set ShowShortHandles to true +// WithShowShortHandles will set ShowShortHandles to true // this makes the printer display the shorthand options instead their shorthand version. -func (p InteractiveContinuePrinter) WithShortHandles() *InteractiveContinuePrinter { - p.ShowshortHandles = true +func (p InteractiveContinuePrinter) WithShowShortHandles() *InteractiveContinuePrinter { + p.ShowShortHandles = true return &p } @@ -108,8 +108,8 @@ func (p InteractiveContinuePrinter) WithSuffixStyle(style *Style) *InteractiveCo // // Example: // -// result, _ := pterm.DefaultInteractiveContinue.Show("Do you want to apply the changes?") -// pterm.Println(result) +// result, _ := pterm.DefaultInteractiveContinue.Show("Do you want to apply the changes?") +// pterm.Println(result) func (p InteractiveContinuePrinter) Show(text ...string) (string, error) { var result string @@ -117,7 +117,7 @@ func (p InteractiveContinuePrinter) Show(text ...string) (string, error) { text = []string{p.DefaultText} } - if p.ShowshortHandles { + if p.ShowShortHandles { p.Handles = p.getShortHandles() } @@ -139,7 +139,7 @@ func (p InteractiveContinuePrinter) Show(text ...string) (string, error) { switch key { case keys.RuneKey: for i, c := range p.Handles { - if !p.ShowshortHandles { + if !p.ShowShortHandles { c = string([]rune(c)[0]) } if char == c || (i == p.DefaultValueIndex && strings.EqualFold(c, char)) { @@ -164,7 +164,7 @@ func (p InteractiveContinuePrinter) Show(text ...string) (string, error) { // getShortHandles returns the short hand answers for the continueation prompt func (p InteractiveContinuePrinter) getShortHandles() []string { - handles := []string{} + var handles []string for _, option := range p.Options { handles = append(handles, strings.ToLower(string([]rune(option)[0]))) } diff --git a/interactive_continue_printer_test.go b/interactive_continue_printer_test.go index 1d3fb0758..8686b284c 100644 --- a/interactive_continue_printer_test.go +++ b/interactive_continue_printer_test.go @@ -64,9 +64,9 @@ func TestInteractiveContinuePrinter_WithDefaultValue_no(t *testing.T) { testza.AssertEqual(t, result, "no") } -func TestInteractiveContinuePrinter_WithShortHandles(t *testing.T) { - p := pterm.DefaultInteractiveContinue.WithShortHandles() - testza.AssertTrue(t, p.ShowshortHandles) +func TestInteractiveContinuePrinter_WithShowShortHandles(t *testing.T) { + p := pterm.DefaultInteractiveContinue.WithShowShortHandles() + testza.AssertTrue(t, p.ShowShortHandles) go func() { keyboard.SimulateKeyPress('n') }() From 420124594696d7da360b252a2428298ea40d85a1 Mon Sep 17 00:00:00 2001 From: Luis Davim Date: Thu, 15 Sep 2022 18:22:31 +0100 Subject: [PATCH 06/10] fix: append the selected value to the prompt --- interactive_continue_printer.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/interactive_continue_printer.go b/interactive_continue_printer.go index 9c186eda2..87d659b1c 100644 --- a/interactive_continue_printer.go +++ b/interactive_continue_printer.go @@ -143,12 +143,14 @@ func (p InteractiveContinuePrinter) Show(text ...string) (string, error) { c = string([]rune(c)[0]) } if char == c || (i == p.DefaultValueIndex && strings.EqualFold(c, char)) { + p.OptionsStyle.Print(p.Options[i]) Println() result = p.Options[i] return true, nil } } case keys.Enter: + p.OptionsStyle.Print(p.Options[p.DefaultValueIndex]) Println() result = p.Options[p.DefaultValueIndex] return true, nil From 04b0e457d00daa66195e250852d1cdcb492f8da3 Mon Sep 17 00:00:00 2001 From: Luis Davim Date: Thu, 15 Sep 2022 18:24:41 +0100 Subject: [PATCH 07/10] refactor: comment format --- interactive_confirm_printer.go | 5 ++--- interactive_continue_printer.go | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/interactive_confirm_printer.go b/interactive_confirm_printer.go index 89e60bc9f..dc37e597f 100644 --- a/interactive_confirm_printer.go +++ b/interactive_confirm_printer.go @@ -89,9 +89,8 @@ func (p InteractiveConfirmPrinter) WithSuffixStyle(style *Style) *InteractiveCon // Show shows the confirm prompt. // // Example: -// -// result, _ := pterm.DefaultInteractiveConfirm.Show("Are you sure?") -// pterm.Println(result) +// result, _ := pterm.DefaultInteractiveConfirm.Show("Are you sure?") +// pterm.Println(result) func (p InteractiveConfirmPrinter) Show(text ...string) (bool, error) { var result bool diff --git a/interactive_continue_printer.go b/interactive_continue_printer.go index 87d659b1c..a3e72b7f5 100644 --- a/interactive_continue_printer.go +++ b/interactive_continue_printer.go @@ -107,9 +107,8 @@ func (p InteractiveContinuePrinter) WithSuffixStyle(style *Style) *InteractiveCo // Show shows the continue prompt. // // Example: -// -// result, _ := pterm.DefaultInteractiveContinue.Show("Do you want to apply the changes?") -// pterm.Println(result) +// result, _ := pterm.DefaultInteractiveContinue.Show("Do you want to apply the changes?") +// pterm.Println(result) func (p InteractiveContinuePrinter) Show(text ...string) (string, error) { var result string From 85ecd599dd3199aceb5e65122fd2cba1ddc9803c Mon Sep 17 00:00:00 2001 From: Luis Davim Date: Thu, 15 Sep 2022 18:34:03 +0100 Subject: [PATCH 08/10] refactor: initiazile handles on getSuffix --- interactive_continue_printer.go | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/interactive_continue_printer.go b/interactive_continue_printer.go index a3e72b7f5..7ac2ee5c0 100644 --- a/interactive_continue_printer.go +++ b/interactive_continue_printer.go @@ -116,16 +116,6 @@ func (p InteractiveContinuePrinter) Show(text ...string) (string, error) { text = []string{p.DefaultText} } - if p.ShowShortHandles { - p.Handles = p.getShortHandles() - } - - if p.Handles == nil || len(p.Handles) == 0 { - p.Handles = make([]string, len(p.Options)) - copy(p.Handles, p.Options) - p.Handles[p.DefaultValueIndex] = cases.Title(language.Und, cases.Compact).String(p.Handles[p.DefaultValueIndex]) - } - p.TextStyle.Print(text[0] + " " + p.getSuffix() + ": ") err := keyboard.Listen(func(keyInfo keys.Key) (stop bool, err error) { @@ -174,10 +164,23 @@ func (p InteractiveContinuePrinter) getShortHandles() []string { return handles } +// setDefaultHandles initialises the handles +func (p *InteractiveContinuePrinter) setDefaultHandles() { + if p.ShowShortHandles { + p.Handles = p.getShortHandles() + } + + if p.Handles == nil || len(p.Handles) == 0 { + p.Handles = make([]string, len(p.Options)) + copy(p.Handles, p.Options) + p.Handles[p.DefaultValueIndex] = cases.Title(language.Und, cases.Compact).String(p.Handles[p.DefaultValueIndex]) + } +} + // getSuffix returns the continueation prompt suffix -func (p InteractiveContinuePrinter) getSuffix() string { +func (p *InteractiveContinuePrinter) getSuffix() string { if p.Handles == nil || len(p.Handles) != len(p.Options) { - panic("Handles not initialized") + p.setDefaultHandles() } return p.SuffixStyle.Sprintf("[%s]", strings.Join(p.Handles, "/")) From f3af99e8add894c09173d5cfd5ee6ab4bab0d773 Mon Sep 17 00:00:00 2001 From: Luis Davim Date: Thu, 15 Sep 2022 18:40:25 +0100 Subject: [PATCH 09/10] fix: typo --- interactive_continue_printer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interactive_continue_printer.go b/interactive_continue_printer.go index 7ac2ee5c0..3e7367711 100644 --- a/interactive_continue_printer.go +++ b/interactive_continue_printer.go @@ -177,7 +177,7 @@ func (p *InteractiveContinuePrinter) setDefaultHandles() { } } -// getSuffix returns the continueation prompt suffix +// getSuffix returns the continuation prompt suffix func (p *InteractiveContinuePrinter) getSuffix() string { if p.Handles == nil || len(p.Handles) != len(p.Options) { p.setDefaultHandles() From 0511586fcc3db0074e4d7c5252fbdec2a2587c62 Mon Sep 17 00:00:00 2001 From: Luis Davim Date: Thu, 15 Sep 2022 18:46:31 +0100 Subject: [PATCH 10/10] refactor: ignore invalid custom handles --- interactive_continue_printer.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/interactive_continue_printer.go b/interactive_continue_printer.go index 3e7367711..3ecb03c72 100644 --- a/interactive_continue_printer.go +++ b/interactive_continue_printer.go @@ -79,7 +79,9 @@ func (p InteractiveContinuePrinter) WithOptions(options []string) *InteractiveCo // WithHandles allows you to customize the short handles for the answers. func (p InteractiveContinuePrinter) WithHandles(handles []string) *InteractiveContinuePrinter { if len(handles) != len(p.Options) { - panic("Invalid number of handles") + Warning.Printf("%v is not a valid set of handles", handles) + p.setDefaultHandles() + return &p } p.Handles = handles return &p