Skip to content

Commit

Permalink
Merge pull request #659 from noborus/csiParamStart
Browse files Browse the repository at this point in the history
Change the range to csiParamStart/csiParamEnd
  • Loading branch information
noborus authored Nov 19, 2024
2 parents 8a41959 + adf7cb5 commit 3c7056e
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 38 deletions.
2 changes: 1 addition & 1 deletion oviewer/content_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,7 @@ func Test_StrToContentsStyle2(t *testing.T) {
}
}

func Test_parseStringUnStyle(t *testing.T) {
func Test_StrToContentUnStyle(t *testing.T) {
t.Parallel()
type args struct {
line string
Expand Down
97 changes: 61 additions & 36 deletions oviewer/convert_es.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,13 @@ const (
oscURL
)

// FinalByte is a character outside the escape sequence.
// If FinalByte is included, the interpretation of the escape sequence is terminated
// and it is considered an error as it did not terminate correctly.
const FinalByte = 0x40
// csiParamStart and csiParamEnd define the range of parameters in the CSI.
// Parameters outside this range will result in an error and will not be considered as CSI.
// Errors within this range will not affect the CSI.
const (
csiParamStart = 0x20
csiParamEnd = 0x3F
)

const (
// Colors256 is the index of the 256 color. 8-bit colors. 0-255.
Expand Down Expand Up @@ -71,6 +74,12 @@ type sgrParams struct {
// convert parses an escape sequence and changes state.
// Returns true if it is an escape sequence and a non-printing character.
func (es *escapeSequence) convert(st *parseState) bool {
return es.paraseEscapeSequence(st)
}

// parseEscapeSequence parses the escape sequence.
// convert parses an escape sequence and changes state.
func (es *escapeSequence) paraseEscapeSequence(st *parseState) bool {
mainc := st.mainc
switch es.state {
case ansiEscape:
Expand Down Expand Up @@ -101,24 +110,7 @@ func (es *escapeSequence) convert(st *parseState) bool {
}
return true
case ansiControlSequence:
switch {
case mainc == 'm': // SGR(Set Graphics Rendition).
st.style = sgrStyle(st.style, es.parameter.String())
case mainc == 'K':
// CSI 0 K or CSI K maintains the style after the newline
// (can change the background color of the line).
params := es.parameter.String()
if params == "" || params == "0" {
st.eolStyle = st.style
}
case mainc >= 'A' && mainc <= 'T':
// Ignore.
case mainc < FinalByte:
es.parameter.WriteRune(mainc)
return true
}
// End of escape sequence.
es.state = ansiText
es.parseCSI(st, mainc)
return true
case otherSequence:
es.state = ansiEscape
Expand Down Expand Up @@ -187,6 +179,29 @@ func (es *escapeSequence) convert(st *parseState) bool {
return false
}

// parseCSI parses the CSI(Control Sequence Introducer) escape sequence.
func (es *escapeSequence) parseCSI(st *parseState, mainc rune) {
switch {
case mainc == 'm': // SGR(Set Graphics Rendition).
st.style = sgrStyle(st.style, es.parameter.String())
case mainc == 'K': // Erase in Line.
// CSI 0 K or CSI K maintains the style after the newline
params := es.parameter.String()
if params == "" || params == "0" {
// can change the background color of the line.
_, bg, _ := st.style.Decompose()
st.eolStyle = st.eolStyle.Background(bg)
}
case mainc >= 'A' && mainc <= 'T': // Cursor Movement.
// Ignore.
case mainc >= csiParamStart && mainc <= csiParamEnd: // Parameters.
es.parameter.WriteRune(mainc)
return
}
// End of escape sequence.
es.state = ansiText
}

// sgrStyle returns tcell.Style from the SGR control sequence.
func sgrStyle(style tcell.Style, paramStr string) tcell.Style {
switch paramStr {
Expand Down Expand Up @@ -227,21 +242,12 @@ func parseSGR(paramStr string) OVStyle {
s.Italic = true
s.UnItalic = false
case 4: // Underline On
s.UnUnderline = false
s.Underline = true
if len(sgr.params) == 0 {
continue
}
// The parameter is specified(4:).
n, err := sgrNumber(sgr.params[0])
if err != nil {
return OVStyle{}
}
// Support only Underline Off (4:0).
if n == 0 {
s.Underline = false
s.UnUnderline = true
if len(sgr.params) > 0 && sgr.params[0] != "" {
s = underLineStyle(s, sgr.params[0])
break
}
s.Underline = true
s.UnUnderline = false
case 5: // Blink On
s.Blink = true
s.UnBlink = false
Expand Down Expand Up @@ -380,6 +386,25 @@ func containsNonDigit(str string) bool {
return false
}

// underLineStyle sets the underline style.
func underLineStyle(s OVStyle, param string) OVStyle {
n, err := sgrNumber(param)
if err != nil {
return s
}

// Support only Underline Off (4:0).
if n == 0 {
s.Underline = false
s.UnUnderline = true
return s
}
// Other than that, it is treated as Underline On.
s.Underline = true
s.UnUnderline = false
return s
}

// parseSGRColor parses 256 color or RGB color.
// Returns the color name and increase in the index (the colon does not increase).
func parseSGRColor(sgr sgrParams) (string, int, error) {
Expand Down
47 changes: 46 additions & 1 deletion oviewer/convert_es_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,32 @@ func Test_escapeSequence_convert(t *testing.T) {
want: true,
wantState: ansiText,
},
{
name: "test-ControlSequenceEnd",
fields: fields{
state: ansiControlSequence,
},
args: args{
st: &parseState{
mainc: '?',
},
},
want: true,
wantState: ansiControlSequence,
},
{
name: "test-ControlSequenceOver",
fields: fields{
state: ansiControlSequence,
},
args: args{
st: &parseState{
mainc: '@',
},
},
want: true,
wantState: ansiText,
},
{
name: "test-SysSequence",
fields: fields{
Expand Down Expand Up @@ -321,7 +347,7 @@ func Test_parseSGR(t *testing.T) {
{
name: "test-forground2",
args: args{
params: "38;5;2",
params: "038;05;02",
},
want: OVStyle{
Foreground: "green",
Expand All @@ -337,6 +363,25 @@ func Test_parseSGR(t *testing.T) {
},
wantErr: false,
},
{
name: "test-forground216_Underline",
args: args{
params: "38;5;216;4",
},
want: OVStyle{
Foreground: "#FFAF87",
Underline: true,
},
},
{
name: "test-reset_Underline",
args: args{
params: "38;5;216;0;4",
},
want: OVStyle{
Underline: true,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down

0 comments on commit 3c7056e

Please sign in to comment.