Skip to content
Merged

dev #62

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,5 +106,5 @@ Other contributions, as well as bug fixes and reviews are also welcome.
The following is a currently moving list of possible enhancements to be made in order to reach `v1.0`:
- [ ] Ensure to the best extent possible a thread-safe access to the command API.
- [ ] Clearer integration/alignment of the various I/O references between raw readline and commands.
- [ ] Clearer and sane model for asynchronous control/cancel of commands.
- [ ] Clearer and sane model for asynchronous control/cancel of commands (with OnKillRun in cobra)
- [ ] Test suite for most important or risky code paths.
7 changes: 5 additions & 2 deletions completer.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ func (c *Console) complete(line []rune, pos int) readline.Completions {

// Assign both completions and command/flags/args usage strings.
comps := readline.CompleteRaw(raw)
comps = comps.Usage(completions.Usage)
comps = comps.Usage("%s", completions.Usage)
comps = c.justifyCommandComps(comps)

// If any errors arose from the completion call itself.
Expand Down Expand Up @@ -251,8 +251,10 @@ func splitCompWords(input string) (words []string, remainder string, err error)
if len(next) == 0 {
remainder = string(escapeChar)
err = errUnterminatedEscape
return

return words, remainder, err
}

c2, l2 := utf8.DecodeRuneInString(next)
if c2 == '\n' {
input = next[l2:]
Expand All @@ -261,6 +263,7 @@ func splitCompWords(input string) (words []string, remainder string, err error)
}

var word string

word, input, err = splitCompWord(input, &buf)
if err != nil {
return words, word + input, err
Expand Down
1 change: 1 addition & 0 deletions console.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ func New(app string) *Console {

// Defaults
console.EmptyChars = []rune{' ', '\t'}

return console
}

Expand Down
1 change: 1 addition & 0 deletions errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ func (e Err) Error() string {
if len(e.message) > 0 {
return fmt.Sprintf("%s: %s", e.message, e.err.Error())
}

return e.err.Error()
}

Expand Down
2 changes: 2 additions & 0 deletions example/interrupt.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ import (
// readline receives an io.EOF error, which is returned with CtrlD.
func exitCtrlD(c *console.Console) {
reader := bufio.NewReader(os.Stdin)

fmt.Print("Confirm exit (Y/y): ")

text, _ := reader.ReadString('\n')
answer := strings.TrimSpace(text)

Expand Down
2 changes: 2 additions & 0 deletions example/main-commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,7 @@ func mainMenuCommands(app *console.Console) console.Commands {
}

flagMap := make(carapace.ActionMap)

cmd.Flags().VisitAll(func(f *pflag.Flag) {
if f.Name == "file" || strings.Contains(f.Usage, "file") {
flagMap[f.Name] = carapace.ActionFiles()
Expand All @@ -583,6 +584,7 @@ func mainMenuCommands(app *console.Console) console.Commands {
for i := 0; i < 10; i++ {
hosts = append(hosts, fmt.Sprintf("host%d", i))
}

c.PositionalCompletion(carapace.ActionValues(hosts...))
}

Expand Down
5 changes: 2 additions & 3 deletions example/menu.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ func makeClientCommands(app *console.Console) console.Commands {
app.Printf("This message is more important, printing it below the prompt and in every menu")
return
}
menu.TransientPrintf(messages[count])
menu.TransientPrintf("%s", messages[count])
count++
}
}()
Expand Down Expand Up @@ -136,7 +136,7 @@ func makeClientCommands(app *console.Console) console.Commands {
if count == 5 {
return nil
}
menu.TransientPrintf(messages[count] + "\n")
menu.TransientPrintf("%s", messages[count]+"\n")
count++
}
}
Expand Down Expand Up @@ -164,7 +164,6 @@ func setupPrompt(m *console.Menu) {
return fmt.Sprintf(prompt, dir)
}

p.Secondary = func() string { return ">" }
p.Right = func() string {
return "\x1b[1;30m" + time.Now().Format("03:04:05.000") + "\x1b[0m"
}
Expand Down
14 changes: 6 additions & 8 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,25 +1,23 @@
module github.com/reeflective/console

go 1.21
go 1.23.6

require (
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
github.com/reeflective/readline v1.0.15
github.com/reeflective/readline v1.1.1
github.com/rsteube/carapace v0.46.3-0.20231214181515-27e49f3c3b69
github.com/spf13/cobra v1.8.1
github.com/spf13/pflag v1.0.5
golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa
github.com/spf13/pflag v1.0.6
golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac
mvdan.cc/sh/v3 v3.7.0
)

require (
github.com/carapace-sh/carapace v1.1.3 // indirect
github.com/carapace-sh/carapace-shlex v1.0.1 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/rsteube/carapace-shlex v0.1.1 // indirect
golang.org/x/sys v0.24.0 // indirect
golang.org/x/term v0.23.0 // indirect
golang.org/x/sys v0.30.0 // indirect
golang.org/x/term v0.29.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
36 changes: 12 additions & 24 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
github.com/carapace-sh/carapace v1.1.3 h1:EpkXrD7+I8TpvdnwGCrPnaDTS24dO514dyUiG+8QCj4=
github.com/carapace-sh/carapace v1.1.3/go.mod h1:djegtVDi/3duSAqZNU+/nCq7XtDRMRZUb5bW0O/HnEs=
github.com/carapace-sh/carapace-shlex v1.0.1 h1:ww0JCgWpOVuqWG7k3724pJ18Lq8gh5pHQs9j3ojUs1c=
github.com/carapace-sh/carapace-shlex v1.0.1/go.mod h1:lJ4ZsdxytE0wHJ8Ta9S7Qq0XpjgjU0mdfCqiI2FHx7M=
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA=
github.com/frankban/quicktest v1.14.5/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
Expand All @@ -19,12 +15,10 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/reeflective/readline v1.0.13 h1:TeJmYw9B7VRPZWfNExr9QHxL1m0iSicyqBSQIRn39Ss=
github.com/reeflective/readline v1.0.13/go.mod h1:3iOe/qyb2jEy0KqLrNlb/CojBVqxga9ACqz/VU22H6A=
github.com/reeflective/readline v1.0.15 h1:uB/M1sAc2yZGO14Ujgr/imLwQXqGdOhDDWAEHF+MBaE=
github.com/reeflective/readline v1.0.15/go.mod h1:3iOe/qyb2jEy0KqLrNlb/CojBVqxga9ACqz/VU22H6A=
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/reeflective/readline v1.1.1 h1:gplCdkwknFmly5BFBwVFJnMmQ/nWMrQvtkk7HizvGV0=
github.com/reeflective/readline v1.1.1/go.mod h1:CwNkh9BmFBBCSO6mdDaNWb34rOqQsI9eYbxyqvOEazY=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/go-internal v1.10.1-0.20230524175051-ec119421bb97 h1:3RPlVWzZ/PDqmVuf/FKHARG5EMid/tl7cv54Sw/QRVY=
Expand All @@ -34,24 +28,18 @@ github.com/rsteube/carapace v0.46.3-0.20231214181515-27e49f3c3b69/go.mod h1:4ZC5
github.com/rsteube/carapace-shlex v0.1.1 h1:fRQEBBKyYKm4TXUabm4tzH904iFWSmXJl3UZhMfQNYU=
github.com/rsteube/carapace-shlex v0.1.1/go.mod h1:zPw1dOFwvLPKStUy9g2BYKanI6bsQMATzDMYQQybo3o=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
golang.org/x/exp v0.0.0-20231214170342-aacd6d4b4611 h1:qCEDpW1G+vcj3Y7Fy52pEM1AWm3abj8WimGYejI3SC4=
golang.org/x/exp v0.0.0-20231214170342-aacd6d4b4611/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI=
golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa h1:ELnwvuAXPNtPk1TJRuGkI9fDTwym6AYBu0qzT8AcHdI=
golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU=
golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk=
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac h1:l5+whBCLH3iH2ZNHYLbAe58bo7yrN4mVcnkHDYz5vvs=
golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac/go.mod h1:hH+7mtFmImwwcMvScyxUhjuVHR3HGaDPMn9rMSUUbxo=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
Expand Down
18 changes: 15 additions & 3 deletions line.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ func split(input string, hl bool) (words []string, remainder string, err error)
}

input = input[l:]

continue
} else if c == escapeChar {
// Look ahead for escaped newline so we can skip over it
Expand All @@ -99,9 +100,12 @@ func split(input string, hl bool) (words []string, remainder string, err error)
if hl {
remainder = string(escapeChar)
}

err = errUnterminatedEscape
return

return words, remainder, err
}

c2, l2 := utf8.DecodeRuneInString(next)
if c2 == '\n' {
if hl {
Expand All @@ -111,20 +115,25 @@ func split(input string, hl bool) (words []string, remainder string, err error)
words[len(words)-1] += string(c) + string(c2)
}
}

input = next[l2:]

continue
}
}

var word string

word, input, err = splitWord(input, &buf, hl)
if err != nil {
remainder = input
return
return words, remainder, err
}

words = append(words, word)
}
return

