Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add OnExit function for interactive text inputs #507

Merged
1 change: 0 additions & 1 deletion .golangci.yml
Expand Up @@ -28,7 +28,6 @@ linters:
- gosec
- govet
- ineffassign
- interfacer
- unconvert
- gosimple
- godox
Expand Down
25 changes: 16 additions & 9 deletions interactive_confirm_printer.go
Expand Up @@ -28,14 +28,15 @@ var (

// InteractiveConfirmPrinter is a printer for interactive confirm prompts.
type InteractiveConfirmPrinter struct {
DefaultValue bool
DefaultText string
TextStyle *Style
ConfirmText string
ConfirmStyle *Style
RejectText string
RejectStyle *Style
SuffixStyle *Style
DefaultValue bool
DefaultText string
TextStyle *Style
ConfirmText string
ConfirmStyle *Style
RejectText string
RejectStyle *Style
SuffixStyle *Style
OnInterruptFunc func()
}

// WithDefaultText sets the default text.
Expand Down Expand Up @@ -86,6 +87,12 @@ func (p InteractiveConfirmPrinter) WithSuffixStyle(style *Style) *InteractiveCon
return &p
}

// OnInterrupt sets the function to execute on exit of the input reader
func (p InteractiveConfirmPrinter) WithOnInterruptFunc(exitFunc func()) *InteractiveConfirmPrinter {
p.OnInterruptFunc = exitFunc
return &p
}

