Skip to content

Commit

Permalink
refactor(term): ansi: dry & improve perf of ansi.Parser
Browse files Browse the repository at this point in the history
Use less allocs and pass dispatcher as an arg
  • Loading branch information
aymanbagabas committed Mar 28, 2024
1 parent edcc8e9 commit ab9afc2
Show file tree
Hide file tree
Showing 27 changed files with 1,113 additions and 5,373 deletions.
24 changes: 14 additions & 10 deletions exp/term/ansi/csi.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,6 @@ type CsiSequence struct {
// most significant bit indicating whether there are more sub-parameters.
Params []int

// ParamsLen contains the number of parameters in the sequence.
// This is the number of all parameters, including sub-parameters.
// Use Len to get the number of parameters segments excluding
// sub-parameters.
ParamsLen int

// Cmd contains the raw command of the sequence.
// The command is a 32-bit integer containing the CSI command byte in the
// lower 8 bits, the private marker in the next 8 bits, and the intermediate
Expand All @@ -44,6 +38,8 @@ type CsiSequence struct {
Cmd int
}

var _ Sequence = CsiSequence{}

// Marker returns the marker byte of the CSI sequence.
// This is always gonna be one of the following '<' '=' '>' '?' and in the
// range of 0x3C-0x3F.
Expand Down Expand Up @@ -80,21 +76,29 @@ func (s CsiSequence) HasMore(i int) bool {
// Subparams returns the sub-parameters of the given parameter.
// It returns nil if the parameter does not exist.
func (s CsiSequence) Subparams(i int) []int {
return parser.Subparams(s.Params, s.ParamsLen, i)
return parser.Subparams(s.Params, i)
}

// Len returns the number of parameters in the sequence.
// This will return the number of parameters in the sequence, excluding any
// sub-parameters.
func (s CsiSequence) Len() int {
return parser.Len(s.Params, s.ParamsLen)
return parser.Len(s.Params)
}

// Range iterates over the parameters of the sequence and calls the given
// function for each parameter.
// The function should return false to stop the iteration.
func (s CsiSequence) Range(fn func(i int, param int, hasMore bool) bool) {
parser.Range(s.Params, s.ParamsLen, fn)
parser.Range(s.Params, fn)
}

// Clone returns a copy of the CSI sequence.
func (s CsiSequence) Clone() Sequence {
return CsiSequence{
Params: append([]int(nil), s.Params...),
Cmd: s.Cmd,
}
}

// String returns a string representation of the sequence.
Expand All @@ -114,7 +118,7 @@ func (s CsiSequence) buffer() *bytes.Buffer {
if param >= 0 {
b.WriteString(strconv.Itoa(param))
}
if i < s.ParamsLen-1 {
if i < len(s.Params)-1 {
if hasMore {
b.WriteByte(':')
} else {
Expand Down
46 changes: 17 additions & 29 deletions exp/term/ansi/csi_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,17 +113,15 @@ func TestCsiSequence_Param(t *testing.T) {
{
name: "param",
s: CsiSequence{
Params: []int{1, 2, 3},
ParamsLen: 3,
Params: []int{1, 2, 3},
},
i: 1,
want: 2,
},
{
name: "missing param",
s: CsiSequence{
Params: []int{1, parser.MissingParam, 3},
ParamsLen: 3,
Params: []int{1, parser.MissingParam, 3},
},
i: 1,
want: -1,
Expand Down Expand Up @@ -154,17 +152,15 @@ func TestCsiSequence_HasMore(t *testing.T) {
{
name: "has more",
s: CsiSequence{
Params: []int{1 | parser.HasMoreFlag, 2, 3},
ParamsLen: 3,
Params: []int{1 | parser.HasMoreFlag, 2, 3},
},
i: 0,
want: true,
},
{
name: "no more",
s: CsiSequence{
Params: []int{1, 2, 3},
ParamsLen: 3,
Params: []int{1, 2, 3},
},
i: 0,
want: false,
Expand Down Expand Up @@ -193,24 +189,21 @@ func TestCsiSequence_Len(t *testing.T) {
{
name: "len",
s: CsiSequence{
Params: []int{1, 2, 3},
ParamsLen: 3,
Params: []int{1, 2, 3},
},
want: 3,
},
{
name: "len with missing param",
s: CsiSequence{
Params: []int{1, parser.MissingParam, 3},
ParamsLen: 3,
Params: []int{1, parser.MissingParam, 3},
},
want: 3,
},
{
name: "len with more flag",
s: CsiSequence{
Params: []int{1 | parser.HasMoreFlag, 2, 3},
ParamsLen: 3,
Params: []int{1 | parser.HasMoreFlag, 2, 3},
},
want: 2,
},
Expand Down Expand Up @@ -238,45 +231,40 @@ func TestCsiSequence_String(t *testing.T) {
{
name: "with data",
s: CsiSequence{
Cmd: 'A',
Params: []int{1, 2, 3},
ParamsLen: 3,
Cmd: 'A',
Params: []int{1, 2, 3},
},
want: "\x1b[1;2;3A",
},
{
name: "with more flag",
s: CsiSequence{
Cmd: 'A',
Params: []int{1 | parser.HasMoreFlag, 2, 3},
ParamsLen: 3,
Cmd: 'A',
Params: []int{1 | parser.HasMoreFlag, 2, 3},
},
want: "\x1b[1:2;3A",
},
{
name: "with intermediate",
s: CsiSequence{
Cmd: 'A' | '$'<<parser.IntermedShift,
Params: []int{1, 2, 3},
ParamsLen: 3,
Cmd: 'A' | '$'<<parser.IntermedShift,
Params: []int{1, 2, 3},
},
want: "\x1b[1;2;3$A",
},
{
name: "with marker",
s: CsiSequence{
Cmd: 'A' | '?'<<parser.MarkerShift,
Params: []int{1, 2, 3},
ParamsLen: 3,
Cmd: 'A' | '?'<<parser.MarkerShift,
Params: []int{1, 2, 3},
},
want: "\x1b[?1;2;3A",
},
{
name: "with marker intermediate and more flag",
s: CsiSequence{
Cmd: 'A' | '?'<<parser.MarkerShift | '$'<<parser.IntermedShift,
Params: []int{1, 2 | parser.HasMoreFlag, 3},
ParamsLen: 3,
Cmd: 'A' | '?'<<parser.MarkerShift | '$'<<parser.IntermedShift,
Params: []int{1, 2 | parser.HasMoreFlag, 3},
},
want: "\x1b[?1;2:3$A",
},
Expand Down
27 changes: 16 additions & 11 deletions exp/term/ansi/dcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,6 @@ type DcsSequence struct {
// This is the data between the final byte and the escape sequence terminator.
Data []byte

// ParamsLen contains the number of parameters in the sequence.
// This is the number of all parameters, including sub-parameters.
// Use Len to get the number of parameters segments excluding
// sub-parameters.
ParamsLen int

// Cmd contains the raw command of the sequence.
// The command is a 32-bit integer containing the DCS command byte in the
// lower 8 bits, the private marker in the next 8 bits, and the intermediate
Expand All @@ -47,12 +41,14 @@ type DcsSequence struct {
Cmd int
}

var _ Sequence = DcsSequence{}

// Marker returns the marker byte of the DCS sequence.
// This is always gonna be one of the following '<' '=' '>' '?' and in the
// range of 0x3C-0x3F.
// Zero is returned if the sequence does not have a marker.
func (s DcsSequence) Marker() int {
return parser.Command(s.Cmd)
return parser.Marker(s.Cmd)
}

// Intermediate returns the intermediate byte of the DCS sequence.
Expand Down Expand Up @@ -83,21 +79,30 @@ func (s DcsSequence) HasMore(i int) bool {
// Subparams returns the sub-parameters of the given parameter.
// It returns nil if the parameter does not exist.
func (s DcsSequence) Subparams(i int) []int {
return parser.Subparams(s.Params, s.ParamsLen, i)
return parser.Subparams(s.Params, i)
}

// Len returns the number of parameters in the sequence.
// This will return the number of parameters in the sequence, excluding any
// sub-parameters.
func (s DcsSequence) Len() int {
return parser.Len(s.Params, s.ParamsLen)
return parser.Len(s.Params)
}

// Range iterates over the parameters of the sequence and calls the given
// function for each parameter.
// The function should return false to stop the iteration.
func (s DcsSequence) Range(fn func(i int, param int, hasMore bool) bool) {
parser.Range(s.Params, s.ParamsLen, fn)
parser.Range(s.Params, fn)
}

// Clone returns a copy of the DCS sequence.
func (s DcsSequence) Clone() Sequence {
return DcsSequence{
Params: append([]int(nil), s.Params...),
Data: append([]byte(nil), s.Data...),
Cmd: s.Cmd,
}
}

// String returns a string representation of the sequence.
Expand All @@ -117,7 +122,7 @@ func (s DcsSequence) buffer() *bytes.Buffer {
if param >= -1 {
b.WriteString(strconv.Itoa(param))
}
if i < s.ParamsLen-1 {
if i < len(s.Params)-1 {
if hasMore {
b.WriteByte(':')
} else {
Expand Down
9 changes: 8 additions & 1 deletion exp/term/ansi/gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 19 additions & 1 deletion exp/term/ansi/osc.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package ansi
import (
"bytes"
"strconv"
"strings"
)

// OscSequence represents an OSC sequence.
Expand All @@ -17,18 +18,35 @@ import (
//
// See ECMA-48 § 5.7.
type OscSequence struct {
// Data contains the raw data of the sequence.
// Data contains the raw data of the sequence including the identifier
// command.
Data []byte

// Cmd contains the raw command of the sequence.
Cmd int
}

var _ Sequence = OscSequence{}

// Command returns the command of the OSC sequence.
func (s OscSequence) Command() int {
return s.Cmd
}

// Params returns the parameters of the OSC sequence split by ';'.
// The first element is the identifier command.
func (s OscSequence) Params() []string {
return strings.Split(string(s.Data), ";")
}

// Clone returns a copy of the OSC sequence.
func (s OscSequence) Clone() Sequence {
return OscSequence{
Data: append([]byte(nil), s.Data...),
Cmd: s.Cmd,
}
}

// String returns the string representation of the OSC sequence.
// To be more compatible with different terminal, this will always return a
// 7-bit formatted sequence, terminated by BEL.
Expand Down
Loading

0 comments on commit ab9afc2

Please sign in to comment.