Repository: FabianWirth/search.nvim Branch: main Commit: 7b8f2315d031 Files: 11 Total size: 27.7 KB Directory structure: gitextract_zr5l5e27/ ├── LICENSE.md ├── README.md ├── lua/ │ └── search/ │ ├── init.lua │ ├── settings.lua │ ├── tab_bar.lua │ ├── tabs/ │ │ ├── collection.lua │ │ ├── init.lua │ │ └── tab.lua │ └── util.lua └── tests/ ├── configuration_spec.lua └── minimal_init.vim ================================================ FILE CONTENTS ================================================ ================================================ FILE: LICENSE.md ================================================ MIT License Copyright (c) 2023 Fabian Wirth Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # search.nvim *"**search.nvim** is a Neovim plugin that enhances the functionality of the [Telescope](https://github.com/nvim-telescope/telescope.nvim) plugin by providing a tab-based search experience. It allows you to seamlessly switch between different search modes within the Telescope window using tabs"* - ChatGPT ![example](https://raw.githubusercontent.com/FabianWirth/search.nvim/main/example.gif) > [!WARNING] > this plugin is in early development and might have some bugs. You can also expect changes to the configuration api. ## Features - **Tab-based Searching**: Easily switch between different search modes, each represented by a tab. - **Integration with Telescope**: Leverages the power of the Telescope plugin for versatile searching. - **Customizable Tabs**: Configure the available tabs according to your preferences. (coming soon) - **keybindings for switching tabs**: switch tabs by configurable keys ## Installation Install search.nvim using your preferred plugin manager. For example: ```lua --- lazy nvim { "FabianWirth/search.nvim", dependencies = { "nvim-telescope/telescope.nvim" } } ``` ## default tabs the default tabs are: - find_files - git_files - live_grep they can be configured in the setup function. ## Usage ### Opening the Tabsearch Window To open the search.nvim window, use the following command: ```lua require('search').open() ``` This will activate the default tab and open the Telescope window with the specified layout. it is also possible to provide a tab_id or tab_name to directly activate a specific tab (id takes precedence over name). Any tab collections defined can also be accessed via the collection key. ```lua require('search').open({ tab_id = 2 }) require('search').open({ tab_name = 'Grep' }) -- if multiple tabs are named the same, the first is selected require('search').open({ collection = 'git' }) -- Open the 'git' collection of pickers ``` ### Switching Tabs Navigate between tabs using the **``** and **``** keys in normal and insert modes. This allows you to switch between different search modes conveniently. ### Customizing Tabs You can customize the available tabs by modifying the tabs table in the plugin configuration. Each tab should be defined as a Lua table with the following properties: - name: Display name of the tab. - tele_func: The Telescope function associated with the tab. - available (optional): A function to determine if the tab is currently available based on certain conditions. For example: ```lua local builtin = require('telescope.builtin') require("search").setup({ mappings = { -- optional: configure the mappings for switching tabs (will be set in normal and insert mode(!)) next = "", prev = "" }, append_tabs = { -- append_tabs will add the provided tabs to the default ones { "Commits", -- or name = "Commits" builtin.git_commits, -- or tele_func = require('telescope.builtin').git_commits available = function() -- optional return vim.fn.isdirectory(".git") == 1 end } }, -- its also possible to overwrite the default tabs using the tabs key instead of append_tabs tabs = { { "Files", function(opts) opts = opts or {} if vim.fn.isdirectory(".git") == 1 then builtin.git_files(opts) else builtin.find_files(opts) end end } }, }) ``` ### Customizing key bindings Simple rebind, will bind the the keys in both normal mode and insert mode. ```lua mappings = { next = "", prev = "" } ``` You can also bind keys in specific modes by supplying a list of key-mode pairs. The following would bind H and L to previous and next in normal mode in addition to binding tab and shift+tab like in the example above. ```lua mappings = { next = { { "L", "n" }, { "", "n" }, { "", "i" } }, prev = { { "H", "n" }, { "", "n" }, { "", "i" } } } ``` ### Tab Collections If you want to group certain pickers together into separate search windows you can use the collections keyword: ```lua local builtin = require('telescope.builtin') require("search").setup({ initial_tab = 1, tabs = { ... }, -- As shown above collections = { -- Here the "git" collection is defined. It follows the same configuraton layout as tabs. git = { initial_tab = 1, -- Git branches tabs = { { name = "Branches", tele_func = builtin.git_branches }, { name = "Commits", tele_func = builtin.git_commits }, { name = "Stashes", tele_func = builtin.git_stash }, } } } }) ``` ### Passing options to telescope #### Passing simple telescope options Options can be passed to each telescope picker using the `tele_opts` table. For example having two tabs, one with hidden files and one without. ```lua local builtin = require('telescope.builtin') require("search").setup({ initial_tab = 1, tabs = { { name = "Files", tele_func = builtin.find_files }, { name = "All Files", tele_func = builtin.find_files, tele_opts = { no_ignore = true, hidden = true }}, }, }) ``` #### Passing default_text The above method should be useful for most use cases. One exception though is passing `default_text` to telescope. Since search.nvim persists the prompt between tabs providing a `default_text` to each tab will break it. Instead default_text can be passed when calling `open`. ```lua require('search').open({ tab_name = 'Grep', default_text = get_visual_selection() }) -- or -- NOTE: tele_opts defined here are only sent to the initial tab. require('search').open({ tab_name = 'Grep', tele_opts = { default_text = get_visual_selection() } }) ``` #### Advanced picker configuration More advanced use cases are best handled by making your own `tele_func` and handle your logic there. ```lua require("search").setup({ initial_tab = 1, tabs = { { name = "Files", tele_func = function() -- Your custom config logic. telescope.find_files({}) end }, }, }) ``` ### known issues - pickers with more-than-average loading time (like lsp related, or http sending pickers) can feel a bit off, since the UI will wait for them to be ready. - heavily custom configured telescope settings (like in many nvim distros) might lead to unexpected errors, please open an issue if you encounter any. - A window with no available pickers can cause neovim to hang. ## License This plugin is licensed under the MIT License. See the [LICENSE](https://github.com/FabianWirth/search.nvim?tab=MIT-1-ov-file) file for details. ------------------------------------------------------------------------------- Happy searching with search.nvim! 🚀 ================================================ FILE: lua/search/init.lua ================================================ local M = {} local util = require('search.util') local settings = require('search.settings') local tab_bar = require('search.tab_bar') local tabs = require('search.tabs') --- opens the tab window and anchors it to the telescope window --- @param telescope_win_id number the id of the telescope window --- @return nil local tab_window = function(telescope_win_id) -- the width of the prompt local telescope_width = vim.fn.winwidth(telescope_win_id) -- if the telescope window is closed, we exit early -- this can happen when the user holds down the tab key if telescope_width == -1 then return end -- create the tab bar window, anchoring it to the telescope window local tab_bar_win = tab_bar.create({ width = telescope_width, relative = 'win', win = telescope_win_id, col = 0, row = 2, }) -- make this window disappear when the telescope window is closed local tele_buf = vim.api.nvim_get_current_buf() vim.api.nvim_create_autocmd("WinLeave", { buffer = tele_buf, nested = true, once = true, callback = function() vim.api.nvim_win_close(tab_bar_win, true) end, }) end --- opens the telescope window and sets the prompt to the one that was used before local open_telescope = function(telescope_opts) M.busy = true local tab = tabs.current() local prompt = M.current_prompt local mode = vim.api.nvim_get_mode().mode -- since some telescope functions are linked to lsp, we need to make sure that we are in the correct buffer -- this would become an issue if we are coming from another tab if vim.api.nvim_get_current_win() ~= M.opened_on_win then vim.api.nvim_set_current_win(M.opened_on_win) end tab:start_waiting() -- Pass along any telescope options. Set the title to the tab name. local tele_opts = tab.tele_opts or {} tele_opts.prompt_title = tab.name -- Merge telescope options passed from different places. -- Merge telescope_opts and tab.tele_opts for k,v in pairs(telescope_opts or {}) do tele_opts[k] = v end -- then we spawn the telescope window local success = pcall(tab.tele_func, tele_opts) -- this (only) happens, if the telescope function actually errors out. -- if the telescope window does not open without error, this is not handled here if not success then M.busy = false tab:fail() M.continue_tab(false) return end -- find a better way to do this -- we might need to wait for the telescope window to open util.do_when(function() -- wait for the window change return M.opened_on_win ~= vim.api.nvim_get_current_win() end, function() tab:stop_waiting() local current_win_id = vim.api.nvim_get_current_win() util.set_keymap() -- now we set the prompt to the one we had before vim.api.nvim_feedkeys(prompt, 't', true) -- If keymaps for navigating panes are defined in normal mode, the prompt should remain in normal mode to allow -- navigating multiple maps at a time. -- If the mode was normal mode before the tab change, then change back to normal mode. This is unless the search -- is being opened using open(), since then then user could be using normal mode in their previous active buffer. if mode == "n" and M.opened_from_buffer == false then vim.api.nvim_feedkeys(vim.api.nvim_replace_termcodes("", true, false, true), 'n', false) end M.opened_from_buffer = false -- TODO: find a better way to do this - defer_fn will work, but will also cause some kind of redrawing -- using vim.wait(n) does not work vim.defer_fn(function() tab_window(current_win_id) M.busy = false end, 4) end, 2000, -- wait for 2 second at most function() M.busy = false tab:fail() M.continue_tab(false) end ) end --- the prompt that was used before M.current_prompt = "" M.direction = "next" M.busy = false M.continue_tab = function(remember) if M.direction == "next" then M.next_tab(remember) else M.previous_tab(remember) end end --- switches to the next tab, preserving the prompt --- only switches to tabs that are available M.next_tab = function(remember) remember = remember == nil and true or remember M.direction = "next" if M.busy then return end util.next_available() if remember then M.remember_prompt() end open_telescope() end --- switches to the previous tab, preserving the prompt M.previous_tab = function(remember) remember = remember == nil and true or remember M.direction = "previous" if M.busy then return end util.previous_available() if remember then M.remember_prompt() end open_telescope() end --- remembers the prompt that was used before M.remember_prompt = function() local current_prompt = vim.api.nvim_get_current_line() -- removing the prefix, by cutting the length local without_prefix = string.sub(current_prompt, M.prefix_len + 1) M.current_prompt = without_prefix end --- resets the state of the search module M.reset = function(opts) opts = opts or {} tabs.current_collection_id = "default" if opts.collection then tabs.current_collection_id = opts.collection end if opts.tab_id then tabs.set_by_id(opts.tab_id) elseif opts.tab_name then tabs.set_by_name(opts.tab_name) else tabs.initial_tab() end M.current_prompt = "" M.opened_on_win = -1 M.opened_from_buffer = true end -- the prefix can be defined in the telescope config, so we need to read -- it's length in the open() method. -- @todo: maybe do the reading somewhere else, to avoid doing so multiple times M.prefix_len = 2 M.opened_on_win = -1 M.opened_from_buffer = true --- opens the telescope window with the current prompt --- this is the function that should be called from the outside M.open = function(opts) -- TODO: find a better way to do this -- this is just a workaround to make sure that the settings are initialized -- if the user did not call setup() themselves if not settings.initialized then settings.setup() end local prefix = require("telescope.config").values.prompt_prefix or "> " M.prefix_len = #prefix M.reset(opts) M.opened_on_win = vim.api.nvim_get_current_win() M.busy = true M.opened_from_buffer = true -- Pass along tele_opts to telescope local tele_func_opts = {} if opts ~= nil then tele_func_opts = opts.tele_opts or {} if tele_func_opts.default_text == nil then tele_func_opts.default_text = opts.default_text or "" end end open_telescope(tele_func_opts) end -- configuration M.setup = function(opts) settings.setup(opts) end return M ================================================ FILE: lua/search/settings.lua ================================================ local M = {} M.default_initial_tab = 1 M.initialized = false M.default_keys = { next = { { "", "n" }, { "", "i" } }, prev = { { "", "n" }, { "", "i" } }, } M.keys = vim.deepcopy(M.default_keys) local builtin = require('telescope.builtin') M.defaults = { { "Files", builtin.find_files, }, { "Git files", builtin.git_files, available = function() return vim.fn.isdirectory(".git") == 1 end }, { "Grep", builtin.live_grep, }, } M.setup = function(opts) opts = opts or {} -- the deepcopz is needed to avoid the tabs being shared between -- between calls of the setup function - not sure if we need this -- but it's better to be safe than sorry local tabs = vim.deepcopy(M.defaults) local initial_tab = M.default_initial_tab local collections = {} -- if the user has specified a custom list of tabs, use that instead -- of the default if opts.tabs ~= nil then tabs = opts.tabs end if opts.collections ~= nil then collections = opts.collections end -- if the user has specified a custom list of tabs to append, append -- them to the current list of tabs if opts.append_tabs ~= nil then for _, tab in ipairs(opts.append_tabs) do table.insert(tabs, tab) end end M.keys = vim.deepcopy(M.default_keys) if opts.mappings ~= nil then if opts.mappings.next ~= nil then M.keys.next = opts.mappings.next end if opts.mappings.prev ~= nil then M.keys.prev = opts.mappings.prev end end -- if the user has specified a custom initial tab, use that instead -- of the default if opts.initial_tab ~= nil then initial_tab = opts.initial_tab end require("search.tabs").init({ tabs = tabs, collections = collections, initial_id = initial_tab, }) M.initialized = true end return M ================================================ FILE: lua/search/tab_bar.lua ================================================ local M = {} local tabs_module = require("search.tabs") M.seperator = "|" M.create = function(conf) local buf_id = vim.api.nvim_create_buf(false, true) local max_width = conf.width local tabline_config = M.make(buf_id, max_width) local config = vim.tbl_extend("keep", tabline_config, conf) return vim.api.nvim_open_win(buf_id, false, config) end M.make = function(buf_id, max_width) local collection = tabs_module.current_collection() local tabs = collection:all() local current_row = "" local content = {} local hil_groups = {} for _, tab in ipairs(tabs) do local tab_name = " " .. tab.name .. " " local len = #tab_name + #M.seperator if #current_row + len > max_width then --- we have to add a new row table.insert(content, current_row) current_row = "" end local group = ""; if tab:is_current(collection) then group = "ActiveSearchTab" end if tab:has_failed() then group = "FailedSearchTab" end if not tab:is_available() then group = "InactiveSearchTab" end if tab:is_waiting() then group = "WaitingSearchTab" end if group ~= "" then table.insert(hil_groups, { s = #current_row, e = #current_row + #tab_name, r = #content, g = group }) end current_row = current_row .. tab_name .. M.seperator end if current_row ~= "" then table.insert(content, current_row) end vim.api.nvim_buf_set_lines(buf_id, 0, -1, false, content) -- Other highlight groups can be found at https://neovim.io/doc/user/syntax.html#%3Ahighlight vim.api.nvim_set_hl(0, 'ActiveSearchTab', vim.api.nvim_get_hl(0, {name="IncSearch"})) vim.api.nvim_set_hl(0, 'FailedSearchTab', vim.api.nvim_get_hl(0, {name="Error"})) vim.api.nvim_set_hl(0, 'InactiveSearchTab', vim.api.nvim_get_hl(0, {name="Conceal"})) vim.api.nvim_set_hl(0, 'WaitingSearchTab', vim.api.nvim_get_hl(0, {name="PmenuKind"})) for _, group in ipairs(hil_groups) do vim.api.nvim_buf_add_highlight(buf_id, -1, group.g, group.r, group.s, group.e) end return { width = max_width, height = #content, style = 'minimal', focusable = false, noautocmd = true, } end return M ================================================ FILE: lua/search/tabs/collection.lua ================================================ local Tab = require('search.tabs.tab') local TabCollection = {} function TabCollection:new(opts) local tab_list = {} for id, t in ipairs(opts.tabs) do local tab = Tab:new(t, id) tab_list[id] = tab end local id = opts.initial_tab or 1 local o = { tab_list = tab_list, current_id = id, initial_id = id, } setmetatable(o, self) self.__index = self return o end function TabCollection:all() return self.tab_list end function TabCollection:current_tab() return self.current_id end --- get the current tab function TabCollection:current() return self.tab_list[self.current_id] end function TabCollection:set_current(id) self.current_id = id end --- get the next tab --- @return Tab # the next tab function TabCollection:next() self.current_id = self.current_id + 1 if self.current_id > #self.tab_list then self.current_id = 1 end return self:current() end --- get the previous tab --- @return Tab # the previous tab function TabCollection:previous() self.current_id = self.current_id - 1 if self.current_id < 1 then self.current_id = #self.tab_list end return self:current() end function TabCollection:initial_tab() self.current_id = self.initial_id return self:current() end --- get the tab with the given name --- @param name string the name of the tab --- @return Tab|nil # the tab with the given name function TabCollection:id_by_name(name) for _, tab in ipairs(self.tab_list) do if tab.name == name then return tab end end return nil end --- set the tab with the given name as the current tab --- @param name string the name of the tab --- @return boolean # true if the tab was found, false otherwise function TabCollection:set_by_name(name) local tab = self:id_by_name( name) if tab then self.current_id = tab.id return true end return false end function TabCollection:set_by_id(id) self.current_id = id end --- get the tab with the given id --- @param id number the id of the tab --- @return Tab|nil # the tab with the given id function TabCollection:find_by_id(id) for _, tab in ipairs(self.tab_list) do if tab.id == id then return tab end end return nil end return TabCollection ================================================ FILE: lua/search/tabs/init.lua ================================================ local M = {} TabCollection = require("search.tabs.collection") --- M.collections = {} M.current_collection_id = "default" --- get all tabs --- @return table # list of all tabs M.all = function() return M.current_collection():all() end M.current_collection = function() return M.collections[M.current_collection_id] end --- initialize the tabs module --- @param opts table with the following keys: -- - tabs: list of tabs -- - inital_tab: id of the tab to start with M.init = function(opts) M.collections["default"] = TabCollection:new(opts) for id, collection_config in pairs(opts.collections) do local collection = TabCollection:new(collection_config) M.collections[id] = collection end end --- get the current tab M.current = function() return M.current_collection():current() end --- get the next tab --- @return Tab # the next tab M.next = function() return M.current_collection():next() end --- get the previous tab --- @return Tab # the previous tab M.previous = function() return M.current_collection():previous() end M.initial_tab = function() return M.current_collection():initial_tab() end --- get the tab with the given name --- @param name string the name of the tab --- @return Tab|nil # the tab with the given name M.id_by_name = function(name) return M.current_collection():id_by_name(name) end --- set the tab with the given name as the current tab --- @param name string the name of the tab --- @return boolean # true if the tab was found, false otherwise M.set_by_name = function(name) return M.current_collection():set_by_name(name) end M.set_by_id = function(id) M.current_collection():set_current(id) end --- get the tab with the given id --- @param id number the id of the tab --- @return Tab|nil # the tab with the given id M.find_by_id = function(id) return M.current_collection():find_by_id(id) end return M ================================================ FILE: lua/search/tabs/tab.lua ================================================ local Tab = {} --- @class Tab --- @field id number --- @field name string --- @field tele_func function --- @field available_func function|nil --- @field failed boolean --- @field wait_for number|nil --- @function new create a new tab function Tab:new(tab, id) local name = tab.name or tab[1] local tele_func = tab.tele_func or tab[2] local tele_opts = tab.tele_opts or tab[3] -- this enables the user to define the function as second argument -- even if the first argument is named as name if name ~= nil and tab[1] ~= nil and type(tab[1]) == "function" then tele_func = tab[1] end local o = { id = id, name = name, tele_func = tele_func, tele_opts = tele_opts, available_func = tab.available, failed = false, waiting = false, } setmetatable(o, self) self.__index = self return o end --- @param self Tab --- @return boolean function Tab:is_current(collection) return collection:current_tab() == self.id end --- @return boolean function Tab:is_available() return self.available_func == nil or self.available_func() end function Tab:fail() self.waiting = false self.failed = true end function Tab:has_failed() return self.failed end function Tab:reset_failed() self.failed = false end function Tab:is_waiting() return self.waiting end function Tab:stop_waiting() self.waiting = false end function Tab:start_waiting() self.failed = false self.waiting = true end return Tab ================================================ FILE: lua/search/util.lua ================================================ local M = {} local tabs = require("search.tabs") local settings = require("search.settings") --- the amount of milliseconds to wait between checks M.await_time = 10 --- runs a function when a condition is met --- @param condition function a function that returns a boolean --- @param callback function a function that is called when the condition is met --- @param max_ms number the maximum amount of milliseconds to wait --- @param fail_callback function a function that is called when the condition is not met in time --- @return any the return value of the callback M.do_when = function(condition, callback, max_ms, fail_callback) if max_ms == nil then max_ms = 1000 end while max_ms > 0 do if condition() then return callback() end vim.wait(M.await_time) max_ms = max_ms - M.await_time end if fail_callback ~= nil then return fail_callback() end end --- binds the tab key to the next tab --- and the shift tab key to the previous tab M.set_keymap = function() -- now we bind our tab key to the next tab local opts = { noremap = true, silent = true } local cmd = "lua require('search').next_tab()" local cmd_p = "lua require('search').previous_tab()" local function set_keymap(keymap, cmd) if type(keymap) == "string" then vim.api.nvim_buf_set_keymap(0, 'n', keymap, cmd, opts) vim.api.nvim_buf_set_keymap(0, 'i', keymap, cmd, opts) else for _, value in ipairs(keymap) do vim.api.nvim_buf_set_keymap(0, value[2], value[1], cmd, opts) end end end set_keymap(settings.keys.next, cmd) set_keymap(settings.keys.prev, cmd_p) end --- switches to the next available tab --- @return Tab # the next available tab M.next_available = function() local start = tabs.current().id while true do local tab = tabs.next() if tab:is_available() then return tab end if tab.id == start then return tab end end end --- switches to the previous available tab --- @return Tab # the previous available tab M.previous_available = function() local start = tabs.current().id while true do local tab = tabs.previous() if tab:is_available() then return tab end if tab.id == start then return tab end end end return M; ================================================ FILE: tests/configuration_spec.lua ================================================ local init = require('search.init') local _tabs = require('search.tabs') local _settings = require('search.settings') local eq = assert.are.same -- for my lsp to stop complaining local describe = describe local it = it local before_each = before_each describe("can configure search.nvim", function() before_each(function() _tabs.tab_list = {} end) it("can use default config", function() init.setup() local tabs = _tabs.all() eq(3, #tabs) eq('Files', tabs[1].name) eq('Git files', tabs[2].name) eq('Grep', tabs[3].name) end) it("can append a tab using long syntax", function() local config = { append_tabs = { { "Custom", function() return "custom" end } } } init.setup(config) eq(4, #_tabs.all()) eq('Custom', _tabs.all()[4].name) eq('custom', _tabs.all()[4].tele_func()) end) it("can append a tab using short syntax", function() _tabs.tab_list = {} local config = { append_tabs = { { "Custom", function() return "custom" end } } } init.setup(config) eq(4, #_tabs.all()) eq('Custom', _tabs.all()[4].name) eq('custom', _tabs.all()[4].tele_func()) end) it("can append tab using partially short syntax", function() local config = { append_tabs = { { "Custom", tele_func = function() return "custom" end } } } init.setup(config) eq(4, #_tabs.all()) eq('Custom', _tabs.all()[4].name) eq('custom', _tabs.all()[4].tele_func()) end) it("can append tab using partially short syntax2", function() local config = { append_tabs = { { name = "Custom", function() return "custom" end } } } init.setup(config) eq(4, #_tabs.all()) eq('Custom', _tabs.all()[4].name) eq('custom', _tabs.all()[4].tele_func()) end) it("can define a available function", function() local config = { tabs = { { "Custom", function() return "custom" end, available = function() return false end } } } init.setup(config) eq(1, #_tabs.all()) eq('Custom', _tabs.all()[1].name) eq('custom', _tabs.all()[1].tele_func()) eq(false, _tabs.all()[1]:is_available()) end) it("can configure mappings", function() local config = { mappings = { next = 'l', prev = 'h', } } init.setup(config) eq('l', _settings.keys.next) eq('h', _settings.keys.prev) end) it("has default mappings", function() init.setup() eq('', _settings.keys.next) eq('', _settings.keys.prev) end) end) ================================================ FILE: tests/minimal_init.vim ================================================ set rtp+=../plenary.nvim set rtp+=../telescope.nvim runtime! plenary.nvim/lua/plenary.lua