1package readline 2 3import ( 4 "bytes" 5 "strings" 6) 7 8// Caller type for dynamic completion 9type DynamicCompleteFunc func(string) []string 10 11type PrefixCompleterInterface interface { 12 Print(prefix string, level int, buf *bytes.Buffer) 13 Do(line []rune, pos int) (newLine [][]rune, length int) 14 GetName() []rune 15 GetChildren() []PrefixCompleterInterface 16 SetChildren(children []PrefixCompleterInterface) 17} 18 19type DynamicPrefixCompleterInterface interface { 20 PrefixCompleterInterface 21 IsDynamic() bool 22 GetDynamicNames(line []rune) [][]rune 23} 24 25type PrefixCompleter struct { 26 Name []rune 27 Dynamic bool 28 Callback DynamicCompleteFunc 29 Children []PrefixCompleterInterface 30} 31 32func (p *PrefixCompleter) Tree(prefix string) string { 33 buf := bytes.NewBuffer(nil) 34 p.Print(prefix, 0, buf) 35 return buf.String() 36} 37 38func Print(p PrefixCompleterInterface, prefix string, level int, buf *bytes.Buffer) { 39 if strings.TrimSpace(string(p.GetName())) != "" { 40 buf.WriteString(prefix) 41 if level > 0 { 42 buf.WriteString("├") 43 buf.WriteString(strings.Repeat("─", (level*4)-2)) 44 buf.WriteString(" ") 45 } 46 buf.WriteString(string(p.GetName()) + "\n") 47 level++ 48 } 49 for _, ch := range p.GetChildren() { 50 ch.Print(prefix, level, buf) 51 } 52} 53 54func (p *PrefixCompleter) Print(prefix string, level int, buf *bytes.Buffer) { 55 Print(p, prefix, level, buf) 56} 57 58func (p *PrefixCompleter) IsDynamic() bool { 59 return p.Dynamic 60} 61 62func (p *PrefixCompleter) GetName() []rune { 63 return p.Name 64} 65 66func (p *PrefixCompleter) GetDynamicNames(line []rune) [][]rune { 67 var names = [][]rune{} 68 for _, name := range p.Callback(string(line)) { 69 names = append(names, []rune(name+" ")) 70 } 71 return names 72} 73 74func (p *PrefixCompleter) GetChildren() []PrefixCompleterInterface { 75 return p.Children 76} 77 78func (p *PrefixCompleter) SetChildren(children []PrefixCompleterInterface) { 79 p.Children = children 80} 81 82func NewPrefixCompleter(pc ...PrefixCompleterInterface) *PrefixCompleter { 83 return PcItem("", pc...) 84} 85 86func PcItem(name string, pc ...PrefixCompleterInterface) *PrefixCompleter { 87 name += " " 88 return &PrefixCompleter{ 89 Name: []rune(name), 90 Dynamic: false, 91 Children: pc, 92 } 93} 94 95func PcItemDynamic(callback DynamicCompleteFunc, pc ...PrefixCompleterInterface) *PrefixCompleter { 96 return &PrefixCompleter{ 97 Callback: callback, 98 Dynamic: true, 99 Children: pc, 100 } 101} 102 103func (p *PrefixCompleter) Do(line []rune, pos int) (newLine [][]rune, offset int) { 104 return doInternal(p, line, pos, line) 105} 106 107func Do(p PrefixCompleterInterface, line []rune, pos int) (newLine [][]rune, offset int) { 108 return doInternal(p, line, pos, line) 109} 110 111func doInternal(p PrefixCompleterInterface, line []rune, pos int, origLine []rune) (newLine [][]rune, offset int) { 112 line = runes.TrimSpaceLeft(line[:pos]) 113 goNext := false 114 var lineCompleter PrefixCompleterInterface 115 for _, child := range p.GetChildren() { 116 childNames := make([][]rune, 1) 117 118 childDynamic, ok := child.(DynamicPrefixCompleterInterface) 119 if ok && childDynamic.IsDynamic() { 120 childNames = childDynamic.GetDynamicNames(origLine) 121 } else { 122 childNames[0] = child.GetName() 123 } 124 125 for _, childName := range childNames { 126 if len(line) >= len(childName) { 127 if runes.HasPrefix(line, childName) { 128 if len(line) == len(childName) { 129 newLine = append(newLine, []rune{' '}) 130 } else { 131 newLine = append(newLine, childName) 132 } 133 offset = len(childName) 134 lineCompleter = child 135 goNext = true 136 } 137 } else { 138 if runes.HasPrefix(childName, line) { 139 newLine = append(newLine, childName[len(line):]) 140 offset = len(line) 141 lineCompleter = child 142 } 143 } 144 } 145 } 146 147 if len(newLine) != 1 { 148 return 149 } 150 151 tmpLine := make([]rune, 0, len(line)) 152 for i := offset; i < len(line); i++ { 153 if line[i] == ' ' { 154 continue 155 } 156 157 tmpLine = append(tmpLine, line[i:]...) 158 return doInternal(lineCompleter, tmpLine, len(tmpLine), origLine) 159 } 160 161 if goNext { 162 return doInternal(lineCompleter, nil, 0, origLine) 163 } 164 return 165} 166