diff --git a/kittens/choose_files/main.go b/kittens/choose_files/main.go index 8881cd7fd..c4808ba0a 100644 --- a/kittens/choose_files/main.go +++ b/kittens/choose_files/main.go @@ -177,14 +177,14 @@ func (h *Handler) draw_screen() (err error) { h.lp.ClearScreen() switch h.state.screen { case NORMAL: - matches, in_progress := h.get_results() + matches, is_complete := h.get_results() h.lp.SetWindowTitle(h.state.WindowTitle()) defer func() { // so that the cursor ends up in the right place h.lp.MoveCursorTo(1, 1) h.draw_search_bar(0) }() y := SEARCH_BAR_HEIGHT - y += h.draw_results(y, 2, matches, in_progress) + y += h.draw_results(y, 2, matches, !is_complete) case SAVE_FILE: err = h.draw_save_file_name_screen() } @@ -227,8 +227,8 @@ func (h *Handler) OnInitialize() (ans string, err error) { } func (h *Handler) current_abspath() string { - matches, in_progress := h.get_results() - if len(matches) > 0 && !in_progress { + matches, _ := h.get_results() + if len(matches) > 0 { if idx := h.state.CurrentIndex(); idx < len(matches) { return matches[idx].abspath } @@ -255,8 +255,8 @@ func (h *Handler) toggle_selection() bool { } func (h *Handler) change_to_current_dir_if_possible() error { - matches, in_progress := h.get_results() - if len(matches) > 0 && !in_progress { + matches, _ := h.get_results() + if len(matches) > 0 { m := h.current_abspath() if st, err := os.Stat(m); err == nil { if !st.IsDir() { @@ -345,7 +345,8 @@ func (h *Handler) OnKeyEvent(ev *loop.KeyEvent) (err error) { func (h *Handler) OnText(text string, from_key_event, in_bracketed_paste bool) (err error) { switch h.state.screen { case NORMAL: - h.state.search_text += text + h.state.SetSearchText(h.state.SearchText() + text) + h.result_manager.set_query(h.state.SearchText()) return h.draw_screen() case SAVE_FILE: if err = h.rl.OnText(text, from_key_event, in_bracketed_paste); err == nil { diff --git a/kittens/choose_files/scan.go b/kittens/choose_files/scan.go index 7a674ed4b..9c896cf86 100644 --- a/kittens/choose_files/scan.go +++ b/kittens/choose_files/scan.go @@ -41,8 +41,12 @@ func (r *ResultItem) Set_relpath(root_dir string) { } } +func (r ResultItem) IsMatching() bool { + return r.score.Score() < uint32(math.MaxUint32) +} + func (r ResultItem) String() string { - return fmt.Sprintf("{text: %#v, abspath: %#v, is_dir: %v, positions: %#v}", r.text, r.abspath, r.ftype.IsDir(), r.positions) + return fmt.Sprintf("{text: %#v, abspath: %#v, score: %v, positions: %#v}", r.text, r.abspath, r.score.Score(), r.positions) } func (r *ResultItem) sorted_positions() []int { @@ -210,12 +214,12 @@ type FileSystemScorer struct { only_dirs bool mutex sync.Mutex renderable_results []*ResultItem - on_results func(error) + on_results func(error, bool) current_worker_wait *sync.WaitGroup scorer *fzf.FuzzyMatcher } -func NewFileSystemScorer(root_dir, query string, only_dirs bool, on_results func(error)) (ans *FileSystemScorer) { +func NewFileSystemScorer(root_dir, query string, only_dirs bool, on_results func(error, bool)) (ans *FileSystemScorer) { return &FileSystemScorer{ query: query, root_dir: root_dir, only_dirs: only_dirs, on_results: on_results, scorer: fzf.NewFuzzyMatcher(fzf.PATH_SCHEME)} @@ -244,7 +248,10 @@ func (fss *FileSystemScorer) Change_query(query string) { if fss.current_worker_wait != nil { fss.current_worker_wait.Wait() } + fss.mutex.Lock() fss.query = query + fss.renderable_results = nil + fss.mutex.Unlock() fss.Start() } @@ -255,15 +262,14 @@ func (fss *FileSystemScorer) worker(on_results chan int, worker_wait *sync.WaitG if r := recover(); r != nil { if fss.keep_going.Load() { st, qerr := utils.Format_stacktrace_on_panic(r) - fss.on_results(fmt.Errorf("%w\n%s", qerr, st)) + fss.on_results(fmt.Errorf("%w\n%s", qerr, st), true) } } else { if fss.keep_going.Load() { - fss.on_results(nil) + fss.on_results(nil, true) } } }() - offset := 0 root_dir := fss.root_dir global_min_score, global_max_score := CombinedScore(math.MaxUint64), CombinedScore(0) var idx uint32 @@ -292,18 +298,19 @@ func (fss *FileSystemScorer) worker(on_results chan int, worker_wait *sync.WaitG } } if fss.query != "" && len(rp) > 0 { - scores, err := fss.scorer.ScoreWithCache(utils.Map(func(r *ResultItem) string { return r.text }, rp), fss.query) + scores, err := fss.scorer.Score(utils.Map(func(r *ResultItem) string { return r.text }, rp), fss.query) if err != nil { return err } for i, r := range rp { r.SetScoreResult(scores[i]) } + rp = utils.Filter(rp, func(r *ResultItem) bool { return r.IsMatching() }) } min_score, max_score := CombinedScore(math.MaxUint64), CombinedScore(0) if len(rp) > 0 { slices.SortFunc(rp, func(a, b *ResultItem) int { return cmp.Compare(a.score, b.score) }) - min_score, max_score = rp[0].score, rp[len(results)-1].score + min_score, max_score = rp[0].score, rp[len(rp)-1].score } var rr []*ResultItem fss.mutex.Lock() @@ -313,19 +320,21 @@ func (fss *FileSystemScorer) worker(on_results chan int, worker_wait *sync.WaitG case min_score >= global_max_score: rr = append(existing, rp...) case max_score < global_min_score: - rr = make([]*ResultItem, len(existing)+len(rp)) + rr = make([]*ResultItem, len(existing)+len(rp), max(16*1024, len(existing)+len(rp), 2*cap(existing))) copy(rr, rp) copy(rr[len(rp):], existing) default: rr = merge_sorted_slices(existing, rp) } - fss.mutex.Lock() - fss.renderable_results = rr global_min_score = min(global_min_score, min_score) global_max_score = min(global_max_score, max_score) + fss.mutex.Lock() + fss.renderable_results = rr fss.mutex.Unlock() return } + + offset := 0 for range on_results { if !fss.keep_going.Load() { break @@ -333,11 +342,11 @@ func (fss *FileSystemScorer) worker(on_results chan int, worker_wait *sync.WaitG results := fss.scanner.Batch(offset) if len(results) > 0 || fss.scanner.Error() != nil { offset += len(results) - fss.on_results(handle_batch(results)) + fss.on_results(handle_batch(results), false) } } if fss.keep_going.Load() { - fss.on_results(handle_batch(fss.scanner.Batch(offset))) + fss.on_results(handle_batch(fss.scanner.Batch(offset)), false) } } @@ -377,7 +386,7 @@ func NewResultManager(err_chan chan error, settings Settings, WakeupMainThread f return ans } -func (m *ResultManager) on_results(err error) { +func (m *ResultManager) on_results(err error, is_finished bool) { if err != nil { m.report_errors <- err m.WakeupMainThread() @@ -385,14 +394,14 @@ func (m *ResultManager) on_results(err error) { } m.mutex.Lock() defer m.mutex.Unlock() - if time.Since(m.last_wakeup_at) > time.Millisecond*50 { + if is_finished || time.Since(m.last_wakeup_at) > time.Millisecond*50 { m.WakeupMainThread() m.last_wakeup_at = time.Now() } } func merge_sorted_slices(a, b []*ResultItem) []*ResultItem { - result := make([]*ResultItem, 0, len(a)+len(b)) + result := make([]*ResultItem, 0, 2*(len(a)+len(b))) i, j := 0, 0 for i < len(a) && j < len(b) { if a[i].score <= b[j].score { @@ -434,7 +443,7 @@ func (m *ResultManager) set_query(query string) { } } -func (h *Handler) get_results() (ans ResultsType, in_progress bool) { +func (h *Handler) get_results() (ans ResultsType, is_complete bool) { if h.result_manager.scorer == nil { return } diff --git a/kittens/choose_files/search-bar.go b/kittens/choose_files/search-bar.go index d4803566a..fd6638f46 100644 --- a/kittens/choose_files/search-bar.go +++ b/kittens/choose_files/search-bar.go @@ -55,7 +55,6 @@ func (h *Handler) draw_search_bar(y int) { h.draw_frame(available_width, SEARCH_BAR_HEIGHT) h.lp.MoveCursorTo(1+left_margin+1, 2+y) h.draw_search_text(available_width - 2) - return } func (h *Handler) handle_edit_keys(ev *loop.KeyEvent) bool {