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

> [!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 **`<Tab>`** and **`<S-Tab>`** 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 = "<Tab>",
prev = "<S-Tab>"
},
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 = "<Tab>",
prev = "<S-Tab>"
}
```
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" }, { "<Tab>", "n" }, { "<Tab>", "i" } },
prev = { { "H", "n" }, { "<S-Tab>", "n" }, { "<S-Tab>", "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("<Esc>", 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 = { { "<Tab>", "n" }, { "<Tab>", "i" } },
prev = { { "<S-Tab>", "n" }, { "<S-Tab>", "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 = "<cmd>lua require('search').next_tab()<CR>"
local cmd_p = "<cmd>lua require('search').previous_tab()<CR>"
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 = '<leader>l',
prev = '<leader>h',
}
}
init.setup(config)
eq('<leader>l', _settings.keys.next)
eq('<leader>h', _settings.keys.prev)
end)
it("has default mappings", function()
init.setup()
eq('<Tab>', _settings.keys.next)
eq('<S-Tab>', _settings.keys.prev)
end)
end)
================================================
FILE: tests/minimal_init.vim
================================================
set rtp+=../plenary.nvim
set rtp+=../telescope.nvim
runtime! plenary.nvim/lua/plenary.lua
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
Condensed preview — 11 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (31K chars).
[
{
"path": "LICENSE.md",
"chars": 1069,
"preview": "MIT License\n\nCopyright (c) 2023 Fabian Wirth\n\nPermission is hereby granted, free of charge, to any person obtaining a co"
},
{
"path": "README.md",
"chars": 6746,
"preview": "# search.nvim\n\n*\"**search.nvim** is a Neovim plugin that enhances the functionality of the [Telescope](https://github.co"
},
{
"path": "lua/search/init.lua",
"chars": 6449,
"preview": "local M = {}\n\nlocal util = require('search.util')\nlocal settings = require('search.settings')\nlocal tab_bar = require('s"
},
{
"path": "lua/search/settings.lua",
"chars": 1780,
"preview": "local M = {}\n\nM.default_initial_tab = 1\n\nM.initialized = false\n\nM.default_keys = {\n\tnext = { { \"<Tab>\", \"n\" }, { \"<Tab>\""
},
{
"path": "lua/search/tab_bar.lua",
"chars": 2124,
"preview": "local M = {}\n\nlocal tabs_module = require(\"search.tabs\")\n\nM.seperator = \"|\"\n\nM.create = function(conf)\n\tlocal buf_id = v"
},
{
"path": "lua/search/tabs/collection.lua",
"chars": 2165,
"preview": "\nlocal Tab = require('search.tabs.tab')\n\nlocal TabCollection = {}\n\nfunction TabCollection:new(opts)\n\tlocal tab_list = {}"
},
{
"path": "lua/search/tabs/init.lua",
"chars": 1867,
"preview": "local M = {}\n\nTabCollection = require(\"search.tabs.collection\")\n---\n\nM.collections = {}\n\nM.current_collection_id = \"defa"
},
{
"path": "lua/search/tabs/tab.lua",
"chars": 1422,
"preview": "\nlocal Tab = {}\n\n--- @class Tab\n--- @field id number\n--- @field name string\n--- @field tele_func function\n--- @field ava"
},
{
"path": "lua/search/util.lua",
"chars": 2238,
"preview": "local M = {}\n\nlocal tabs = require(\"search.tabs\")\nlocal settings = require(\"search.settings\")\n\n--- the amount of millise"
},
{
"path": "tests/configuration_spec.lua",
"chars": 2464,
"preview": "local init = require('search.init')\nlocal _tabs = require('search.tabs')\nlocal _settings = require('search.settings')\n\nl"
},
{
"path": "tests/minimal_init.vim",
"chars": 90,
"preview": "set rtp+=../plenary.nvim\nset rtp+=../telescope.nvim\nruntime! plenary.nvim/lua/plenary.lua\n"
}
]
About this extraction
This page contains the full source code of the FabianWirth/search.nvim GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 11 files (27.7 KB), approximately 7.6k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.