diff --git a/tools/tui/subseq/score.go b/tools/tui/subseq/score.go index bc71444ce..ba8e0fa94 100644 --- a/tools/tui/subseq/score.go +++ b/tools/tui/subseq/score.go @@ -8,7 +8,6 @@ import ( "strings" "github.com/kovidgoyal/kitty/tools/utils" - "github.com/kovidgoyal/kitty/tools/utils/images" ) var _ = fmt.Print @@ -202,10 +201,7 @@ func score_item(item string, idx int, needle []rune, opts *resolved_options_type } func ScoreItems(query string, items []string, opts Options) []*Match { - ctx := images.Context{} - ctx.SetNumberOfThreads(opts.NumberOfThreads) ans := make([]*Match, len(items)) - results := make(chan *Match, len(items)) nr := []rune(strings.ToLower(query)) if opts.Level1 == "" { opts.Level1 = LEVEL1 @@ -219,15 +215,12 @@ func ScoreItems(query string, items []string, opts Options) []*Match { ropts := resolved_options_type{ level1: []rune(opts.Level1), level2: []rune(opts.Level2), level3: []rune(opts.Level3), } - ctx.Parallel(0, len(items), func(nums <-chan int) { + utils.Run_in_parallel_over_range(opts.NumberOfThreads, func(start, limit int) error { w := workspace_type{} - for i := range nums { - results <- score_item(items[i], i, nr, &ropts, &w) + for i := start; i < limit; i++ { + ans[i] = score_item(items[i], i, nr, &ropts, &w) } - }) - close(results) - for x := range results { - ans[x.idx] = x - } + return nil + }, 0, len(items)) return ans } diff --git a/tools/utils/misc.go b/tools/utils/misc.go index 12470f60f..dbae484b4 100644 --- a/tools/utils/misc.go +++ b/tools/utils/misc.go @@ -12,6 +12,7 @@ import ( "slices" "strconv" "strings" + "sync" "golang.org/x/exp/constraints" ) @@ -86,6 +87,42 @@ func Format_stacktrace_on_panic(r any) (text string, err error) { return strings.TrimSpace(text), err } +// Run the specified function in parallel over chunks from the specified range. +// If the function panics, it is turned into a regular error. +func Run_in_parallel_over_range(num_procs int, f func(int, int) error, start, limit int) (err error) { + num_items := limit - start + if num_procs <= 0 { + num_procs = runtime.NumCPU() + } + num_procs = max(1, min(num_procs, num_items)) + chunk_sz := max(1, num_items/num_procs) + var wg sync.WaitGroup + echan := make(chan error, num_procs) + for start < limit { + end := min(start+chunk_sz, limit) + wg.Add(1) + go func(start, end int) { + defer func() { + if r := recover(); r != nil { + stacktrace, e := Format_stacktrace_on_panic(r) + echan <- fmt.Errorf("%s\n\n%w", stacktrace, e) + } + wg.Done() + }() + if err := f(start, end); err != nil { + echan <- err + } + }(start, end) + start = end + } + wg.Wait() + close(echan) + for qerr := range echan { + return qerr + } + return + +} func Map[T any, O any](f func(x T) O, s []T) []O { ans := make([]O, 0, len(s)) for _, x := range s {