package readline import ( "container/list" "fmt" "os" "syscall" "time" "unicode" "unsafe" "golang.org/x/crypto/ssh/terminal" ) // IsTerminal returns true if the given file descriptor is a terminal. func IsTerminal(fd int) bool { return terminal.IsTerminal(fd) } func MakeRaw(fd int) (*terminal.State, error) { return terminal.MakeRaw(fd) } func Restore(fd int, state *terminal.State) error { err := terminal.Restore(fd, state) if err != nil { if err.Error() == "errno 0" { err = nil } } return nil } func IsPrintable(key rune) bool { isInSurrogateArea := key >= 0xd800 && key <= 0xdbff return key >= 32 && !isInSurrogateArea } func escapeExKey(r rune) rune { switch r { case 'D': r = CharBackward case 'C': r = CharForward case 'A': r = CharPrev case 'B': r = CharNext } return r } func escapeKey(r rune) rune { switch r { case 'b': r = MetaPrev case 'f': r = MetaNext case 'd': r = MetaDelete case CharTranspose: r = MetaTranspose case CharBackspace: r = MetaBackspace case CharEsc: } return r } func Debug(o ...interface{}) { f, _ := os.OpenFile("debug.tmp", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) fmt.Fprintln(f, o...) f.Close() } type winsize struct { Row uint16 Col uint16 Xpixel uint16 Ypixel uint16 } func getWidth() int { ws := &winsize{} retCode, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(syscall.Stdin), uintptr(syscall.TIOCGWINSZ), uintptr(unsafe.Pointer(ws))) if int(retCode) == -1 { panic(errno) } return int(ws.Col) } func debugList(l *list.List) { idx := 0 for e := l.Front(); e != nil; e = e.Next() { Debug(idx, fmt.Sprintf("%+v", e.Value)) idx++ } } func equalRunes(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 sleep(n int) { Debug(n) time.Sleep(2000 * time.Millisecond) } func LineCount(w int) int { screenWidth := getWidth() r := w / screenWidth if w%screenWidth != 0 { r++ } return r } 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 } 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 { if i >= 'a' && i <= 'z' { return false } if i >= 'A' && i <= 'Z' { return false } 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 AggRunes(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 = CopyRunes(candicate[0][:size]) for i := 0; i < len(candicate); i++ { n := CopyRunes(candicate[i]) copy(n, n[size:]) candicate[i] = n[:len(n)-size] } } return } func CopyRunes(r []rune) []rune { n := make([]rune, len(r)) copy(n, r) return n } func EqualRunes(r, r2 []rune) bool { if len(r) != len(r2) { return false } for idx := range r { if r[idx] != r2[idx] { return false } } return true } func RunesHasPrefix(r, prefix []rune) bool { if len(r) < len(prefix) { return false } return EqualRunes(r[:len(prefix)], prefix) }