csi.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. package ansi
  2. import (
  3. "bytes"
  4. "strconv"
  5. "github.com/charmbracelet/x/ansi/parser"
  6. )
  7. // CsiSequence represents a control sequence introducer (CSI) sequence.
  8. //
  9. // The sequence starts with a CSI sequence, CSI (0x9B) in a 8-bit environment
  10. // or ESC [ (0x1B 0x5B) in a 7-bit environment, followed by any number of
  11. // parameters in the range of 0x30-0x3F, then by any number of intermediate
  12. // byte in the range of 0x20-0x2F, then finally with a single final byte in the
  13. // range of 0x20-0x7E.
  14. //
  15. // CSI P..P I..I F
  16. //
  17. // See ECMA-48 § 5.4.
  18. type CsiSequence struct {
  19. // Params contains the raw parameters of the sequence.
  20. // This is a slice of integers, where each integer is a 32-bit integer
  21. // containing the parameter value in the lower 31 bits and a flag in the
  22. // most significant bit indicating whether there are more sub-parameters.
  23. Params []int
  24. // Cmd contains the raw command of the sequence.
  25. // The command is a 32-bit integer containing the CSI command byte in the
  26. // lower 8 bits, the private marker in the next 8 bits, and the intermediate
  27. // byte in the next 8 bits.
  28. //
  29. // CSI ? u
  30. //
  31. // Is represented as:
  32. //
  33. // 'u' | '?' << 8
  34. Cmd int
  35. }
  36. var _ Sequence = CsiSequence{}
  37. // Marker returns the marker byte of the CSI sequence.
  38. // This is always gonna be one of the following '<' '=' '>' '?' and in the
  39. // range of 0x3C-0x3F.
  40. // Zero is returned if the sequence does not have a marker.
  41. func (s CsiSequence) Marker() int {
  42. return parser.Marker(s.Cmd)
  43. }
  44. // Intermediate returns the intermediate byte of the CSI sequence.
  45. // An intermediate byte is in the range of 0x20-0x2F. This includes these
  46. // characters from ' ', '!', '"', '#', '$', '%', '&', ”', '(', ')', '*', '+',
  47. // ',', '-', '.', '/'.
  48. // Zero is returned if the sequence does not have an intermediate byte.
  49. func (s CsiSequence) Intermediate() int {
  50. return parser.Intermediate(s.Cmd)
  51. }
  52. // Command returns the command byte of the CSI sequence.
  53. func (s CsiSequence) Command() int {
  54. return parser.Command(s.Cmd)
  55. }
  56. // Param returns the parameter at the given index.
  57. // It returns -1 if the parameter does not exist.
  58. func (s CsiSequence) Param(i int) int {
  59. return parser.Param(s.Params, i)
  60. }
  61. // HasMore returns true if the parameter has more sub-parameters.
  62. func (s CsiSequence) HasMore(i int) bool {
  63. return parser.HasMore(s.Params, i)
  64. }
  65. // Subparams returns the sub-parameters of the given parameter.
  66. // It returns nil if the parameter does not exist.
  67. func (s CsiSequence) Subparams(i int) []int {
  68. return parser.Subparams(s.Params, i)
  69. }
  70. // Len returns the number of parameters in the sequence.
  71. // This will return the number of parameters in the sequence, excluding any
  72. // sub-parameters.
  73. func (s CsiSequence) Len() int {
  74. return parser.Len(s.Params)
  75. }
  76. // Range iterates over the parameters of the sequence and calls the given
  77. // function for each parameter.
  78. // The function should return false to stop the iteration.
  79. func (s CsiSequence) Range(fn func(i int, param int, hasMore bool) bool) {
  80. parser.Range(s.Params, fn)
  81. }
  82. // Clone returns a copy of the CSI sequence.
  83. func (s CsiSequence) Clone() Sequence {
  84. return CsiSequence{
  85. Params: append([]int(nil), s.Params...),
  86. Cmd: s.Cmd,
  87. }
  88. }
  89. // String returns a string representation of the sequence.
  90. // The string will always be in the 7-bit format i.e (ESC [ P..P I..I F).
  91. func (s CsiSequence) String() string {
  92. return s.buffer().String()
  93. }
  94. // buffer returns a buffer containing the sequence.
  95. func (s CsiSequence) buffer() *bytes.Buffer {
  96. var b bytes.Buffer
  97. b.WriteString("\x1b[")
  98. if m := s.Marker(); m != 0 {
  99. b.WriteByte(byte(m))
  100. }
  101. s.Range(func(i, param int, hasMore bool) bool {
  102. if param >= 0 {
  103. b.WriteString(strconv.Itoa(param))
  104. }
  105. if i < len(s.Params)-1 {
  106. if hasMore {
  107. b.WriteByte(':')
  108. } else {
  109. b.WriteByte(';')
  110. }
  111. }
  112. return true
  113. })
  114. if i := s.Intermediate(); i != 0 {
  115. b.WriteByte(byte(i))
  116. }
  117. b.WriteByte(byte(s.Command()))
  118. return &b
  119. }
  120. // Bytes returns the byte representation of the sequence.
  121. // The bytes will always be in the 7-bit format i.e (ESC [ P..P I..I F).
  122. func (s CsiSequence) Bytes() []byte {
  123. return s.buffer().Bytes()
  124. }