-
-
Notifications
You must be signed in to change notification settings - Fork 13
/
Copy pathpicker.lua
266 lines (226 loc) · 8.65 KB
/
picker.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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
---@module "utils.class.picker"
---@author sravioli
---@license GNU-GPLv3
---@diagnostic disable: undefined-field
local Utils = require "utils"
local fs, Logger = Utils.fn.fs, Utils.class.logger
local wt = require "wezterm"
local config_dir = wt.config_dir
-- {{{1 Meta
--~ {{{2 wt.Window
---@class wt.Window
---@field active_key_table function
---@field active_pane function
---@field active_tab function
---@field active_workspace function
---@field composition_status function
---@field copy_to_clipboard function
---@field current_event function
---@field effective_config function
---@field focus function
---@field get_appearance function
---@field get_config_overrides function
---@field get_dimensions function
---@field get_selection_escapes_for_pane function
---@field get_selection_text_for_pane function
---@field is_focused function
---@field keyboard_modifiers function
---@field leader_is_active function
---@field maximize function
---@field mux_window function
---@field perform_action function
---@field restore function
---@field set_config_overrides function
---@field set_inner_size function
---@field set_left_status function
---@field set_position function
---@field set_right_status function
---@field toast_notification function
---@field toggle_fullscreen function
---@field window_id function
--~ }}}
--~ {{{2 wt.Pane
---@class wt.Pane
---@field activate function
---@field get_current_working_dir function
---@field get_cursor_position function
---@field get_dimensions function
---@field get_domain_name function
---@field get_foreground_process_info function
---@field get_foreground_process_name function
---@field get_lines_as_escapes function
---@field get_lines_as_text function
---@field get_logical_lines_as_text function
---@field get_metadata function
---@field get_semantic_zone_at function
---@field get_semantic_zones function
---@field get_text_from_region function
---@field get_text_from_semantic_zone function
---@field get_title function
---@field get_tty_name function
---@field get_user_vars function
---@field has_unseen_output function
---@field inject_output function
---@field is_alt_screen_active function
---@field move_to_new_tab function
---@field move_to_new_window function
---@field mux_pane function
---@field pane_id function
---@field paste function
---@field send_paste function
---@field send_text function
---@field split function
---@field tab function
---@field window function
--~ }}}
--~ {{{2 Utils.Class.Picker
---@alias Module PickList
---@alias Choice { id: string, label: string|table }
---@alias Choice.private { module: Module, value: Choice }
---@class Utils.Class.Picker
---@field subdir string name of the picker module
---@field title string defaults to `"Pick a value"`
---@field choices? Choice[] defaults to `{}`
---@field __choices? Choice.private[] defaults to `{}`
---@field fuzzy? boolean defaults to `false`
---@field alphabet? string defaults to `"1234567890abcdefghilmnopqrstuvwxyz"`
---@field description? string defaults to `"Select an item."`
---@field fuzzy_description? string defaults to `"Fuzzy matching: "`
---@field comp? fun(a, b): boolean function to sort choices
---@field build? fun(__choices: Choice.private[], comp: function, opts: { window: wt.Window, pane: wt.Pane }): Choice[]
---@field new? fun(opts: Utils.Class.Picker): Utils.Class.Picker
---@field private log? Utils.Class.Logger
--~ }}}
--~ {{{2 PickList.Opts
---@alias PickList.Opts { window: wt.Window, pane: wt.Pane, id: string|nil, label: string|nil }
---@alias PickList.getReturn string|{ id: string|nil, label: string|table|nil }
---@class PickList
---@field get fun(): PickList.getReturn
---@field activate fun(Config: table, opts: PickList.Opts): nil
--~ }}}
-- }}}
-- {{{1 Helper functions
local h = {}
---Normalize an item to the `choices` format
---@param item string|number
---@return table normalized_item
function h.normalize(item)
return type(item) == "table" and item or { id = item }
end
---Determines whether the given item is an array or not.
---@param item PickList.getReturn
---@return boolean result whether or not the item is an array
function h.is_array(item)
return type(item) == "table" and item[1] ~= nil
end
---Build the choices table
---@param items table
---@param comp? fun(a, b): boolean
---@return Choice[] choices
function h.build(items, comp)
local choices = {}
for _, item in pairs(items) do
choices[#choices + 1] = { id = item.value.id, label = item.value.label }
end
table.sort(choices, comp or function(a, b)
return a.id < b.id
end)
return choices
end
---Converts a file path to a lua require path
---@param path string
---@return string require_path
function h.path_to_module(path)
return (path:sub(#config_dir + 2):gsub("%.lua$", ""):gsub(fs.path_separator, "."))
end
-- }}}
---@class Utils.Class.Picker
local M = {}
---Creates a new picker object
---@param opts Utils.Class.Picker
---@return Utils.Class.Picker Picker
function M.new(opts)
local self = setmetatable({}, { __index = M })
self.title = opts.title or "Pick a value"
self.choices = {}
self.__choices = {}
self.log = Logger:new("Picker > " .. self.title)
self.comp = opts.comp
self.build = opts.build or h.build
self.fuzzy = opts.fuzzy or false
self.alphabet = opts.alphabet or "1234567890abcdefghilmnopqrstuvwxyz"
self.description = opts.description or "Select an item."
self.fuzzy_description = opts.fuzzy_description or "Fuzzy matching: "
local dir = fs.pathconcat(config_dir, "picker", "assets", opts.subdir)
local paths = fs.ls_dir(dir)
if not paths then
self.log:error("Cannot read files from %s", dir)
return {}
end
for i = 1, #paths do
self:register(h.path_to_module(paths[i]))
end
return self
end
---Registers the module by requiring it and filling the `__choices` table
---@param name string the lua require path to the module
function M:register(name)
---@class PickList
local module = require(name)
local result = module.get()
if h.is_array(result) then
for i = 1, #result do
local item = h.normalize(result[i])
self.__choices[item.id] =
{ module = module, value = { id = item.id, label = item.label } }
self.log:debug("registered item: %s", self.__choices[item.id])
end
else
---@cast result string
result = h.normalize(result)
self.__choices[result.id] =
{ module = module, value = { id = result.id, label = result.label } }
self.log:debug("registered item: %s", self.__choices[result.id])
end
end
---Activates the selected module
---@param Overrides table config overrides
---@param opts PickList.Opts
---@return nil
function M:select(Overrides, opts)
local choice = self.__choices[opts.id]
if not choice then
return self.log:error("%s is not defined for %s", opts.id, self.title)
end
choice.module.activate(Overrides, opts)
end
---Invoke the picker.
---@return nil
function M:pick()
return wt.action_callback(function(window, pane)
local opts = { window = window, pane = pane }
window:perform_action(
wt.action.InputSelector {
action = wt.action_callback(function(inner_window, _, id, label)
if not id and not label then
self.log:error "cancelled by user"
else
local callback_opts = { window = window, pane = pane, id = id, label = label }
self.log:info("applying %s", id)
local Overrides = inner_window:get_config_overrides() or {}
self:select(Overrides, callback_opts)
window:set_config_overrides(Overrides)
end
end),
title = self.title,
choices = self.build(self.__choices, self.comp, opts),
fuzzy = self.fuzzy,
description = self.description,
fuzzy_description = self.fuzzy_description,
alphabet = self.alphabet,
},
pane
)
end)
end
return M