Compare commits

...

5 commits

Author SHA1 Message Date
73536b848f docs: update the API section of the docs 2023-03-01 21:24:02 +00:00
df6b58bc7d docs: update IvyFd command description 2023-03-01 21:24:02 +00:00
47193a0f4f docs: add IvyWorkspaceSymbols to the readme 2023-03-01 21:24:02 +00:00
c930fb1594 feat: add IvyWorkspaceSymbol command
This is a command that will search the lsp workspace symbols on a search
term.
2023-03-01 08:23:07 +00:00
2188552724 refactor: introduce backend concept and split out finders / sorters
We now have a concept of a 'backend' this is the same as the current
sorters and finders but with added info like the keymap so they can all
be registered as one. This will allow us to split our backends into
modues so we can better maintain then.
2023-03-01 08:17:23 +00:00
8 changed files with 229 additions and 67 deletions

View file

@ -51,11 +51,12 @@ error: linker `cc` not found
A command can be run that will launch the completion UI
| Command | Key Map | Description |
| ---------- | ----------- | ------------------------------------------------------ |
| IvyFd | \<leader\>p | Find files in your project with the fd cli file finder |
| ------------------ | ----------- | ----------------------------------------------------------- |
| IvyFd | \<leader\>p | Find files in your project with a custom rust file finder |
| IvyAg | \<leader\>/ | Find content in files using the silver searcher |
| IvyBuffers | \<leader\>b | Search though open buffers |
| IvyLines | | Search the lines in the current buffer |
| IvyWorkspaceSymbol | | Search for workspace symbols using the lsp workspace/symbol |
### Actions
@ -70,6 +71,41 @@ Action can be run on selected candidates provide functionality
## API
### ivy.run
The `ivy.run` function is the core function in the plugin, it will launch the
completion window and display the items from your items function. When the
users accept one of the candidates with an [action](#actions), it will call the
callback function to in most cases open the item in the desired location.
```lua
---@param name string
---@param items fun(input: string): { content: string }[] | string
---@param callback fun(result: string, action: string)
vim.ivy.run = function(name, items, callback) end
```
#### Name `string`
The name is the display name for the command and will be the name of the buffer
in the completion window
#### Items `fun(input: string): { content: string }[] | string`
The items function is a function that will return the candidates to display in
the completion window. This can return a string where each line will be a
completion item. Or an array of tables where the `content` will be the
completion item.
#### Callback `fun(result: string, action: string)`
The function that will run when the user selects a completion item. Generally
this will open the item in the desired location. For example, in the file
finder with will open the file in a new buffer. If the user selects the
vertical split action it will open the buffer in a new `vsplit`
#### Example
```lua
vim.ivy.run(
-- The name given to the results window and displayed to the user
@ -77,7 +113,30 @@ Action can be run on selected candidates provide functionality
-- Call back function to get all the candidates that will be displayed in
-- the results window, The `input` will be passed in, so you can filter
-- your results with the value from the prompt
function(input) return { "One", "Two", Three } end,
function(input)
vim.ivy.run(
-- The name given to the results window and displayed to the user
"Title",
-- Call back function to get all the candidates that will be displayed in
-- the results window, The `input` will be passed in, so you can filter
-- your results with the value from the prompt
function(input)
return {
{ content = "One" },
{ content = "Two" },
{ content = "Three" },
}
end,
-- Action callback that will be called on the completion or peek actions.
-- The currently selected item is passed in as the result.
function(result) vim.cmd("edit " .. result) end
)
return {
{ content = "One" },
{ content = "Two" },
{ content = "Three" },
}
end,
-- Action callback that will be called on the completion or peek actions.
-- The currently selected item is passed in as the result.
function(result) vim.cmd("edit " .. result) end

12
lua/ivy/backends/ag.lua Normal file
View file

@ -0,0 +1,12 @@
local utils = require "ivy.utils"
local ag = {
name = "AG",
command = "IvyAg",
description = "Run ag to search for content in files",
keymap = "<leader>/",
items = utils.command_finder "ag",
callback = utils.vimgrep_action(),
}
return ag

View file

@ -0,0 +1,38 @@
local libivy = require "ivy.libivy"
local utils = require "ivy.utils"
local function items(input)
local list = {}
local buffers = vim.api.nvim_list_bufs()
for index = 1, #buffers do
local buffer = buffers[index]
-- Get the relative path from the current working directory. We need to
-- substring +2 to remove the `/` from the start of the path to give us a
-- true relative path
local buffer_name = vim.api.nvim_buf_get_name(buffer):sub(#vim.fn.getcwd() + 2, -1)
local file_type = vim.api.nvim_buf_get_option(buffer, "filetype")
if vim.api.nvim_buf_is_loaded(buffer) and file_type ~= "ivy" and #buffer_name > 0 then
local score = libivy.ivy_match(input, buffer_name)
if score > -200 or #input == 0 then
table.insert(list, { score = score, content = buffer_name })
end
end
end
table.sort(list, function(a, b)
return a.score < b.score
end)
return list
end
local buffers = {
name = "Buffers",
command = "IvyBuffers",
description = "List all of the current open buffers",
keymap = "<leader>b",
items = items,
callback = utils.file_action(),
}
return buffers

View file

@ -0,0 +1,17 @@
local libivy = require "ivy.libivy"
local utils = require "ivy.utils"
local function items(term)
return libivy.ivy_files(term, vim.fn.getcwd())
end
local files = {
name = "Files",
command = "IvyFd",
description = "Find files in the project",
keymap = "<leader>p",
items = items,
callback = utils.file_action(),
}
return files

View file

@ -0,0 +1,32 @@
local utils = require "ivy.utils"
local libivy = require "ivy.libivy"
local function items(input)
local list = {}
local lines = vim.api.nvim_buf_get_lines(vim.ivy.origin(), 0, -1, false)
for index = 1, #lines do
local line = lines[index]
local score = libivy.ivy_match(input, line)
if score > -200 then
local prefix = string.rep(" ", 4 - #tostring(index)) .. index .. ": "
table.insert(list, { score = score, content = prefix .. line })
end
end
table.sort(list, function(a, b)
return a.score < b.score
end)
return list
end
local lines = {
name = "Lines",
command = "IvyLines",
description = "Search though the lines in the current buffer",
items = items,
callback = utils.line_action(),
}
return lines

View file

@ -0,0 +1,40 @@
local window = require "ivy.window"
local utils = require "ivy.utils"
local previous_results = {}
local function set_items(items)
window.set_items(items)
previous_results = items
end
local function items(input)
local buffer_number = window.origin_buffer
local cwd = vim.fn.getcwd()
local results = {}
vim.lsp.buf_request(buffer_number, "workspace/symbol", { query = input }, function(err, server_result, _, _)
if err ~= nil then
set_items { content = "-- There was an error with workspace/symbol --" }
return
end
local locations = vim.lsp.util.symbols_to_items(server_result or {}, buffer_number) or {}
for index = 1, #locations do
local item = locations[index]
local relative_path = item.filename:sub(#cwd + 2, -1)
table.insert(results, { content = relative_path .. ":" .. item.lnum .. ": " .. item.text })
end
set_items(results)
end)
return previous_results
end
local lsp_workspace_symbols = {
name = "WorkspaceSymbols",
command = "IvyWorkspaceSymbols",
description = "Search for workspace symbols using the lsp workspace/symbol",
items = items,
callback = utils.vimgrep_action(),
}
return lsp_workspace_symbols

View file

@ -27,6 +27,7 @@ window.index = 0
window.origin = nil
window.window = nil
window.buffer = nil
window.origin_buffer = nil
window.initialize = function()
window.make_buffer()
@ -34,6 +35,7 @@ end
window.make_buffer = function()
window.origin = vim.api.nvim_get_current_win()
window.origin_buffer = vim.api.nvim_win_get_buf(0)
vim.api.nvim_command "botright split new"
window.buffer = vim.api.nvim_win_get_buf(0)
@ -138,6 +140,7 @@ window.destroy = function()
window.buffer = nil
window.window = nil
window.origin = nil
window.origin_buffer = nil
window.index = 0
end

View file

@ -1,73 +1,34 @@
local controller = require "ivy.controller"
local utils = require "ivy.utils"
local libivy = require "ivy.libivy"
-- Put the controller in to the vim global so we can access it in mappings
-- better without requires. You can call controller commands like `vim.ivy.xxx`.
-- luacheck: ignore
vim.ivy = controller
vim.api.nvim_create_user_command("IvyAg", function()
vim.ivy.run("AG", utils.command_finder "ag", utils.vimgrep_action())
end, { bang = true, desc = "Run ag to search for content in files" })
local register_backend = function(backend)
assert(backend.command, "The backend must have a command")
assert(backend.items, "The backend must have a items function")
assert(backend.callback, "The backend must have a callback function")
vim.api.nvim_create_user_command("IvyFd", function()
vim.ivy.run("Files", function(term)
return libivy.ivy_files(term, vim.fn.getcwd())
end, utils.file_action())
end, { bang = true, desc = "Find files in the project" })
vim.api.nvim_create_user_command("IvyBuffers", function()
vim.ivy.run("Buffers", function(input)
local list = {}
local buffers = vim.api.nvim_list_bufs()
for index = 1, #buffers do
local buffer = buffers[index]
-- Get the relative path from the current working directory. We need to
-- substring +2 to remove the `/` from the start of the path to give us a
-- true relative path
local buffer_name = vim.api.nvim_buf_get_name(buffer):sub(#vim.fn.getcwd() + 2, -1)
local file_type = vim.api.nvim_buf_get_option(buffer, "filetype")
if vim.api.nvim_buf_is_loaded(buffer) and file_type ~= "ivy" and #buffer_name > 0 then
local score = libivy.ivy_match(input, buffer_name)
if score > -200 or #input == 0 then
table.insert(list, { score = score, content = buffer_name })
end
end
local user_command_options = { bang = true }
if backend.description ~= nil then
user_command_options.desc = backend.description
end
table.sort(list, function(a, b)
return a.score < b.score
end)
local name = backend.name or backend.command
vim.api.nvim_create_user_command(backend.command, function()
vim.ivy.run(name, backend.items, backend.callback)
end, user_command_options)
return list
end, utils.file_action())
end, { bang = true, desc = "List all of the current open buffers" })
vim.api.nvim_create_user_command("IvyLines", function()
vim.ivy.run("Lines", function(input)
local list = {}
local lines = vim.api.nvim_buf_get_lines(vim.ivy.origin(), 0, -1, false)
for index = 1, #lines do
local line = lines[index]
local score = libivy.ivy_match(input, line)
if score > -200 then
local prefix = string.rep(" ", 4 - #tostring(index)) .. index .. ": "
table.insert(list, { score = score, content = prefix .. line })
end
if backend.keymap ~= nil then
vim.api.nvim_set_keymap("n", backend.keymap, "<cmd>" .. backend.command .. "<CR>", { nowait = true, silent = true })
end
end
table.sort(list, function(a, b)
return a.score < b.score
end)
return list
end, utils.line_action())
end, { bang = true, desc = "List all of the current open buffers" })
vim.api.nvim_set_keymap("n", "<leader>b", "<cmd>IvyBuffers<CR>", { nowait = true, silent = true })
vim.api.nvim_set_keymap("n", "<leader>p", "<cmd>IvyFd<CR>", { nowait = true, silent = true })
vim.api.nvim_set_keymap("n", "<leader>/", "<cmd>IvyAg<CR>", { nowait = true, silent = true })
register_backend(require "ivy.backends.ag")
register_backend(require "ivy.backends.buffers")
register_backend(require "ivy.backends.files")
register_backend(require "ivy.backends.lines")
register_backend(require "ivy.backends.lsp-workspace-symbols")
vim.cmd "highlight IvyMatch cterm=bold gui=bold"