mirror of https://github.com/chzyer/readline.git
227 lines
3.8 KiB
Go
227 lines
3.8 KiB
Go
package readline
|
|
|
|
import (
|
|
"bytes"
|
|
"unicode"
|
|
"unicode/utf8"
|
|
"golang.org/x/text/width"
|
|
)
|
|
|
|
var runes = Runes{}
|
|
var TabWidth = 4
|
|
|
|
type Runes struct{}
|
|
|
|
func (Runes) EqualRune(a, b rune, fold bool) bool {
|
|
if a == b {
|
|
return true
|
|
}
|
|
if !fold {
|
|
return false
|
|
}
|
|
if a > b {
|
|
a, b = b, a
|
|
}
|
|
if b < utf8.RuneSelf && 'A' <= a && a <= 'Z' {
|
|
if b == a+'a'-'A' {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (r Runes) EqualRuneFold(a, b rune) bool {
|
|
return r.EqualRune(a, b, true)
|
|
}
|
|
|
|
func (r Runes) EqualFold(a, b []rune) bool {
|
|
if len(a) != len(b) {
|
|
return false
|
|
}
|
|
for i := 0; i < len(a); i++ {
|
|
if r.EqualRuneFold(a[i], b[i]) {
|
|
continue
|
|
}
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
func (rs Runes) IndexAllBckEx(r, sub []rune, fold bool) int {
|
|
for i := len(r) - len(sub); i >= 0; i-- {
|
|
found := true
|
|
for j := 0; j < len(sub); j++ {
|
|
if !rs.EqualRune(r[i+j], sub[j], fold) {
|
|
found = false
|
|
break
|
|
}
|
|
}
|
|
if found {
|
|
return i
|
|
}
|
|
}
|
|
return -1
|
|
}
|
|
|
|
// Search in runes from end to front
|
|
func (rs Runes) IndexAllBck(r, sub []rune) int {
|
|
return rs.IndexAllBckEx(r, sub, false)
|
|
}
|
|
|
|
// Search in runes from front to end
|
|
func (rs Runes) IndexAll(r, sub []rune) int {
|
|
return rs.IndexAllEx(r, sub, false)
|
|
}
|
|
|
|
func (rs Runes) IndexAllEx(r, sub []rune, fold bool) 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 !rs.EqualRune(r[i+j], sub[j], fold) {
|
|
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 r == '\t' {
|
|
return TabWidth
|
|
}
|
|
if unicode.IsOneOf(zeroWidth, r) {
|
|
return 0
|
|
}
|
|
switch width.LookupRune(r).Kind() {
|
|
case width.EastAsianWide, width.EastAsianFullwidth:
|
|
return 2
|
|
default:
|
|
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) HasPrefixFold(r, prefix []rune) bool {
|
|
if len(r) < len(prefix) {
|
|
return false
|
|
}
|
|
return runes.EqualFold(r[:len(prefix)], prefix)
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
func (Runes) TrimSpaceLeft(in []rune) []rune {
|
|
firstIndex := len(in)
|
|
for i, r := range in {
|
|
if unicode.IsSpace(r) == false {
|
|
firstIndex = i
|
|
break
|
|
}
|
|
}
|
|
return in[firstIndex:]
|
|
}
|