Last active
June 20, 2024 21:23
-
-
Save bluss/31ab305c11c07d7560b26b374864ac4e to your computer and use it in GitHub Desktop.
Telescope find_files/live_grep but with support for toggling no-ignore/hidden
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- Flextelescope | |
-- | |
-- Copyright bluss; Apache 2.0 license | |
-- | |
-- Replace telescope builtins find_files and live_grep | |
-- with wrappers that support toggling no-ignore/hidden | |
local M = {} | |
local this_plugin = "flextelescope" | |
---@class FlextelescopeOptions | |
---@field replace_builtins boolean: Override telescope builtins with wrappers, default true | |
---@field show_hidden_in_git boolean?: Always show hidden in git repos, default true | |
---@field toggle_ignore_key string|boolean?: Mapping for toggle ignore/hidden, default C-h | |
---@field toggle_layout_key string|boolean?: Mapping for toggle vertical/horizontal, default M-h | |
---@field parent_dir_key string|boolean?: Mapping for set cwd to parent dir, default M-p | |
---@field reset_cwd_key string|boolean?: Mapping for reset cwd, default M-c | |
M.defaults = { | |
replace_builtins = true, | |
show_hidden_in_git = true, | |
toggle_ignore_key = "<C-h>", | |
toggle_layout_key = "<M-h>", | |
parent_dir_key = "<M-p>", | |
reset_cwd_key = "<M-c>", | |
} | |
---@type FlextelescopeOptions | |
M.options = M.defaults -- written in setup | |
local attach_mapping_modes = {"i"} | |
local relaunch_marker = this_plugin .. "_relaunch" | |
local state_prefix = this_plugin | |
local telescope_builtin_original = nil | |
-- on first call, original telescope builtins are copied to a separate table | |
-- return saved builtins table | |
---@return table | |
local function _save_builtins() | |
if not telescope_builtin_original then | |
telescope_builtin_original = {} | |
for key, values in pairs(require("telescope.builtin")) do | |
telescope_builtin_original[key] = values | |
end | |
M._telescope_builtin = telescope_builtin_original | |
end | |
return telescope_builtin_original | |
end | |
---@param searcher string | |
---@param opts table | |
local function _call_telescope_builtin(searcher, opts) | |
-- vim.notify("Calling " .. searcher .. " with " .. vim.inspect(opts), vim.log.levels.DEBUG) | |
_save_builtins()[searcher](opts) | |
end | |
local function install_replacement(name, func) | |
-- install an override of a telescope builtin | |
-- on first call, original builtins are copied to a separate table | |
_save_builtins() | |
require("telescope.builtin")[name] = func | |
end | |
local function find_git_root_from(path) | |
local ret = vim.fs.dirname(vim.fs.find({".git"}, { path = path, upward = true })[1]) | |
return ret | |
end | |
---@class SearcherMemory | |
---@field opts table | |
---@field orig_opts table | |
---@field func function | |
local SearcherMemory = {} | |
SearcherMemory.__index = SearcherMemory | |
function SearcherMemory.new(func, opts) | |
local self = {} | |
opts = opts or {} | |
self.opts = opts | |
self.orig_opts = vim.deepcopy(opts) | |
self.func = func | |
return setmetatable(self, SearcherMemory) | |
end | |
function SearcherMemory.reset(self) | |
self.opts = vim.deepcopy(self.orig_opts) | |
end | |
function SearcherMemory.get_opts(self) | |
return self.opts | |
end | |
function SearcherMemory.close_and_relaunch(self, prompt_bufnr) | |
local opts = self.opts | |
local func = self.func | |
assert(func) | |
assert(opts) | |
local actions = require("telescope.actions") | |
actions.close(prompt_bufnr) | |
local action_state = require("telescope.actions.state") | |
local curline = action_state.get_current_line() | |
opts.default_text = curline | |
opts[relaunch_marker] = true | |
return func(opts) | |
end | |
---@param func function | |
function SearcherMemory.modify_opts(self, func) | |
func(self.opts) | |
end | |
---@return boolean: true if not relaunch | |
local function enter_searcher(func, opts) | |
if opts[relaunch_marker] then | |
opts[relaunch_marker] = nil | |
return false | |
end | |
local state = require("telescope.state") | |
state.set_global_key(state_prefix, SearcherMemory.new(func, opts)) | |
return true | |
end | |
function M.modify_relaunch_mapping(modifier) | |
return function(prompt_bufnr) | |
local state = require("telescope.state") | |
---@type SearcherMemory | |
local memory = state.get_global_key(state_prefix) | |
if not memory then | |
vim.notify_once("Expected searcher memory but found none", vim.log.levels.ERROR) | |
return | |
end | |
memory:modify_opts(modifier) | |
vim.schedule(function() memory:close_and_relaunch(prompt_bufnr) end) | |
end | |
end | |
local function isstring(obj) | |
return type(obj) == "string" | |
end | |
function M._common_mappings(_, map) | |
if isstring(M.options.toggle_ignore_key) then | |
local function toggle_ignore(...) | |
return M.modify_relaunch_mapping(function(o) o.no_ignore = not o.no_ignore end)(...) | |
end | |
map(attach_mapping_modes, M.options.toggle_ignore_key, toggle_ignore) | |
end | |
if isstring(M.options.toggle_layout_key) then | |
local function toggle_layout(...) | |
return M.modify_relaunch_mapping(function(o) | |
local default_strategy = require("telescope.config").values.layout_strategy | |
if (o.layout_strategy or default_strategy) == "horizontal" then | |
o.layout_strategy = "vertical" | |
else | |
o.layout_strategy = "horizontal" | |
end | |
end)(...) | |
end | |
map(attach_mapping_modes, M.options.toggle_layout_key, toggle_layout) | |
end | |
if isstring(M.options.parent_dir_key) then | |
local function go_to_parent_dir(...) | |
M.modify_relaunch_mapping(function(o) | |
local cwd = o.cwd or vim.fn.getcwd() | |
o.cwd = vim.fn.fnamemodify(cwd, ":h") | |
end)(...) | |
end | |
map(attach_mapping_modes, M.options.parent_dir_key, go_to_parent_dir) | |
end | |
if isstring(M.options.reset_cwd_key) then | |
local function reset_cwd(...) | |
M.modify_relaunch_mapping(function(o) o.cwd = nil end)(...) | |
end | |
map(attach_mapping_modes, M.options.reset_cwd_key, reset_cwd) | |
end | |
return true | |
end | |
local function my_find_files(opts) | |
opts = opts or {} | |
local searcher = "find_files" | |
local first_call = enter_searcher(my_find_files, opts) | |
if first_call then | |
opts.orig_prompt = opts.prompt_title or "Find Files" | |
end | |
opts.attach_mappings = M._common_mappings | |
local searcher_name = opts.orig_prompt | |
local suffix = nil | |
local cwd_state = opts.cwd | |
if cwd_state then | |
opts.cwd = cwd_state | |
suffix = "CWD=" .. vim.fn.pathshorten(tostring(cwd_state), 2) | |
end | |
if opts.no_ignore then | |
opts.hidden = true | |
opts.prompt_title = searcher_name .. " <ALL>" | |
else | |
opts.hidden = false | |
opts.prompt_title = searcher_name | |
end | |
-- show hidden in git repos - because they are tracked when not ignored | |
if M.options.show_hidden_in_git and not opts.no_ignore then | |
if find_git_root_from(opts.cwd or vim.fn.getcwd()) then | |
opts.hidden = true | |
end | |
end | |
if suffix then | |
opts.prompt_title = opts.prompt_title .. " " .. suffix | |
end | |
_call_telescope_builtin(searcher, opts) | |
end | |
M.find_files = my_find_files | |
local function my_live_grep(opts) | |
opts = opts or {} | |
local searcher = "live_grep" | |
local first_call = enter_searcher(my_live_grep, opts) | |
if first_call then | |
opts.orig_prompt = opts.prompt_title or "Live Grep" | |
end | |
opts.attach_mappings = M._common_mappings | |
local searcher_name = opts.orig_prompt | |
local suffix = nil | |
if opts.cwd then | |
suffix = "CWD=" .. vim.fn.pathshorten(tostring(opts.cwd), 2) | |
end | |
if opts.no_ignore then | |
-- rg -uu | |
if not opts.additional_args then | |
opts.additional_args = {} | |
end | |
table.insert(opts.additional_args, "-uu") | |
opts.prompt_title = searcher_name .. " <ALL>" | |
else | |
opts.additional_args = vim.tbl_filter(function(elt) return elt ~= "-uu" end, opts.additional_args or {}) | |
opts.prompt_title = searcher_name | |
end | |
-- show hidden in git repos - because they are tracked when not ignored | |
if M.options.show_hidden_in_git and not opts.no_ignore then | |
if find_git_root_from(opts.cwd or vim.fn.getcwd()) then | |
if not vim.tbl_contains(opts.additional_args, "--hidden") then | |
table.insert(opts.additional_args, "--hidden") | |
end | |
end | |
end | |
if suffix then | |
opts.prompt_title = opts.prompt_title .. " " .. suffix | |
end | |
_call_telescope_builtin(searcher, opts) | |
end | |
M.live_grep = my_live_grep | |
---@param opts FlextelescopeOptions? | |
function M.setup(opts) | |
vim.validate({ | |
opts = {opts, "t", true}, | |
}) | |
M.options = vim.tbl_deep_extend("force", M.defaults, opts or {}) | |
if M.options.replace_builtins then | |
install_replacement("find_files", M.find_files) | |
install_replacement("live_grep", M.live_grep) | |
end | |
end | |
return M |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment