├── .gitignore ├── README.md └── lua └── telescope └── _extensions └── togglescope.lua /.gitignore: -------------------------------------------------------------------------------- 1 | .luarc.json 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Togglescope 2 | 3 | **Togglescope** is an extension for [telescope.nvim](https://github.com/nvim-telescope/telescope.nvim#pickers) that allows you to toggle between two picker configurations with just one keypress. 4 | 5 | ![togglescope in action](https://user-images.githubusercontent.com/40017636/234332528-1dc07a2a-d8a3-4be2-ac79-7882d0bbc0e9.gif) 6 | 7 | ## Example Use Case 8 | 9 | By default, telescope's live_grep or find_files pickers do not search through hidden or gitignored files. I sometimes find myself grepping for a string, only to then remember that the string I'm looking for is in some random file inside an ignored directory. 10 | 11 | Now if you're like me, and you struggle to remember how to launch a picker with a configuration that doesn't ignore the file you're looking for, **togglescope** is the tool for you! 12 | 13 | Just hit the keymap you configured, and like magic your picker's configuration is swapped with a config more suitable to the special job you're trying to do. 14 | If you change your mind and want your default config back, just hit the same keymap again and everything returns to the way it was. 15 | 16 | ## Installation 17 | 18 | You can install & configure togglescope with any package manager. 19 | There's three steps to the process: 20 | 21 | 1. Install the package as a dependency to `"nvim-telescope/telescope.nvim"` 22 | 2. Add your togglescope configuration under `extensions.togglescope` to your telescope config 23 | 3. Set up keymaps for your modified pickers 24 | 25 | ### With Lazy.nvim 26 | 27 | ```lua 28 | { 29 | --- 1. Register "Theo-Steiner/togglescope" as a dependency to telescope 30 | "nvim-telescope/telescope.nvim", 31 | dependencies = { 32 | "Theo-Steiner/togglescope" 33 | }, 34 | --- 2. Configure togglescope in the extensions setting of your telescope config 35 | config = function() 36 | require("telescope").setup({ 37 | extensions = { 38 | togglescope = { 39 | find_files = { 40 | [''] = { 41 | hidden = true, 42 | } 43 | } 44 | } 45 | }, 46 | }) 47 | end, 48 | --- 3. Configure a keymap to launch the togglescope picker 49 | keys = { 50 | { 51 | "ff", 52 | function() 53 | require('telescope').extensions.togglescope.find_files() 54 | end 55 | }, 56 | }, 57 | } 58 | ``` 59 | 60 | ## Configuration 61 | 62 | Togglescope is configured via `extensions.togglescope` of your telescope config. 63 | A valid `extensions.togglescope` config is structured as `picker_name > keymap > picker_config`. 64 | ```lua 65 | --- A picker that you want to add a toggleable config to. 66 | --- All builtin pickers of telescope are valid as picker_name. 67 | --- @type 'find_files' | 'live_grep' | 'grep_string' ...and so on 68 | --- @see github.com/nvim-telescope/telescope.nvim#pickers 69 | local picker_name = 'find_files' 70 | 71 | --- The keymap that toggles between the toggleable config and the default config. 72 | --- For now the keymap will always be set in insert and normal mode. 73 | --- If necessary I might make this configurable at a later point. 74 | --- @type '' | '' | '' ...whatever you want! 75 | local keymap = '' 76 | 77 | --- The toggleable_config you want to switch to when you hit your keybinding. 78 | --- 'togglescope_title' is a special property that allows you to set a title 79 | --- that is displayed when your toggleable config is active. 80 | --- @type {[string]: any, togglescope_title: string} ...any valid picker config! 81 | local toggleable_config = { 82 | no_ignore = true, 83 | togglescope_title = "Find Files (hidden)" 84 | } 85 | 86 | --- @type {[picker_name]: {[keymap]: toggleable_config}} 87 | local togglescope_config = {[picker_name]: {[keymap]: toggleable_config}} 88 | 89 | require("telescope").setup({ 90 | extensions = { 91 | togglescope = togglescope_config 92 | }, 93 | }) 94 | ``` 95 | 96 | ## Usage 97 | 98 | For every `picker_name` you add as a top level key to your `togglescope_config`, togglescope will generate a modified picker that has a keymapping to toggle between your toggleable config and the picker's default config (the config you originially launched the picker with). 99 | These modified pickers are accessible from `require('telescope').extensions.togglescope` and can be used as you would use builtin telescope pickers. 100 | 101 | ```lua 102 | --- old keymap 103 | { 104 | "ff", 105 | function() 106 | require('telescope.builtin').find_files() 107 | end 108 | } 109 | --- simply becomes 110 | { 111 | "ff", 112 | function() 113 | require('telescope').extensions.togglescope.find_files() 114 | end 115 | } 116 | ``` 117 | 118 | ## Togglescope Recipies 119 | 120 | I thought it might be useful to collect a few useful togglescope configs, so that users can just copy/paste a config they are interested in! 121 | 122 | ### How I (Author) Use Togglescope 123 | 124 | For now I use togglescope to toggle between searching through hidden files using the find_files and live_grep pickers with BurntSushi/ripgrep as a search engine. 125 | 126 | ```lua 127 | local togglescope_config = { 128 | --- configure find_files as a togglescope picker 129 | find_files = { 130 | --- on alternate file hotkey toggle to the below config 131 | [''] = { 132 | --- search through hidden files/directories 133 | hidden = true, 134 | --- search through ignored directories/files 135 | --- (I occasionally want to look into node_modules) 136 | no_ignore = true, 137 | --- when this config is active, set the title to this 138 | togglescope_title = "Find Files (hidden)" 139 | } 140 | }, 141 | --- configure find_files as a togglescope picker 142 | live_grep = { 143 | --- on alternate file hotkey toggle to the below config 144 | [''] = { 145 | --- flags are passed to ripgrep using "additional_args" 146 | additional_args = { 147 | --- search through hidden files/directories 148 | '--hidden', 149 | --- search through ignored directories/files 150 | '--no-ignore', 151 | --- specify a glob for the search 152 | "-g", 153 | --- ignore the glob of "package-lock.json" 154 | "!package-lock.json", 155 | }, 156 | --- when this config is active, set the title to this 157 | togglescope_title = "Live Grep (hidden)" 158 | } 159 | } 160 | } 161 | require('telescope').setup({ 162 | extensions = { 163 | --- configure togglescope with the above config 164 | togglescope = togglescope_config 165 | }, 166 | defaults = { 167 | --- set an ignore pattern to always ignore files in the .git directory 168 | file_ignore_patterns = { "^.git/" }, 169 | } 170 | }) 171 | ``` 172 | 173 | ## Acknowledgements 174 | 175 | This extension was heavily influenced and inspired by https://github.com/molecule-man/telescope-menufacture. 176 | -------------------------------------------------------------------------------- /lua/telescope/_extensions/togglescope.lua: -------------------------------------------------------------------------------- 1 | local has_telescope, telescope = pcall(require, 'telescope') 2 | if not has_telescope then 3 | error 'Could not find telescope.nvim (https://github.com/nvim-telescope/telescope.nvim)' 4 | end 5 | 6 | local builtin = require('telescope.builtin') 7 | 8 | --- internal for now, might expose later if needed 9 | --- @type {defaults: {[string]: Opts}} 10 | local M = { 11 | defaults = {}, 12 | } 13 | 14 | --- Generate a function, that calls a picker alternately with its original or modified opts 15 | --- @param picker_name PickerName 16 | --- @param toggle_opts Opts 17 | M.toggle = function(toggle_opts, picker_name) 18 | --- @param _opts Opts 19 | --- @param launch PickerFunction 20 | return function(_opts, launch) 21 | --- deepcopy, so telescope internal tables are not modified 22 | --- @type Opts 23 | local opts = vim.deepcopy(_opts) 24 | if not M.defaults[picker_name] then 25 | --- deepcopy, so defaults are not modified after the fact 26 | M.defaults[picker_name] = vim.deepcopy(opts) 27 | end 28 | if vim.deep_equal(opts, M.defaults[picker_name]) then 29 | opts['prompt_title'] = toggle_opts['togglescope_title'] or 'toggled' 30 | opts = vim.tbl_deep_extend('force', opts, toggle_opts) 31 | else 32 | opts = M.defaults[picker_name] 33 | end 34 | launch(opts, true) 35 | end 36 | end 37 | 38 | --- Generates a "launch" function, that launches a picker with preconfigured keyhandlers 39 | --- @param picker_fn PickerFunction 40 | --- @param toggle_fns {[Keybinding]: Opts} 41 | M.add_action = function(picker_fn, toggle_fns) 42 | local query = nil 43 | --- @param opts Opts 44 | local function launch(opts, is_keymap_invocation) 45 | opts = opts or {} 46 | if not is_keymap_invocation then 47 | --- reset default opts 48 | M.defaults = {} 49 | end 50 | 51 | opts.attach_mappings = function(new_bufnr, map) 52 | -- restore previous query if exists 53 | if query then 54 | local picker = require('telescope.actions.state').get_current_picker(new_bufnr) 55 | picker:set_prompt(query) 56 | -- delete saved query 57 | query = nil 58 | end 59 | for keybinding, toggle_fn in pairs(toggle_fns) do 60 | map({ "n", "i" }, keybinding, function(current_bufnr) 61 | local picker = require('telescope.actions.state').get_current_picker(current_bufnr) 62 | -- save current query 63 | query = picker:_get_prompt() 64 | toggle_fn(opts, launch) 65 | end) 66 | end 67 | return true 68 | end 69 | 70 | --- call the picker function with modified opts 71 | picker_fn(vim.tbl_extend('force', opts, { default_text = opts.prompt_value })) 72 | end 73 | 74 | return launch 75 | end 76 | 77 | --- Generates a picker based on a builtin picker, with custom attach_mappings 78 | --- @param picker_name PickerName 79 | --- @param attach_config AttachConfig 80 | M.generate_picker = function(picker_name, attach_config) 81 | local picker_fn = builtin[picker_name] 82 | if not picker_fn then 83 | error('Could not find picker ' .. picker_name) 84 | end 85 | --- every valid top level entry of the attach_config is a key-combo (eg. '') 86 | --- each key map toggles a certain set of toggle_opts 87 | local toggle_functions = {} 88 | for keybinding, toggle_opts in pairs(attach_config) do 89 | toggle_functions[keybinding] = M.toggle(toggle_opts, picker_name) 90 | end 91 | return M.add_action(picker_fn, toggle_functions) 92 | end 93 | 94 | --- exposed to the enduser - holds generated pickers 95 | --- @type {config: Config} | TogglescopePickers 96 | local togglescope = {} 97 | 98 | --- @see github.com/nvim-telescope/telescope.nvim/blob/master/developers.md#bundling-as-extension 99 | return telescope.register_extension { 100 | --- is run after telescope setup 101 | --- @param user_config Config 102 | setup = function(user_config) 103 | --- accept user_config 104 | togglescope.config = user_config or {} 105 | --- generate a picker with custom attach mappings for every top level entry of the config 106 | --- currently only "telescope.builtin" pickers are valid top level entries 107 | for picker_name, picker_config in pairs(togglescope.config) do 108 | togglescope[picker_name] = M.generate_picker(picker_name, picker_config) 109 | end 110 | end, 111 | --- make generated pickers accessible via 112 | --- require('telescope').extensions.togglescope[picker_name] 113 | exports = togglescope, 114 | } 115 | 116 | --- TYPES 117 | 118 | ---@alias PickerName 119 | ---| "'{picker_name}'" # currently only telescope builtin pickers are allowed 120 | 121 | ---@alias Keybinding 122 | ---| string # such as '' 123 | 124 | ---@alias Opts 125 | ---| {[string]: string | number | {} | fun(number, any): any} 126 | 127 | ---@alias AttachConfig 128 | ---| {[Keybinding]: Opts} 129 | 130 | --- @alias Config 131 | ---| {[string]: AttachConfig} 132 | 133 | --- @alias PickerFunction 134 | ---| fun(Opts, is_keymap_invocation?: boolean): nil 135 | 136 | --- @alias TogglescopePickers 137 | ---| {[PickerName]: PickerFunction} 138 | --------------------------------------------------------------------------------