telescope.nvim icon indicating copy to clipboard operation
telescope.nvim copied to clipboard

Open multiple files at once

Open kishikaisei opened this issue 2 years ago β€’ 56 comments

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.

kishikaisei avatar Jul 25 '21 15:07 kishikaisei

With default bindings, you can select entries with <Tab> or <Shift>+<Tab>. You can somewhat get what you're looking for with <Alt> + q, which send the selected entries to the quickfix list. I believe that there is currently not an action to open select entries in a buffer, but could be implemented.

# toggle entry
require('telescope.actions').toggle_selection

# send selected to quickfix
require('telescope.actions').send_selected_to_qflist

defaults defined here

caojoshua avatar Jul 25 '21 23:07 caojoshua

That's an alternative way which seems promising, but how can you the said files from the qflist after sending the files there?

kishikaisei avatar Jul 26 '21 03:07 kishikaisei

but how can you the said files

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

caojoshua avatar Jul 26 '21 04:07 caojoshua

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 :)

fdschmidt93 avatar Jul 28 '21 06:07 fdschmidt93

Maybe until @fdschmidt93 finishes his PR something like this can help (That's what I'm currently using):

local actions = require('telescope.actions')
local action_state = require('telescope.actions.state')

function custom_actions.fzf_multi_select(prompt_bufnr)
  local picker = action_state.get_current_picker(prompt_bufnr)
  local num_selections = table.getn(picker:get_multi_selection())

  if num_selections > 1 then
    local picker = action_state.get_current_picker(prompt_bufnr)
    for _, entry in ipairs(picker:get_multi_selection()) do
      vim.cmd(string.format("%s %s", ":e!", entry.value))
    end
    vim.cmd('stopinsert')
  else
    actions.file_edit(prompt_bufnr)
  end
end

require('telescope').setup {
  defaults = {
    file_ignore_patterns = { "node_modules", ".git" },
    mappings = { 
      i = {
        ['<esc>'] = actions.close,
        ['<C-j>'] = actions.move_selection_next,
        ['<C-k>'] = actions.move_selection_previous,
        ['<tab>'] = actions.toggle_selection + actions.move_selection_next,
        ['<s-tab>'] = actions.toggle_selection + actions.move_selection_previous,
        ['<cr>'] = custom_actions.fzf_multi_select,
      },
      n = {
        ['<esc>'] = actions.close,
        ['<tab>'] = actions.toggle_selection + actions.move_selection_next,
        ['<s-tab>'] = actions.toggle_selection + actions.move_selection_previous,
        ['<cr>'] = custom_actions.fzf_multi_select
      }
    },
  }
}

shlomocarmeter avatar Jul 29 '21 12:07 shlomocarmeter

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. And it is very cool that I can search for some files, select them, clear the search query, search for other files, select them, and when I open the files, all of them are there. Very nice.

kishikaisei avatar Jul 29 '21 21:07 kishikaisei

The ability to open multiple files (and more specifically open in multiple new vertical splits with <C-v>) is one of the only features I miss from fzf at this point.

Corey-Keller avatar Aug 18 '21 22:08 Corey-Keller

@Corey-Keller you can change the line:

vim.cmd(string.format("%s %s", ":e!", entry.value))

to

vim.cmd(string.format("%s %s", ":vsplit!", entry.value))

in my solution above to achive openning in multiple vertical splits

shlomocarmeter avatar Aug 19 '21 09:08 shlomocarmeter

Maybe until @fdschmidt93 finishes his PR something like this can help (That's what I'm currently using):

@shlomocarmeter Sorry how do I add that? I've tried to put it into a lua <<EOF section in my init.vim but that didn't work ... I got an error:

E5108: Error executing lua [string ":lua"]:4: attempt to index global 'custom_actions' (a nil value)

ghost avatar Sep 23 '21 12:09 ghost

@stephanECD I believe you need to add a local object before the function is declared:

local custom_actions = {}

mrswats avatar Oct 05 '21 07:10 mrswats

@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).

~~The following modified code avoids this (and adds options for horizontal split, vertical split, and tab opening modes).~~

NOTE: See updated version below which fixes a number of issues.

jeetsukumaran avatar Nov 29 '21 20:11 jeetsukumaran

@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!

shlomocarmeter avatar Nov 29 '21 20:11 shlomocarmeter

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 avatar Nov 30 '21 03:11 jeetsukumaran

@jeetsukumaran Nice, original works better than updated.

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.

