diff --git a/escape.go b/escape.go index 7c500d9..cb557f0 100644 --- a/escape.go +++ b/escape.go @@ -39,6 +39,7 @@ func (self noInstruction) isInstruction() {} const ( stateNone escapeState = iota stateEscape + stateCharacterSetDesignation stateCSI stateParams stateOSC @@ -144,9 +145,19 @@ func (ei *escapeInterpreter) parseOne(ch []byte) (isEscape bool, err error) { case characterEquals(ch, ']'): ei.state = stateOSC return true, nil + case characterEquals(ch, '('), + characterEquals(ch, ')'), + characterEquals(ch, '*'), + characterEquals(ch, '+'): + ei.state = stateCharacterSetDesignation + return true, nil default: return false, errNotCSI } + case stateCharacterSetDesignation: + // Not supported, so just skip it + ei.state = stateNone + return true, nil case stateCSI: switch { case len(ch) == 1 && ch[0] >= '0' && ch[0] <= '9': diff --git a/escape_test.go b/escape_test.go index b217cc0..0073463 100644 --- a/escape_test.go +++ b/escape_test.go @@ -28,6 +28,26 @@ func TestParseOne(t *testing.T) { parseEscRunes(t, ei, "\x1b[1K") _, ok = ei.instruction.(noInstruction) assert.Equal(t, true, ok) + + ei = newEscapeInterpreter(OutputNormal) + parseEscRunes(t, ei, "\x1b(B") + _, ok = ei.instruction.(noInstruction) + assert.Equal(t, true, ok) + + ei = newEscapeInterpreter(OutputNormal) + parseEscRunes(t, ei, "\x1b)0") + _, ok = ei.instruction.(noInstruction) + assert.Equal(t, true, ok) + + ei = newEscapeInterpreter(OutputNormal) + parseEscRunes(t, ei, "\x1b*A") + _, ok = ei.instruction.(noInstruction) + assert.Equal(t, true, ok) + + ei = newEscapeInterpreter(OutputNormal) + parseEscRunes(t, ei, "\x1b+K") + _, ok = ei.instruction.(noInstruction) + assert.Equal(t, true, ok) } func TestParseOneColours(t *testing.T) { diff --git a/view.go b/view.go index ead9aa9..9ba8b7e 100644 --- a/view.go +++ b/view.go @@ -167,9 +167,6 @@ type View struct { // If HasLoader is true, the message will be appended with a spinning loader animation HasLoader bool - // IgnoreCarriageReturns tells us whether to ignore '\r' characters - IgnoreCarriageReturns bool - // ParentView is the view which catches events bubbled up from the given view if there's no matching handler ParentView *View @@ -465,6 +462,10 @@ func characterEquals(chr []byte, b byte) bool { return len(chr) == 1 && chr[0] == b } +func isCRLF(chr []byte) bool { + return len(chr) == 2 && chr[0] == '\r' && chr[1] == '\n' +} + // String returns a string from a given cell slice. func (l lineType) String() string { var str strings.Builder @@ -840,7 +841,7 @@ func (v *View) write(p []byte) { chr, remaining, width, state = uniseg.FirstGraphemeCluster(remaining, state) switch { - case characterEquals(chr, '\n'): + case characterEquals(chr, '\n') || isCRLF(chr): finishLine() advanceToNextLine() case characterEquals(chr, '\r'):