diff --git a/doc/telescope.txt b/doc/telescope.txt index ea3126c..9559767 100644 --- a/doc/telescope.txt +++ b/doc/telescope.txt @@ -74,6 +74,20 @@ telescope.setup({opts}) *telescope.setup()* - "descending" (default) - "ascending" + *telescope.defaults.tiebreak* + tiebreak: ~ + A function that determines how to break a tie when two entries have + the same score. + Having a function that always returns false would keep the entries in + the order they are found, so existing_entry before current_entry. + Vice versa always returning true would place the current_entry + before the existing_entry. + + Signature: function(current_entry, existing_entry, prompt) -> boolean + + Default: function that breaks the tie based on the length of the + entry's ordinal + *telescope.defaults.selection_strategy* selection_strategy: ~ Determines how the cursor acts after each sort iteration. diff --git a/lua/telescope/config.lua b/lua/telescope/config.lua index 043ac8a..5bf8602 100644 --- a/lua/telescope/config.lua +++ b/lua/telescope/config.lua @@ -153,6 +153,25 @@ append( - "ascending"]] ) +append( + "tiebreak", + function(current_entry, existing_entry, _) + return #current_entry.ordinal < #existing_entry.ordinal + end, + [[ + A function that determines how to break a tie when two entries have + the same score. + Having a function that always returns false would keep the entries in + the order they are found, so existing_entry before current_entry. + Vice versa always returning true would place the current_entry + before the existing_entry. + + Signature: function(current_entry, existing_entry, prompt) -> boolean + + Default: function that breaks the tie based on the length of the + entry's ordinal]] +) + append( "selection_strategy", "reset", diff --git a/lua/telescope/entry_manager.lua b/lua/telescope/entry_manager.lua index 1e94c80..a8331e4 100644 --- a/lua/telescope/entry_manager.lua +++ b/lua/telescope/entry_manager.lua @@ -105,7 +105,7 @@ function EntryManager:_append_container(picker, new_container, should_update) end end -function EntryManager:add_entry(picker, score, entry) +function EntryManager:add_entry(picker, score, entry, prompt) score = score or 0 local max_res = self.max_results @@ -137,7 +137,7 @@ function EntryManager:add_entry(picker, score, entry) return self:_insert_container_before(picker, index, node, new_container) end - if score < 1 and container[2] == score and #entry.ordinal < #container[1].ordinal then + if score < 1 and container[2] == score and picker.tiebreak(entry, container[1], prompt) then return self:_insert_container_before(picker, index, node, new_container) end diff --git a/lua/telescope/pickers.lua b/lua/telescope/pickers.lua index 9385a49..6e966a5 100644 --- a/lua/telescope/pickers.lua +++ b/lua/telescope/pickers.lua @@ -105,6 +105,7 @@ function Picker:new(opts) scroll_strategy = get_default(opts.scroll_strategy, config.values.scroll_strategy), sorting_strategy = get_default(opts.sorting_strategy, config.values.sorting_strategy), + tiebreak = get_default(opts.tiebreak, config.values.tiebreak), selection_strategy = get_default(opts.selection_strategy, config.values.selection_strategy), layout_strategy = layout_strategy, @@ -1210,7 +1211,8 @@ function Picker:get_result_processor(find_id, prompt, status_updater) local count = 0 local cb_add = function(score, entry) - self.manager:add_entry(self, score, entry) + -- may need the prompt for tiebreak + self.manager:add_entry(self, score, entry, prompt) status_updater { completed = false } end diff --git a/lua/tests/automated/entry_manager_spec.lua b/lua/tests/automated/entry_manager_spec.lua index bae23bc..6d2b5d3 100644 --- a/lua/tests/automated/entry_manager_spec.lua +++ b/lua/tests/automated/entry_manager_spec.lua @@ -6,7 +6,7 @@ describe("process_result", function() it("works with one entry", function() local manager = EntryManager:new(5, nil) - manager:add_entry(nil, 1, "hello") + manager:add_entry(nil, 1, "hello", "") eq(1, manager:get_score(1)) end) @@ -14,8 +14,8 @@ describe("process_result", function() it("works with two entries", function() local manager = EntryManager:new(5, nil) - manager:add_entry(nil, 1, "hello") - manager:add_entry(nil, 2, "later") + manager:add_entry(nil, 1, "hello", "") + manager:add_entry(nil, 2, "later", "") eq(2, manager.linked_states.size) @@ -30,7 +30,7 @@ describe("process_result", function() end) assert(called_count == 0) - manager:add_entry(nil, 1, "hello") + manager:add_entry(nil, 1, "hello", "") assert(called_count == 1) end) @@ -41,8 +41,8 @@ describe("process_result", function() end) assert(called_count == 0) - manager:add_entry(nil, 1, "hello") - manager:add_entry(nil, 2, "world") + manager:add_entry(nil, 1, "hello", "") + manager:add_entry(nil, 2, "world", "") assert(called_count == 2) end) @@ -51,8 +51,8 @@ describe("process_result", function() local manager = EntryManager:new(5, function() called_count = called_count + 1 end) - manager:add_entry(nil, 5, "worse result") - manager:add_entry(nil, 2, "better result") + manager:add_entry(nil, 5, "worse result", "") + manager:add_entry(nil, 2, "better result", "") eq("better result", manager:get_entry(1)) eq("worse result", manager:get_entry(2)) @@ -65,8 +65,8 @@ describe("process_result", function() local manager = EntryManager:new(1, function() called_count = called_count + 1 end) - manager:add_entry(nil, 2, "better result") - manager:add_entry(nil, 5, "worse result") + manager:add_entry(nil, 2, "better result", "") + manager:add_entry(nil, 5, "worse result", "") eq("better result", manager:get_entry(1)) eq(1, called_count) @@ -92,7 +92,8 @@ describe("process_result", function() rawset(t, k, val) return val end, - }) + }), + "" ) eq("wow", manager:get_ordinal(1)) @@ -105,9 +106,9 @@ describe("process_result", function() it("should not loop a bunch", function() local info = {} local manager = EntryManager:new(5, nil, info) - manager:add_entry(nil, 4, "better result") - manager:add_entry(nil, 3, "better result") - manager:add_entry(nil, 2, "better result") + manager:add_entry(nil, 4, "better result", "") + manager:add_entry(nil, 3, "better result", "") + manager:add_entry(nil, 2, "better result", "") -- Loops once to find 3 < 4 -- Loops again to find 2 < 3 @@ -117,9 +118,9 @@ describe("process_result", function() it("should not loop a bunch, part 2", function() local info = {} local manager = EntryManager:new(5, nil, info) - manager:add_entry(nil, 4, "better result") - manager:add_entry(nil, 2, "better result") - manager:add_entry(nil, 3, "better result") + manager:add_entry(nil, 4, "better result", "") + manager:add_entry(nil, 2, "better result", "") + manager:add_entry(nil, 3, "better result", "") -- Loops again to find 2 < 4 -- Loops once to find 3 > 2 @@ -129,9 +130,9 @@ describe("process_result", function() it("should update worst score in all append case", function() local manager = EntryManager:new(2, nil) - manager:add_entry(nil, 2, "result 2") - manager:add_entry(nil, 3, "result 3") - manager:add_entry(nil, 4, "result 4") + manager:add_entry(nil, 2, "result 2", "") + manager:add_entry(nil, 3, "result 3", "") + manager:add_entry(nil, 4, "result 4", "") eq(3, manager.worst_acceptable_score) end) @@ -141,9 +142,9 @@ describe("process_result", function() local manager = EntryManager:new(2, function() called_count = called_count + 1 end) - manager:add_entry(nil, 5, "worse result") - manager:add_entry(nil, 4, "less worse result") - manager:add_entry(nil, 2, "better result") + manager:add_entry(nil, 5, "worse result", "") + manager:add_entry(nil, 4, "less worse result", "") + manager:add_entry(nil, 2, "better result", "") -- Once for insert 5 -- Once for prepend 4 @@ -153,4 +154,36 @@ describe("process_result", function() eq("better result", manager:get_entry(1)) eq(4, manager.worst_acceptable_score) end) + + it("should call tiebreaker if score is the same, sort length", function() + local manager = EntryManager:new(5, nil) + local picker = { + tiebreak = function(curr, prev, prompt) + eq("asdf", prompt) + return #curr < #prev + end, + } + + manager:add_entry(picker, 0.5, "same same", "asdf") + manager:add_entry(picker, 0.5, "same", "asdf") + + eq("same", manager:get_entry(1)) + eq("same same", manager:get_entry(2)) + end) + + it("should call tiebreaker if score is the same, keep initial", function() + local manager = EntryManager:new(5, nil) + local picker = { + tiebreak = function(_, _, prompt) + eq("asdf", prompt) + return false + end, + } + + manager:add_entry(picker, 0.5, "same same", "asdf") + manager:add_entry(picker, 0.5, "same", "asdf") + + eq("same", manager:get_entry(2)) + eq("same same", manager:get_entry(1)) + end) end)