Skip to content
This repository was archived by the owner on Apr 17, 2025. It is now read-only.

Commit a4a394c

Browse files
authored
Merge pull request #412 from hinell/master
feat(ui): keep track of recently used item
2 parents 76f96aa + 33e24d1 commit a4a394c

File tree

5 files changed

+61
-29
lines changed

5 files changed

+61
-29
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -385,7 +385,8 @@ require('legendary').setup({
385385
-- }
386386
-- })
387387
sort = {
388-
-- sort most recently used item to the top
388+
-- put most recently selected item first, this works
389+
-- both within global and item group lists
389390
most_recent_first = true,
390391
-- sort user-defined items before built-in items
391392
user_items_first = true,

lua/legendary/api/executor.lua

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
local Toolbox = require('legendary.toolbox')
22
local Log = require('legendary.log')
33
local Config = require('legendary.config')
4+
local State = require('legendary.data.state')
45
local util = require('legendary.util')
56

67
local function update_item_frecency_score(item)
@@ -83,6 +84,7 @@ end
8384
function M.exec_item(item, context)
8485
vim.schedule(function()
8586
M.restore_context(context, function()
87+
State.last_executed_item = item
8688
update_item_frecency_score(item)
8789
if Toolbox.is_function(item) then
8890
item.implementation()
@@ -122,12 +124,11 @@ end
122124
---still return true.
123125
---@param ignore_filters boolean|nil whether to ignore the filters used when selecting the item, default false
124126
function M.repeat_previous(ignore_filters)
125-
local State = require('legendary.data.state')
126-
if State.most_recent_item then
127+
if State.last_executed_item then
127128
if not ignore_filters and State.most_recent_filters then
128129
for _, filter in ipairs(State.most_recent_filters) do
129130
-- if any filter does not match, abort executions
130-
local err, matches = pcall(filter, State.most_recent_item)
131+
local err, matches = pcall(filter, State.last_executed_item)
131132
if not err and not matches then
132133
Log.warn(
133134
'Previously executed item no longer matches previously used filters, use `:LegendaryRepeat!`'
@@ -138,7 +139,7 @@ function M.repeat_previous(ignore_filters)
138139
end
139140
end
140141
local context = M.build_context()
141-
M.exec_item(State.most_recent_item, context)
142+
M.exec_item(State.last_executed_item, context)
142143
end
143144
end
144145

lua/legendary/data/itemlist.lua

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ local Log = require('legendary.log')
1414
---@field private sorted boolean
1515
local ItemList = class('ItemList')
1616

17+
ItemList.TOPLEVEL_LIST_ID = 'toplevel'
18+
1719
---@private
1820
function ItemList:initialize()
1921
self.items = {}
@@ -106,16 +108,31 @@ function ItemList:filter(filters, context)
106108
end, 'Took %s ms to filter items in context.')
107109
end
108110

111+
---@class ItemListSortInplaceOpts
112+
---@field itemgroup string
113+
109114
---Sort the list *IN PLACE* according to config.
110115
---THIS MODIFIES THE LIST IN PLACE.
111-
function ItemList:sort_inplace()
116+
--- @param opts ItemListSortInplaceOpts
117+
function ItemList:sort_inplace(opts)
112118
-- inline require to avoid circular dependency
113119
local State = require('legendary.data.state')
114-
local opts = Config.sort
120+
vim.validate({
121+
itemgroup = { opts.itemgroup, 'string', true },
122+
})
123+
124+
-- Merge Config into local opts
125+
opts = vim.tbl_extend('keep', opts, Config.sort)
115126

116127
-- if no items have been added, and the most recent item has not changed,
117128
-- we're already sorted
118-
if self.sorted and (not opts.most_recent_first or (self.items[1] == State.most_recent_item)) then
129+
if
130+
self.sorted
131+
and (
132+
not opts.most_recent_first
133+
or (self.items[1] == State.itemgroup_history[opts.itemgroup or ItemList.TOPLEVEL_LIST_ID])
134+
)
135+
then
119136
return
120137
end
121138

@@ -179,7 +196,7 @@ function ItemList:sort_inplace()
179196
end
180197

181198
if opts.most_recent_first then
182-
if item1 == State.most_recent_item then
199+
if item1 == State.itemgroup_history[opts.itemgroup or ItemList.TOPLEVEL_LIST_ID] then
183200
return true
184201
end
185202
end
@@ -213,9 +230,11 @@ function ItemList:sort_inplace()
213230
-- sort by most recent last, and after other sorts are done
214231
-- if most recent is already at top, nothing to do, and attempting to sort will cause
215232
-- an error since it doesn't need to be sorted
216-
if opts.most_recent_first and State.most_recent_item and State.most_recent_item ~= self.items[1] then
233+
if
234+
opts.most_recent_first and State.itemgroup_history[opts.itemgroup or ItemList.TOPLEVEL_LIST_ID] ~= self.items[1]
235+
then
217236
items = Sorter.mergesort(items, function(item)
218-
return item == State.most_recent_item
237+
return item == State.itemgroup_history[opts.itemgroup or ItemList.TOPLEVEL_LIST_ID]
219238
end)
220239
end
221240

lua/legendary/data/state.lua

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@ local ItemList = require('legendary.data.itemlist')
22

33
---@class LegendaryState
44
---@field items ItemList
5-
---@field most_recent_item LegendaryItem|nil
5+
---@field last_executed_item LegendaryItem|nil
66
---@field most_recent_filters LegendaryItemFilter[]|nil
7+
---@field itemgroup_history table<string, LegendaryItem>
78
local M = {}
89

910
M.items = ItemList:create()
10-
M.most_recent_item = nil
11+
M.last_executed_item = nil
1112
M.most_recent_filters = nil
13+
M.itemgroup_history = {}
1214

1315
return M

lua/legendary/ui/init.lua

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@ local Toolbox = require('legendary.toolbox')
66
local Format = require('legendary.ui.format')
77
local Executor = require('legendary.api.executor')
88
local Log = require('legendary.log')
9+
local ItemList = require('legendary.data.itemlist')
910

1011
---@class LegendaryUi
1112
---@field select fun(opts:LegendaryFindOpts)
1213
local M = {}
1314

14-
---@class LegendaryFindOpts
15+
---@class LegendaryFindOpts : ItemListSortInplaceOpts
1516
---@field itemgroup string Find items in this item group only
1617
---@field filters LegendaryItemFilter[]
1718
---@field select_prompt string|fun():string
@@ -23,25 +24,26 @@ local M = {}
2324
---@overload fun(opts:LegendaryFindOpts,context:LegendaryEditorContext)
2425
local function select_inner(opts, context, itemlist)
2526
opts = opts or {}
27+
28+
vim.validate({
29+
itemgroup = { opts.itemgroup, 'string', true },
30+
select_prompt = { opts.select_prompt, 'function', true },
31+
})
32+
2633
if itemlist then
27-
Log.trace('Relaunching select UI for an item group')
28-
else
2934
Log.trace('Launching select UI')
30-
end
31-
32-
-- if no itemlist passed
33-
if itemlist == nil then
34-
-- if an item group is specified, use that
35+
else
36+
Log.trace('Relaunching select UI for an item group')
37+
-- if no itemlist passed, try to use itemgroup
38+
-- if an item group id is specified, use that
3539
local itemgroup = State.items:get_item_group(opts.itemgroup)
3640
if itemgroup then
3741
itemlist = itemgroup.items
42+
else
43+
Log.error('Expected itemlist, got %s.\n %s', type(itemlist), vim.inspect(itemlist))
3844
end
3945
end
4046

41-
-- finally, use full item list if no other lists are specified
42-
itemlist = itemlist or State.items
43-
opts = opts or {}
44-
4547
local prompt = opts.select_prompt or Config.select_prompt
4648
if type(prompt) == 'function' then
4749
prompt = prompt()
@@ -51,7 +53,7 @@ local function select_inner(opts, context, itemlist)
5153
-- implementation of `sort_inplace` checks if
5254
-- sorting is actually needed and does nothing
5355
-- if it does not need to be sorted.
54-
itemlist:sort_inplace()
56+
itemlist:sort_inplace(opts)
5557

5658
local filters = opts.filters or {}
5759
if type(filters) ~= 'table' then
@@ -81,12 +83,19 @@ local function select_inner(opts, context, itemlist)
8183
return
8284
end
8385

86+
State.itemgroup_history[opts.itemgroup or ItemList.TOPLEVEL_LIST_ID] = selected
87+
8488
if Toolbox.is_itemgroup(selected) then
85-
return select_inner(opts, context, selected.items)
89+
local item_group_id = selected:id()
90+
91+
local opts_next = vim.tbl_extend('force', opts, {
92+
itemgroup = item_group_id,
93+
})
94+
95+
return select_inner(opts_next, context)
8696
end
8797

8898
Log.trace('Preparing to execute selected item')
89-
State.most_recent_item = selected
9099
Executor.exec_item(selected, context)
91100
end)
92101
end
@@ -96,7 +105,7 @@ end
96105
function M.select(opts)
97106
vim.cmd('doautocmd User LegendaryUiPre')
98107
local context = Executor.build_context()
99-
select_inner(opts, context)
108+
select_inner(opts, context, State.items)
100109
end
101110

102111
return M

0 commit comments

Comments
 (0)