chore: refactor cmd base caller
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
package clibase
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// AnsiRegex 匹配终端 ANSI 控制序列的通用正则表达式
|
||||
var AnsiRegex = regexp.MustCompile("\x1b\\[[0-9;]*[a-zA-Z]")
|
||||
|
||||
// CleanWriter 过滤输出流中的终端回车符覆写及 ANSI 色彩代码
|
||||
type CleanWriter struct {
|
||||
out io.Writer
|
||||
buf []byte
|
||||
}
|
||||
|
||||
// NewCleanWriter 构造输出清洗器
|
||||
func NewCleanWriter(out io.Writer) *CleanWriter {
|
||||
return &CleanWriter{out: out}
|
||||
}
|
||||
|
||||
func (c *CleanWriter) Write(p []byte) (n int, err error) {
|
||||
c.buf = append(c.buf, p...)
|
||||
|
||||
for {
|
||||
idx := bytes.IndexAny(c.buf, "\r\n")
|
||||
if idx == -1 {
|
||||
break
|
||||
}
|
||||
|
||||
if c.buf[idx] == '\r' && idx == len(c.buf)-1 {
|
||||
// 跨块截断的回车,等待下一块
|
||||
break
|
||||
}
|
||||
|
||||
char := c.buf[idx]
|
||||
line := string(c.buf[:idx])
|
||||
c.buf = c.buf[idx+1:]
|
||||
|
||||
if char == '\r' && len(c.buf) > 0 && c.buf[0] == '\n' {
|
||||
c.buf = c.buf[1:]
|
||||
char = '\n'
|
||||
}
|
||||
|
||||
s := AnsiRegex.ReplaceAllString(line, "")
|
||||
|
||||
if char == '\r' {
|
||||
continue // 忽略终端进度条的同行覆盖
|
||||
}
|
||||
|
||||
if s != "" {
|
||||
c.out.Write([]byte(s + "\n"))
|
||||
}
|
||||
}
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// Flush 输出末尾缓冲
|
||||
func (c *CleanWriter) Flush() {
|
||||
if len(c.buf) > 0 {
|
||||
s := string(c.buf)
|
||||
s = strings.TrimSuffix(s, "\r")
|
||||
s = AnsiRegex.ReplaceAllString(s, "")
|
||||
if s != "" {
|
||||
c.out.Write([]byte(s + "\n"))
|
||||
}
|
||||
c.buf = nil
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user