Files
kitty/tools/cli/wcswidth_kitten.go
2025-04-11 11:40:23 +05:30

162 lines
4.1 KiB
Go

package cli
import (
"bytes"
"fmt"
"strconv"
"strings"
"github.com/google/go-cmp/cmp"
"kitty"
"kitty/tools/tui/loop"
"kitty/tools/utils"
"kitty/tools/wcswidth"
)
var _ = fmt.Print
type test_struct struct {
description string
num int
expected_cursor_positions []int
actual_cursor_positions []int
payload string
}
const cursor_position_report = "\x1b[6n"
func run_tests(tests []*test_struct) (err error) {
lp, err := loop.New(loop.NoAlternateScreen)
if err != nil {
return err
}
buf := strings.Builder{}
buf.WriteString(loop.PENDING_UPDATE.EscapeCodeToSet())
for _, t := range tests {
buf.WriteString(t.payload)
buf.WriteString("\r\x1b[K")
if buf.Len() > 512*1024 {
buf.WriteString(loop.PENDING_UPDATE.EscapeCodeToReset())
buf.WriteString(loop.PENDING_UPDATE.EscapeCodeToSet())
}
}
buf.WriteString(loop.PENDING_UPDATE.EscapeCodeToReset())
buf.WriteString("\x1b[c")
lp.OnInitialize = func() (string, error) {
lp.SetCursorVisible(false)
lp.Printf("Running %d tests, please wait...\n", len(tests))
lp.QueueWriteString(buf.String())
return "", err
}
lp.OnFinalize = func() string {
lp.SetCursorVisible(true)
return ""
}
current_test_idx := 0
lp.OnEscapeCode = func(typ loop.EscapeCodeType, data []byte) error {
if typ == loop.CSI {
switch data[len(data)-1] {
case 'c':
lp.Quit(0)
case 'R':
if idx := bytes.IndexByte(data, ';'); idx > -1 {
if xpos, e := strconv.Atoi(utils.UnsafeBytesToString(data[idx+1 : len(data)-1])); e == nil {
t := tests[current_test_idx]
if len(t.actual_cursor_positions) >= len(t.expected_cursor_positions) && current_test_idx+1 < len(tests) {
current_test_idx += 1
t = tests[current_test_idx]
}
t.actual_cursor_positions = append(t.actual_cursor_positions, xpos-1)
}
}
}
}
return nil
}
if err = lp.Run(); err != nil {
return err
}
return show_results(tests)
}
func show_results(tests []*test_struct) (err error) {
num_failures := 0
for _, t := range tests {
if diff := cmp.Diff(t.expected_cursor_positions, t.actual_cursor_positions); diff != "" {
fmt.Println("\x1b[31mTest failed\x1b[39m:", t.description)
fmt.Println(diff)
num_failures++
}
}
if num_failures > 0 {
err = fmt.Errorf("%d out of %d tests failed.", num_failures, len(tests))
} else {
fmt.Println("All tests passed!")
}
return
}
func has_control_chars(text string) bool {
for _, ch := range text {
if ch < ' ' {
return true
}
}
return false
}
func main(allowed_tests *utils.Set[int]) (rc int, err error) {
var tests []*test_struct
if gb_tests, err := kitty.LoadGraphemeBreakTests(); err == nil {
for _, t := range gb_tests {
test_num := len(tests) + 1
desc := fmt.Sprintf("Test: #%d: Unicode GraphemeBreakTest: %s", test_num, t.Comment)
text := strings.Join(t.Data, "")
if has_control_chars(text) {
continue
}
payload := " " + text + cursor_position_report
test := test_struct{num: test_num, description: desc, payload: payload, expected_cursor_positions: []int{1 + wcswidth.Stringwidth(text)}}
tests = append(tests, &test)
}
if allowed_tests.Len() > 0 {
temp := make([]*test_struct, 0, len(tests))
for _, t := range tests {
if allowed_tests.Has(t.num) {
temp = append(temp, t)
}
}
tests = temp
}
if err = run_tests(tests); err != nil {
return 1, err
}
} else {
return 1, err
}
return
}
func WcswidthKittenEntryPoint(root *Command) {
root.AddSubCommand(&Command{
Name: "__width_test__",
Usage: "[test number to run...]",
HelpText: "Test the terminal for compliance with the kitty text-sizing specification's splitting of text into cells. You can optionally specify specific test numbers to run.",
Hidden: true,
OnlyArgsAllowed: true,
Run: func(cmd *Command, args []string) (rc int, err error) {
allowed_tests := utils.NewSet[int]()
for _, arg := range args {
if x, err := strconv.Atoi(arg); err == nil {
allowed_tests.Add(x)
} else {
return 1, fmt.Errorf("%s is not a valid test number", arg)
}
}
return main(allowed_tests)
},
})
}