diff --git a/commands/readline/bind.go b/commands/readline/bind.go index 2af32de..dca0eb2 100644 --- a/commands/readline/bind.go +++ b/commands/readline/bind.go @@ -85,8 +85,8 @@ Exporting binds: } var name string - var reeflective = "reeflective" - var buf = &cfgBuilder{buf: &strings.Builder{}} + reeflective := "reeflective" + buf := &cfgBuilder{buf: &strings.Builder{}} // First prepare the branching strings for any // needed conditionals (App, Lib, keymap, etc) diff --git a/commands/readline/completers.go b/commands/readline/completers.go index 135a121..d199852 100644 --- a/commands/readline/completers.go +++ b/commands/readline/completers.go @@ -20,7 +20,6 @@ package readline import ( "fmt" - "strings" "github.com/rsteube/carapace" "github.com/spf13/cobra" @@ -84,7 +83,7 @@ func completeBindSequences(sh *readline.Shell, cmd *cobra.Command) carapace.Acti // Look for the current argument and check whether or not it's quoted. // If yes, only include quotes at the end of the inserted value. // If no quotes, include them in both. - if strings.HasPrefix(ctx.Value, "\"") || ctx.Value == "" { + if ctx.Value == "" { completions = completions.Prefix("\"") } diff --git a/commands/readline/export.go b/commands/readline/export.go index 67f70e9..25ff043 100644 --- a/commands/readline/export.go +++ b/commands/readline/export.go @@ -49,7 +49,7 @@ func listVars(shell *readline.Shell, buf *cfgBuilder, cmd *cobra.Command) { return } - var variables = make([]string, len(shell.Config.Vars)) + variables := make([]string, len(shell.Config.Vars)) for variable := range shell.Config.Vars { variables = append(variables, variable) @@ -191,7 +191,7 @@ func listBinds(shell *readline.Shell, buf *cfgBuilder, cmd *cobra.Command, keyma } // Get all the commands, used to sort the displays. - var commands = make([]string, len(shell.Keymap.Commands())) + commands := make([]string, len(shell.Keymap.Commands())) for command := range shell.Keymap.Commands() { commands = append(commands, command) } @@ -276,7 +276,7 @@ func listBindsRC(shell *readline.Shell, buf *cfgBuilder, cmd *cobra.Command, key } // Get all the commands, used to sort the displays. - var commands = make([]string, len(shell.Keymap.Commands())) + commands := make([]string, len(shell.Keymap.Commands())) for command := range shell.Keymap.Commands() { commands = append(commands, command) } diff --git a/completer.go b/completer.go index b0554e9..4babc24 100644 --- a/completer.go +++ b/completer.go @@ -10,8 +10,8 @@ import ( "unicode" "unicode/utf8" - "github.com/rsteube/carapace" "github.com/rsteube/carapace/pkg/style" + completer "github.com/rsteube/carapace/pkg/x" "github.com/rsteube/carapace/pkg/xdg" "github.com/reeflective/readline" @@ -26,15 +26,16 @@ func (c *Console) complete(line []rune, pos int) readline.Completions { // Prepare arguments for the carapace completer // (we currently need those two dummies for avoiding a panic). - args = append([]string{"examples", "_carapace"}, args...) + args = append([]string{c.name, "_carapace"}, args...) // Call the completer with our current command context. - values, meta := carapace.Complete(menu.Command, args, c.completeCommands(menu)) + completions, err := completer.Complete(menu.Command, args...) - // Tranfer all completion results to our readline shell completions. - raw := make([]readline.Completion, len(values)) + // The completions are never nil: fill out our own object + // with everything it contains, regardless of errors. + raw := make([]readline.Completion, len(completions.Values)) - for idx, val := range values { + for idx, val := range completions.Values.Decolor() { raw[idx] = readline.Completion{ Value: unescapeValue(prefixComp, prefixLine, val.Value), Display: val.Display, @@ -42,45 +43,44 @@ func (c *Console) complete(line []rune, pos int) readline.Completions { Style: val.Style, Tag: val.Tag, } + + if !completions.Nospace.Matches(val.Value) { + raw[idx].Value = val.Value + " " + } } // Assign both completions and command/flags/args usage strings. comps := readline.CompleteRaw(raw) - comps = comps.Usage(meta.Usage) + comps = comps.Usage(completions.Usage) comps = c.justifyCommandComps(comps) - // Suffix matchers for the completions if any. - suffixes, err := meta.Nospace.MarshalJSON() - if len(suffixes) > 0 && err == nil { - comps = comps.NoSpace([]rune(string(suffixes))...) + // If any errors arose from the completion call itself. + if err != nil { + comps = readline.CompleteMessage("failed to load config: " + err.Error()) } - // Other status/error messages - for _, msg := range meta.Messages.Get() { + // Completion status/errors + for _, msg := range completions.Messages.Get() { comps = comps.Merge(readline.CompleteMessage(msg)) } + // Suffix matchers for the completions if any. + suffixes, err := completions.Nospace.MarshalJSON() + if len(suffixes) > 0 && err == nil { + comps = comps.NoSpace([]rune(string(suffixes))...) + } + // If we have a quote/escape sequence unaccounted // for in our completions, add it to all of them. comps = comps.Prefix(prefixComp) comps.PREFIX = prefixLine - return comps -} + // Finally, reset our command tree for the next call. + completer.ClearStorage() + menu.resetPreRun() + menu.hideFilteredCommands(menu.Command) -// Regenerate commands and apply any filters. -func (c *Console) completeCommands(menu *Menu) func() { - commands := func() { - cmd := menu.Command - if menu.cmds != nil { - cmd = menu.cmds() - } - - menu.resetPreRun() - menu.hideFilteredCommands(cmd) - } - - return commands + return comps } func (c *Console) justifyCommandComps(comps readline.Completions) readline.Completions { diff --git a/console.go b/console.go index 619dd69..795363a 100644 --- a/console.go +++ b/console.go @@ -81,10 +81,11 @@ func New(app string) *Console { console.shell.History.Add(name, defaultMenu.histories[name]) } - // Command completion, syntax highlighting, multiline callbacks, etc. + // Syntax highlighting, multiline callbacks, etc. console.shell.AcceptMultiline = console.acceptMultiline console.shell.SyntaxHighlighter = console.highlightSyntax + // Completion console.shell.Completer = console.complete console.defaultStyleConfig() diff --git a/example/README.md b/example/README.md index 7307712..e21a92b 100644 --- a/example/README.md +++ b/example/README.md @@ -10,12 +10,15 @@ This directory contains an example console application containing: ## Installing Assuming that you have a working Go toolchain: ```bash -# Clone the repository and go to example directory -git clone https://github.com/reeflective/console && cd console/example - -# Build and run the console -go build . && ./example +go install github.com/reeflective/console/example ``` + + + + + + + ## Directories and files The files/directories below are listed in the order in which a user would want to diff --git a/example/main-commands.go b/example/main-commands.go index 3da9c18..5b5fde5 100644 --- a/example/main-commands.go +++ b/example/main-commands.go @@ -6,11 +6,12 @@ import ( "os/exec" "strings" - "github.com/reeflective/console" - "github.com/reeflective/console/commands/readline" "github.com/rsteube/carapace" "github.com/spf13/cobra" "github.com/spf13/pflag" + + "github.com/reeflective/console" + "github.com/reeflective/console/commands/readline" ) // mainMenuCommands - Create the commands for the main menu. diff --git a/example/menu.go b/example/menu.go index 0559151..59f8cec 100644 --- a/example/menu.go +++ b/example/menu.go @@ -9,8 +9,9 @@ import ( "path/filepath" "time" - "github.com/reeflective/console" "github.com/spf13/cobra" + + "github.com/reeflective/console" ) // In here we create some menus which hold different command trees. diff --git a/go.mod b/go.mod index cc9d752..8e8f9b0 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.21 require ( github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 github.com/reeflective/readline v1.0.12 - github.com/rsteube/carapace v0.45.0 + github.com/rsteube/carapace v0.46.3-0.20231209231049-19872eee702a github.com/spf13/cobra v1.8.0 github.com/spf13/pflag v1.0.5 golang.org/x/exp v0.0.0-20231127185646-65229373498e @@ -15,10 +15,11 @@ require ( require ( github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/rivo/uniseg v0.4.4 // indirect + github.com/rsteube/carapace-shlex v0.1.1 // indirect golang.org/x/sys v0.15.0 // indirect golang.org/x/term v0.15.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) -replace github.com/rsteube/carapace v0.45.0 => github.com/reeflective/carapace v0.25.2-0.20230816093630-a30f5184fa0d +// replace github.com/rsteube/carapace v0.45.0 => github.com/reeflective/carapace v0.25.2-0.20230816093630-a30f5184fa0d diff --git a/go.sum b/go.sum index b87aa95..b386a12 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,3 @@ -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.3/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= @@ -15,18 +14,17 @@ 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/carapace v0.25.2-0.20230816093630-a30f5184fa0d h1:RK0OaQs+3CMJnfXc5SNEg+Kbu4A2AVljPuG5/HcaUdM= -github.com/reeflective/carapace v0.25.2-0.20230816093630-a30f5184fa0d/go.mod h1:jkLt41Ne2TD2xPuMdX/2O05Smhy8vMgG7O2TYvC0yOc= -github.com/reeflective/readline v1.0.11 h1:4+aiebj7a89hTRJOMM98H+md1Kxu+v1XkfdCs0n6odQ= -github.com/reeflective/readline v1.0.11/go.mod h1:mcD0HxNVJVteVwDm9caXKg52nQACVyfh8EyuBmgVlzY= github.com/reeflective/readline v1.0.12 h1:QPhnlGCqWXR4iZvApU5RJ5Bo3vIaVAW6ICBJ8F8QZII= github.com/reeflective/readline v1.0.12/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/rogpeppe/go-internal v1.10.1-0.20230524175051-ec119421bb97 h1:3RPlVWzZ/PDqmVuf/FKHARG5EMid/tl7cv54Sw/QRVY= github.com/rogpeppe/go-internal v1.10.1-0.20230524175051-ec119421bb97/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/rsteube/carapace v0.46.3-0.20231209231049-19872eee702a h1:DotkLg9BRx7a9/QEICipXFFKxwWISkW8e0HhloIPwno= +github.com/rsteube/carapace v0.46.3-0.20231209231049-19872eee702a/go.mod h1:4ZC5bulItu9t9sZ5yPcHgPREd8rPf274Q732n+wfl/o= +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.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=