return words, remainder, err
}

// splitWord has been modified to return the remainder of the input (the part that has not been
Expand Down Expand Up @@ -185,6 +194,7 @@ escape:
}
input = input[l:]
}

goto raw

single:
Expand Down Expand Up @@ -270,11 +280,13 @@ func trimSpacesMatch(remain []string) (trimmed []string) {

func (c *Console) lineEmpty(line string) bool {
empty := true

for _, r := range line {
if !strings.ContainsRune(string(c.EmptyChars), r) {
empty = false
break
}
}

return empty
}
9 changes: 5 additions & 4 deletions menu.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ func (m *Menu) DeleteHistorySource(name string) {
name = " (" + name + ")"
}

name = fmt.Sprintf("local history%s", name)
name = "local history" + name
}

delete(m.histories, name)
Expand Down Expand Up @@ -169,7 +169,7 @@ func (m *Menu) TransientPrintf(msg string, args ...any) (n int, err error) {
buf := m.out.String()
m.out.Reset()

return m.console.TransientPrintf(buf)
return m.console.TransientPrintf("%s", buf)
}

// Printf prints a message to the console, but only if the current menu
Expand All @@ -196,7 +196,7 @@ func (m *Menu) Printf(msg string, args ...any) (n int, err error) {
buf := m.out.String()
m.out.Reset()

return m.console.Printf(buf)
return m.console.Printf("%s", buf)
}

// CheckIsAvailable checks if a target command is marked as filtered
Expand Down Expand Up @@ -330,7 +330,7 @@ func (m *Menu) defaultHistoryName() string {
name = " (" + m.name + ")"
}

return fmt.Sprintf("local history%s", name)
return "local history" + name
}

func (m *Menu) errorFilteredCommandTemplate(filters []string) string {
Expand All @@ -347,6 +347,7 @@ func tmpl(w io.Writer, text string, data interface{}) error {
t := template.New("top")
t.Funcs(templateFuncs)
template.Must(t.Parse(text))

return t.Execute(w, data)
}

Expand Down
Loading