diff --git a/lua/telescope/builtin/__internal.lua b/lua/telescope/builtin/__internal.lua index 1687401eb0..139185e04a 100644 --- a/lua/telescope/builtin/__internal.lua +++ b/lua/telescope/builtin/__internal.lua @@ -14,1509 +14,1520 @@ local conf = require("telescope.config").values -- Makes sure aliased options are set correctly local function apply_cwd_only_aliases(opts) - local has_cwd_only = opts.cwd_only ~= nil - local has_only_cwd = opts.only_cwd ~= nil + local has_cwd_only = opts.cwd_only ~= nil + local has_only_cwd = opts.only_cwd ~= nil - if has_only_cwd and not has_cwd_only then - -- Internally, use cwd_only - opts.cwd_only = opts.only_cwd - opts.only_cwd = nil - end + if has_only_cwd and not has_cwd_only then + -- Internally, use cwd_only + opts.cwd_only = opts.only_cwd + opts.only_cwd = nil + end - return opts + return opts end ---@return boolean local function buf_in_cwd(bufname, cwd) - if cwd:sub(-1) ~= Path.path.sep then - cwd = cwd .. Path.path.sep - end - local bufname_prefix = bufname:sub(1, #cwd) - return bufname_prefix == cwd + if cwd:sub(-1) ~= Path.path.sep then + cwd = cwd .. Path.path.sep + end + local bufname_prefix = bufname:sub(1, #cwd) + return bufname_prefix == cwd end local internal = {} internal.builtin = function(opts) - opts.include_extensions = vim.F.if_nil(opts.include_extensions, false) - opts.use_default_opts = vim.F.if_nil(opts.use_default_opts, false) - - local objs = {} - - for k, v in pairs(require "telescope.builtin") do - local debug_info = debug.getinfo(v) - table.insert(objs, { - filename = string.sub(debug_info.source, 2), - text = k, - }) - end - - local title = "Telescope Builtin" - - if opts.include_extensions then - title = "Telescope Pickers" - for ext, funcs in pairs(require("telescope").extensions) do - for func_name, func_obj in pairs(funcs) do - -- Only include exported functions whose name doesn't begin with an underscore - if type(func_obj) == "function" and string.sub(func_name, 0, 1) ~= "_" then - local debug_info = debug.getinfo(func_obj) - table.insert(objs, { - filename = string.sub(debug_info.source, 2), - text = string.format("%s : %s", ext, func_name), - }) - end - end - end - end - - table.sort(objs, function(a, b) - return a.text < b.text - end) - - opts.bufnr = vim.api.nvim_get_current_buf() - opts.winnr = vim.api.nvim_get_current_win() - pickers - .new(opts, { - prompt_title = title, - finder = finders.new_table { - results = objs, - entry_maker = function(entry) - return make_entry.set_default_entry_mt({ - value = entry, - text = entry.text, - display = entry.text, - ordinal = entry.text, - filename = entry.filename, - }, opts) - end, - }, - previewer = previewers.builtin.new(opts), - sorter = conf.generic_sorter(opts), - attach_mappings = function(_) - actions.select_default:replace(function(prompt_bufnr) - local selection = action_state.get_selected_entry() - if not selection then - utils.__warn_no_selection "builtin.builtin" - return - end - - -- we do this to avoid any surprises - opts.include_extensions = nil - - local picker_opts - if not opts.use_default_opts then - picker_opts = opts - end - - actions.close(prompt_bufnr) - vim.schedule(function() - if string.match(selection.text, " : ") then - -- Call appropriate function from extensions - local split_string = vim.split(selection.text, " : ") - local ext = split_string[1] - local func = split_string[2] - require("telescope").extensions[ext][func](picker_opts) - else - -- Call appropriate telescope builtin - require("telescope.builtin")[selection.text](picker_opts) - end - end) - end) - return true - end, - }) - :find() + opts.include_extensions = vim.F.if_nil(opts.include_extensions, false) + opts.use_default_opts = vim.F.if_nil(opts.use_default_opts, false) + + local objs = {} + if opts.title==nil then + opts.title = "Telescope Builtin" + end + + for k, v in pairs(require "telescope.builtin") do + if opts.filter_match == nil or (k:match(opts.filter_match) ~= nil) then + local debug_info = debug.getinfo(v) + table.insert(objs, { + filename = string.sub(debug_info.source, 2), + text = k, + }) + end + end + + + if opts.include_extensions then + for ext, funcs in pairs(require("telescope").extensions) do + for func_name, func_obj in pairs(funcs) do + print(opts.filter_match) + -- Only include exported functions whose name doesn't begin with an underscore + if type(func_obj) == "function" and string.sub(func_name, 0, 1) ~= "_" then + local debug_info = debug.getinfo(func_obj) + table.insert(objs, { + filename = string.sub(debug_info.source, 2), + text = string.format("%s : %s", ext, func_name), + }) + end + end + end + end + + table.sort(objs, function(a, b) + return a.text < b.text + end) + + opts.bufnr = vim.api.nvim_get_current_buf() + opts.winnr = vim.api.nvim_get_current_win() + pickers + .new(opts, { + prompt_title = opts.title, + finder = finders.new_table { + results = objs, + entry_maker = function(entry) + return make_entry.set_default_entry_mt({ + value = entry, + text = entry.text, + display = entry.text, + ordinal = entry.text, + filename = entry.filename, + }, opts) + end, + }, + previewer = previewers.builtin.new(opts), + sorter = conf.generic_sorter(opts), + attach_mappings = function(_) + actions.select_default:replace(function(prompt_bufnr) + local selection = action_state.get_selected_entry() + if not selection then + utils.__warn_no_selection "builtin.builtin" + return + end + + -- we do this to avoid any surprises + opts.include_extensions = nil + + local picker_opts + if not opts.use_default_opts then + picker_opts = opts + end + + actions.close(prompt_bufnr) + vim.schedule(function() + if string.match(selection.text, " : ") then + -- Call appropriate function from extensions + local split_string = vim.split(selection.text, " : ") + local ext = split_string[1] + local func = split_string[2] + require("telescope").extensions[ext][func](picker_opts) + else + -- Call appropriate telescope builtin + require("telescope.builtin")[selection.text](picker_opts) + end + end) + end) + return true + end, + }) + :find() end internal.resume = function(opts) - opts = opts or {} - opts.cache_index = vim.F.if_nil(opts.cache_index, 1) - - local cached_pickers = state.get_global_key "cached_pickers" - if cached_pickers == nil or vim.tbl_isempty(cached_pickers) then - utils.notify("builtin.resume", { - msg = "No cached picker(s).", - level = "INFO", - }) - return - end - local picker = cached_pickers[opts.cache_index] - if picker == nil then - utils.notify("builtin.resume", { - msg = string.format("Index too large as there are only '%s' pickers cached", #cached_pickers), - level = "ERROR", - }) - return - end - -- reset layout strategy and get_window_options if default as only one is valid - -- and otherwise unclear which was actually set - if picker.layout_strategy == conf.layout_strategy then - picker.layout_strategy = nil - end - if picker.get_window_options == p_window.get_window_options then - picker.get_window_options = nil - end - picker.cache_picker.index = opts.cache_index - - -- avoid partial `opts.cache_picker` at picker creation - if opts.cache_picker ~= false then - picker.cache_picker = vim.tbl_extend("keep", opts.cache_picker or {}, picker.cache_picker) - else - picker.cache_picker.disabled = true - end - opts.cache_picker = nil - picker.previewer = picker.all_previewers - if picker.hidden_previewer then - picker.hidden_previewer = nil - opts.previewer = vim.F.if_nil(opts.previewer, false) - end - opts.resumed_picker = true - pickers.new(opts, picker):find() + opts = opts or {} + opts.cache_index = vim.F.if_nil(opts.cache_index, 1) + + local cached_pickers = state.get_global_key "cached_pickers" + if cached_pickers == nil or vim.tbl_isempty(cached_pickers) then + utils.notify("builtin.resume", { + msg = "No cached picker(s).", + level = "INFO", + }) + return + end + local picker = cached_pickers[opts.cache_index] + if picker == nil then + utils.notify("builtin.resume", { + msg = string.format("Index too large as there are only '%s' pickers cached", #cached_pickers), + level = "ERROR", + }) + return + end + -- reset layout strategy and get_window_options if default as only one is valid + -- and otherwise unclear which was actually set + if picker.layout_strategy == conf.layout_strategy then + picker.layout_strategy = nil + end + if picker.get_window_options == p_window.get_window_options then + picker.get_window_options = nil + end + picker.cache_picker.index = opts.cache_index + + -- avoid partial `opts.cache_picker` at picker creation + if opts.cache_picker ~= false then + picker.cache_picker = vim.tbl_extend("keep", opts.cache_picker or {}, picker.cache_picker) + else + picker.cache_picker.disabled = true + end + opts.cache_picker = nil + picker.previewer = picker.all_previewers + if picker.hidden_previewer then + picker.hidden_previewer = nil + opts.previewer = vim.F.if_nil(opts.previewer, false) + end + opts.resumed_picker = true + pickers.new(opts, picker):find() end internal.pickers = function(opts) - local cached_pickers = state.get_global_key "cached_pickers" - if cached_pickers == nil or vim.tbl_isempty(cached_pickers) then - utils.notify("builtin.pickers", { - msg = "No cached picker(s).", - level = "INFO", - }) - return - end - - opts = opts or {} - - -- clear cache picker for immediate pickers.new and pass option to resumed picker - if opts.cache_picker ~= nil then - opts._cache_picker = opts.cache_picker - opts.cache_picker = nil - end - - pickers - .new(opts, { - prompt_title = "Pickers", - finder = finders.new_table { - results = cached_pickers, - entry_maker = make_entry.gen_from_picker(opts), - }, - previewer = previewers.pickers.new(opts), - sorter = conf.generic_sorter(opts), - cache_picker = false, - attach_mappings = function(_, map) - actions.select_default:replace(function(prompt_bufnr) - local curr_picker = action_state.get_current_picker(prompt_bufnr) - local curr_entry = action_state.get_selected_entry() - if not curr_entry then - return - end - - actions.close(prompt_bufnr) - - local selection_index, _ = utils.list_find(function(v) - if curr_entry.value == v.value then - return true - end - return false - end, curr_picker.finder.results) - - opts.cache_picker = opts._cache_picker - opts["cache_index"] = selection_index - opts["initial_mode"] = cached_pickers[selection_index].initial_mode - internal.resume(opts) - end) - map({ "i", "n" }, "", actions.remove_selected_picker) - return true - end, - }) - :find() + local cached_pickers = state.get_global_key "cached_pickers" + if cached_pickers == nil or vim.tbl_isempty(cached_pickers) then + utils.notify("builtin.pickers", { + msg = "No cached picker(s).", + level = "INFO", + }) + return + end + + opts = opts or {} + + -- clear cache picker for immediate pickers.new and pass option to resumed picker + if opts.cache_picker ~= nil then + opts._cache_picker = opts.cache_picker + opts.cache_picker = nil + end + + pickers + .new(opts, { + prompt_title = "Pickers", + finder = finders.new_table { + results = cached_pickers, + entry_maker = make_entry.gen_from_picker(opts), + }, + previewer = previewers.pickers.new(opts), + sorter = conf.generic_sorter(opts), + cache_picker = false, + attach_mappings = function(_, map) + actions.select_default:replace(function(prompt_bufnr) + local curr_picker = action_state.get_current_picker(prompt_bufnr) + local curr_entry = action_state.get_selected_entry() + if not curr_entry then + return + end + + actions.close(prompt_bufnr) + + local selection_index, _ = utils.list_find(function(v) + if curr_entry.value == v.value then + return true + end + return false + end, curr_picker.finder.results) + + opts.cache_picker = opts._cache_picker + opts["cache_index"] = selection_index + opts["initial_mode"] = cached_pickers[selection_index].initial_mode + internal.resume(opts) + end) + map({ "i", "n" }, "", actions.remove_selected_picker) + return true + end, + }) + :find() end internal.planets = function(opts) - local show_pluto = opts.show_pluto or false - local show_moon = opts.show_moon or false - - local sourced_file = require("plenary.debug_utils").sourced_filepath() - local base_directory = vim.fn.fnamemodify(sourced_file, ":h:h:h:h") - - local globbed_files = vim.fn.globpath(base_directory .. "/data/memes/planets/", "*", true, true) - local acceptable_files = {} - for _, v in ipairs(globbed_files) do - if (show_pluto or not v:find "pluto") and (show_moon or not v:find "moon") then - table.insert(acceptable_files, vim.fn.fnamemodify(v, ":t")) - end - end - - pickers - .new(opts, { - prompt_title = "Planets", - finder = finders.new_table { - results = acceptable_files, - entry_maker = function(line) - return make_entry.set_default_entry_mt({ - ordinal = line, - display = line, - filename = base_directory .. "/data/memes/planets/" .. line, - }, opts) - end, - }, - previewer = previewers.cat.new(opts), - sorter = conf.generic_sorter(opts), - attach_mappings = function(prompt_bufnr) - actions.select_default:replace(function() - local selection = action_state.get_selected_entry() - if selection == nil then - utils.__warn_no_selection "builtin.planets" - return - end - - actions.close(prompt_bufnr) - print("Enjoy astronomy! You viewed:", selection.display) - end) - - return true - end, - }) - :find() + local show_pluto = opts.show_pluto or false + local show_moon = opts.show_moon or false + + local sourced_file = require("plenary.debug_utils").sourced_filepath() + local base_directory = vim.fn.fnamemodify(sourced_file, ":h:h:h:h") + + local globbed_files = vim.fn.globpath(base_directory .. "/data/memes/planets/", "*", true, true) + local acceptable_files = {} + for _, v in ipairs(globbed_files) do + if (show_pluto or not v:find "pluto") and (show_moon or not v:find "moon") then + table.insert(acceptable_files, vim.fn.fnamemodify(v, ":t")) + end + end + + pickers + .new(opts, { + prompt_title = "Planets", + finder = finders.new_table { + results = acceptable_files, + entry_maker = function(line) + return make_entry.set_default_entry_mt({ + ordinal = line, + display = line, + filename = base_directory .. "/data/memes/planets/" .. line, + }, opts) + end, + }, + previewer = previewers.cat.new(opts), + sorter = conf.generic_sorter(opts), + attach_mappings = function(prompt_bufnr) + actions.select_default:replace(function() + local selection = action_state.get_selected_entry() + if selection == nil then + utils.__warn_no_selection "builtin.planets" + return + end + + actions.close(prompt_bufnr) + print("Enjoy astronomy! You viewed:", selection.display) + end) + + return true + end, + }) + :find() end internal.symbols = function(opts) - local initial_mode = vim.fn.mode() - local files = vim.api.nvim_get_runtime_file("data/telescope-sources/*.json", true) - local data_path = (function() - if not opts.symbol_path then - return Path:new { vim.fn.stdpath "data", "telescope", "symbols" } - else - return Path:new { opts.symbol_path } - end - end)() - if data_path:exists() then - for _, v in ipairs(require("plenary.scandir").scan_dir(data_path:absolute(), { search_pattern = "%.json$" })) do - table.insert(files, v) - end - end - - if #files == 0 then - utils.notify("builtin.symbols", { - msg = "No sources found! Check out https://github.com/nvim-telescope/telescope-symbols.nvim " - .. "for some prebuild symbols or how to create you own symbol source.", - level = "ERROR", - }) - return - end - - local sources = {} - if opts.sources then - for _, v in ipairs(files) do - for _, s in ipairs(opts.sources) do - if v:find(s) then - table.insert(sources, v) - end - end - end - else - sources = files - end - - local results = {} - for _, source in ipairs(sources) do - local data = vim.json.decode(Path:new(source):read()) - for _, entry in ipairs(data) do - table.insert(results, entry) - end - end - - pickers - .new(opts, { - prompt_title = "Symbols", - finder = finders.new_table { - results = results, - entry_maker = function(entry) - return make_entry.set_default_entry_mt({ - value = entry, - ordinal = entry[1] .. " " .. entry[2], - display = entry[1] .. " " .. entry[2], - }, opts) - end, - }, - sorter = conf.generic_sorter(opts), - attach_mappings = function(_) - if initial_mode == "i" then - actions.select_default:replace(actions.insert_symbol_i) - else - actions.select_default:replace(actions.insert_symbol) - end - return true - end, - }) - :find() + local initial_mode = vim.fn.mode() + local files = vim.api.nvim_get_runtime_file("data/telescope-sources/*.json", true) + local data_path = (function() + if not opts.symbol_path then + return Path:new { vim.fn.stdpath "data", "telescope", "symbols" } + else + return Path:new { opts.symbol_path } + end + end)() + if data_path:exists() then + for _, v in ipairs(require("plenary.scandir").scan_dir(data_path:absolute(), { search_pattern = "%.json$" })) do + table.insert(files, v) + end + end + + if #files == 0 then + utils.notify("builtin.symbols", { + msg = "No sources found! Check out https://github.com/nvim-telescope/telescope-symbols.nvim " + .. "for some prebuild symbols or how to create you own symbol source.", + level = "ERROR", + }) + return + end + + local sources = {} + if opts.sources then + for _, v in ipairs(files) do + for _, s in ipairs(opts.sources) do + if v:find(s) then + table.insert(sources, v) + end + end + end + else + sources = files + end + + local results = {} + for _, source in ipairs(sources) do + local data = vim.json.decode(Path:new(source):read()) + for _, entry in ipairs(data) do + table.insert(results, entry) + end + end + + pickers + .new(opts, { + prompt_title = "Symbols", + finder = finders.new_table { + results = results, + entry_maker = function(entry) + return make_entry.set_default_entry_mt({ + value = entry, + ordinal = entry[1] .. " " .. entry[2], + display = entry[1] .. " " .. entry[2], + }, opts) + end, + }, + sorter = conf.generic_sorter(opts), + attach_mappings = function(_) + if initial_mode == "i" then + actions.select_default:replace(actions.insert_symbol_i) + else + actions.select_default:replace(actions.insert_symbol) + end + return true + end, + }) + :find() end internal.commands = function(opts) - pickers - .new(opts, { - prompt_title = "Commands", - finder = finders.new_table { - results = (function() - local command_iter = vim.api.nvim_get_commands {} - local commands = {} - - for _, cmd in pairs(command_iter) do - table.insert(commands, cmd) - end - - local need_buf_command = vim.F.if_nil(opts.show_buf_command, true) - - if need_buf_command then - local buf_command_iter = vim.api.nvim_buf_get_commands(0, {}) - buf_command_iter[true] = nil -- remove the redundant entry - for _, cmd in pairs(buf_command_iter) do - table.insert(commands, cmd) - end - end - return commands - end)(), - - entry_maker = opts.entry_maker or make_entry.gen_from_commands(opts), - }, - sorter = conf.generic_sorter(opts), - attach_mappings = function(prompt_bufnr) - actions.select_default:replace(function() - local selection = action_state.get_selected_entry() - if selection == nil then - utils.__warn_no_selection "builtin.commands" - return - end - - actions.close(prompt_bufnr) - local val = selection.value - local cmd = string.format([[:%s ]], val.name) - - if val.nargs == "0" then - local cr = vim.api.nvim_replace_termcodes("", true, false, true) - cmd = cmd .. cr - end - vim.cmd [[stopinsert]] - vim.api.nvim_feedkeys(cmd, "nt", false) - end) - - return true - end, - }) - :find() + pickers + .new(opts, { + prompt_title = "Commands", + finder = finders.new_table { + results = (function() + local command_iter = vim.api.nvim_get_commands {} + local commands = {} + + for _, cmd in pairs(command_iter) do + table.insert(commands, cmd) + end + + local need_buf_command = vim.F.if_nil(opts.show_buf_command, true) + + if need_buf_command then + local buf_command_iter = vim.api.nvim_buf_get_commands(0, {}) + buf_command_iter[true] = nil -- remove the redundant entry + for _, cmd in pairs(buf_command_iter) do + table.insert(commands, cmd) + end + end + return commands + end)(), + + entry_maker = opts.entry_maker or make_entry.gen_from_commands(opts), + }, + sorter = conf.generic_sorter(opts), + attach_mappings = function(prompt_bufnr) + actions.select_default:replace(function() + local selection = action_state.get_selected_entry() + if selection == nil then + utils.__warn_no_selection "builtin.commands" + return + end + + actions.close(prompt_bufnr) + local val = selection.value + local cmd = string.format([[:%s ]], val.name) + + if val.nargs == "0" then + local cr = vim.api.nvim_replace_termcodes("", true, false, true) + cmd = cmd .. cr + end + vim.cmd [[stopinsert]] + vim.api.nvim_feedkeys(cmd, "nt", false) + end) + + return true + end, + }) + :find() end internal.quickfix = function(opts) - local qf_identifier = opts.id or vim.F.if_nil(opts.nr, "$") - local locations = vim.fn.getqflist({ [opts.id and "id" or "nr"] = qf_identifier, items = true }).items - - if vim.tbl_isempty(locations) then - utils.notify("builtin.quickfix", { msg = "No quickfix items", level = "INFO" }) - return - end - - pickers - .new(opts, { - prompt_title = "Quickfix", - finder = finders.new_table { - results = locations, - entry_maker = opts.entry_maker or make_entry.gen_from_quickfix(opts), - }, - previewer = conf.qflist_previewer(opts), - sorter = conf.generic_sorter(opts), - }) - :find() + local qf_identifier = opts.id or vim.F.if_nil(opts.nr, "$") + local locations = vim.fn.getqflist({ [opts.id and "id" or "nr"] = qf_identifier, items = true }).items + + if vim.tbl_isempty(locations) then + utils.notify("builtin.quickfix", { msg = "No quickfix items", level = "INFO" }) + return + end + + pickers + .new(opts, { + prompt_title = "Quickfix", + finder = finders.new_table { + results = locations, + entry_maker = opts.entry_maker or make_entry.gen_from_quickfix(opts), + }, + previewer = conf.qflist_previewer(opts), + sorter = conf.generic_sorter(opts), + }) + :find() end internal.quickfixhistory = function(opts) - local qflists = {} - for i = 1, 10 do -- (n)vim keeps at most 10 quickfix lists in full - -- qf weirdness: id = 0 gets id of quickfix list nr - local qflist = vim.fn.getqflist { nr = i, id = 0, title = true, items = true } - if not vim.tbl_isempty(qflist.items) then - table.insert(qflists, qflist) - end - end - local entry_maker = opts.make_entry - or function(entry) - return make_entry.set_default_entry_mt({ - value = entry.title or "Untitled", - ordinal = entry.title or "Untitled", - display = entry.title or "Untitled", - nr = entry.nr, - id = entry.id, - items = entry.items, - }, opts) - end - local qf_entry_maker = make_entry.gen_from_quickfix(opts) - pickers - .new(opts, { - prompt_title = "Quickfix History", - finder = finders.new_table { - results = qflists, - entry_maker = entry_maker, - }, - previewer = previewers.new_buffer_previewer { - title = "Quickfix List Preview", - dyn_title = function(_, entry) - return entry.title - end, - - get_buffer_by_name = function(_, entry) - return "quickfixlist_" .. tostring(entry.nr) - end, - - define_preview = function(self, entry) - if self.state.bufname then - return - end - local entries = vim.tbl_map(function(i) - return qf_entry_maker(i):display() - end, entry.items) - vim.api.nvim_buf_set_lines(self.state.bufnr, 0, -1, false, entries) - end, - }, - sorter = conf.generic_sorter(opts), - attach_mappings = function(_, map) - action_set.select:replace(function(prompt_bufnr) - local nr = action_state.get_selected_entry().nr - actions.close(prompt_bufnr) - internal.quickfix { nr = nr } - end) - - map({ "i", "n" }, "", function(prompt_bufnr) - local nr = action_state.get_selected_entry().nr - actions.close(prompt_bufnr) - vim.cmd(nr .. "chistory") - vim.cmd "botright copen" - end) - return true - end, - }) - :find() + local qflists = {} + for i = 1, 10 do -- (n)vim keeps at most 10 quickfix lists in full + -- qf weirdness: id = 0 gets id of quickfix list nr + local qflist = vim.fn.getqflist { nr = i, id = 0, title = true, items = true } + if not vim.tbl_isempty(qflist.items) then + table.insert(qflists, qflist) + end + end + local entry_maker = opts.make_entry + or function(entry) + return make_entry.set_default_entry_mt({ + value = entry.title or "Untitled", + ordinal = entry.title or "Untitled", + display = entry.title or "Untitled", + nr = entry.nr, + id = entry.id, + items = entry.items, + }, opts) + end + local qf_entry_maker = make_entry.gen_from_quickfix(opts) + pickers + .new(opts, { + prompt_title = "Quickfix History", + finder = finders.new_table { + results = qflists, + entry_maker = entry_maker, + }, + previewer = previewers.new_buffer_previewer { + title = "Quickfix List Preview", + dyn_title = function(_, entry) + return entry.title + end, + + get_buffer_by_name = function(_, entry) + return "quickfixlist_" .. tostring(entry.nr) + end, + + define_preview = function(self, entry) + if self.state.bufname then + return + end + local entries = vim.tbl_map(function(i) + return qf_entry_maker(i):display() + end, entry.items) + vim.api.nvim_buf_set_lines(self.state.bufnr, 0, -1, false, entries) + end, + }, + sorter = conf.generic_sorter(opts), + attach_mappings = function(_, map) + action_set.select:replace(function(prompt_bufnr) + local nr = action_state.get_selected_entry().nr + actions.close(prompt_bufnr) + internal.quickfix { nr = nr } + end) + + map({ "i", "n" }, "", function(prompt_bufnr) + local nr = action_state.get_selected_entry().nr + actions.close(prompt_bufnr) + vim.cmd(nr .. "chistory") + vim.cmd "botright copen" + end) + return true + end, + }) + :find() end internal.loclist = function(opts) - local locations = vim.fn.getloclist(0) - local filenames = {} - for _, value in pairs(locations) do - local bufnr = value.bufnr - if filenames[bufnr] == nil then - filenames[bufnr] = vim.api.nvim_buf_get_name(bufnr) - end - value.filename = filenames[bufnr] - end - - if vim.tbl_isempty(locations) then - utils.notify("builtin.loclist", { msg = "No loclist items", level = "INFO" }) - return - end - - pickers - .new(opts, { - prompt_title = "Loclist", - finder = finders.new_table { - results = locations, - entry_maker = opts.entry_maker or make_entry.gen_from_quickfix(opts), - }, - previewer = conf.qflist_previewer(opts), - sorter = conf.generic_sorter(opts), - }) - :find() + local locations = vim.fn.getloclist(0) + local filenames = {} + for _, value in pairs(locations) do + local bufnr = value.bufnr + if filenames[bufnr] == nil then + filenames[bufnr] = vim.api.nvim_buf_get_name(bufnr) + end + value.filename = filenames[bufnr] + end + + if vim.tbl_isempty(locations) then + utils.notify("builtin.loclist", { msg = "No loclist items", level = "INFO" }) + return + end + + pickers + .new(opts, { + prompt_title = "Loclist", + finder = finders.new_table { + results = locations, + entry_maker = opts.entry_maker or make_entry.gen_from_quickfix(opts), + }, + previewer = conf.qflist_previewer(opts), + sorter = conf.generic_sorter(opts), + }) + :find() end internal.oldfiles = function(opts) - opts = apply_cwd_only_aliases(opts) - opts.include_current_session = vim.F.if_nil(opts.include_current_session, true) - - local current_buffer = vim.api.nvim_get_current_buf() - local current_file = vim.api.nvim_buf_get_name(current_buffer) - local results = {} - - if utils.iswin then -- for slash problem in windows - current_file = current_file:gsub("/", "\\") - end - - if opts.include_current_session then - for _, buffer in ipairs(utils.split_lines(vim.fn.execute ":buffers! t")) do - local match = tonumber(string.match(buffer, "%s*(%d+)")) - local open_by_lsp = string.match(buffer, "line 0$") - if match and not open_by_lsp then - local file = vim.api.nvim_buf_get_name(match) - if utils.iswin then - file = file:gsub("/", "\\") - end - if vim.loop.fs_stat(file) and match ~= current_buffer then - table.insert(results, file) - end - end - end - end - - for _, file in ipairs(vim.v.oldfiles) do - if utils.iswin then - file = file:gsub("/", "\\") - end - local file_stat = vim.loop.fs_stat(file) - if file_stat and file_stat.type == "file" and not vim.tbl_contains(results, file) and file ~= current_file then - table.insert(results, file) - end - end - - if opts.cwd_only or opts.cwd then - local cwd = opts.cwd_only and vim.loop.cwd() or opts.cwd - results = vim.tbl_filter(function(file) - return buf_in_cwd(file, cwd) - end, results) - end - - pickers - .new(opts, { - prompt_title = "Oldfiles", - __locations_input = true, - finder = finders.new_table { - results = results, - entry_maker = opts.entry_maker or make_entry.gen_from_file(opts), - }, - sorter = conf.file_sorter(opts), - previewer = conf.grep_previewer(opts), - }) - :find() + opts = apply_cwd_only_aliases(opts) + opts.include_current_session = vim.F.if_nil(opts.include_current_session, true) + + local current_buffer = vim.api.nvim_get_current_buf() + local current_file = vim.api.nvim_buf_get_name(current_buffer) + local results = {} + + if utils.iswin then -- for slash problem in windows + current_file = current_file:gsub("/", "\\") + end + + if opts.include_current_session then + for _, buffer in ipairs(utils.split_lines(vim.fn.execute ":buffers! t")) do + local match = tonumber(string.match(buffer, "%s*(%d+)")) + local open_by_lsp = string.match(buffer, "line 0$") + if match and not open_by_lsp then + local file = vim.api.nvim_buf_get_name(match) + if utils.iswin then + file = file:gsub("/", "\\") + end + if vim.loop.fs_stat(file) and match ~= current_buffer then + table.insert(results, file) + end + end + end + end + + for _, file in ipairs(vim.v.oldfiles) do + if utils.iswin then + file = file:gsub("/", "\\") + end + local file_stat = vim.loop.fs_stat(file) + if file_stat and file_stat.type == "file" and not vim.tbl_contains(results, file) and file ~= current_file then + table.insert(results, file) + end + end + + if opts.cwd_only or opts.cwd then + local cwd = opts.cwd_only and vim.loop.cwd() or opts.cwd + results = vim.tbl_filter(function(file) + return buf_in_cwd(file, cwd) + end, results) + end + + pickers + .new(opts, { + prompt_title = "Oldfiles", + __locations_input = true, + finder = finders.new_table { + results = results, + entry_maker = opts.entry_maker or make_entry.gen_from_file(opts), + }, + sorter = conf.file_sorter(opts), + previewer = conf.grep_previewer(opts), + }) + :find() end internal.command_history = function(opts) - local history_string = vim.fn.execute "history cmd" - local history_list = utils.split_lines(history_string) - - local results = {} - local filter_fn = opts.filter_fn - - for i = #history_list, 3, -1 do - local item = history_list[i] - local _, finish = string.find(item, "%d+ +") - local cmd = string.sub(item, finish + 1) - - if filter_fn then - if filter_fn(cmd) then - table.insert(results, cmd) - end - else - table.insert(results, cmd) - end - end - - pickers - .new(opts, { - prompt_title = "Command History", - finder = finders.new_table(results), - sorter = conf.generic_sorter(opts), - - attach_mappings = function(_, map) - actions.select_default:replace(actions.set_command_line) - map({ "i", "n" }, "", actions.edit_command_line) - - -- TODO: Find a way to insert the text... it seems hard. - -- map('i', '', actions.insert_value, { expr = true }) - - return true - end, - }) - :find() + local history_string = vim.fn.execute "history cmd" + local history_list = utils.split_lines(history_string) + + local results = {} + local filter_fn = opts.filter_fn + + for i = #history_list, 3, -1 do + local item = history_list[i] + local _, finish = string.find(item, "%d+ +") + local cmd = string.sub(item, finish + 1) + + if filter_fn then + if filter_fn(cmd) then + table.insert(results, cmd) + end + else + table.insert(results, cmd) + end + end + + pickers + .new(opts, { + prompt_title = "Command History", + finder = finders.new_table(results), + sorter = conf.generic_sorter(opts), + + attach_mappings = function(_, map) + actions.select_default:replace(actions.set_command_line) + map({ "i", "n" }, "", actions.edit_command_line) + + -- TODO: Find a way to insert the text... it seems hard. + -- map('i', '', actions.insert_value, { expr = true }) + + return true + end, + }) + :find() end internal.search_history = function(opts) - local search_string = vim.fn.execute "history search" - local search_list = utils.split_lines(search_string) - - local results = {} - for i = #search_list, 3, -1 do - local item = search_list[i] - local _, finish = string.find(item, "%d+ +") - table.insert(results, string.sub(item, finish + 1)) - end - - pickers - .new(opts, { - prompt_title = "Search History", - finder = finders.new_table(results), - sorter = conf.generic_sorter(opts), - - attach_mappings = function(_, map) - actions.select_default:replace(actions.set_search_line) - map({ "i", "n" }, "", actions.edit_search_line) - - -- TODO: Find a way to insert the text... it seems hard. - -- map('i', '', actions.insert_value, { expr = true }) - - return true - end, - }) - :find() + local search_string = vim.fn.execute "history search" + local search_list = utils.split_lines(search_string) + + local results = {} + for i = #search_list, 3, -1 do + local item = search_list[i] + local _, finish = string.find(item, "%d+ +") + table.insert(results, string.sub(item, finish + 1)) + end + + pickers + .new(opts, { + prompt_title = "Search History", + finder = finders.new_table(results), + sorter = conf.generic_sorter(opts), + + attach_mappings = function(_, map) + actions.select_default:replace(actions.set_search_line) + map({ "i", "n" }, "", actions.edit_search_line) + + -- TODO: Find a way to insert the text... it seems hard. + -- map('i', '', actions.insert_value, { expr = true }) + + return true + end, + }) + :find() end internal.vim_options = function(opts) - local res = {} - for _, v in pairs(vim.api.nvim_get_all_options_info()) do - local ok, value = pcall(vim.api.nvim_get_option_value, v.name, {}) - if ok then - v.value = value - table.insert(res, v) - end - end - table.sort(res, function(left, right) - return left.name < right.name - end) - - pickers - .new(opts, { - prompt_title = "options", - finder = finders.new_table { - results = res, - entry_maker = opts.entry_maker or make_entry.gen_from_vimoptions(opts), - }, - sorter = conf.generic_sorter(opts), - attach_mappings = function() - actions.select_default:replace(function() - local selection = action_state.get_selected_entry() - if selection == nil then - utils.__warn_no_selection "builtin.vim_options" - return - end - - local esc = "" - if vim.fn.mode() == "i" then - esc = vim.api.nvim_replace_termcodes("", true, false, true) - end - - vim.api.nvim_feedkeys( - selection.value.type == "boolean" and string.format("%s:set %s!", esc, selection.value.name) - or string.format("%s:set %s=%s", esc, selection.value.name, selection.value.value), - "m", - true - ) - end) - - return true - end, - }) - :find() + local res = {} + for _, v in pairs(vim.api.nvim_get_all_options_info()) do + local ok, value = pcall(vim.api.nvim_get_option_value, v.name, {}) + if ok then + v.value = value + table.insert(res, v) + end + end + table.sort(res, function(left, right) + return left.name < right.name + end) + + pickers + .new(opts, { + prompt_title = "options", + finder = finders.new_table { + results = res, + entry_maker = opts.entry_maker or make_entry.gen_from_vimoptions(opts), + }, + sorter = conf.generic_sorter(opts), + attach_mappings = function() + actions.select_default:replace(function() + local selection = action_state.get_selected_entry() + if selection == nil then + utils.__warn_no_selection "builtin.vim_options" + return + end + + local esc = "" + if vim.fn.mode() == "i" then + esc = vim.api.nvim_replace_termcodes("", true, false, true) + end + + vim.api.nvim_feedkeys( + selection.value.type == "boolean" and string.format("%s:set %s!", esc, selection.value.name) + or string.format("%s:set %s=%s", esc, selection.value.name, selection.value.value), + "m", + true + ) + end) + + return true + end, + }) + :find() end internal.help_tags = function(opts) - opts.lang = vim.F.if_nil(opts.lang, vim.o.helplang) - opts.fallback = vim.F.if_nil(opts.fallback, true) - opts.file_ignore_patterns = {} - - local langs = vim.split(opts.lang, ",", { trimempty = true }) - if opts.fallback and not vim.tbl_contains(langs, "en") then - table.insert(langs, "en") - end - local langs_map = {} - for _, lang in ipairs(langs) do - langs_map[lang] = true - end - - local tag_files = {} - local function add_tag_file(lang, file) - if langs_map[lang] then - if tag_files[lang] then - table.insert(tag_files[lang], file) - else - tag_files[lang] = { file } - end - end - end - - local help_files = {} - - local rtp = vim.o.runtimepath - -- extend the runtime path with all plugins not loaded by lazy.nvim - local lazy = package.loaded["lazy.core.util"] - if lazy and lazy.get_unloaded_rtp then - local paths = lazy.get_unloaded_rtp "" - if #paths > 0 then - rtp = rtp .. "," .. table.concat(paths, ",") - end - end - local all_files = vim.fn.globpath(rtp, "doc/*", 1, 1) - for _, fullpath in ipairs(all_files) do - local file = utils.path_tail(fullpath) - if file == "tags" then - add_tag_file("en", fullpath) - elseif file:match "^tags%-..$" then - local lang = file:sub(-2) - add_tag_file(lang, fullpath) - else - help_files[file] = fullpath - end - end - - local tags = {} - local tags_map = {} - local delimiter = string.char(9) - for _, lang in ipairs(langs) do - for _, file in ipairs(tag_files[lang] or {}) do - local lines = utils.split_lines(Path:new(file):read(), { trimempty = true }) - for _, line in ipairs(lines) do - -- TODO: also ignore tagComment starting with ';' - if not line:match "^!_TAG_" then - local fields = vim.split(line, delimiter, { trimempty = true }) - if #fields == 3 and not tags_map[fields[1]] then - if fields[1] ~= "help-tags" or fields[2] ~= "tags" then - table.insert(tags, { - name = fields[1], - filename = help_files[fields[2]], - cmd = fields[3], - lang = lang, - }) - tags_map[fields[1]] = true - end - end - end - end - end - end - - pickers - .new(opts, { - prompt_title = "Help", - finder = finders.new_table { - results = tags, - entry_maker = function(entry) - return make_entry.set_default_entry_mt({ - value = entry.name .. "@" .. entry.lang, - display = entry.name, - ordinal = entry.name, - filename = entry.filename, - cmd = entry.cmd, - }, opts) - end, - }, - previewer = previewers.help.new(opts), - sorter = conf.generic_sorter(opts), - attach_mappings = function(prompt_bufnr) - action_set.select:replace(function(_, cmd) - local selection = action_state.get_selected_entry() - if selection == nil then - utils.__warn_no_selection "builtin.help_tags" - return - end - - actions.close(prompt_bufnr) - if cmd == "default" or cmd == "horizontal" then - vim.cmd("help " .. selection.value) - elseif cmd == "vertical" then - vim.cmd("vert help " .. selection.value) - elseif cmd == "tab" then - vim.cmd("tab help " .. selection.value) - end - end) - - return true - end, - }) - :find() + opts.lang = vim.F.if_nil(opts.lang, vim.o.helplang) + opts.fallback = vim.F.if_nil(opts.fallback, true) + opts.file_ignore_patterns = {} + + local langs = vim.split(opts.lang, ",", { trimempty = true }) + if opts.fallback and not vim.tbl_contains(langs, "en") then + table.insert(langs, "en") + end + local langs_map = {} + for _, lang in ipairs(langs) do + langs_map[lang] = true + end + + local tag_files = {} + local function add_tag_file(lang, file) + if langs_map[lang] then + if tag_files[lang] then + table.insert(tag_files[lang], file) + else + tag_files[lang] = { file } + end + end + end + + local help_files = {} + + local rtp = vim.o.runtimepath + -- extend the runtime path with all plugins not loaded by lazy.nvim + local lazy = package.loaded["lazy.core.util"] + if lazy and lazy.get_unloaded_rtp then + local paths = lazy.get_unloaded_rtp "" + if #paths > 0 then + rtp = rtp .. "," .. table.concat(paths, ",") + end + end + local all_files = vim.fn.globpath(rtp, "doc/*", 1, 1) + for _, fullpath in ipairs(all_files) do + local file = utils.path_tail(fullpath) + if file == "tags" then + add_tag_file("en", fullpath) + elseif file:match "^tags%-..$" then + local lang = file:sub(-2) + add_tag_file(lang, fullpath) + else + help_files[file] = fullpath + end + end + + local tags = {} + local tags_map = {} + local delimiter = string.char(9) + for _, lang in ipairs(langs) do + for _, file in ipairs(tag_files[lang] or {}) do + local lines = utils.split_lines(Path:new(file):read(), { trimempty = true }) + for _, line in ipairs(lines) do + -- TODO: also ignore tagComment starting with ';' + if not line:match "^!_TAG_" then + local fields = vim.split(line, delimiter, { trimempty = true }) + if #fields == 3 and not tags_map[fields[1]] then + if fields[1] ~= "help-tags" or fields[2] ~= "tags" then + table.insert(tags, { + name = fields[1], + filename = help_files[fields[2]], + cmd = fields[3], + lang = lang, + }) + tags_map[fields[1]] = true + end + end + end + end + end + end + + pickers + .new(opts, { + prompt_title = "Help", + finder = finders.new_table { + results = tags, + entry_maker = function(entry) + return make_entry.set_default_entry_mt({ + value = entry.name .. "@" .. entry.lang, + display = entry.name, + ordinal = entry.name, + filename = entry.filename, + cmd = entry.cmd, + }, opts) + end, + }, + previewer = previewers.help.new(opts), + sorter = conf.generic_sorter(opts), + attach_mappings = function(prompt_bufnr) + action_set.select:replace(function(_, cmd) + local selection = action_state.get_selected_entry() + if selection == nil then + utils.__warn_no_selection "builtin.help_tags" + return + end + + actions.close(prompt_bufnr) + if cmd == "default" or cmd == "horizontal" then + vim.cmd("help " .. selection.value) + elseif cmd == "vertical" then + vim.cmd("vert help " .. selection.value) + elseif cmd == "tab" then + vim.cmd("tab help " .. selection.value) + end + end) + + return true + end, + }) + :find() end internal.man_pages = function(opts) - opts.sections = vim.F.if_nil(opts.sections, { "1" }) - assert(utils.islist(opts.sections), "sections should be a list") - opts.man_cmd = utils.get_lazy_default(opts.man_cmd, function() - local uname = vim.loop.os_uname() - local sysname = string.lower(uname.sysname) - if sysname == "darwin" then - local major_version = tonumber(vim.fn.matchlist(uname.release, [[^\(\d\+\)\..*]])[2]) or 0 - return major_version >= 22 and { "apropos", "." } or { "apropos", " " } - elseif sysname == "freebsd" then - return { "apropos", "." } - else - return { "apropos", "" } - end - end) - opts.entry_maker = opts.entry_maker or make_entry.gen_from_apropos(opts) - opts.env = { PATH = vim.env.PATH, MANPATH = vim.env.MANPATH } - - pickers - .new(opts, { - prompt_title = "Man", - finder = finders.new_oneshot_job(opts.man_cmd, opts), - previewer = previewers.man.new(opts), - sorter = conf.generic_sorter(opts), - attach_mappings = function(prompt_bufnr) - action_set.select:replace(function(_, cmd) - local selection = action_state.get_selected_entry() - if selection == nil then - utils.__warn_no_selection "builtin.man_pages" - return - end - - local args = selection.section .. " " .. selection.value - actions.close(prompt_bufnr) - if cmd == "default" or cmd == "horizontal" then - vim.cmd("Man " .. args) - elseif cmd == "vertical" then - vim.cmd("vert Man " .. args) - elseif cmd == "tab" then - vim.cmd("tab Man " .. args) - end - end) - - return true - end, - }) - :find() + opts.sections = vim.F.if_nil(opts.sections, { "1" }) + assert(utils.islist(opts.sections), "sections should be a list") + opts.man_cmd = utils.get_lazy_default(opts.man_cmd, function() + local uname = vim.loop.os_uname() + local sysname = string.lower(uname.sysname) + if sysname == "darwin" then + local major_version = tonumber(vim.fn.matchlist(uname.release, [[^\(\d\+\)\..*]])[2]) or 0 + return major_version >= 22 and { "apropos", "." } or { "apropos", " " } + elseif sysname == "freebsd" then + return { "apropos", "." } + else + return { "apropos", "" } + end + end) + opts.entry_maker = opts.entry_maker or make_entry.gen_from_apropos(opts) + opts.env = { PATH = vim.env.PATH, MANPATH = vim.env.MANPATH } + + pickers + .new(opts, { + prompt_title = "Man", + finder = finders.new_oneshot_job(opts.man_cmd, opts), + previewer = previewers.man.new(opts), + sorter = conf.generic_sorter(opts), + attach_mappings = function(prompt_bufnr) + action_set.select:replace(function(_, cmd) + local selection = action_state.get_selected_entry() + if selection == nil then + utils.__warn_no_selection "builtin.man_pages" + return + end + + local args = selection.section .. " " .. selection.value + actions.close(prompt_bufnr) + if cmd == "default" or cmd == "horizontal" then + vim.cmd("Man " .. args) + elseif cmd == "vertical" then + vim.cmd("vert Man " .. args) + elseif cmd == "tab" then + vim.cmd("tab Man " .. args) + end + end) + + return true + end, + }) + :find() end internal.reloader = function(opts) - local package_list = vim.tbl_keys(package.loaded) - - -- filter out packages we don't want and track the longest package name - local column_len = 0 - for index, module_name in pairs(package_list) do - if - type(require(module_name)) ~= "table" - or module_name:sub(1, 1) == "_" - or package.searchpath(module_name, package.path) == nil - then - table.remove(package_list, index) - elseif #module_name > column_len then - column_len = #module_name - end - end - opts.column_len = vim.F.if_nil(opts.column_len, column_len) - - pickers - .new(opts, { - prompt_title = "Packages", - finder = finders.new_table { - results = package_list, - entry_maker = opts.entry_maker or make_entry.gen_from_packages(opts), - }, - -- previewer = previewers.vim_buffer.new(opts), - sorter = conf.generic_sorter(opts), - - attach_mappings = function(prompt_bufnr) - actions.select_default:replace(function() - local selection = action_state.get_selected_entry() - if selection == nil then - utils.__warn_no_selection "builtin.reloader" - return - end - - actions.close(prompt_bufnr) - require("plenary.reload").reload_module(selection.value) - utils.notify("builtin.reloader", { - msg = string.format("[%s] - module reloaded", selection.value), - level = "INFO", - }) - end) - - return true - end, - }) - :find() -end - -internal.buffers = function(opts) - opts = apply_cwd_only_aliases(opts) - - local bufnrs = vim.tbl_filter(function(bufnr) - if 1 ~= vim.fn.buflisted(bufnr) then - return false - end - -- only hide unloaded buffers if opts.show_all_buffers is false, keep them listed if true or nil - if opts.show_all_buffers == false and not vim.api.nvim_buf_is_loaded(bufnr) then - return false - end - if opts.ignore_current_buffer and bufnr == vim.api.nvim_get_current_buf() then - return false - end - - local bufname = vim.api.nvim_buf_get_name(bufnr) - - if opts.cwd_only and not buf_in_cwd(bufname, vim.loop.cwd()) then - return false - end - if not opts.cwd_only and opts.cwd and not buf_in_cwd(bufname, opts.cwd) then - return false - end - return true - end, vim.api.nvim_list_bufs()) - - if not next(bufnrs) then - utils.notify("builtin.buffers", { msg = "No buffers found with the provided options", level = "INFO" }) - return - end - - if opts.sort_mru then - table.sort(bufnrs, function(a, b) - return vim.fn.getbufinfo(a)[1].lastused > vim.fn.getbufinfo(b)[1].lastused - end) - end - - if type(opts.sort_buffers) == "function" then - table.sort(bufnrs, opts.sort_buffers) - end - - local buffers = {} - local default_selection_idx = 1 - for i, bufnr in ipairs(bufnrs) do - local flag = bufnr == vim.fn.bufnr "" and "%" or (bufnr == vim.fn.bufnr "#" and "#" or " ") - - if opts.sort_lastused and not opts.ignore_current_buffer and flag == "#" then - default_selection_idx = 2 - end - - local element = { - bufnr = bufnr, - flag = flag, - info = vim.fn.getbufinfo(bufnr)[1], - } - - if opts.sort_lastused and (flag == "#" or flag == "%") then - local idx = ((buffers[1] ~= nil and buffers[1].flag == "%") and 2 or 1) - table.insert(buffers, idx, element) - else - if opts.select_current and flag == "%" then - default_selection_idx = i - end - table.insert(buffers, element) - end - end - - if not opts.bufnr_width then - local max_bufnr = math.max(unpack(bufnrs)) - opts.bufnr_width = #tostring(max_bufnr) - end - - pickers - .new(opts, { - prompt_title = "Buffers", - finder = finders.new_table { - results = buffers, - entry_maker = opts.entry_maker or make_entry.gen_from_buffer(opts), - }, - previewer = conf.grep_previewer(opts), - sorter = conf.generic_sorter(opts), - default_selection_index = default_selection_idx, - attach_mappings = function(_, map) - map({ "i", "n" }, "", actions.delete_buffer) - return true - end, - }) - :find() -end - -internal.colorscheme = function(opts) - local before_background = vim.o.background - local before_color = vim.api.nvim_exec2("colorscheme", { output = true }).output - local need_restore = not not opts.enable_preview - - local colors = opts.colors or { before_color } - if not vim.tbl_contains(colors, before_color) then - table.insert(colors, 1, before_color) - end - - colors = vim.list_extend( - colors, - vim.tbl_filter(function(color) - return not vim.tbl_contains(colors, color) - end, vim.fn.getcompletion("", "color")) - ) - - -- if lazy is available, extend the colors list with unloaded colorschemes - local lazy = package.loaded["lazy.core.util"] - if lazy and lazy.get_unloaded_rtp then - local paths = lazy.get_unloaded_rtp "" - local all_files = vim.fn.globpath(table.concat(paths, ","), "colors/*", 1, 1) - for _, f in ipairs(all_files) do - local color = vim.fn.fnamemodify(f, ":t:r") - if not vim.tbl_contains(colors, color) then - table.insert(colors, color) - end - end - end - - if opts.ignore_builtins then - -- stylua: ignore - local builtins = { - "blue", "darkblue", "default", "delek", "desert", "elflord", "evening", - "habamax", "industry", "koehler", "lunaperche", "morning", "murphy", - "pablo", "peachpuff", "quiet", "retrobox", "ron", "shine", "slate", - "sorbet", "torte", "vim", "wildcharm", "zaibatsu", "zellner", - } - colors = vim.tbl_filter(function(color) - return not vim.tbl_contains(builtins, color) - end, colors) - end - - local previewer - if opts.enable_preview then - -- define previewer - local bufnr = vim.api.nvim_get_current_buf() - local p = vim.api.nvim_buf_get_name(bufnr) - - -- show current buffer content in previewer - previewer = previewers.new_buffer_previewer { - get_buffer_by_name = function() - return p - end, - define_preview = function(self) - if vim.loop.fs_stat(p) then - conf.buffer_previewer_maker(p, self.state.bufnr, { bufname = self.state.bufname }) - else - local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false) - vim.api.nvim_buf_set_lines(self.state.bufnr, 0, -1, false, lines) - end - end, - } - end - - local picker = pickers.new(opts, { - prompt_title = "Change Colorscheme", - finder = finders.new_table { - results = colors, - }, - sorter = conf.generic_sorter(opts), - previewer = previewer, - attach_mappings = function(prompt_bufnr) - actions.select_default:replace(function() - local selection = action_state.get_selected_entry() - if selection == nil then - utils.__warn_no_selection "builtin.colorscheme" - return - end - - need_restore = false - actions.close(prompt_bufnr) - vim.cmd.colorscheme(selection.value) - end) - return true - end, - on_complete = { - function() - local selection = action_state.get_selected_entry() - if selection == nil then - utils.__warn_no_selection "builtin.colorscheme" - return - end - if opts.enable_preview then - vim.cmd.colorscheme(selection.value) - end - end, - }, - }) - - if opts.enable_preview then - -- rewrite picker.close_windows. restore color if needed - local close_windows = picker.close_windows - picker.close_windows = function(status) - close_windows(status) - if need_restore then - vim.o.background = before_background - vim.cmd.colorscheme(before_color) - end - end - - -- rewrite picker.set_selection so that color schemes can be previewed when the current - -- selection is shifted using the keyboard or if an item is clicked with the mouse - local set_selection = picker.set_selection - picker.set_selection = function(self, row) - set_selection(self, row) - local selection = action_state.get_selected_entry() - if selection == nil then - utils.__warn_no_selection "builtin.colorscheme" - return - end - if opts.enable_preview then - vim.cmd.colorscheme(selection.value) - end - end - end - - picker:find() -end - -internal.marks = function(opts) - local local_marks = { - items = vim.fn.getmarklist(opts.bufnr), - name_func = function(_, line) - return vim.api.nvim_buf_get_lines(opts.bufnr, line - 1, line, false)[1] - end, - } - local global_marks = { - items = vim.fn.getmarklist(), - name_func = function(mark, _) - -- get buffer name if it is opened, otherwise get file name - return vim.api.nvim_get_mark(mark, {})[4] - end, - } - local marks_table = {} - local marks_others = {} - local bufname = vim.api.nvim_buf_get_name(opts.bufnr) - local all_marks = {} - opts.mark_type = vim.F.if_nil(opts.mark_type, "all") - if opts.mark_type == "all" then - all_marks = { local_marks, global_marks } - elseif opts.mark_type == "local" then - all_marks = { local_marks } - elseif opts.mark_type == "global" then - all_marks = { global_marks } - end - - for _, cnf in ipairs(all_marks) do - for _, v in ipairs(cnf.items) do - -- strip the first single quote character - local mark = string.sub(v.mark, 2, 3) - local _, lnum, col, _ = unpack(v.pos) - local name = cnf.name_func(mark, lnum) - -- same format to :marks command - local line = string.format("%s %6d %4d %s", mark, lnum, col - 1, name) - local row = { - line = line, - lnum = lnum, - col = col, - filename = utils.path_expand(v.file or bufname), - } - -- non alphanumeric marks goes to last - if mark:match "%w" then - table.insert(marks_table, row) - else - table.insert(marks_others, row) - end - end - end - marks_table = vim.fn.extend(marks_table, marks_others) - - pickers - .new(opts, { - prompt_title = "Marks", - finder = finders.new_table { - results = marks_table, - entry_maker = opts.entry_maker or make_entry.gen_from_marks(opts), - }, - previewer = conf.grep_previewer(opts), - sorter = conf.generic_sorter(opts), - push_cursor_on_edit = true, - push_tagstack_on_edit = true, - }) - :find() -end - -internal.registers = function(opts) - local registers_table = { '"', "-", "#", "=", "/", "*", "+", ":", ".", "%" } - - -- named - for i = 0, 9 do - table.insert(registers_table, tostring(i)) - end - - -- alphabetical - for i = 65, 90 do - table.insert(registers_table, string.char(i)) - end - - pickers - .new(opts, { - prompt_title = "Registers", - finder = finders.new_table { - results = registers_table, - entry_maker = opts.entry_maker or make_entry.gen_from_registers(opts), - }, - sorter = conf.generic_sorter(opts), - attach_mappings = function(_, map) - actions.select_default:replace(actions.paste_register) - map({ "i", "n" }, "", actions.edit_register) - - return true - end, - }) - :find() -end - -internal.keymaps = function(opts) - opts.modes = vim.F.if_nil(opts.modes, { "n", "i", "c", "x" }) - opts.show_plug = vim.F.if_nil(opts.show_plug, true) - opts.only_buf = vim.F.if_nil(opts.only_buf, false) - - local keymap_encountered = {} -- used to make sure no duplicates are inserted into keymaps_table - local keymaps_table = {} - local max_len_lhs = 0 - - -- helper function to populate keymaps_table and determine max_len_lhs - local function extract_keymaps(keymaps) - for _, keymap in pairs(keymaps) do - local keymap_key = keymap.buffer .. keymap.mode .. keymap.lhs -- should be distinct for every keymap - if not keymap_encountered[keymap_key] then - keymap_encountered[keymap_key] = true - if - (opts.show_plug or not string.find(keymap.lhs, "")) - and (not opts.lhs_filter or opts.lhs_filter(keymap.lhs)) - and (not opts.filter or opts.filter(keymap)) - then - table.insert(keymaps_table, keymap) - max_len_lhs = math.max(max_len_lhs, #utils.display_termcodes(keymap.lhs)) - end - end - end - end - - for _, mode in pairs(opts.modes) do - local global = vim.api.nvim_get_keymap(mode) - local buf_local = vim.api.nvim_buf_get_keymap(0, mode) - if not opts.only_buf then - extract_keymaps(global) - end - extract_keymaps(buf_local) - end - opts.width_lhs = max_len_lhs + 1 - - pickers - .new(opts, { - prompt_title = "Key Maps", - finder = finders.new_table { - results = keymaps_table, - entry_maker = opts.entry_maker or make_entry.gen_from_keymaps(opts), - }, - sorter = conf.generic_sorter(opts), - attach_mappings = function(prompt_bufnr) - actions.select_default:replace(function() - local selection = action_state.get_selected_entry() - if selection == nil then - utils.__warn_no_selection "builtin.keymaps" - return - end - - vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes(selection.value.lhs, true, false, true), "t", true) - return actions.close(prompt_bufnr) - end) - return true - end, - }) - :find() -end - -internal.filetypes = function(opts) - local filetypes = vim.fn.getcompletion("", "filetype") - - pickers - .new(opts, { - prompt_title = "Filetypes", - finder = finders.new_table { - results = filetypes, - }, - sorter = conf.generic_sorter(opts), - attach_mappings = function(prompt_bufnr) - actions.select_default:replace(function() - local selection = action_state.get_selected_entry() - if selection == nil then - print "[telescope] Nothing currently selected" - return - end - - actions.close(prompt_bufnr) - vim.cmd("setfiletype " .. selection[1]) - end) - return true - end, - }) - :find() -end - -internal.highlights = function(opts) - local highlights = vim.fn.getcompletion("", "highlight") - - pickers - .new(opts, { - prompt_title = "Highlights", - finder = finders.new_table { - results = highlights, - entry_maker = opts.entry_maker or make_entry.gen_from_highlights(opts), - }, - sorter = conf.generic_sorter(opts), - attach_mappings = function(prompt_bufnr) - actions.select_default:replace(function() - local selection = action_state.get_selected_entry() - if selection == nil then - utils.__warn_no_selection "builtin.highlights" - return - end - - actions.close(prompt_bufnr) - vim.cmd("hi " .. selection.value) - end) - return true - end, - previewer = previewers.highlights.new(opts), - }) - :find() -end - -internal.autocommands = function(opts) - local autocmds = vim.api.nvim_get_autocmds {} - table.sort(autocmds, function(lhs, rhs) - return lhs.event < rhs.event - end) - pickers - .new(opts, { - prompt_title = "autocommands", - finder = finders.new_table { - results = autocmds, - entry_maker = opts.entry_maker or make_entry.gen_from_autocommands(opts), - }, - previewer = previewers.autocommands.new(opts), - sorter = conf.generic_sorter(opts), - attach_mappings = function(prompt_bufnr) - action_set.select:replace_if(function() - local selection = action_state.get_selected_entry() - if selection == nil then - return false - end - local val = selection.value - local cb = val.callback - if vim.is_callable(cb) then - if type(cb) ~= "string" then - local f = type(cb) == "function" and cb or rawget(getmetatable(cb), "__call") - local info = debug.getinfo(f, "S") - local file = info.source:match "^@(.+)" - local lnum = info.linedefined - if file and (lnum or 0) > 0 then - selection.filename, selection.lnum, selection.col = file, lnum, 1 - return false - end - end - end - local group_name = val.group_name ~= "" and val.group_name or "" - local output = - vim.fn.execute("verb autocmd " .. group_name .. " " .. val.event .. " " .. val.pattern, "silent") - for line in output:gmatch "[^\r\n]+" do - local source_file = line:match "Last set from (.*) line %d*$" or line:match "Last set from (.*)$" - if source_file and source_file ~= "Lua" then - selection.filename = source_file - local source_lnum = line:match "line (%d*)$" or "1" - selection.lnum = tonumber(source_lnum) - selection.col = 1 - return false - end - end - return true - end, function() - local selection = action_state.get_selected_entry() - actions.close(prompt_bufnr) - print("You selected autocmd: " .. vim.inspect(selection.value)) - end) - - return true - end, - }) - :find() -end - -internal.spell_suggest = function(opts) - local cursor_word = vim.fn.expand "" - local suggestions = vim.fn.spellsuggest(cursor_word) - - pickers - .new(opts, { - prompt_title = "Spelling Suggestions", - finder = finders.new_table { - results = suggestions, - }, - sorter = conf.generic_sorter(opts), - attach_mappings = function(prompt_bufnr) - actions.select_default:replace(function() - local selection = action_state.get_selected_entry() - if selection == nil then - utils.__warn_no_selection "builtin.spell_suggest" - return - end - - action_state.get_current_picker(prompt_bufnr)._original_mode = "i" - actions.close(prompt_bufnr) - vim.cmd('normal! "_ciw' .. selection[1]) - vim.cmd "stopinsert" - end) - return true - end, - }) - :find() -end - -internal.tagstack = function(opts) - opts = opts or {} - local tagstack = vim.fn.gettagstack().items - - local tags = {} - for i = #tagstack, 1, -1 do - local tag = tagstack[i] - tag.bufnr = tag.from[1] - if vim.api.nvim_buf_is_valid(tag.bufnr) then - tags[#tags + 1] = tag - tag.filename = vim.fn.bufname(tag.bufnr) - tag.lnum = tag.from[2] - tag.col = tag.from[3] - - tag.text = vim.api.nvim_buf_get_lines(tag.bufnr, tag.lnum - 1, tag.lnum, false)[1] or "" - end - end - - if vim.tbl_isempty(tags) then - utils.notify("builtin.tagstack", { - msg = "No tagstack available", - level = "WARN", - }) - return - end - - pickers - .new(opts, { - prompt_title = "TagStack", - finder = finders.new_table { - results = tags, - entry_maker = make_entry.gen_from_quickfix(opts), - }, - previewer = conf.qflist_previewer(opts), - sorter = conf.generic_sorter(opts), - }) - :find() -end - -internal.jumplist = function(opts) - opts = opts or {} - local jumplist = vim.fn.getjumplist()[1] - - -- reverse the list - local sorted_jumplist = {} - for i = #jumplist, 1, -1 do - if vim.api.nvim_buf_is_valid(jumplist[i].bufnr) then - jumplist[i].text = vim.api.nvim_buf_get_lines(jumplist[i].bufnr, jumplist[i].lnum - 1, jumplist[i].lnum, false)[1] - or "" - table.insert(sorted_jumplist, jumplist[i]) - end - end - - pickers - .new(opts, { - prompt_title = "Jumplist", - finder = finders.new_table { - results = sorted_jumplist, - entry_maker = make_entry.gen_from_quickfix(opts), - }, - previewer = conf.qflist_previewer(opts), - sorter = conf.generic_sorter(opts), - }) - :find() -end - -local function apply_checks(mod) - for k, v in pairs(mod) do - mod[k] = function(opts) - opts = opts or {} - - v(opts) - end - end - - return mod -end - -return apply_checks(internal) + local package_list = vim.tbl_keys(package.loaded) + + -- filter out packages we don't want and track the longest package name + local column_len = 0 + for index, module_name in pairs(package_list) do + if + type(require(module_name)) ~= "table" + or module_name:sub(1, 1) == "_" + or package.searchpath(module_name, package.path) == nil + then + table.remove(package_list, index) + elseif #module_name > column_len then + column_len = #module_name + end + end + opts.column_len = vim.F.if_nil(opts.column_len, column_len) + + pickers + .new(opts, { + prompt_title = "Packages", + finder = finders.new_table { + results = package_list, + entry_maker = opts.entry_maker or make_entry.gen_from_packages(opts), + }, + -- previewer = previewers.vim_buffer.new(opts), + sorter = conf.generic_sorter(opts), + + attach_mappings = function(prompt_bufnr) + actions.select_default:replace(function() + local selection = action_state.get_selected_entry() + if selection == nil then + utils.__warn_no_selection "builtin.reloader" + return + end + + actions.close(prompt_bufnr) + require("plenary.reload").reload_module(selection.value) + utils.notify("builtin.reloader", { + msg = string.format("[%s] - module reloaded", selection.value), + level = "INFO", + }) + end) + + return true + end, + }) + :find() + end + + internal.buffers = function(opts) + opts = apply_cwd_only_aliases(opts) + + local bufnrs = vim.tbl_filter(function(bufnr) + if 1 ~= vim.fn.buflisted(bufnr) then + return false + end + -- only hide unloaded buffers if opts.show_all_buffers is false, keep them listed if true or nil + if opts.show_all_buffers == false and not vim.api.nvim_buf_is_loaded(bufnr) then + return false + end + if opts.ignore_current_buffer and bufnr == vim.api.nvim_get_current_buf() then + return false + end + + local bufname = vim.api.nvim_buf_get_name(bufnr) + + if opts.cwd_only and not buf_in_cwd(bufname, vim.loop.cwd()) then + return false + end + if not opts.cwd_only and opts.cwd and not buf_in_cwd(bufname, opts.cwd) then + return false + end + return true + end, vim.api.nvim_list_bufs()) + + if not next(bufnrs) then + utils.notify("builtin.buffers", { msg = "No buffers found with the provided options", level = "INFO" }) + return + end + + if opts.sort_mru then + table.sort(bufnrs, function(a, b) + return vim.fn.getbufinfo(a)[1].lastused > vim.fn.getbufinfo(b)[1].lastused + end) + end + + if type(opts.sort_buffers) == "function" then + table.sort(bufnrs, opts.sort_buffers) + end + + local buffers = {} + local default_selection_idx = 1 + for i, bufnr in ipairs(bufnrs) do + local flag = bufnr == vim.fn.bufnr "" and "%" or (bufnr == vim.fn.bufnr "#" and "#" or " ") + + if opts.sort_lastused and not opts.ignore_current_buffer and flag == "#" then + default_selection_idx = 2 + end + + local info = vim.fn.getbufinfo(bufnr)[1] + + if utils.iswin then -- for slash problem in windows + info.name = info.name:gsub("/", "\\") + end + + local element = { + bufnr = bufnr, + flag = flag, + info = info, + } + + if opts.sort_lastused and (flag == "#" or flag == "%") then + local idx = ((buffers[1] ~= nil and buffers[1].flag == "%") and 2 or 1) + table.insert(buffers, idx, element) + else + if opts.select_current and flag == "%" then + default_selection_idx = i + end + table.insert(buffers, element) + end + end + + if not opts.bufnr_width then + local max_bufnr = math.max(unpack(bufnrs)) + opts.bufnr_width = #tostring(max_bufnr) + end + + pickers + .new(opts, { + prompt_title = "Buffers", + finder = finders.new_table { + results = buffers, + entry_maker = opts.entry_maker or make_entry.gen_from_buffer(opts), + }, + previewer = conf.grep_previewer(opts), + sorter = conf.generic_sorter(opts), + default_selection_index = default_selection_idx, + attach_mappings = function(_, map) + map({ "i", "n" }, "", actions.delete_buffer) + return true + end, + }) + :find() + end + + internal.colorscheme = function(opts) + local before_background = vim.o.background + local before_color = vim.api.nvim_exec2("colorscheme", { output = true }).output + local need_restore = not not opts.enable_preview + + local colors = opts.colors or { before_color } + if not vim.tbl_contains(colors, before_color) then + table.insert(colors, 1, before_color) + end + + colors = vim.list_extend( + colors, + vim.tbl_filter(function(color) + return not vim.tbl_contains(colors, color) + end, vim.fn.getcompletion("", "color")) + ) + + -- if lazy is available, extend the colors list with unloaded colorschemes + local lazy = package.loaded["lazy.core.util"] + if lazy and lazy.get_unloaded_rtp then + local paths = lazy.get_unloaded_rtp "" + local all_files = vim.fn.globpath(table.concat(paths, ","), "colors/*", 1, 1) + for _, f in ipairs(all_files) do + local color = vim.fn.fnamemodify(f, ":t:r") + if not vim.tbl_contains(colors, color) then + table.insert(colors, color) + end + end + end + + if opts.ignore_builtins then + -- stylua: ignore + local builtins = { + "blue", "darkblue", "default", "delek", "desert", "elflord", "evening", + "habamax", "industry", "koehler", "lunaperche", "morning", "murphy", + "pablo", "peachpuff", "quiet", "retrobox", "ron", "shine", "slate", + "sorbet", "torte", "vim", "wildcharm", "zaibatsu", "zellner", + } + colors = vim.tbl_filter(function(color) + return not vim.tbl_contains(builtins, color) + end, colors) + end + + local previewer + if opts.enable_preview then + -- define previewer + local bufnr = vim.api.nvim_get_current_buf() + local p = vim.api.nvim_buf_get_name(bufnr) + + -- show current buffer content in previewer + previewer = previewers.new_buffer_previewer { + get_buffer_by_name = function() + return p + end, + define_preview = function(self) + if vim.loop.fs_stat(p) then + conf.buffer_previewer_maker(p, self.state.bufnr, { bufname = self.state.bufname }) + else + local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false) + vim.api.nvim_buf_set_lines(self.state.bufnr, 0, -1, false, lines) + end + end, + } + end + + local picker = pickers.new(opts, { + prompt_title = "Change Colorscheme", + finder = finders.new_table { + results = colors, + }, + sorter = conf.generic_sorter(opts), + previewer = previewer, + attach_mappings = function(prompt_bufnr) + actions.select_default:replace(function() + local selection = action_state.get_selected_entry() + if selection == nil then + utils.__warn_no_selection "builtin.colorscheme" + return + end + + need_restore = false + actions.close(prompt_bufnr) + vim.cmd.colorscheme(selection.value) + end) + return true + end, + on_complete = { + function() + local selection = action_state.get_selected_entry() + if selection == nil then + utils.__warn_no_selection "builtin.colorscheme" + return + end + if opts.enable_preview then + vim.cmd.colorscheme(selection.value) + end + end, + }, + }) + + if opts.enable_preview then + -- rewrite picker.close_windows. restore color if needed + local close_windows = picker.close_windows + picker.close_windows = function(status) + close_windows(status) + if need_restore then + vim.o.background = before_background + vim.cmd.colorscheme(before_color) + end + end + + -- rewrite picker.set_selection so that color schemes can be previewed when the current + -- selection is shifted using the keyboard or if an item is clicked with the mouse + local set_selection = picker.set_selection + picker.set_selection = function(self, row) + set_selection(self, row) + local selection = action_state.get_selected_entry() + if selection == nil then + utils.__warn_no_selection "builtin.colorscheme" + return + end + if opts.enable_preview then + vim.cmd.colorscheme(selection.value) + end + end + end + + picker:find() + end + + internal.marks = function(opts) + local local_marks = { + items = vim.fn.getmarklist(opts.bufnr), + name_func = function(_, line) + return vim.api.nvim_buf_get_lines(opts.bufnr, line - 1, line, false)[1] + end, + } + local global_marks = { + items = vim.fn.getmarklist(), + name_func = function(mark, _) + -- get buffer name if it is opened, otherwise get file name + return vim.api.nvim_get_mark(mark, {})[4] + end, + } + local marks_table = {} + local marks_others = {} + local bufname = vim.api.nvim_buf_get_name(opts.bufnr) + local all_marks = {} + opts.mark_type = vim.F.if_nil(opts.mark_type, "all") + if opts.mark_type == "all" then + all_marks = { local_marks, global_marks } + elseif opts.mark_type == "local" then + all_marks = { local_marks } + elseif opts.mark_type == "global" then + all_marks = { global_marks } + end + + for _, cnf in ipairs(all_marks) do + for _, v in ipairs(cnf.items) do + -- strip the first single quote character + local mark = string.sub(v.mark, 2, 3) + local _, lnum, col, _ = unpack(v.pos) + local name = cnf.name_func(mark, lnum) + -- same format to :marks command + local line = string.format("%s %6d %4d %s", mark, lnum, col - 1, name) + local row = { + line = line, + lnum = lnum, + col = col, + filename = utils.path_expand(v.file or bufname), + } + -- non alphanumeric marks goes to last + if mark:match "%w" then + table.insert(marks_table, row) + else + table.insert(marks_others, row) + end + end + end + marks_table = vim.fn.extend(marks_table, marks_others) + + pickers + .new(opts, { + prompt_title = "Marks", + finder = finders.new_table { + results = marks_table, + entry_maker = opts.entry_maker or make_entry.gen_from_marks(opts), + }, + previewer = conf.grep_previewer(opts), + sorter = conf.generic_sorter(opts), + push_cursor_on_edit = true, + push_tagstack_on_edit = true, + }) + :find() + end + + internal.registers = function(opts) + local registers_table = { '"', "-", "#", "=", "/", "*", "+", ":", ".", "%" } + + -- named + for i = 0, 9 do + table.insert(registers_table, tostring(i)) + end + + -- alphabetical + for i = 65, 90 do + table.insert(registers_table, string.char(i)) + end + + pickers + .new(opts, { + prompt_title = "Registers", + finder = finders.new_table { + results = registers_table, + entry_maker = opts.entry_maker or make_entry.gen_from_registers(opts), + }, + sorter = conf.generic_sorter(opts), + attach_mappings = function(_, map) + actions.select_default:replace(actions.paste_register) + map({ "i", "n" }, "", actions.edit_register) + + return true + end, + }) + :find() + end + + internal.keymaps = function(opts) + opts.modes = vim.F.if_nil(opts.modes, { "n", "i", "c", "x" }) + opts.show_plug = vim.F.if_nil(opts.show_plug, true) + opts.only_buf = vim.F.if_nil(opts.only_buf, false) + + local keymap_encountered = {} -- used to make sure no duplicates are inserted into keymaps_table + local keymaps_table = {} + local max_len_lhs = 0 + + -- helper function to populate keymaps_table and determine max_len_lhs + local function extract_keymaps(keymaps) + for _, keymap in pairs(keymaps) do + local keymap_key = keymap.buffer .. keymap.mode .. keymap.lhs -- should be distinct for every keymap + if not keymap_encountered[keymap_key] then + keymap_encountered[keymap_key] = true + if + (opts.show_plug or not string.find(keymap.lhs, "")) + and (not opts.lhs_filter or opts.lhs_filter(keymap.lhs)) + and (not opts.filter or opts.filter(keymap)) + then + table.insert(keymaps_table, keymap) + max_len_lhs = math.max(max_len_lhs, #utils.display_termcodes(keymap.lhs)) + end + end + end + end + + for _, mode in pairs(opts.modes) do + local global = vim.api.nvim_get_keymap(mode) + local buf_local = vim.api.nvim_buf_get_keymap(0, mode) + if not opts.only_buf then + extract_keymaps(global) + end + extract_keymaps(buf_local) + end + opts.width_lhs = max_len_lhs + 1 + + pickers + .new(opts, { + prompt_title = "Key Maps", + finder = finders.new_table { + results = keymaps_table, + entry_maker = opts.entry_maker or make_entry.gen_from_keymaps(opts), + }, + sorter = conf.generic_sorter(opts), + attach_mappings = function(prompt_bufnr) + actions.select_default:replace(function() + local selection = action_state.get_selected_entry() + if selection == nil then + utils.__warn_no_selection "builtin.keymaps" + return + end + + vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes(selection.value.lhs, true, false, true), "t", true) + return actions.close(prompt_bufnr) + end) + return true + end, + }) + :find() + end + + internal.filetypes = function(opts) + local filetypes = vim.fn.getcompletion("", "filetype") + + pickers + .new(opts, { + prompt_title = "Filetypes", + finder = finders.new_table { + results = filetypes, + }, + sorter = conf.generic_sorter(opts), + attach_mappings = function(prompt_bufnr) + actions.select_default:replace(function() + local selection = action_state.get_selected_entry() + if selection == nil then + print "[telescope] Nothing currently selected" + return + end + + actions.close(prompt_bufnr) + vim.cmd("setfiletype " .. selection[1]) + end) + return true + end, + }) + :find() + end + + internal.highlights = function(opts) + local highlights = vim.fn.getcompletion("", "highlight") + + pickers + .new(opts, { + prompt_title = "Highlights", + finder = finders.new_table { + results = highlights, + entry_maker = opts.entry_maker or make_entry.gen_from_highlights(opts), + }, + sorter = conf.generic_sorter(opts), + attach_mappings = function(prompt_bufnr) + actions.select_default:replace(function() + local selection = action_state.get_selected_entry() + if selection == nil then + utils.__warn_no_selection "builtin.highlights" + return + end + + actions.close(prompt_bufnr) + vim.cmd("hi " .. selection.value) + end) + return true + end, + previewer = previewers.highlights.new(opts), + }) + :find() + end + + internal.autocommands = function(opts) + local autocmds = vim.api.nvim_get_autocmds {} + table.sort(autocmds, function(lhs, rhs) + return lhs.event < rhs.event + end) + pickers + .new(opts, { + prompt_title = "autocommands", + finder = finders.new_table { + results = autocmds, + entry_maker = opts.entry_maker or make_entry.gen_from_autocommands(opts), + }, + previewer = previewers.autocommands.new(opts), + sorter = conf.generic_sorter(opts), + attach_mappings = function(prompt_bufnr) + action_set.select:replace_if(function() + local selection = action_state.get_selected_entry() + if selection == nil then + return false + end + local val = selection.value + local cb = val.callback + if vim.is_callable(cb) then + if type(cb) ~= "string" then + local f = type(cb) == "function" and cb or rawget(getmetatable(cb), "__call") + local info = debug.getinfo(f, "S") + local file = info.source:match "^@(.+)" + local lnum = info.linedefined + if file and (lnum or 0) > 0 then + selection.filename, selection.lnum, selection.col = file, lnum, 1 + return false + end + end + end + local group_name = val.group_name ~= "" and val.group_name or "" + local output = + vim.fn.execute("verb autocmd " .. group_name .. " " .. val.event .. " " .. val.pattern, "silent") + for line in output:gmatch "[^\r\n]+" do + local source_file = line:match "Last set from (.*) line %d*$" or line:match "Last set from (.*)$" + if source_file and source_file ~= "Lua" then + selection.filename = source_file + local source_lnum = line:match "line (%d*)$" or "1" + selection.lnum = tonumber(source_lnum) + selection.col = 1 + return false + end + end + return true + end, function() + local selection = action_state.get_selected_entry() + actions.close(prompt_bufnr) + print("You selected autocmd: " .. vim.inspect(selection.value)) + end) + + return true + end, + }) + :find() + end + + internal.spell_suggest = function(opts) + local cursor_word = vim.fn.expand "" + local suggestions = vim.fn.spellsuggest(cursor_word) + + pickers + .new(opts, { + prompt_title = "Spelling Suggestions", + finder = finders.new_table { + results = suggestions, + }, + sorter = conf.generic_sorter(opts), + attach_mappings = function(prompt_bufnr) + actions.select_default:replace(function() + local selection = action_state.get_selected_entry() + if selection == nil then + utils.__warn_no_selection "builtin.spell_suggest" + return + end + + action_state.get_current_picker(prompt_bufnr)._original_mode = "i" + actions.close(prompt_bufnr) + vim.cmd('normal! "_ciw' .. selection[1]) + vim.cmd "stopinsert" + end) + return true + end, + }) + :find() + end + + internal.tagstack = function(opts) + opts = opts or {} + local tagstack = vim.fn.gettagstack().items + + local tags = {} + for i = #tagstack, 1, -1 do + local tag = tagstack[i] + tag.bufnr = tag.from[1] + if vim.api.nvim_buf_is_valid(tag.bufnr) then + tags[#tags + 1] = tag + tag.filename = vim.fn.bufname(tag.bufnr) + tag.lnum = tag.from[2] + tag.col = tag.from[3] + + tag.text = vim.api.nvim_buf_get_lines(tag.bufnr, tag.lnum - 1, tag.lnum, false)[1] or "" + end + end + + if vim.tbl_isempty(tags) then + utils.notify("builtin.tagstack", { + msg = "No tagstack available", + level = "WARN", + }) + return + end + + pickers + .new(opts, { + prompt_title = "TagStack", + finder = finders.new_table { + results = tags, + entry_maker = make_entry.gen_from_quickfix(opts), + }, + previewer = conf.qflist_previewer(opts), + sorter = conf.generic_sorter(opts), + }) + :find() + end + + internal.jumplist = function(opts) + opts = opts or {} + local jumplist = vim.fn.getjumplist()[1] + + -- reverse the list + local sorted_jumplist = {} + for i = #jumplist, 1, -1 do + if vim.api.nvim_buf_is_valid(jumplist[i].bufnr) then + jumplist[i].text = vim.api.nvim_buf_get_lines(jumplist[i].bufnr, jumplist[i].lnum - 1, jumplist[i].lnum, false) + [1] + or "" + table.insert(sorted_jumplist, jumplist[i]) + end + end + + pickers + .new(opts, { + prompt_title = "Jumplist", + finder = finders.new_table { + results = sorted_jumplist, + entry_maker = make_entry.gen_from_quickfix(opts), + }, + previewer = conf.qflist_previewer(opts), + sorter = conf.generic_sorter(opts), + }) + :find() + end + + local function apply_checks(mod) + for k, v in pairs(mod) do + mod[k] = function(opts) + opts = opts or {} + + v(opts) + end + end + + return mod + end + + return apply_checks(internal) diff --git a/lua/telescope/builtin/init.lua b/lua/telescope/builtin/init.lua index 6e27c27ab3..a984cd6d29 100644 --- a/lua/telescope/builtin/init.lua +++ b/lua/telescope/builtin/init.lua @@ -242,6 +242,8 @@ builtin.git_stash = require_on_exported_call("telescope.builtin.__git").stash ---@param opts table: options to pass to the picker ---@field include_extensions boolean: if true will show the pickers of the installed extensions (default: false) ---@field use_default_opts boolean: if the selected picker should use its default options (default: false) +---@field filter_match string: if the filter_match is provided, it will be used to filter the picker (default: nil) +---@field title string: the title of the picker (default: "Telescope Builtin") builtin.builtin = require_on_exported_call("telescope.builtin.__internal").builtin --- Opens the previous picker in the identical state (incl. multi selections) diff --git a/lua/telescope/make_entry.lua b/lua/telescope/make_entry.lua index e629eeaecc..6e2930aeec 100644 --- a/lua/telescope/make_entry.lua +++ b/lua/telescope/make_entry.lua @@ -1360,8 +1360,12 @@ function make_entry.gen_from_git_status(opts) local status_x = git_abbrev[x] or {} local status_y = git_abbrev[y] or {} - local display_path, path_style = utils.transform_path(opts, entry.path) - + local display_path = entry.path + local path_style= nil + if utils.iswin then --for plenary problem + display_path = display_path:gsub("/", "\\") + end + display_path ,path_style = utils.transform_path(opts, display_path) local empty_space = " " return displayer { { status_x.icon or empty_space, status_x.hl },