Separate VT100 writer

This commit is contained in:
c-bata 2018-06-21 01:31:29 +09:00
parent de18dfb152
commit 2f58d53a52
5 changed files with 351 additions and 630 deletions

View File

@ -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)
}

View File

@ -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,

334
output_vt100.go Normal file
View File

@ -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'},
}

View File

@ -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) {

View File

@ -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(),