- 会话分叉: 按 c 键从历史会话分叉,支持带方向提示 - 启动自动加载历史记录 - 增量扫描: 缓存内存化、目录 modTime 跳过、已删除条目裁剪 - 刷新按钮复用 onTabSwitch 单一入口
135 lines
2.5 KiB
Go
135 lines
2.5 KiB
Go
package internal
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"encoding/base64"
|
|
"math/big"
|
|
"runtime"
|
|
"strings"
|
|
"unicode/utf16"
|
|
"unicode/utf8"
|
|
)
|
|
|
|
// 平台信息(启动时确定)
|
|
var (
|
|
isWindows = runtime.GOOS == "windows"
|
|
platformKey = runtime.GOOS + "-" + runtime.GOARCH
|
|
)
|
|
|
|
// toWinPath 将路径中的 / 替换为 \
|
|
func toWinPath(s string) string {
|
|
return strings.ReplaceAll(s, "/", "\\")
|
|
}
|
|
|
|
// --- 字符串宽度 ---
|
|
|
|
func stringWidth(s string) int {
|
|
w := 0
|
|
for _, r := range s {
|
|
w += runeWidth(r)
|
|
}
|
|
return w
|
|
}
|
|
|
|
func runeWidth(r rune) int {
|
|
if r >= 0x1100 &&
|
|
(r <= 0x115F || r == 0x2329 || r == 0x232A ||
|
|
(r >= 0x2E80 && r <= 0xA4CF && r != 0x303F) ||
|
|
(r >= 0xAC00 && r <= 0xD7A3) ||
|
|
(r >= 0xF900 && r <= 0xFAFF) ||
|
|
(r >= 0xFE10 && r <= 0xFE19) ||
|
|
(r >= 0xFE30 && r <= 0xFE6F) ||
|
|
(r >= 0xFF01 && r <= 0xFF60) ||
|
|
(r >= 0xFFE0 && r <= 0xFFE6) ||
|
|
(r >= 0x20000 && r <= 0x2FFFD) ||
|
|
(r >= 0x30000 && r <= 0x3FFFD)) {
|
|
return 2
|
|
}
|
|
return 1
|
|
}
|
|
|
|
func padRightByWidth(s string, targetW int) string {
|
|
w := stringWidth(s)
|
|
if w >= targetW {
|
|
return s
|
|
}
|
|
return s + strings.Repeat(" ", targetW-w)
|
|
}
|
|
|
|
func truncateByWidth(s string, maxW int) string {
|
|
w := 0
|
|
for i := 0; i < len(s); {
|
|
r, size := utf8.DecodeRuneInString(s[i:])
|
|
rw := runeWidth(r)
|
|
if w+rw > maxW {
|
|
return s[:i]
|
|
}
|
|
w += rw
|
|
i += size
|
|
}
|
|
return s
|
|
}
|
|
|
|
func wrapByWidth(s string, maxW int) []string {
|
|
if maxW <= 0 || s == "" {
|
|
return []string{s}
|
|
}
|
|
var lines []string
|
|
for s != "" {
|
|
cut := truncateByWidth(s, maxW)
|
|
lines = append(lines, cut)
|
|
s = s[len(cut):]
|
|
}
|
|
return lines
|
|
}
|
|
|
|
func fitWidth(s string, w int) string {
|
|
return padRightByWidth(truncateByWidth(s, w), w)
|
|
}
|
|
|
|
// --- 视口 ---
|
|
|
|
func viewport(cursor, total, height int) (int, int) {
|
|
if total <= height {
|
|
return 0, total
|
|
}
|
|
half := height / 2
|
|
start := cursor - half
|
|
if start < 0 {
|
|
start = 0
|
|
}
|
|
end := start + height
|
|
if end > total {
|
|
end = total
|
|
start = end - height
|
|
if start < 0 {
|
|
start = 0
|
|
}
|
|
}
|
|
return start, end
|
|
}
|
|
|
|
func padLinesTo(lines []string, w, target int) string {
|
|
for len(lines) < target {
|
|
lines = append(lines, strings.Repeat(" ", w))
|
|
}
|
|
return strings.Join(lines, "\n")
|
|
}
|
|
|
|
// --- Windows Terminal ---
|
|
|
|
func encodePSCommand(script string) string {
|
|
u16 := utf16.Encode([]rune(script))
|
|
b := make([]byte, len(u16)*2)
|
|
for i, r := range u16 {
|
|
b[i*2] = byte(r)
|
|
b[i*2+1] = byte(r >> 8)
|
|
}
|
|
return base64.StdEncoding.EncodeToString(b)
|
|
}
|
|
|
|
func randRange(min, max int) int {
|
|
n, _ := rand.Int(rand.Reader, big.NewInt(int64(max-min)))
|
|
return min + int(n.Int64())
|
|
}
|