forked from mirror/readline
[complete] add segment complete
This commit is contained in:
parent
d85c8b4802
commit
4606bfd979
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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))
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue