Skip to content

Commit c25db27

Browse files
committed
feat: use prompt for input
1 parent d17f47b commit c25db27

3 files changed

Lines changed: 224 additions & 64 deletions

File tree

lua/telescope/_extensions/file_browser/actions.lua

Lines changed: 86 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@ local transform_mod = require("telescope.actions.mt").transform_mod
4141
local Path = require "plenary.path"
4242
local popup = require "plenary.popup"
4343

44+
-- custom input cb so finder works with built-in input
45+
local stem_prompt = function(prompt)
46+
return { prompt = prompt:find(Path.path.sep) and table.remove(Path:new(prompt):_split()) or prompt }
47+
end
48+
4449
local fb_actions = setmetatable({}, {
4550
__index = function(_, k)
4651
error("Key does not exist for 'fb_actions': " .. tostring(k))
@@ -101,7 +106,13 @@ fb_actions.create = function(prompt_bufnr)
101106
local finder = current_picker.finder
102107

103108
local default = get_target_dir(finder) .. os_sep
104-
vim.ui.input({ prompt = "Insert the file name: ", default = default, completion = "file" }, function(input)
109+
-- vim.ui.input({ prompt = "Insert the file name: ", default = default }, function(file)
110+
fb_utils.input({
111+
prompt = "Insert the file name: ",
112+
default = default,
113+
prompt_bufnr = prompt_bufnr,
114+
on_input_filter_cb = stem_prompt,
115+
}, function(input)
105116
vim.cmd [[ redraw ]] -- redraw to clear out vim.ui.prompt to avoid hit-enter prompt
106117
local file = create(input, finder)
107118
if file then
@@ -232,51 +243,58 @@ fb_actions.rename = function(prompt_bufnr)
232243
fb_utils.notify("action.rename", { msg = "Please select a valid file or folder!", level = "WARN", quiet = quiet })
233244
return
234245
end
235-
vim.ui.input({ prompt = "Insert a new name: ", default = old_path:absolute(), completion = "file" }, function(file)
236-
vim.cmd [[ redraw ]] -- redraw to clear out vim.ui.prompt to avoid hit-enter prompt
237-
if file == "" or file == nil then
238-
fb_utils.notify("action.rename", { msg = "Renaming aborted!", level = "WARN", quiet = quiet })
239-
return
240-
end
241-
local new_path = Path:new(file)
242-
243-
if old_path.filename == new_path.filename then
244-
fb_utils.notify("action.rename", {
245-
msg = string.format(
246-
"Name of selection unchanged! Skipping.",
247-
new_path.filename:sub(#new_path:parent().filename + 2)
248-
),
249-
level = "WARN",
250-
quiet = quiet,
251-
})
252-
return
253-
end
254-
if new_path:exists() then
255-
fb_utils.notify("action.rename", {
256-
msg = string.format("%s already exists! Skipping.", new_path.filename:sub(#new_path:parent().filename + 2)),
257-
level = "WARN",
258-
quiet = quiet,
259-
})
260-
return
261-
end
246+
fb_utils.input(
247+
{
248+
prompt = "Rename: " .. table.remove(entry.Path:_split()),
249+
prompt_bufnr = prompt_bufnr,
250+
on_input_filter_cb = stem_prompt,
251+
},
252+
function(file)
253+
vim.cmd [[ redraw ]] -- redraw to clear out vim.ui.prompt to avoid hit-enter prompt
254+
if file == "" or file == nil then
255+
fb_utils.notify("action.rename", { msg = "Renaming aborted!", level = "WARN", quiet = quiet })
256+
return
257+
end
258+
local new_path = Path:new(file)
259+
260+
if old_path.filename == new_path.filename then
261+
fb_utils.notify("action.rename", {
262+
msg = string.format(
263+
"Name of selection unchanged! Skipping.",
264+
new_path.filename:sub(#new_path:parent().filename + 2)
265+
),
266+
level = "WARN",
267+
quiet = quiet,
268+
})
269+
return
270+
end
271+
if new_path:exists() then
272+
fb_utils.notify("action.rename", {
273+
msg = string.format("%s already exists! Skipping.", new_path.filename:sub(#new_path:parent().filename + 2)),
274+
level = "WARN",
275+
quiet = quiet,
276+
})
277+
return
278+
end
262279

263-
-- rename changes old_name in place
264-
local old_name = old_path:absolute()
280+
-- rename changes old_name in place
281+
local old_name = old_path:absolute()
265282

266-
old_path:rename { new_name = new_path.filename }
267-
if not new_path:is_dir() then
268-
fb_utils.rename_buf(old_name, new_path:absolute())
269-
else
270-
fb_utils.rename_dir_buf(old_name, new_path:absolute())
271-
end
283+
old_path:rename { new_name = new_path.filename }
284+
if not new_path:is_dir() then
285+
fb_utils.rename_buf(old_name, new_path:absolute())
286+
else
287+
fb_utils.rename_dir_buf(old_name, new_path:absolute())
288+
end
272289

273-
-- persist multi selections unambiguously by only removing renamed entry
274-
if current_picker:is_multi_selected(entry) then
275-
current_picker._multi:drop(entry)
290+
-- persist multi selections unambiguously by only removing renamed entry
291+
if current_picker:is_multi_selected(entry) then
292+
current_picker._multi:drop(entry)
293+
end
294+
fb_utils.selection_callback(current_picker, new_path:absolute())
295+
current_picker:refresh(current_picker.finder)
276296
end
277-
fb_utils.selection_callback(current_picker, new_path:absolute())
278-
current_picker:refresh(current_picker.finder)
279-
end)
297+
)
280298
end
281299
end
282300

@@ -383,9 +401,9 @@ fb_actions.copy = function(prompt_bufnr)
383401
end
384402
if exists then
385403
exists = false
386-
vim.ui.input({
404+
fb_utils.input({
387405
prompt = string.format(
388-
"Please enter a new name, <CR> to overwrite (merge), or <ESC> to skip file (folder):\n",
406+
"Please enter a new name, <CR> to overwrite (merge), or <ESC> to skip file (folder):",
389407
name
390408
),
391409
default = destination:absolute(),
@@ -457,29 +475,33 @@ fb_actions.remove = function(prompt_bufnr)
457475
local message = "Selections to be deleted: " .. table.concat(files, ", ")
458476
fb_utils.notify("actions.remove", { msg = message, level = "INFO", quiet = quiet })
459477
-- TODO fix default vim.ui.input and nvim-notify 'selections to be deleted' message
460-
vim.ui.input({ prompt = "Remove selections [y/N]: " }, function(input)
461-
vim.cmd [[ redraw ]] -- redraw to clear out vim.ui.prompt to avoid hit-enter prompt
462-
if input and input:lower() == "y" then
463-
for _, p in ipairs(selections) do
464-
local is_dir = p:is_dir()
465-
p:rm { recursive = is_dir }
466-
-- clean up opened buffers
467-
if not is_dir then
468-
fb_utils.delete_buf(p:absolute())
469-
else
470-
fb_utils.delete_dir_buf(p:absolute())
478+
-- vim.ui.input({ prompt = "Remove selections [y/N]: " }, function(input)
479+
fb_utils.input(
480+
{ prompt = "Remove selections [y/N]: ", prompt_bufnr = prompt_bufnr, on_input_filter_cb = stem_prompt },
481+
function(input)
482+
vim.cmd [[ redraw ]] -- redraw to clear out vim.ui.prompt to avoid hit-enter prompt
483+
if input and input:lower() == "y" then
484+
for _, p in ipairs(selections) do
485+
local is_dir = p:is_dir()
486+
p:rm { recursive = is_dir }
487+
-- clean up opened buffers
488+
if not is_dir then
489+
fb_utils.delete_buf(p:absolute())
490+
else
491+
fb_utils.delete_dir_buf(p:absolute())
492+
end
493+
table.insert(removed, p.filename:sub(#p:parent().filename + 2))
471494
end
472-
table.insert(removed, p.filename:sub(#p:parent().filename + 2))
495+
fb_utils.notify(
496+
"actions.remove",
497+
{ msg = "Removed: " .. table.concat(removed, ", "), level = "INFO", quiet = quiet }
498+
)
499+
current_picker:refresh(current_picker.finder)
500+
else
501+
fb_utils.notify("actions.remove", { msg = "Removing selections aborted!", level = "INFO", quiet = quiet })
473502
end
474-
fb_utils.notify(
475-
"actions.remove",
476-
{ msg = "Removed: " .. table.concat(removed, ", "), level = "INFO", quiet = quiet }
477-
)
478-
current_picker:refresh(current_picker.finder)
479-
else
480-
fb_utils.notify("actions.remove", { msg = "Removing selections aborted!", level = "INFO", quiet = quiet })
481503
end
482-
end)
504+
)
483505
end
484506

485507
--- Toggle hidden files or folders for |telescope-file-browser.picker.file_browser|.

lua/telescope/_extensions/file_browser/make_entry.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
local fb_utils = require "telescope._extensions.file_browser.utils"
12
local utils = require "telescope.utils"
23
local log = require "telescope.log"
34
local entry_display = require "telescope.pickers.entry_display"

lua/telescope/_extensions/file_browser/utils.lua

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ local a = vim.api
22

33
local action_state = require "telescope.actions.state"
44
local utils = require "telescope.utils"
5+
local mappings = require "telescope.mappings"
56

67
local Path = require "plenary.path"
78
local os_sep = Path.path.sep
@@ -191,4 +192,140 @@ fb_utils.selection_callback = function(current_picker, absolute_path)
191192
end)
192193
end
193194

195+
fb_utils.get_fb_prompt = function()
196+
local prompt_buf = vim.tbl_filter(function(b)
197+
return vim.bo[b].filetype == "TelescopePrompt"
198+
end, vim.api.nvim_list_bufs())
199+
-- vim.ui.{input, select} might be telescope pickers
200+
if #prompt_buf > 1 then
201+
for _, buf in ipairs(prompt_buf) do
202+
local current_picker = action_state.get_current_picker(prompt_buf)
203+
if current_picker.finder._browse_files then
204+
prompt_buf = buf
205+
break
206+
end
207+
end
208+
else
209+
prompt_buf = prompt_buf[1]
210+
end
211+
return prompt_buf
212+
end
213+
214+
local set_prompt = function(prompt_bufnr)
215+
local value = action_state.get_selected_entry().value
216+
local current_picker = action_state.get_current_picker(prompt_bufnr)
217+
current_picker:reset_prompt(value)
218+
end
219+
220+
local get_action = function(action_name, keymappings)
221+
return vim.tbl_filter(function(mapping)
222+
return mapping.func[1] == action_name
223+
end, keymappings)[1].func
224+
end
225+
226+
-- keep_mappings: array of {mode = "n|i", lhs = string }k
227+
local clear_mappings = function(prompt_bufnr, keep_mappings)
228+
mappings.clear(prompt_bufnr)
229+
for _, m in ipairs { "n", "i" } do
230+
vim.tbl_map(function(keymap)
231+
local keep_map = vim.tbl_filter(function(map)
232+
if map.mode == m and map.lhs == keymap.lhs then
233+
return true
234+
end
235+
end, keep_mappings)
236+
if vim.tbl_isempty(keep_map) then
237+
vim.api.nvim_buf_del_keymap(prompt_bufnr, m, keymap.lhs)
238+
end
239+
end, vim.api.nvim_buf_get_keymap(prompt_bufnr, m))
240+
end
241+
end
242+
243+
local function clear_buffer_mappings(bufnr)
244+
for _, mode in ipairs { "n", "i" } do
245+
local buffer_mappings = vim.api.nvim_buf_get_keymap(bufnr, mode)
246+
for _, mapping in ipairs(buffer_mappings) do
247+
vim.api.nvim_buf_del_keymap(bufnr, mode, mapping.lhs)
248+
end
249+
end
250+
end
251+
252+
-- TODO
253+
-- [x] handle ESC, <C-c>
254+
-- [ ] multiple prompts?
255+
-- [ ] refactor into components
256+
-- [ ] namespace for mappings ...
257+
-- highlighting with prompt callback
258+
fb_utils.input = function(opts, on_confirm)
259+
opts.prompt_bufnr = vim.F.if_nil(opts.prompt_bufnr, fb_utils.get_fb_prompt())
260+
local current_picker = action_state.get_current_picker(opts.prompt_bufnr)
261+
local picker_status = {
262+
prompt = current_picker:_get_prompt(),
263+
prompt_prefix = current_picker.prompt_prefix,
264+
title = current_picker.prompt_title,
265+
selection_strategy = current_picker.selection_strategy,
266+
on_input_filter_cb = current_picker._on_input_filter_cb,
267+
attach_mappings = current_picker.attach_mappings,
268+
}
269+
270+
mappings.clear(opts.prompt_bufnr)
271+
272+
opts.on_input_filter_cb = vim.F.if_nil(opts.on_input_filter_cb)
273+
opts.prompt_prefix = vim.F.if_nil(opts.prompt_prefix, current_picker.prompt_prefix)
274+
275+
current_picker.selection_strategy = vim.F.if_nil(opts.selection_strategy, "none")
276+
current_picker.prompt_border:change_title(opts.prompt)
277+
-- vim.fn.prompt_setprompt(opts.prompt_bufnr, opts.prompt_prefix)
278+
current_picker.prompt_prefix = opts.prompt_prefix
279+
current_picker:reset_prompt(opts.default or "")
280+
current_picker._on_input_filter_cb = vim.F.if_nil(opts.on_input_filter_cb, function() end)
281+
282+
local _on_confirm = function(_, confirm_opts)
283+
confirm_opts = confirm_opts or {}
284+
confirm_opts.nil_input = vim.F.if_nil(confirm_opts.nil_input, false)
285+
local prompt = current_picker:_get_prompt()
286+
current_picker._finder_attached = true
287+
current_picker.prompt_border:change_title(picker_status.title)
288+
current_picker.selection_strategy = picker_status.selection_strategy
289+
current_picker.prompt_prefix = picker_status.prompt_prefix
290+
current_picker._on_input_filter_cb = picker_status.on_input_filter_cb
291+
current_picker._finder_attached = true
292+
vim.fn.prompt_setprompt(opts.prompt_bufnr, picker_status.prompt_prefix)
293+
current_picker:reset_prompt ""
294+
-- clear all input mappings prior to re-attaching original fb mappings
295+
clear_buffer_mappings(opts.prompt_bufnr)
296+
mappings.clear(opts.prompt_bufnr)
297+
require("telescope.actions.mt").clear_all()
298+
mappings.apply_keymap(opts.prompt_bufnr, picker_status.attach_mappings, require("telescope.config").values.mappings)
299+
on_confirm(not confirm_opts.nil_input and prompt or nil)
300+
end
301+
302+
local attach_mappings = function(_, map)
303+
local actions = require "telescope.actions"
304+
for _, action in ipairs { actions.move_selection_next, actions.move_selection_previous } do
305+
action:enhance {
306+
pre = function()
307+
current_picker:_toggle_finder_attach()
308+
end,
309+
post = function()
310+
set_prompt(opts.prompt_bufnr)
311+
current_picker:_toggle_finder_attach()
312+
end,
313+
}
314+
actions.select_default:replace(_on_confirm)
315+
actions.close:replace(function()
316+
_on_confirm(_, { nil_input = true })
317+
end)
318+
map("i", "<C-c>", actions.close)
319+
map("i", "<CR>", actions.select_default)
320+
map("n", "<ESC>", actions.close)
321+
return false
322+
end
323+
end
324+
-- clear all mappings prior to attaching input mappings
325+
clear_buffer_mappings(opts.prompt_bufnr)
326+
mappings.clear(opts.prompt_bufnr)
327+
require("telescope.actions.mt").clear_all()
328+
mappings.apply_keymap(opts.prompt_bufnr, attach_mappings, {})
329+
end
330+
194331
return fb_utils

0 commit comments

Comments
 (0)