96 lines
2.2 KiB
Go
96 lines
2.2 KiB
Go
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++ {
|
|
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.
|
|
func StringWidth(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)
|
|
width += w
|
|
i += len(cluster) - 1
|
|
pstate = parser.GroundState
|
|
continue
|
|
}
|
|
|
|
if action == parser.PrintAction {
|
|
width++
|
|
}
|
|
|
|
pstate = state
|
|
}
|
|
|
|
return width
|
|
}
|