blob: 58d724872bfef786460174b054781504a232bc63 [file] [log] [blame]
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
}