Use go-ansiterm version matching docker/pkg/term/windows v1.11

pull/6/head
Dong Liu 2017-07-14 18:23:16 +08:00
parent ca1b408bf5
commit 72044a11a1
26 changed files with 695 additions and 331 deletions

4
Godeps/Godeps.json generated
View File

@ -55,11 +55,11 @@
}, },
{ {
"ImportPath": "github.com/Azure/go-ansiterm", "ImportPath": "github.com/Azure/go-ansiterm",
"Rev": "fa152c58bc15761d0200cb75fe958b89a9d4888e" "Rev": "70b2c90b260171e829f1ebd7c17f600c11858dbe"
}, },
{ {
"ImportPath": "github.com/Azure/go-ansiterm/winterm", "ImportPath": "github.com/Azure/go-ansiterm/winterm",
"Rev": "fa152c58bc15761d0200cb75fe958b89a9d4888e" "Rev": "70b2c90b260171e829f1ebd7c17f600c11858dbe"
}, },
{ {
"ImportPath": "github.com/Azure/go-autorest/autorest", "ImportPath": "github.com/Azure/go-autorest/autorest",

View File

@ -22,7 +22,10 @@ go_library(
"parser.go", "parser.go",
"parser_action_helpers.go", "parser_action_helpers.go",
"parser_actions.go", "parser_actions.go",
"parser_test_helpers.go",
"parser_test_utilities.go",
"states.go", "states.go",
"test_event_handler.go",
"utilities.go", "utilities.go",
], ],
tags = ["automanaged"], tags = ["automanaged"],

View File

@ -7,6 +7,3 @@ For example the parser might receive "ESC, [, A" as a stream of three characters
The parser (parser.go) is a partial implementation of this state machine (http://vt100.net/emu/vt500_parser.png). There are also two event handler implementations, one for tests (test_event_handler.go) to validate that the expected events are being produced and called, the other is a Windows implementation (winterm/win_event_handler.go). The parser (parser.go) is a partial implementation of this state machine (http://vt100.net/emu/vt500_parser.png). There are also two event handler implementations, one for tests (test_event_handler.go) to validate that the expected events are being produced and called, the other is a Windows implementation (winterm/win_event_handler.go).
See parser_test.go for examples exercising the state machine and generating appropriate function calls. See parser_test.go for examples exercising the state machine and generating appropriate function calls.
-----
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.

View File

@ -124,32 +124,32 @@ func getByteRange(start byte, end byte) []byte {
return bytes return bytes
} }
var toGroundBytes = getToGroundBytes() var ToGroundBytes = getToGroundBytes()
var executors = getExecuteBytes() var Executors = getExecuteBytes()
// SPACE 20+A0 hex Always and everywhere a blank space // SPACE 20+A0 hex Always and everywhere a blank space
// Intermediate 20-2F hex !"#$%&'()*+,-./ // Intermediate 20-2F hex !"#$%&'()*+,-./
var intermeds = getByteRange(0x20, 0x2F) var Intermeds = getByteRange(0x20, 0x2F)
// Parameters 30-3F hex 0123456789:;<=>? // Parameters 30-3F hex 0123456789:;<=>?
// CSI Parameters 30-39, 3B hex 0123456789; // CSI Parameters 30-39, 3B hex 0123456789;
var csiParams = getByteRange(0x30, 0x3F) var CsiParams = getByteRange(0x30, 0x3F)
var csiCollectables = append(getByteRange(0x30, 0x39), getByteRange(0x3B, 0x3F)...) var CsiCollectables = append(getByteRange(0x30, 0x39), getByteRange(0x3B, 0x3F)...)
// Uppercase 40-5F hex @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ // Uppercase 40-5F hex @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_
var upperCase = getByteRange(0x40, 0x5F) var UpperCase = getByteRange(0x40, 0x5F)
// Lowercase 60-7E hex `abcdefghijlkmnopqrstuvwxyz{|}~ // Lowercase 60-7E hex `abcdefghijlkmnopqrstuvwxyz{|}~
var lowerCase = getByteRange(0x60, 0x7E) var LowerCase = getByteRange(0x60, 0x7E)
// Alphabetics 40-7E hex (all of upper and lower case) // Alphabetics 40-7E hex (all of upper and lower case)
var alphabetics = append(upperCase, lowerCase...) var Alphabetics = append(UpperCase, LowerCase...)
var printables = getByteRange(0x20, 0x7F) var Printables = getByteRange(0x20, 0x7F)
var escapeIntermediateToGroundBytes = getByteRange(0x30, 0x7E) var EscapeIntermediateToGroundBytes = getByteRange(0x30, 0x7E)
var escapeToGroundBytes = getEscapeToGroundBytes() var EscapeToGroundBytes = getEscapeToGroundBytes()
// See http://www.vt100.net/emu/vt500_parser.png for description of the complex // See http://www.vt100.net/emu/vt500_parser.png for description of the complex
// byte ranges below // byte ranges below

View File

@ -1,6 +1,6 @@
package ansiterm package ansiterm
type ansiContext struct { type AnsiContext struct {
currentChar byte currentChar byte
paramBuffer []byte paramBuffer []byte
interBuffer []byte interBuffer []byte

View File

@ -1,41 +1,41 @@
package ansiterm package ansiterm
type csiEntryState struct { type CsiEntryState struct {
baseState BaseState
} }
func (csiState csiEntryState) Handle(b byte) (s state, e error) { func (csiState CsiEntryState) Handle(b byte) (s State, e error) {
logger.Infof("CsiEntry::Handle %#x", b) logger.Infof("CsiEntry::Handle %#x", b)
nextState, err := csiState.baseState.Handle(b) nextState, err := csiState.BaseState.Handle(b)
if nextState != nil || err != nil { if nextState != nil || err != nil {
return nextState, err return nextState, err
} }
switch { switch {
case sliceContains(alphabetics, b): case sliceContains(Alphabetics, b):
return csiState.parser.ground, nil return csiState.parser.Ground, nil
case sliceContains(csiCollectables, b): case sliceContains(CsiCollectables, b):
return csiState.parser.csiParam, nil return csiState.parser.CsiParam, nil
case sliceContains(executors, b): case sliceContains(Executors, b):
return csiState, csiState.parser.execute() return csiState, csiState.parser.execute()
} }
return csiState, nil return csiState, nil
} }
func (csiState csiEntryState) Transition(s state) error { func (csiState CsiEntryState) Transition(s State) error {
logger.Infof("CsiEntry::Transition %s --> %s", csiState.Name(), s.Name()) logger.Infof("CsiEntry::Transition %s --> %s", csiState.Name(), s.Name())
csiState.baseState.Transition(s) csiState.BaseState.Transition(s)
switch s { switch s {
case csiState.parser.ground: case csiState.parser.Ground:
return csiState.parser.csiDispatch() return csiState.parser.csiDispatch()
case csiState.parser.csiParam: case csiState.parser.CsiParam:
switch { switch {
case sliceContains(csiParams, csiState.parser.context.currentChar): case sliceContains(CsiParams, csiState.parser.context.currentChar):
csiState.parser.collectParam() csiState.parser.collectParam()
case sliceContains(intermeds, csiState.parser.context.currentChar): case sliceContains(Intermeds, csiState.parser.context.currentChar):
csiState.parser.collectInter() csiState.parser.collectInter()
} }
} }
@ -43,7 +43,7 @@ func (csiState csiEntryState) Transition(s state) error {
return nil return nil
} }
func (csiState csiEntryState) Enter() error { func (csiState CsiEntryState) Enter() error {
csiState.parser.clear() csiState.parser.clear()
return nil return nil
} }

View File

@ -1,36 +1,36 @@
package ansiterm package ansiterm
type csiParamState struct { type CsiParamState struct {
baseState BaseState
} }
func (csiState csiParamState) Handle(b byte) (s state, e error) { func (csiState CsiParamState) Handle(b byte) (s State, e error) {
logger.Infof("CsiParam::Handle %#x", b) logger.Infof("CsiParam::Handle %#x", b)
nextState, err := csiState.baseState.Handle(b) nextState, err := csiState.BaseState.Handle(b)
if nextState != nil || err != nil { if nextState != nil || err != nil {
return nextState, err return nextState, err
} }
switch { switch {
case sliceContains(alphabetics, b): case sliceContains(Alphabetics, b):
return csiState.parser.ground, nil return csiState.parser.Ground, nil
case sliceContains(csiCollectables, b): case sliceContains(CsiCollectables, b):
csiState.parser.collectParam() csiState.parser.collectParam()
return csiState, nil return csiState, nil
case sliceContains(executors, b): case sliceContains(Executors, b):
return csiState, csiState.parser.execute() return csiState, csiState.parser.execute()
} }
return csiState, nil return csiState, nil
} }
func (csiState csiParamState) Transition(s state) error { func (csiState CsiParamState) Transition(s State) error {
logger.Infof("CsiParam::Transition %s --> %s", csiState.Name(), s.Name()) logger.Infof("CsiParam::Transition %s --> %s", csiState.Name(), s.Name())
csiState.baseState.Transition(s) csiState.BaseState.Transition(s)
switch s { switch s {
case csiState.parser.ground: case csiState.parser.Ground:
return csiState.parser.csiDispatch() return csiState.parser.csiDispatch()
} }

View File

@ -1,34 +1,34 @@
package ansiterm package ansiterm
type escapeIntermediateState struct { type EscapeIntermediateState struct {
baseState BaseState
} }
func (escState escapeIntermediateState) Handle(b byte) (s state, e error) { func (escState EscapeIntermediateState) Handle(b byte) (s State, e error) {
logger.Infof("escapeIntermediateState::Handle %#x", b) logger.Infof("EscapeIntermediateState::Handle %#x", b)
nextState, err := escState.baseState.Handle(b) nextState, err := escState.BaseState.Handle(b)
if nextState != nil || err != nil { if nextState != nil || err != nil {
return nextState, err return nextState, err
} }
switch { switch {
case sliceContains(intermeds, b): case sliceContains(Intermeds, b):
return escState, escState.parser.collectInter() return escState, escState.parser.collectInter()
case sliceContains(executors, b): case sliceContains(Executors, b):
return escState, escState.parser.execute() return escState, escState.parser.execute()
case sliceContains(escapeIntermediateToGroundBytes, b): case sliceContains(EscapeIntermediateToGroundBytes, b):
return escState.parser.ground, nil return escState.parser.Ground, nil
} }
return escState, nil return escState, nil
} }
func (escState escapeIntermediateState) Transition(s state) error { func (escState EscapeIntermediateState) Transition(s State) error {
logger.Infof("escapeIntermediateState::Transition %s --> %s", escState.Name(), s.Name()) logger.Infof("EscapeIntermediateState::Transition %s --> %s", escState.Name(), s.Name())
escState.baseState.Transition(s) escState.BaseState.Transition(s)
switch s { switch s {
case escState.parser.ground: case escState.parser.Ground:
return escState.parser.escDispatch() return escState.parser.escDispatch()
} }

View File

@ -1,47 +1,47 @@
package ansiterm package ansiterm
type escapeState struct { type EscapeState struct {
baseState BaseState
} }
func (escState escapeState) Handle(b byte) (s state, e error) { func (escState EscapeState) Handle(b byte) (s State, e error) {
logger.Infof("escapeState::Handle %#x", b) logger.Infof("EscapeState::Handle %#x", b)
nextState, err := escState.baseState.Handle(b) nextState, err := escState.BaseState.Handle(b)
if nextState != nil || err != nil { if nextState != nil || err != nil {
return nextState, err return nextState, err
} }
switch { switch {
case b == ANSI_ESCAPE_SECONDARY: case b == ANSI_ESCAPE_SECONDARY:
return escState.parser.csiEntry, nil return escState.parser.CsiEntry, nil
case b == ANSI_OSC_STRING_ENTRY: case b == ANSI_OSC_STRING_ENTRY:
return escState.parser.oscString, nil return escState.parser.OscString, nil
case sliceContains(executors, b): case sliceContains(Executors, b):
return escState, escState.parser.execute() return escState, escState.parser.execute()
case sliceContains(escapeToGroundBytes, b): case sliceContains(EscapeToGroundBytes, b):
return escState.parser.ground, nil return escState.parser.Ground, nil
case sliceContains(intermeds, b): case sliceContains(Intermeds, b):
return escState.parser.escapeIntermediate, nil return escState.parser.EscapeIntermediate, nil
} }
return escState, nil return escState, nil
} }
func (escState escapeState) Transition(s state) error { func (escState EscapeState) Transition(s State) error {
logger.Infof("Escape::Transition %s --> %s", escState.Name(), s.Name()) logger.Infof("Escape::Transition %s --> %s", escState.Name(), s.Name())
escState.baseState.Transition(s) escState.BaseState.Transition(s)
switch s { switch s {
case escState.parser.ground: case escState.parser.Ground:
return escState.parser.escDispatch() return escState.parser.escDispatch()
case escState.parser.escapeIntermediate: case escState.parser.EscapeIntermediate:
return escState.parser.collectInter() return escState.parser.collectInter()
} }
return nil return nil
} }
func (escState escapeState) Enter() error { func (escState EscapeState) Enter() error {
escState.parser.clear() escState.parser.clear()
return nil return nil
} }

View File

@ -1,22 +1,22 @@
package ansiterm package ansiterm
type groundState struct { type GroundState struct {
baseState BaseState
} }
func (gs groundState) Handle(b byte) (s state, e error) { func (gs GroundState) Handle(b byte) (s State, e error) {
gs.parser.context.currentChar = b gs.parser.context.currentChar = b
nextState, err := gs.baseState.Handle(b) nextState, err := gs.BaseState.Handle(b)
if nextState != nil || err != nil { if nextState != nil || err != nil {
return nextState, err return nextState, err
} }
switch { switch {
case sliceContains(printables, b): case sliceContains(Printables, b):
return gs, gs.parser.print() return gs, gs.parser.print()
case sliceContains(executors, b): case sliceContains(Executors, b):
return gs, gs.parser.execute() return gs, gs.parser.execute()
} }

View File

@ -1,19 +1,19 @@
package ansiterm package ansiterm
type oscStringState struct { type OscStringState struct {
baseState BaseState
} }
func (oscState oscStringState) Handle(b byte) (s state, e error) { func (oscState OscStringState) Handle(b byte) (s State, e error) {
logger.Infof("OscString::Handle %#x", b) logger.Infof("OscString::Handle %#x", b)
nextState, err := oscState.baseState.Handle(b) nextState, err := oscState.BaseState.Handle(b)
if nextState != nil || err != nil { if nextState != nil || err != nil {
return nextState, err return nextState, err
} }
switch { switch {
case isOscStringTerminator(b): case isOscStringTerminator(b):
return oscState.parser.ground, nil return oscState.parser.Ground, nil
} }
return oscState, nil return oscState, nil

View File

@ -2,6 +2,7 @@ package ansiterm
import ( import (
"errors" "errors"
"fmt"
"io/ioutil" "io/ioutil"
"os" "os"
@ -11,18 +12,18 @@ import (
var logger *logrus.Logger var logger *logrus.Logger
type AnsiParser struct { type AnsiParser struct {
currState state currState State
eventHandler AnsiEventHandler eventHandler AnsiEventHandler
context *ansiContext context *AnsiContext
csiEntry state CsiEntry State
csiParam state CsiParam State
dcsEntry state DcsEntry State
escape state Escape State
escapeIntermediate state EscapeIntermediate State
error state Error State
ground state Ground State
oscString state OscString State
stateMap []state stateMap []State
} }
func CreateParser(initialState string, evtHandler AnsiEventHandler) *AnsiParser { func CreateParser(initialState string, evtHandler AnsiEventHandler) *AnsiParser {
@ -40,27 +41,27 @@ func CreateParser(initialState string, evtHandler AnsiEventHandler) *AnsiParser
parser := &AnsiParser{ parser := &AnsiParser{
eventHandler: evtHandler, eventHandler: evtHandler,
context: &ansiContext{}, context: &AnsiContext{},
} }
parser.csiEntry = csiEntryState{baseState{name: "CsiEntry", parser: parser}} parser.CsiEntry = CsiEntryState{BaseState{name: "CsiEntry", parser: parser}}
parser.csiParam = csiParamState{baseState{name: "CsiParam", parser: parser}} parser.CsiParam = CsiParamState{BaseState{name: "CsiParam", parser: parser}}
parser.dcsEntry = dcsEntryState{baseState{name: "DcsEntry", parser: parser}} parser.DcsEntry = DcsEntryState{BaseState{name: "DcsEntry", parser: parser}}
parser.escape = escapeState{baseState{name: "Escape", parser: parser}} parser.Escape = EscapeState{BaseState{name: "Escape", parser: parser}}
parser.escapeIntermediate = escapeIntermediateState{baseState{name: "EscapeIntermediate", parser: parser}} parser.EscapeIntermediate = EscapeIntermediateState{BaseState{name: "EscapeIntermediate", parser: parser}}
parser.error = errorState{baseState{name: "Error", parser: parser}} parser.Error = ErrorState{BaseState{name: "Error", parser: parser}}
parser.ground = groundState{baseState{name: "Ground", parser: parser}} parser.Ground = GroundState{BaseState{name: "Ground", parser: parser}}
parser.oscString = oscStringState{baseState{name: "OscString", parser: parser}} parser.OscString = OscStringState{BaseState{name: "OscString", parser: parser}}
parser.stateMap = []state{ parser.stateMap = []State{
parser.csiEntry, parser.CsiEntry,
parser.csiParam, parser.CsiParam,
parser.dcsEntry, parser.DcsEntry,
parser.escape, parser.Escape,
parser.escapeIntermediate, parser.EscapeIntermediate,
parser.error, parser.Error,
parser.ground, parser.Ground,
parser.oscString, parser.OscString,
} }
parser.currState = getState(initialState, parser.stateMap) parser.currState = getState(initialState, parser.stateMap)
@ -69,7 +70,7 @@ func CreateParser(initialState string, evtHandler AnsiEventHandler) *AnsiParser
return parser return parser
} }
func getState(name string, states []state) state { func getState(name string, states []State) State {
for _, el := range states { for _, el := range states {
if el.Name() == name { if el.Name() == name {
return el return el
@ -98,7 +99,7 @@ func (ap *AnsiParser) handle(b byte) error {
if newState == nil { if newState == nil {
logger.Warning("newState is nil") logger.Warning("newState is nil")
return errors.New("New state of 'nil' is invalid.") return errors.New(fmt.Sprintf("New state of 'nil' is invalid."))
} }
if newState != ap.currState { if newState != ap.currState {
@ -110,7 +111,7 @@ func (ap *AnsiParser) handle(b byte) error {
return nil return nil
} }
func (ap *AnsiParser) changeState(newState state) error { func (ap *AnsiParser) changeState(newState State) error {
logger.Infof("ChangeState %s --> %s", ap.currState.Name(), newState.Name()) logger.Infof("ChangeState %s --> %s", ap.currState.Name(), newState.Name())
// Exit old state // Exit old state

View File

@ -31,7 +31,7 @@ func parseParams(bytes []byte) ([]string, error) {
return params, nil return params, nil
} }
func parseCmd(context ansiContext) (string, error) { func parseCmd(context AnsiContext) (string, error) {
return string(context.currentChar), nil return string(context.currentChar), nil
} }

View File

@ -113,7 +113,7 @@ func (ap *AnsiParser) print() error {
} }
func (ap *AnsiParser) clear() error { func (ap *AnsiParser) clear() error {
ap.context = &ansiContext{} ap.context = &AnsiContext{}
return nil return nil
} }

View File

@ -0,0 +1,114 @@
package ansiterm
import (
"fmt"
"testing"
)
func getStateNames() []string {
parser, _ := createTestParser("Ground")
stateNames := []string{}
for _, state := range parser.stateMap {
stateNames = append(stateNames, state.Name())
}
return stateNames
}
func stateTransitionHelper(t *testing.T, start string, end string, bytes []byte) {
for _, b := range bytes {
bytes := []byte{byte(b)}
parser, _ := createTestParser(start)
parser.Parse(bytes)
validateState(t, parser.currState, end)
}
}
func anyToXHelper(t *testing.T, bytes []byte, expectedState string) {
for _, s := range getStateNames() {
stateTransitionHelper(t, s, expectedState, bytes)
}
}
func funcCallParamHelper(t *testing.T, bytes []byte, start string, expected string, expectedCalls []string) {
parser, evtHandler := createTestParser(start)
parser.Parse(bytes)
validateState(t, parser.currState, expected)
validateFuncCalls(t, evtHandler.FunctionCalls, expectedCalls)
}
func parseParamsHelper(t *testing.T, bytes []byte, expectedParams []string) {
params, err := parseParams(bytes)
if err != nil {
t.Errorf("Parameter parse error: %v", err)
return
}
if len(params) != len(expectedParams) {
t.Errorf("Parsed parameters: %v", params)
t.Errorf("Expected parameters: %v", expectedParams)
t.Errorf("Parameter length failure: %d != %d", len(params), len(expectedParams))
return
}
for i, v := range expectedParams {
if v != params[i] {
t.Errorf("Parsed parameters: %v", params)
t.Errorf("Expected parameters: %v", expectedParams)
t.Errorf("Parameter parse failure: %s != %s at position %d", v, params[i], i)
}
}
}
func cursorSingleParamHelper(t *testing.T, command byte, funcName string) {
funcCallParamHelper(t, []byte{command}, "CsiEntry", "Ground", []string{fmt.Sprintf("%s([1])", funcName)})
funcCallParamHelper(t, []byte{'0', command}, "CsiEntry", "Ground", []string{fmt.Sprintf("%s([1])", funcName)})
funcCallParamHelper(t, []byte{'2', command}, "CsiEntry", "Ground", []string{fmt.Sprintf("%s([2])", funcName)})
funcCallParamHelper(t, []byte{'2', '3', command}, "CsiEntry", "Ground", []string{fmt.Sprintf("%s([23])", funcName)})
funcCallParamHelper(t, []byte{'2', ';', '3', command}, "CsiEntry", "Ground", []string{fmt.Sprintf("%s([2])", funcName)})
funcCallParamHelper(t, []byte{'2', ';', '3', ';', '4', command}, "CsiEntry", "Ground", []string{fmt.Sprintf("%s([2])", funcName)})
}
func cursorTwoParamHelper(t *testing.T, command byte, funcName string) {
funcCallParamHelper(t, []byte{command}, "CsiEntry", "Ground", []string{fmt.Sprintf("%s([1 1])", funcName)})
funcCallParamHelper(t, []byte{'0', command}, "CsiEntry", "Ground", []string{fmt.Sprintf("%s([1 1])", funcName)})
funcCallParamHelper(t, []byte{'2', command}, "CsiEntry", "Ground", []string{fmt.Sprintf("%s([2 1])", funcName)})
funcCallParamHelper(t, []byte{'2', '3', command}, "CsiEntry", "Ground", []string{fmt.Sprintf("%s([23 1])", funcName)})
funcCallParamHelper(t, []byte{'2', ';', '3', command}, "CsiEntry", "Ground", []string{fmt.Sprintf("%s([2 3])", funcName)})
funcCallParamHelper(t, []byte{'2', ';', '3', ';', '4', command}, "CsiEntry", "Ground", []string{fmt.Sprintf("%s([2 3])", funcName)})
}
func eraseHelper(t *testing.T, command byte, funcName string) {
funcCallParamHelper(t, []byte{command}, "CsiEntry", "Ground", []string{fmt.Sprintf("%s([0])", funcName)})
funcCallParamHelper(t, []byte{'0', command}, "CsiEntry", "Ground", []string{fmt.Sprintf("%s([0])", funcName)})
funcCallParamHelper(t, []byte{'1', command}, "CsiEntry", "Ground", []string{fmt.Sprintf("%s([1])", funcName)})
funcCallParamHelper(t, []byte{'2', command}, "CsiEntry", "Ground", []string{fmt.Sprintf("%s([2])", funcName)})
funcCallParamHelper(t, []byte{'3', command}, "CsiEntry", "Ground", []string{fmt.Sprintf("%s([3])", funcName)})
funcCallParamHelper(t, []byte{'4', command}, "CsiEntry", "Ground", []string{fmt.Sprintf("%s([0])", funcName)})
funcCallParamHelper(t, []byte{'1', ';', '2', command}, "CsiEntry", "Ground", []string{fmt.Sprintf("%s([1])", funcName)})
}
func scrollHelper(t *testing.T, command byte, funcName string) {
funcCallParamHelper(t, []byte{command}, "CsiEntry", "Ground", []string{fmt.Sprintf("%s([1])", funcName)})
funcCallParamHelper(t, []byte{'0', command}, "CsiEntry", "Ground", []string{fmt.Sprintf("%s([1])", funcName)})
funcCallParamHelper(t, []byte{'1', command}, "CsiEntry", "Ground", []string{fmt.Sprintf("%s([1])", funcName)})
funcCallParamHelper(t, []byte{'5', command}, "CsiEntry", "Ground", []string{fmt.Sprintf("%s([5])", funcName)})
funcCallParamHelper(t, []byte{'4', ';', '6', command}, "CsiEntry", "Ground", []string{fmt.Sprintf("%s([4])", funcName)})
}
func clearOnStateChangeHelper(t *testing.T, start string, end string, bytes []byte) {
p, _ := createTestParser(start)
fillContext(p.context)
p.Parse(bytes)
validateState(t, p.currState, end)
validateEmptyContext(t, p.context)
}
func c0Helper(t *testing.T, bytes []byte, expectedState string, expectedCalls []string) {
parser, evtHandler := createTestParser("Ground")
parser.Parse(bytes)
validateState(t, parser.currState, expectedState)
validateFuncCalls(t, evtHandler.FunctionCalls, expectedCalls)
}

View File

@ -0,0 +1,66 @@
package ansiterm
import (
"testing"
)
func createTestParser(s string) (*AnsiParser, *TestAnsiEventHandler) {
evtHandler := CreateTestAnsiEventHandler()
parser := CreateParser(s, evtHandler)
return parser, evtHandler
}
func validateState(t *testing.T, actualState State, expectedStateName string) {
actualName := "Nil"
if actualState != nil {
actualName = actualState.Name()
}
if actualName != expectedStateName {
t.Errorf("Invalid State: '%s' != '%s'", actualName, expectedStateName)
}
}
func validateFuncCalls(t *testing.T, actualCalls []string, expectedCalls []string) {
actualCount := len(actualCalls)
expectedCount := len(expectedCalls)
if actualCount != expectedCount {
t.Errorf("Actual calls: %v", actualCalls)
t.Errorf("Expected calls: %v", expectedCalls)
t.Errorf("Call count error: %d != %d", actualCount, expectedCount)
return
}
for i, v := range actualCalls {
if v != expectedCalls[i] {
t.Errorf("Actual calls: %v", actualCalls)
t.Errorf("Expected calls: %v", expectedCalls)
t.Errorf("Mismatched calls: %s != %s with lengths %d and %d", v, expectedCalls[i], len(v), len(expectedCalls[i]))
}
}
}
func fillContext(context *AnsiContext) {
context.currentChar = 'A'
context.paramBuffer = []byte{'C', 'D', 'E'}
context.interBuffer = []byte{'F', 'G', 'H'}
}
func validateEmptyContext(t *testing.T, context *AnsiContext) {
var expectedCurrChar byte = 0x0
if context.currentChar != expectedCurrChar {
t.Errorf("Currentchar mismatch '%#x' != '%#x'", context.currentChar, expectedCurrChar)
}
if len(context.paramBuffer) != 0 {
t.Errorf("Non-empty parameter buffer: %v", context.paramBuffer)
}
if len(context.paramBuffer) != 0 {
t.Errorf("Non-empty intermediate buffer: %v", context.interBuffer)
}
}

View File

@ -1,52 +1,52 @@
package ansiterm package ansiterm
type stateID int type StateId int
type state interface { type State interface {
Enter() error Enter() error
Exit() error Exit() error
Handle(byte) (state, error) Handle(byte) (State, error)
Name() string Name() string
Transition(state) error Transition(State) error
} }
type baseState struct { type BaseState struct {
name string name string
parser *AnsiParser parser *AnsiParser
} }
func (base baseState) Enter() error { func (base BaseState) Enter() error {
return nil return nil
} }
func (base baseState) Exit() error { func (base BaseState) Exit() error {
return nil return nil
} }
func (base baseState) Handle(b byte) (s state, e error) { func (base BaseState) Handle(b byte) (s State, e error) {
switch { switch {
case b == CSI_ENTRY: case b == CSI_ENTRY:
return base.parser.csiEntry, nil return base.parser.CsiEntry, nil
case b == DCS_ENTRY: case b == DCS_ENTRY:
return base.parser.dcsEntry, nil return base.parser.DcsEntry, nil
case b == ANSI_ESCAPE_PRIMARY: case b == ANSI_ESCAPE_PRIMARY:
return base.parser.escape, nil return base.parser.Escape, nil
case b == OSC_STRING: case b == OSC_STRING:
return base.parser.oscString, nil return base.parser.OscString, nil
case sliceContains(toGroundBytes, b): case sliceContains(ToGroundBytes, b):
return base.parser.ground, nil return base.parser.Ground, nil
} }
return nil, nil return nil, nil
} }
func (base baseState) Name() string { func (base BaseState) Name() string {
return base.name return base.name
} }
func (base baseState) Transition(s state) error { func (base BaseState) Transition(s State) error {
if s == base.parser.ground { if s == base.parser.Ground {
execBytes := []byte{0x18} execBytes := []byte{0x18}
execBytes = append(execBytes, 0x1A) execBytes = append(execBytes, 0x1A)
execBytes = append(execBytes, getByteRange(0x80, 0x8F)...) execBytes = append(execBytes, getByteRange(0x80, 0x8F)...)
@ -62,10 +62,10 @@ func (base baseState) Transition(s state) error {
return nil return nil
} }
type dcsEntryState struct { type DcsEntryState struct {
baseState BaseState
} }
type errorState struct { type ErrorState struct {
baseState BaseState
} }

View File

@ -0,0 +1,173 @@
package ansiterm
import (
"fmt"
"strconv"
)
type TestAnsiEventHandler struct {
FunctionCalls []string
}
func CreateTestAnsiEventHandler() *TestAnsiEventHandler {
evtHandler := TestAnsiEventHandler{}
evtHandler.FunctionCalls = make([]string, 0)
return &evtHandler
}
func (h *TestAnsiEventHandler) recordCall(call string, params []string) {
s := fmt.Sprintf("%s(%v)", call, params)
h.FunctionCalls = append(h.FunctionCalls, s)
}
func (h *TestAnsiEventHandler) Print(b byte) error {
h.recordCall("Print", []string{string(b)})
return nil
}
func (h *TestAnsiEventHandler) Execute(b byte) error {
h.recordCall("Execute", []string{string(b)})
return nil
}
func (h *TestAnsiEventHandler) CUU(param int) error {
h.recordCall("CUU", []string{strconv.Itoa(param)})
return nil
}
func (h *TestAnsiEventHandler) CUD(param int) error {
h.recordCall("CUD", []string{strconv.Itoa(param)})
return nil
}
func (h *TestAnsiEventHandler) CUF(param int) error {
h.recordCall("CUF", []string{strconv.Itoa(param)})
return nil
}
func (h *TestAnsiEventHandler) CUB(param int) error {
h.recordCall("CUB", []string{strconv.Itoa(param)})
return nil
}
func (h *TestAnsiEventHandler) CNL(param int) error {
h.recordCall("CNL", []string{strconv.Itoa(param)})
return nil
}
func (h *TestAnsiEventHandler) CPL(param int) error {
h.recordCall("CPL", []string{strconv.Itoa(param)})
return nil
}
func (h *TestAnsiEventHandler) CHA(param int) error {
h.recordCall("CHA", []string{strconv.Itoa(param)})
return nil
}
func (h *TestAnsiEventHandler) VPA(param int) error {
h.recordCall("VPA", []string{strconv.Itoa(param)})
return nil
}
func (h *TestAnsiEventHandler) CUP(x int, y int) error {
xS, yS := strconv.Itoa(x), strconv.Itoa(y)
h.recordCall("CUP", []string{xS, yS})
return nil
}
func (h *TestAnsiEventHandler) HVP(x int, y int) error {
xS, yS := strconv.Itoa(x), strconv.Itoa(y)
h.recordCall("HVP", []string{xS, yS})
return nil
}
func (h *TestAnsiEventHandler) DECTCEM(visible bool) error {
h.recordCall("DECTCEM", []string{strconv.FormatBool(visible)})
return nil
}
func (h *TestAnsiEventHandler) DECOM(visible bool) error {
h.recordCall("DECOM", []string{strconv.FormatBool(visible)})
return nil
}
func (h *TestAnsiEventHandler) DECCOLM(use132 bool) error {
h.recordCall("DECOLM", []string{strconv.FormatBool(use132)})
return nil
}
func (h *TestAnsiEventHandler) ED(param int) error {
h.recordCall("ED", []string{strconv.Itoa(param)})
return nil
}
func (h *TestAnsiEventHandler) EL(param int) error {
h.recordCall("EL", []string{strconv.Itoa(param)})
return nil
}
func (h *TestAnsiEventHandler) IL(param int) error {
h.recordCall("IL", []string{strconv.Itoa(param)})
return nil
}
func (h *TestAnsiEventHandler) DL(param int) error {
h.recordCall("DL", []string{strconv.Itoa(param)})
return nil
}
func (h *TestAnsiEventHandler) ICH(param int) error {
h.recordCall("ICH", []string{strconv.Itoa(param)})
return nil
}
func (h *TestAnsiEventHandler) DCH(param int) error {
h.recordCall("DCH", []string{strconv.Itoa(param)})
return nil
}
func (h *TestAnsiEventHandler) SGR(params []int) error {
strings := []string{}
for _, v := range params {
strings = append(strings, strconv.Itoa(v))
}
h.recordCall("SGR", strings)
return nil
}
func (h *TestAnsiEventHandler) SU(param int) error {
h.recordCall("SU", []string{strconv.Itoa(param)})
return nil
}
func (h *TestAnsiEventHandler) SD(param int) error {
h.recordCall("SD", []string{strconv.Itoa(param)})
return nil
}
func (h *TestAnsiEventHandler) DA(params []string) error {
h.recordCall("DA", params)
return nil
}
func (h *TestAnsiEventHandler) DECSTBM(top int, bottom int) error {
topS, bottomS := strconv.Itoa(top), strconv.Itoa(bottom)
h.recordCall("DECSTBM", []string{topS, bottomS})
return nil
}
func (h *TestAnsiEventHandler) RI() error {
h.recordCall("RI", nil)
return nil
}
func (h *TestAnsiEventHandler) IND() error {
h.recordCall("IND", nil)
return nil
}
func (h *TestAnsiEventHandler) Flush() error {
return nil
}

View File

@ -9,7 +9,7 @@ import (
"strings" "strings"
"syscall" "syscall"
"github.com/Azure/go-ansiterm" . "github.com/Azure/go-ansiterm"
) )
// Windows keyboard constants // Windows keyboard constants
@ -85,17 +85,17 @@ func newAnsiCommand(command []byte) *ansiCommand {
if lastCharIndex != 0 { if lastCharIndex != 0 {
start := 1 start := 1
// skip if double char escape sequence // skip if double char escape sequence
if command[0] == ansiterm.ANSI_ESCAPE_PRIMARY && command[1] == ansiterm.ANSI_ESCAPE_SECONDARY { if command[0] == ANSI_ESCAPE_PRIMARY && command[1] == ANSI_ESCAPE_SECONDARY {
start++ start++
} }
// convert this to GetNextParam method // convert this to GetNextParam method
ac.Parameters = strings.Split(string(command[start:lastCharIndex]), ansiterm.ANSI_PARAMETER_SEP) ac.Parameters = strings.Split(string(command[start:lastCharIndex]), ANSI_PARAMETER_SEP)
} }
return ac return ac
} }
func (ac *ansiCommand) paramAsSHORT(index int, defaultValue int16) int16 { func (ac *ansiCommand) paramAsSHORT(index int, defaultValue SHORT) SHORT {
if index < 0 || index >= len(ac.Parameters) { if index < 0 || index >= len(ac.Parameters) {
return defaultValue return defaultValue
} }
@ -105,7 +105,7 @@ func (ac *ansiCommand) paramAsSHORT(index int, defaultValue int16) int16 {
return defaultValue return defaultValue
} }
return int16(param) return SHORT(param)
} }
func (ac *ansiCommand) String() string { func (ac *ansiCommand) String() string {
@ -119,12 +119,12 @@ func (ac *ansiCommand) String() string {
// See http://manpages.ubuntu.com/manpages/intrepid/man4/console_codes.4.html. // See http://manpages.ubuntu.com/manpages/intrepid/man4/console_codes.4.html.
func isAnsiCommandChar(b byte) bool { func isAnsiCommandChar(b byte) bool {
switch { switch {
case ansiterm.ANSI_COMMAND_FIRST <= b && b <= ansiterm.ANSI_COMMAND_LAST && b != ansiterm.ANSI_ESCAPE_SECONDARY: case ANSI_COMMAND_FIRST <= b && b <= ANSI_COMMAND_LAST && b != ANSI_ESCAPE_SECONDARY:
return true return true
case b == ansiterm.ANSI_CMD_G1 || b == ansiterm.ANSI_CMD_OSC || b == ansiterm.ANSI_CMD_DECPAM || b == ansiterm.ANSI_CMD_DECPNM: case b == ANSI_CMD_G1 || b == ANSI_CMD_OSC || b == ANSI_CMD_DECPAM || b == ANSI_CMD_DECPNM:
// non-CSI escape sequence terminator // non-CSI escape sequence terminator
return true return true
case b == ansiterm.ANSI_CMD_STR_TERM || b == ansiterm.ANSI_BEL: case b == ANSI_CMD_STR_TERM || b == ANSI_BEL:
// String escape sequence terminator // String escape sequence terminator
return true return true
} }
@ -132,11 +132,11 @@ func isAnsiCommandChar(b byte) bool {
} }
func isXtermOscSequence(command []byte, current byte) bool { func isXtermOscSequence(command []byte, current byte) bool {
return (len(command) >= 2 && command[0] == ansiterm.ANSI_ESCAPE_PRIMARY && command[1] == ansiterm.ANSI_CMD_OSC && current != ansiterm.ANSI_BEL) return (len(command) >= 2 && command[0] == ANSI_ESCAPE_PRIMARY && command[1] == ANSI_CMD_OSC && current != ANSI_BEL)
} }
func isCharacterSelectionCmdChar(b byte) bool { func isCharacterSelectionCmdChar(b byte) bool {
return (b == ansiterm.ANSI_CMD_G0 || b == ansiterm.ANSI_CMD_G1 || b == ansiterm.ANSI_CMD_G2 || b == ansiterm.ANSI_CMD_G3) return (b == ANSI_CMD_G0 || b == ANSI_CMD_G1 || b == ANSI_CMD_G2 || b == ANSI_CMD_G3)
} }
// bytesToHex converts a slice of bytes to a human-readable string. // bytesToHex converts a slice of bytes to a human-readable string.
@ -150,7 +150,7 @@ func bytesToHex(b []byte) string {
// ensureInRange adjusts the passed value, if necessary, to ensure it is within // ensureInRange adjusts the passed value, if necessary, to ensure it is within
// the passed min / max range. // the passed min / max range.
func ensureInRange(n int16, min int16, max int16) int16 { func ensureInRange(n SHORT, min SHORT, max SHORT) SHORT {
if n < min { if n < min {
return min return min
} else if n > max { } else if n > max {

View File

@ -66,21 +66,21 @@ const (
// -- The attributes are combined to produce various colors (e.g., Blue + Green will create Cyan). // -- The attributes are combined to produce various colors (e.g., Blue + Green will create Cyan).
// Clearing all foreground or background colors results in black; setting all creates white. // Clearing all foreground or background colors results in black; setting all creates white.
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms682088(v=vs.85).aspx#_win32_character_attributes. // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms682088(v=vs.85).aspx#_win32_character_attributes.
FOREGROUND_BLUE uint16 = 0x0001 FOREGROUND_BLUE WORD = 0x0001
FOREGROUND_GREEN uint16 = 0x0002 FOREGROUND_GREEN WORD = 0x0002
FOREGROUND_RED uint16 = 0x0004 FOREGROUND_RED WORD = 0x0004
FOREGROUND_INTENSITY uint16 = 0x0008 FOREGROUND_INTENSITY WORD = 0x0008
FOREGROUND_MASK uint16 = 0x000F FOREGROUND_MASK WORD = 0x000F
BACKGROUND_BLUE uint16 = 0x0010 BACKGROUND_BLUE WORD = 0x0010
BACKGROUND_GREEN uint16 = 0x0020 BACKGROUND_GREEN WORD = 0x0020
BACKGROUND_RED uint16 = 0x0040 BACKGROUND_RED WORD = 0x0040
BACKGROUND_INTENSITY uint16 = 0x0080 BACKGROUND_INTENSITY WORD = 0x0080
BACKGROUND_MASK uint16 = 0x00F0 BACKGROUND_MASK WORD = 0x00F0
COMMON_LVB_MASK uint16 = 0xFF00 COMMON_LVB_MASK WORD = 0xFF00
COMMON_LVB_REVERSE_VIDEO uint16 = 0x4000 COMMON_LVB_REVERSE_VIDEO WORD = 0x4000
COMMON_LVB_UNDERSCORE uint16 = 0x8000 COMMON_LVB_UNDERSCORE WORD = 0x8000
// Input event types // Input event types
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683499(v=vs.85).aspx. // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683499(v=vs.85).aspx.
@ -104,53 +104,60 @@ const (
) )
// Windows API Console types // Windows API Console types
// -- See https://msdn.microsoft.com/en-us/library/windows/desktop/aa383751(v=vs.85).aspx for core types (e.g., SHORT)
// -- See https://msdn.microsoft.com/en-us/library/windows/desktop/ms682101(v=vs.85).aspx for Console specific types (e.g., COORD) // -- See https://msdn.microsoft.com/en-us/library/windows/desktop/ms682101(v=vs.85).aspx for Console specific types (e.g., COORD)
// -- See https://msdn.microsoft.com/en-us/library/aa296569(v=vs.60).aspx for comments on alignment // -- See https://msdn.microsoft.com/en-us/library/aa296569(v=vs.60).aspx for comments on alignment
type ( type (
SHORT int16
BOOL int32
WORD uint16
WCHAR uint16
DWORD uint32
CHAR_INFO struct { CHAR_INFO struct {
UnicodeChar uint16 UnicodeChar WCHAR
Attributes uint16 Attributes WORD
} }
CONSOLE_CURSOR_INFO struct { CONSOLE_CURSOR_INFO struct {
Size uint32 Size DWORD
Visible int32 Visible BOOL
} }
CONSOLE_SCREEN_BUFFER_INFO struct { CONSOLE_SCREEN_BUFFER_INFO struct {
Size COORD Size COORD
CursorPosition COORD CursorPosition COORD
Attributes uint16 Attributes WORD
Window SMALL_RECT Window SMALL_RECT
MaximumWindowSize COORD MaximumWindowSize COORD
} }
COORD struct { COORD struct {
X int16 X SHORT
Y int16 Y SHORT
} }
SMALL_RECT struct { SMALL_RECT struct {
Left int16 Left SHORT
Top int16 Top SHORT
Right int16 Right SHORT
Bottom int16 Bottom SHORT
} }
// INPUT_RECORD is a C/C++ union of which KEY_EVENT_RECORD is one case, it is also the largest // INPUT_RECORD is a C/C++ union of which KEY_EVENT_RECORD is one case, it is also the largest
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683499(v=vs.85).aspx. // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683499(v=vs.85).aspx.
INPUT_RECORD struct { INPUT_RECORD struct {
EventType uint16 EventType WORD
KeyEvent KEY_EVENT_RECORD KeyEvent KEY_EVENT_RECORD
} }
KEY_EVENT_RECORD struct { KEY_EVENT_RECORD struct {
KeyDown int32 KeyDown BOOL
RepeatCount uint16 RepeatCount WORD
VirtualKeyCode uint16 VirtualKeyCode WORD
VirtualScanCode uint16 VirtualScanCode WORD
UnicodeChar uint16 UnicodeChar WCHAR
ControlKeyState uint32 ControlKeyState DWORD
} }
WINDOW_BUFFER_SIZE struct { WINDOW_BUFFER_SIZE struct {
@ -158,12 +165,12 @@ type (
} }
) )
// boolToBOOL converts a Go bool into a Windows int32. // boolToBOOL converts a Go bool into a Windows BOOL.
func boolToBOOL(f bool) int32 { func boolToBOOL(f bool) BOOL {
if f { if f {
return int32(1) return BOOL(1)
} else { } else {
return int32(0) return BOOL(0)
} }
} }
@ -235,7 +242,7 @@ func SetConsoleScreenBufferSize(handle uintptr, coord COORD) error {
// SetConsoleTextAttribute sets the attributes of characters written to the // SetConsoleTextAttribute sets the attributes of characters written to the
// console screen buffer by the WriteFile or WriteConsole function. // console screen buffer by the WriteFile or WriteConsole function.
// See http://msdn.microsoft.com/en-us/library/windows/desktop/ms686047(v=vs.85).aspx. // See http://msdn.microsoft.com/en-us/library/windows/desktop/ms686047(v=vs.85).aspx.
func SetConsoleTextAttribute(handle uintptr, attribute uint16) error { func SetConsoleTextAttribute(handle uintptr, attribute WORD) error {
r1, r2, err := setConsoleTextAttributeProc.Call(handle, uintptr(attribute), 0) r1, r2, err := setConsoleTextAttributeProc.Call(handle, uintptr(attribute), 0)
use(attribute) use(attribute)
return checkError(r1, r2, err) return checkError(r1, r2, err)
@ -273,7 +280,7 @@ func ReadConsoleInput(handle uintptr, buffer []INPUT_RECORD, count *uint32) erro
// It returns true if the handle was signaled; false otherwise. // It returns true if the handle was signaled; false otherwise.
// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms687032(v=vs.85).aspx. // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms687032(v=vs.85).aspx.
func WaitForSingleObject(handle uintptr, msWait uint32) (bool, error) { func WaitForSingleObject(handle uintptr, msWait uint32) (bool, error) {
r1, _, err := waitForSingleObjectProc.Call(handle, uintptr(uint32(msWait))) r1, _, err := waitForSingleObjectProc.Call(handle, uintptr(DWORD(msWait)))
switch r1 { switch r1 {
case WAIT_ABANDONED, WAIT_TIMEOUT: case WAIT_ABANDONED, WAIT_TIMEOUT:
return false, nil return false, nil
@ -313,8 +320,8 @@ func checkError(r1, r2 uintptr, err error) error {
// coordToPointer converts a COORD into a uintptr (by fooling the type system). // coordToPointer converts a COORD into a uintptr (by fooling the type system).
func coordToPointer(c COORD) uintptr { func coordToPointer(c COORD) uintptr {
// Note: This code assumes the two SHORTs are correctly laid out; the "cast" to uint32 is just to get a pointer to pass. // Note: This code assumes the two SHORTs are correctly laid out; the "cast" to DWORD is just to get a pointer to pass.
return uintptr(*((*uint32)(unsafe.Pointer(&c)))) return uintptr(*((*DWORD)(unsafe.Pointer(&c))))
} }
// use is a no-op, but the compiler cannot see that it is. // use is a no-op, but the compiler cannot see that it is.

View File

@ -2,7 +2,9 @@
package winterm package winterm
import "github.com/Azure/go-ansiterm" import (
. "github.com/Azure/go-ansiterm"
)
const ( const (
FOREGROUND_COLOR_MASK = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE FOREGROUND_COLOR_MASK = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE
@ -11,83 +13,83 @@ const (
// collectAnsiIntoWindowsAttributes modifies the passed Windows text mode flags to reflect the // collectAnsiIntoWindowsAttributes modifies the passed Windows text mode flags to reflect the
// request represented by the passed ANSI mode. // request represented by the passed ANSI mode.
func collectAnsiIntoWindowsAttributes(windowsMode uint16, inverted bool, baseMode uint16, ansiMode int16) (uint16, bool) { func collectAnsiIntoWindowsAttributes(windowsMode WORD, inverted bool, baseMode WORD, ansiMode SHORT) (WORD, bool) {
switch ansiMode { switch ansiMode {
// Mode styles // Mode styles
case ansiterm.ANSI_SGR_BOLD: case ANSI_SGR_BOLD:
windowsMode = windowsMode | FOREGROUND_INTENSITY windowsMode = windowsMode | FOREGROUND_INTENSITY
case ansiterm.ANSI_SGR_DIM, ansiterm.ANSI_SGR_BOLD_DIM_OFF: case ANSI_SGR_DIM, ANSI_SGR_BOLD_DIM_OFF:
windowsMode &^= FOREGROUND_INTENSITY windowsMode &^= FOREGROUND_INTENSITY
case ansiterm.ANSI_SGR_UNDERLINE: case ANSI_SGR_UNDERLINE:
windowsMode = windowsMode | COMMON_LVB_UNDERSCORE windowsMode = windowsMode | COMMON_LVB_UNDERSCORE
case ansiterm.ANSI_SGR_REVERSE: case ANSI_SGR_REVERSE:
inverted = true inverted = true
case ansiterm.ANSI_SGR_REVERSE_OFF: case ANSI_SGR_REVERSE_OFF:
inverted = false inverted = false
case ansiterm.ANSI_SGR_UNDERLINE_OFF: case ANSI_SGR_UNDERLINE_OFF:
windowsMode &^= COMMON_LVB_UNDERSCORE windowsMode &^= COMMON_LVB_UNDERSCORE
// Foreground colors // Foreground colors
case ansiterm.ANSI_SGR_FOREGROUND_DEFAULT: case ANSI_SGR_FOREGROUND_DEFAULT:
windowsMode = (windowsMode &^ FOREGROUND_MASK) | (baseMode & FOREGROUND_MASK) windowsMode = (windowsMode &^ FOREGROUND_MASK) | (baseMode & FOREGROUND_MASK)
case ansiterm.ANSI_SGR_FOREGROUND_BLACK: case ANSI_SGR_FOREGROUND_BLACK:
windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK) windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK)
case ansiterm.ANSI_SGR_FOREGROUND_RED: case ANSI_SGR_FOREGROUND_RED:
windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK) | FOREGROUND_RED windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK) | FOREGROUND_RED
case ansiterm.ANSI_SGR_FOREGROUND_GREEN: case ANSI_SGR_FOREGROUND_GREEN:
windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK) | FOREGROUND_GREEN windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK) | FOREGROUND_GREEN
case ansiterm.ANSI_SGR_FOREGROUND_YELLOW: case ANSI_SGR_FOREGROUND_YELLOW:
windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK) | FOREGROUND_RED | FOREGROUND_GREEN windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK) | FOREGROUND_RED | FOREGROUND_GREEN
case ansiterm.ANSI_SGR_FOREGROUND_BLUE: case ANSI_SGR_FOREGROUND_BLUE:
windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK) | FOREGROUND_BLUE windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK) | FOREGROUND_BLUE
case ansiterm.ANSI_SGR_FOREGROUND_MAGENTA: case ANSI_SGR_FOREGROUND_MAGENTA:
windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK) | FOREGROUND_RED | FOREGROUND_BLUE windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK) | FOREGROUND_RED | FOREGROUND_BLUE
case ansiterm.ANSI_SGR_FOREGROUND_CYAN: case ANSI_SGR_FOREGROUND_CYAN:
windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK) | FOREGROUND_GREEN | FOREGROUND_BLUE windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK) | FOREGROUND_GREEN | FOREGROUND_BLUE
case ansiterm.ANSI_SGR_FOREGROUND_WHITE: case ANSI_SGR_FOREGROUND_WHITE:
windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK) | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE windowsMode = (windowsMode &^ FOREGROUND_COLOR_MASK) | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE
// Background colors // Background colors
case ansiterm.ANSI_SGR_BACKGROUND_DEFAULT: case ANSI_SGR_BACKGROUND_DEFAULT:
// Black with no intensity // Black with no intensity
windowsMode = (windowsMode &^ BACKGROUND_MASK) | (baseMode & BACKGROUND_MASK) windowsMode = (windowsMode &^ BACKGROUND_MASK) | (baseMode & BACKGROUND_MASK)
case ansiterm.ANSI_SGR_BACKGROUND_BLACK: case ANSI_SGR_BACKGROUND_BLACK:
windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK)
case ansiterm.ANSI_SGR_BACKGROUND_RED: case ANSI_SGR_BACKGROUND_RED:
windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) | BACKGROUND_RED windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) | BACKGROUND_RED
case ansiterm.ANSI_SGR_BACKGROUND_GREEN: case ANSI_SGR_BACKGROUND_GREEN:
windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) | BACKGROUND_GREEN windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) | BACKGROUND_GREEN
case ansiterm.ANSI_SGR_BACKGROUND_YELLOW: case ANSI_SGR_BACKGROUND_YELLOW:
windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) | BACKGROUND_RED | BACKGROUND_GREEN windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) | BACKGROUND_RED | BACKGROUND_GREEN
case ansiterm.ANSI_SGR_BACKGROUND_BLUE: case ANSI_SGR_BACKGROUND_BLUE:
windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) | BACKGROUND_BLUE windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) | BACKGROUND_BLUE
case ansiterm.ANSI_SGR_BACKGROUND_MAGENTA: case ANSI_SGR_BACKGROUND_MAGENTA:
windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) | BACKGROUND_RED | BACKGROUND_BLUE windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) | BACKGROUND_RED | BACKGROUND_BLUE
case ansiterm.ANSI_SGR_BACKGROUND_CYAN: case ANSI_SGR_BACKGROUND_CYAN:
windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) | BACKGROUND_GREEN | BACKGROUND_BLUE windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) | BACKGROUND_GREEN | BACKGROUND_BLUE
case ansiterm.ANSI_SGR_BACKGROUND_WHITE: case ANSI_SGR_BACKGROUND_WHITE:
windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) | BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE windowsMode = (windowsMode &^ BACKGROUND_COLOR_MASK) | BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE
} }
@ -95,6 +97,6 @@ func collectAnsiIntoWindowsAttributes(windowsMode uint16, inverted bool, baseMod
} }
// invertAttributes inverts the foreground and background colors of a Windows attributes value // invertAttributes inverts the foreground and background colors of a Windows attributes value
func invertAttributes(windowsMode uint16) uint16 { func invertAttributes(windowsMode WORD) WORD {
return (COMMON_LVB_MASK & windowsMode) | ((FOREGROUND_MASK & windowsMode) << 4) | ((BACKGROUND_MASK & windowsMode) >> 4) return (COMMON_LVB_MASK & windowsMode) | ((FOREGROUND_MASK & windowsMode) << 4) | ((BACKGROUND_MASK & windowsMode) >> 4)
} }

View File

@ -3,11 +3,11 @@
package winterm package winterm
const ( const (
horizontal = iota Horizontal = iota
vertical Vertical
) )
func (h *windowsAnsiEventHandler) getCursorWindow(info *CONSOLE_SCREEN_BUFFER_INFO) SMALL_RECT { func (h *WindowsAnsiEventHandler) getCursorWindow(info *CONSOLE_SCREEN_BUFFER_INFO) SMALL_RECT {
if h.originMode { if h.originMode {
sr := h.effectiveSr(info.Window) sr := h.effectiveSr(info.Window)
return SMALL_RECT{ return SMALL_RECT{
@ -27,7 +27,7 @@ func (h *windowsAnsiEventHandler) getCursorWindow(info *CONSOLE_SCREEN_BUFFER_IN
} }
// setCursorPosition sets the cursor to the specified position, bounded to the screen size // setCursorPosition sets the cursor to the specified position, bounded to the screen size
func (h *windowsAnsiEventHandler) setCursorPosition(position COORD, window SMALL_RECT) error { func (h *WindowsAnsiEventHandler) setCursorPosition(position COORD, window SMALL_RECT) error {
position.X = ensureInRange(position.X, window.Left, window.Right) position.X = ensureInRange(position.X, window.Left, window.Right)
position.Y = ensureInRange(position.Y, window.Top, window.Bottom) position.Y = ensureInRange(position.Y, window.Top, window.Bottom)
err := SetConsoleCursorPosition(h.fd, position) err := SetConsoleCursorPosition(h.fd, position)
@ -38,15 +38,15 @@ func (h *windowsAnsiEventHandler) setCursorPosition(position COORD, window SMALL
return err return err
} }
func (h *windowsAnsiEventHandler) moveCursorVertical(param int) error { func (h *WindowsAnsiEventHandler) moveCursorVertical(param int) error {
return h.moveCursor(vertical, param) return h.moveCursor(Vertical, param)
} }
func (h *windowsAnsiEventHandler) moveCursorHorizontal(param int) error { func (h *WindowsAnsiEventHandler) moveCursorHorizontal(param int) error {
return h.moveCursor(horizontal, param) return h.moveCursor(Horizontal, param)
} }
func (h *windowsAnsiEventHandler) moveCursor(moveMode int, param int) error { func (h *WindowsAnsiEventHandler) moveCursor(moveMode int, param int) error {
info, err := GetConsoleScreenBufferInfo(h.fd) info, err := GetConsoleScreenBufferInfo(h.fd)
if err != nil { if err != nil {
return err return err
@ -54,10 +54,10 @@ func (h *windowsAnsiEventHandler) moveCursor(moveMode int, param int) error {
position := info.CursorPosition position := info.CursorPosition
switch moveMode { switch moveMode {
case horizontal: case Horizontal:
position.X += int16(param) position.X += SHORT(param)
case vertical: case Vertical:
position.Y += int16(param) position.Y += SHORT(param)
} }
if err = h.setCursorPosition(position, h.getCursorWindow(info)); err != nil { if err = h.setCursorPosition(position, h.getCursorWindow(info)); err != nil {
@ -67,7 +67,7 @@ func (h *windowsAnsiEventHandler) moveCursor(moveMode int, param int) error {
return nil return nil
} }
func (h *windowsAnsiEventHandler) moveCursorLine(param int) error { func (h *WindowsAnsiEventHandler) moveCursorLine(param int) error {
info, err := GetConsoleScreenBufferInfo(h.fd) info, err := GetConsoleScreenBufferInfo(h.fd)
if err != nil { if err != nil {
return err return err
@ -75,7 +75,7 @@ func (h *windowsAnsiEventHandler) moveCursorLine(param int) error {
position := info.CursorPosition position := info.CursorPosition
position.X = 0 position.X = 0
position.Y += int16(param) position.Y += SHORT(param)
if err = h.setCursorPosition(position, h.getCursorWindow(info)); err != nil { if err = h.setCursorPosition(position, h.getCursorWindow(info)); err != nil {
return err return err
@ -84,14 +84,14 @@ func (h *windowsAnsiEventHandler) moveCursorLine(param int) error {
return nil return nil
} }
func (h *windowsAnsiEventHandler) moveCursorColumn(param int) error { func (h *WindowsAnsiEventHandler) moveCursorColumn(param int) error {
info, err := GetConsoleScreenBufferInfo(h.fd) info, err := GetConsoleScreenBufferInfo(h.fd)
if err != nil { if err != nil {
return err return err
} }
position := info.CursorPosition position := info.CursorPosition
position.X = int16(param) - 1 position.X = SHORT(param) - 1
if err = h.setCursorPosition(position, h.getCursorWindow(info)); err != nil { if err = h.setCursorPosition(position, h.getCursorWindow(info)); err != nil {
return err return err

View File

@ -2,9 +2,11 @@
package winterm package winterm
import "github.com/Azure/go-ansiterm" import (
. "github.com/Azure/go-ansiterm"
)
func (h *windowsAnsiEventHandler) clearRange(attributes uint16, fromCoord COORD, toCoord COORD) error { func (h *WindowsAnsiEventHandler) clearRange(attributes WORD, fromCoord COORD, toCoord COORD) error {
// Ignore an invalid (negative area) request // Ignore an invalid (negative area) request
if toCoord.Y < fromCoord.Y { if toCoord.Y < fromCoord.Y {
return nil return nil
@ -58,7 +60,7 @@ func (h *windowsAnsiEventHandler) clearRange(attributes uint16, fromCoord COORD,
return nil return nil
} }
func (h *windowsAnsiEventHandler) clearRect(attributes uint16, fromCoord COORD, toCoord COORD) error { func (h *WindowsAnsiEventHandler) clearRect(attributes WORD, fromCoord COORD, toCoord COORD) error {
region := SMALL_RECT{Top: fromCoord.Y, Left: fromCoord.X, Bottom: toCoord.Y, Right: toCoord.X} region := SMALL_RECT{Top: fromCoord.Y, Left: fromCoord.X, Bottom: toCoord.Y, Right: toCoord.X}
width := toCoord.X - fromCoord.X + 1 width := toCoord.X - fromCoord.X + 1
height := toCoord.Y - fromCoord.Y + 1 height := toCoord.Y - fromCoord.Y + 1
@ -70,7 +72,7 @@ func (h *windowsAnsiEventHandler) clearRect(attributes uint16, fromCoord COORD,
buffer := make([]CHAR_INFO, size) buffer := make([]CHAR_INFO, size)
char := CHAR_INFO{ansiterm.FILL_CHARACTER, attributes} char := CHAR_INFO{WCHAR(FILL_CHARACTER), attributes}
for i := 0; i < int(size); i++ { for i := 0; i < int(size); i++ {
buffer[i] = char buffer[i] = char
} }

View File

@ -3,9 +3,9 @@
package winterm package winterm
// effectiveSr gets the current effective scroll region in buffer coordinates // effectiveSr gets the current effective scroll region in buffer coordinates
func (h *windowsAnsiEventHandler) effectiveSr(window SMALL_RECT) scrollRegion { func (h *WindowsAnsiEventHandler) effectiveSr(window SMALL_RECT) scrollRegion {
top := addInRange(window.Top, h.sr.top, window.Top, window.Bottom) top := AddInRange(window.Top, h.sr.top, window.Top, window.Bottom)
bottom := addInRange(window.Top, h.sr.bottom, window.Top, window.Bottom) bottom := AddInRange(window.Top, h.sr.bottom, window.Top, window.Bottom)
if top >= bottom { if top >= bottom {
top = window.Top top = window.Top
bottom = window.Bottom bottom = window.Bottom
@ -13,7 +13,7 @@ func (h *windowsAnsiEventHandler) effectiveSr(window SMALL_RECT) scrollRegion {
return scrollRegion{top: top, bottom: bottom} return scrollRegion{top: top, bottom: bottom}
} }
func (h *windowsAnsiEventHandler) scrollUp(param int) error { func (h *WindowsAnsiEventHandler) scrollUp(param int) error {
info, err := GetConsoleScreenBufferInfo(h.fd) info, err := GetConsoleScreenBufferInfo(h.fd)
if err != nil { if err != nil {
return err return err
@ -23,11 +23,11 @@ func (h *windowsAnsiEventHandler) scrollUp(param int) error {
return h.scroll(param, sr, info) return h.scroll(param, sr, info)
} }
func (h *windowsAnsiEventHandler) scrollDown(param int) error { func (h *WindowsAnsiEventHandler) scrollDown(param int) error {
return h.scrollUp(-param) return h.scrollUp(-param)
} }
func (h *windowsAnsiEventHandler) deleteLines(param int) error { func (h *WindowsAnsiEventHandler) deleteLines(param int) error {
info, err := GetConsoleScreenBufferInfo(h.fd) info, err := GetConsoleScreenBufferInfo(h.fd)
if err != nil { if err != nil {
return err return err
@ -44,12 +44,12 @@ func (h *windowsAnsiEventHandler) deleteLines(param int) error {
} }
} }
func (h *windowsAnsiEventHandler) insertLines(param int) error { func (h *WindowsAnsiEventHandler) insertLines(param int) error {
return h.deleteLines(-param) return h.deleteLines(-param)
} }
// scroll scrolls the provided scroll region by param lines. The scroll region is in buffer coordinates. // scroll scrolls the provided scroll region by param lines. The scroll region is in buffer coordinates.
func (h *windowsAnsiEventHandler) scroll(param int, sr scrollRegion, info *CONSOLE_SCREEN_BUFFER_INFO) error { func (h *WindowsAnsiEventHandler) scroll(param int, sr scrollRegion, info *CONSOLE_SCREEN_BUFFER_INFO) error {
logger.Infof("scroll: scrollTop: %d, scrollBottom: %d", sr.top, sr.bottom) logger.Infof("scroll: scrollTop: %d, scrollBottom: %d", sr.top, sr.bottom)
logger.Infof("scroll: windowTop: %d, windowBottom: %d", info.Window.Top, info.Window.Bottom) logger.Infof("scroll: windowTop: %d, windowBottom: %d", info.Window.Top, info.Window.Bottom)
@ -64,7 +64,7 @@ func (h *windowsAnsiEventHandler) scroll(param int, sr scrollRegion, info *CONSO
// Origin to which area should be copied // Origin to which area should be copied
destOrigin := COORD{ destOrigin := COORD{
X: 0, X: 0,
Y: sr.top - int16(param), Y: sr.top - SHORT(param),
} }
char := CHAR_INFO{ char := CHAR_INFO{
@ -78,7 +78,7 @@ func (h *windowsAnsiEventHandler) scroll(param int, sr scrollRegion, info *CONSO
return nil return nil
} }
func (h *windowsAnsiEventHandler) deleteCharacters(param int) error { func (h *WindowsAnsiEventHandler) deleteCharacters(param int) error {
info, err := GetConsoleScreenBufferInfo(h.fd) info, err := GetConsoleScreenBufferInfo(h.fd)
if err != nil { if err != nil {
return err return err
@ -86,12 +86,12 @@ func (h *windowsAnsiEventHandler) deleteCharacters(param int) error {
return h.scrollLine(param, info.CursorPosition, info) return h.scrollLine(param, info.CursorPosition, info)
} }
func (h *windowsAnsiEventHandler) insertCharacters(param int) error { func (h *WindowsAnsiEventHandler) insertCharacters(param int) error {
return h.deleteCharacters(-param) return h.deleteCharacters(-param)
} }
// scrollLine scrolls a line horizontally starting at the provided position by a number of columns. // scrollLine scrolls a line horizontally starting at the provided position by a number of columns.
func (h *windowsAnsiEventHandler) scrollLine(columns int, position COORD, info *CONSOLE_SCREEN_BUFFER_INFO) error { func (h *WindowsAnsiEventHandler) scrollLine(columns int, position COORD, info *CONSOLE_SCREEN_BUFFER_INFO) error {
// Copy from and clip to the scroll region (full buffer width) // Copy from and clip to the scroll region (full buffer width)
scrollRect := SMALL_RECT{ scrollRect := SMALL_RECT{
Top: position.Y, Top: position.Y,
@ -102,7 +102,7 @@ func (h *windowsAnsiEventHandler) scrollLine(columns int, position COORD, info *
// Origin to which area should be copied // Origin to which area should be copied
destOrigin := COORD{ destOrigin := COORD{
X: position.X - int16(columns), X: position.X - SHORT(columns),
Y: position.Y, Y: position.Y,
} }

View File

@ -4,6 +4,6 @@ package winterm
// AddInRange increments a value by the passed quantity while ensuring the values // AddInRange increments a value by the passed quantity while ensuring the values
// always remain within the supplied min / max range. // always remain within the supplied min / max range.
func addInRange(n int16, increment int16, min int16, max int16) int16 { func AddInRange(n SHORT, increment SHORT, min SHORT, max SHORT) SHORT {
return ensureInRange(n+increment, min, max) return ensureInRange(n+increment, min, max)
} }

View File

@ -8,19 +8,19 @@ import (
"os" "os"
"strconv" "strconv"
"github.com/Azure/go-ansiterm" . "github.com/Azure/go-ansiterm"
"github.com/Sirupsen/logrus" "github.com/Sirupsen/logrus"
) )
var logger *logrus.Logger var logger *logrus.Logger
type windowsAnsiEventHandler struct { type WindowsAnsiEventHandler struct {
fd uintptr fd uintptr
file *os.File file *os.File
infoReset *CONSOLE_SCREEN_BUFFER_INFO infoReset *CONSOLE_SCREEN_BUFFER_INFO
sr scrollRegion sr scrollRegion
buffer bytes.Buffer buffer bytes.Buffer
attributes uint16 attributes WORD
inverted bool inverted bool
wrapNext bool wrapNext bool
drewMarginByte bool drewMarginByte bool
@ -30,10 +30,10 @@ type windowsAnsiEventHandler struct {
curPos COORD curPos COORD
} }
func CreateWinEventHandler(fd uintptr, file *os.File) ansiterm.AnsiEventHandler { func CreateWinEventHandler(fd uintptr, file *os.File) AnsiEventHandler {
logFile := ioutil.Discard logFile := ioutil.Discard
if isDebugEnv := os.Getenv(ansiterm.LogEnv); isDebugEnv == "1" { if isDebugEnv := os.Getenv(LogEnv); isDebugEnv == "1" {
logFile, _ = os.Create("winEventHandler.log") logFile, _ = os.Create("winEventHandler.log")
} }
@ -48,7 +48,7 @@ func CreateWinEventHandler(fd uintptr, file *os.File) ansiterm.AnsiEventHandler
return nil return nil
} }
return &windowsAnsiEventHandler{ return &WindowsAnsiEventHandler{
fd: fd, fd: fd,
file: file, file: file,
infoReset: infoReset, infoReset: infoReset,
@ -57,8 +57,8 @@ func CreateWinEventHandler(fd uintptr, file *os.File) ansiterm.AnsiEventHandler
} }
type scrollRegion struct { type scrollRegion struct {
top int16 top SHORT
bottom int16 bottom SHORT
} }
// simulateLF simulates a LF or CR+LF by scrolling if necessary to handle the // simulateLF simulates a LF or CR+LF by scrolling if necessary to handle the
@ -68,7 +68,7 @@ type scrollRegion struct {
// //
// In the false case, the caller should ensure that a carriage return // In the false case, the caller should ensure that a carriage return
// and line feed are inserted or that the text is otherwise wrapped. // and line feed are inserted or that the text is otherwise wrapped.
func (h *windowsAnsiEventHandler) simulateLF(includeCR bool) (bool, error) { func (h *WindowsAnsiEventHandler) simulateLF(includeCR bool) (bool, error) {
if h.wrapNext { if h.wrapNext {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return false, err return false, err
@ -89,25 +89,24 @@ func (h *windowsAnsiEventHandler) simulateLF(includeCR bool) (bool, error) {
h.updatePos(pos) h.updatePos(pos)
} }
return false, nil return false, nil
} } else {
// A custom scroll region is active. Scroll the window manually to simulate
// A custom scroll region is active. Scroll the window manually to simulate // the LF.
// the LF. if err := h.Flush(); err != nil {
if err := h.Flush(); err != nil {
return false, err
}
logger.Info("Simulating LF inside scroll region")
if err := h.scrollUp(1); err != nil {
return false, err
}
if includeCR {
pos.X = 0
if err := SetConsoleCursorPosition(h.fd, pos); err != nil {
return false, err return false, err
} }
logger.Info("Simulating LF inside scroll region")
if err := h.scrollUp(1); err != nil {
return false, err
}
if includeCR {
pos.X = 0
if err := SetConsoleCursorPosition(h.fd, pos); err != nil {
return false, err
}
}
return true, nil
} }
return true, nil
} else if pos.Y < info.Window.Bottom { } else if pos.Y < info.Window.Bottom {
// Let Windows handle the LF. // Let Windows handle the LF.
pos.Y++ pos.Y++
@ -134,7 +133,7 @@ func (h *windowsAnsiEventHandler) simulateLF(includeCR bool) (bool, error) {
} }
// executeLF executes a LF without a CR. // executeLF executes a LF without a CR.
func (h *windowsAnsiEventHandler) executeLF() error { func (h *WindowsAnsiEventHandler) executeLF() error {
handled, err := h.simulateLF(false) handled, err := h.simulateLF(false)
if err != nil { if err != nil {
return err return err
@ -146,7 +145,7 @@ func (h *windowsAnsiEventHandler) executeLF() error {
if err != nil { if err != nil {
return err return err
} }
h.buffer.WriteByte(ansiterm.ANSI_LINE_FEED) h.buffer.WriteByte(ANSI_LINE_FEED)
if pos.X != 0 { if pos.X != 0 {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
@ -160,7 +159,7 @@ func (h *windowsAnsiEventHandler) executeLF() error {
return nil return nil
} }
func (h *windowsAnsiEventHandler) Print(b byte) error { func (h *WindowsAnsiEventHandler) Print(b byte) error {
if h.wrapNext { if h.wrapNext {
h.buffer.WriteByte(h.marginByte) h.buffer.WriteByte(h.marginByte)
h.clearWrap() h.clearWrap()
@ -183,9 +182,9 @@ func (h *windowsAnsiEventHandler) Print(b byte) error {
return nil return nil
} }
func (h *windowsAnsiEventHandler) Execute(b byte) error { func (h *WindowsAnsiEventHandler) Execute(b byte) error {
switch b { switch b {
case ansiterm.ANSI_TAB: case ANSI_TAB:
logger.Info("Execute(TAB)") logger.Info("Execute(TAB)")
// Move to the next tab stop, but preserve auto-wrap if already set. // Move to the next tab stop, but preserve auto-wrap if already set.
if !h.wrapNext { if !h.wrapNext {
@ -206,11 +205,11 @@ func (h *windowsAnsiEventHandler) Execute(b byte) error {
} }
return nil return nil
case ansiterm.ANSI_BEL: case ANSI_BEL:
h.buffer.WriteByte(ansiterm.ANSI_BEL) h.buffer.WriteByte(ANSI_BEL)
return nil return nil
case ansiterm.ANSI_BACKSPACE: case ANSI_BACKSPACE:
if h.wrapNext { if h.wrapNext {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
@ -224,15 +223,15 @@ func (h *windowsAnsiEventHandler) Execute(b byte) error {
if pos.X > 0 { if pos.X > 0 {
pos.X-- pos.X--
h.updatePos(pos) h.updatePos(pos)
h.buffer.WriteByte(ansiterm.ANSI_BACKSPACE) h.buffer.WriteByte(ANSI_BACKSPACE)
} }
return nil return nil
case ansiterm.ANSI_VERTICAL_TAB, ansiterm.ANSI_FORM_FEED: case ANSI_VERTICAL_TAB, ANSI_FORM_FEED:
// Treat as true LF. // Treat as true LF.
return h.executeLF() return h.executeLF()
case ansiterm.ANSI_LINE_FEED: case ANSI_LINE_FEED:
// Simulate a CR and LF for now since there is no way in go-ansiterm // Simulate a CR and LF for now since there is no way in go-ansiterm
// to tell if the LF should include CR (and more things break when it's // to tell if the LF should include CR (and more things break when it's
// missing than when it's incorrectly added). // missing than when it's incorrectly added).
@ -240,9 +239,9 @@ func (h *windowsAnsiEventHandler) Execute(b byte) error {
if handled || err != nil { if handled || err != nil {
return err return err
} }
return h.buffer.WriteByte(ansiterm.ANSI_LINE_FEED) return h.buffer.WriteByte(ANSI_LINE_FEED)
case ansiterm.ANSI_CARRIAGE_RETURN: case ANSI_CARRIAGE_RETURN:
if h.wrapNext { if h.wrapNext {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
@ -256,7 +255,7 @@ func (h *windowsAnsiEventHandler) Execute(b byte) error {
if pos.X != 0 { if pos.X != 0 {
pos.X = 0 pos.X = 0
h.updatePos(pos) h.updatePos(pos)
h.buffer.WriteByte(ansiterm.ANSI_CARRIAGE_RETURN) h.buffer.WriteByte(ANSI_CARRIAGE_RETURN)
} }
return nil return nil
@ -265,7 +264,7 @@ func (h *windowsAnsiEventHandler) Execute(b byte) error {
} }
} }
func (h *windowsAnsiEventHandler) CUU(param int) error { func (h *WindowsAnsiEventHandler) CUU(param int) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
@ -274,7 +273,7 @@ func (h *windowsAnsiEventHandler) CUU(param int) error {
return h.moveCursorVertical(-param) return h.moveCursorVertical(-param)
} }
func (h *windowsAnsiEventHandler) CUD(param int) error { func (h *WindowsAnsiEventHandler) CUD(param int) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
@ -283,7 +282,7 @@ func (h *windowsAnsiEventHandler) CUD(param int) error {
return h.moveCursorVertical(param) return h.moveCursorVertical(param)
} }
func (h *windowsAnsiEventHandler) CUF(param int) error { func (h *WindowsAnsiEventHandler) CUF(param int) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
@ -292,7 +291,7 @@ func (h *windowsAnsiEventHandler) CUF(param int) error {
return h.moveCursorHorizontal(param) return h.moveCursorHorizontal(param)
} }
func (h *windowsAnsiEventHandler) CUB(param int) error { func (h *WindowsAnsiEventHandler) CUB(param int) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
@ -301,7 +300,7 @@ func (h *windowsAnsiEventHandler) CUB(param int) error {
return h.moveCursorHorizontal(-param) return h.moveCursorHorizontal(-param)
} }
func (h *windowsAnsiEventHandler) CNL(param int) error { func (h *WindowsAnsiEventHandler) CNL(param int) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
@ -310,7 +309,7 @@ func (h *windowsAnsiEventHandler) CNL(param int) error {
return h.moveCursorLine(param) return h.moveCursorLine(param)
} }
func (h *windowsAnsiEventHandler) CPL(param int) error { func (h *WindowsAnsiEventHandler) CPL(param int) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
@ -319,7 +318,7 @@ func (h *windowsAnsiEventHandler) CPL(param int) error {
return h.moveCursorLine(-param) return h.moveCursorLine(-param)
} }
func (h *windowsAnsiEventHandler) CHA(param int) error { func (h *WindowsAnsiEventHandler) CHA(param int) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
@ -328,7 +327,7 @@ func (h *windowsAnsiEventHandler) CHA(param int) error {
return h.moveCursorColumn(param) return h.moveCursorColumn(param)
} }
func (h *windowsAnsiEventHandler) VPA(param int) error { func (h *WindowsAnsiEventHandler) VPA(param int) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
@ -340,11 +339,11 @@ func (h *windowsAnsiEventHandler) VPA(param int) error {
} }
window := h.getCursorWindow(info) window := h.getCursorWindow(info)
position := info.CursorPosition position := info.CursorPosition
position.Y = window.Top + int16(param) - 1 position.Y = window.Top + SHORT(param) - 1
return h.setCursorPosition(position, window) return h.setCursorPosition(position, window)
} }
func (h *windowsAnsiEventHandler) CUP(row int, col int) error { func (h *WindowsAnsiEventHandler) CUP(row int, col int) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
@ -356,11 +355,11 @@ func (h *windowsAnsiEventHandler) CUP(row int, col int) error {
} }
window := h.getCursorWindow(info) window := h.getCursorWindow(info)
position := COORD{window.Left + int16(col) - 1, window.Top + int16(row) - 1} position := COORD{window.Left + SHORT(col) - 1, window.Top + SHORT(row) - 1}
return h.setCursorPosition(position, window) return h.setCursorPosition(position, window)
} }
func (h *windowsAnsiEventHandler) HVP(row int, col int) error { func (h *WindowsAnsiEventHandler) HVP(row int, col int) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
@ -369,7 +368,7 @@ func (h *windowsAnsiEventHandler) HVP(row int, col int) error {
return h.CUP(row, col) return h.CUP(row, col)
} }
func (h *windowsAnsiEventHandler) DECTCEM(visible bool) error { func (h *WindowsAnsiEventHandler) DECTCEM(visible bool) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
@ -378,7 +377,7 @@ func (h *windowsAnsiEventHandler) DECTCEM(visible bool) error {
return nil return nil
} }
func (h *windowsAnsiEventHandler) DECOM(enable bool) error { func (h *WindowsAnsiEventHandler) DECOM(enable bool) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
@ -388,7 +387,7 @@ func (h *windowsAnsiEventHandler) DECOM(enable bool) error {
return h.CUP(1, 1) return h.CUP(1, 1)
} }
func (h *windowsAnsiEventHandler) DECCOLM(use132 bool) error { func (h *WindowsAnsiEventHandler) DECCOLM(use132 bool) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
@ -401,7 +400,7 @@ func (h *windowsAnsiEventHandler) DECCOLM(use132 bool) error {
if err != nil { if err != nil {
return err return err
} }
targetWidth := int16(80) targetWidth := SHORT(80)
if use132 { if use132 {
targetWidth = 132 targetWidth = 132
} }
@ -427,7 +426,7 @@ func (h *windowsAnsiEventHandler) DECCOLM(use132 bool) error {
return SetConsoleCursorPosition(h.fd, COORD{0, 0}) return SetConsoleCursorPosition(h.fd, COORD{0, 0})
} }
func (h *windowsAnsiEventHandler) ED(param int) error { func (h *WindowsAnsiEventHandler) ED(param int) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
@ -486,7 +485,7 @@ func (h *windowsAnsiEventHandler) ED(param int) error {
return nil return nil
} }
func (h *windowsAnsiEventHandler) EL(param int) error { func (h *WindowsAnsiEventHandler) EL(param int) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
@ -527,7 +526,7 @@ func (h *windowsAnsiEventHandler) EL(param int) error {
return nil return nil
} }
func (h *windowsAnsiEventHandler) IL(param int) error { func (h *WindowsAnsiEventHandler) IL(param int) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
@ -536,7 +535,7 @@ func (h *windowsAnsiEventHandler) IL(param int) error {
return h.insertLines(param) return h.insertLines(param)
} }
func (h *windowsAnsiEventHandler) DL(param int) error { func (h *WindowsAnsiEventHandler) DL(param int) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
@ -545,7 +544,7 @@ func (h *windowsAnsiEventHandler) DL(param int) error {
return h.deleteLines(param) return h.deleteLines(param)
} }
func (h *windowsAnsiEventHandler) ICH(param int) error { func (h *WindowsAnsiEventHandler) ICH(param int) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
@ -554,7 +553,7 @@ func (h *windowsAnsiEventHandler) ICH(param int) error {
return h.insertCharacters(param) return h.insertCharacters(param)
} }
func (h *windowsAnsiEventHandler) DCH(param int) error { func (h *WindowsAnsiEventHandler) DCH(param int) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
@ -563,7 +562,7 @@ func (h *windowsAnsiEventHandler) DCH(param int) error {
return h.deleteCharacters(param) return h.deleteCharacters(param)
} }
func (h *windowsAnsiEventHandler) SGR(params []int) error { func (h *WindowsAnsiEventHandler) SGR(params []int) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
@ -580,13 +579,13 @@ func (h *windowsAnsiEventHandler) SGR(params []int) error {
} else { } else {
for _, attr := range params { for _, attr := range params {
if attr == ansiterm.ANSI_SGR_RESET { if attr == ANSI_SGR_RESET {
h.attributes = h.infoReset.Attributes h.attributes = h.infoReset.Attributes
h.inverted = false h.inverted = false
continue continue
} }
h.attributes, h.inverted = collectAnsiIntoWindowsAttributes(h.attributes, h.inverted, h.infoReset.Attributes, int16(attr)) h.attributes, h.inverted = collectAnsiIntoWindowsAttributes(h.attributes, h.inverted, h.infoReset.Attributes, SHORT(attr))
} }
} }
@ -602,7 +601,7 @@ func (h *windowsAnsiEventHandler) SGR(params []int) error {
return nil return nil
} }
func (h *windowsAnsiEventHandler) SU(param int) error { func (h *WindowsAnsiEventHandler) SU(param int) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
@ -611,7 +610,7 @@ func (h *windowsAnsiEventHandler) SU(param int) error {
return h.scrollUp(param) return h.scrollUp(param)
} }
func (h *windowsAnsiEventHandler) SD(param int) error { func (h *WindowsAnsiEventHandler) SD(param int) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
@ -620,29 +619,29 @@ func (h *windowsAnsiEventHandler) SD(param int) error {
return h.scrollDown(param) return h.scrollDown(param)
} }
func (h *windowsAnsiEventHandler) DA(params []string) error { func (h *WindowsAnsiEventHandler) DA(params []string) error {
logger.Infof("DA: [%v]", params) logger.Infof("DA: [%v]", params)
// DA cannot be implemented because it must send data on the VT100 input stream, // DA cannot be implemented because it must send data on the VT100 input stream,
// which is not available to go-ansiterm. // which is not available to go-ansiterm.
return nil return nil
} }
func (h *windowsAnsiEventHandler) DECSTBM(top int, bottom int) error { func (h *WindowsAnsiEventHandler) DECSTBM(top int, bottom int) error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
logger.Infof("DECSTBM: [%d, %d]", top, bottom) logger.Infof("DECSTBM: [%d, %d]", top, bottom)
// Windows is 0 indexed, Linux is 1 indexed // Windows is 0 indexed, Linux is 1 indexed
h.sr.top = int16(top - 1) h.sr.top = SHORT(top - 1)
h.sr.bottom = int16(bottom - 1) h.sr.bottom = SHORT(bottom - 1)
// This command also moves the cursor to the origin. // This command also moves the cursor to the origin.
h.clearWrap() h.clearWrap()
return h.CUP(1, 1) return h.CUP(1, 1)
} }
func (h *windowsAnsiEventHandler) RI() error { func (h *WindowsAnsiEventHandler) RI() error {
if err := h.Flush(); err != nil { if err := h.Flush(); err != nil {
return err return err
} }
@ -657,17 +656,17 @@ func (h *windowsAnsiEventHandler) RI() error {
sr := h.effectiveSr(info.Window) sr := h.effectiveSr(info.Window)
if info.CursorPosition.Y == sr.top { if info.CursorPosition.Y == sr.top {
return h.scrollDown(1) return h.scrollDown(1)
} else {
return h.moveCursorVertical(-1)
} }
return h.moveCursorVertical(-1)
} }
func (h *windowsAnsiEventHandler) IND() error { func (h *WindowsAnsiEventHandler) IND() error {
logger.Info("IND: []") logger.Info("IND: []")
return h.executeLF() return h.executeLF()
} }
func (h *windowsAnsiEventHandler) Flush() error { func (h *WindowsAnsiEventHandler) Flush() error {
h.curInfo = nil h.curInfo = nil
if h.buffer.Len() > 0 { if h.buffer.Len() > 0 {
logger.Infof("Flush: [%s]", h.buffer.Bytes()) logger.Infof("Flush: [%s]", h.buffer.Bytes())
@ -684,7 +683,7 @@ func (h *windowsAnsiEventHandler) Flush() error {
return err return err
} }
charInfo := []CHAR_INFO{{UnicodeChar: uint16(h.marginByte), Attributes: info.Attributes}} charInfo := []CHAR_INFO{{UnicodeChar: WCHAR(h.marginByte), Attributes: info.Attributes}}
size := COORD{1, 1} size := COORD{1, 1}
position := COORD{0, 0} position := COORD{0, 0}
region := SMALL_RECT{Left: info.CursorPosition.X, Top: info.CursorPosition.Y, Right: info.CursorPosition.X, Bottom: info.CursorPosition.Y} region := SMALL_RECT{Left: info.CursorPosition.X, Top: info.CursorPosition.Y, Right: info.CursorPosition.X, Bottom: info.CursorPosition.Y}
@ -698,7 +697,7 @@ func (h *windowsAnsiEventHandler) Flush() error {
// cacheConsoleInfo ensures that the current console screen information has been queried // cacheConsoleInfo ensures that the current console screen information has been queried
// since the last call to Flush(). It must be called before accessing h.curInfo or h.curPos. // since the last call to Flush(). It must be called before accessing h.curInfo or h.curPos.
func (h *windowsAnsiEventHandler) getCurrentInfo() (COORD, *CONSOLE_SCREEN_BUFFER_INFO, error) { func (h *WindowsAnsiEventHandler) getCurrentInfo() (COORD, *CONSOLE_SCREEN_BUFFER_INFO, error) {
if h.curInfo == nil { if h.curInfo == nil {
info, err := GetConsoleScreenBufferInfo(h.fd) info, err := GetConsoleScreenBufferInfo(h.fd)
if err != nil { if err != nil {
@ -710,7 +709,7 @@ func (h *windowsAnsiEventHandler) getCurrentInfo() (COORD, *CONSOLE_SCREEN_BUFFE
return h.curPos, h.curInfo, nil return h.curPos, h.curInfo, nil
} }
func (h *windowsAnsiEventHandler) updatePos(pos COORD) { func (h *WindowsAnsiEventHandler) updatePos(pos COORD) {
if h.curInfo == nil { if h.curInfo == nil {
panic("failed to call getCurrentInfo before calling updatePos") panic("failed to call getCurrentInfo before calling updatePos")
} }
@ -720,7 +719,7 @@ func (h *windowsAnsiEventHandler) updatePos(pos COORD) {
// clearWrap clears the state where the cursor is in the margin // clearWrap clears the state where the cursor is in the margin
// waiting for the next character before wrapping the line. This must // waiting for the next character before wrapping the line. This must
// be done before most operations that act on the cursor. // be done before most operations that act on the cursor.
func (h *windowsAnsiEventHandler) clearWrap() { func (h *WindowsAnsiEventHandler) clearWrap() {
h.wrapNext = false h.wrapNext = false
h.drewMarginByte = false h.drewMarginByte = false
} }