Separate VT100 writer
This commit is contained in:
parent
de18dfb152
commit
2f58d53a52
|
@ -6,6 +6,7 @@ type WinSize struct {
|
|||
Col uint16
|
||||
}
|
||||
|
||||
// DisplayAttribute represents display attributes like Blinking, Bold, Italic and so on.
|
||||
type DisplayAttribute int
|
||||
|
||||
const (
|
||||
|
@ -163,5 +164,8 @@ type ConsoleWriter interface {
|
|||
/* Font */
|
||||
|
||||
// SetColor sets text and background colors. and specify whether text is bold.
|
||||
// Deprecated. This interface is not cool, please use SetDisplayAttributes.
|
||||
SetColor(fg, bg Color, bold bool)
|
||||
// SetDisplayAttributes set display attributes (Set colors, blink, bold, italic and so on).
|
||||
SetDisplayAttributes(fg, bg Color, attrs ...DisplayAttribute)
|
||||
}
|
||||
|
|
332
output_posix.go
332
output_posix.go
|
@ -3,40 +3,14 @@
|
|||
package prompt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strconv"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// PosixWriter is a ConsoleWriter implementation for POSIX environment.
|
||||
// To control terminal emulator, this outputs VT100 escape sequences.
|
||||
type PosixWriter struct {
|
||||
fd int
|
||||
buffer []byte
|
||||
}
|
||||
|
||||
// WriteRaw to write raw byte array
|
||||
func (w *PosixWriter) WriteRaw(data []byte) {
|
||||
w.buffer = append(w.buffer, data...)
|
||||
return
|
||||
}
|
||||
|
||||
// Write to write safety byte array by removing control sequences.
|
||||
func (w *PosixWriter) Write(data []byte) {
|
||||
w.WriteRaw(bytes.Replace(data, []byte{0x1b}, []byte{'?'}, -1))
|
||||
return
|
||||
}
|
||||
|
||||
// WriteRawStr to write raw string
|
||||
func (w *PosixWriter) WriteRawStr(data string) {
|
||||
w.WriteRaw([]byte(data))
|
||||
return
|
||||
}
|
||||
|
||||
// WriteStr to write safety string by removing control sequences.
|
||||
func (w *PosixWriter) WriteStr(data string) {
|
||||
w.Write([]byte(data))
|
||||
return
|
||||
VT100Writer
|
||||
fd int
|
||||
}
|
||||
|
||||
// Flush to flush buffer
|
||||
|
@ -49,309 +23,11 @@ func (w *PosixWriter) Flush() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
/* Erase */
|
||||
|
||||
// EraseScreen erases the screen with the background colour and moves the cursor to home.
|
||||
func (w *PosixWriter) EraseScreen() {
|
||||
w.WriteRaw([]byte{0x1b, '[', '2', 'J'})
|
||||
return
|
||||
}
|
||||
|
||||
// EraseUp erases the screen from the current line up to the top of the screen.
|
||||
func (w *PosixWriter) EraseUp() {
|
||||
w.WriteRaw([]byte{0x1b, '[', '2', 'J'})
|
||||
return
|
||||
}
|
||||
|
||||
// EraseDown erases the screen from the current line down to the bottom of the screen.
|
||||
func (w *PosixWriter) EraseDown() {
|
||||
w.WriteRaw([]byte{0x1b, '[', 'J'})
|
||||
return
|
||||
}
|
||||
|
||||
// EraseStartOfLine erases from the current cursor position to the start of the current line.
|
||||
func (w *PosixWriter) EraseStartOfLine() {
|
||||
w.WriteRaw([]byte{0x1b, '[', '1', 'K'})
|
||||
return
|
||||
}
|
||||
|
||||
// EraseEndOfLine erases from the current cursor position to the end of the current line.
|
||||
func (w *PosixWriter) EraseEndOfLine() {
|
||||
w.WriteRaw([]byte{0x1b, '[', 'K'})
|
||||
return
|
||||
}
|
||||
|
||||
// EraseLine erases the entire current line.
|
||||
func (w *PosixWriter) EraseLine() {
|
||||
w.WriteRaw([]byte{0x1b, '[', '2', 'K'})
|
||||
return
|
||||
}
|
||||
|
||||
/* Cursor */
|
||||
|
||||
// ShowCursor stops blinking cursor and show.
|
||||
func (w *PosixWriter) ShowCursor() {
|
||||
w.WriteRaw([]byte{0x1b, '[', '?', '1', '2', 'l', 0x1b, '[', '?', '2', '5', 'h'})
|
||||
}
|
||||
|
||||
// HideCursor hides cursor.
|
||||
func (w *PosixWriter) HideCursor() {
|
||||
w.WriteRaw([]byte{0x1b, '[', '?', '2', '5', 'l'})
|
||||
return
|
||||
}
|
||||
|
||||
// CursorGoTo sets the cursor position where subsequent text will begin.
|
||||
func (w *PosixWriter) CursorGoTo(row, col int) {
|
||||
if row == 0 && col == 0 {
|
||||
// If no row/column parameters are provided (ie. <ESC>[H), the cursor will move to the home position.
|
||||
w.WriteRaw([]byte{0x1b, '[', 'H'})
|
||||
return
|
||||
}
|
||||
r := strconv.Itoa(row)
|
||||
c := strconv.Itoa(col)
|
||||
w.WriteRaw([]byte{0x1b, '['})
|
||||
w.WriteRaw([]byte(r))
|
||||
w.WriteRaw([]byte{';'})
|
||||
w.WriteRaw([]byte(c))
|
||||
w.WriteRaw([]byte{'H'})
|
||||
return
|
||||
}
|
||||
|
||||
// CursorUp moves the cursor up by 'n' rows; the default count is 1.
|
||||
func (w *PosixWriter) CursorUp(n int) {
|
||||
if n == 0 {
|
||||
return
|
||||
} else if n < 0 {
|
||||
w.CursorDown(-n)
|
||||
return
|
||||
}
|
||||
s := strconv.Itoa(n)
|
||||
w.WriteRaw([]byte{0x1b, '['})
|
||||
w.WriteRaw([]byte(s))
|
||||
w.WriteRaw([]byte{'A'})
|
||||
return
|
||||
}
|
||||
|
||||
// CursorDown moves the cursor down by 'n' rows; the default count is 1.
|
||||
func (w *PosixWriter) CursorDown(n int) {
|
||||
if n == 0 {
|
||||
return
|
||||
} else if n < 0 {
|
||||
w.CursorUp(-n)
|
||||
return
|
||||
}
|
||||
s := strconv.Itoa(n)
|
||||
w.WriteRaw([]byte{0x1b, '['})
|
||||
w.WriteRaw([]byte(s))
|
||||
w.WriteRaw([]byte{'B'})
|
||||
return
|
||||
}
|
||||
|
||||
// CursorForward moves the cursor forward by 'n' columns; the default count is 1.
|
||||
func (w *PosixWriter) CursorForward(n int) {
|
||||
if n == 0 {
|
||||
return
|
||||
} else if n < 0 {
|
||||
w.CursorBackward(-n)
|
||||
return
|
||||
}
|
||||
s := strconv.Itoa(n)
|
||||
w.WriteRaw([]byte{0x1b, '['})
|
||||
w.WriteRaw([]byte(s))
|
||||
w.WriteRaw([]byte{'C'})
|
||||
return
|
||||
}
|
||||
|
||||
// CursorBackward moves the cursor backward by 'n' columns; the default count is 1.
|
||||
func (w *PosixWriter) CursorBackward(n int) {
|
||||
if n == 0 {
|
||||
return
|
||||
} else if n < 0 {
|
||||
w.CursorForward(-n)
|
||||
return
|
||||
}
|
||||
s := strconv.Itoa(n)
|
||||
w.WriteRaw([]byte{0x1b, '['})
|
||||
w.WriteRaw([]byte(s))
|
||||
w.WriteRaw([]byte{'D'})
|
||||
return
|
||||
}
|
||||
|
||||
// AskForCPR asks for a cursor position report (CPR).
|
||||
func (w *PosixWriter) AskForCPR() {
|
||||
// CPR: Cursor Position Request.
|
||||
w.WriteRaw([]byte{0x1b, '[', '6', 'n'})
|
||||
w.Flush()
|
||||
return
|
||||
}
|
||||
|
||||
// SaveCursor saves current cursor position.
|
||||
func (w *PosixWriter) SaveCursor() {
|
||||
w.WriteRaw([]byte{0x1b, '[', 's'})
|
||||
return
|
||||
}
|
||||
|
||||
// UnSaveCursor restores cursor position after a Save Cursor.
|
||||
func (w *PosixWriter) UnSaveCursor() {
|
||||
w.WriteRaw([]byte{0x1b, '[', 'u'})
|
||||
return
|
||||
}
|
||||
|
||||
/* Scrolling */
|
||||
|
||||
// ScrollDown scrolls display down one line.
|
||||
func (w *PosixWriter) ScrollDown() {
|
||||
w.WriteRaw([]byte{0x1b, 'D'})
|
||||
return
|
||||
}
|
||||
|
||||
// ScrollUp scroll display up one line.
|
||||
func (w *PosixWriter) ScrollUp() {
|
||||
w.WriteRaw([]byte{0x1b, 'M'})
|
||||
return
|
||||
}
|
||||
|
||||
/* Title */
|
||||
|
||||
// SetTitle sets a title of terminal window.
|
||||
func (w *PosixWriter) SetTitle(title string) {
|
||||
titleBytes := []byte(title)
|
||||
patterns := []struct {
|
||||
from []byte
|
||||
to []byte
|
||||
}{
|
||||
{
|
||||
from: []byte{0x13},
|
||||
to: []byte{},
|
||||
},
|
||||
{
|
||||
from: []byte{0x07},
|
||||
to: []byte{},
|
||||
},
|
||||
}
|
||||
for i := range patterns {
|
||||
titleBytes = bytes.Replace(titleBytes, patterns[i].from, patterns[i].to, -1)
|
||||
}
|
||||
|
||||
w.WriteRaw([]byte{0x1b, ']', '2', ';'})
|
||||
w.WriteRaw(titleBytes)
|
||||
w.WriteRaw([]byte{0x07})
|
||||
return
|
||||
}
|
||||
|
||||
// ClearTitle clears a title of terminal window.
|
||||
func (w *PosixWriter) ClearTitle() {
|
||||
w.WriteRaw([]byte{0x1b, ']', '2', ';', 0x07})
|
||||
return
|
||||
}
|
||||
|
||||
/* Font */
|
||||
|
||||
// SetColor sets text and background colors. and specify whether text is bold.
|
||||
// Deprecated. This interface is not cool, please use SetDisplayAttributes.
|
||||
func (w *PosixWriter) SetColor(fg, bg Color, bold bool) {
|
||||
if bold {
|
||||
w.SetDisplayAttributes(fg, bg, DisplayBold)
|
||||
} else {
|
||||
w.SetDisplayAttributes(fg, bg, DisplayDefaultFont)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SetDisplayAttributes set display attributes (Set colors, blink, bold, italic and so on).
|
||||
func (w *PosixWriter) SetDisplayAttributes(fg, bg Color, attrs ...DisplayAttribute) {
|
||||
w.WriteRaw([]byte{0x1b, '['}) // control sequence introducer
|
||||
defer w.WriteRaw([]byte{'m'}) // final character
|
||||
|
||||
var separator byte = ';'
|
||||
for i := range attrs {
|
||||
p, ok := displayAttributeParameters[attrs[i]]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
w.WriteRaw(p)
|
||||
w.WriteRaw([]byte{separator})
|
||||
}
|
||||
|
||||
f, ok := foregroundANSIColors[fg]
|
||||
if !ok {
|
||||
f = foregroundANSIColors[DefaultColor]
|
||||
}
|
||||
w.WriteRaw(f)
|
||||
w.WriteRaw([]byte{separator})
|
||||
b, ok := backgroundANSIColors[bg]
|
||||
if !ok {
|
||||
b = backgroundANSIColors[DefaultColor]
|
||||
}
|
||||
w.WriteRaw(b)
|
||||
return
|
||||
}
|
||||
|
||||
var displayAttributeParameters = map[DisplayAttribute][]byte{
|
||||
DisplayReset: {'0'},
|
||||
DisplayBold: {'1'},
|
||||
DisplayLowIntensity: {'2'},
|
||||
DisplayItalic: {'3'},
|
||||
DisplayUnderline: {'4'},
|
||||
DisplayBlink: {'5'},
|
||||
DisplayRapidBlink: {'6'},
|
||||
DisplayReverse: {'7'},
|
||||
DisplayInvisible: {'8'},
|
||||
DisplayCrossedOut: {'9'},
|
||||
DisplayDefaultFont: {'1', '0'},
|
||||
}
|
||||
|
||||
var foregroundANSIColors = map[Color][]byte{
|
||||
DefaultColor: {'3', '9'},
|
||||
|
||||
// Low intensity.
|
||||
Black: {'3', '0'},
|
||||
DarkRed: {'3', '1'},
|
||||
DarkGreen: {'3', '2'},
|
||||
Brown: {'3', '3'},
|
||||
DarkBlue: {'3', '4'},
|
||||
Purple: {'3', '5'},
|
||||
Cyan: {'3', '6'},
|
||||
LightGray: {'3', '7'},
|
||||
|
||||
// High intensity.
|
||||
DarkGray: {'9', '0'},
|
||||
Red: {'9', '1'},
|
||||
Green: {'9', '2'},
|
||||
Yellow: {'9', '3'},
|
||||
Blue: {'9', '4'},
|
||||
Fuchsia: {'9', '5'},
|
||||
Turquoise: {'9', '6'},
|
||||
White: {'9', '7'},
|
||||
}
|
||||
|
||||
var backgroundANSIColors = map[Color][]byte{
|
||||
DefaultColor: {'4', '9'},
|
||||
|
||||
// Low intensity.
|
||||
Black: {'4', '0'},
|
||||
DarkRed: {'4', '1'},
|
||||
DarkGreen: {'4', '2'},
|
||||
Brown: {'4', '3'},
|
||||
DarkBlue: {'4', '4'},
|
||||
Purple: {'4', '5'},
|
||||
Cyan: {'4', '6'},
|
||||
LightGray: {'4', '7'},
|
||||
|
||||
// High intensity
|
||||
DarkGray: {'1', '0', '0'},
|
||||
Red: {'1', '0', '1'},
|
||||
Green: {'1', '0', '2'},
|
||||
Yellow: {'1', '0', '3'},
|
||||
Blue: {'1', '0', '4'},
|
||||
Fuchsia: {'1', '0', '5'},
|
||||
Turquoise: {'1', '0', '6'},
|
||||
White: {'1', '0', '7'},
|
||||
}
|
||||
|
||||
var _ ConsoleWriter = &PosixWriter{}
|
||||
|
||||
// NewStandardOutputWriter returns ConsoleWriter object to write to stdout.
|
||||
// This generates VT100 escape sequences because almost terminal emulators
|
||||
// in POSIX OS built on top of a VT100 specification.
|
||||
func NewStandardOutputWriter() *PosixWriter {
|
||||
return &PosixWriter{
|
||||
fd: syscall.Stdout,
|
||||
|
|
|
@ -0,0 +1,334 @@
|
|||
package prompt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// VT100Writer generates VT100 escape sequences.
|
||||
type VT100Writer struct {
|
||||
buffer []byte
|
||||
}
|
||||
|
||||
// WriteRaw to write raw byte array
|
||||
func (w *VT100Writer) WriteRaw(data []byte) {
|
||||
w.buffer = append(w.buffer, data...)
|
||||
return
|
||||
}
|
||||
|
||||
// Write to write safety byte array by removing control sequences.
|
||||
func (w *VT100Writer) Write(data []byte) {
|
||||
w.WriteRaw(bytes.Replace(data, []byte{0x1b}, []byte{'?'}, -1))
|
||||
return
|
||||
}
|
||||
|
||||
// WriteRawStr to write raw string
|
||||
func (w *VT100Writer) WriteRawStr(data string) {
|
||||
w.WriteRaw([]byte(data))
|
||||
return
|
||||
}
|
||||
|
||||
// WriteStr to write safety string by removing control sequences.
|
||||
func (w *VT100Writer) WriteStr(data string) {
|
||||
w.Write([]byte(data))
|
||||
return
|
||||
}
|
||||
|
||||
/* Erase */
|
||||
|
||||
// EraseScreen erases the screen with the background colour and moves the cursor to home.
|
||||
func (w *VT100Writer) EraseScreen() {
|
||||
w.WriteRaw([]byte{0x1b, '[', '2', 'J'})
|
||||
return
|
||||
}
|
||||
|
||||
// EraseUp erases the screen from the current line up to the top of the screen.
|
||||
func (w *VT100Writer) EraseUp() {
|
||||
w.WriteRaw([]byte{0x1b, '[', '2', 'J'})
|
||||
return
|
||||
}
|
||||
|
||||
// EraseDown erases the screen from the current line down to the bottom of the screen.
|
||||
func (w *VT100Writer) EraseDown() {
|
||||
w.WriteRaw([]byte{0x1b, '[', 'J'})
|
||||
return
|
||||
}
|
||||
|
||||
// EraseStartOfLine erases from the current cursor position to the start of the current line.
|
||||
func (w *VT100Writer) EraseStartOfLine() {
|
||||
w.WriteRaw([]byte{0x1b, '[', '1', 'K'})
|
||||
return
|
||||
}
|
||||
|
||||
// EraseEndOfLine erases from the current cursor position to the end of the current line.
|
||||
func (w *VT100Writer) EraseEndOfLine() {
|
||||
w.WriteRaw([]byte{0x1b, '[', 'K'})
|
||||
return
|
||||
}
|
||||
|
||||
// EraseLine erases the entire current line.
|
||||
func (w *VT100Writer) EraseLine() {
|
||||
w.WriteRaw([]byte{0x1b, '[', '2', 'K'})
|
||||
return
|
||||
}
|
||||
|
||||
/* Cursor */
|
||||
|
||||
// ShowCursor stops blinking cursor and show.
|
||||
func (w *VT100Writer) ShowCursor() {
|
||||
w.WriteRaw([]byte{0x1b, '[', '?', '1', '2', 'l', 0x1b, '[', '?', '2', '5', 'h'})
|
||||
}
|
||||
|
||||
// HideCursor hides cursor.
|
||||
func (w *VT100Writer) HideCursor() {
|
||||
w.WriteRaw([]byte{0x1b, '[', '?', '2', '5', 'l'})
|
||||
return
|
||||
}
|
||||
|
||||
// CursorGoTo sets the cursor position where subsequent text will begin.
|
||||
func (w *VT100Writer) CursorGoTo(row, col int) {
|
||||
if row == 0 && col == 0 {
|
||||
// If no row/column parameters are provided (ie. <ESC>[H), the cursor will move to the home position.
|
||||
w.WriteRaw([]byte{0x1b, '[', 'H'})
|
||||
return
|
||||
}
|
||||
r := strconv.Itoa(row)
|
||||
c := strconv.Itoa(col)
|
||||
w.WriteRaw([]byte{0x1b, '['})
|
||||
w.WriteRaw([]byte(r))
|
||||
w.WriteRaw([]byte{';'})
|
||||
w.WriteRaw([]byte(c))
|
||||
w.WriteRaw([]byte{'H'})
|
||||
return
|
||||
}
|
||||
|
||||
// CursorUp moves the cursor up by 'n' rows; the default count is 1.
|
||||
func (w *VT100Writer) CursorUp(n int) {
|
||||
if n == 0 {
|
||||
return
|
||||
} else if n < 0 {
|
||||
w.CursorDown(-n)
|
||||
return
|
||||
}
|
||||
s := strconv.Itoa(n)
|
||||
w.WriteRaw([]byte{0x1b, '['})
|
||||
w.WriteRaw([]byte(s))
|
||||
w.WriteRaw([]byte{'A'})
|
||||
return
|
||||
}
|
||||
|
||||
// CursorDown moves the cursor down by 'n' rows; the default count is 1.
|
||||
func (w *VT100Writer) CursorDown(n int) {
|
||||
if n == 0 {
|
||||
return
|
||||
} else if n < 0 {
|
||||
w.CursorUp(-n)
|
||||
return
|
||||
}
|
||||
s := strconv.Itoa(n)
|
||||
w.WriteRaw([]byte{0x1b, '['})
|
||||
w.WriteRaw([]byte(s))
|
||||
w.WriteRaw([]byte{'B'})
|
||||
return
|
||||
}
|
||||
|
||||
// CursorForward moves the cursor forward by 'n' columns; the default count is 1.
|
||||
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, '['})
|
||||
w.WriteRaw([]byte(s))
|
||||
w.WriteRaw([]byte{'C'})
|
||||
return
|
||||
}
|
||||
|
||||
// CursorBackward moves the cursor backward by 'n' columns; the default count is 1.
|
||||
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, '['})
|
||||
w.WriteRaw([]byte(s))
|
||||
w.WriteRaw([]byte{'D'})
|
||||
return
|
||||
}
|
||||
|
||||
// AskForCPR asks for a cursor position report (CPR).
|
||||
func (w *VT100Writer) AskForCPR() {
|
||||
// CPR: Cursor Position Request.
|
||||
w.WriteRaw([]byte{0x1b, '[', '6', 'n'})
|
||||
return
|
||||
}
|
||||
|
||||
// SaveCursor saves current cursor position.
|
||||
func (w *VT100Writer) SaveCursor() {
|
||||
w.WriteRaw([]byte{0x1b, '[', 's'})
|
||||
return
|
||||
}
|
||||
|
||||
// UnSaveCursor restores cursor position after a Save Cursor.
|
||||
func (w *VT100Writer) UnSaveCursor() {
|
||||
w.WriteRaw([]byte{0x1b, '[', 'u'})
|
||||
return
|
||||
}
|
||||
|
||||
/* Scrolling */
|
||||
|
||||
// ScrollDown scrolls display down one line.
|
||||
func (w *VT100Writer) ScrollDown() {
|
||||
w.WriteRaw([]byte{0x1b, 'D'})
|
||||
return
|
||||
}
|
||||
|
||||
// ScrollUp scroll display up one line.
|
||||
func (w *VT100Writer) ScrollUp() {
|
||||
w.WriteRaw([]byte{0x1b, 'M'})
|
||||
return
|
||||
}
|
||||
|
||||
/* Title */
|
||||
|
||||
// SetTitle sets a title of terminal window.
|
||||
func (w *VT100Writer) SetTitle(title string) {
|
||||
titleBytes := []byte(title)
|
||||
patterns := []struct {
|
||||
from []byte
|
||||
to []byte
|
||||
}{
|
||||
{
|
||||
from: []byte{0x13},
|
||||
to: []byte{},
|
||||
},
|
||||
{
|
||||
from: []byte{0x07},
|
||||
to: []byte{},
|
||||
},
|
||||
}
|
||||
for i := range patterns {
|
||||
titleBytes = bytes.Replace(titleBytes, patterns[i].from, patterns[i].to, -1)
|
||||
}
|
||||
|
||||
w.WriteRaw([]byte{0x1b, ']', '2', ';'})
|
||||
w.WriteRaw(titleBytes)
|
||||
w.WriteRaw([]byte{0x07})
|
||||
return
|
||||
}
|
||||
|
||||
// ClearTitle clears a title of terminal window.
|
||||
func (w *VT100Writer) ClearTitle() {
|
||||
w.WriteRaw([]byte{0x1b, ']', '2', ';', 0x07})
|
||||
return
|
||||
}
|
||||
|
||||
/* Font */
|
||||
|
||||
// SetColor sets text and background colors. and specify whether text is bold.
|
||||
// Deprecated. This interface is not cool, please use SetDisplayAttributes.
|
||||
func (w *VT100Writer) SetColor(fg, bg Color, bold bool) {
|
||||
if bold {
|
||||
w.SetDisplayAttributes(fg, bg, DisplayBold)
|
||||
} else {
|
||||
w.SetDisplayAttributes(fg, bg, DisplayDefaultFont)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// SetDisplayAttributes set display attributes (Set colors, blink, bold, italic and so on).
|
||||
func (w *VT100Writer) SetDisplayAttributes(fg, bg Color, attrs ...DisplayAttribute) {
|
||||
w.WriteRaw([]byte{0x1b, '['}) // control sequence introducer
|
||||
defer w.WriteRaw([]byte{'m'}) // final character
|
||||
|
||||
var separator byte = ';'
|
||||
for i := range attrs {
|
||||
p, ok := displayAttributeParameters[attrs[i]]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
w.WriteRaw(p)
|
||||
w.WriteRaw([]byte{separator})
|
||||
}
|
||||
|
||||
f, ok := foregroundANSIColors[fg]
|
||||
if !ok {
|
||||
f = foregroundANSIColors[DefaultColor]
|
||||
}
|
||||
w.WriteRaw(f)
|
||||
w.WriteRaw([]byte{separator})
|
||||
b, ok := backgroundANSIColors[bg]
|
||||
if !ok {
|
||||
b = backgroundANSIColors[DefaultColor]
|
||||
}
|
||||
w.WriteRaw(b)
|
||||
return
|
||||
}
|
||||
|
||||
var displayAttributeParameters = map[DisplayAttribute][]byte{
|
||||
DisplayReset: {'0'},
|
||||
DisplayBold: {'1'},
|
||||
DisplayLowIntensity: {'2'},
|
||||
DisplayItalic: {'3'},
|
||||
DisplayUnderline: {'4'},
|
||||
DisplayBlink: {'5'},
|
||||
DisplayRapidBlink: {'6'},
|
||||
DisplayReverse: {'7'},
|
||||
DisplayInvisible: {'8'},
|
||||
DisplayCrossedOut: {'9'},
|
||||
DisplayDefaultFont: {'1', '0'},
|
||||
}
|
||||
|
||||
var foregroundANSIColors = map[Color][]byte{
|
||||
DefaultColor: {'3', '9'},
|
||||
|
||||
// Low intensity.
|
||||
Black: {'3', '0'},
|
||||
DarkRed: {'3', '1'},
|
||||
DarkGreen: {'3', '2'},
|
||||
Brown: {'3', '3'},
|
||||
DarkBlue: {'3', '4'},
|
||||
Purple: {'3', '5'},
|
||||
Cyan: {'3', '6'},
|
||||
LightGray: {'3', '7'},
|
||||
|
||||
// High intensity.
|
||||
DarkGray: {'9', '0'},
|
||||
Red: {'9', '1'},
|
||||
Green: {'9', '2'},
|
||||
Yellow: {'9', '3'},
|
||||
Blue: {'9', '4'},
|
||||
Fuchsia: {'9', '5'},
|
||||
Turquoise: {'9', '6'},
|
||||
White: {'9', '7'},
|
||||
}
|
||||
|
||||
var backgroundANSIColors = map[Color][]byte{
|
||||
DefaultColor: {'4', '9'},
|
||||
|
||||
// Low intensity.
|
||||
Black: {'4', '0'},
|
||||
DarkRed: {'4', '1'},
|
||||
DarkGreen: {'4', '2'},
|
||||
Brown: {'4', '3'},
|
||||
DarkBlue: {'4', '4'},
|
||||
Purple: {'4', '5'},
|
||||
Cyan: {'4', '6'},
|
||||
LightGray: {'4', '7'},
|
||||
|
||||
// High intensity
|
||||
DarkGray: {'1', '0', '0'},
|
||||
Red: {'1', '0', '1'},
|
||||
Green: {'1', '0', '2'},
|
||||
Yellow: {'1', '0', '3'},
|
||||
Blue: {'1', '0', '4'},
|
||||
Fuchsia: {'1', '0', '5'},
|
||||
Turquoise: {'1', '0', '6'},
|
||||
White: {'1', '0', '7'},
|
||||
}
|
|
@ -1,5 +1,3 @@
|
|||
// +build !windows
|
||||
|
||||
package prompt
|
||||
|
||||
import (
|
||||
|
@ -7,7 +5,7 @@ import (
|
|||
"testing"
|
||||
)
|
||||
|
||||
func TestPosixWriterWrite(t *testing.T) {
|
||||
func TestVT100WriterWrite(t *testing.T) {
|
||||
scenarioTable := []struct {
|
||||
input []byte
|
||||
expected []byte
|
||||
|
@ -23,7 +21,7 @@ func TestPosixWriterWrite(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, s := range scenarioTable {
|
||||
pw := &PosixWriter{}
|
||||
pw := &VT100Writer{}
|
||||
pw.Write(s.input)
|
||||
|
||||
if !bytes.Equal(pw.buffer, s.expected) {
|
||||
|
@ -32,7 +30,7 @@ func TestPosixWriterWrite(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestPosixWriterWriteStr(t *testing.T) {
|
||||
func TestVT100WriterWriteStr(t *testing.T) {
|
||||
scenarioTable := []struct {
|
||||
input string
|
||||
expected []byte
|
||||
|
@ -48,7 +46,7 @@ func TestPosixWriterWriteStr(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, s := range scenarioTable {
|
||||
pw := &PosixWriter{}
|
||||
pw := &VT100Writer{}
|
||||
pw.WriteStr(s.input)
|
||||
|
||||
if !bytes.Equal(pw.buffer, s.expected) {
|
||||
|
@ -57,7 +55,7 @@ func TestPosixWriterWriteStr(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestPosixWriterWriteRawStr(t *testing.T) {
|
||||
func TestVT100WriterWriteRawStr(t *testing.T) {
|
||||
scenarioTable := []struct {
|
||||
input string
|
||||
expected []byte
|
||||
|
@ -73,7 +71,7 @@ func TestPosixWriterWriteRawStr(t *testing.T) {
|
|||
}
|
||||
|
||||
for _, s := range scenarioTable {
|
||||
pw := &PosixWriter{}
|
||||
pw := &VT100Writer{}
|
||||
pw.WriteRawStr(s.input)
|
||||
|
||||
if !bytes.Equal(pw.buffer, s.expected) {
|
|
@ -3,9 +3,7 @@
|
|||
package prompt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"strconv"
|
||||
|
||||
"github.com/mattn/go-colorable"
|
||||
)
|
||||
|
@ -13,32 +11,8 @@ import (
|
|||
// WindowsWriter is a ConsoleWriter implementation for Win32 console.
|
||||
// Output is converted from VT100 escape sequences by mattn/go-colorable.
|
||||
type WindowsWriter struct {
|
||||
out io.Writer
|
||||
buffer []byte
|
||||
}
|
||||
|
||||
// WriteRaw to write raw byte array
|
||||
func (w *WindowsWriter) WriteRaw(data []byte) {
|
||||
w.buffer = append(w.buffer, data...)
|
||||
return
|
||||
}
|
||||
|
||||
// Write to write safety byte array by removing control sequences.
|
||||
func (w *WindowsWriter) Write(data []byte) {
|
||||
w.WriteRaw(bytes.Replace(data, []byte{0x1b}, []byte{'?'}, -1))
|
||||
return
|
||||
}
|
||||
|
||||
// WriteRawStr to write raw string
|
||||
func (w *WindowsWriter) WriteRawStr(data string) {
|
||||
w.WriteRaw([]byte(data))
|
||||
return
|
||||
}
|
||||
|
||||
// WriteStr to write safety string by removing control sequences.
|
||||
func (w *WindowsWriter) WriteStr(data string) {
|
||||
w.Write([]byte(data))
|
||||
return
|
||||
VT100Writer
|
||||
out io.Writer
|
||||
}
|
||||
|
||||
// Flush to flush buffer
|
||||
|
@ -51,275 +25,10 @@ func (w *WindowsWriter) Flush() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
/* Erase */
|
||||
|
||||
// EraseScreen erases the screen with the background colour and moves the cursor to home.
|
||||
func (w *WindowsWriter) EraseScreen() {
|
||||
w.WriteRaw([]byte{0x1b, 0x5b, 0x32, 0x4a})
|
||||
return
|
||||
}
|
||||
|
||||
// EraseUp erases the screen from the current line up to the top of the screen.
|
||||
func (w *WindowsWriter) EraseUp() {
|
||||
w.WriteRaw([]byte{0x1b, 0x5b, 0x31, 0x4a})
|
||||
return
|
||||
}
|
||||
|
||||
// EraseDown erases the screen from the current line down to the bottom of the screen.
|
||||
func (w *WindowsWriter) EraseDown() {
|
||||
w.WriteRaw([]byte{0x1b, 0x5b, 0x4a})
|
||||
return
|
||||
}
|
||||
|
||||
// EraseStartOfLine erases from the current cursor position to the start of the current line.
|
||||
func (w *WindowsWriter) EraseStartOfLine() {
|
||||
w.WriteRaw([]byte{0x1b, 0x5b, 0x31, 0x4b})
|
||||
return
|
||||
}
|
||||
|
||||
// EraseEndOfLine erases from the current cursor position to the end of the current line.
|
||||
func (w *WindowsWriter) EraseEndOfLine() {
|
||||
w.WriteRaw([]byte{0x1b, 0x5b, 0x4b})
|
||||
return
|
||||
}
|
||||
|
||||
// EraseLine erases the entire current line.
|
||||
func (w *WindowsWriter) EraseLine() {
|
||||
w.WriteRaw([]byte{0x1b, 0x5b, 0x32, 0x4b})
|
||||
return
|
||||
}
|
||||
|
||||
/* Cursor */
|
||||
|
||||
// ShowCursor stops blinking cursor and show.
|
||||
func (w *WindowsWriter) ShowCursor() {
|
||||
w.WriteRaw([]byte{0x1b, 0x5b, 0x3f, 0x31, 0x32, 0x6c, 0x1b, 0x5b, 0x3f, 0x32, 0x35, 0x68})
|
||||
}
|
||||
|
||||
// HideCursor hides cursor.
|
||||
func (w *WindowsWriter) HideCursor() {
|
||||
w.WriteRaw([]byte{0x1b, 0x5b, 0x3f, 0x32, 0x35, 0x6c})
|
||||
return
|
||||
}
|
||||
|
||||
// CursorGoTo sets the cursor position where subsequent text will begin.
|
||||
func (w *WindowsWriter) CursorGoTo(row, col int) {
|
||||
if row == 0 && col == 0 {
|
||||
// If no row/column parameters are provided (ie. <ESC>[H), the cursor will move to the home position.
|
||||
w.WriteRaw([]byte{0x1b, 0x5b, 0x3b, 0x48})
|
||||
return
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
// CursorUp moves the cursor up by 'n' rows; the default count is 1.
|
||||
func (w *WindowsWriter) 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
|
||||
}
|
||||
|
||||
// CursorDown moves the cursor down by 'n' rows; the default count is 1.
|
||||
func (w *WindowsWriter) 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
|
||||
}
|
||||
|
||||
// CursorForward moves the cursor forward by 'n' columns; the default count is 1.
|
||||
func (w *WindowsWriter) 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
|
||||
}
|
||||
|
||||
// CursorBackward moves the cursor backward by 'n' columns; the default count is 1.
|
||||
func (w *WindowsWriter) 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
|
||||
}
|
||||
|
||||
// AskForCPR asks for a cursor position report (CPR).
|
||||
func (w *WindowsWriter) AskForCPR() {
|
||||
// CPR: Cursor Position Request.
|
||||
w.WriteRaw([]byte{0x1b, 0x5b, 0x36, 0x6e})
|
||||
w.Flush()
|
||||
return
|
||||
}
|
||||
|
||||
// SaveCursor saves current cursor position.
|
||||
func (w *WindowsWriter) SaveCursor() {
|
||||
w.WriteRaw([]byte{0x1b, 0x5b, 0x73})
|
||||
return
|
||||
}
|
||||
|
||||
// UnSaveCursor restores cursor position after a Save Cursor.
|
||||
func (w *WindowsWriter) UnSaveCursor() {
|
||||
w.WriteRaw([]byte{0x1b, 0x5b, 0x75})
|
||||
return
|
||||
}
|
||||
|
||||
/* Scrolling */
|
||||
|
||||
// ScrollDown scrolls display down one line.
|
||||
func (w *WindowsWriter) ScrollDown() {
|
||||
w.WriteRaw([]byte{0x1b, 0x44})
|
||||
return
|
||||
}
|
||||
|
||||
// ScrollUp scroll display up one line.
|
||||
func (w *WindowsWriter) ScrollUp() {
|
||||
w.WriteRaw([]byte{0x1b, 0x4d})
|
||||
return
|
||||
}
|
||||
|
||||
/* Title */
|
||||
|
||||
// SetTitle sets a title of terminal window.
|
||||
func (w *WindowsWriter) SetTitle(title string) {
|
||||
titleBytes := []byte(title)
|
||||
patterns := []struct {
|
||||
from []byte
|
||||
to []byte
|
||||
}{
|
||||
{
|
||||
from: []byte{0x13},
|
||||
to: []byte{},
|
||||
},
|
||||
{
|
||||
from: []byte{0x07},
|
||||
to: []byte{},
|
||||
},
|
||||
}
|
||||
for i := range patterns {
|
||||
titleBytes = bytes.Replace(titleBytes, patterns[i].from, patterns[i].to, -1)
|
||||
}
|
||||
|
||||
w.WriteRaw([]byte{0x1b, 0x5d, 0x32, 0x3b})
|
||||
w.WriteRaw(titleBytes)
|
||||
w.WriteRaw([]byte{0x07})
|
||||
return
|
||||
}
|
||||
|
||||
// ClearTitle clears a title of terminal window.
|
||||
func (w *WindowsWriter) ClearTitle() {
|
||||
w.WriteRaw([]byte{0x1b, 0x5d, 0x32, 0x3b, 0x07})
|
||||
return
|
||||
}
|
||||
|
||||
/* Font */
|
||||
|
||||
// SetColor sets text and background colors. and specify whether text is bold.
|
||||
func (w *WindowsWriter) 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.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
|
||||
}
|
||||
|
||||
var _ ConsoleWriter = &WindowsWriter{}
|
||||
|
||||
// NewStandardOutputWriter returns ConsoleWriter object to write to stdout.
|
||||
// This generates win32 control sequences.
|
||||
func NewStandardOutputWriter() *WindowsWriter {
|
||||
return &WindowsWriter{
|
||||
out: colorable.NewColorableStdout(),
|
||||
|
|
Loading…
Reference in New Issue