mirror of https://github.com/chzyer/readline.git
Dynamic autocompletion (#60)
* Dynamic autocompletion implemented Now there is a new item type "PcItemDynamic" which takes function as parameter. When it comes to autocomplete at this position, the function is called, the whole line is given to it (for example if autocompletion depend on previous autocompleted field) and functio should return possible strings how to continue. Example usage: * listing some dynamic key-value storage * listing files in directory * Dynamic autocompletion: Updated example * Dynamic autocompletion: Internal Do() is passing the original full line To serve it to dynamic autocompletion functions. Previously passed line was only following segment (which doesn't work). * Dynamic autocompletion: New dynamic interface added + type assertion in Do function Do function was split into doInternal with changed declaration and Do with original declaration.
This commit is contained in:
parent
dc578a10ae
commit
cffdf641d1
|
@ -5,6 +5,9 @@ import (
|
|||
"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)
|
||||
|
@ -13,8 +16,16 @@ type PrefixCompleterInterface interface {
|
|||
SetChildren(children []PrefixCompleterInterface)
|
||||
}
|
||||
|
||||
type DynamicPrefixCompleterInterface interface {
|
||||
PrefixCompleterInterface
|
||||
IsDynamic() bool
|
||||
GetDynamicNames(line []rune) [][]rune
|
||||
}
|
||||
|
||||
type PrefixCompleter struct {
|
||||
Name []rune
|
||||
Dynamic bool
|
||||
Callback DynamicCompleteFunc
|
||||
Children []PrefixCompleterInterface
|
||||
}
|
||||
|
||||
|
@ -44,10 +55,22 @@ 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
|
||||
}
|
||||
|
@ -64,36 +87,59 @@ 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 Do(p, line, pos)
|
||||
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() {
|
||||
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
|
||||
}
|
||||
childNames := make([][]rune, 1)
|
||||
|
||||
childDynamic, ok := child.(DynamicPrefixCompleterInterface)
|
||||
if ok && childDynamic.IsDynamic() {
|
||||
childNames = childDynamic.GetDynamicNames(origLine)
|
||||
} else {
|
||||
if runes.HasPrefix(childName, line) {
|
||||
newLine = append(newLine, childName[len(line):])
|
||||
offset = len(line)
|
||||
lineCompleter = child
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -109,11 +155,11 @@ func Do(p PrefixCompleterInterface, line []rune, pos int) (newLine [][]rune, off
|
|||
}
|
||||
|
||||
tmpLine = append(tmpLine, line[i:]...)
|
||||
return lineCompleter.Do(tmpLine, len(tmpLine))
|
||||
return doInternal(lineCompleter, tmpLine, len(tmpLine), origLine)
|
||||
}
|
||||
|
||||
if goNext {
|
||||
return lineCompleter.Do(nil, 0)
|
||||
return doInternal(lineCompleter, nil, 0, origLine)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package main
|
|||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -16,6 +17,18 @@ func usage(w io.Writer) {
|
|||
io.WriteString(w, completer.Tree(" "))
|
||||
}
|
||||
|
||||
// Function constructor - constructs new function for listing given directory
|
||||
func listFiles(path string) func(string) []string {
|
||||
return func(line string) []string {
|
||||
names := make([]string, 0)
|
||||
files, _ := ioutil.ReadDir(path)
|
||||
for _, f := range files {
|
||||
names = append(names, f.Name())
|
||||
}
|
||||
return names
|
||||
}
|
||||
}
|
||||
|
||||
var completer = readline.NewPrefixCompleter(
|
||||
readline.PcItem("mode",
|
||||
readline.PcItem("vi"),
|
||||
|
@ -23,6 +36,12 @@ var completer = readline.NewPrefixCompleter(
|
|||
),
|
||||
readline.PcItem("login"),
|
||||
readline.PcItem("say",
|
||||
readline.PcItemDynamic(listFiles("./"),
|
||||
readline.PcItem("with",
|
||||
readline.PcItem("following"),
|
||||
readline.PcItem("items"),
|
||||
),
|
||||
),
|
||||
readline.PcItem("hello"),
|
||||
readline.PcItem("bye"),
|
||||
),
|
||||
|
@ -119,7 +138,7 @@ func main() {
|
|||
break
|
||||
}
|
||||
go func() {
|
||||
for _ = range time.Tick(time.Second) {
|
||||
for range time.Tick(time.Second) {
|
||||
log.Println(line)
|
||||
}
|
||||
}()
|
||||
|
|
Loading…
Reference in New Issue