diff --git a/lua/telescope/_extensions/file_browser/actions.lua b/lua/telescope/_extensions/file_browser/actions.lua index c39ea910..0f7b4acf 100644 --- a/lua/telescope/_extensions/file_browser/actions.lua +++ b/lua/telescope/_extensions/file_browser/actions.lua @@ -41,6 +41,11 @@ local transform_mod = require("telescope.actions.mt").transform_mod local Path = require "plenary.path" local popup = require "plenary.popup" +-- custom input cb so finder works with built-in input +local stem_prompt = function(prompt) + return { prompt = prompt:find(Path.path.sep) and table.remove(Path:new(prompt):_split()) or prompt } +end + local fb_actions = setmetatable({}, { __index = function(_, k) error("Key does not exist for 'fb_actions': " .. tostring(k)) @@ -101,7 +106,13 @@ fb_actions.create = function(prompt_bufnr) local finder = current_picker.finder local default = get_target_dir(finder) .. os_sep - vim.ui.input({ prompt = "Insert the file name: ", default = default, completion = "file" }, function(input) + -- vim.ui.input({ prompt = "Insert the file name: ", default = default }, function(file) + fb_utils.input({ + prompt = "Insert the file name: ", + default = default, + prompt_bufnr = prompt_bufnr, + on_input_filter_cb = stem_prompt, + }, function(input) vim.cmd [[ redraw ]] -- redraw to clear out vim.ui.prompt to avoid hit-enter prompt local file = create(input, finder) if file then @@ -232,7 +243,11 @@ fb_actions.rename = function(prompt_bufnr) fb_utils.notify("action.rename", { msg = "Please select a valid file or folder!", level = "WARN", quiet = quiet }) return end - vim.ui.input({ prompt = "Insert a new name: ", default = old_path:absolute(), completion = "file" }, function(file) + fb_utils.input({ + prompt = "Rename: " .. table.remove(entry.Path:_split()), + prompt_bufnr = prompt_bufnr, + on_input_filter_cb = stem_prompt, + }, function(file) vim.cmd [[ redraw ]] -- redraw to clear out vim.ui.prompt to avoid hit-enter prompt if file == "" or file == nil then fb_utils.notify("action.rename", { msg = "Renaming aborted!", level = "WARN", quiet = quiet }) @@ -383,9 +398,9 @@ fb_actions.copy = function(prompt_bufnr) end if exists then exists = false - vim.ui.input({ + fb_utils.input({ prompt = string.format( - "Please enter a new name, to overwrite (merge), or to skip file (folder):\n", + "Please enter a new name, to overwrite (merge), or to skip file (folder):", name ), default = destination:absolute(), @@ -457,29 +472,33 @@ fb_actions.remove = function(prompt_bufnr) local message = "Selections to be deleted: " .. table.concat(files, ", ") fb_utils.notify("actions.remove", { msg = message, level = "INFO", quiet = quiet }) -- TODO fix default vim.ui.input and nvim-notify 'selections to be deleted' message - vim.ui.input({ prompt = "Remove selections [y/N]: " }, function(input) - vim.cmd [[ redraw ]] -- redraw to clear out vim.ui.prompt to avoid hit-enter prompt - if input and input:lower() == "y" then - for _, p in ipairs(selections) do - local is_dir = p:is_dir() - p:rm { recursive = is_dir } - -- clean up opened buffers - if not is_dir then - fb_utils.delete_buf(p:absolute()) - else - fb_utils.delete_dir_buf(p:absolute()) + -- vim.ui.input({ prompt = "Remove selections [y/N]: " }, function(input) + fb_utils.input( + { prompt = "Remove selections [y/N]: ", prompt_bufnr = prompt_bufnr, on_input_filter_cb = stem_prompt }, + function(input) + vim.cmd [[ redraw ]] -- redraw to clear out vim.ui.prompt to avoid hit-enter prompt + if input and input:lower() == "y" then + for _, p in ipairs(selections) do + local is_dir = p:is_dir() + p:rm { recursive = is_dir } + -- clean up opened buffers + if not is_dir then + fb_utils.delete_buf(p:absolute()) + else + fb_utils.delete_dir_buf(p:absolute()) + end + table.insert(removed, p.filename:sub(#p:parent().filename + 2)) end - table.insert(removed, p.filename:sub(#p:parent().filename + 2)) + fb_utils.notify( + "actions.remove", + { msg = "Removed: " .. table.concat(removed, ", "), level = "INFO", quiet = quiet } + ) + current_picker:refresh(current_picker.finder) + else + fb_utils.notify("actions.remove", { msg = "Removing selections aborted!", level = "INFO", quiet = quiet }) end - fb_utils.notify( - "actions.remove", - { msg = "Removed: " .. table.concat(removed, ", "), level = "INFO", quiet = quiet } - ) - current_picker:refresh(current_picker.finder) - else - fb_utils.notify("actions.remove", { msg = "Removing selections aborted!", level = "INFO", quiet = quiet }) end - end) + ) end --- Toggle hidden files or folders for |telescope-file-browser.picker.file_browser|. diff --git a/lua/telescope/_extensions/file_browser/make_entry.lua b/lua/telescope/_extensions/file_browser/make_entry.lua index daa7cfcc..d1bff515 100644 --- a/lua/telescope/_extensions/file_browser/make_entry.lua +++ b/lua/telescope/_extensions/file_browser/make_entry.lua @@ -1,3 +1,4 @@ +local fb_utils = require "telescope._extensions.file_browser.utils" local utils = require "telescope.utils" local log = require "telescope.log" local entry_display = require "telescope.pickers.entry_display" diff --git a/lua/telescope/_extensions/file_browser/utils.lua b/lua/telescope/_extensions/file_browser/utils.lua index fb59f5fc..213e401f 100644 --- a/lua/telescope/_extensions/file_browser/utils.lua +++ b/lua/telescope/_extensions/file_browser/utils.lua @@ -2,6 +2,7 @@ local a = vim.api local action_state = require "telescope.actions.state" local utils = require "telescope.utils" +local mappings = require "telescope.mappings" local Path = require "plenary.path" local os_sep = Path.path.sep @@ -191,4 +192,140 @@ fb_utils.selection_callback = function(current_picker, absolute_path) end) end +fb_utils.get_fb_prompt = function() + local prompt_buf = vim.tbl_filter(function(b) + return vim.bo[b].filetype == "TelescopePrompt" + end, vim.api.nvim_list_bufs()) + -- vim.ui.{input, select} might be telescope pickers + if #prompt_buf > 1 then + for _, buf in ipairs(prompt_buf) do + local current_picker = action_state.get_current_picker(prompt_buf) + if current_picker.finder._browse_files then + prompt_buf = buf + break + end + end + else + prompt_buf = prompt_buf[1] + end + return prompt_buf +end + +local set_prompt = function(prompt_bufnr) + local value = action_state.get_selected_entry().value + local current_picker = action_state.get_current_picker(prompt_bufnr) + current_picker:reset_prompt(value) +end + +local get_action = function(action_name, keymappings) + return vim.tbl_filter(function(mapping) + return mapping.func[1] == action_name + end, keymappings)[1].func +end + +-- keep_mappings: array of {mode = "n|i", lhs = string }k +local clear_mappings = function(prompt_bufnr, keep_mappings) + mappings.clear(prompt_bufnr) + for _, m in ipairs { "n", "i" } do + vim.tbl_map(function(keymap) + local keep_map = vim.tbl_filter(function(map) + if map.mode == m and map.lhs == keymap.lhs then + return true + end + end, keep_mappings) + if vim.tbl_isempty(keep_map) then + vim.api.nvim_buf_del_keymap(prompt_bufnr, m, keymap.lhs) + end + end, vim.api.nvim_buf_get_keymap(prompt_bufnr, m)) + end +end + +local function clear_buffer_mappings(bufnr) + for _, mode in ipairs { "n", "i" } do + local buffer_mappings = vim.api.nvim_buf_get_keymap(bufnr, mode) + for _, mapping in ipairs(buffer_mappings) do + vim.api.nvim_buf_del_keymap(bufnr, mode, mapping.lhs) + end + end +end + +-- TODO +-- [x] handle ESC, +-- [ ] multiple prompts? +-- [ ] refactor into components +-- [ ] namespace for mappings ... +-- highlighting with prompt callback +fb_utils.input = function(opts, on_confirm) + opts.prompt_bufnr = vim.F.if_nil(opts.prompt_bufnr, fb_utils.get_fb_prompt()) + local current_picker = action_state.get_current_picker(opts.prompt_bufnr) + local picker_status = { + prompt = current_picker:_get_prompt(), + prompt_prefix = current_picker.prompt_prefix, + title = current_picker.prompt_title, + selection_strategy = current_picker.selection_strategy, + on_input_filter_cb = current_picker._on_input_filter_cb, + attach_mappings = current_picker.attach_mappings, + } + + mappings.clear(opts.prompt_bufnr) + + opts.on_input_filter_cb = vim.F.if_nil(opts.on_input_filter_cb) + opts.prompt_prefix = vim.F.if_nil(opts.prompt_prefix, current_picker.prompt_prefix) + + current_picker.selection_strategy = vim.F.if_nil(opts.selection_strategy, "none") + current_picker.prompt_border:change_title(opts.prompt) + -- vim.fn.prompt_setprompt(opts.prompt_bufnr, opts.prompt_prefix) + current_picker.prompt_prefix = opts.prompt_prefix + current_picker:reset_prompt(opts.default or "") + current_picker._on_input_filter_cb = vim.F.if_nil(opts.on_input_filter_cb, function() end) + + local _on_confirm = function(_, confirm_opts) + confirm_opts = confirm_opts or {} + confirm_opts.nil_input = vim.F.if_nil(confirm_opts.nil_input, false) + local prompt = current_picker:_get_prompt() + current_picker._finder_attached = true + current_picker.prompt_border:change_title(picker_status.title) + current_picker.selection_strategy = picker_status.selection_strategy + current_picker.prompt_prefix = picker_status.prompt_prefix + current_picker._on_input_filter_cb = picker_status.on_input_filter_cb + current_picker._finder_attached = true + vim.fn.prompt_setprompt(opts.prompt_bufnr, picker_status.prompt_prefix) + current_picker:reset_prompt "" + -- clear all input mappings prior to re-attaching original fb mappings + clear_buffer_mappings(opts.prompt_bufnr) + mappings.clear(opts.prompt_bufnr) + require("telescope.actions.mt").clear_all() + mappings.apply_keymap(opts.prompt_bufnr, picker_status.attach_mappings, require("telescope.config").values.mappings) + on_confirm(not confirm_opts.nil_input and prompt or nil) + end + + local attach_mappings = function(_, map) + local actions = require "telescope.actions" + for _, action in ipairs { actions.move_selection_next, actions.move_selection_previous } do + action:enhance { + pre = function() + current_picker:_toggle_finder_attach() + end, + post = function() + set_prompt(opts.prompt_bufnr) + current_picker:_toggle_finder_attach() + end, + } + actions.select_default:replace(_on_confirm) + actions.close:replace(function() + _on_confirm(_, { nil_input = true }) + end) + map("i", "", actions.close) + map("i", "", actions.select_default) + map("n", "", actions.close) + return false + end + end + -- clear all mappings prior to attaching input mappings + clear_buffer_mappings(opts.prompt_bufnr) + mappings.clear(opts.prompt_bufnr) + require("telescope.actions.mt").clear_all() + mappings.apply_keymap(opts.prompt_bufnr, attach_mappings, {}) +end + return fb_utils