Skip to content

feat(ansi): sequences: add iterator and reader for ANSI escape sequences#756

Open
aymanbagabas wants to merge 9 commits intomainfrom
ansi/sequences
Open

feat(ansi): sequences: add iterator and reader for ANSI escape sequences#756
aymanbagabas wants to merge 9 commits intomainfrom
ansi/sequences

Conversation

@aymanbagabas
Copy link
Copy Markdown
Contributor

@aymanbagabas aymanbagabas commented Jan 27, 2026

This adds a new package sequences that provides an iterator and a reader for ANSI escape sequences and grapheme clusters. The iterator allows for easy traversal of strings containing ANSI codes, while the reader integrates with bufio.Scanner for convenient scanning of input streams.

Fixes: #215

Here's an example parsing escape sequences from stdin that you can test with something like printf 'abc 🛡️\x1b[1;2;3;4;;m \x1bP0q;abc\x1b\\' | go run ./parserlog3

// Package main demonstrates usage.
package main

import (
	"log"
	"os"
	"strconv"
	"strings"

	"github.com/charmbracelet/x/ansi"
	"github.com/charmbracelet/x/ansi/sequences"
)

func main() {
	var params []int
	var hasMore []bool
	s := sequences.FromReader(os.Stdin)
	s.Exec(func(b byte) {
		log.Printf("Exec: %q", b)
	})
	s.Param(func(i int, val int, hm bool) {
		params = append(params, val)
		hasMore = append(hasMore, hm)
	})
	s.Cmd(func(typ byte, prefix byte, intermed byte, final byte) {
		t := "INV"
		switch typ {
		case ansi.CSI:
			t = "CSI"
		case ansi.DCS:
			t = "DCS"
		}
		log.Printf("Cmd: type=%q prefix=%q intermed=%q final=%q params=%v", t, prefix, intermed, final, formatParams(params, hasMore))
		params = params[:0]
		hasMore = hasMore[:0]
	})
	s.Data(func(typ byte, d []byte, term byte) {
		t := "INV"
		switch typ {
		case ansi.DCS:
			t = "DCS"
		case ansi.OSC:
			t = "OSC"
		case ansi.APC:
			t = "APC"
		case ansi.SOS:
			t = "SOS"
		case ansi.PM:
			t = "PM"
		}
		log.Printf("Data: type=%q data=%q cancelled=%v", t, d, term == 0)
	})
	s.Print(func(d []byte, width int) {
		log.Printf("Print: %q width=%d", d, width)
	})

	for s.Scan() {
		tok := s.Bytes()
		width := s.Width()
		state := s.State()
		log.Printf("Token: %q width=%d state=%v", tok, width, state)
	}
}

func formatParams(params []int, hasMore []bool) string {
	if len(params) != len(hasMore) {
		return ""
	}
	var sb strings.Builder
	for i, p := range params {
		if i > 0 {
			if hasMore[i-1] {
				sb.WriteString(":")
			} else {
				sb.WriteString(";")
			}
		}
		sb.WriteString(strconv.Itoa(p))
	}
	return sb.String()
}

This adds a new package `sequences` that provides an iterator and a
reader for ANSI escape sequences and grapheme clusters. The iterator
allows for easy traversal of strings containing ANSI codes, while the
reader integrates with `bufio.Scanner` for convenient scanning of input
streams.

Fixes: #215
@codecov
Copy link
Copy Markdown

codecov Bot commented Jan 27, 2026

Codecov Report

❌ Patch coverage is 40.94828% with 137 lines in your changes missing coverage. Please review.
✅ Project coverage is 52.27%. Comparing base (33beb0e) to head (c828ff4).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
ansi/sequences/splitfunc.go 45.76% 87 Missing and 9 partials ⚠️
ansi/sequences/iterator.go 0.00% 20 Missing ⚠️
ansi/sequences/reader.go 40.00% 12 Missing ⚠️
ansi/parser_decode.go 40.00% 9 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##             main     #756       +/-   ##
===========================================
+ Coverage   29.07%   52.27%   +23.20%     
===========================================
  Files         182       57      -125     
  Lines       15867     3516    -12351     
===========================================
- Hits         4613     1838     -2775     
+ Misses      10965     1611     -9354     
+ Partials      289       67      -222     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants