Modernize Go code

This commit is contained in:
Kovid Goyal
2026-03-10 14:47:41 +05:30
parent 3d13cf1ca5
commit eddaaed3e3
16 changed files with 64 additions and 87 deletions

View File

@@ -96,8 +96,8 @@ func get_dependencies(path string) (ans []dependency) {
for _, line := range lines("otool", "-L", path) { for _, line := range lines("otool", "-L", path) {
line = strings.TrimSpace(line) line = strings.TrimSpace(line)
if strings.Contains(line, "compatibility") && !strings.HasSuffix(line, ":") { if strings.Contains(line, "compatibility") && !strings.HasSuffix(line, ":") {
idx := strings.IndexByte(line, '(') before, _, _ := strings.Cut(line, "(")
dep := strings.TrimSpace(line[:idx]) dep := strings.TrimSpace(before)
ans = append(ans, dependency{path: dep, is_id: dep == install_name}) ans = append(ans, dependency{path: dep, is_id: dep == install_name})
} }
} }

View File

@@ -342,9 +342,9 @@ func GetChoices(o *Options) (response string, err error) {
lp.QueueWriteString(strings.Repeat("\r\n", y)) lp.QueueWriteString(strings.Repeat("\r\n", y))
for _, line := range msg_lines { for _, line := range msg_lines {
if replacement_text != "" { if replacement_text != "" {
idx := strings.Index(line, replacement_text) before, _, ok := strings.Cut(line, replacement_text)
if idx > -1 { if ok {
x := wcswidth.Stringwidth(line[:idx]) x := wcswidth.Stringwidth(before)
replacement_range = Range{x, x + wcswidth.Stringwidth(replacement_text), y} replacement_range = Range{x, x + wcswidth.Stringwidth(replacement_text), y}
} }
} }

View File

@@ -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} st := loop.SizedText{Subscale_denominator: 2, Subscale_numerator: 1, Vertical_alignment: 2, Width: 1}
graphemes := wcswidth.SplitIntoGraphemes(m) graphemes := wcswidth.SplitIntoGraphemes(m)
for len(graphemes) > 0 { for len(graphemes) > 0 {
s := "" var s strings.Builder
for w := 0; w < 2 && len(graphemes) > 0; { for w := 0; w < 2 && len(graphemes) > 0; {
w += wcswidth.Stringwidth(graphemes[0]) w += wcswidth.Stringwidth(graphemes[0])
s += graphemes[0] s.WriteString(graphemes[0])
graphemes = graphemes[1:] graphemes = graphemes[1:]
} }
h.lp.DrawSizedText(s, st) h.lp.DrawSizedText(s.String(), st)
} }
if spinner != "" { if spinner != "" {
h.lp.QueueWriteString(spinner) h.lp.QueueWriteString(spinner)

View File

@@ -388,11 +388,9 @@ func run_get_loop(opts *Options, args []string) (err error) {
if getting_data_for != current_mime { if getting_data_for != current_mime {
if prev := requested_mimes[getting_data_for]; prev != nil && !prev.all_data_received { if prev := requested_mimes[getting_data_for]; prev != nil && !prev.all_data_received {
prev.all_data_received = true prev.all_data_received = true
wg.Add(1) wg.Go(func() {
go func() {
prev.commit() prev.commit()
wg.Done() })
}()
} }
getting_data_for = current_mime getting_data_for = current_mime
@@ -405,11 +403,9 @@ func run_get_loop(opts *Options, args []string) (err error) {
case "DONE": case "DONE":
if prev := requested_mimes[getting_data_for]; getting_data_for != "" && prev != nil && !prev.all_data_received { if prev := requested_mimes[getting_data_for]; getting_data_for != "" && prev != nil && !prev.all_data_received {
prev.all_data_received = true prev.all_data_received = true
wg.Add(1) wg.Go(func() {
go func() {
prev.commit() prev.commit()
wg.Done() })
}()
getting_data_for = "" getting_data_for = ""
} }
lp.Quit(0) lp.Quit(0)

View File

@@ -255,7 +255,7 @@ func (h *Handler) updateFilter() {
// Score each column independently // Score each column independently
colResults := [3][]fzf.Result{} colResults := [3][]fzf.Result{}
for c := 0; c < 3; c++ { for c := range 3 {
results, err := h.matcher.Score(colSlices[c], h.query) results, err := h.matcher.Score(colSlices[c], h.query)
if err == nil { if err == nil {
colResults[c] = results colResults[c] = results
@@ -276,7 +276,7 @@ func (h *Handler) updateFilter() {
bestScore := uint(0) bestScore := uint(0)
bestCol := 0 bestCol := 0
var bestPositions []int 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 { if colResults[c] != nil && i < len(colResults[c]) && colResults[c][i].Score > bestScore {
bestScore = colResults[c][i].Score bestScore = colResults[c][i].Score
bestCol = c bestCol = c
@@ -357,10 +357,7 @@ func (h *Handler) draw_screen() {
resultsStartY := 2 resultsStartY := 2
helpY := height - 1 helpY := height - 1
hintsY := height hintsY := height
resultsHeight := helpY - resultsStartY resultsHeight := max(helpY-resultsStartY, 1)
if resultsHeight < 1 {
resultsHeight = 1
}
h.results_start_y = resultsStartY h.results_start_y = resultsStartY
h.results_height = resultsHeight h.results_height = resultsHeight
@@ -381,10 +378,7 @@ func (h *Handler) draw_screen() {
h.lp.MoveCursorTo(1, helpY) h.lp.MoveCursorTo(1, helpY)
if b := h.selectedBinding(); b != nil && b.Help != "" { if b := h.selectedBinding(); b != nil && b.Help != "" {
helpStr := b.Help helpStr := b.Help
maxLen := width - 2 maxLen := max(width-2, 3)
if maxLen < 3 {
maxLen = 3
}
if wcswidth.Stringwidth(helpStr) > maxLen { if wcswidth.Stringwidth(helpStr) > maxLen {
// Truncate by runes to avoid breaking multi-byte characters // Truncate by runes to avoid breaking multi-byte characters
runes := []rune(helpStr) runes := []rune(helpStr)

View File

@@ -65,7 +65,7 @@ func TestFlattenAllBindings(t *testing.T) {
func TestDefaultModeComesFirst(t *testing.T) { func TestDefaultModeComesFirst(t *testing.T) {
h := newTestHandler() h := newTestHandler()
// First 5 items should be from default mode // 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 != "" { 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) t.Fatalf("Item %d should be from default mode, got mode=%q", i, h.all_items[i].binding.Mode)
} }

View File

@@ -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 // 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. // sense to highlight the single changed central region of the whole line.
max_idx := max(len(left_words), len(right_words)) 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] lc := idx < len(left_words) && left_changed_abs[idx]
rc := idx < len(right_words) && right_changed_abs[idx] rc := idx < len(right_words) && right_changed_abs[idx]
if lc != rc { if lc != rc {

View File

@@ -9,6 +9,7 @@ import (
"github.com/kovidgoyal/kitty/tools/tty" "github.com/kovidgoyal/kitty/tools/tty"
"io" "io"
"os" "os"
"strings"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
) )
@@ -17,24 +18,24 @@ var _ = fmt.Print
func print_key(buf []byte, ctx *markup.Context) { func print_key(buf []byte, ctx *markup.Context) {
const ctrl_keys = "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" const ctrl_keys = "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
unix := "" var unix strings.Builder
send_text := "" var send_text strings.Builder
for _, ch := range buf { for _, ch := range buf {
switch { switch {
case int(ch) < len(ctrl_keys): case int(ch) < len(ctrl_keys):
unix += "^" + ctrl_keys[ch:ch+1] unix.WriteString("^" + ctrl_keys[ch:ch+1])
case ch == 127: case ch == 127:
unix += "^?" unix.WriteString("^?")
default: default:
unix += string(rune(ch)) unix.WriteString(string(rune(ch)))
} }
} }
for _, ch := range string(buf) { for _, ch := range string(buf) {
q := fmt.Sprintf("%#v", string(ch)) 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(unix.String() + "\t\t")
os.Stdout.WriteString(ctx.Yellow(send_text) + "\r\n") os.Stdout.WriteString(ctx.Yellow(send_text.String()) + "\r\n")
} }
func run_legacy_loop(opts *Options) (err error) { func run_legacy_loop(opts *Options) (err error) {

View File

@@ -125,7 +125,7 @@ func run_tests(tests []*test_struct) (err error) {
case 'c': case 'c':
lp.Quit(0) lp.Quit(0)
case 'R': 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 { if cpos, err := cpos_from_report(utils.UnsafeBytesToString(data[:len(data)-1])); err != nil {
return err return err
} else { } else {

View File

@@ -6,6 +6,7 @@ import (
"bytes" "bytes"
"encoding/base64" "encoding/base64"
"fmt" "fmt"
"slices"
"strconv" "strconv"
"strings" "strings"
@@ -261,7 +262,7 @@ func Run(args []string) (rc int, err error) {
meta, payload, _ := strings.Cut(rest, ";") meta, payload, _ := strings.Cut(rest, ";")
// Parse metadata key=value pairs separated by ':' // Parse metadata key=value pairs separated by ':'
meta_map := make(map[string]string) meta_map := make(map[string]string)
for _, kv := range strings.Split(meta, ":") { for kv := range strings.SplitSeq(meta, ":") {
k, v, _ := strings.Cut(kv, "=") k, v, _ := strings.Cut(kv, "=")
if k != "" { if k != "" {
meta_map[k] = v meta_map[k] = v
@@ -300,19 +301,15 @@ func Run(args []string) (rc int, err error) {
mimes := strings.Fields(payload) mimes := strings.Fields(payload)
dnd.drop_mimes = mimes dnd.drop_mimes = mimes
// Request data for text/plain first, then text/uri-list // Request data for text/plain first, then text/uri-list
for _, m := range mimes { if slices.Contains(mimes, "text/plain") {
if m == "text/plain" { dnd.collecting = "text/plain"
dnd.collecting = "text/plain" lp.QueueWriteString(dnd_request_data("text/plain"))
lp.QueueWriteString(dnd_request_data("text/plain")) return nil
return nil
}
} }
for _, m := range mimes { if slices.Contains(mimes, "text/uri-list") {
if m == "text/uri-list" { dnd.collecting = "text/uri-list"
dnd.collecting = "text/uri-list" lp.QueueWriteString(dnd_request_data("text/uri-list"))
lp.QueueWriteString(dnd_request_data("text/uri-list")) return nil
return nil
}
} }
// Nothing to collect, signal done // Nothing to collect, signal done
lp.QueueWriteString(dnd_finish()) lp.QueueWriteString(dnd_finish())
@@ -325,25 +322,23 @@ func Run(args []string) (rc int, err error) {
if dnd.collecting == "text/plain" { if dnd.collecting == "text/plain" {
text := dnd.collect_buf.String() text := dnd.collect_buf.String()
// Get first line // Get first line
if idx := strings.IndexByte(text, '\n'); idx >= 0 { if before, _, ok := strings.Cut(text, "\n"); ok {
dnd.plain_text = text[:idx] dnd.plain_text = before
} else { } else {
dnd.plain_text = text dnd.plain_text = text
} }
dnd.collect_buf.Reset() dnd.collect_buf.Reset()
// Now request text/uri-list if available // Now request text/uri-list if available
for _, m := range dnd.drop_mimes { if slices.Contains(dnd.drop_mimes, "text/uri-list") {
if m == "text/uri-list" { dnd.collecting = "text/uri-list"
dnd.collecting = "text/uri-list" lp.QueueWriteString(dnd_request_data("text/uri-list"))
lp.QueueWriteString(dnd_request_data("text/uri-list")) return nil
return nil
}
} }
} else if dnd.collecting == "text/uri-list" { } else if dnd.collecting == "text/uri-list" {
text := dnd.collect_buf.String() text := dnd.collect_buf.String()
dnd.collect_buf.Reset() dnd.collect_buf.Reset()
// Parse URI list: lines starting with # are comments // 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") line = strings.TrimRight(line, "\r")
if line != "" && !strings.HasPrefix(line, "#") { if line != "" && !strings.HasPrefix(line, "#") {
dnd.uri_list = append(dnd.uri_list, line) dnd.uri_list = append(dnd.uri_list, line)

View File

@@ -646,10 +646,3 @@ func find_hash(hh []BlockHash, hv uint64) (uint64, bool) {
} }
return 0, false return 0, false
} }
func min(a, b int) int {
if a < b {
return a
}
return b
}

View File

@@ -1,6 +1,5 @@
// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net> // License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>
//go:build darwin || freebsd || openbsd || netbsd || dragonfly //go:build darwin || freebsd || openbsd || netbsd || dragonfly
// +build darwin freebsd openbsd netbsd dragonfly
package tty package tty

View File

@@ -309,8 +309,8 @@ func (self *Readline) history_search_highlighter(text string, x, y int) string {
lines := utils.Splitlines(text) lines := utils.Splitlines(text)
for _, tok := range self.history_search.tokens { for _, tok := range self.history_search.tokens {
for i, line := range lines { for i, line := range lines {
if idx := strings.Index(line, tok); idx > -1 { if before, after, ok := strings.Cut(line, tok); ok {
lines[i] = line[:idx] + self.fmt_ctx.Green(tok) + line[idx+len(tok):] lines[i] = before + self.fmt_ctx.Green(tok) + after
break break
} }
} }

View File

@@ -69,11 +69,11 @@ func ScanFuncForSeparator(sep string) StringScannerScanFunc {
} }
return func(data string) (remaining_data, token string) { return func(data string) (remaining_data, token string) {
idx := strings.Index(data, sep) before, after, ok := strings.Cut(data, sep)
if idx < 0 { if !ok {
return "", data return "", data
} }
return data[idx+len(sep):], data[:idx] return after, before
} }
} }

View File

@@ -9,50 +9,50 @@ import (
// Benchmark color parsing functions to demonstrate performance // Benchmark color parsing functions to demonstrate performance
func BenchmarkParseOklch(b *testing.B) { func BenchmarkParseOklch(b *testing.B) {
for i := 0; i < b.N; i++ { for b.Loop() {
_, _ = parseOklch("0.5 0.1 180") _, _ = parseOklch("0.5 0.1 180")
} }
} }
func BenchmarkParseLab(b *testing.B) { func BenchmarkParseLab(b *testing.B) {
for i := 0; i < b.N; i++ { for b.Loop() {
_, _ = parseLab("50 0 0") _, _ = parseLab("50 0 0")
} }
} }
func BenchmarkParseColorHex(b *testing.B) { func BenchmarkParseColorHex(b *testing.B) {
for i := 0; i < b.N; i++ { for b.Loop() {
_, _ = ParseColor("#ff0000") _, _ = ParseColor("#ff0000")
} }
} }
func BenchmarkParseColorOklch(b *testing.B) { func BenchmarkParseColorOklch(b *testing.B) {
for i := 0; i < b.N; i++ { for b.Loop() {
_, _ = ParseColor("oklch(0.5 0.1 180)") _, _ = ParseColor("oklch(0.5 0.1 180)")
} }
} }
func BenchmarkParseColorLab(b *testing.B) { func BenchmarkParseColorLab(b *testing.B) {
for i := 0; i < b.N; i++ { for b.Loop() {
_, _ = ParseColor("lab(50 0 0)") _, _ = ParseColor("lab(50 0 0)")
} }
} }
func BenchmarkParseColorWithComment(b *testing.B) { func BenchmarkParseColorWithComment(b *testing.B) {
for i := 0; i < b.N; i++ { for b.Loop() {
_, _ = ParseColor("oklch(0.5 0.1 180) # vibrant color") _, _ = ParseColor("oklch(0.5 0.1 180) # vibrant color")
} }
} }
// Benchmark the gamut mapping algorithm specifically // Benchmark the gamut mapping algorithm specifically
func BenchmarkOklchToSrgbGamutMap(b *testing.B) { 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 oklchToSrgbGamutMap(0.7, 0.4, 25) // Very saturated color requiring gamut mapping
} }
} }
func BenchmarkOklchToSrgbGamutMapInGamut(b *testing.B) { func BenchmarkOklchToSrgbGamutMapInGamut(b *testing.B) {
for i := 0; i < b.N; i++ { for b.Loop() {
oklchToSrgbGamutMap(0.5, 0.05, 180) // Already in gamut oklchToSrgbGamutMap(0.5, 0.05, 180) // Already in gamut
} }
} }
@@ -71,8 +71,7 @@ func BenchmarkParseManyColors(b *testing.B) {
"green", "green",
} }
b.ResetTimer() for b.Loop() {
for i := 0; i < b.N; i++ {
for _, color := range colors { for _, color := range colors {
_, _ = ParseColor(color) _, _ = ParseColor(color)
} }

View File

@@ -388,10 +388,10 @@ var charprops_t3 = [106]CharProps{
// Array accessor function that avoids bounds checking // Array accessor function that avoids bounds checking
func charprops_for(x uint32) CharProps { 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) t1_shifted := (t1 << charprops_shift) + (uintptr(x) & charprops_mask)
t2 := uintptr(*(*uint8)(unsafe.Pointer(uintptr(unsafe.Pointer(&charprops_t2[0])) + t1_shifted*1))) t2 := uintptr(*(*uint8)(unsafe.Add(unsafe.Pointer(&charprops_t2[0]), t1_shifted*1)))
return *(*CharProps)(unsafe.Pointer(uintptr(unsafe.Pointer(&charprops_t3[0])) + t2*4)) return *(*CharProps)(unsafe.Add(unsafe.Pointer(&charprops_t3[0]), t2*4))
} }
const graphemesegmentationresult_mask = 15 const graphemesegmentationresult_mask = 15
@@ -3285,9 +3285,9 @@ var graphemesegmentationresult_t2 = [2880]GraphemeSegmentationResult{
// Array accessor function that avoids bounds checking // Array accessor function that avoids bounds checking
func graphemesegmentationresult_for(x uint16) GraphemeSegmentationResult { 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) 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 { func grapheme_segmentation_key(r GraphemeSegmentationResult, ch CharProps) uint16 {