diff --git a/complete_helper.go b/complete_helper.go index f609d44..58d7248 100644 --- a/complete_helper.go +++ b/complete_helper.go @@ -5,6 +5,9 @@ import ( "strings" ) +// Caller type for dynamic completion +type DynamicCompleteFunc func(string) []string + type PrefixCompleterInterface interface { Print(prefix string, level int, buf *bytes.Buffer) Do(line []rune, pos int) (newLine [][]rune, length int) @@ -13,8 +16,16 @@ type PrefixCompleterInterface interface { SetChildren(children []PrefixCompleterInterface) } +type DynamicPrefixCompleterInterface interface { + PrefixCompleterInterface + IsDynamic() bool + GetDynamicNames(line []rune) [][]rune +} + type PrefixCompleter struct { Name []rune + Dynamic bool + Callback DynamicCompleteFunc Children []PrefixCompleterInterface } @@ -44,10 +55,22 @@ func (p *PrefixCompleter) Print(prefix string, level int, buf *bytes.Buffer) { Print(p, prefix, level, buf) } +func (p *PrefixCompleter) IsDynamic() bool { + return p.Dynamic +} + func (p *PrefixCompleter) GetName() []rune { return p.Name } +func (p *PrefixCompleter) GetDynamicNames(line []rune) [][]rune { + var names = [][]rune{} + for _, name := range p.Callback(string(line)) { + names = append(names, []rune(name+" ")) + } + return names +} + func (p *PrefixCompleter) GetChildren() []PrefixCompleterInterface { return p.Children } @@ -64,36 +87,59 @@ func PcItem(name string, pc ...PrefixCompleterInterface) *PrefixCompleter { name += " " return &PrefixCompleter{ Name: []rune(name), + Dynamic: false, + Children: pc, + } +} + +func PcItemDynamic(callback DynamicCompleteFunc, pc ...PrefixCompleterInterface) *PrefixCompleter { + return &PrefixCompleter{ + Callback: callback, + Dynamic: true, Children: pc, } } func (p *PrefixCompleter) Do(line []rune, pos int) (newLine [][]rune, offset int) { - return Do(p, line, pos) + return doInternal(p, line, pos, line) } func Do(p PrefixCompleterInterface, line []rune, pos int) (newLine [][]rune, offset int) { + return doInternal(p, line, pos, line) +} + +func doInternal(p PrefixCompleterInterface, line []rune, pos int, origLine []rune) (newLine [][]rune, offset int) { line = runes.TrimSpaceLeft(line[:pos]) goNext := false var lineCompleter PrefixCompleterInterface for _, child := range p.GetChildren() { - childName := child.GetName() - if len(line) >= len(childName) { - if runes.HasPrefix(line, childName) { - if len(line) == len(childName) { - newLine = append(newLine, []rune{' '}) - } else { - newLine = append(newLine, childName) - } - offset = len(childName) - lineCompleter = child - goNext = true - } + childNames := make([][]rune, 1) + + childDynamic, ok := child.(DynamicPrefixCompleterInterface) + if ok && childDynamic.IsDynamic() { + childNames = childDynamic.GetDynamicNames(origLine) } else { - if runes.HasPrefix(childName, line) { - newLine = append(newLine, childName[len(line):]) - offset = len(line) - lineCompleter = child + childNames[0] = child.GetName() + } + + for _, childName := range childNames { + if len(line) >= len(childName) { + if runes.HasPrefix(line, childName) { + if len(line) == len(childName) { + newLine = append(newLine, []rune{' '}) + } else { + newLine = append(newLine, childName) + } + offset = len(childName) + lineCompleter = child + goNext = true + } + } else { + if runes.HasPrefix(childName, line) { + newLine = append(newLine, childName[len(line):]) + offset = len(line) + lineCompleter = child + } } } } @@ -109,11 +155,11 @@ func Do(p PrefixCompleterInterface, line []rune, pos int) (newLine [][]rune, off } tmpLine = append(tmpLine, line[i:]...) - return lineCompleter.Do(tmpLine, len(tmpLine)) + return doInternal(lineCompleter, tmpLine, len(tmpLine), origLine) } if goNext { - return lineCompleter.Do(nil, 0) + return doInternal(lineCompleter, nil, 0, origLine) } return } diff --git a/example/readline-demo/readline-demo.go b/example/readline-demo/readline-demo.go index eeb3605..d80e59e 100644 --- a/example/readline-demo/readline-demo.go +++ b/example/readline-demo/readline-demo.go @@ -3,6 +3,7 @@ package main import ( "fmt" "io" + "io/ioutil" "log" "strconv" "strings" @@ -16,6 +17,18 @@ func usage(w io.Writer) { io.WriteString(w, completer.Tree(" ")) } +// Function constructor - constructs new function for listing given directory +func listFiles(path string) func(string) []string { + return func(line string) []string { + names := make([]string, 0) + files, _ := ioutil.ReadDir(path) + for _, f := range files { + names = append(names, f.Name()) + } + return names + } +} + var completer = readline.NewPrefixCompleter( readline.PcItem("mode", readline.PcItem("vi"), @@ -23,6 +36,12 @@ var completer = readline.NewPrefixCompleter( ), readline.PcItem("login"), readline.PcItem("say", + readline.PcItemDynamic(listFiles("./"), + readline.PcItem("with", + readline.PcItem("following"), + readline.PcItem("items"), + ), + ), readline.PcItem("hello"), readline.PcItem("bye"), ), @@ -119,7 +138,7 @@ func main() { break } go func() { - for _ = range time.Tick(time.Second) { + for range time.Tick(time.Second) { log.Println(line) } }()