| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113 |
- package ansi
- import (
- "bytes"
- "github.com/charmbracelet/x/ansi/parser"
- "github.com/mattn/go-runewidth"
- "github.com/rivo/uniseg"
- )
- // Strip removes ANSI escape codes from a string.
- func Strip(s string) string {
- var (
- buf bytes.Buffer // buffer for collecting printable characters
- ri int // rune index
- rw int // rune width
- pstate = parser.GroundState // initial state
- )
- // This implements a subset of the Parser to only collect runes and
- // printable characters.
- for i := 0; i < len(s); i++ {
- if pstate == parser.Utf8State {
- // During this state, collect rw bytes to form a valid rune in the
- // buffer. After getting all the rune bytes into the buffer,
- // transition to GroundState and reset the counters.
- buf.WriteByte(s[i])
- ri++
- if ri < rw {
- continue
- }
- pstate = parser.GroundState
- ri = 0
- rw = 0
- continue
- }
- state, action := parser.Table.Transition(pstate, s[i])
- switch action {
- case parser.CollectAction:
- if state == parser.Utf8State {
- // This action happens when we transition to the Utf8State.
- rw = utf8ByteLen(s[i])
- buf.WriteByte(s[i])
- ri++
- }
- case parser.PrintAction, parser.ExecuteAction:
- // collects printable ASCII and non-printable characters
- buf.WriteByte(s[i])
- }
- // Transition to the next state.
- // The Utf8State is managed separately above.
- if pstate != parser.Utf8State {
- pstate = state
- }
- }
- return buf.String()
- }
- // StringWidth returns the width of a string in cells. This is the number of
- // cells that the string will occupy when printed in a terminal. ANSI escape
- // codes are ignored and wide characters (such as East Asians and emojis) are
- // accounted for.
- // This treats the text as a sequence of grapheme clusters.
- func StringWidth(s string) int {
- return stringWidth(GraphemeWidth, s)
- }
- // StringWidthWc returns the width of a string in cells. This is the number of
- // cells that the string will occupy when printed in a terminal. ANSI escape
- // codes are ignored and wide characters (such as East Asians and emojis) are
- // accounted for.
- // This treats the text as a sequence of wide characters and runes.
- func StringWidthWc(s string) int {
- return stringWidth(WcWidth, s)
- }
- func stringWidth(m Method, s string) int {
- if s == "" {
- return 0
- }
- var (
- pstate = parser.GroundState // initial state
- cluster string
- width int
- )
- for i := 0; i < len(s); i++ {
- state, action := parser.Table.Transition(pstate, s[i])
- if state == parser.Utf8State {
- var w int
- cluster, _, w, _ = uniseg.FirstGraphemeClusterInString(s[i:], -1)
- if m == WcWidth {
- w = runewidth.StringWidth(cluster)
- }
- width += w
- i += len(cluster) - 1
- pstate = parser.GroundState
- continue
- }
- if action == parser.PrintAction {
- width++
- }
- pstate = state
- }
- return width
- }
|