Complete factoring of comp to own modules

pull/85/head v0.8.0
rwxrob 2 years ago
parent 499a9d1f83
commit c81f800235
No known key found for this signature in database
GPG Key ID: 2B9111F33082AE77

@ -378,8 +378,13 @@ want the specific reasons.
builtins themselves can obviously be used immediately and has a much
smaller chance of changing in the future.
* **Force `bonzai` Dependency in CacheMap, Conf, and Completer**
## Style Guidelines
* Use "deciduous tree" emoji 🌳 to mark Bonzai stuff
* Everything through `go fmt` or equiv, no exceptions
* In Vim `set textwidth=72` (not 80 to line numbers fit)
* Use `/* */` for package documentation comment, `//` elsewhere
@ -388,7 +393,6 @@ want the specific reasons.
* Package globals that will be used a lot can be single capital
* Must be good reason to use more than 4 character pkg name
* Avoid unnecessary comments
* Use "deciduous tree" emoji 🌳 to mark Bonzai branches and commands
## Printing, Formatting, and Emphasis

@ -55,10 +55,13 @@ type CacheMap interface {
OverWrite(with string) error // safely replace all cache
}
// Completer defines a function to complete the given leaf Command with
// the provided arguments, if any. Completer functions must never be
// passed a nil Command or nil as the args slice. See comp.Standard.
type Completer func(leaf Command, args ...string) []string
// Completer specifies a struct with a Complete function that will
// complete the given bonzai.Command with the given arguments.
// The Complete function must never panic and always return at least an
// empty slice of strings.
type Completer interface {
Complete(x Command, args ...string) []string
}
// Section is a section from the Other attribute.
type Section interface {
@ -91,7 +94,7 @@ type Command interface {
GetHidden() []string
GetOther() []Section
GetOtherTitles() []string
GetCompleter() Completer
GetComp() Completer
GetCaller() Command
GetMinArgs() int
GetMinParm() int

@ -1 +0,0 @@
# Future Home of Other Completion Methods

@ -1,65 +0,0 @@
// Copyright 2022 Robert S. Muhlestein.
// SPDX-License-Identifier: Apache-2.0
package comp
import (
"path/filepath"
"strings"
"github.com/rwxrob/bonzai"
"github.com/rwxrob/fn/filt"
"github.com/rwxrob/fs"
"github.com/rwxrob/fs/dir"
)
// File returns all file names for the directory and file prefix passed.
// If nothing is passed assumes the current working directory. This
// completer is roughly based on the behavior of the bash shell with
// forward slashes as separators and escaped spaces. By using this
// completer (instead of the shell) the command line interface remains
// consistent across all runtimes. Note that unlike bash completion no
// indication of the type of file is provided.
func File(x bonzai.Command, args ...string) []string {
if len(args) > 1 {
return []string{}
}
if args == nil || (len(args) > 0 && args[0] == "") {
return dir.EntriesWithSlash(".")
}
// catch edge cases
if len(args) == 0 {
if x != nil {
return []string{x.GetName()} // will add tailing space
}
return dir.EntriesWithSlash("")
}
first := strings.TrimRight(args[0], string(filepath.Separator))
d, pre := filepath.Split(first)
if d == "" {
list := filt.HasPrefix(dir.Entries("."), pre)
if len(list) == 1 && fs.IsDir(list[0]) {
return dir.EntriesWithSlash(list[0])
}
return dir.AddSlash(list)
}
for {
list := filt.BaseHasPrefix(dir.Entries(d), pre)
if len(list) > 1 {
return dir.AddSlash(list)
}
if fs.IsDir(list[0]) {
d = list[0]
continue
}
return dir.AddSlash(list)
}
return []string{}
}

@ -1,75 +0,0 @@
// Copyright 2022 Robert S. Muhlestein.
// SPDX-License-Identifier: Apache-2.0
package comp_test
import (
"fmt"
"os"
"github.com/rwxrob/bonzai/comp"
)
func ExampleFile_nil() {
os.Chdir("testdata/file")
defer os.Chdir("../..")
fmt.Println(comp.File(nil))
fmt.Println(comp.File(nil, ""))
//Output:
// [bar/ blah/ come/ foo.go]
// [bar/ blah/ come/ foo.go]
}
func ExampleFile_pre_File() {
os.Chdir("testdata/file")
defer os.Chdir("../..")
fmt.Println(comp.File(nil, "fo"))
//Output:
// [foo.go]
}
func ExampleFile_pre_Dir_Only() {
os.Chdir("testdata/file")
defer os.Chdir("../..")
fmt.Println(comp.File(nil, "bar"))
fmt.Println(comp.File(nil, "bar/"))
//Output:
// [bar/foo.go bar/other]
// [bar/foo.go bar/other]
}
func ExampleFile_pre_Dir_or_Files() {
os.Chdir("testdata/file")
defer os.Chdir("../..")
fmt.Println(comp.File(nil, "b"))
//Output:
// [bar/ blah/]
}
func ExampleFile_pre_Dir_Specific() {
os.Chdir("testdata/file")
defer os.Chdir("../..")
fmt.Println(comp.File(nil, "blah"))
//Output:
// [blah/file1 blah/file2]
}
func ExampleFile_pre_Dir_Recurse() {
os.Chdir("testdata/file")
defer os.Chdir("../..")
fmt.Println(comp.File(nil, "com"))
fmt.Println(comp.File(nil, "come/"))
//Output:
// [come/one]
// [come/one]
}
func ExampleFile_dir_File() {
os.Chdir("testdata/file")
defer os.Chdir("../..")
fmt.Println(comp.File(nil, "bar/fo"))
fmt.Println(comp.File(nil, "bar/foo"))
//Output:
// [bar/foo.go]
// [bar/foo.go]
}

@ -1,48 +0,0 @@
// Copyright 2022 Robert S. Muhlestein.
// SPDX-License-Identifier: Apache-2.0
package comp
import (
"github.com/rwxrob/bonzai"
"github.com/rwxrob/fn/filt"
"github.com/rwxrob/structs/set/text/set"
)
// Standard completion is resolved as follows:
//
// 1. If leaf has Completer function, delegate to it
//
// 2. If leaf has no arguments, return all Commands and Params
//
// 3. If first argument is the name of a Command return it only even
// if in the Hidden list
//
// 4. Otherwise, return every Command or Param that is not in the
// Hidden list and HasPrefix matching the first arg
//
// See bonzai.Completer.
func Standard(x bonzai.Command, args ...string) []string {
// if has completer, delegate
if c := x.GetCompleter(); c != nil {
return c(x, args...)
}
// not sure we've completed the command name itself yet
if len(args) == 0 {
return []string{x.GetName()}
}
// build list of visible commands and params
list := []string{}
list = append(list, x.GetCommandNames()...)
list = append(list, x.GetParams()...)
list = set.Minus[string, string](list, x.GetHidden())
if len(args) == 0 {
return list
}
return filt.HasPrefix(list, args[0])
}

@ -1,55 +0,0 @@
// Copyright 2022 Robert S. Muhlestein.
// SPDX-License-Identifier: Apache-2.0
package comp_test
import (
"fmt"
"github.com/rwxrob/bonzai"
"github.com/rwxrob/bonzai/comp"
Z "github.com/rwxrob/bonzai/z"
"github.com/rwxrob/fn/filt"
)
func ExampleStandard() {
foo := new(Z.Cmd)
foo.Params = []string{"box"}
foo.Add("bar")
foo.Add("blah")
// if no args, we have to assume the command isn't finished yet
fmt.Println(comp.Standard(foo))
// we know it's not a command, but no prefix just yet
// (usually this is when a space has been added after the command)
fmt.Println(comp.Standard(foo, ""))
// everything that begins with a (nothing)
fmt.Println(comp.Standard(foo, `a`))
// everything that begins with b (which is everything)
fmt.Println(comp.Standard(foo, `b`))
// everything that begins with bl (just blah)
fmt.Println(comp.Standard(foo, `bl`))
// give own completer for days of the week
foo.Completer = func(cmd bonzai.Command, args ...string) []string {
list := []string{"mon", "tue", "wed", "thu", "fri", "sat", "sun"}
if len(args) == 0 {
return list
}
return filt.HasPrefix(list, args[0])
}
fmt.Println(comp.Standard(foo, `t`))
//Output:
// []
// [bar blah box]
// []
// [bar blah box]
// [blah]
// [tue thu]
}

@ -3,6 +3,7 @@ module github.com/rwxrob/bonzai
go 1.18
require (
github.com/rwxrob/compcmd v0.1.1
github.com/rwxrob/fn v0.3.3
github.com/rwxrob/fs v0.5.0
github.com/rwxrob/scan v0.6.1
@ -12,7 +13,7 @@ require (
)
require (
golang.org/x/crypto v0.0.0-20220408190544-5352b0902921 // indirect
golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f // indirect
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect
golang.org/x/sys v0.0.0-20220412071739-889880a91fd5 // indirect
golang.org/x/term v0.0.0-20220411215600-e5f449aeb171 // indirect
)

@ -1,3 +1,9 @@
github.com/rwxrob/compcmd v0.0.1 h1:92l0Sc44fznL4ChZ0h1oru83i99sBhfPm4Rmfn06VAM=
github.com/rwxrob/compcmd v0.0.1/go.mod h1:l4anbcmNEBs4vNb2uxfD45jSgm2FmPvpYfBNGqlO3rc=
github.com/rwxrob/compcmd v0.1.0 h1:We6J4mrzTNp0ra6tHbJ51VFkdNWyzcdCcUUWCQ8y9Wk=
github.com/rwxrob/compcmd v0.1.0/go.mod h1:l4anbcmNEBs4vNb2uxfD45jSgm2FmPvpYfBNGqlO3rc=
github.com/rwxrob/compcmd v0.1.1 h1:VCpA28yXWSS5ijAe6N5DswYektT1KT3ZryirINZVYgs=
github.com/rwxrob/compcmd v0.1.1/go.mod h1:l4anbcmNEBs4vNb2uxfD45jSgm2FmPvpYfBNGqlO3rc=
github.com/rwxrob/fn v0.3.3 h1:ymRQGWDhrrvoHKXLJ4WZlgI2qrC7gMOotowQMGvwmVQ=
github.com/rwxrob/fn v0.3.3/go.mod h1:omPqOqEB+dDna09z5pi5YFxq4IZqDvv3wFPUCES5LvY=
github.com/rwxrob/fs v0.5.0 h1:1AcZUMBzYhippXl0RfYb+KMo67Sj+e5eSUkOKTx2rUU=
@ -17,8 +23,14 @@ github.com/rwxrob/to v0.5.2/go.mod h1:lojk6scni4ZRYjnKJO/f2DVRTW0BB6l9LZQ/NvZZt4
golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220408190544-5352b0902921 h1:iU7T1X1J6yxDr0rda54sWGkHgOp5XJrqm79gcNlC2VM=
golang.org/x/crypto v0.0.0-20220408190544-5352b0902921/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 h1:kUhD7nTDoI3fVd9G4ORWrbV5NY0liEs/Jg2pv5f+bBA=
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/sys v0.0.0-20220406163625-3f8b81556e12/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f h1:8w7RhxzTVgUzw/AH/9mUV5q0vMgy40SQRursCcfmkCw=
golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412071739-889880a91fd5 h1:NubxfvTRuNb4RVzWrIDAUzUvREH1HkCD4JjyQTSG9As=
golang.org/x/sys v0.0.0-20220412071739-889880a91fd5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.0.0-20220411215600-e5f449aeb171 h1:EH1Deb8WZJ0xc0WK//leUHXcX9aLE5SymusoTmMZye8=
golang.org/x/term v0.0.0-20220411215600-e5f449aeb171/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=

@ -28,6 +28,7 @@ import (
"strings"
"github.com/rwxrob/bonzai"
"github.com/rwxrob/compcmd"
"github.com/rwxrob/term"
)
@ -64,6 +65,16 @@ var ExeName string
// will be assumed to be string arguments to prepend. See Run.
var Commands map[string][]any
// Comp may be optionally assigned any implementation of
// bonzai.Completer and will be used as the default if a Command does not
// provide its own. Comp is assigned rwxrob/compcmd.Completer by default.
// This can be overriden by Bonzai tree developers through simple
// assignment to their own preference. However, for consistency, this
// default is strongly recommended, at least for all branch commands (as
// opposed to leafs). See z.Cmd.Run for documentation on completion
// mode.
var Comp = compcmd.New()
// Conf may be optionally assigned any implementation of
// a bonzai.Configurer. Once assigned it should not be reassigned at any
// later time during runtime. Certain Bonzai branches and commands may

@ -13,7 +13,6 @@ import (
"text/template"
"github.com/rwxrob/bonzai"
"github.com/rwxrob/bonzai/comp"
"github.com/rwxrob/fn/each"
"github.com/rwxrob/fn/maps"
"github.com/rwxrob/fn/redu"
@ -47,8 +46,8 @@ type Cmd struct {
Params []string `json:"params,omitempty"`
Hidden []string `json:"hidden,omitempty"`
// standard or custom completer
Completer bonzai.Completer `json:"-"`
// standard or custom completer, usually of form compfoo.New()
Comp bonzai.Completer `json:"-"`
// where the work happens
Caller *Cmd `json:"-"`
@ -206,7 +205,7 @@ func (x *Cmd) cacheSections() {
// COMP_LINE
//
// When COMP_LINE is set, Run prints a list of possible completions to
// standard output by calling its first-class Completer function
// standard output by calling its Comp.Complete function
// (default Z.Comp). Each Cmd therefore manages its own completion and
// can draw from a rich ecosystem of Completers or assign its own custom
// one. This enables very powerful completions including dynamic
@ -251,14 +250,19 @@ func (x *Cmd) Run() {
line := os.Getenv("COMP_LINE")
if line != "" {
var list []string
// find the leaf command
lineargs := ArgsFrom(line)
if len(lineargs) == 2 {
list = append(list, maps.KeysWithPrefix(Aliases, lineargs[1])...)
}
cmd, args := x.Seek(lineargs[1:])
if cmd.Completer == nil {
// FIXME(rwxrob) change comp.Standard to Comp (Z.Comp)
list = append(list, comp.Standard(cmd, args...)...)
// default completer or package aliases, always exits
if cmd.Comp == nil {
if Comp != nil {
list = append(list, Comp.Complete(cmd, args...)...)
}
if len(list) == 1 && len(lineargs) == 2 {
if v, has := Aliases[list[0]]; has {
fmt.Println(strings.Join(EscAll(v), " "))
@ -268,7 +272,9 @@ func (x *Cmd) Run() {
each.Println(list)
Exit()
}
each.Println(cmd.Completer(cmd, args...))
// own completer, delegate
each.Println(cmd.Comp.Complete(cmd, args...))
Exit()
}
@ -674,8 +680,8 @@ func (x *Cmd) GetOtherTitles() []string {
return titles
}
// GetCompleter fulfills the Command interface.
func (x *Cmd) GetCompleter() bonzai.Completer { return x.Completer }
// GetComp fulfills the Command interface.
func (x *Cmd) GetComp() bonzai.Completer { return x.Comp }
// GetCaller fulfills the bonzai.Command interface.
func (x *Cmd) GetCaller() bonzai.Command { return x.Caller }

Loading…
Cancel
Save