From eddaaed3e3898e98fa10032001e928e3eb2e542b Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 10 Mar 2026 14:47:41 +0530 Subject: [PATCH] Modernize Go code --- bypy/devenv.go | 4 +-- kittens/ask/choices.go | 6 ++-- kittens/choose_files/results.go | 6 ++-- kittens/clipboard/read.go | 12 +++---- kittens/command_palette/main.go | 14 +++----- kittens/command_palette/main_test.go | 2 +- kittens/diff/patch.go | 2 +- kittens/show_key/legacy.go | 17 ++++----- tools/cli/wcswidth_kitten.go | 2 +- tools/cmd/mouse_demo/main.go | 39 +++++++++------------ tools/rsync/algorithm.go | 7 ---- tools/tty/tty_bsd.go | 1 - tools/tui/readline/history.go | 4 +-- tools/utils/strings.go | 6 ++-- tools/utils/style/colorspaces_bench_test.go | 19 +++++----- tools/wcswidth/char-props-data.go | 10 +++--- 16 files changed, 64 insertions(+), 87 deletions(-) diff --git a/bypy/devenv.go b/bypy/devenv.go index 53c6bf132..e6ad092ff 100644 --- a/bypy/devenv.go +++ b/bypy/devenv.go @@ -96,8 +96,8 @@ func get_dependencies(path string) (ans []dependency) { for _, line := range lines("otool", "-L", path) { line = strings.TrimSpace(line) if strings.Contains(line, "compatibility") && !strings.HasSuffix(line, ":") { - idx := strings.IndexByte(line, '(') - dep := strings.TrimSpace(line[:idx]) + before, _, _ := strings.Cut(line, "(") + dep := strings.TrimSpace(before) ans = append(ans, dependency{path: dep, is_id: dep == install_name}) } } diff --git a/kittens/ask/choices.go b/kittens/ask/choices.go index 252dc63d9..85cbea13c 100644 --- a/kittens/ask/choices.go +++ b/kittens/ask/choices.go @@ -342,9 +342,9 @@ func GetChoices(o *Options) (response string, err error) { lp.QueueWriteString(strings.Repeat("\r\n", y)) for _, line := range msg_lines { if replacement_text != "" { - idx := strings.Index(line, replacement_text) - if idx > -1 { - x := wcswidth.Stringwidth(line[:idx]) + before, _, ok := strings.Cut(line, replacement_text) + if ok { + x := wcswidth.Stringwidth(before) replacement_range = Range{x, x + wcswidth.Stringwidth(replacement_text), y} } } diff --git a/kittens/choose_files/results.go b/kittens/choose_files/results.go index 840cfd14c..6afc819fe 100644 --- a/kittens/choose_files/results.go +++ b/kittens/choose_files/results.go @@ -292,13 +292,13 @@ func (h *Handler) draw_num_of_matches(num_shown, y int, in_progress bool) { st := loop.SizedText{Subscale_denominator: 2, Subscale_numerator: 1, Vertical_alignment: 2, Width: 1} graphemes := wcswidth.SplitIntoGraphemes(m) for len(graphemes) > 0 { - s := "" + var s strings.Builder for w := 0; w < 2 && len(graphemes) > 0; { w += wcswidth.Stringwidth(graphemes[0]) - s += graphemes[0] + s.WriteString(graphemes[0]) graphemes = graphemes[1:] } - h.lp.DrawSizedText(s, st) + h.lp.DrawSizedText(s.String(), st) } if spinner != "" { h.lp.QueueWriteString(spinner) diff --git a/kittens/clipboard/read.go b/kittens/clipboard/read.go index e2e138caf..c5f7dbb41 100644 --- a/kittens/clipboard/read.go +++ b/kittens/clipboard/read.go @@ -388,11 +388,9 @@ func run_get_loop(opts *Options, args []string) (err error) { if getting_data_for != current_mime { if prev := requested_mimes[getting_data_for]; prev != nil && !prev.all_data_received { prev.all_data_received = true - wg.Add(1) - go func() { + wg.Go(func() { prev.commit() - wg.Done() - }() + }) } getting_data_for = current_mime @@ -405,11 +403,9 @@ func run_get_loop(opts *Options, args []string) (err error) { case "DONE": if prev := requested_mimes[getting_data_for]; getting_data_for != "" && prev != nil && !prev.all_data_received { prev.all_data_received = true - wg.Add(1) - go func() { + wg.Go(func() { prev.commit() - wg.Done() - }() + }) getting_data_for = "" } lp.Quit(0) diff --git a/kittens/command_palette/main.go b/kittens/command_palette/main.go index d9e983163..e0d26653f 100644 --- a/kittens/command_palette/main.go +++ b/kittens/command_palette/main.go @@ -255,7 +255,7 @@ func (h *Handler) updateFilter() { // Score each column independently colResults := [3][]fzf.Result{} - for c := 0; c < 3; c++ { + for c := range 3 { results, err := h.matcher.Score(colSlices[c], h.query) if err == nil { colResults[c] = results @@ -276,7 +276,7 @@ func (h *Handler) updateFilter() { bestScore := uint(0) bestCol := 0 var bestPositions []int - for c := 0; c < 3; c++ { + for c := range 3 { if colResults[c] != nil && i < len(colResults[c]) && colResults[c][i].Score > bestScore { bestScore = colResults[c][i].Score bestCol = c @@ -357,10 +357,7 @@ func (h *Handler) draw_screen() { resultsStartY := 2 helpY := height - 1 hintsY := height - resultsHeight := helpY - resultsStartY - if resultsHeight < 1 { - resultsHeight = 1 - } + resultsHeight := max(helpY-resultsStartY, 1) h.results_start_y = resultsStartY h.results_height = resultsHeight @@ -381,10 +378,7 @@ func (h *Handler) draw_screen() { h.lp.MoveCursorTo(1, helpY) if b := h.selectedBinding(); b != nil && b.Help != "" { helpStr := b.Help - maxLen := width - 2 - if maxLen < 3 { - maxLen = 3 - } + maxLen := max(width-2, 3) if wcswidth.Stringwidth(helpStr) > maxLen { // Truncate by runes to avoid breaking multi-byte characters runes := []rune(helpStr) diff --git a/kittens/command_palette/main_test.go b/kittens/command_palette/main_test.go index b30a46f04..a489e608a 100644 --- a/kittens/command_palette/main_test.go +++ b/kittens/command_palette/main_test.go @@ -65,7 +65,7 @@ func TestFlattenAllBindings(t *testing.T) { func TestDefaultModeComesFirst(t *testing.T) { h := newTestHandler() // First 5 items should be from default mode - for i := 0; i < 5; i++ { + for i := range 5 { if h.all_items[i].binding.Mode != "" { t.Fatalf("Item %d should be from default mode, got mode=%q", i, h.all_items[i].binding.Mode) } diff --git a/kittens/diff/patch.go b/kittens/diff/patch.go index 18cd49d95..0f2cece01 100644 --- a/kittens/diff/patch.go +++ b/kittens/diff/patch.go @@ -215,7 +215,7 @@ func word_diff_center(left, right string, re *regexp.Regexp) Center { // on exactly one side, the lines differ in word count and it makes more // sense to highlight the single changed central region of the whole line. max_idx := max(len(left_words), len(right_words)) - for idx := 0; idx < max_idx; idx++ { + for idx := range max_idx { lc := idx < len(left_words) && left_changed_abs[idx] rc := idx < len(right_words) && right_changed_abs[idx] if lc != rc { diff --git a/kittens/show_key/legacy.go b/kittens/show_key/legacy.go index e1061aeb0..efccb1593 100644 --- a/kittens/show_key/legacy.go +++ b/kittens/show_key/legacy.go @@ -9,6 +9,7 @@ import ( "github.com/kovidgoyal/kitty/tools/tty" "io" "os" + "strings" "golang.org/x/sys/unix" ) @@ -17,24 +18,24 @@ var _ = fmt.Print func print_key(buf []byte, ctx *markup.Context) { const ctrl_keys = "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" - unix := "" - send_text := "" + var unix strings.Builder + var send_text strings.Builder for _, ch := range buf { switch { case int(ch) < len(ctrl_keys): - unix += "^" + ctrl_keys[ch:ch+1] + unix.WriteString("^" + ctrl_keys[ch:ch+1]) case ch == 127: - unix += "^?" + unix.WriteString("^?") default: - unix += string(rune(ch)) + unix.WriteString(string(rune(ch))) } } for _, ch := range string(buf) { q := fmt.Sprintf("%#v", string(ch)) - send_text += q[1 : len(q)-1] + send_text.WriteString(q[1 : len(q)-1]) } - os.Stdout.WriteString(unix + "\t\t") - os.Stdout.WriteString(ctx.Yellow(send_text) + "\r\n") + os.Stdout.WriteString(unix.String() + "\t\t") + os.Stdout.WriteString(ctx.Yellow(send_text.String()) + "\r\n") } func run_legacy_loop(opts *Options) (err error) { diff --git a/tools/cli/wcswidth_kitten.go b/tools/cli/wcswidth_kitten.go index 596ab2311..445f4733e 100644 --- a/tools/cli/wcswidth_kitten.go +++ b/tools/cli/wcswidth_kitten.go @@ -125,7 +125,7 @@ func run_tests(tests []*test_struct) (err error) { case 'c': lp.Quit(0) case 'R': - if idx := bytes.IndexByte(data, ';'); idx > -1 { + if found := bytes.Contains(data, []byte{';'}); found { if cpos, err := cpos_from_report(utils.UnsafeBytesToString(data[:len(data)-1])); err != nil { return err } else { diff --git a/tools/cmd/mouse_demo/main.go b/tools/cmd/mouse_demo/main.go index c6b2dba36..67219bc05 100644 --- a/tools/cmd/mouse_demo/main.go +++ b/tools/cmd/mouse_demo/main.go @@ -6,6 +6,7 @@ import ( "bytes" "encoding/base64" "fmt" + "slices" "strconv" "strings" @@ -261,7 +262,7 @@ func Run(args []string) (rc int, err error) { meta, payload, _ := strings.Cut(rest, ";") // Parse metadata key=value pairs separated by ':' meta_map := make(map[string]string) - for _, kv := range strings.Split(meta, ":") { + for kv := range strings.SplitSeq(meta, ":") { k, v, _ := strings.Cut(kv, "=") if k != "" { meta_map[k] = v @@ -300,19 +301,15 @@ func Run(args []string) (rc int, err error) { mimes := strings.Fields(payload) dnd.drop_mimes = mimes // Request data for text/plain first, then text/uri-list - for _, m := range mimes { - if m == "text/plain" { - dnd.collecting = "text/plain" - lp.QueueWriteString(dnd_request_data("text/plain")) - return nil - } + if slices.Contains(mimes, "text/plain") { + dnd.collecting = "text/plain" + lp.QueueWriteString(dnd_request_data("text/plain")) + return nil } - for _, m := range mimes { - if m == "text/uri-list" { - dnd.collecting = "text/uri-list" - lp.QueueWriteString(dnd_request_data("text/uri-list")) - return nil - } + if slices.Contains(mimes, "text/uri-list") { + dnd.collecting = "text/uri-list" + lp.QueueWriteString(dnd_request_data("text/uri-list")) + return nil } // Nothing to collect, signal done lp.QueueWriteString(dnd_finish()) @@ -325,25 +322,23 @@ func Run(args []string) (rc int, err error) { if dnd.collecting == "text/plain" { text := dnd.collect_buf.String() // Get first line - if idx := strings.IndexByte(text, '\n'); idx >= 0 { - dnd.plain_text = text[:idx] + if before, _, ok := strings.Cut(text, "\n"); ok { + dnd.plain_text = before } else { dnd.plain_text = text } dnd.collect_buf.Reset() // Now request text/uri-list if available - for _, m := range dnd.drop_mimes { - if m == "text/uri-list" { - dnd.collecting = "text/uri-list" - lp.QueueWriteString(dnd_request_data("text/uri-list")) - return nil - } + if slices.Contains(dnd.drop_mimes, "text/uri-list") { + dnd.collecting = "text/uri-list" + lp.QueueWriteString(dnd_request_data("text/uri-list")) + return nil } } else if dnd.collecting == "text/uri-list" { text := dnd.collect_buf.String() dnd.collect_buf.Reset() // Parse URI list: lines starting with # are comments - for _, line := range strings.Split(text, "\n") { + for line := range strings.SplitSeq(text, "\n") { line = strings.TrimRight(line, "\r") if line != "" && !strings.HasPrefix(line, "#") { dnd.uri_list = append(dnd.uri_list, line) diff --git a/tools/rsync/algorithm.go b/tools/rsync/algorithm.go index e78308bb2..7e0c88a84 100644 --- a/tools/rsync/algorithm.go +++ b/tools/rsync/algorithm.go @@ -646,10 +646,3 @@ func find_hash(hh []BlockHash, hv uint64) (uint64, bool) { } return 0, false } - -func min(a, b int) int { - if a < b { - return a - } - return b -} diff --git a/tools/tty/tty_bsd.go b/tools/tty/tty_bsd.go index ed9b335ad..5a3899815 100644 --- a/tools/tty/tty_bsd.go +++ b/tools/tty/tty_bsd.go @@ -1,6 +1,5 @@ // License: GPLv3 Copyright: 2022, Kovid Goyal, //go:build darwin || freebsd || openbsd || netbsd || dragonfly -// +build darwin freebsd openbsd netbsd dragonfly package tty diff --git a/tools/tui/readline/history.go b/tools/tui/readline/history.go index 9befa788a..c4504ca16 100644 --- a/tools/tui/readline/history.go +++ b/tools/tui/readline/history.go @@ -309,8 +309,8 @@ func (self *Readline) history_search_highlighter(text string, x, y int) string { lines := utils.Splitlines(text) for _, tok := range self.history_search.tokens { for i, line := range lines { - if idx := strings.Index(line, tok); idx > -1 { - lines[i] = line[:idx] + self.fmt_ctx.Green(tok) + line[idx+len(tok):] + if before, after, ok := strings.Cut(line, tok); ok { + lines[i] = before + self.fmt_ctx.Green(tok) + after break } } diff --git a/tools/utils/strings.go b/tools/utils/strings.go index 37bfbdad2..351f31491 100644 --- a/tools/utils/strings.go +++ b/tools/utils/strings.go @@ -69,11 +69,11 @@ func ScanFuncForSeparator(sep string) StringScannerScanFunc { } return func(data string) (remaining_data, token string) { - idx := strings.Index(data, sep) - if idx < 0 { + before, after, ok := strings.Cut(data, sep) + if !ok { return "", data } - return data[idx+len(sep):], data[:idx] + return after, before } } diff --git a/tools/utils/style/colorspaces_bench_test.go b/tools/utils/style/colorspaces_bench_test.go index 2774ab960..18b413988 100644 --- a/tools/utils/style/colorspaces_bench_test.go +++ b/tools/utils/style/colorspaces_bench_test.go @@ -9,50 +9,50 @@ import ( // Benchmark color parsing functions to demonstrate performance func BenchmarkParseOklch(b *testing.B) { - for i := 0; i < b.N; i++ { + for b.Loop() { _, _ = parseOklch("0.5 0.1 180") } } func BenchmarkParseLab(b *testing.B) { - for i := 0; i < b.N; i++ { + for b.Loop() { _, _ = parseLab("50 0 0") } } func BenchmarkParseColorHex(b *testing.B) { - for i := 0; i < b.N; i++ { + for b.Loop() { _, _ = ParseColor("#ff0000") } } func BenchmarkParseColorOklch(b *testing.B) { - for i := 0; i < b.N; i++ { + for b.Loop() { _, _ = ParseColor("oklch(0.5 0.1 180)") } } func BenchmarkParseColorLab(b *testing.B) { - for i := 0; i < b.N; i++ { + for b.Loop() { _, _ = ParseColor("lab(50 0 0)") } } func BenchmarkParseColorWithComment(b *testing.B) { - for i := 0; i < b.N; i++ { + for b.Loop() { _, _ = ParseColor("oklch(0.5 0.1 180) # vibrant color") } } // Benchmark the gamut mapping algorithm specifically func BenchmarkOklchToSrgbGamutMap(b *testing.B) { - for i := 0; i < b.N; i++ { + for b.Loop() { oklchToSrgbGamutMap(0.7, 0.4, 25) // Very saturated color requiring gamut mapping } } func BenchmarkOklchToSrgbGamutMapInGamut(b *testing.B) { - for i := 0; i < b.N; i++ { + for b.Loop() { oklchToSrgbGamutMap(0.5, 0.05, 180) // Already in gamut } } @@ -71,8 +71,7 @@ func BenchmarkParseManyColors(b *testing.B) { "green", } - b.ResetTimer() - for i := 0; i < b.N; i++ { + for b.Loop() { for _, color := range colors { _, _ = ParseColor(color) } diff --git a/tools/wcswidth/char-props-data.go b/tools/wcswidth/char-props-data.go index a67cfb776..da5257a89 100644 --- a/tools/wcswidth/char-props-data.go +++ b/tools/wcswidth/char-props-data.go @@ -388,10 +388,10 @@ var charprops_t3 = [106]CharProps{ // Array accessor function that avoids bounds checking func charprops_for(x uint32) CharProps { - t1 := uintptr(*(*uint8)(unsafe.Pointer(uintptr(unsafe.Pointer(&charprops_t1[0])) + uintptr(x>>charprops_shift)*1))) + t1 := uintptr(*(*uint8)(unsafe.Add(unsafe.Pointer(&charprops_t1[0]), uintptr(x>>charprops_shift)*1))) t1_shifted := (t1 << charprops_shift) + (uintptr(x) & charprops_mask) - t2 := uintptr(*(*uint8)(unsafe.Pointer(uintptr(unsafe.Pointer(&charprops_t2[0])) + t1_shifted*1))) - return *(*CharProps)(unsafe.Pointer(uintptr(unsafe.Pointer(&charprops_t3[0])) + t2*4)) + t2 := uintptr(*(*uint8)(unsafe.Add(unsafe.Pointer(&charprops_t2[0]), t1_shifted*1))) + return *(*CharProps)(unsafe.Add(unsafe.Pointer(&charprops_t3[0]), t2*4)) } const graphemesegmentationresult_mask = 15 @@ -3285,9 +3285,9 @@ var graphemesegmentationresult_t2 = [2880]GraphemeSegmentationResult{ // Array accessor function that avoids bounds checking func graphemesegmentationresult_for(x uint16) GraphemeSegmentationResult { - t1 := uintptr(*(*uint8)(unsafe.Pointer(uintptr(unsafe.Pointer(&graphemesegmentationresult_t1[0])) + uintptr(x>>graphemesegmentationresult_shift)*1))) + t1 := uintptr(*(*uint8)(unsafe.Add(unsafe.Pointer(&graphemesegmentationresult_t1[0]), uintptr(x>>graphemesegmentationresult_shift)*1))) t1_shifted := (t1 << graphemesegmentationresult_shift) + (uintptr(x) & graphemesegmentationresult_mask) - return *(*GraphemeSegmentationResult)(unsafe.Pointer(uintptr(unsafe.Pointer(&graphemesegmentationresult_t2[0])) + t1_shifted*2)) + return *(*GraphemeSegmentationResult)(unsafe.Add(unsafe.Pointer(&graphemesegmentationresult_t2[0]), t1_shifted*2)) } func grapheme_segmentation_key(r GraphemeSegmentationResult, ch CharProps) uint16 {