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

Open multiple files at once #1048

Open
kishikaisei opened this issue Jul 25, 2021 · 56 comments
Open

Open multiple files at once #1048

kishikaisei opened this issue Jul 25, 2021 · 56 comments
Labels
enhancement Enhancement to performance, inner workings or existent features

Comments

@kishikaisei
Copy link

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 kishikaisei added the enhancement Enhancement to performance, inner workings or existent features label Jul 25, 2021
@caojoshua
Copy link
Contributor

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

@kishikaisei
Copy link
Author

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

@caojoshua
Copy link
Contributor

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

@fdschmidt93
Copy link
Member

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

@shlomocarmeter
Copy link

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
      }
    },
  }
}

@kishikaisei
Copy link
Author

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.

@Corey-Keller
Copy link

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.

@shlomocarmeter
Copy link

@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

@ghost
Copy link

ghost commented Sep 23, 2021

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)

@mrswats
Copy link

mrswats commented Oct 5, 2021

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

local custom_actions = {}

@jeetsukumaran
Copy link

jeetsukumaran commented Nov 29, 2021

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

@shlomocarmeter
Copy link

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

@jeetsukumaran
Copy link

jeetsukumaran commented Nov 30, 2021

The above does not work well when Telescope returns files that have a different cwd from the Neovim session.

Below is a fixed version.

@Racle
Copy link

Racle commented Nov 30, 2021

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

@joelpalmer
Copy link
Contributor

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

["<M-q>"] = actions.send_selected_to_qflist + actions.open_qflist,

...and then loop through and open them. But, the solution here has the desired behavior and experience.

This is fantastic @shlomocarmeter and @jeetsukumaran 🎸🚀

@jeetsukumaran
Copy link

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

@Racle
Copy link

Racle commented Nov 30, 2021

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

@joelpalmer
Copy link
Contributor

@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

@mottram
Copy link

mottram commented Nov 30, 2021

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

@jeetsukumaran
Copy link

jeetsukumaran commented Nov 30, 2021

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.

@mottram
Copy link

mottram commented Nov 30, 2021

That does the trick - thanks!

@joelpalmer
Copy link
Contributor

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!

@sQVe
Copy link

sQVe commented Dec 1, 2021

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

@ghost
Copy link

ghost commented Dec 1, 2021

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.

@joelpalmer
Copy link
Contributor

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

@jeetsukumaran
Copy link

jeetsukumaran commented Dec 1, 2021

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
Copy link

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.

@Integralist
Copy link

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
Copy link

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

@rebelot
Copy link

rebelot commented Aug 19, 2022

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,
    },
}

@mawkler
Copy link

mawkler commented Aug 20, 2022

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

@rebelot
Copy link

rebelot commented Aug 20, 2022

@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

@mawkler
Copy link

mawkler commented Aug 22, 2022

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!

@gstokkink
Copy link

gstokkink commented Aug 24, 2022

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

@rebelot
Copy link

rebelot commented Aug 24, 2022

@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

@gstokkink
Copy link

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

@shlomi-aknin
Copy link

@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

@aquillano
Copy link

Any chance we can get this addition/fix back into the core plugin?

@AXGKl
Copy link

AXGKl commented Jan 27, 2023

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 ?1cat 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 },  }, }
Screencast

@Conni2461
Copy link
Member

Any chance we can get this addition/fix back into the core plugin?

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

@jesseleite
Copy link
Contributor

jesseleite commented Aug 16, 2023

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 <CR> mapping for both multi AND single file selection...

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 <CR>. If multiple file selections are detected, it will edit/open each file. If hitting <CR> on a single selection, it'll fall back to actions.select_default...

require('telescope').setup {
  defaults = {
    mappings = {
      i = {
        ['<CR>'] = select_one_or_multi,
      }
    }
  }
}

jordelver added a commit to jordelver/dotfiles that referenced this issue Sep 4, 2023
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)
@grota
Copy link
Contributor

grota commented Feb 2, 2024

I would also like to chime in with another solution.

I set telescope.defaults.mappings like this:

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 ~/.config/nvim/lua/my/telescope/actions.lua

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

@taras-biletskyi
Copy link

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 <CR> mapping for both multi AND single file selection...

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 <CR>. If multiple file selections are detected, it will edit/open each file. If hitting <CR> on a single selection, it'll fall back to actions.select_default...

require('telescope').setup {
  defaults = {
    mappings = {
      i = {
        ['<CR>'] = select_one_or_multi,
      }
    }
  }
}

If anyone uses that, you can also pass row number j.lnum if you are using this for live grep and stuff. I found it handy.

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

@mr-j0nes
Copy link

mr-j0nes commented May 7, 2024

@taras-biletskyi beautiful hack. And if I want to open the files in tabs I can replace "edit" with "tabnew"

@marcelarie
Copy link
Contributor

marcelarie commented May 10, 2024

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

@lakshya-sky
Copy link

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

@qozymandias
Copy link

qozymandias commented Jun 19, 2024

I tweaked @lakshya-sky 's snippet to include columns! I found this works better for telescope's builtin.lsp_references function.

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

jersmith added a commit to jersmith/my-neovim-init that referenced this issue Jun 28, 2024
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)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement Enhancement to performance, inner workings or existent features
Projects
None yet
Development

No branches or pull requests