forked from mrjones2014/legendary.nvim
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathexecutor.lua
146 lines (137 loc) · 4.97 KB
/
executor.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
local Toolbox = require('legendary.toolbox')
local Log = require('legendary.log')
local Config = require('legendary.config')
local State = require('legendary.data.state')
local util = require('legendary.util')
local function update_item_frecency_score(item)
if Config.sort.frecency ~= false then
if require('legendary.api.db').is_supported() then
Log.trace('Updating scoring data for selected item.')
local DbClient = require('legendary.api.db.client').get_client()
-- if bootstrapping fails, bail
if not require('legendary.api.db').is_supported() then
Log.debug(
'Config.sort.frecency is enabled, but sqlite is not available or database could not be opened, '
.. 'frecency is automatically disabled.'
)
return
end
DbClient.update_item_score(item)
else
Log.debug(
'Config.sort.frecency is enabled, but sqlite is not available or database could not be opened, '
.. 'frecency is automatically disabled.'
)
end
end
end
local M = {}
---@class LegendaryEditorContext
---@field buf integer
---@field buftype string
---@field filetype string
---@field mode string
---@field cursor_pos integer[] { row, col }
---@field marks integer[]|nil
---Build a context object containing information about the editor
---state *before* triggering the finder so that it can be
---restored before executing the item.
---@param buf number buffer ID to build context for, used only for testing
---@overload fun():LegendaryEditorContext
---@return table
function M.build_context(buf)
buf = buf or vim.api.nvim_get_current_buf()
return {
buf = buf,
buftype = vim.api.nvim_buf_get_option(buf, 'buftype') or '',
filetype = vim.api.nvim_buf_get_option(buf, 'filetype') or '',
mode = vim.fn.mode(),
cursor_pos = vim.api.nvim_win_get_cursor(vim.api.nvim_get_current_win()),
marks = Toolbox.get_marks(),
}
end
---Restore editor state based on context
---@param context LegendaryEditorContext
function M.restore_context(context, callback)
Toolbox.set_marks(context.marks)
if vim.startswith(context.mode, 'n') then
vim.cmd('stopinsert')
elseif Toolbox.is_visual_mode(context.mode) then
-- we can't just use `gv` since vim.ui.select aborts visual mode without any trace
vim.cmd(string.format('normal! %s%s', context.mode, vim.api.nvim_replace_termcodes('<esc>', true, false, true)))
Toolbox.set_marks(context.marks)
vim.cmd('normal! gv')
elseif vim.startswith(context.mode, 'i') then
vim.cmd('startinsert')
else
Log.info('Sorry, only normal, insert, and visual mode executions are supported by legendary.nvim.')
return
end
vim.schedule(function()
callback()
end)
end
---Execute an item
---@param item LegendaryItem
---@param context LegendaryEditorContext
function M.exec_item(item, context)
vim.schedule(function()
M.restore_context(context, function()
State.last_executed_item = item
update_item_frecency_score(item)
if Toolbox.is_function(item) then
item.implementation()
elseif Toolbox.is_command(item) then
local cmd = (item--[[@as Command]]):vim_cmd()
if item.unfinished == true then
util.exec_feedkeys(string.format(':%s ', cmd))
else
-- if in visual mode and the command supports range, add marks
if
vim.tbl_get(item, 'opts', 'range') == true
and Toolbox.is_visual_mode(context.mode)
and not vim.startswith(cmd, "'<,'>")
then
cmd = string.format("'<,'>%s", cmd)
end
vim.cmd(cmd)
end
elseif Toolbox.is_keymap(item) then
util.exec_feedkeys(item.keys, item.builtin)
elseif Toolbox.is_autocmd(item) then
local impl = item.implementation
if type(impl) == 'function' then
impl()
else
util.exec_feedkeys(impl --[[@as string]])
end
else
Log.debug('Unsupported item type selected from finder UI: %s', item)
end
end)
end)
return 'g@'
end
---Repeat execution of the previously selected item. By default, only executes if the previously used filters
---still return true.
---@param ignore_filters boolean|nil whether to ignore the filters used when selecting the item, default false
function M.repeat_previous(ignore_filters)
if State.last_executed_item then
if not ignore_filters and State.most_recent_filters then
for _, filter in ipairs(State.most_recent_filters) do
-- if any filter does not match, abort executions
local err, matches = pcall(filter, State.last_executed_item)
if not err and not matches then
Log.warn(
'Previously executed item no longer matches previously used filters, use `:LegendaryRepeat!`'
.. " or `require('legendary').repeat_previous(true)` to execute anyway."
)
return
end
end
end
local context = M.build_context()
M.exec_item(State.last_executed_item, context)
end
end
return M