// Show shows the confirm prompt.
//
// Example:
Expand All @@ -95,7 +102,7 @@ func (p InteractiveConfirmPrinter) WithSuffixStyle(style *Style) *InteractiveCon
func (p InteractiveConfirmPrinter) Show(text ...string) (bool, error) {
// should be the first defer statement to make sure it is executed last
// and all the needed cleanup can be done before
cancel, exit := internal.NewCancelationSignal()
cancel, exit := internal.NewCancelationSignal(p.OnInterruptFunc)
defer exit()

var result bool
Expand Down
12 changes: 12 additions & 0 deletions interactive_confirm_printer_test.go
@@ -1,6 +1,7 @@
package pterm_test

import (
"reflect"
"testing"

"atomicgo.dev/keyboard"
Expand Down Expand Up @@ -126,3 +127,14 @@ func TestInteractiveConfirmPrinter_WithTextStyle(t *testing.T) {
p := pterm.DefaultInteractiveConfirm.WithTextStyle(style)
testza.AssertEqual(t, p.TextStyle, style)
}

func TestInteractiveConfirmPrinter_WithOnInterruptFunc(t *testing.T) {
// OnInterrupt function defaults to nil
pd := pterm.InteractiveConfirmPrinter{}
testza.AssertNil(t, pd.OnInterruptFunc)

// Verify OnInterrupt is set
exitfunc := func() {}
p := pterm.DefaultInteractiveConfirm.WithOnInterruptFunc(exitfunc)
testza.AssertEqual(t, reflect.ValueOf(p.OnInterruptFunc).Pointer(), reflect.ValueOf(exitfunc).Pointer())
}
29 changes: 18 additions & 11 deletions interactive_multiselect_printer.go
Expand Up @@ -32,16 +32,17 @@ var (

// InteractiveMultiselectPrinter is a printer for interactive multiselect menus.
type InteractiveMultiselectPrinter struct {
DefaultText string
TextStyle *Style
Options []string
OptionStyle *Style
DefaultOptions []string
MaxHeight int
Selector string
SelectorStyle *Style
Filter bool
Checkmark *Checkmark
DefaultText string
TextStyle *Style
Options []string
OptionStyle *Style
DefaultOptions []string
MaxHeight int
Selector string
SelectorStyle *Style
Filter bool
Checkmark *Checkmark
OnInterruptFunc func()

selectedOption int
selectedOptions []int
Expand Down Expand Up @@ -109,11 +110,17 @@ func (p InteractiveMultiselectPrinter) WithCheckmark(checkmark *Checkmark) *Inte
return &p
}

// OnInterrupt sets the function to execute on exit of the input reader
func (p InteractiveMultiselectPrinter) WithOnInterruptFunc(exitFunc func()) *InteractiveMultiselectPrinter {
p.OnInterruptFunc = exitFunc
return &p
}

// Show shows the interactive multiselect menu and returns the selected entry.
func (p *InteractiveMultiselectPrinter) Show(text ...string) ([]string, error) {
// should be the first defer statement to make sure it is executed last
// and all the needed cleanup can be done before
cancel, exit := internal.NewCancelationSignal()
cancel, exit := internal.NewCancelationSignal(p.OnInterruptFunc)
defer exit()

if len(text) == 0 || Sprint(text[0]) == "" {
Expand Down
12 changes: 12 additions & 0 deletions interactive_multiselect_printer_test.go
@@ -1,6 +1,7 @@
package pterm_test

import (
"reflect"
"testing"

"atomicgo.dev/keyboard"
Expand Down Expand Up @@ -66,3 +67,14 @@ func TestInteractiveMultiselectPrinter_WithCheckmark(t *testing.T) {
p := pterm.DefaultInteractiveMultiselect.WithCheckmark(&pterm.Checkmark{Checked: "+", Unchecked: "-"}).WithOptions([]string{"a", "b", "c"})
testza.AssertEqual(t, p.Checkmark, &pterm.Checkmark{Checked: "+", Unchecked: "-"})
}

func TestInteractiveMultiselectPrinter_WithOnInterruptFunc(t *testing.T) {
// OnInterrupt function defaults to nil
pd := pterm.InteractiveMultiselectPrinter{}
testza.AssertNil(t, pd.OnInterruptFunc)

// Verify OnInterrupt is set
exitfunc := func() {}
p := pterm.DefaultInteractiveMultiselect.WithOnInterruptFunc(exitfunc)
testza.AssertEqual(t, reflect.ValueOf(p.OnInterruptFunc).Pointer(), reflect.ValueOf(exitfunc).Pointer())
}
25 changes: 16 additions & 9 deletions interactive_select_printer.go
Expand Up @@ -28,14 +28,15 @@ var (

// InteractiveSelectPrinter is a printer for interactive select menus.
type InteractiveSelectPrinter struct {
TextStyle *Style
DefaultText string
Options []string
OptionStyle *Style
DefaultOption string
MaxHeight int
Selector string
SelectorStyle *Style
TextStyle *Style
DefaultText string
Options []string
OptionStyle *Style
DefaultOption string
MaxHeight int
Selector string
SelectorStyle *Style
OnInterruptFunc func()

selectedOption int
result string
Expand Down Expand Up @@ -71,11 +72,17 @@ func (p InteractiveSelectPrinter) WithMaxHeight(maxHeight int) *InteractiveSelec
return &p
}

// OnInterrupt sets the function to execute on exit of the input reader
func (p InteractiveSelectPrinter) WithOnInterruptFunc(exitFunc func()) *InteractiveSelectPrinter {
p.OnInterruptFunc = exitFunc
return &p
}

// Show shows the interactive select menu and returns the selected entry.
func (p *InteractiveSelectPrinter) Show(text ...string) (string, error) {
// should be the first defer statement to make sure it is executed last
// and all the needed cleanup can be done before
cancel, exit := internal.NewCancelationSignal()
cancel, exit := internal.NewCancelationSignal(p.OnInterruptFunc)
defer exit()

if len(text) == 0 || Sprint(text[0]) == "" {
Expand Down
12 changes: 12 additions & 0 deletions interactive_select_printer_test.go
@@ -1,6 +1,7 @@
package pterm_test

import (
"reflect"
"testing"

"atomicgo.dev/keyboard"
Expand Down Expand Up @@ -49,3 +50,14 @@ func TestInteractiveSelectPrinter_WithMaxHeight(t *testing.T) {
p := pterm.DefaultInteractiveSelect.WithMaxHeight(1337)
testza.AssertEqual(t, p.MaxHeight, 1337)
}

func TestInteractiveSelectPrinter_WithOnInterruptFunc(t *testing.T) {
// OnInterrupt function defaults to nil
pd := pterm.InteractiveSelectPrinter{}
testza.AssertNil(t, pd.OnInterruptFunc)

// Verify OnInterrupt is set
exitfunc := func() {}
p := pterm.DefaultInteractiveSelect.WithOnInterruptFunc(exitfunc)
testza.AssertEqual(t, reflect.ValueOf(p.OnInterruptFunc).Pointer(), reflect.ValueOf(exitfunc).Pointer())
}
17 changes: 12 additions & 5 deletions interactive_textinput_printer.go
Expand Up @@ -21,10 +21,11 @@ var (

// InteractiveTextInputPrinter is a printer for interactive select menus.
type InteractiveTextInputPrinter struct {
TextStyle *Style
DefaultText string
MultiLine bool
Mask string
TextStyle *Style
DefaultText string
MultiLine bool
Mask string
OnInterruptFunc func()

input []string
cursorXPos int
Expand Down Expand Up @@ -56,11 +57,17 @@ func (p InteractiveTextInputPrinter) WithMask(mask string) *InteractiveTextInput
return &p
}

// OnInterrupt sets the function to execute on exit of the input reader
func (p InteractiveTextInputPrinter) WithOnInterruptFunc(exitFunc func()) *InteractiveTextInputPrinter {
p.OnInterruptFunc = exitFunc
return &p
}

// Show shows the interactive select menu and returns the selected entry.
func (p InteractiveTextInputPrinter) Show(text ...string) (string, error) {
// should be the first defer statement to make sure it is executed last
// and all the needed cleanup can be done before
cancel, exit := internal.NewCancelationSignal()
cancel, exit := internal.NewCancelationSignal(p.OnInterruptFunc)
defer exit()

var areaText string
Expand Down
12 changes: 12 additions & 0 deletions interactive_textinput_printer_test.go
@@ -1,6 +1,7 @@
package pterm_test

import (
"reflect"
"testing"

"atomicgo.dev/keyboard"
Expand Down Expand Up @@ -41,3 +42,14 @@ func TestInteractiveTextInputPrinter_WithMask(t *testing.T) {
result, _ := pterm.DefaultInteractiveTextInput.WithMask("*").Show()
testza.AssertEqual(t, result, "abc")
}

func TestInteractiveTextInputPrinter_WithOnInterruptFunc(t *testing.T) {
// OnInterrupt function defaults to nil
pd := pterm.InteractiveTextInputPrinter{}
testza.AssertNil(t, pd.OnInterruptFunc)

// Verify OnInterrupt is set
exitfunc := func() {}
p := pterm.DefaultInteractiveTextInput.WithOnInterruptFunc(exitfunc)
testza.AssertEqual(t, reflect.ValueOf(p.OnInterruptFunc).Pointer(), reflect.ValueOf(exitfunc).Pointer())
}
11 changes: 4 additions & 7 deletions internal/cancelation_signal.go
@@ -1,19 +1,16 @@
package internal

import (
"os"
)

// NewCancelationSignal for keeping track of a cancelation
func NewCancelationSignal() (func(), func()) {
func NewCancelationSignal(interruptFunc func()) (func(), func()) {
canceled := false

cancel := func() {
canceled = true
}

exit := func() {
if canceled {
os.Exit(1)
if canceled && interruptFunc != nil {
interruptFunc()
}
}

Expand Down