chore: initial commit
This commit is contained in:
commit
b82f1af2a1
8 changed files with 401 additions and 0 deletions
12
.luacheckrc
Normal file
12
.luacheckrc
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
-- Rerun tests only if their modification time changed.
|
||||
cache = true
|
||||
|
||||
std = luajit
|
||||
codes = true
|
||||
|
||||
self = false
|
||||
|
||||
-- Global objects defined by the C code
|
||||
read_globals = {
|
||||
"vim",
|
||||
}
|
||||
6
.stylua.toml
Normal file
6
.stylua.toml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
column_width = 120
|
||||
line_endings = "Unix"
|
||||
indent_type = "Spaces"
|
||||
indent_width = 2
|
||||
quote_style = "AutoPreferDouble"
|
||||
call_parentheses = "None"
|
||||
61
README.md
Normal file
61
README.md
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
<div align="center">
|
||||
|
||||
# ivy.nvim
|
||||
|
||||
An [ivy-mode](https://github.com/abo-abo/swiper#ivy) port to neovim. Ivy is a
|
||||
generic completion mechanism for ~~Emacs~~ Nvim
|
||||
|
||||
</div>
|
||||
|
||||
## Installation
|
||||
|
||||
### Manually
|
||||
|
||||
```sh
|
||||
git clone https://github.com/AdeAttwood/ivy.nvim ~/.config/nvim/pack/bundle/start/ivy.nvim
|
||||
```
|
||||
|
||||
### Plugin managers
|
||||
|
||||
TODO: Add docs in the plugin managers I don't use any
|
||||
|
||||
## Features
|
||||
|
||||
### Commands
|
||||
|
||||
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 |
|
||||
| IvyAg | \<leader\>/ | Find content in files using the silver searcher |
|
||||
| IvyBuffers | | Search though open buffers |
|
||||
|
||||
### Actions
|
||||
|
||||
Action can be run on selected candidates provide functionality
|
||||
|
||||
| Action | Description |
|
||||
| -------- | ------------------------------------------------------------------------------ |
|
||||
| Complete | Run the completion function, usually this will be opening a file |
|
||||
| Peek | Run the completion function on a selection, but don't close the results window |
|
||||
|
||||
## API
|
||||
|
||||
```lua
|
||||
vim.ivy.run(
|
||||
-- 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,
|
||||
-- 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
|
||||
)
|
||||
```
|
||||
|
||||
## Other stuff you might like
|
||||
|
||||
- [ivy-mode](https://github.com/abo-abo/swiper#ivy) - An emacs package that was the inspiration for this nvim plugin
|
||||
- [Command-T](https://github.com/wincent/command-t) - Vim plugin I used before I started this one
|
||||
- [telescope.nvim](https://github.com/nvim-telescope/telescope.nvim) - Another competition plugin, lots of people are using
|
||||
56
lua/ivy/controller.lua
Normal file
56
lua/ivy/controller.lua
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
local window = require "ivy.window"
|
||||
local prompt = require "ivy.prompt"
|
||||
|
||||
local controller = {}
|
||||
|
||||
controller.items = nil
|
||||
controller.callback = nil
|
||||
|
||||
controller.run = function(items, callback)
|
||||
controller.callback = callback
|
||||
controller.items = items
|
||||
|
||||
window.initialize()
|
||||
controller.input ""
|
||||
end
|
||||
|
||||
controller.input = function(char)
|
||||
prompt.input(char)
|
||||
window.set_items(controller.items(prompt.text()))
|
||||
end
|
||||
|
||||
controller.search = function(value)
|
||||
prompt.set(value)
|
||||
window.set_items(controller.items(prompt.text()))
|
||||
end
|
||||
|
||||
controller.complete = function()
|
||||
controller.checkpoint()
|
||||
controller.destroy()
|
||||
end
|
||||
|
||||
controller.checkpoint = function()
|
||||
vim.api.nvim_set_current_win(window.previous)
|
||||
controller.callback(window.get_current_selection())
|
||||
vim.api.nvim_set_current_win(window.window)
|
||||
end
|
||||
|
||||
controller.next = function()
|
||||
window.index = window.index + 1
|
||||
window.update()
|
||||
end
|
||||
|
||||
controller.previous = function()
|
||||
window.index = window.index - 1
|
||||
window.update()
|
||||
end
|
||||
|
||||
controller.destroy = function()
|
||||
controller.items = nil
|
||||
controller.callback = nil
|
||||
|
||||
window.destroy()
|
||||
prompt.destroy()
|
||||
end
|
||||
|
||||
return controller
|
||||
37
lua/ivy/prompt.lua
Normal file
37
lua/ivy/prompt.lua
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
-- The prefix that will be before the search text for the user
|
||||
local prompt_prefix = ">> "
|
||||
|
||||
local prompt = {}
|
||||
|
||||
prompt.value = ""
|
||||
|
||||
prompt.text = function()
|
||||
return prompt.value
|
||||
end
|
||||
prompt.update = function()
|
||||
vim.notify(prompt_prefix .. prompt.text())
|
||||
end
|
||||
|
||||
prompt.input = function(char)
|
||||
if char == "BACKSPACE" then
|
||||
prompt.value = string.sub(prompt.value, 0, -2)
|
||||
elseif char == "\\\\" then
|
||||
prompt.value = prompt.value .. "\\"
|
||||
else
|
||||
prompt.value = prompt.value .. char
|
||||
end
|
||||
|
||||
prompt.update()
|
||||
end
|
||||
|
||||
prompt.set = function(value)
|
||||
prompt.value = value
|
||||
prompt.update()
|
||||
end
|
||||
|
||||
prompt.destroy = function()
|
||||
prompt.value = ""
|
||||
vim.notify ""
|
||||
end
|
||||
|
||||
return prompt
|
||||
57
lua/ivy/utils.lua
Normal file
57
lua/ivy/utils.lua
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
local utils = {}
|
||||
|
||||
utils.command_finder = function(command, min)
|
||||
if min == nil then
|
||||
min = 3
|
||||
end
|
||||
|
||||
return function(input)
|
||||
-- Dont run the commands unless we have somting to search that wont
|
||||
-- return a ton of results or on some commands the command files with
|
||||
-- no search term
|
||||
if #input < min then
|
||||
return "-- Please type more than " .. min .. " chars --"
|
||||
end
|
||||
|
||||
-- TODO(ade): Think if we want to start escaping the command here. I
|
||||
-- dont know if its causing issues while trying to use regex especially
|
||||
-- with word boundaries `input:gsub("'", "\\'"):gsub('"', '\\"')`
|
||||
local handle = io.popen(command .. " " .. input .. " 2>&1")
|
||||
if handle == nil then
|
||||
return {}
|
||||
end
|
||||
local result = handle:read "*a"
|
||||
handle:close()
|
||||
|
||||
return result
|
||||
end
|
||||
end
|
||||
|
||||
utils.vimgrep_action = function()
|
||||
return function(item)
|
||||
-- Match file and line form vimgrep style commands
|
||||
local file = item:match "([^:]+):"
|
||||
local line = item:match ":(%d+):"
|
||||
|
||||
-- Cant do anything if we cant find a file to go to
|
||||
if file == nil then
|
||||
return
|
||||
end
|
||||
|
||||
vim.cmd("edit " .. file)
|
||||
if line ~= nil then
|
||||
vim.cmd(line)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
utils.file_action = function()
|
||||
return function(file)
|
||||
if file == nil then
|
||||
return
|
||||
end
|
||||
vim.cmd("edit " .. file)
|
||||
end
|
||||
end
|
||||
|
||||
return utils
|
||||
139
lua/ivy/window.lua
Normal file
139
lua/ivy/window.lua
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
-- Constent options that will be used for the keymaps
|
||||
local opts = { noremap = true, silent = true, nowait = true }
|
||||
|
||||
-- All of the base chars that will be used for an "input" operation on the
|
||||
-- prompt
|
||||
-- stylua: ignore
|
||||
local chars = {
|
||||
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W",
|
||||
"X", "Y", "Z", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t",
|
||||
"u", "v", "w", "x", "y", "z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "<", ">", "`", "@", "#", "~", "!",
|
||||
"\"", "$", "%", "^", "&", "/", "(", ")", "=", "+", "*", "-", "_", ".", ",", ";", ":", "?", "\\", "|", "'", "{", "}",
|
||||
"[", "]", " ",
|
||||
}
|
||||
|
||||
local function parse_lines(lines)
|
||||
local items = {}
|
||||
for line in lines:gmatch "[^\r\n]+" do
|
||||
table.insert(items, line)
|
||||
end
|
||||
|
||||
return items
|
||||
end
|
||||
|
||||
local function parse_array(arr)
|
||||
return arr
|
||||
end
|
||||
|
||||
local window = {}
|
||||
|
||||
window.index = 0
|
||||
window.previous = nil
|
||||
window.window = nil
|
||||
window.buffer = nil
|
||||
|
||||
window.initialize = function()
|
||||
window.make_buffer()
|
||||
end
|
||||
|
||||
window.make_buffer = function()
|
||||
window.previous = vim.api.nvim_get_current_win()
|
||||
|
||||
vim.api.nvim_command "botright split new"
|
||||
window.buffer = vim.api.nvim_win_get_buf(0)
|
||||
window.window = vim.api.nvim_get_current_win()
|
||||
|
||||
vim.api.nvim_win_set_option(window.window, "number", false)
|
||||
vim.api.nvim_win_set_option(window.window, "relativenumber", false)
|
||||
vim.api.nvim_win_set_option(window.window, "signcolumn", "no")
|
||||
|
||||
vim.api.nvim_buf_set_option(window.buffer, "filetype", "ivy")
|
||||
vim.api.nvim_buf_set_var(window.buffer, "bufftype", "nofile")
|
||||
|
||||
for index = 1, #chars do
|
||||
local char = chars[index]
|
||||
if char == "'" then
|
||||
char = "\\'"
|
||||
end
|
||||
if char == "\\" then
|
||||
char = "\\\\\\\\"
|
||||
end
|
||||
vim.api.nvim_buf_set_keymap(window.buffer, "n", chars[index], "<cmd>lua vim.ivy.input('" .. char .. "')<CR>", opts)
|
||||
end
|
||||
|
||||
vim.api.nvim_buf_set_keymap(window.buffer, "n", "<C-c>", "<cmd>lua vim.ivy.destroy()<CR>", opts)
|
||||
vim.api.nvim_buf_set_keymap(window.buffer, "n", "<C-w>", "<cmd>lua vim.ivy.search('')<CR>", opts)
|
||||
vim.api.nvim_buf_set_keymap(window.buffer, "n", "<C-n>", "<cmd>lua vim.ivy.next()<CR>", opts)
|
||||
vim.api.nvim_buf_set_keymap(window.buffer, "n", "<C-p>", "<cmd>lua vim.ivy.previous()<CR>", opts)
|
||||
vim.api.nvim_buf_set_keymap(window.buffer, "n", "<C-M-n>", "<cmd>lua vim.ivy.next(); vim.ivy.checkpoint()<CR>", opts)
|
||||
vim.api.nvim_buf_set_keymap(
|
||||
window.buffer,
|
||||
"n",
|
||||
"<C-M-p>",
|
||||
"<cmd>lua vim.ivy.previous(); vim.ivy.checkpoint()<CR>",
|
||||
opts
|
||||
)
|
||||
vim.api.nvim_buf_set_keymap(window.buffer, "n", "<CR>", "<cmd>lua vim.ivy.complete()<CR>", opts)
|
||||
vim.api.nvim_buf_set_keymap(window.buffer, "n", "<BS>", "<cmd>lua vim.ivy.input('BACKSPACE')<CR>", opts)
|
||||
end
|
||||
|
||||
window.get_current_selection = function()
|
||||
local line = vim.api.nvim_buf_get_lines(window.buffer, window.index, window.index + 1, true)
|
||||
if line == nil then
|
||||
line = { "" }
|
||||
end
|
||||
|
||||
return line[1]
|
||||
end
|
||||
|
||||
window.get_buffer = function()
|
||||
if window.buffer == nil then
|
||||
window.make_buffer()
|
||||
end
|
||||
|
||||
return window.buffer
|
||||
end
|
||||
|
||||
window.update = function()
|
||||
-- TODO(ade): Add a guard in so we can not go out of range on the results buffer
|
||||
vim.api.nvim_win_set_cursor(window.window, { window.index + 1, 0 })
|
||||
end
|
||||
|
||||
window.set_items = function(items)
|
||||
local lines = {}
|
||||
|
||||
if type(items) == "string" then
|
||||
lines = parse_lines(items)
|
||||
elseif type(items) == "table" then
|
||||
lines = parse_array(items)
|
||||
end
|
||||
|
||||
if #lines == 0 then
|
||||
lines = { "-- No Items --" }
|
||||
end
|
||||
|
||||
vim.api.nvim_buf_set_lines(window.get_buffer(), 0, 9999, false, lines)
|
||||
|
||||
local line_count = #lines
|
||||
window.index = 0
|
||||
|
||||
if line_count > 10 then
|
||||
line_count = 10
|
||||
end
|
||||
vim.api.nvim_win_set_height(window.window, line_count)
|
||||
|
||||
window.update()
|
||||
end
|
||||
|
||||
window.destroy = function()
|
||||
if type(window.buffer) == "number" then
|
||||
vim.api.nvim_buf_delete(window.buffer, { force = true })
|
||||
end
|
||||
|
||||
window.buffer = nil
|
||||
window.window = nil
|
||||
window.previous = nil
|
||||
window.index = 0
|
||||
end
|
||||
|
||||
return window
|
||||
33
plugin/ivy.lua
Normal file
33
plugin/ivy.lua
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
local controller = require "ivy.controller"
|
||||
local utils = require "ivy.utils"
|
||||
|
||||
-- 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`.
|
||||
vim.ivy = controller
|
||||
|
||||
vim.api.nvim_create_user_command("IvyAg", function()
|
||||
vim.ivy.run(utils.command_finder "ag", utils.vimgrep_action())
|
||||
end, { bang = true, desc = "Run ag to search for content in files" })
|
||||
|
||||
vim.api.nvim_create_user_command("IvyFd", function()
|
||||
vim.ivy.run(utils.command_finder("fd --hidden --type f --exclude .git", 0), utils.file_action())
|
||||
end, { bang = true, desc = "Find files in the project" })
|
||||
|
||||
vim.api.nvim_create_user_command("IvyBuffers", function()
|
||||
vim.ivy.run(function(input)
|
||||
local list = {}
|
||||
local buffers = vim.api.nvim_list_bufs()
|
||||
for index = 1, #buffers do
|
||||
local buffer = buffers[index]
|
||||
local buffer_name = vim.api.nvim_buf_get_name(buffer)
|
||||
if vim.api.nvim_buf_is_loaded(buffer) and #buffer_name > 0 then
|
||||
table.insert(list, buffer_name)
|
||||
end
|
||||
end
|
||||
|
||||
return list
|
||||
end, utils.file_action())
|
||||
end, { bang = true, desc = "List all of the current open buffers" })
|
||||
|
||||
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 })
|
||||
Loading…
Reference in a new issue