VictoriaMetrics/vendor/github.com/ergochat/readline/internal/runes/runes.go

265 lines
4.6 KiB
Go

package runes
import (
"bytes"
"golang.org/x/text/width"
"unicode"
"unicode/utf8"
)
var TabWidth = 4
func 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 EqualRuneFold(a, b rune) bool {
return EqualRune(a, b, true)
}
func EqualFold(a, b []rune) bool {
if len(a) != len(b) {
return false
}
for i := 0; i < len(a); i++ {
if EqualRuneFold(a[i], b[i]) {
continue
}
return false
}
return true
}
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
}
func 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 !EqualRune(r[i+j], sub[j], fold) {
found = false
break
}
}
if found {
return i
}
}
return -1
}
// Search in runes from end to front
func IndexAllBck(r, sub []rune) int {
return IndexAllBckEx(r, sub, false)
}
// Search in runes from front to end
func IndexAll(r, sub []rune) int {
return IndexAllEx(r, sub, false)
}
func 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 !EqualRune(r[i+j], sub[j], fold) {
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 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 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 HasPrefixFold(r, prefix []rune) bool {
if len(r) < len(prefix) {
return false
}
return EqualFold(r[:len(prefix)], prefix)
}
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
}
func TrimSpaceLeft(in []rune) []rune {
firstIndex := len(in)
for i, r := range in {
if unicode.IsSpace(r) == false {
firstIndex = i
break
}
}
return in[firstIndex:]
}
func IsWordBreak(i rune) bool {
switch {
case i >= 'a' && i <= 'z':
case i >= 'A' && i <= 'Z':
case i >= '0' && i <= '9':
default:
return true
}
return false
}
// split prompt + runes into lines by screenwidth starting from an offset.
// the prompt should be filtered before passing to only its display runes.
// if you know the width of the next character, pass it in as it is used
// to decide if we generate an extra empty rune array to show next is new
// line.
func SplitByLine(prompt, rs []rune, offset, screenWidth, nextWidth int) [][]rune {
ret := make([][]rune, 0)
prs := append(prompt, rs...)
si := 0
currentWidth := offset
for i, r := range prs {
w := Width(r)
if r == '\n' {
ret = append(ret, prs[si:i+1])
si = i + 1
currentWidth = 0
} else if currentWidth+w > screenWidth {
ret = append(ret, prs[si:i])
si = i
currentWidth = 0
}
currentWidth += w
}
ret = append(ret, prs[si:])
if currentWidth+nextWidth > screenWidth {
ret = append(ret, []rune{})
}
return ret
}