Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F132289
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
12 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/.luacheckrc b/.luacheckrc
new file mode 100644
index 0000000..68cf318
--- /dev/null
+++ b/.luacheckrc
@@ -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",
+}
diff --git a/.stylua.toml b/.stylua.toml
new file mode 100644
index 0000000..ecb6dca
--- /dev/null
+++ b/.stylua.toml
@@ -0,0 +1,6 @@
+column_width = 120
+line_endings = "Unix"
+indent_type = "Spaces"
+indent_width = 2
+quote_style = "AutoPreferDouble"
+call_parentheses = "None"
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..14b5ab0
--- /dev/null
+++ b/README.md
@@ -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
diff --git a/lua/ivy/controller.lua b/lua/ivy/controller.lua
new file mode 100644
index 0000000..57b34fe
--- /dev/null
+++ b/lua/ivy/controller.lua
@@ -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
diff --git a/lua/ivy/prompt.lua b/lua/ivy/prompt.lua
new file mode 100644
index 0000000..264cc1b
--- /dev/null
+++ b/lua/ivy/prompt.lua
@@ -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
diff --git a/lua/ivy/utils.lua b/lua/ivy/utils.lua
new file mode 100644
index 0000000..c087167
--- /dev/null
+++ b/lua/ivy/utils.lua
@@ -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
diff --git a/lua/ivy/window.lua b/lua/ivy/window.lua
new file mode 100644
index 0000000..1c0c7c1
--- /dev/null
+++ b/lua/ivy/window.lua
@@ -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
diff --git a/plugin/ivy.lua b/plugin/ivy.lua
new file mode 100644
index 0000000..96bdf5f
--- /dev/null
+++ b/plugin/ivy.lua
@@ -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 })
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Fri, May 8, 9:58 PM (1 w, 5 h ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
8992
Default Alt Text
(12 KB)
Attached To
Mode
R1 ivy.nvim
Attached
Detach File
Event Timeline
Log In to Comment