-
-
Notifications
You must be signed in to change notification settings - Fork 810
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
Open multiple files at once #1048
Comments
With default bindings, you can select entries with
defaults defined here |
That's an alternative way which seems promising, but how can you the said files from the qflist after sending the files there? |
I'm gonna guess you mean how to open the said files I'm not sure if there is a quick one liner to do this, but a quick google search show me something like: https://gist.github.com/larsthegeek/4063239 |
I'm currently working on multi-select style actions and will be opening a PR with my WIP branch soon. It's quite convoluted to support everything nicely, so it'll be ready when it's ready :) |
Maybe until @fdschmidt93 finishes his PR something like this can help (That's what I'm currently using):
|
This is the best way right now, it takes a second to load the opened files, but the ability to batch open is very handy. |
The ability to open multiple files (and more specifically open in multiple new vertical splits with ) is one of the only features I miss from fzf at this point. |
@Corey-Keller you can change the line:
to
in my solution above to achive openning in multiple vertical splits |
@shlomocarmeter Sorry how do I add that? I've tried to put it into a
|
@stephanECD I believe you need to add a local object before the function is declared: local custom_actions = {} |
@shlomocarmeter Thanks so much for sharing this approach! One minor details is that the '[No Name]' buffer remains listed after loading files (and is in fact the first buffer that the user sees after selecting).
NOTE: See updated version below which fixes a number of issues. |
@jeetsukumaran That seems great! I did notice the no name buffer remains but wasn't able to fix this. I'll try your modified code on my config. Thanks! |
The above does not work well when Telescope returns files that have a different cwd from the Neovim session. Below is a fixed version. |
@jeetsukumaran If I use updated version, it opens file (correct name in top bar) but buffer is empty. Original seems to be working as intended. Don't know if this is happening because of airblade/vim-rooter or not, but at least original works. |
Same experience for me. I'm not using airblade/vim-rooter. I tried function custom_actions._multiopen(prompt_bufnr, open_cmd)
local picker = action_state.get_current_picker(prompt_bufnr)
- local num_selections = table.getn(picker:get_multi_selection())
+ local num_selections = #picker:get_multi_selection()
if num_selections > 1 then
... You can currently send all your multi-selections to the telescope.nvim/lua/telescope/mappings.lua Line 49 in 1c57cc6
...and then loop through and open them. But, the solution here has the desired behavior and experience. This is fantastic @shlomocarmeter and @jeetsukumaran 🎸🚀 |
@joelpalmer and @Racle -- are you using Windows machines? Maybe because I'm not special-casing/abstracting out the path separator to account for Windows vs POSIX ( |
Nope, pop_os 21.04 and neovim v0.6.0-dev. |
macOS 11.6 and NVIM v0.6.0-dev+647-gb16b7021e |
I'm having the same problem with the 'updated' version - it opens buffers with the correct filename and path, except that they're under a new, non-existant directory named E.g., if I select the files This is on macOS 10.15.7, nvim v0.6.0-dev+646-g6e70b7b31 |
What must be happening is that |
That does the trick - thanks! |
Yep! I am on |
Anyone know how I could close |
Wouldn't it make sense to set the default for open split to Also, is there a way to have a custom action for multi-open to open all of them in splits in a new tab, instead of the current one? Because that's what is probably happening 95% of the time someone needs to multi-open files, to have them all in a new tab. |
Hi @jeetsukumaran & web searchers - Found one other line that causes problems with various pickers, both builtin & from extensions (telescope zoxide & telescope git_commits etc) that need their specified action (as opposed to This change allows the appropriate action to be set. I know this is just an awesome workaround solution to give us multi-selection while the team builds out the full feature with all the settings & edge case madness covered. I figured this case would hit more people as they come across this issue and try it. Once again, thank you for this @jeetsukumaran - I don't know how I lived without it prior to yesterday! else
if open_cmd == "vsplit" then
actions.file_vsplit(prompt_bufnr)
elseif open_cmd == "split" then
actions.file_split(prompt_bufnr)
elseif open_cmd == "tabe" then
actions.file_tab(prompt_bufnr)
else
- actions.file_edit(prompt_bufnr)
+ actions.select_default(prompt_bufnr)
end
end
... |
You are free to use whatever key-mapping you like. I just use And 100% of the time when I open files, multiple or otherwise, I don't do it tabs. In fact, I barely use tabs. So, again, YMMV, and you are free to modify the code to do as you wish. |
Nice! Thanks for sharing. |
I tried @jeetsukumaran solution and just get:
|
I also tried @dartacao solution and that didn't error but it also didn't work, it would just open a random file instead. |
My take on this:
local actions = require("telescope.actions")
local transform_mod = require("telescope.actions.mt").transform_mod
local function multiopen(prompt_bufnr, method)
local cmd_map = {
vertical = "vsplit",
horizontal = "split",
tab = "tabe",
default = "edit"
}
local picker = action_state.get_current_picker(prompt_bufnr)
local multi_selection = picker:get_multi_selection()
if #multi_selection > 1 then
require("telescope.pickers").on_close_prompt(prompt_bufnr)
pcall(vim.api.nvim_set_current_win, picker.original_win_id)
for i, entry in ipairs(multi_selection) do
-- opinionated use-case
local cmd = i == 1 and "edit" or cmd_map[method]
vim.cmd(string.format("%s %s", cmd, entry.value))
end
else
actions["select_" .. method](prompt_bufnr)
end
end
local custom_actions = transform_mod({
multi_selection_open_vertical = function(prompt_bufnr)
multiopen(prompt_bufnr, "vertical")
end,
multi_selection_open_horizontal = function(prompt_bufnr)
multiopen(prompt_bufnr, "horizontal")
end,
multi_selection_open_tab = function(prompt_bufnr)
multiopen(prompt_bufnr, "tab")
end,
multi_selection_open = function(prompt_bufnr)
multiopen(prompt_bufnr, "default")
end,
})
local function stopinsert(callback)
return function(prompt_bufnr)
vim.cmd.stopinsert()
vim.schedule(function()
callback(prompt_bufnr)
end)
end
end
local multi_open_mappings = {
i = {
["<C-v>"] = stopinsert(custom_actions.multi_selection_open_vertical),
["<C-s>"] = stopinsert(custom_actions.multi_selection_open_horizontal),
["<C-t>"] = stopinsert(custom_actions.multi_selection_open_tab),
["<CR>"] = stopinsert(custom_actions.multi_selection_open)
},
n = {
["<C-v>"] = custom_actions.multi_selection_open_vertical,
["<C-s>"] = custom_actions.multi_selection_open_horizontal,
["<C-t>"] = custom_actions.multi_selection_open_tab,
["<CR>"] = custom_actions.multi_selection_open,
},
}
|
@rebelot I tried out your implementation but I get the error: E5108: Error executing lua ~/.config/nvim/lua/configs/telescope_multiopen.lua:46: attempt to index field 'cmd' (a function value)
stack traceback:
.../melker/.config/nvim/lua/configs/telescope_multiopen.lua:46: in function 'key_func'
...ack/packer/opt/telescope.nvim/lua/telescope/mappings.lua:338: in function 'execute_keymap'
[string ":lua"]:1: in main chunk
1 change; before #14 2022/07/29 20:19:31
1 change; before #26 1 second ago , after pressing enter from telescope's find_files. Here's the file based on your code and here's how I add them to my telescope configuration. What am I doing wrong? I'm on Neovim nightly NVIM v0.8.0-dev+580-g45ba2e147. |
pickers = {
oldfiles = {
mappings = multi_open_mappings,
},
find_files = {
follow = true,
mappings = multi_open_mappings,
},
buffers = {
sort_mru = true,
mappings = multi_open_mappings,
},
... although I'm planning on making this its own plugin/extension and properly handle line/column numbers and perform some other boilerplate checks (as in the default select actions) so it can be used with grep_string and alike |
Thank you for your response, updating Neovim nightly solved it! If you do create a plugin, please post a link to it in this thread! |
@rebelot your solution works perfectly for |
@gstokkink @melkster You can find a working solution that should work as a default mapping, supporting line and columns: local function multiopen(prompt_bufnr, method)
local edit_file_cmd_map = {
vertical = "vsplit",
horizontal = "split",
tab = "tabedit",
default = "edit",
}
local edit_buf_cmd_map = {
vertical = "vert sbuffer",
horizontal = "sbuffer",
tab = "tab sbuffer",
default = "buffer",
}
local picker = action_state.get_current_picker(prompt_bufnr)
local multi_selection = picker:get_multi_selection()
if #multi_selection > 1 then
require("telescope.pickers").on_close_prompt(prompt_bufnr)
pcall(vim.api.nvim_set_current_win, picker.original_win_id)
for i, entry in ipairs(multi_selection) do
local filename, row, col
if entry.path or entry.filename then
filename = entry.path or entry.filename
row = entry.row or entry.lnum
col = vim.F.if_nil(entry.col, 1)
elseif not entry.bufnr then
local value = entry.value
if not value then
return
end
if type(value) == "table" then
value = entry.display
end
local sections = vim.split(value, ":")
filename = sections[1]
row = tonumber(sections[2])
col = tonumber(sections[3])
end
local entry_bufnr = entry.bufnr
if entry_bufnr then
if not vim.api.nvim_buf_get_option(entry_bufnr, "buflisted") then
vim.api.nvim_buf_set_option(entry_bufnr, "buflisted", true)
end
local command = i == 1 and "buffer" or edit_buf_cmd_map[method]
pcall(vim.cmd, string.format("%s %s", command, vim.api.nvim_buf_get_name(entry_bufnr)))
else
local command = i == 1 and "edit" or edit_file_cmd_map[method]
if vim.api.nvim_buf_get_name(0) ~= filename or command ~= "edit" then
filename = require("plenary.path"):new(vim.fn.fnameescape(filename)):normalize(vim.loop.cwd())
pcall(vim.cmd, string.format("%s %s", command, filename))
end
end
if row and col then
pcall(vim.api.nvim_win_set_cursor, 0, { row, col })
end
end
else
actions["select_" .. method](prompt_bufnr)
end
end |
@rebelot seems like that works perfectly, also for |
@rebelot This is awesome man! just a small change for this: |
Any chance we can get this addition/fix back into the core plugin? |
Here is yet another approach - based on @dartacao - had also problems with qflist. Maybe worth sharing. It works with any picker which displays files, i.e. without the custom options for selected pickers. ~/.config/pds/setup/astro master !2 ?1 ❯ cat plugins/telescope.lua
local actions = require("telescope.actions")
local action_state = require("telescope.actions.state")
local mm = { -- my mappings
["<CR>"] = function(pb)
local picker = action_state.get_current_picker(pb)
local multi = picker:get_multi_selection()
actions.select_default(pb) -- the normal enter behaviour
for _, j in pairs(multi) do
if j.path ~= nil then -- is it a file -> open it as well:
vim.cmd(string.format("%s %s", "edit", j.path))
end
end
end,
}
return { defaults = { mappings = { i = mm, n = mm }, }, } |
this involves more than just opening multiple files, this should be resolved at action level so it can be made available as an interface. and that part makes it kinda hard with our action interface that allows to replace and enhance actions on consumer level. Its still on the roadmap and will be implemented once i have a little bit more time. sorry |
In case this helps anyone, I just modified a few of the above approaches to act more like fzf.vim, so that you can map and use a single local select_one_or_multi = function(prompt_bufnr)
local picker = require('telescope.actions.state').get_current_picker(prompt_bufnr)
local multi = picker:get_multi_selection()
if not vim.tbl_isempty(multi) then
require('telescope.actions').close(prompt_bufnr)
for _, j in pairs(multi) do
if j.path ~= nil then
vim.cmd(string.format('%s %s', 'edit', j.path))
end
end
else
require('telescope.actions').select_default(prompt_bufnr)
end
end This allows the user to map require('telescope').setup {
defaults = {
mappings = {
i = {
['<CR>'] = select_one_or_multi,
}
}
}
} |
If multiple file selections are detected, open each file. If hitting <CR> on a single selection fall back to the default behaviour. From: nvim-telescope/telescope.nvim#1048 (comment)
I would also like to chime in with another solution. I set local myactions = require("my.telescope.actions")
...
...
opts.defaults.mappings.i["<c-t>"] = myactions.select_tab
opts.defaults.mappings.i["<C-v>"] = myactions.select_vertical Then this is local M = {}
local transform_mod = require("telescope.actions.mt").transform_mod
local action_state = require "telescope.actions.state"
local state = require "telescope.state"
local telescope_pickers = require "telescope.pickers"
local action_set = require "telescope.actions.set"
local get_entries = function(prompt_bufnr)
local picker = action_state.get_current_picker(prompt_bufnr)
local multi_selection = picker:get_multi_selection()
return #multi_selection > 1 and multi_selection or { action_state.get_selected_entry() }
end
local set_status_with_close_func = function(prompt_bufnr, orig_status, orig_picker, close_func)
orig_status.picker = orig_picker
orig_picker.close_windows = close_func
state.set_status(prompt_bufnr, orig_status)
end
--- works around telescope's api (bad hack)
local get_action_set_edit_with_multi_support = function(command)
return function(prompt_bufnr)
local orig_status = state.get_status(prompt_bufnr)
local orig_picker = orig_status.picker
local orig_close_windows = orig_picker.close_windows
for _, entry in ipairs(get_entries(prompt_bufnr)) do
state.set_global_key("selected_entry", entry)
set_status_with_close_func(prompt_bufnr, orig_status, orig_picker, function() end)
action_set.edit(prompt_bufnr, command)
end
set_status_with_close_func(prompt_bufnr, orig_status, orig_picker, orig_close_windows)
telescope_pickers.on_close_prompt(prompt_bufnr)
end
end
--- Like actions.select_tab but supports multiple selections
M.select_tab = get_action_set_edit_with_multi_support(action_state.select_key_to_edit_key("tab"))
--- Like actions.select_vertical but supports multiple selections
M.select_vertical = get_action_set_edit_with_multi_support(action_state.select_key_to_edit_key("vertical"))
M = transform_mod(M)
return M The hackiest part might be of interest to @Conni2461 as a kind of a guideline for possible refactor of |
If anyone uses that, you can also pass row number if j.path ~= nil then
if j.lnum ~= nil then
vim.cmd(string.format("%s %s:%s", "edit", j.path, j.lnum))
else
vim.cmd(string.format("%s %s", "edit", j.path))
end
end |
@taras-biletskyi beautiful hack. And if I want to open the files in tabs I can replace |
This is what worked best for me: local actions = require "telescope.actions"
-- Opens marked items in a quickfix list.
-- if there are no marked items, it opens all items in a quickfix list.
local smart_send_to_qflist = function(prompt_bufnr)
local multi = picker:get_multi_selection()
local picker =
require("telescope.actions.state").get_current_picker(
prompt_bufnr
)
if not vim.tbl_isempty(multi) then
actions.send_selected_to_qflist(prompt_bufnr)
else
actions.send_to_qflist(prompt_bufnr)
end
actions.open_qflist(prompt_bufnr)
end
require "telescope".setup {
defaults = {
mappings = {
i = { ["<C-q>" = smart_send_to_qflist }
n = { ["<C-q>" = smart_send_to_qflist }
}
}
} |
tweaked @taras-biletskyi 's solution to open multiple files for live grep. local select_one_or_multi = function(prompt_bufnr)
local picker = require('telescope.actions.state').get_current_picker(prompt_bufnr)
local multi = picker:get_multi_selection()
if not vim.tbl_isempty(multi) then
require('telescope.actions').close(prompt_bufnr)
for _, j in pairs(multi) do
if j.path ~= nil then
if j.lnum ~= nil then
vim.cmd(string.format("%s +%s %s", "edit", j.lnum, j.path))
else
vim.cmd(string.format("%s %s", "edit", j.path))
end
end
end
else
require('telescope.actions').select_default(prompt_bufnr)
end
end |
I tweaked @lakshya-sky 's snippet to include columns! I found this works better for telescope's local multiopen = function(prompt_bufnr)
local picker = require('telescope.actions.state').get_current_picker(prompt_bufnr)
local multi = picker:get_multi_selection()
if vim.tbl_isempty(multi) then
require('telescope.actions').select_default(prompt_bufnr)
return
end
require('telescope.actions').close(prompt_bufnr)
for _, entry in pairs(multi) do
local filename = entry.filename or entry.value
local lnum = entry.lnum or 1
local lcol = entry.col or 1
if filename then
vim.cmd(string.format("tabnew +%d %s", lnum, filename))
vim.cmd(string.format("normal! %dG%d|", lnum, lcol))
end
end
end |
Adds a function for getting the multiple selections from the Telescope picker and invokes on <CR>. Works like fzf now. Fix from here: nvim-telescope/telescope.nvim#1048 (comment)
Is your feature request related to a problem? Please describe.
When using find_files, I would like to be able to open multiple results at once.
Describe the solution you'd like
In telescope, in normal mode, having the ability to toggle selection using space for instance, and then open them at once.
Describe alternatives you've considered
The only alternative was to open each file one by one by using the same search text, and try to remember which file I opened already.
Additional context
I find the need for this feature in 2 occasions, having multiple files with same name but in different folders and when I wanted to open all files specific to a feature in a codebase.
The text was updated successfully, but these errors were encountered: