Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: use prompt for input #161

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions doc/telescope-file-browser.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
================================================================================
*telescope-file-browser.nvim*
NVIM *telescope-file-browser.nvim*

`telescope-file-browser.nvim` is an extension for telescope.nvim. It helps you
efficiently create, delete, rename, or move files powered by navigation from
Expand Down Expand Up @@ -47,7 +47,7 @@ https://github.com/nvim-telescope/telescope-file-browser.nvim


================================================================================
*telescope-file-browser.picker*
PICKER *telescope-file-browser.picker*

You can use the file browser as follows
>
Expand Down Expand Up @@ -151,7 +151,7 @@ fb_picker.file_browser({opts}) *telescope-file-browser.picker.file_browser()*


================================================================================
*telescope-file-browser.actions*
ACTIONS *telescope-file-browser.actions*

The file browser actions are functions enable file system operations from
within the file browser picker. In particular, the actions include creation,
Expand Down Expand Up @@ -365,7 +365,7 @@ fb_actions.sort_by_date() *telescope-file-browser.actions.sort_by_date()*


================================================================================
*telescope-file-browser.finders*
FINDERS *telescope-file-browser.finders*

The file browser finders power the picker with both a file and folder browser.

Expand Down
68 changes: 44 additions & 24 deletions lua/telescope/_extensions/file_browser/actions.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -232,7 +243,12 @@ 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,
detach_finder = true,
}, 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 })
Expand Down Expand Up @@ -383,9 +399,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, <CR> to overwrite (merge), or <ESC> to skip file (folder):\n",
"Please enter a new name, <CR> to overwrite (merge), or <ESC> to skip file (folder):",
name
),
default = destination:absolute(),
Expand Down Expand Up @@ -457,29 +473,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|.
Expand Down
1 change: 1 addition & 0 deletions lua/telescope/_extensions/file_browser/make_entry.lua
Original file line number Diff line number Diff line change
@@ -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"
Expand Down
144 changes: 144 additions & 0 deletions lua/telescope/_extensions/file_browser/utils.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -191,4 +192,147 @@ 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, <C-c>
-- [ ] 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)
opts.detach_finder = vim.F.if_nil(opts.detach_finder, false)
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()
if not opts.detach_finder then
current_picker:_toggle_finder_attach()
end
end,
post = function()
if not opts.detach_finder then
set_prompt(opts.prompt_bufnr)
current_picker:_toggle_finder_attach()
end
end,
}
actions.select_default:replace(_on_confirm)
actions.close:replace(function()
_on_confirm(_, { nil_input = true })
end)
map("i", "<C-c>", actions.close)
map("i", "<CR>", actions.select_default)
map("n", "<ESC>", 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, {})
if opts.detach_finder then
current_picker._finder_attached = false
end
end

return fb_utils