| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273 |
- package ansi
- import (
- "bytes"
- "github.com/charmbracelet/x/ansi/parser"
- "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++ {
- var state, action byte
- if pstate != parser.Utf8State {
- state, action = parser.Table.Transition(pstate, s[i])
- }
- switch {
- case 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
- case action == parser.PrintAction:
- // This action happens when we transition to the Utf8State.
- if w := utf8ByteLen(s[i]); w > 1 {
- rw = w
- buf.WriteByte(s[i])
- ri++
- break
- }
- fallthrough
- case action == 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.
- func StringWidth(s string) int {
- if s == "" {
- return 0
- }
- return uniseg.StringWidth(Strip(s))
- }
|