| package readline |
| |
| import ( |
| "bytes" |
| "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) |
| GetName() []rune |
| GetChildren() []PrefixCompleterInterface |
| SetChildren(children []PrefixCompleterInterface) |
| } |
| |
| type DynamicPrefixCompleterInterface interface { |
| PrefixCompleterInterface |
| IsDynamic() bool |
| GetDynamicNames(line []rune) [][]rune |
| } |
| |
| type PrefixCompleter struct { |
| Name []rune |
| Dynamic bool |
| Callback DynamicCompleteFunc |
| Children []PrefixCompleterInterface |
| } |
| |
| func (p *PrefixCompleter) Tree(prefix string) string { |
| buf := bytes.NewBuffer(nil) |
| p.Print(prefix, 0, buf) |
| return buf.String() |
| } |
| |
| func Print(p PrefixCompleterInterface, prefix string, level int, buf *bytes.Buffer) { |
| if strings.TrimSpace(string(p.GetName())) != "" { |
| buf.WriteString(prefix) |
| if level > 0 { |
| buf.WriteString("├") |
| buf.WriteString(strings.Repeat("─", (level*4)-2)) |
| buf.WriteString(" ") |
| } |
| buf.WriteString(string(p.GetName()) + "\n") |
| level++ |
| } |
| for _, ch := range p.GetChildren() { |
| ch.Print(prefix, level, buf) |
| } |
| } |
| |
| 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 |
| } |
| |
| func (p *PrefixCompleter) SetChildren(children []PrefixCompleterInterface) { |
| p.Children = children |
| } |
| |
| func NewPrefixCompleter(pc ...PrefixCompleterInterface) *PrefixCompleter { |
| return PcItem("", pc...) |
| } |
| |
| 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 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() { |
| childNames := make([][]rune, 1) |
| |
| childDynamic, ok := child.(DynamicPrefixCompleterInterface) |
| if ok && childDynamic.IsDynamic() { |
| childNames = childDynamic.GetDynamicNames(origLine) |
| } else { |
| 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 |
| } |
| } |
| } |
| } |
| |
| 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 doInternal(lineCompleter, tmpLine, len(tmpLine), origLine) |
| } |
| |
| if goNext { |
| return doInternal(lineCompleter, nil, 0, origLine) |
| } |
| return |
| } |