From f89cf57370ea52d09cbc22f3bae7e03eacf2a931 Mon Sep 17 00:00:00 2001 From: Dan Cripe Date: Wed, 6 Jan 2016 17:39:08 -0600 Subject: [PATCH] Change implementation drastically. Signed-off-by: Dan Cripe --- complete.go | 59 ++++++++++++++++++--- complete_helper.go | 127 +++++---------------------------------------- 2 files changed, 64 insertions(+), 122 deletions(-) diff --git a/complete.go b/complete.go index 8ee823d..94adb11 100644 --- a/complete.go +++ b/complete.go @@ -9,13 +9,56 @@ import ( ) type AutoCompleter interface { - // Readline will pass the whole line and current offset to it - // Completer need to pass all the candidates, and how long they shared the same characters in line - // Example: - // Do("g", 1) => ["o", "it", "it-shell", "rep"], 1 - // Do("gi", 2) => ["t", "t-shell"], 1 - // Do("git", 3) => ["", "-shell"], 0 - Do(line []rune, pos int) (newLine [][]rune, length int) + GetName() []rune + GetChildren() []AutoCompleter +} + +func Do(ac AutoCompleter, line []rune, pos int) (newLine [][]rune, offset int) { + line = line[:pos] + goNext := false + var lineCompleter AutoCompleter + + for _, child := range ac.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 + } + } else { + if runes.HasPrefix(childName, line) { + newLine = append(newLine, childName[len(line):]) + offset = len(line) + lineCompleter = child + } + } + } + + if len(newLine) != 1 { + return + } + + tmpLine := make([]rune, 0, len(line)) + for i := offset; i < len(line); i++ { + if line[i] == ' ' { + continue + } + + tmpLine = append(tmpLine, line[i:]...) + return Do(lineCompleter, tmpLine, len(tmpLine)) + } + + if goNext { + return Do(lineCompleter, nil, 0) + } + + return } type opCompleter struct { @@ -75,7 +118,7 @@ func (o *opCompleter) OnComplete() { o.ExitCompleteSelectMode() o.candidateSource = rs - newLines, offset := o.ac.Do(rs, buf.idx) + newLines, offset := Do(o.ac, rs, buf.idx) if len(newLines) == 0 { o.ExitCompleteMode(false) return diff --git a/complete_helper.go b/complete_helper.go index 27c5df2..b74e6d5 100644 --- a/complete_helper.go +++ b/complete_helper.go @@ -1,128 +1,27 @@ package readline -import ( - "bytes" - "strings" - - "github.com/chzyer/readline/runes" -) - -type DynChildrenFunc func() [][]rune - type PrefixCompleter struct { - Name []rune - Children []*PrefixCompleter - DynChildren DynChildrenFunc + Name []rune + Children []AutoCompleter } -func empty() [][]rune { - return [][]rune{} +func (p PrefixCompleter) GetName() []rune { + return p.Name } -func (p *PrefixCompleter) Tree(prefix string) string { - buf := bytes.NewBuffer(nil) - p.Print(prefix, 0, buf) - return buf.String() +func (p PrefixCompleter) GetChildren() []AutoCompleter { + return p.Children } -func (p *PrefixCompleter) Print(prefix string, level int, buf *bytes.Buffer) { - if strings.TrimSpace(string(p.Name)) != "" { - buf.WriteString(prefix) - if level > 0 { - buf.WriteString("├") - buf.WriteString(strings.Repeat("─", (level*4)-2)) - buf.WriteString(" ") - } - buf.WriteString(string(p.Name) + "\n") - level++ - } - for _, ch := range p.Children { - ch.Print(prefix, level, buf) - } -} - -func NewPrefixCompleter(pc ...*PrefixCompleter) *PrefixCompleter { +func NewPrefixCompleter(pc ...AutoCompleter) AutoCompleter { return PcItem("", pc...) } -func PcDyn(name string, term DynChildrenFunc) *PrefixCompleter { +func PcItem(name string, pc ...AutoCompleter) AutoCompleter { name += " " - return &PrefixCompleter{ - Name: []rune(name), - DynChildren: term, - } -} - -func PcItem(name string, pc ...*PrefixCompleter) *PrefixCompleter { - name += " " - return &PrefixCompleter{ - Name: []rune(name), - Children: pc, - DynChildren: empty, - } -} - -func (p *PrefixCompleter) Do(line []rune, pos int) (newLine [][]rune, offset int) { - end := PcItem("") - line = line[:pos] - goNext := false - var lineCompleter *PrefixCompleter - for _, child := range p.Children { - if len(line) >= len(child.Name) { - if runes.HasPrefix(line, child.Name) { - if len(line) == len(child.Name) { - newLine = append(newLine, []rune{' '}) - } else { - newLine = append(newLine, child.Name) - } - offset = len(child.Name) - lineCompleter = child - goNext = true - } - } else { - if runes.HasPrefix(child.Name, line) { - newLine = append(newLine, child.Name[len(line):]) - offset = len(line) - lineCompleter = child - } - } - } - for _, dynChild := range p.DynChildren() { - if len(line) >= len(dynChild) { - if runes.HasPrefix(line, dynChild) { - if len(line) == len(dynChild) { - newLine = append(newLine, []rune{' '}) - } else { - newLine = append(newLine, dynChild) - } - offset = len(dynChild) - lineCompleter = end - } - } else { - if runes.HasPrefix(dynChild, line) { - newLine = append(newLine, dynChild[len(line):]) - offset = len(line) - lineCompleter = end - } - } - } - - if len(newLine) != 1 { - return - } - - tmpLine := make([]rune, 0, len(line)) - for i := offset; i < len(line); i++ { - if line[i] == ' ' { - continue - } - - tmpLine = append(tmpLine, line[i:]...) - return lineCompleter.Do(tmpLine, len(tmpLine)) - } - - if goNext { - return lineCompleter.Do(nil, 0) - } - return + result := AutoCompleter(PrefixCompleter{ + Name: []rune(name), + Children: pc, + }) + return result }