dcs.go 4.4 KB

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