diff --git a/completer.go b/completer.go index c60604d..bf7de61 100644 --- a/completer.go +++ b/completer.go @@ -1,6 +1,7 @@ package console import ( + "fmt" "strings" "github.com/carapace-sh/carapace" @@ -12,8 +13,14 @@ import ( "github.com/reeflective/console/internal/line" ) -func (c *Console) complete(input []rune, pos int) readline.Completions { +func (c *Console) complete(input []rune, pos int) (comps readline.Completions) { menu := c.activeMenu() + defer func() { + if r := recover(); r != nil { + comps = readline.CompleteMessage(fmt.Sprintf("completion error: %v", r)) + menu.resetPreRun() + } + }() // Ensure the carapace library is called so that the function // completer.Complete() variable is correctly initialized before use. @@ -56,7 +63,7 @@ func (c *Console) complete(input []rune, pos int) readline.Completions { } // Assign both completions and command/flags/args usage strings. - comps := readline.CompleteRaw(raw) + comps = readline.CompleteRaw(raw) comps = comps.Usage("%s", completions.Usage) comps = c.justifyCommandComps(comps) diff --git a/completer_test.go b/completer_test.go new file mode 100644 index 0000000..f1253ca --- /dev/null +++ b/completer_test.go @@ -0,0 +1,21 @@ +package console + +import "testing" + +func TestCompleteRecoversFromPanic(t *testing.T) { + c := New("test") + menu := c.activeMenu() + menu.Command = nil + + defer func() { + if r := recover(); r != nil { + t.Fatalf("complete panicked: %v", r) + } + }() + + _ = c.complete(nil, 0) + + if menu.Command == nil { + t.Fatal("complete did not restore the command tree after panic") + } +}