Racle avatar Nov 30 '21 07:11 Racle

@jeetsukumaran Nice, original works better than updated.

If I use updated version, it opens file (correct name in top bar) but buffer is empty. Original seems to be working as intended.

Same experience for me. I'm not using airblade/vim-rooter. I tried open and open_vsplit and they worked beautifully with @jeetsukumaran's original.
Also, the length operator gets rid of the getn deprecation warning.

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 QuickFix list... https://github.com/nvim-telescope/telescope.nvim/blob/1c57cc6140644695f0d9bd71b63de45feeca6ae7/lua/telescope/mappings.lua#L49 ...and then loop through and open them. But, the solution here has the desired behavior and experience.

This is fantastic @shlomocarmeter and @jeetsukumaran πŸŽΈπŸš€

joelpalmer avatar Nov 30 '21 10:11 joelpalmer

@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 (/ vs \), this might be an issue?

jeetsukumaran avatar Nov 30 '21 10:11 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 (/ vs \), this might be an issue?

Nope, pop_os 21.04 and neovim v0.6.0-dev.

Racle avatar Nov 30 '21 10:11 Racle

@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 (/ vs \), this might be an issue?

Nope, pop_os 21.04 and neovim v0.6.0-dev.

macOS 11.6 and NVIM v0.6.0-dev+647-gb16b7021e
@jeetsukumaran

joelpalmer avatar Nov 30 '21 10:11 joelpalmer

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 nil.

E.g., if I select the files ./dir/foo.txt & ./bar.txt and hit enter, I get an empty buffer because Telescope has opened nil/./dir/foo.txt & nil/./bar.txt.

This is on macOS 10.15.7, nvim v0.6.0-dev+646-g6e70b7b31

mottram avatar Nov 30 '21 14:11 mottram

What must be happening is that local cwd = picker.cwd is resulting in nil. Maybe because it was not specified and does not default to anything? Version below fixes this and some other issues.

jeetsukumaran avatar Nov 30 '21 20:11 jeetsukumaran

That does the trick - thanks!

mottram avatar Nov 30 '21 22:11 mottram

That does the trick - thanks!

Yep! I am on NVIM v0.7.0-dev+653-g7229afcd6 and MacOS 11.6. This last revision works perfectly. I also had a bug in my config that was giving me repros that didn't match the original use case. It's wonderful to have this multi-selection solution working. Thank you @jeetsukumaran!

joelpalmer avatar Nov 30 '21 22:11 joelpalmer

Anyone know how I could close dashboard or alpha after opening files with the custom multi_selection_open action? It currently stays open.

sQVe avatar Dec 01 '21 08:12 sQVe

Wouldn't it make sense to set the default for open split to c-x to keep it compatible to fzf.vim?

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.

ghost avatar Dec 01 '21 12:12 ghost

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 file_edit) to be used in the case where only one item is selected and file_edit is not an appropriate action.

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
...   

joelpalmer avatar Dec 01 '21 13:12 joelpalmer

Wouldn't it make sense to set the default for open split to c-x to keep it compatible to fzf.vim?

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.

You are free to use whatever key-mapping you like. I just use <C-s>.

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.

jeetsukumaran avatar Dec 01 '21 19:12 jeetsukumaran

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 file_edit) to be used in the case where only one item is selected and file_edit is not an appropriate action.

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
...   

Nice! Thanks for sharing.

jeetsukumaran avatar Dec 01 '21 19:12 jeetsukumaran

So, this might break (again) things, and clobbers the current quickfix list, but it fixes a number of other issues:

  • it goes to the correct line number if available
  • works the same for single/multiple selections
  • handles different pickers populating the selection entry in different ways (e.g., path, filename, value; this is probably something that should homogenized and made consistent by developer decree eventually).
  • correctly opens the files at the correct locations in the correct window.

As noted, it clobbers the quickfix list. It works through populating the quickfix list through Telescope, and then opening the files. It's pretty straightforward to add some logic that saves/restores the quickfix list. I'll post an updated version if I have time to put that together.

local telescope_custom_actions = {}

function telescope_custom_actions._multiopen(prompt_bufnr, open_cmd)
    local picker = action_state.get_current_picker(prompt_bufnr)
    local selected_entry = action_state.get_selected_entry()
    local num_selections = #picker:get_multi_selection()
    if not num_selections or num_selections <= 1 then
        actions.add_selection(prompt_bufnr)
    end
    actions.send_selected_to_qflist(prompt_bufnr)
    vim.cmd("cfdo " .. open_cmd)
