diff --git a/complete.go b/complete.go index 883983b..52035a3 100644 --- a/complete.go +++ b/complete.go @@ -4,8 +4,6 @@ import ( "bytes" "fmt" "io" - - "github.com/chzyer/readline/runes" ) type AutoCompleter interface { diff --git a/complete_helper.go b/complete_helper.go index 4c10f51..430fbf3 100644 --- a/complete_helper.go +++ b/complete_helper.go @@ -3,8 +3,6 @@ package readline import ( "bytes" "strings" - - "github.com/chzyer/readline/runes" ) type PrefixCompleterInterface interface { diff --git a/complete_segment.go b/complete_segment.go index 7b3dacf..2349576 100644 --- a/complete_segment.go +++ b/complete_segment.go @@ -1,7 +1,5 @@ package readline -import "github.com/chzyer/readline/runes" - type SegmentCompleter interface { // a // |- a1 diff --git a/history.go b/history.go index e2e0235..21e4d33 100644 --- a/history.go +++ b/history.go @@ -6,8 +6,6 @@ import ( "fmt" "os" "strings" - - "github.com/chzyer/readline/runes" ) type hisItem struct { diff --git a/runebuf.go b/runebuf.go index 40450e8..57aaa5f 100644 --- a/runebuf.go +++ b/runebuf.go @@ -5,8 +5,6 @@ import ( "bytes" "io" "strings" - - "github.com/chzyer/readline/runes" ) type runeBufferBck struct { diff --git a/runes.go b/runes.go new file mode 100644 index 0000000..b0a878b --- /dev/null +++ b/runes.go @@ -0,0 +1,156 @@ +package readline + +import ( + "bytes" + "unicode" +) + +var runes = Runes{} + +type Runes struct{} + +func (Runes) Equal(a, b []rune) bool { + if len(a) != len(b) { + return false + } + for i := 0; i < len(a); i++ { + if a[i] != b[i] { + return false + } + } + return true +} + +// Search in runes from end to front +func (Runes) IndexAllBck(r, sub []rune) int { + for i := len(r) - len(sub); i >= 0; i-- { + found := true + for j := 0; j < len(sub); j++ { + if r[i+j] != sub[j] { + found = false + break + } + } + if found { + return i + } + } + return -1 +} + +// Search in runes from front to end +func (Runes) IndexAll(r, sub []rune) int { + for i := 0; i < len(r); i++ { + found := true + if len(r[i:]) < len(sub) { + return -1 + } + for j := 0; j < len(sub); j++ { + if r[i+j] != sub[j] { + found = false + break + } + } + if found { + return i + } + } + return -1 +} + +func (Runes) Index(r rune, rs []rune) int { + for i := 0; i < len(rs); i++ { + if rs[i] == r { + return i + } + } + return -1 +} + +func (Runes) ColorFilter(r []rune) []rune { + newr := make([]rune, 0, len(r)) + for pos := 0; pos < len(r); pos++ { + if r[pos] == '\033' && r[pos+1] == '[' { + idx := runes.Index('m', r[pos+2:]) + if idx == -1 { + continue + } + pos += idx + 2 + continue + } + newr = append(newr, r[pos]) + } + return newr +} + +var zeroWidth = []*unicode.RangeTable{ + unicode.Mn, + unicode.Me, + unicode.Cc, + unicode.Cf, +} + +var doubleWidth = []*unicode.RangeTable{ + unicode.Han, + unicode.Hangul, + unicode.Hiragana, + unicode.Katakana, +} + +func (Runes) Width(r rune) int { + if unicode.IsOneOf(zeroWidth, r) { + return 0 + } + if unicode.IsOneOf(doubleWidth, r) { + return 2 + } + return 1 +} + +func (Runes) WidthAll(r []rune) (length int) { + for i := 0; i < len(r); i++ { + length += runes.Width(r[i]) + } + return +} + +func (Runes) Backspace(r []rune) []byte { + return bytes.Repeat([]byte{'\b'}, runes.WidthAll(r)) +} + +func (Runes) Copy(r []rune) []rune { + n := make([]rune, len(r)) + copy(n, r) + return n +} + +func (Runes) HasPrefix(r, prefix []rune) bool { + if len(r) < len(prefix) { + return false + } + return runes.Equal(r[:len(prefix)], prefix) +} + +func (Runes) Aggregate(candicate [][]rune) (same []rune, size int) { + for i := 0; i < len(candicate[0]); i++ { + for j := 0; j < len(candicate)-1; j++ { + if i >= len(candicate[j]) || i >= len(candicate[j+1]) { + goto aggregate + } + if candicate[j][i] != candicate[j+1][i] { + goto aggregate + } + } + size = i + 1 + } +aggregate: + if size > 0 { + same = runes.Copy(candicate[0][:size]) + for i := 0; i < len(candicate); i++ { + n := runes.Copy(candicate[i]) + copy(n, n[size:]) + candicate[i] = n[:len(n)-size] + } + } + return +} diff --git a/runes/runes.go b/runes/runes.go index c5a7502..b069440 100644 --- a/runes/runes.go +++ b/runes/runes.go @@ -1,3 +1,6 @@ +// deprecated. +// see https://github.com/chzyer/readline/issues/43 +// use github.com/chzyer/readline/runes.go package runes import ( diff --git a/runes_test.go b/runes_test.go new file mode 100644 index 0000000..9c56d79 --- /dev/null +++ b/runes_test.go @@ -0,0 +1,68 @@ +package readline + +import ( + "reflect" + "testing" +) + +type twidth struct { + r []rune + length int +} + +func TestRuneWidth(t *testing.T) { + rs := []twidth{ + {[]rune("☭"), 1}, + {[]rune("a"), 1}, + {[]rune("你"), 2}, + {runes.ColorFilter([]rune("☭\033[13;1m你")), 3}, + } + for _, r := range rs { + if w := runes.WidthAll(r.r); w != r.length { + t.Fatal("result not expect", r.r, r.length, w) + } + } +} + +type tagg struct { + r [][]rune + e [][]rune + length int +} + +func TestAggRunes(t *testing.T) { + rs := []tagg{ + { + [][]rune{[]rune("ab"), []rune("a"), []rune("abc")}, + [][]rune{[]rune("b"), []rune(""), []rune("bc")}, + 1, + }, + { + [][]rune{[]rune("addb"), []rune("ajkajsdf"), []rune("aasdfkc")}, + [][]rune{[]rune("ddb"), []rune("jkajsdf"), []rune("asdfkc")}, + 1, + }, + { + [][]rune{[]rune("ddb"), []rune("ajksdf"), []rune("aasdfkc")}, + [][]rune{[]rune("ddb"), []rune("ajksdf"), []rune("aasdfkc")}, + 0, + }, + { + [][]rune{[]rune("ddb"), []rune("ddajksdf"), []rune("ddaasdfkc")}, + [][]rune{[]rune("b"), []rune("ajksdf"), []rune("aasdfkc")}, + 2, + }, + } + for _, r := range rs { + same, off := runes.Aggregate(r.r) + if off != r.length { + t.Fatal("result not expect", off) + } + if len(same) != off { + t.Fatal("result not expect", same) + } + if !reflect.DeepEqual(r.r, r.e) { + t.Fatal("result not expect") + } + } +} diff --git a/utils.go b/utils.go index 8ee2f8f..7ec052c 100644 --- a/utils.go +++ b/utils.go @@ -5,8 +5,6 @@ import ( "bytes" "strconv" - "github.com/chzyer/readline/runes" - "golang.org/x/crypto/ssh/terminal" )