peco.peco/internal/util/util.go
Daisuke Maki 4b1dfa1c4d fix: fast-path StripANSISequence to skip regexp when no ESC byte
Benchmark (10k lines):
  plain text: 1023us -> 37us (-96%), 1.7MiB -> 0B (-100%), 30k -> 0 allocs
  95% plain:  1064us -> 162us (-85%), 1720KiB -> 63KiB (-96%), 30.5k -> 2k allocs
2026-02-22 00:26:02 +09:00

99 lines
2.3 KiB
Go

package util
import (
"errors"
"regexp"
"strings"
"unicode"
)
type fder interface {
Fd() uintptr
}
// CaseInsensitiveIndexFunc returns a function that matches runes equal to r, ignoring case.
func CaseInsensitiveIndexFunc(r rune) func(rune) bool {
lr := unicode.ToUpper(r)
return func(v rune) bool {
return lr == unicode.ToUpper(v)
}
}
// CaseInsensitiveIndex returns the byte index of the first rune in s that
// is case-insensitively equal to r. Returns -1 if not found. This avoids
// the closure allocation of CaseInsensitiveIndexFunc + strings.IndexFunc.
func CaseInsensitiveIndex(s string, r rune) int {
upper := unicode.ToUpper(r)
for i, c := range s {
if unicode.ToUpper(c) == upper {
return i
}
}
return -1
}
// ContainsUpper reports whether the string contains any uppercase letter.
func ContainsUpper(query string) bool {
for _, c := range query {
if unicode.IsUpper(c) {
return true
}
}
return false
}
// Global var used to strips ansi sequences
var reANSIEscapeChars = regexp.MustCompile("\x1B\\[(?:[0-9]{1,2}(?:;[0-9]{1,2})?)*[a-zA-Z]")
// StripANSISequence strips ANSI escape sequences from the given string.
// Fast-path: if the string contains no ESC byte, return it unchanged
// to avoid a regexp allocation.
func StripANSISequence(s string) string {
if !strings.Contains(s, "\x1b") {
return s
}
return reANSIEscapeChars.ReplaceAllString(s, "")
}
type ignorable interface {
Ignorable() bool
}
type collectResults interface {
CollectResults() bool
}
type exitStatuser interface {
ExitStatus() int
}
// IsIgnorableError checks whether err implements the Ignorable interface and returns true.
func IsIgnorableError(err error) bool {
for e := err; e != nil; e = errors.Unwrap(e) {
if v, ok := e.(ignorable); ok {
return v.Ignorable()
}
}
return false
}
// IsCollectResultsError checks whether err signals that results should be collected.
func IsCollectResultsError(err error) bool {
for e := err; e != nil; e = errors.Unwrap(e) {
if v, ok := e.(collectResults); ok {
return v.CollectResults()
}
}
return false
}
// GetExitStatus extracts the exit status code from an error, returning 1 and false if not found.
func GetExitStatus(err error) (int, bool) {
for e := err; e != nil; e = errors.Unwrap(e) {
if ese, ok := e.(exitStatuser); ok {
return ese.ExitStatus(), true
}
}
return 1, false
}