end
function telescope_custom_actions.multi_selection_open_vsplit(prompt_bufnr)
    telescope_custom_actions._multiopen(prompt_bufnr, "vsplit")
end
function telescope_custom_actions.multi_selection_open_split(prompt_bufnr)
    telescope_custom_actions._multiopen(prompt_bufnr, "split")
end
function telescope_custom_actions.multi_selection_open_tab(prompt_bufnr)
    telescope_custom_actions._multiopen(prompt_bufnr, "tabe")
end
function telescope_custom_actions.multi_selection_open(prompt_bufnr)
    telescope_custom_actions._multiopen(prompt_bufnr, "edit")
end

require('telescope').setup({
    defaults = {
        mappings = {
            i = {
                ["<ESC>"] = actions.close,
                ["<C-J>"] = actions.move_selection_next,
                ["<C-K>"] = actions.move_selection_previous,
                ["<TAB>"] = actions.toggle_selection,
                ["<C-TAB>"] = actions.toggle_selection + actions.move_selection_next,
                ["<S-TAB>"] = actions.toggle_selection + actions.move_selection_previous,
                ["<CR>"] = telescope_custom_actions.multi_selection_open,
                ["<C-V>"] = telescope_custom_actions.multi_selection_open_vsplit,
                ["<C-S>"] = telescope_custom_actions.multi_selection_open_split,
                ["<C-T>"] = telescope_custom_actions.multi_selection_open_tab,
                ["<C-DOWN>"] = require('telescope.actions').cycle_history_next,
                ["<C-UP>"] = require('telescope.actions').cycle_history_prev,
            },
            n = i,
        },

   }
})

jeetsukumaran avatar Dec 14 '21 20:12 jeetsukumaran

I'm not sure if something changed, but I tried @jeetsukumaran's workaround, and it always opens the first selection twice. For example, if I multi-select "a.txt" and "b.txt", then press c-s, I get three splits open:

  • a.txt
  • a.txt
  • b.txt

The quickfix list contains two files, as expected.

rcorre avatar Jan 23 '22 17:01 rcorre

I get three splits open

I suspect what's happening here is that the first split was your original window that was open before you launched Telescope. Your command creates a new split for each file you try to open, but does not try to close any existing split. So if there are two files that's two extra splits, plus the original file for a total of three.

jeetsukumaran avatar Jan 23 '22 19:01 jeetsukumaran

Possibly? Though even if the first split is an empty buffer, it gets filled with a.txt. It's as if telescope is doing a regular open action, then opening two splits.

rcorre avatar Jan 23 '22 22:01 rcorre

@jeetsukumaran Your solution works great, except that it clashes with other selectors which makes <CR> behave strangely. Is it possible to give these mappings only to find_files, so that I don't have to put them in default?

Edit: Here's how I solved it if anyone else is having the same problem:

require('telescope').setup {
  pickers = {
    find_files = {
      mappings = {
        i = {
          ['<CR>'] = telescope_custom_actions.multi_selection_open,
          ['<C-v>'] = telescope_custom_actions.multi_selection_open_vsplit,
          ['<C-s>'] = telescope_custom_actions.multi_selection_open_split,
          ['<C-t>'] = telescope_custom_actions.multi_selection_open_tab,
        }
      }
    }
  }
}

mawkler avatar Jan 24 '22 15:01 mawkler

Here's my version which (I think) avoids @rcorre's problem:

local multiopen = function(prompt_bufnr, open_cmd)
  local picker = action_state.get_current_picker(prompt_bufnr)
  local num_selections = #picker:get_multi_selection()
  if not num_selections or num_selections <= 1 then
    actions.add_selection(prompt_bufnr)
  end
  actions.send_selected_to_qflist(prompt_bufnr)

  local results = vim.fn.getqflist()

  for _, result in ipairs(results) do
    local current_file = vim.fn.bufname()
    local next_file = vim.fn.bufname(result.bufnr)

    if current_file == "" then
      vim.api.nvim_command("edit" .. " " .. next_file)
    else
      vim.api.nvim_command(open_cmd .. " " .. next_file)
    end
  end

  vim.api.nvim_command("cd .")
end

https://github.com/AlexChalk/dotfiles/blob/aa976ab595c61ae6edf0acd51dc8523473f1d9b7/nvim/lua/conf/telescope.lua#L4-L27

