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