| 209 | } |
| 210 | |
| 211 | func formatDebugTermDecode(data []byte) string { |
| 212 | if len(data) == 0 { |
| 213 | return "" |
| 214 | } |
| 215 | lines := make([]string, 0) |
| 216 | // textBuf accumulates text across CSI-C (cursor forward) sequences so consecutive |
| 217 | // "word CSI-C word" runs collapse into a single TXT line. The // NC annotation goes |
| 218 | // on the last segment only. |
| 219 | textBuf := "" |
| 220 | totalCSpaces := 0 |
| 221 | flushText := func() { |
| 222 | if textBuf == "" && totalCSpaces == 0 { |
| 223 | return |
| 224 | } |
| 225 | segs := splitOnCRLFRuns(textBuf) |
| 226 | if len(segs) == 0 { |
| 227 | segs = []string{textBuf} |
| 228 | } |
| 229 | for i, seg := range segs { |
| 230 | if i == len(segs)-1 && totalCSpaces > 0 { |
| 231 | lines = append(lines, fmt.Sprintf("TXT %s // %dC", strconv.Quote(seg), totalCSpaces)) |
| 232 | } else { |
| 233 | lines = append(lines, "TXT "+strconv.Quote(seg)) |
| 234 | } |
| 235 | } |
| 236 | textBuf = "" |
| 237 | totalCSpaces = 0 |
| 238 | } |
| 239 | for i := 0; i < len(data); { |
| 240 | b := data[i] |
| 241 | if b == 0x1b { |
| 242 | if i+1 >= len(data) { |
| 243 | flushText() |
| 244 | lines = append(lines, "ESC") |
| 245 | i++ |
| 246 | continue |
| 247 | } |
| 248 | next := data[i+1] |
| 249 | switch next { |
| 250 | case '[': |
| 251 | seq, end := consumeDebugTermCSI(data, i) |
| 252 | if n, ok := parseCursorForwardN(seq); ok { |
| 253 | textBuf += strings.Repeat(" ", n) |
| 254 | totalCSpaces += n |
| 255 | } else { |
| 256 | flushText() |
| 257 | lines = append(lines, formatDebugTermCSILine(seq)) |
| 258 | } |
| 259 | i = end |
| 260 | case ']': |
| 261 | flushText() |
| 262 | seq, end := consumeDebugTermOSC(data, i) |
| 263 | lines = append(lines, formatDebugTermOSCLine(seq)) |
| 264 | i = end |
| 265 | case 'P': |
| 266 | flushText() |
| 267 | seq, end := consumeDebugTermST(data, i) |
| 268 | lines = append(lines, "DCS "+strconv.QuoteToASCII(string(seq))) |