The pushing/pulling from quickfix is kind of silly since I'm not using cfdo, but I wasn't sure how else to get a list of filenames to open.

AlexChalk avatar Mar 26 '22 23:03 AlexChalk

I know this issue is close to stale by now, but if anyone comes by and would like to solve this without the qf list being messed up, here's how I did it:

local opts_ff = { attach_mappings = function(prompt_bufnr, map)
	local actions = require "telescope.actions"
	actions.select_default:replace(
		function(prompt_bufnr)
			local actions = require "telescope.actions"
			local state = require "telescope.actions.state"
			local picker = state.get_current_picker(prompt_bufnr)
			local multi = picker:get_multi_selection()
			local single = picker:get_selection()
			local str = ""
			if #multi > 0 then
				for i,j in pairs(multi) do
					str = str.."edit "..j[1].." | "
				end
			end
			str = str.."edit "..single[1]
			-- To avoid populating qf or doing ":edit! file", close the prompt first
			actions.close(prompt_bufnr)
			vim.api.nvim_command(str)
		end)
	return true
end }
-- And then to call find_files with a mapping or whatever:
vim.keymap.set('n', '<C-f>', function() return require("telescope.builtin").find_files(opts_ff) end, s)

henriqpsantos avatar May 28 '22 15:05 henriqpsantos

I know this issue is close to stale by now, but if anyone comes by and would like to solve this without the qf list being messed up, vim.keymap.set('n', '<C-f>', function() return require("telescope.builtin").find_files(opts_ff) end, s)

@dartacao Thanks, could you advise how to use a command :MultiOpen instead of <c-f> ?

ssh352 avatar Jun 25 '22 06:06 ssh352

@ssh352 Yeah, I think it shouldn't be too tough. maybe you can just do something like:

vim.api.nvim_create_user_command(
  "MultiOpen",
  function()
    return require("telescope.builtin").find_files(opts_ff)
  end)

I think that should work, though I haven't actually tried it. See this for more info.

henriqpsantos avatar Jun 27 '22 09:06 henriqpsantos

I tried @jeetsukumaran solution and just get:

E5108: Error executing lua [string ":lua"]:4: attempt to index global 'action_state' (a nil value)
stack traceback:
        [string ":lua"]:4: in function '_multiopen'
        [string ":lua"]:14: in function 'key_func'
        ...e/nvim/plugged/telescope.nvim/lua/telescope/mappings.lua:242: in function 'execute_keymap'
        [string ":lua"]:1: in main chunk

Integralist avatar Jul 01 '22 18:07 Integralist

I also tried @dartacao solution and that didn't error but it also didn't work, it would just open a random file instead.

Integralist avatar Jul 01 '22 18:07 Integralist

My take on this:

  • it works regardless of working directory
  • it works regardless of multi or single selection
  • execute callbacks from normal mode to avoid common problems with TS folds
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 avatar Aug 19 '22 16:08 rebelot

@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.

mawkler avatar Aug 20 '22 08:08 mawkler

@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)

... What am I doing wrong? I'm on Neovim nightly NVIM v0.8.0-dev+580-g45ba2e147.

vim.cmd can now be indexed, so update nightly or replace vim.cmd.stopinsert() with vim.cmd("stopinsert"). I'd also recommend to no use it as default mappings, because it is not safe on non file-based pickers. Something like:

    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

rebelot avatar Aug 20 '22 12:08 rebelot

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!

mawkler avatar Aug 22 '22 10:08 mawkler

@rebelot your solution works perfectly for find_files, thanks a lot! It doesn't work yet with some other finders like grep_string, but it seems you are already aware of this. This really should have been default functionality by now.

gstokkink avatar Aug 24 '22 14:08 gstokkink

@gstokkink @melkster

You can find a working solution that should work as a default mapping, supporting line and columns: part of the logic was salvaged from the default action. I'd like to have the feedback of some of the devs for this.

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 avatar Aug 24 '22 16:08 rebelot

@rebelot seems like that works perfectly, also for live_grep et al :) Thanks!

gstokkink avatar Aug 25 '22 06:08 gstokkink

@gstokkink @melkster

You can find a working solution that should work as a default mapping, supporting line and columns: part of the logic was salvaged from the default action. I'd like to have the feedback of some of the devs for this.

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 This is awesome man! just a small change for this: pcall(vim.api.nvim_win_set_cursor, 0, { row, col-1 }) to get to the right column

shlomi-aknin avatar Aug 25 '22 17:08 shlomi-aknin