[complete] add segment complete

This commit is contained in:
chzyer 2016-04-02 01:23:53 +08:00
parent d85c8b4802
commit 4606bfd979
3 changed files with 230 additions and 5 deletions

View File

@ -12,16 +12,16 @@ type AutoCompleter interface {
// Readline will pass the whole line and current offset to it
// Completer need to pass all the candidates, and how long they shared the same characters in line
// Example:
// [go, git, git-shell, grep]
// Do("g", 1) => ["o", "it", "it-shell", "rep"], 1
// Do("gi", 2) => ["t", "t-shell"], 1
// Do("git", 3) => ["", "-shell"], 0
// Do("gi", 2) => ["t", "t-shell"], 2
// Do("git", 3) => ["", "-shell"], 3
Do(line []rune, pos int) (newLine [][]rune, length int)
}
type opCompleter struct {
w io.Writer
op *Operation
ac AutoCompleter
width int
inCompleteMode bool
@ -37,7 +37,6 @@ func newOpCompleter(w io.Writer, op *Operation, width int) *opCompleter {
return &opCompleter{
w: w,
op: op,
ac: op.cfg.AutoComplete,
width: width,
}
}
@ -77,7 +76,7 @@ func (o *opCompleter) OnComplete() {
o.ExitCompleteSelectMode()
o.candidateSource = rs
newLines, offset := o.ac.Do(rs, buf.idx)
newLines, offset := o.op.cfg.AutoComplete.Do(rs, buf.idx)
if len(newLines) == 0 {
o.ExitCompleteMode(false)
return

66
complete_segment.go Normal file
View File

@ -0,0 +1,66 @@
package readline
type SegmentCompleter interface {
// a
// |- a1
// |--- a11
// |- a2
// b
// input:
// DoTree([], 0) [a, b]
// DoTree([a], 1) [a]
// DoTree([a, ], 0) [a1, a2]
// DoTree([a, a], 1) [a1, a2]
// DoTree([a, a1], 2) [a1]
// DoTree([a, a1, ], 0) [a11]
// DoTree([a, a1, a], 1) [a11]
DoSegment([][]rune, int) [][]rune
}
type dumpSegmentCompleter struct {
f func([][]rune, int) [][]rune
}
func (d *dumpSegmentCompleter) DoSegment(segment [][]rune, n int) [][]rune {
return d.f(segment, n)
}
func SegmentFunc(f func([][]rune, int) [][]rune) AutoCompleter {
return &SegmentComplete{&dumpSegmentCompleter{f}}
}
type SegmentComplete struct {
SegmentCompleter
}
func RetSegment(segments [][]rune, cands [][]rune, idx int) ([][]rune, int) {
ret := make([][]rune, 0, len(cands))
lastSegment := segments[len(segments)-1]
for _, cand := range cands {
ret = append(ret, cand[len(lastSegment):])
}
return ret, idx
}
func SplitSegment(line []rune, pos int) ([][]rune, int) {
segs := [][]rune{}
lastIdx := -1
line = line[:pos]
pos = 0
for idx, l := range line {
if l == ' ' {
pos = 0
segs = append(segs, line[lastIdx+1:idx])
lastIdx = idx
} else {
pos++
}
}
segs = append(segs, line[lastIdx+1:])
return segs, pos
}
func (c *SegmentComplete) Do(line []rune, pos int) (newLine [][]rune, offset int) {
segment, idx := SplitSegment(line, pos)
return RetSegment(segment, c.DoSegment(segment, idx), idx)
}

160
complete_segment_test.go Normal file
View File

@ -0,0 +1,160 @@
package readline
import (
"fmt"
"testing"
"gopkg.in/logex.v1"
"github.com/chzyer/test"
)
func rs(s [][]rune) []string {
ret := make([]string, len(s))
for idx, ss := range s {
ret[idx] = string(ss)
}
return ret
}
func sr(s ...string) [][]rune {
ret := make([][]rune, len(s))
for idx, ss := range s {
ret[idx] = []rune(ss)
}
return ret
}
func TestRetSegment(t *testing.T) {
defer test.New(t)
// a
// |- a1
// |--- a11
// |--- a12
// |- a2
// |--- a21
// b
ret := []struct {
Segments [][]rune
Cands [][]rune
idx int
Ret [][]rune
pos int
}{
{sr(""), sr("a", "b"), 0, sr("a", "b"), 0},
{sr("a"), sr("a"), 1, sr(""), 1},
{sr("a", ""), sr("a1", "a2"), 0, sr("a1", "a2"), 0},
{sr("a", "a"), sr("a1", "a2"), 1, sr("1", "2"), 1},
{sr("a", "a1"), sr("a1"), 2, sr(""), 2},
}
for idx, r := range ret {
ret, pos := RetSegment(r.Segments, r.Cands, r.idx)
test.Equal(ret, r.Ret, fmt.Errorf("%v", idx))
test.Equal(pos, r.pos, fmt.Errorf("%v", idx))
}
}
func TestSplitSegment(t *testing.T) {
defer test.New(t)
// a
// |- a1
// |--- a11
// |--- a12
// |- a2
// |--- a21
// b
ret := []struct {
Line string
Pos int
Segments [][]rune
Idx int
}{
{"", 0, sr(""), 0},
{"a", 1, sr("a"), 1},
{"a ", 2, sr("a", ""), 0},
{"a a", 3, sr("a", "a"), 1},
{"a a1", 4, sr("a", "a1"), 2},
{"a a1 ", 5, sr("a", "a1", ""), 0},
}
for i, r := range ret {
ret, idx := SplitSegment([]rune(r.Line), r.Pos)
test.Equal(rs(ret), rs(r.Segments), fmt.Errorf("%v", i))
test.Equal(idx, r.Idx, fmt.Errorf("%v", i))
}
}
type Tree struct {
Name string
Children []Tree
}
func TestSegmentCompleter(t *testing.T) {
defer test.New(t)
tree := Tree{"", []Tree{
{"a", []Tree{
{"a1", []Tree{
{"a11", nil},
{"a12", nil},
}},
{"a2", []Tree{
{"a21", nil},
}},
}},
{"b", nil},
}}
s := SegmentFunc(func(ret [][]rune, n int) [][]rune {
tree := tree
main:
for level := 0; level < len(ret); {
name := string(ret[level])
for _, t := range tree.Children {
if t.Name == name {
tree = t
level++
continue main
}
}
ret = make([][]rune, len(tree.Children))
for idx, r := range tree.Children {
ret[idx] = []rune(r.Name)
}
return ret
}
logex.Info(rs(ret), n, tree)
return [][]rune{[]rune(tree.Name)}
})
// a
// |- a1
// |--- a11
// |--- a12
// |- a2
// |--- a21
// b
ret := []struct {
Line string
Pos int
Ret [][]rune
Share int
}{
{"", 0, sr("a", "b"), 0},
{"a", 1, sr(""), 1},
{"a ", 2, sr("a1", "a2"), 0},
{"a a", 3, sr("1", "2"), 1},
{"a a1", 4, sr(""), 2},
{"a a1 ", 5, sr("a11", "a12"), 0},
{"a a1 a", 6, sr("11", "12"), 1},
{"a a1 a1", 7, sr("1", "2"), 2},
{"a a1 a11", 8, sr(""), 3},
}
for i, r := range ret {
newLine, length := s.Do([]rune(r.Line), r.Pos)
test.Equal(newLine, r.Ret, fmt.Errorf("%v", i))
test.Equal(length, r.Share, fmt.Errorf("%v", i))
}
}