Change implementation drastically.

Signed-off-by: Dan Cripe <dan@quattronetworks.com>
This commit is contained in:
Dan Cripe 2016-01-06 17:39:08 -06:00
parent cd2d269a73
commit f89cf57370
2 changed files with 64 additions and 122 deletions

View File

@ -9,13 +9,56 @@ import (
) )
type AutoCompleter interface { type AutoCompleter interface {
// Readline will pass the whole line and current offset to it GetName() []rune
// Completer need to pass all the candidates, and how long they shared the same characters in line GetChildren() []AutoCompleter
// Example: }
// Do("g", 1) => ["o", "it", "it-shell", "rep"], 1
// Do("gi", 2) => ["t", "t-shell"], 1 func Do(ac AutoCompleter, line []rune, pos int) (newLine [][]rune, offset int) {
// Do("git", 3) => ["", "-shell"], 0 line = line[:pos]
Do(line []rune, pos int) (newLine [][]rune, length int) 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 { type opCompleter struct {
@ -75,7 +118,7 @@ func (o *opCompleter) OnComplete() {
o.ExitCompleteSelectMode() o.ExitCompleteSelectMode()
o.candidateSource = rs o.candidateSource = rs
newLines, offset := o.ac.Do(rs, buf.idx) newLines, offset := Do(o.ac, rs, buf.idx)
if len(newLines) == 0 { if len(newLines) == 0 {
o.ExitCompleteMode(false) o.ExitCompleteMode(false)
return return

View File

@ -1,128 +1,27 @@
package readline package readline
import (
"bytes"
"strings"
"github.com/chzyer/readline/runes"
)
type DynChildrenFunc func() [][]rune
type PrefixCompleter struct { type PrefixCompleter struct {
Name []rune Name []rune
Children []*PrefixCompleter Children []AutoCompleter
DynChildren DynChildrenFunc
} }
func empty() [][]rune { func (p PrefixCompleter) GetName() []rune {
return [][]rune{} return p.Name
} }
func (p *PrefixCompleter) Tree(prefix string) string { func (p PrefixCompleter) GetChildren() []AutoCompleter {
buf := bytes.NewBuffer(nil) return p.Children
p.Print(prefix, 0, buf)
return buf.String()
} }
func (p *PrefixCompleter) Print(prefix string, level int, buf *bytes.Buffer) { func NewPrefixCompleter(pc ...AutoCompleter) AutoCompleter {
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 {
return PcItem("", pc...) return PcItem("", pc...)
} }
func PcDyn(name string, term DynChildrenFunc) *PrefixCompleter { func PcItem(name string, pc ...AutoCompleter) AutoCompleter {
name += " " name += " "
return &PrefixCompleter{ result := AutoCompleter(PrefixCompleter{
Name: []rune(name), Name: []rune(name),
DynChildren: term, Children: pc,
} })
} return result
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
} }