mirror of https://github.com/chzyer/readline.git
support ANSI escape sequence for windows
This commit is contained in:
parent
9edb463230
commit
6eb29567f6
10
runebuf.go
10
runebuf.go
|
@ -246,7 +246,7 @@ func (r *RuneBuffer) IdxLine() int {
|
|||
// the cursor will in the first line, otherwise will in the second line
|
||||
// this situation only occurs in golang's Stdout
|
||||
// TODO: figure out why
|
||||
if totalWidth%w == 0 && len(r.buf) == r.idx {
|
||||
if totalWidth%w == 0 && len(r.buf) == r.idx && !isWindows {
|
||||
line--
|
||||
}
|
||||
|
||||
|
@ -328,17 +328,15 @@ func (r *RuneBuffer) cleanOutput() []byte {
|
|||
buf.Write([]byte("\033[J")) // just like ^k :)
|
||||
|
||||
idxLine := r.IdxLine()
|
||||
|
||||
if idxLine == 0 {
|
||||
buf.WriteString("\033[2K\r")
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
for i := 0; i < idxLine; i++ {
|
||||
buf.WriteString("\033[2K\r")
|
||||
if i != idxLine-1 {
|
||||
buf.WriteByte('\b')
|
||||
}
|
||||
buf.WriteString("\033[2K\r\033[A")
|
||||
}
|
||||
buf.WriteString("\033[2K\r")
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
|
|
71
std_ansi.go
71
std_ansi.go
|
@ -3,14 +3,13 @@
|
|||
package readline
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"unicode/utf8"
|
||||
"unsafe"
|
||||
|
||||
"gopkg.in/bufio.v1"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -28,7 +27,7 @@ type ANSIWriter struct {
|
|||
func NewANSIWriter(w io.Writer) *ANSIWriter {
|
||||
a := &ANSIWriter{
|
||||
target: w,
|
||||
ch: make(chan rune, 1024),
|
||||
ch: make(chan rune),
|
||||
}
|
||||
go a.ioloop()
|
||||
return a
|
||||
|
@ -105,9 +104,30 @@ read:
|
|||
func (a *ANSIWriter) ioloopEscSeq(w *bufio.Writer, r rune, argptr *[]string) bool {
|
||||
arg := *argptr
|
||||
var err error
|
||||
|
||||
if r >= 'A' && r <= 'D' {
|
||||
count := short(GetInt(arg, 1))
|
||||
info, err := GetConsoleScreenBufferInfo()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
switch r {
|
||||
case 'A': // up
|
||||
info.dwCursorPosition.y -= count
|
||||
case 'B': // down
|
||||
info.dwCursorPosition.y += count
|
||||
case 'C': // right
|
||||
info.dwCursorPosition.x += count
|
||||
case 'D': // left
|
||||
info.dwCursorPosition.x -= count
|
||||
}
|
||||
SetConsoleCursorPosition(&info.dwCursorPosition)
|
||||
return false
|
||||
}
|
||||
|
||||
switch r {
|
||||
case 'J':
|
||||
eraseLine()
|
||||
killLines()
|
||||
case 'K':
|
||||
eraseLine()
|
||||
case 'm':
|
||||
|
@ -121,7 +141,11 @@ func (a *ANSIWriter) ioloopEscSeq(w *bufio.Writer, r rune, argptr *[]string) boo
|
|||
}
|
||||
if c >= 30 && c < 40 {
|
||||
color |= ColorTableFg[c-30]
|
||||
} else if c == 0 {
|
||||
} else if c >= 40 && c < 50 {
|
||||
color |= ColorTableBg[c-40]
|
||||
} else if c == 4 {
|
||||
color |= COMMON_LVB_UNDERSCORE
|
||||
} else { // unknown code treat as reset
|
||||
color = ColorTableFg[7]
|
||||
}
|
||||
}
|
||||
|
@ -129,16 +153,13 @@ func (a *ANSIWriter) ioloopEscSeq(w *bufio.Writer, r rune, argptr *[]string) boo
|
|||
break
|
||||
}
|
||||
kernel.SetConsoleTextAttribute(stdout, uintptr(color))
|
||||
case 'A':
|
||||
case 'B':
|
||||
case 'C':
|
||||
case 'D':
|
||||
case '\007':
|
||||
case '\007': // set title
|
||||
case ';':
|
||||
if len(arg) == 0 || arg[len(arg)-1] != "" {
|
||||
arg = append(arg, "")
|
||||
*argptr = arg
|
||||
}
|
||||
fallthrough
|
||||
return true
|
||||
default:
|
||||
if len(arg) == 0 {
|
||||
arg = append(arg, "")
|
||||
|
@ -167,15 +188,39 @@ func (a *ANSIWriter) Write(b []byte) (int, error) {
|
|||
return off, nil
|
||||
}
|
||||
|
||||
func killLines() error {
|
||||
sbi, err := GetConsoleScreenBufferInfo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
size := (sbi.dwCursorPosition.y - sbi.dwSize.y) * sbi.dwSize.x
|
||||
size += sbi.dwCursorPosition.x
|
||||
|
||||
var written int
|
||||
kernel.FillConsoleOutputAttribute(stdout, uintptr(ColorTableFg[7]),
|
||||
uintptr(size),
|
||||
sbi.dwCursorPosition.ptr(),
|
||||
uintptr(unsafe.Pointer(&written)),
|
||||
)
|
||||
return kernel.FillConsoleOutputCharacterW(stdout, uintptr(' '),
|
||||
uintptr(size),
|
||||
sbi.dwCursorPosition.ptr(),
|
||||
uintptr(unsafe.Pointer(&written)),
|
||||
)
|
||||
}
|
||||
|
||||
func eraseLine() error {
|
||||
sbi, err := GetConsoleScreenBufferInfo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
size := sbi.dwSize.x
|
||||
sbi.dwCursorPosition.x = 0
|
||||
var written int
|
||||
return kernel.FillConsoleOutputCharacterW(stdout, uintptr(' '),
|
||||
uintptr(sbi.dwSize.x-sbi.dwCursorPosition.x),
|
||||
uintptr(size),
|
||||
sbi.dwCursorPosition.ptr(),
|
||||
uintptr(unsafe.Pointer(&written)),
|
||||
)
|
||||
|
@ -192,6 +237,8 @@ const (
|
|||
COLOR_BGREEN = 0x0020
|
||||
COLOR_BRED = 0x0040
|
||||
COLOR_BINTENSITY = 0x0080
|
||||
|
||||
COMMON_LVB_UNDERSCORE = 0x8000
|
||||
)
|
||||
|
||||
var ColorTableFg = []word{
|
||||
|
|
15
utils.go
15
utils.go
|
@ -1,6 +1,7 @@
|
|||
package readline
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"syscall"
|
||||
"unicode"
|
||||
|
||||
|
@ -8,7 +9,8 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
StdinFd = int(uintptr(syscall.Stdin))
|
||||
StdinFd = int(uintptr(syscall.Stdin))
|
||||
isWindows = false
|
||||
)
|
||||
|
||||
// IsTerminal returns true if the given file descriptor is a terminal.
|
||||
|
@ -231,3 +233,14 @@ func RunesHasPrefix(r, prefix []rune) bool {
|
|||
}
|
||||
return RunesEqual(r[:len(prefix)], prefix)
|
||||
}
|
||||
|
||||
func GetInt(s []string, def int) int {
|
||||
if len(s) == 0 {
|
||||
return def
|
||||
}
|
||||
c, err := strconv.Atoi(s[0])
|
||||
if err != nil {
|
||||
return def
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
|
|
@ -2,6 +2,10 @@
|
|||
|
||||
package readline
|
||||
|
||||
func init() {
|
||||
isWindows = true
|
||||
}
|
||||
|
||||
// get width of the terminal
|
||||
func getWidth() int {
|
||||
info, _ := GetConsoleScreenBufferInfo()
|
||||
|
|
|
@ -18,11 +18,14 @@ type Kernel struct {
|
|||
SetConsoleTextAttribute,
|
||||
GetConsoleScreenBufferInfo,
|
||||
FillConsoleOutputCharacterW,
|
||||
FillConsoleOutputAttribute,
|
||||
GetConsoleCursorInfo,
|
||||
GetStdHandle CallFunc
|
||||
}
|
||||
|
||||
type short int16
|
||||
type word uint16
|
||||
type dword uint32
|
||||
|
||||
type _COORD struct {
|
||||
x short
|
||||
|
@ -48,6 +51,11 @@ type _SMALL_RECT struct {
|
|||
bottom short
|
||||
}
|
||||
|
||||
type _CONSOLE_CURSOR_INFO struct {
|
||||
dwSize dword
|
||||
bVisible bool
|
||||
}
|
||||
|
||||
type CallFunc func(u ...uintptr) error
|
||||
|
||||
func NewKernel() *Kernel {
|
||||
|
@ -101,3 +109,13 @@ func GetConsoleScreenBufferInfo() (*_CONSOLE_SCREEN_BUFFER_INFO, error) {
|
|||
)
|
||||
return t, err
|
||||
}
|
||||
|
||||
func GetConsoleCursorInfo() (*_CONSOLE_CURSOR_INFO, error) {
|
||||
t := new(_CONSOLE_CURSOR_INFO)
|
||||
err := kernel.GetConsoleCursorInfo(stdout, uintptr(unsafe.Pointer(t)))
|
||||
return t, err
|
||||
}
|
||||
|
||||
func SetConsoleCursorPosition(c *_COORD) error {
|
||||
return kernel.SetConsoleCursorPosition(stdout, c.ptr())
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue