mirror of https://github.com/chzyer/readline.git
add runes package
This commit is contained in:
parent
bfa8c1dfdb
commit
55809b401d
|
@ -4,6 +4,8 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
|
"github.com/chzyer/readline/runes"
|
||||||
)
|
)
|
||||||
|
|
||||||
type AutoCompleter interface {
|
type AutoCompleter interface {
|
||||||
|
@ -65,7 +67,7 @@ func (o *opCompleter) OnComplete() {
|
||||||
buf := o.op.buf
|
buf := o.op.buf
|
||||||
rs := buf.Runes()
|
rs := buf.Runes()
|
||||||
|
|
||||||
if o.IsInCompleteMode() && RunesEqual(rs, o.candidateSource) {
|
if o.IsInCompleteMode() && runes.Equal(rs, o.candidateSource) {
|
||||||
o.EnterCompleteSelectMode()
|
o.EnterCompleteSelectMode()
|
||||||
o.doSelect()
|
o.doSelect()
|
||||||
return
|
return
|
||||||
|
@ -176,7 +178,7 @@ func (o *opCompleter) CompleteRefresh() {
|
||||||
lineCnt := o.op.buf.CursorLineCount()
|
lineCnt := o.op.buf.CursorLineCount()
|
||||||
colWidth := 0
|
colWidth := 0
|
||||||
for _, c := range o.candidate {
|
for _, c := range o.candidate {
|
||||||
w := RunesWidth(c)
|
w := runes.WidthAll(c)
|
||||||
if w > colWidth {
|
if w > colWidth {
|
||||||
colWidth = w
|
colWidth = w
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package readline
|
package readline
|
||||||
|
|
||||||
|
import "github.com/chzyer/readline/runes"
|
||||||
|
|
||||||
type PrefixCompleter struct {
|
type PrefixCompleter struct {
|
||||||
Name []rune
|
Name []rune
|
||||||
Children []*PrefixCompleter
|
Children []*PrefixCompleter
|
||||||
|
@ -25,14 +27,14 @@ func (p *PrefixCompleter) Do(line []rune, pos int) (newLine [][]rune, offset int
|
||||||
var lineCompleter *PrefixCompleter
|
var lineCompleter *PrefixCompleter
|
||||||
for _, child := range p.Children {
|
for _, child := range p.Children {
|
||||||
if len(line) >= len(child.Name) {
|
if len(line) >= len(child.Name) {
|
||||||
if RunesHasPrefix(line, child.Name) {
|
if runes.HasPrefix(line, child.Name) {
|
||||||
newLine = append(newLine, child.Name)
|
newLine = append(newLine, child.Name)
|
||||||
offset = len(child.Name)
|
offset = len(child.Name)
|
||||||
lineCompleter = child
|
lineCompleter = child
|
||||||
goNext = true
|
goNext = true
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if RunesHasPrefix(child.Name, line) {
|
if runes.HasPrefix(child.Name, line) {
|
||||||
newLine = append(newLine, child.Name[len(line):])
|
newLine = append(newLine, child.Name[len(line):])
|
||||||
offset = len(line)
|
offset = len(line)
|
||||||
lineCompleter = child
|
lineCompleter = child
|
||||||
|
|
|
@ -5,6 +5,8 @@ import (
|
||||||
"container/list"
|
"container/list"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/chzyer/readline/runes"
|
||||||
)
|
)
|
||||||
|
|
||||||
type hisItem struct {
|
type hisItem struct {
|
||||||
|
@ -110,7 +112,7 @@ func (o *opHistory) FindHistoryBck(isNewSearch bool, rs []rune, start int) (int,
|
||||||
item = item[:start]
|
item = item[:start]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
idx := RunesIndexBck(item, rs)
|
idx := runes.IndexAllBck(item, rs)
|
||||||
if idx < 0 {
|
if idx < 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -135,7 +137,7 @@ func (o *opHistory) FindHistoryFwd(isNewSearch bool, rs []rune, start int) (int,
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
idx := RunesIndex(item, rs)
|
idx := runes.IndexAll(item, rs)
|
||||||
if idx < 0 {
|
if idx < 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -187,7 +189,7 @@ func (o *opHistory) NewHistory(current []rune) {
|
||||||
prev := back.Prev()
|
prev := back.Prev()
|
||||||
if prev != nil {
|
if prev != nil {
|
||||||
use := o.showItem(o.current.Value.(*hisItem))
|
use := o.showItem(o.current.Value.(*hisItem))
|
||||||
if RunesEqual(use, prev.Value.(*hisItem).Source) {
|
if runes.Equal(use, prev.Value.(*hisItem).Source) {
|
||||||
o.current = o.history.Back()
|
o.current = o.history.Back()
|
||||||
o.current.Value.(*hisItem).Clean()
|
o.current.Value.(*hisItem).Clean()
|
||||||
o.historyVer++
|
o.historyVer++
|
||||||
|
|
16
runebuf.go
16
runebuf.go
|
@ -3,6 +3,8 @@ package readline
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
|
"github.com/chzyer/readline/runes"
|
||||||
)
|
)
|
||||||
|
|
||||||
type runeBufferBck struct {
|
type runeBufferBck struct {
|
||||||
|
@ -44,11 +46,11 @@ func NewRuneBuffer(w io.Writer, prompt string) *RuneBuffer {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RuneBuffer) CurrentWidth(x int) int {
|
func (r *RuneBuffer) CurrentWidth(x int) int {
|
||||||
return RunesWidth(r.buf[:x])
|
return runes.WidthAll(r.buf[:x])
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RuneBuffer) PromptLen() int {
|
func (r *RuneBuffer) PromptLen() int {
|
||||||
return RunesWidth(RunesColorFilter(r.prompt))
|
return runes.WidthAll(runes.ColorFilter(r.prompt))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RuneBuffer) RuneSlice(i int) []rune {
|
func (r *RuneBuffer) RuneSlice(i int) []rune {
|
||||||
|
@ -267,7 +269,7 @@ func (r *RuneBuffer) MoveToLineEnd() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RuneBuffer) LineCount() int {
|
func (r *RuneBuffer) LineCount() int {
|
||||||
return LineCount(RunesWidth(r.buf) + r.PromptLen())
|
return LineCount(runes.WidthAll(r.buf) + r.PromptLen())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RuneBuffer) MoveTo(ch rune, prevChar, reverse bool) (success bool) {
|
func (r *RuneBuffer) MoveTo(ch rune, prevChar, reverse bool) (success bool) {
|
||||||
|
@ -300,7 +302,7 @@ func (r *RuneBuffer) MoveTo(ch rune, prevChar, reverse bool) (success bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RuneBuffer) IdxLine() int {
|
func (r *RuneBuffer) IdxLine() int {
|
||||||
totalWidth := RunesWidth(r.buf[:r.idx]) + r.PromptLen()
|
totalWidth := runes.WidthAll(r.buf[:r.idx]) + r.PromptLen()
|
||||||
w := getWidth()
|
w := getWidth()
|
||||||
if w == 0 {
|
if w == 0 {
|
||||||
return 0
|
return 0
|
||||||
|
@ -336,7 +338,7 @@ func (r *RuneBuffer) output() []byte {
|
||||||
buf.WriteString(string(r.prompt))
|
buf.WriteString(string(r.prompt))
|
||||||
buf.Write([]byte(string(r.buf)))
|
buf.Write([]byte(string(r.buf)))
|
||||||
if len(r.buf) > r.idx {
|
if len(r.buf) > r.idx {
|
||||||
buf.Write(bytes.Repeat([]byte{'\b'}, RunesWidth(r.buf[r.idx:])))
|
buf.Write(runes.Backspace(r.buf[r.idx:]))
|
||||||
}
|
}
|
||||||
return buf.Bytes()
|
return buf.Bytes()
|
||||||
}
|
}
|
||||||
|
@ -350,9 +352,9 @@ func (r *RuneBuffer) Reset() []rune {
|
||||||
|
|
||||||
func (r *RuneBuffer) calWidth(m int) int {
|
func (r *RuneBuffer) calWidth(m int) int {
|
||||||
if m > 0 {
|
if m > 0 {
|
||||||
return RunesWidth(r.buf[r.idx : r.idx+m])
|
return runes.WidthAll(r.buf[r.idx : r.idx+m])
|
||||||
}
|
}
|
||||||
return RunesWidth(r.buf[r.idx+m : r.idx])
|
return runes.WidthAll(r.buf[r.idx+m : r.idx])
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RuneBuffer) SetStyle(start, end int, style string) {
|
func (r *RuneBuffer) SetStyle(start, end int, style string) {
|
||||||
|
|
|
@ -0,0 +1,152 @@
|
||||||
|
package runes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"unicode"
|
||||||
|
)
|
||||||
|
|
||||||
|
func 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 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 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 Index(r rune, rs []rune) int {
|
||||||
|
for i := 0; i < len(rs); i++ {
|
||||||
|
if rs[i] == r {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func 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 := 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 Width(r rune) int {
|
||||||
|
if unicode.IsOneOf(zeroWidth, r) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if unicode.IsOneOf(doubleWidth, r) {
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func WidthAll(r []rune) (length int) {
|
||||||
|
for i := 0; i < len(r); i++ {
|
||||||
|
length += Width(r[i])
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func Backspace(r []rune) []byte {
|
||||||
|
return bytes.Repeat([]byte{'\b'}, WidthAll(r))
|
||||||
|
}
|
||||||
|
|
||||||
|
func Copy(r []rune) []rune {
|
||||||
|
n := make([]rune, len(r))
|
||||||
|
copy(n, r)
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
func HasPrefix(r, prefix []rune) bool {
|
||||||
|
if len(r) < len(prefix) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return Equal(r[:len(prefix)], prefix)
|
||||||
|
}
|
||||||
|
|
||||||
|
func 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 = Copy(candicate[0][:size])
|
||||||
|
for i := 0; i < len(candicate); i++ {
|
||||||
|
n := Copy(candicate[i])
|
||||||
|
copy(n, n[size:])
|
||||||
|
candicate[i] = n[:len(n)-size]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
package runes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type twidth struct {
|
||||||
|
r []rune
|
||||||
|
length int
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRuneWidth(t *testing.T) {
|
||||||
|
runes := []twidth{
|
||||||
|
{[]rune("☭"), 1},
|
||||||
|
{[]rune("a"), 1},
|
||||||
|
{[]rune("你"), 2},
|
||||||
|
{ColorFilter([]rune("☭\033[13;1m你")), 3},
|
||||||
|
}
|
||||||
|
for _, r := range runes {
|
||||||
|
if w := 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) {
|
||||||
|
runes := []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 runes {
|
||||||
|
same, off := 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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
143
utils.go
143
utils.go
|
@ -3,7 +3,6 @@ package readline
|
||||||
import (
|
import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"syscall"
|
"syscall"
|
||||||
"unicode"
|
|
||||||
|
|
||||||
"golang.org/x/crypto/ssh/terminal"
|
"golang.org/x/crypto/ssh/terminal"
|
||||||
)
|
)
|
||||||
|
@ -72,18 +71,6 @@ func escapeKey(r rune) rune {
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
func RunesEqual(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
|
|
||||||
}
|
|
||||||
|
|
||||||
// calculate how many lines for N character
|
// calculate how many lines for N character
|
||||||
func LineCount(w int) int {
|
func LineCount(w int) int {
|
||||||
screenWidth := getWidth()
|
screenWidth := getWidth()
|
||||||
|
@ -94,43 +81,6 @@ func LineCount(w int) int {
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
// Search in runes from end to front
|
|
||||||
func RunesIndexBck(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 RunesIndex(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 IsWordBreak(i rune) bool {
|
func IsWordBreak(i rune) bool {
|
||||||
if i >= 'a' && i <= 'z' {
|
if i >= 'a' && i <= 'z' {
|
||||||
return false
|
return false
|
||||||
|
@ -141,99 +91,6 @@ func IsWordBreak(i rune) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
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 RuneIndex(r rune, rs []rune) int {
|
|
||||||
for i := 0; i < len(rs); i++ {
|
|
||||||
if rs[i] == r {
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
func RunesColorFilter(r []rune) []rune {
|
|
||||||
newr := make([]rune, 0, len(r))
|
|
||||||
for pos := 0; pos < len(r); pos++ {
|
|
||||||
if r[pos] == '\033' && r[pos+1] == '[' {
|
|
||||||
idx := RuneIndex('m', r[pos+2:])
|
|
||||||
if idx == -1 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
pos += idx + 2
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
newr = append(newr, r[pos])
|
|
||||||
}
|
|
||||||
return newr
|
|
||||||
}
|
|
||||||
|
|
||||||
func RuneWidth(r rune) int {
|
|
||||||
if unicode.IsOneOf(zeroWidth, r) {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
if unicode.IsOneOf(doubleWidth, r) {
|
|
||||||
return 2
|
|
||||||
}
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
func RunesWidth(r []rune) (length int) {
|
|
||||||
for i := 0; i < len(r); i++ {
|
|
||||||
length += RuneWidth(r[i])
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func RunesAggregate(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 = RunesCopy(candicate[0][:size])
|
|
||||||
for i := 0; i < len(candicate); i++ {
|
|
||||||
n := RunesCopy(candicate[i])
|
|
||||||
copy(n, n[size:])
|
|
||||||
candicate[i] = n[:len(n)-size]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func RunesCopy(r []rune) []rune {
|
|
||||||
n := make([]rune, len(r))
|
|
||||||
copy(n, r)
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
|
|
||||||
func RunesHasPrefix(r, prefix []rune) bool {
|
|
||||||
if len(r) < len(prefix) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return RunesEqual(r[:len(prefix)], prefix)
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetInt(s []string, def int) int {
|
func GetInt(s []string, def int) int {
|
||||||
if len(s) == 0 {
|
if len(s) == 0 {
|
||||||
return def
|
return def
|
||||||
|
|
|
@ -1,68 +1 @@
|
||||||
package readline
|
package readline
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
type twidth struct {
|
|
||||||
r []rune
|
|
||||||
length int
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRuneWidth(t *testing.T) {
|
|
||||||
runes := []twidth{
|
|
||||||
{[]rune("☭"), 1},
|
|
||||||
{[]rune("a"), 1},
|
|
||||||
{[]rune("你"), 2},
|
|
||||||
{RunesColorFilter([]rune("☭\033[13;1m你")), 3},
|
|
||||||
}
|
|
||||||
for _, r := range runes {
|
|
||||||
if w := RunesWidth(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) {
|
|
||||||
runes := []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 runes {
|
|
||||||
same, off := RunesAggregate(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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue