windows support
This commit is contained in:
parent
9ec1dc9dc5
commit
df1341fe00
6
emacs.go
6
emacs.go
@ -1,9 +1,5 @@
|
|||||||
package prompt
|
package prompt
|
||||||
|
|
||||||
import (
|
|
||||||
"syscall"
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
========
|
========
|
||||||
@ -114,7 +110,7 @@ var emacsKeyBindings = []KeyBind{
|
|||||||
{
|
{
|
||||||
Key: ControlL,
|
Key: ControlL,
|
||||||
Fn: func(buf *Buffer) {
|
Fn: func(buf *Buffer) {
|
||||||
out := &VT100Writer{fd: syscall.Stdout}
|
out := NewVT100StandardOutputWriter()
|
||||||
out.EraseScreen()
|
out.EraseScreen()
|
||||||
out.CursorGoTo(0, 0)
|
out.CursorGoTo(0, 0)
|
||||||
},
|
},
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
package prompt
|
package prompt
|
||||||
|
|
||||||
import "syscall"
|
|
||||||
|
|
||||||
// Option is the type to replace default parameters.
|
// Option is the type to replace default parameters.
|
||||||
// prompt.New accepts any number of options (this is functional option pattern).
|
// prompt.New accepts any number of options (this is functional option pattern).
|
||||||
type Option func(prompt *Prompt) error
|
type Option func(prompt *Prompt) error
|
||||||
@ -176,10 +174,10 @@ func OptionAddKeyBind(b ...KeyBind) Option {
|
|||||||
// New returns a Prompt with powerful auto-completion.
|
// New returns a Prompt with powerful auto-completion.
|
||||||
func New(executor Executor, completer Completer, opts ...Option) *Prompt {
|
func New(executor Executor, completer Completer, opts ...Option) *Prompt {
|
||||||
pt := &Prompt{
|
pt := &Prompt{
|
||||||
in: &VT100Parser{fd: syscall.Stdin},
|
in: NewVT100StandardInputParser(),
|
||||||
renderer: &Render{
|
renderer: &Render{
|
||||||
prefix: "> ",
|
prefix: "> ",
|
||||||
out: &VT100Writer{fd: syscall.Stdout},
|
out: NewVT100StandardOutputWriter(),
|
||||||
prefixTextColor: Blue,
|
prefixTextColor: Blue,
|
||||||
prefixBGColor: DefaultColor,
|
prefixBGColor: DefaultColor,
|
||||||
inputTextColor: DefaultColor,
|
inputTextColor: DefaultColor,
|
||||||
|
69
prompt.go
69
prompt.go
@ -4,8 +4,6 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
|
||||||
"syscall"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -59,12 +57,12 @@ func (p *Prompt) Run() {
|
|||||||
|
|
||||||
bufCh := make(chan []byte, 128)
|
bufCh := make(chan []byte, 128)
|
||||||
stopReadBufCh := make(chan struct{})
|
stopReadBufCh := make(chan struct{})
|
||||||
go readBuffer(bufCh, stopReadBufCh)
|
go p.readBuffer(bufCh, stopReadBufCh)
|
||||||
|
|
||||||
exitCh := make(chan int)
|
exitCh := make(chan int)
|
||||||
winSizeCh := make(chan *WinSize)
|
winSizeCh := make(chan *WinSize)
|
||||||
stopHandleSignalCh := make(chan struct{})
|
stopHandleSignalCh := make(chan struct{})
|
||||||
go handleSignals(p.in, exitCh, winSizeCh, stopHandleSignalCh)
|
go p.handleSignals(exitCh, winSizeCh, stopHandleSignalCh)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
@ -87,8 +85,8 @@ func (p *Prompt) Run() {
|
|||||||
|
|
||||||
// Set raw mode
|
// Set raw mode
|
||||||
p.in.Setup()
|
p.in.Setup()
|
||||||
go readBuffer(bufCh, stopReadBufCh)
|
go p.readBuffer(bufCh, stopReadBufCh)
|
||||||
go handleSignals(p.in, exitCh, winSizeCh, stopHandleSignalCh)
|
go p.handleSignals(exitCh, winSizeCh, stopHandleSignalCh)
|
||||||
} else {
|
} else {
|
||||||
p.completion.Update(*p.buf.Document())
|
p.completion.Update(*p.buf.Document())
|
||||||
p.renderer.Render(p.buf, p.completion)
|
p.renderer.Render(p.buf, p.completion)
|
||||||
@ -217,7 +215,7 @@ func (p *Prompt) Input() string {
|
|||||||
p.renderer.Render(p.buf, p.completion)
|
p.renderer.Render(p.buf, p.completion)
|
||||||
bufCh := make(chan []byte, 128)
|
bufCh := make(chan []byte, 128)
|
||||||
stopReadBufCh := make(chan struct{})
|
stopReadBufCh := make(chan struct{})
|
||||||
go readBuffer(bufCh, stopReadBufCh)
|
go p.readBuffer(bufCh, stopReadBufCh)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
@ -249,60 +247,3 @@ func (p *Prompt) tearDown() {
|
|||||||
p.in.TearDown()
|
p.in.TearDown()
|
||||||
p.renderer.TearDown()
|
p.renderer.TearDown()
|
||||||
}
|
}
|
||||||
|
|
||||||
func readBuffer(bufCh chan []byte, stopCh chan struct{}) {
|
|
||||||
buf := make([]byte, maxReadBytes)
|
|
||||||
|
|
||||||
log.Printf("[INFO] readBuffer start")
|
|
||||||
for {
|
|
||||||
time.Sleep(10 * time.Millisecond)
|
|
||||||
select {
|
|
||||||
case <-stopCh:
|
|
||||||
log.Print("[INFO] stop readBuffer")
|
|
||||||
return
|
|
||||||
default:
|
|
||||||
if n, err := syscall.Read(syscall.Stdin, buf); err == nil {
|
|
||||||
cbuf := make([]byte, n)
|
|
||||||
copy(cbuf, buf[:n])
|
|
||||||
bufCh <- cbuf
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleSignals(in ConsoleParser, exitCh chan int, winSizeCh chan *WinSize, stop chan struct{}) {
|
|
||||||
sigCh := make(chan os.Signal, 1)
|
|
||||||
signal.Notify(
|
|
||||||
sigCh,
|
|
||||||
syscall.SIGINT,
|
|
||||||
syscall.SIGTERM,
|
|
||||||
syscall.SIGQUIT,
|
|
||||||
syscall.SIGWINCH,
|
|
||||||
)
|
|
||||||
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-stop:
|
|
||||||
log.Println("[INFO] stop handleSignals")
|
|
||||||
return
|
|
||||||
case s := <-sigCh:
|
|
||||||
switch s {
|
|
||||||
case syscall.SIGINT: // kill -SIGINT XXXX or Ctrl+c
|
|
||||||
log.Println("[SIGNAL] Catch SIGINT")
|
|
||||||
exitCh <- 0
|
|
||||||
|
|
||||||
case syscall.SIGTERM: // kill -SIGTERM XXXX
|
|
||||||
log.Println("[SIGNAL] Catch SIGTERM")
|
|
||||||
exitCh <- 1
|
|
||||||
|
|
||||||
case syscall.SIGQUIT: // kill -SIGQUIT XXXX
|
|
||||||
log.Println("[SIGNAL] Catch SIGQUIT")
|
|
||||||
exitCh <- 0
|
|
||||||
|
|
||||||
case syscall.SIGWINCH:
|
|
||||||
log.Println("[SIGNAL] Catch SIGWINCH")
|
|
||||||
winSizeCh <- in.GetWinSize()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
29
prompt_posix.go
Normal file
29
prompt_posix.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package prompt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (p *Prompt) readBuffer(bufCh chan []byte, stopCh chan struct{}) {
|
||||||
|
buf := make([]byte, maxReadBytes)
|
||||||
|
|
||||||
|
log.Printf("[INFO] readBuffer start")
|
||||||
|
for {
|
||||||
|
time.Sleep(10 * time.Millisecond)
|
||||||
|
select {
|
||||||
|
case <-stopCh:
|
||||||
|
log.Print("[INFO] stop readBuffer")
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
if n, err := syscall.Read(syscall.Stdin, buf); err == nil {
|
||||||
|
cbuf := make([]byte, n)
|
||||||
|
copy(cbuf, buf[:n])
|
||||||
|
bufCh <- cbuf
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
30
prompt_windows.go
Normal file
30
prompt_windows.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// +build windows
|
||||||
|
|
||||||
|
package prompt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
"unicode/utf8"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (p *Prompt) readBuffer(bufCh chan []byte, stopCh chan struct{}) {
|
||||||
|
buf := make([]byte, maxReadBytes)
|
||||||
|
|
||||||
|
log.Printf("[INFO] readBuffer start")
|
||||||
|
for {
|
||||||
|
time.Sleep(10 * time.Millisecond)
|
||||||
|
select {
|
||||||
|
case <-stopCh:
|
||||||
|
log.Print("[INFO] stop readBuffer")
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
if r, err := p.in.(*VT100Parser).tty.ReadRune(); err == nil {
|
||||||
|
n := utf8.EncodeRune(buf[:], r)
|
||||||
|
cbuf := make([]byte, n)
|
||||||
|
copy(cbuf, buf[:n])
|
||||||
|
bufCh <- cbuf
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
48
signal_posix.go
Normal file
48
signal_posix.go
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
// +build !windows
|
||||||
|
|
||||||
|
package prompt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (p *Prompt) handleSignals(exitCh chan int, winSizeCh chan *WinSize, stop chan struct{}) {
|
||||||
|
in := p.in
|
||||||
|
sigCh := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(
|
||||||
|
sigCh,
|
||||||
|
syscall.SIGINT,
|
||||||
|
syscall.SIGTERM,
|
||||||
|
syscall.SIGQUIT,
|
||||||
|
syscall.SIGWINCH,
|
||||||
|
)
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-stop:
|
||||||
|
log.Println("[INFO] stop handleSignals")
|
||||||
|
return
|
||||||
|
case s := <-sigCh:
|
||||||
|
switch s {
|
||||||
|
case syscall.SIGINT: // kill -SIGINT XXXX or Ctrl+c
|
||||||
|
log.Println("[SIGNAL] Catch SIGINT")
|
||||||
|
exitCh <- 0
|
||||||
|
|
||||||
|
case syscall.SIGTERM: // kill -SIGTERM XXXX
|
||||||
|
log.Println("[SIGNAL] Catch SIGTERM")
|
||||||
|
exitCh <- 1
|
||||||
|
|
||||||
|
case syscall.SIGQUIT: // kill -SIGQUIT XXXX
|
||||||
|
log.Println("[SIGNAL] Catch SIGQUIT")
|
||||||
|
exitCh <- 0
|
||||||
|
|
||||||
|
case syscall.SIGWINCH:
|
||||||
|
log.Println("[SIGNAL] Catch SIGWINCH")
|
||||||
|
winSizeCh <- in.GetWinSize()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
41
signal_windows.go
Normal file
41
signal_windows.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
// +build windows
|
||||||
|
|
||||||
|
package prompt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (p *Prompt) handleSignals(exitCh chan int, winSizeCh chan *WinSize, stop chan struct{}) {
|
||||||
|
sigCh := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(
|
||||||
|
sigCh,
|
||||||
|
syscall.SIGINT,
|
||||||
|
syscall.SIGTERM,
|
||||||
|
syscall.SIGQUIT,
|
||||||
|
)
|
||||||
|
|
||||||
|
for {
|
||||||
|
s := <-sigCh
|
||||||
|
switch s {
|
||||||
|
case syscall.SIGINT: // kill -SIGINT XXXX or Ctrl+c
|
||||||
|
log.Println("[SIGNAL] Catch SIGINT")
|
||||||
|
exitCh <- 0
|
||||||
|
|
||||||
|
case syscall.SIGTERM: // kill -SIGTERM XXXX
|
||||||
|
log.Println("[SIGNAL] Catch SIGTERM")
|
||||||
|
exitCh <- 1
|
||||||
|
|
||||||
|
case syscall.SIGQUIT: // kill -SIGQUIT XXXX
|
||||||
|
log.Println("[SIGNAL] Catch SIGQUIT")
|
||||||
|
exitCh <- 0
|
||||||
|
|
||||||
|
default:
|
||||||
|
time.Sleep(10 * time.Millisecond)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,5 @@
|
|||||||
|
// +build !windows
|
||||||
|
|
||||||
package prompt
|
package prompt
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
192
vt100_input_windows.go
Normal file
192
vt100_input_windows.go
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
// +build windows
|
||||||
|
|
||||||
|
package prompt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/mattn/go-tty"
|
||||||
|
)
|
||||||
|
|
||||||
|
type VT100Parser struct {
|
||||||
|
fd syscall.Handle
|
||||||
|
tty *tty.TTY
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *VT100Parser) Setup() error {
|
||||||
|
tty, err := tty.Open()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
t.tty = tty
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *VT100Parser) TearDown() error {
|
||||||
|
return t.tty.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *VT100Parser) GetKey(b []byte) Key {
|
||||||
|
for _, k := range asciiSequences {
|
||||||
|
if bytes.Compare(k.ASCIICode, b) == 0 {
|
||||||
|
return k.Key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NotDefined
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetWinSize returns winsize struct which is the response of ioctl(2).
|
||||||
|
func (t *VT100Parser) GetWinSize() *WinSize {
|
||||||
|
w, h, err := t.tty.Size()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return &WinSize{
|
||||||
|
Row: uint16(h),
|
||||||
|
Col: uint16(w),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var asciiSequences []*ASCIICode = []*ASCIICode{
|
||||||
|
{Key: Escape, ASCIICode: []byte{0x1b}},
|
||||||
|
|
||||||
|
{Key: ControlSpace, ASCIICode: []byte{0x00}},
|
||||||
|
{Key: ControlA, ASCIICode: []byte{0x1}},
|
||||||
|
{Key: ControlB, ASCIICode: []byte{0x2}},
|
||||||
|
{Key: ControlC, ASCIICode: []byte{0x3}},
|
||||||
|
{Key: ControlD, ASCIICode: []byte{0x4}},
|
||||||
|
{Key: ControlE, ASCIICode: []byte{0x5}},
|
||||||
|
{Key: ControlF, ASCIICode: []byte{0x6}},
|
||||||
|
{Key: ControlG, ASCIICode: []byte{0x7}},
|
||||||
|
{Key: ControlH, ASCIICode: []byte{0x8}},
|
||||||
|
//{Key: ControlI, ASCIICode: []byte{0x9}},
|
||||||
|
//{Key: ControlJ, ASCIICode: []byte{0xa}},
|
||||||
|
{Key: ControlK, ASCIICode: []byte{0xb}},
|
||||||
|
{Key: ControlL, ASCIICode: []byte{0xc}},
|
||||||
|
{Key: ControlM, ASCIICode: []byte{0xd}},
|
||||||
|
{Key: ControlN, ASCIICode: []byte{0xe}},
|
||||||
|
{Key: ControlO, ASCIICode: []byte{0xf}},
|
||||||
|
{Key: ControlP, ASCIICode: []byte{0x10}},
|
||||||
|
{Key: ControlQ, ASCIICode: []byte{0x11}},
|
||||||
|
{Key: ControlR, ASCIICode: []byte{0x12}},
|
||||||
|
{Key: ControlS, ASCIICode: []byte{0x13}},
|
||||||
|
{Key: ControlT, ASCIICode: []byte{0x14}},
|
||||||
|
{Key: ControlU, ASCIICode: []byte{0x15}},
|
||||||
|
{Key: ControlV, ASCIICode: []byte{0x16}},
|
||||||
|
{Key: ControlW, ASCIICode: []byte{0x17}},
|
||||||
|
{Key: ControlX, ASCIICode: []byte{0x18}},
|
||||||
|
{Key: ControlY, ASCIICode: []byte{0x19}},
|
||||||
|
{Key: ControlZ, ASCIICode: []byte{0x1a}},
|
||||||
|
|
||||||
|
{Key: ControlBackslash, ASCIICode: []byte{0x1c}},
|
||||||
|
{Key: ControlSquareClose, ASCIICode: []byte{0x1d}},
|
||||||
|
{Key: ControlCircumflex, ASCIICode: []byte{0x1e}},
|
||||||
|
{Key: ControlUnderscore, ASCIICode: []byte{0x1f}},
|
||||||
|
{Key: Backspace, ASCIICode: []byte{0x7f}},
|
||||||
|
|
||||||
|
{Key: Up, ASCIICode: []byte{0x1b, 0x5b, 0x41}},
|
||||||
|
{Key: Down, ASCIICode: []byte{0x1b, 0x5b, 0x42}},
|
||||||
|
{Key: Right, ASCIICode: []byte{0x1b, 0x5b, 0x43}},
|
||||||
|
{Key: Left, ASCIICode: []byte{0x1b, 0x5b, 0x44}},
|
||||||
|
{Key: Home, ASCIICode: []byte{0x1b, 0x5b, 0x48}},
|
||||||
|
{Key: Home, ASCIICode: []byte{0x1b, 0x4f, 0x48}},
|
||||||
|
{Key: End, ASCIICode: []byte{0x1b, 0x5b, 0x70}},
|
||||||
|
{Key: End, ASCIICode: []byte{0x1b, 0x4f, 0x70}},
|
||||||
|
|
||||||
|
{Key: Enter, ASCIICode: []byte{0xa}},
|
||||||
|
{Key: Delete, ASCIICode: []byte{0x1b, 0x5b, 0x33, 0x7e}},
|
||||||
|
{Key: ShiftDelete, ASCIICode: []byte{0x1b, 0x5b, 0x33, 0x3b, 0x02, 0x7e}},
|
||||||
|
{Key: ControlDelete, ASCIICode: []byte{0x1b, 0x5b, 0x33, 0x3b, 0x05, 0x7e}},
|
||||||
|
{Key: Home, ASCIICode: []byte{0x1b, 0x5b, 0x01, 0x7e}},
|
||||||
|
{Key: End, ASCIICode: []byte{0x1b, 0x5b, 0x04, 0x7e}},
|
||||||
|
{Key: PageUp, ASCIICode: []byte{0x1b, 0x5b, 0x05, 0x7e}},
|
||||||
|
{Key: PageDown, ASCIICode: []byte{0x1b, 0x5b, 0x06, 0x7e}},
|
||||||
|
{Key: Home, ASCIICode: []byte{0x1b, 0x5b, 0x07, 0x7e}},
|
||||||
|
{Key: End, ASCIICode: []byte{0x1b, 0x5b, 0x09, 0x7e}},
|
||||||
|
{Key: Tab, ASCIICode: []byte{0x9}},
|
||||||
|
{Key: BackTab, ASCIICode: []byte{0x1b, 0x5b, 0x5a}},
|
||||||
|
{Key: Insert, ASCIICode: []byte{0x1b, 0x5b, 0x02, 0x7e}},
|
||||||
|
|
||||||
|
{Key: F1, ASCIICode: []byte{0x1b, 0x4f, 0x50}},
|
||||||
|
{Key: F2, ASCIICode: []byte{0x1b, 0x4f, 0x51}},
|
||||||
|
{Key: F3, ASCIICode: []byte{0x1b, 0x4f, 0x52}},
|
||||||
|
{Key: F4, ASCIICode: []byte{0x1b, 0x4f, 0x53}},
|
||||||
|
|
||||||
|
{Key: F1, ASCIICode: []byte{0x1b, 0x4f, 0x50, 0x41}}, // Linux console
|
||||||
|
{Key: F2, ASCIICode: []byte{0x1b, 0x5b, 0x5b, 0x42}}, // Linux console
|
||||||
|
{Key: F3, ASCIICode: []byte{0x1b, 0x5b, 0x5b, 0x43}}, // Linux console
|
||||||
|
{Key: F4, ASCIICode: []byte{0x1b, 0x5b, 0x5b, 0x44}}, // Linux console
|
||||||
|
{Key: F5, ASCIICode: []byte{0x1b, 0x5b, 0x5b, 0x45}}, // Linux console
|
||||||
|
|
||||||
|
{Key: F1, ASCIICode: []byte{0x1b, 0x5b, 0x11, 0x7e}}, // rxvt-unicode
|
||||||
|
{Key: F2, ASCIICode: []byte{0x1b, 0x5b, 0x12, 0x7e}}, // rxvt-unicode
|
||||||
|
{Key: F3, ASCIICode: []byte{0x1b, 0x5b, 0x13, 0x7e}}, // rxvt-unicode
|
||||||
|
{Key: F4, ASCIICode: []byte{0x1b, 0x5b, 0x14, 0x7e}}, // rxvt-unicode
|
||||||
|
|
||||||
|
{Key: F5, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x35, 0x7e}},
|
||||||
|
{Key: F6, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x37, 0x7e}},
|
||||||
|
{Key: F7, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x38, 0x7e}},
|
||||||
|
{Key: F8, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x39, 0x7e}},
|
||||||
|
{Key: F9, ASCIICode: []byte{0x1b, 0x5b, 0x32, 0x30, 0x7e}},
|
||||||
|
{Key: F10, ASCIICode: []byte{0x1b, 0x5b, 0x32, 0x31, 0x7e}},
|
||||||
|
{Key: F11, ASCIICode: []byte{0x1b, 0x5b, 0x32, 0x32, 0x7e}},
|
||||||
|
{Key: F12, ASCIICode: []byte{0x1b, 0x5b, 0x32, 0x34, 0x7e, 0x8}},
|
||||||
|
{Key: F13, ASCIICode: []byte{0x1b, 0x5b, 0x25, 0x7e}},
|
||||||
|
{Key: F14, ASCIICode: []byte{0x1b, 0x5b, 0x26, 0x7e}},
|
||||||
|
{Key: F15, ASCIICode: []byte{0x1b, 0x5b, 0x28, 0x7e}},
|
||||||
|
{Key: F16, ASCIICode: []byte{0x1b, 0x5b, 0x29, 0x7e}},
|
||||||
|
{Key: F17, ASCIICode: []byte{0x1b, 0x5b, 0x31, 0x7e}},
|
||||||
|
{Key: F18, ASCIICode: []byte{0x1b, 0x5b, 0x32, 0x7e}},
|
||||||
|
{Key: F19, ASCIICode: []byte{0x1b, 0x5b, 0x33, 0x7e}},
|
||||||
|
{Key: F20, ASCIICode: []byte{0x1b, 0x5b, 0x34, 0x7e}},
|
||||||
|
|
||||||
|
// Xterm
|
||||||
|
{Key: F13, ASCIICode: []byte{0x1b, 0x5b, 0x01, 0x3b, 0x02, 0x50}},
|
||||||
|
{Key: F14, ASCIICode: []byte{0x1b, 0x5b, 0x01, 0x3b, 0x02, 0x51}},
|
||||||
|
// &ASCIICode{Key: F15, ASCIICode: []byte{0x1b, 0x5b, 0x01, 0x3b, 0x02, 0x52}}, // Conflicts with CPR response
|
||||||
|
{Key: F16, ASCIICode: []byte{0x1b, 0x5b, 0x01, 0x3b, 0x02, 0x52}},
|
||||||
|
{Key: F17, ASCIICode: []byte{0x1b, 0x5b, 0x15, 0x3b, 0x02, 0x7e}},
|
||||||
|
{Key: F18, ASCIICode: []byte{0x1b, 0x5b, 0x17, 0x3b, 0x02, 0x7e}},
|
||||||
|
{Key: F19, ASCIICode: []byte{0x1b, 0x5b, 0x18, 0x3b, 0x02, 0x7e}},
|
||||||
|
{Key: F20, ASCIICode: []byte{0x1b, 0x5b, 0x19, 0x3b, 0x02, 0x7e}},
|
||||||
|
{Key: F21, ASCIICode: []byte{0x1b, 0x5b, 0x20, 0x3b, 0x02, 0x7e}},
|
||||||
|
{Key: F22, ASCIICode: []byte{0x1b, 0x5b, 0x21, 0x3b, 0x02, 0x7e}},
|
||||||
|
{Key: F23, ASCIICode: []byte{0x1b, 0x5b, 0x23, 0x3b, 0x02, 0x7e}},
|
||||||
|
{Key: F24, ASCIICode: []byte{0x1b, 0x5b, 0x24, 0x3b, 0x02, 0x7e}},
|
||||||
|
|
||||||
|
{Key: ControlUp, ASCIICode: []byte{0x1b, 0x5b, 0x01, 0x3b, 0x5a}},
|
||||||
|
{Key: ControlDown, ASCIICode: []byte{0x1b, 0x5b, 0x01, 0x3b, 0x5b}},
|
||||||
|
{Key: ControlRight, ASCIICode: []byte{0x1b, 0x5b, 0x01, 0x3b, 0x5c}},
|
||||||
|
{Key: ControlLeft, ASCIICode: []byte{0x1b, 0x5b, 0x01, 0x3b, 0x5d}},
|
||||||
|
|
||||||
|
{Key: ShiftUp, ASCIICode: []byte{0x1b, 0x5b, 0x01, 0x2a}},
|
||||||
|
{Key: ShiftDown, ASCIICode: []byte{0x1b, 0x5b, 0x01, 0x2b}},
|
||||||
|
{Key: ShiftRight, ASCIICode: []byte{0x1b, 0x5b, 0x01, 0x2c}},
|
||||||
|
{Key: ShiftLeft, ASCIICode: []byte{0x1b, 0x5b, 0x01, 0x2d}},
|
||||||
|
|
||||||
|
// Tmux sends following keystrokes when control+arrow is pressed, but for
|
||||||
|
// Emacs ansi-term sends the same sequences for normal arrow keys. Consider
|
||||||
|
// it a normal arrow press, because that's more important.
|
||||||
|
{Key: Up, ASCIICode: []byte{0x1b, 0x4f, 0x41}},
|
||||||
|
{Key: Down, ASCIICode: []byte{0x1b, 0x4f, 0x42}},
|
||||||
|
{Key: Right, ASCIICode: []byte{0x1b, 0x4f, 0x43}},
|
||||||
|
{Key: Left, ASCIICode: []byte{0x1b, 0x4f, 0x44}},
|
||||||
|
|
||||||
|
{Key: ControlUp, ASCIICode: []byte{0x1b, 0x5b, 0x05, 0x41}},
|
||||||
|
{Key: ControlDown, ASCIICode: []byte{0x1b, 0x5b, 0x05, 0x42}},
|
||||||
|
{Key: ControlRight, ASCIICode: []byte{0x1b, 0x5b, 0x05, 0x43}},
|
||||||
|
{Key: ControlLeft, ASCIICode: []byte{0x1b, 0x5b, 0x05, 0x44}},
|
||||||
|
|
||||||
|
{Key: ControlRight, ASCIICode: []byte{0x1b, 0x5b, 0x4f, 0x63}}, // rxvt
|
||||||
|
{Key: ControlLeft, ASCIICode: []byte{0x1b, 0x5b, 0x4f, 0x64}}, // rxvt
|
||||||
|
|
||||||
|
{Key: Ignore, ASCIICode: []byte{0x1b, 0x5b, 0x45}}, // Xterm
|
||||||
|
{Key: Ignore, ASCIICode: []byte{0x1b, 0x5b, 0x46}}, // Linux console
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewVT100StandardInputParser() *VT100Parser {
|
||||||
|
return &VT100Parser{
|
||||||
|
fd: syscall.Stdin,
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,5 @@
|
|||||||
|
// +build !windows
|
||||||
|
|
||||||
package prompt
|
package prompt
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
299
vt100_output_windows.go
Normal file
299
vt100_output_windows.go
Normal file
@ -0,0 +1,299 @@
|
|||||||
|
// +build windows
|
||||||
|
|
||||||
|
package prompt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/mattn/go-colorable"
|
||||||
|
)
|
||||||
|
|
||||||
|
type VT100Writer struct {
|
||||||
|
out io.Writer
|
||||||
|
buffer []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *VT100Writer) WriteRaw(data []byte) {
|
||||||
|
w.buffer = append(w.buffer, data...)
|
||||||
|
// Flush because sometimes the render is broken when a large amount data in buffer.
|
||||||
|
w.Flush()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *VT100Writer) Write(data []byte) {
|
||||||
|
w.WriteRaw(byteFilter(data, writeFilter))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *VT100Writer) WriteRawStr(data string) {
|
||||||
|
w.WriteRaw([]byte(data))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *VT100Writer) WriteStr(data string) {
|
||||||
|
w.Write([]byte(data))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *VT100Writer) Flush() error {
|
||||||
|
_, err := w.out.Write(w.buffer)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
w.buffer = []byte{}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Erase */
|
||||||
|
|
||||||
|
func (w *VT100Writer) EraseScreen() {
|
||||||
|
w.WriteRaw([]byte{0x1b, 0x5b, 0x32, 0x4a})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *VT100Writer) EraseUp() {
|
||||||
|
w.WriteRaw([]byte{0x1b, 0x5b, 0x31, 0x4a})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *VT100Writer) EraseDown() {
|
||||||
|
w.WriteRaw([]byte{0x1b, 0x5b, 0x4a})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *VT100Writer) EraseStartOfLine() {
|
||||||
|
w.WriteRaw([]byte{0x1b, 0x5b, 0x31, 0x4b})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *VT100Writer) EraseEndOfLine() {
|
||||||
|
w.WriteRaw([]byte{0x1b, 0x5b, 0x4b})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *VT100Writer) EraseLine() {
|
||||||
|
w.WriteRaw([]byte{0x1b, 0x5b, 0x32, 0x4b})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Cursor */
|
||||||
|
|
||||||
|
func (w *VT100Writer) ShowCursor() {
|
||||||
|
w.WriteRaw([]byte{0x1b, 0x5b, 0x3f, 0x31, 0x32, 0x6c, 0x1b, 0x5b, 0x3f, 0x32, 0x35, 0x68})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *VT100Writer) HideCursor() {
|
||||||
|
w.WriteRaw([]byte{0x1b, 0x5b, 0x3f, 0x32, 0x35, 0x6c})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *VT100Writer) CursorGoTo(row, col int) {
|
||||||
|
r := strconv.Itoa(row)
|
||||||
|
c := strconv.Itoa(col)
|
||||||
|
w.WriteRaw([]byte{0x1b, 0x5b})
|
||||||
|
w.WriteRaw([]byte(r))
|
||||||
|
w.WriteRaw([]byte{0x3b})
|
||||||
|
w.WriteRaw([]byte(c))
|
||||||
|
w.WriteRaw([]byte{0x48})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *VT100Writer) CursorUp(n int) {
|
||||||
|
if n < 0 {
|
||||||
|
w.CursorDown(n)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s := strconv.Itoa(n)
|
||||||
|
w.WriteRaw([]byte{0x1b, 0x5b})
|
||||||
|
w.WriteRaw([]byte(s))
|
||||||
|
w.WriteRaw([]byte{0x41})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *VT100Writer) CursorDown(n int) {
|
||||||
|
if n < 0 {
|
||||||
|
w.CursorUp(n)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s := strconv.Itoa(n)
|
||||||
|
w.WriteRaw([]byte{0x1b, 0x5b})
|
||||||
|
w.WriteRaw([]byte(s))
|
||||||
|
w.WriteRaw([]byte{0x42})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *VT100Writer) CursorForward(n int) {
|
||||||
|
if n == 0 {
|
||||||
|
return
|
||||||
|
} else if n < 0 {
|
||||||
|
w.CursorBackward(-n)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s := strconv.Itoa(n)
|
||||||
|
w.WriteRaw([]byte{0x1b, 0x5b})
|
||||||
|
w.WriteRaw([]byte(s))
|
||||||
|
w.WriteRaw([]byte{0x43})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *VT100Writer) CursorBackward(n int) {
|
||||||
|
if n == 0 {
|
||||||
|
return
|
||||||
|
} else if n < 0 {
|
||||||
|
w.CursorForward(-n)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s := strconv.Itoa(n)
|
||||||
|
w.WriteRaw([]byte{0x1b, 0x5b})
|
||||||
|
w.WriteRaw([]byte(s))
|
||||||
|
w.WriteRaw([]byte{0x44})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *VT100Writer) AskForCPR() {
|
||||||
|
// CPR: Cursor Position Request.
|
||||||
|
w.WriteRaw([]byte{0x1b, 0x5b, 0x36, 0x6e})
|
||||||
|
w.Flush()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *VT100Writer) SaveCursor() {
|
||||||
|
w.WriteRaw([]byte{0x1b, 0x5b, 0x73})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *VT100Writer) UnSaveCursor() {
|
||||||
|
w.WriteRaw([]byte{0x1b, 0x5b, 0x75})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Scrolling */
|
||||||
|
|
||||||
|
func (w *VT100Writer) ScrollDown() {
|
||||||
|
w.WriteRaw([]byte{0x1b, 0x44})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *VT100Writer) ScrollUp() {
|
||||||
|
w.WriteRaw([]byte{0x1b, 0x4d})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Title */
|
||||||
|
|
||||||
|
func (w *VT100Writer) SetTitle(title string) {
|
||||||
|
w.WriteRaw([]byte{0x1b, 0x5d, 0x32, 0x3b})
|
||||||
|
w.WriteRaw(byteFilter([]byte(title), setTextFilter))
|
||||||
|
w.WriteRaw([]byte{0x07})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *VT100Writer) ClearTitle() {
|
||||||
|
w.WriteRaw([]byte{0x1b, 0x5d, 0x32, 0x3b, 0x07})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Font */
|
||||||
|
|
||||||
|
func (w *VT100Writer) SetColor(fg, bg Color, bold bool) {
|
||||||
|
f, ok := foregroundANSIColors[fg]
|
||||||
|
if !ok {
|
||||||
|
f, _ = foregroundANSIColors[DefaultColor]
|
||||||
|
}
|
||||||
|
b, ok := backgroundANSIColors[bg]
|
||||||
|
if !ok {
|
||||||
|
b, _ = backgroundANSIColors[DefaultColor]
|
||||||
|
}
|
||||||
|
w.out.Write([]byte{0x1b, 0x5b, 0x33, 0x39, 0x3b, 0x34, 0x39, 0x6d})
|
||||||
|
w.WriteRaw([]byte{0x1b, 0x5b})
|
||||||
|
if !bold {
|
||||||
|
w.WriteRaw([]byte{0x30, 0x3b})
|
||||||
|
}
|
||||||
|
w.WriteRaw(f)
|
||||||
|
w.WriteRaw([]byte{0x3b})
|
||||||
|
w.WriteRaw(b)
|
||||||
|
if bold {
|
||||||
|
w.WriteRaw([]byte{0x3b, 0x31})
|
||||||
|
}
|
||||||
|
w.WriteRaw([]byte{0x6d})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var foregroundANSIColors = map[Color][]byte{
|
||||||
|
DefaultColor: {0x33, 0x39}, // 39
|
||||||
|
|
||||||
|
// Low intensity.
|
||||||
|
Black: {0x33, 0x30}, // 30
|
||||||
|
DarkRed: {0x33, 0x31}, // 31
|
||||||
|
DarkGreen: {0x33, 0x32}, // 32
|
||||||
|
Brown: {0x33, 0x33}, // 33
|
||||||
|
DarkBlue: {0x33, 0x34}, // 34
|
||||||
|
Purple: {0x33, 0x35}, // 35
|
||||||
|
Cyan: {0x33, 0x36}, //36
|
||||||
|
LightGray: {0x33, 0x37}, //37
|
||||||
|
|
||||||
|
// High intensity.
|
||||||
|
DarkGray: {0x39, 0x30}, // 90
|
||||||
|
Red: {0x39, 0x31}, // 91
|
||||||
|
Green: {0x39, 0x32}, // 92
|
||||||
|
Yellow: {0x39, 0x33}, // 93
|
||||||
|
Blue: {0x39, 0x34}, // 94
|
||||||
|
Fuchsia: {0x39, 0x35}, // 95
|
||||||
|
Turquoise: {0x39, 0x36}, // 96
|
||||||
|
White: {0x39, 0x37}, // 97
|
||||||
|
}
|
||||||
|
|
||||||
|
var backgroundANSIColors = map[Color][]byte{
|
||||||
|
DefaultColor: {0x34, 0x39}, // 49
|
||||||
|
|
||||||
|
// Low intensity.
|
||||||
|
Black: {0x34, 0x30}, // 40
|
||||||
|
DarkRed: {0x34, 0x31}, // 41
|
||||||
|
DarkGreen: {0x34, 0x32}, // 42
|
||||||
|
Brown: {0x34, 0x33}, // 43
|
||||||
|
DarkBlue: {0x34, 0x34}, // 44
|
||||||
|
Purple: {0x34, 0x35}, // 45
|
||||||
|
Cyan: {0x34, 0x36}, // 46
|
||||||
|
LightGray: {0x34, 0x37}, // 47
|
||||||
|
|
||||||
|
// High intensity
|
||||||
|
DarkGray: {0x31, 0x30, 0x30}, // 100
|
||||||
|
Red: {0x31, 0x30, 0x31}, // 101
|
||||||
|
Green: {0x31, 0x30, 0x32}, // 102
|
||||||
|
Yellow: {0x31, 0x30, 0x33}, // 103
|
||||||
|
Blue: {0x31, 0x30, 0x34}, // 104
|
||||||
|
Fuchsia: {0x31, 0x30, 0x35}, // 105
|
||||||
|
Turquoise: {0x31, 0x30, 0x36}, // 106
|
||||||
|
White: {0x31, 0x30, 0x37}, // 107
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeFilter(buf byte) bool {
|
||||||
|
return buf != 0x1b && buf != 0x3f
|
||||||
|
}
|
||||||
|
|
||||||
|
func setTextFilter(buf byte) bool {
|
||||||
|
return buf != 0x1b && buf != 0x07
|
||||||
|
}
|
||||||
|
|
||||||
|
func byteFilter(buf []byte, fn ...func(b byte) bool) []byte {
|
||||||
|
if len(fn) == 0 {
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
ret := make([]byte, 0, len(buf))
|
||||||
|
f := fn[0]
|
||||||
|
for i, n := range buf {
|
||||||
|
if f(n) {
|
||||||
|
ret = append(ret, buf[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return byteFilter(ret, fn[1:]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ ConsoleWriter = &VT100Writer{}
|
||||||
|
|
||||||
|
func NewVT100StandardOutputWriter() *VT100Writer {
|
||||||
|
return &VT100Writer{
|
||||||
|
out: colorable.NewColorableStdout(),
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user