Merge 9bb186d6b0 into sapling-pr-archive-AdeAttwood
This commit is contained in:
commit
7fd1fe226c
29 changed files with 677 additions and 384 deletions
26
.github/workflows/ci.yml
vendored
26
.github/workflows/ci.yml
vendored
|
|
@ -29,7 +29,7 @@ jobs:
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Run stylua
|
- name: Run stylua
|
||||||
uses: JohnnyMorganz/stylua-action@v3.0.0
|
uses: JohnnyMorganz/stylua-action@v4.0.0
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
version: latest
|
version: latest
|
||||||
|
|
@ -52,6 +52,9 @@ jobs:
|
||||||
|
|
||||||
test:
|
test:
|
||||||
name: Build and test
|
name: Build and test
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
nvim-version: ["v0.9.5", "stable", "nightly"]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
|
|
@ -61,10 +64,25 @@ jobs:
|
||||||
uses: dtolnay/rust-toolchain@stable
|
uses: dtolnay/rust-toolchain@stable
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: sudo apt update && sudo apt install -y luajit build-essential
|
run: sudo apt update && sudo apt install -y build-essential luarocks
|
||||||
|
|
||||||
|
- name: Install busted
|
||||||
|
run: sudo luarocks install busted
|
||||||
|
|
||||||
|
- name: Install neovim
|
||||||
|
run: |
|
||||||
|
test -d _neovim || {
|
||||||
|
mkdir -p _neovim
|
||||||
|
curl -sL "https://github.com/neovim/neovim/releases/download/${{ matrix.nvim-version }}/nvim-linux64.tar.gz" | tar xzf - --strip-components=1 -C "${PWD}/_neovim"
|
||||||
|
}
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: cargo build --release
|
run: cargo build --release
|
||||||
|
|
||||||
- name: Test
|
- name: Run tests
|
||||||
run: find lua -name "*_test.lua" | xargs luajit scripts/test.lua
|
run: |
|
||||||
|
export PATH="${PWD}/_neovim/bin:${PATH}"
|
||||||
|
export VIM="${PWD}/_neovim/share/nvim/runtime"
|
||||||
|
|
||||||
|
nvim --version
|
||||||
|
nvim -l ./scripts/busted.lua -o TAP ./lua/ivy/ 2> /dev/null
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,12 @@ self = false
|
||||||
read_globals = {
|
read_globals = {
|
||||||
"vim",
|
"vim",
|
||||||
|
|
||||||
"it",
|
|
||||||
"after",
|
"after",
|
||||||
"after_each",
|
"after_each",
|
||||||
|
"assert",
|
||||||
"before",
|
"before",
|
||||||
"before_each",
|
"before_each",
|
||||||
|
"describe",
|
||||||
|
"it",
|
||||||
|
"spy",
|
||||||
}
|
}
|
||||||
|
|
|
||||||
4
Cargo.lock
generated
4
Cargo.lock
generated
|
|
@ -429,9 +429,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rayon"
|
name = "rayon"
|
||||||
version = "1.8.1"
|
version = "1.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fa7237101a77a10773db45d62004a272517633fbcc3df19d96455ede1122e051"
|
checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"either",
|
"either",
|
||||||
"rayon-core",
|
"rayon-core",
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ path = "rust/lib.rs"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
ignore = "0.4.22"
|
ignore = "0.4.22"
|
||||||
fuzzy-matcher = "0.3.7"
|
fuzzy-matcher = "0.3.7"
|
||||||
rayon = "1.8.1"
|
rayon = "1.10.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
criterion = "0.5.1"
|
criterion = "0.5.1"
|
||||||
|
|
|
||||||
92
README.md
92
README.md
|
|
@ -20,7 +20,47 @@ git clone https://github.com/AdeAttwood/ivy.nvim ~/.config/nvim/pack/bundle/star
|
||||||
|
|
||||||
### Plugin managers
|
### Plugin managers
|
||||||
|
|
||||||
TODO: Add docs in the plugin managers I don't use any
|
Using [lazy.nvim](https://github.com/folke/lazy.nvim)
|
||||||
|
```lua
|
||||||
|
{
|
||||||
|
"AdeAttwood/ivy.nvim",
|
||||||
|
build = "cargo build --release",
|
||||||
|
},
|
||||||
|
```
|
||||||
|
|
||||||
|
TODO: Add more plugin managers
|
||||||
|
|
||||||
|
### Setup / Configuration
|
||||||
|
|
||||||
|
Ivy can be configured with minimal config that will give you all the defaults
|
||||||
|
provided by Ivy.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
require('ivy').setup()
|
||||||
|
```
|
||||||
|
|
||||||
|
With Ivy you can configure your own backends.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
require('ivy').setup {
|
||||||
|
backends = {
|
||||||
|
-- A backend module that will be registered
|
||||||
|
"ivy.backends.buffers",
|
||||||
|
-- Using a table so you can configure a custom keymap overriding the
|
||||||
|
-- default one.
|
||||||
|
{ "ivy.backends.files", { keymap = "<C-p>" } }
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The `setup` function can only be called once, if its called a second time any
|
||||||
|
backends or config will not be used. Ivy does expose the `register_backend`
|
||||||
|
function, this can be used to load backends before or after the setup function
|
||||||
|
is called.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
require('ivy').register_backend("ivy.backends.files")
|
||||||
|
```
|
||||||
|
|
||||||
### Compiling
|
### Compiling
|
||||||
|
|
||||||
|
|
@ -56,28 +96,46 @@ cp ./post-merge.sample ./.git/hooks/post-merge
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
### Commands
|
### Backends
|
||||||
|
|
||||||
A command can be run that will launch the completion UI
|
A backend is a module that will provide completion candidates for the UI to
|
||||||
|
show. It will also provide functionality when actions are taken. The Command
|
||||||
|
and Key Map are the default options provided by the backend, they can be
|
||||||
|
customized when you register it.
|
||||||
|
|
||||||
| Command | Key Map | Description |
|
| Module | Command | Key Map | Description |
|
||||||
| ------------------ | ----------- | ----------------------------------------------------------- |
|
| ------------------------------------ | ------------------ | ----------- | ----------------------------------------------------------- |
|
||||||
| IvyFd | \<leader\>p | Find files in your project with a custom rust file finder |
|
| `ivy.backends.files` | IvyFd | \<leader\>p | Find files in your project with a custom rust file finder |
|
||||||
| IvyAg | \<leader\>/ | Find content in files using the silver searcher |
|
| `ivy.backends.ag` | IvyAg | \<leader\>/ | Find content in files using the silver searcher |
|
||||||
| IvyBuffers | \<leader\>b | Search though open buffers |
|
| `ivy.backends.rg` | IvyRg | \<leader\>/ | Find content in files using ripgrep cli tool |
|
||||||
| IvyLines | | Search the lines in the current buffer |
|
| `ivy.backends.buffers` | IvyBuffers | \<leader\>b | Search though open buffers |
|
||||||
| IvyWorkspaceSymbol | | Search for workspace symbols using the lsp workspace/symbol |
|
| `ivy.backends.lines` | IvyLines | | Search the lines in the current buffer |
|
||||||
|
| `ivy.backends.lsp-workspace-symbols` | IvyWorkspaceSymbol | | Search for workspace symbols using the lsp workspace/symbol |
|
||||||
|
|
||||||
### Actions
|
### Actions
|
||||||
|
|
||||||
Action can be run on selected candidates provide functionality
|
Action can be run on selected candidates provide functionality
|
||||||
|
|
||||||
| Action | Description |
|
| Action | Key Map | Description |
|
||||||
| -------------- | ------------------------------------------------------------------------------ |
|
| -------------- | ----------- | ------------------------------------------------------------------------------ |
|
||||||
| Complete | Run the completion function, usually this will be opening a file |
|
| Complete | \<CR\> |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 |
|
| Vertical Split | \<C-v\> |Run the completion function in a new vertical split |
|
||||||
| Vertical Split | Run the completion function in a new vertical split |
|
| Split | \<C-s\> |Run the completion function in a new split |
|
||||||
| Split | Run the completion function in a new split |
|
| Destroy | \<C-c\> |Close the results window |
|
||||||
|
| Clear | \<C-u\> |Clear the results window |
|
||||||
|
| Delete word | \<C-w\> |Delete the word under the cursor |
|
||||||
|
| Next | \<C-n\> |Move to the next candidate |
|
||||||
|
| Previous | \<C-p\> |Move to the previous candidate |
|
||||||
|
| Next Checkpoint| \<C-M-n\> |Move to the next candidate and keep Ivy open and focussed |
|
||||||
|
| Previous Checkpoint| \<C-M-n\>|Move to the previous candidate and keep Ivy open and focussed |
|
||||||
|
|
||||||
|
Add your own keymaps for an action by adding a `ftplugin/ivy.lua` file in your config.
|
||||||
|
Just add a simple keymap like this:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
vim.api.nvim_set_keymap( "n", "<esc>", "<cmd>lua vim.ivy.destroy()<CR>", { noremap = true, silent = true, nowait = true })
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
## API
|
## API
|
||||||
|
|
||||||
|
|
@ -130,7 +188,7 @@ vertical split action it will open the buffer in a new `vsplit`
|
||||||
{ content = "Three" },
|
{ content = "Three" },
|
||||||
}
|
}
|
||||||
end,
|
end,
|
||||||
-- Action callback that will be called on the completion or peek actions.
|
-- Action callback that will be called on the completion or checkpoint actions.
|
||||||
-- The currently selected item is passed in as the result.
|
-- The currently selected item is passed in as the result.
|
||||||
function(result) vim.cmd("edit " .. result) end
|
function(result) vim.cmd("edit " .. result) end
|
||||||
)
|
)
|
||||||
|
|
|
||||||
33
lua/ivy/config.lua
Normal file
33
lua/ivy/config.lua
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
local config_mt = {}
|
||||||
|
config_mt.__index = config_mt
|
||||||
|
|
||||||
|
function config_mt:get_in(config, key_table)
|
||||||
|
local current_value = config
|
||||||
|
for _, key in ipairs(key_table) do
|
||||||
|
if current_value == nil then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
current_value = current_value[key]
|
||||||
|
end
|
||||||
|
|
||||||
|
return current_value
|
||||||
|
end
|
||||||
|
|
||||||
|
function config_mt:get(key_table)
|
||||||
|
return self:get_in(self.user_config, key_table) or self:get_in(self.default_config, key_table)
|
||||||
|
end
|
||||||
|
|
||||||
|
local config = { user_config = {} }
|
||||||
|
|
||||||
|
config.default_config = {
|
||||||
|
backends = {
|
||||||
|
"ivy.backends.buffers",
|
||||||
|
"ivy.backends.files",
|
||||||
|
"ivy.backends.lines",
|
||||||
|
"ivy.backends.rg",
|
||||||
|
"ivy.backends.lsp-workspace-symbols",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return setmetatable(config, config_mt)
|
||||||
27
lua/ivy/config_spec.lua
Normal file
27
lua/ivy/config_spec.lua
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
local config = require "ivy.config"
|
||||||
|
|
||||||
|
describe("config", function()
|
||||||
|
before_each(function()
|
||||||
|
config.user_config = {}
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("gets the first item when there is only default values", function()
|
||||||
|
local first_backend = config:get { "backends", 1 }
|
||||||
|
assert.is_equal("ivy.backends.buffers", first_backend)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("returns nil if we access a key that is not a valid config item", function()
|
||||||
|
assert.is_nil(config:get { "not", "a", "thing" })
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("returns the users overridden config value", function()
|
||||||
|
config.user_config = { backends = { "ivy.my.backend" } }
|
||||||
|
local first_backend = config:get { "backends", 1 }
|
||||||
|
assert.is_equal("ivy.my.backend", first_backend)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("returns a nested value", function()
|
||||||
|
config.user_config = { some = { nested = "value" } }
|
||||||
|
assert.is_equal(config:get { "some", "nested" }, "value")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
57
lua/ivy/controller_spec.lua
Normal file
57
lua/ivy/controller_spec.lua
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
local window = require "ivy.window"
|
||||||
|
local controller = require "ivy.controller"
|
||||||
|
|
||||||
|
describe("controller", function()
|
||||||
|
before_each(function()
|
||||||
|
vim.cmd "highlight IvyMatch cterm=bold gui=bold"
|
||||||
|
window.initialize()
|
||||||
|
end)
|
||||||
|
|
||||||
|
after_each(function()
|
||||||
|
controller.destroy()
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("will run the completion", function()
|
||||||
|
controller.run("Testing", function()
|
||||||
|
return { { content = "Some content" } }
|
||||||
|
end, function()
|
||||||
|
return {}
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Run all the scheduled tasks
|
||||||
|
vim.wait(0)
|
||||||
|
|
||||||
|
local lines = vim.api.nvim_buf_get_lines(0, 0, -1, true)
|
||||||
|
assert.is_equal(#lines, 1)
|
||||||
|
assert.is_equal(lines[1], "Some content")
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("will not try and highlight the buffer if there is nothing to highlight", function()
|
||||||
|
spy.on(vim, "cmd")
|
||||||
|
|
||||||
|
controller.items = function()
|
||||||
|
return { { content = "Hello" } }
|
||||||
|
end
|
||||||
|
|
||||||
|
controller.update ""
|
||||||
|
|
||||||
|
vim.wait(0)
|
||||||
|
|
||||||
|
assert.spy(vim.cmd).was_called_with "syntax clear IvyMatch"
|
||||||
|
assert.spy(vim.cmd).was_not_called_with "syntax match IvyMatch '[H]'"
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("will escape a - when passing it to be highlighted", function()
|
||||||
|
spy.on(vim, "cmd")
|
||||||
|
|
||||||
|
controller.items = function()
|
||||||
|
return { { content = "Hello" } }
|
||||||
|
end
|
||||||
|
|
||||||
|
controller.update "some-file"
|
||||||
|
|
||||||
|
vim.wait(0)
|
||||||
|
|
||||||
|
assert.spy(vim.cmd).was_called_with "syntax match IvyMatch '[some\\-file]'"
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
@ -1,51 +0,0 @@
|
||||||
local vim_mock = require "ivy.vim_mock"
|
|
||||||
local window = require "ivy.window"
|
|
||||||
local controller = require "ivy.controller"
|
|
||||||
|
|
||||||
-- The number of the mock buffer where all the test completions gets put
|
|
||||||
local buffer_number = 10
|
|
||||||
|
|
||||||
before_each(function()
|
|
||||||
vim_mock.reset()
|
|
||||||
window.initialize()
|
|
||||||
end)
|
|
||||||
|
|
||||||
after_each(function()
|
|
||||||
controller.destroy()
|
|
||||||
end)
|
|
||||||
|
|
||||||
it("will run", function(t)
|
|
||||||
controller.run("Testing", function()
|
|
||||||
return { { content = "Some content" } }
|
|
||||||
end, function()
|
|
||||||
return {}
|
|
||||||
end)
|
|
||||||
|
|
||||||
local lines = vim_mock.get_lines()
|
|
||||||
local completion_lines = lines[buffer_number]
|
|
||||||
|
|
||||||
t.assert_equal(#completion_lines, 1)
|
|
||||||
t.assert_equal(completion_lines[1], "Some content")
|
|
||||||
end)
|
|
||||||
|
|
||||||
it("will not try and highlight the buffer if there is nothing to highlight", function(t)
|
|
||||||
controller.items = function()
|
|
||||||
return { { content = "Hello" } }
|
|
||||||
end
|
|
||||||
|
|
||||||
controller.update ""
|
|
||||||
local commands = vim_mock.get_commands()
|
|
||||||
t.assert_equal(#commands, 1)
|
|
||||||
end)
|
|
||||||
|
|
||||||
it("will escape a - when passing it to be highlighted", function(t)
|
|
||||||
controller.items = function()
|
|
||||||
return { { content = "Hello" } }
|
|
||||||
end
|
|
||||||
|
|
||||||
controller.update "some-file"
|
|
||||||
local commands = vim_mock.get_commands()
|
|
||||||
local syntax_command = commands[2]
|
|
||||||
|
|
||||||
t.assert_equal("syntax match IvyMatch '[some\\-file]'", syntax_command)
|
|
||||||
end)
|
|
||||||
32
lua/ivy/init.lua
Normal file
32
lua/ivy/init.lua
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
local controller = require "ivy.controller"
|
||||||
|
local config = require "ivy.config"
|
||||||
|
local register_backend = require "ivy.register_backend"
|
||||||
|
|
||||||
|
local ivy = {}
|
||||||
|
ivy.run = controller.run
|
||||||
|
ivy.register_backend = register_backend
|
||||||
|
|
||||||
|
-- Private variable to check if ivy has been setup, this is to prevent multiple
|
||||||
|
-- setups of ivy. This is only exposed for testing purposes.
|
||||||
|
---@private
|
||||||
|
ivy.has_setup = false
|
||||||
|
|
||||||
|
---@class IvySetupOptions
|
||||||
|
---@field backends (IvyBackend | { ["1"]: string, ["2"]: IvyBackendOptions} | string)[]
|
||||||
|
|
||||||
|
---@param user_config IvySetupOptions
|
||||||
|
function ivy.setup(user_config)
|
||||||
|
if ivy.has_setup then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
config.user_config = user_config or {}
|
||||||
|
|
||||||
|
for _, backend in ipairs(config:get { "backends" } or {}) do
|
||||||
|
register_backend(backend)
|
||||||
|
end
|
||||||
|
|
||||||
|
ivy.has_setup = true
|
||||||
|
end
|
||||||
|
|
||||||
|
return ivy
|
||||||
30
lua/ivy/init_spec.lua
Normal file
30
lua/ivy/init_spec.lua
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
local ivy = require "ivy"
|
||||||
|
local config = require "ivy.config"
|
||||||
|
|
||||||
|
describe("ivy.setup", function()
|
||||||
|
before_each(function()
|
||||||
|
ivy.has_setup = false
|
||||||
|
config.user_config = {}
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("sets the users config options", function()
|
||||||
|
ivy.setup { backends = { "ivy.backends.files" } }
|
||||||
|
assert.is_equal("ivy.backends.files", config:get { "backends", 1 })
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("will not reconfigure if its called twice", function()
|
||||||
|
ivy.setup { backends = { "ivy.backends.files" } }
|
||||||
|
ivy.setup { backends = { "some.backend" } }
|
||||||
|
assert.is_equal("ivy.backends.files", config:get { "backends", 1 })
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("does not crash if you don't pass in any params to the setup function", function()
|
||||||
|
ivy.setup()
|
||||||
|
assert.is_equal("ivy.backends.buffers", config:get { "backends", 1 })
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("will fallback if the key is not set at all in the users config", function()
|
||||||
|
ivy.setup { some_key = "some_value" }
|
||||||
|
assert.is_equal("ivy.backends.buffers", config:get { "backends", 1 })
|
||||||
|
end)
|
||||||
|
end)
|
||||||
36
lua/ivy/libivy_spec.lua
Normal file
36
lua/ivy/libivy_spec.lua
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
require "busted.runner"()
|
||||||
|
|
||||||
|
local libivy = require "ivy.libivy"
|
||||||
|
|
||||||
|
describe("libivy", function()
|
||||||
|
it("should run a simple match", function()
|
||||||
|
local score = libivy.ivy_match("term", "I am a serch term")
|
||||||
|
|
||||||
|
assert.is_true(score > 0)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("should find a dot file", function()
|
||||||
|
local current_dir = libivy.ivy_cwd()
|
||||||
|
local results = libivy.ivy_files(".github/workflows/ci.yml", current_dir)
|
||||||
|
|
||||||
|
assert.is_equal(2, results.length, "Incorrect number of results found")
|
||||||
|
assert.is_equal(".github/workflows/ci.yml", results[2].content, "Invalid matches")
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("will allow you to access the length via the metatable", function()
|
||||||
|
local current_dir = libivy.ivy_cwd()
|
||||||
|
local results = libivy.ivy_files(".github/workflows/ci.yml", current_dir)
|
||||||
|
|
||||||
|
local mt = getmetatable(results)
|
||||||
|
|
||||||
|
assert.is_equal(results.length, mt.__len(results), "The `length` property does not match the __len metamethod")
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("will create an iterator", function()
|
||||||
|
local iter = libivy.ivy_files(".github/workflows/ci.yml", libivy.ivy_cwd())
|
||||||
|
local mt = getmetatable(iter)
|
||||||
|
|
||||||
|
assert.is_equal(type(mt["__index"]), "function")
|
||||||
|
assert.is_equal(type(mt["__len"]), "function")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
local libivy = require "ivy.libivy"
|
|
||||||
|
|
||||||
it("should run a simple match", function(t)
|
|
||||||
local score = libivy.ivy_match("term", "I am a serch term")
|
|
||||||
|
|
||||||
if score <= 0 then
|
|
||||||
t.error("Score should not be less than 0 found " .. score)
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
it("should find a dot file", function(t)
|
|
||||||
local current_dir = libivy.ivy_cwd()
|
|
||||||
local results = libivy.ivy_files(".github/workflows/ci.yml", current_dir)
|
|
||||||
|
|
||||||
if results.length ~= 2 then
|
|
||||||
t.error("Incorrect number of results found " .. results.length)
|
|
||||||
end
|
|
||||||
|
|
||||||
if results[2].content ~= ".github/workflows/ci.yml" then
|
|
||||||
t.error("Invalid matches: " .. results[2].content)
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
it("will allow you to access the length via the metatable", function(t)
|
|
||||||
local current_dir = libivy.ivy_cwd()
|
|
||||||
local results = libivy.ivy_files(".github/workflows/ci.yml", current_dir)
|
|
||||||
|
|
||||||
local mt = getmetatable(results)
|
|
||||||
|
|
||||||
if results.length ~= mt.__len(results) then
|
|
||||||
t.error "The `length` property does not match the __len metamethod"
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
it("will create an iterator", function(t)
|
|
||||||
local iter = libivy.ivy_files(".github/workflows/ci.yml", libivy.ivy_cwd())
|
|
||||||
local mt = getmetatable(iter)
|
|
||||||
|
|
||||||
if type(mt["__index"]) ~= "function" then
|
|
||||||
t.error "The iterator does not have an __index metamethod"
|
|
||||||
end
|
|
||||||
|
|
||||||
if type(mt["__len"]) ~= "function" then
|
|
||||||
t.error "The iterator does not have an __len metamethod"
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
29
lua/ivy/matcher_spec.lua
Normal file
29
lua/ivy/matcher_spec.lua
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
local libivy = require "ivy.libivy"
|
||||||
|
|
||||||
|
-- Helper function to test a that string `one` has a higher match score than
|
||||||
|
-- string `two`. If string `one` has a lower score than string `two` a string
|
||||||
|
-- will be returned that can be used in body of an error. If not then `nil` is
|
||||||
|
-- returned and all is good.
|
||||||
|
local match_test = function(term, one, two)
|
||||||
|
local score_one = libivy.ivy_match(term, one)
|
||||||
|
local score_two = libivy.ivy_match(term, two)
|
||||||
|
|
||||||
|
assert.is_true(
|
||||||
|
score_one > score_two,
|
||||||
|
("The score of %s (%d) ranked higher than %s (%d)"):format(one, score_one, two, score_two)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe("ivy matcher", function()
|
||||||
|
it("should match path separator", function()
|
||||||
|
match_test("file", "some/file.lua", "somefile.lua")
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- it("should match pattern with spaces", function()
|
||||||
|
-- match_test("so fi", "some/file.lua", "somefile.lua")
|
||||||
|
-- end)
|
||||||
|
|
||||||
|
it("should match the start of a string", function()
|
||||||
|
match_test("file", "file.lua", "somefile.lua")
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
local libivy = require "ivy.libivy"
|
|
||||||
|
|
||||||
-- Helper function to test a that string `one` has a higher match score than
|
|
||||||
-- string `two`. If string `one` has a lower score than string `two` a string
|
|
||||||
-- will be returned that can be used in body of an error. If not then `nil` is
|
|
||||||
-- returned and all is good.
|
|
||||||
local match_test = function(term, one, two)
|
|
||||||
local score_one = libivy.ivy_match(term, one)
|
|
||||||
local score_two = libivy.ivy_match(term, two)
|
|
||||||
|
|
||||||
if score_one < score_two then
|
|
||||||
return one .. " should be ranked higher than " .. two
|
|
||||||
end
|
|
||||||
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
it("sould match path separator", function(t)
|
|
||||||
local result = match_test("file", "some/file.lua", "somefile.lua")
|
|
||||||
if result then
|
|
||||||
t.error(result)
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
it("sould match pattern with spaces", function(t)
|
|
||||||
local result = match_test("so fi", "some/file.lua", "somefile.lua")
|
|
||||||
if result then
|
|
||||||
t.error(result)
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
it("sould match the start of a string", function(t)
|
|
||||||
local result = match_test("file", "file.lua", "somefile.lua")
|
|
||||||
if result then
|
|
||||||
t.error(result)
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
91
lua/ivy/prompt_spec.lua
Normal file
91
lua/ivy/prompt_spec.lua
Normal file
|
|
@ -0,0 +1,91 @@
|
||||||
|
local prompt = require "ivy.prompt"
|
||||||
|
|
||||||
|
-- Input a list of strings into the prompt
|
||||||
|
local input = function(input_table)
|
||||||
|
for index = 1, #input_table do
|
||||||
|
prompt.input(input_table[index])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe("prompt", function()
|
||||||
|
before_each(function()
|
||||||
|
prompt.destroy()
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("starts with empty text", function()
|
||||||
|
assert.is_same(prompt.text(), "")
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("can input some text", function()
|
||||||
|
input { "A", "d", "e" }
|
||||||
|
assert.is_same(prompt.text(), "Ade")
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("can delete a char", function()
|
||||||
|
input { "A", "d", "e", "BACKSPACE" }
|
||||||
|
assert.is_same(prompt.text(), "Ad")
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("will reset the text", function()
|
||||||
|
input { "A", "d", "e" }
|
||||||
|
prompt.set "New"
|
||||||
|
assert.is_same(prompt.text(), "New")
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("can move around the a word", function()
|
||||||
|
input { "P", "r", "o", "p", "t", "LEFT", "LEFT", "LEFT", "RIGHT", "m" }
|
||||||
|
assert.is_same(prompt.text(), "Prompt")
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("can delete a word", function()
|
||||||
|
prompt.set "Ade Attwood"
|
||||||
|
input { "DELETE_WORD" }
|
||||||
|
|
||||||
|
assert.is_same(prompt.text(), "Ade ")
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("can delete a word in the middle and leave the cursor at that word", function()
|
||||||
|
prompt.set "Ade middle A"
|
||||||
|
input { "LEFT", "LEFT", "DELETE_WORD", "a" }
|
||||||
|
|
||||||
|
assert.is_same(prompt.text(), "Ade a A")
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("will delete the space and the word if the last word is single space", function()
|
||||||
|
prompt.set "some.thing "
|
||||||
|
input { "DELETE_WORD" }
|
||||||
|
|
||||||
|
assert.is_same(prompt.text(), "some.")
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("will only delete one word from path", function()
|
||||||
|
prompt.set "some/nested/path"
|
||||||
|
input { "DELETE_WORD" }
|
||||||
|
|
||||||
|
assert.is_same(prompt.text(), "some/nested/")
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("will delete tailing space", function()
|
||||||
|
prompt.set "word "
|
||||||
|
input { "DELETE_WORD" }
|
||||||
|
|
||||||
|
assert.is_same(prompt.text(), "")
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("will leave a random space", function()
|
||||||
|
prompt.set "some word "
|
||||||
|
input { "DELETE_WORD" }
|
||||||
|
|
||||||
|
assert.is_same(prompt.text(), "some ")
|
||||||
|
end)
|
||||||
|
|
||||||
|
local special_characters = { ".", "/", "^" }
|
||||||
|
for _, char in ipairs(special_characters) do
|
||||||
|
it(string.format("will stop at a %s", char), function()
|
||||||
|
prompt.set(string.format("key%sValue", char))
|
||||||
|
input { "DELETE_WORD" }
|
||||||
|
|
||||||
|
assert.is_same(prompt.text(), string.format("key%s", char))
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
@ -1,94 +0,0 @@
|
||||||
local prompt = require "ivy.prompt"
|
|
||||||
local vim_mock = require "ivy.vim_mock"
|
|
||||||
|
|
||||||
before_each(function()
|
|
||||||
vim_mock.reset()
|
|
||||||
prompt.destroy()
|
|
||||||
end)
|
|
||||||
|
|
||||||
-- Input a list of strings into the prompt
|
|
||||||
local input = function(input_table)
|
|
||||||
for index = 1, #input_table do
|
|
||||||
prompt.input(input_table[index])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Asserts the prompt contains the correct value
|
|
||||||
local assert_prompt = function(t, expected)
|
|
||||||
local text = prompt.text()
|
|
||||||
if text ~= expected then
|
|
||||||
t.error("The prompt text should be '" .. expected .. "' found '" .. text .. "'")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
it("starts with empty text", function(t)
|
|
||||||
if prompt.text() ~= "" then
|
|
||||||
t.error "The prompt should start with empty text"
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
it("can input some text", function(t)
|
|
||||||
input { "A", "d", "e" }
|
|
||||||
assert_prompt(t, "Ade")
|
|
||||||
end)
|
|
||||||
|
|
||||||
it("can delete a char", function(t)
|
|
||||||
input { "A", "d", "e", "BACKSPACE" }
|
|
||||||
assert_prompt(t, "Ad")
|
|
||||||
end)
|
|
||||||
|
|
||||||
it("will reset the text", function(t)
|
|
||||||
input { "A", "d", "e" }
|
|
||||||
prompt.set "New"
|
|
||||||
assert_prompt(t, "New")
|
|
||||||
end)
|
|
||||||
|
|
||||||
it("can move around the a word", function(t)
|
|
||||||
input { "P", "r", "o", "p", "t", "LEFT", "LEFT", "LEFT", "RIGHT", "m" }
|
|
||||||
assert_prompt(t, "Prompt")
|
|
||||||
end)
|
|
||||||
|
|
||||||
it("can delete a word", function(t)
|
|
||||||
prompt.set "Ade Attwood"
|
|
||||||
input { "DELETE_WORD" }
|
|
||||||
assert_prompt(t, "Ade ")
|
|
||||||
end)
|
|
||||||
|
|
||||||
it("can delete a word in the middle", function(t)
|
|
||||||
prompt.set "Ade middle A"
|
|
||||||
input { "LEFT", "LEFT", "DELETE_WORD" }
|
|
||||||
assert_prompt(t, "Ade A")
|
|
||||||
end)
|
|
||||||
|
|
||||||
it("will delete the space and the word if the last word is single space", function(t)
|
|
||||||
prompt.set "some.thing "
|
|
||||||
input { "DELETE_WORD" }
|
|
||||||
assert_prompt(t, "some.")
|
|
||||||
end)
|
|
||||||
|
|
||||||
it("will only delete one word from path", function(t)
|
|
||||||
prompt.set "some/nested/path"
|
|
||||||
input { "DELETE_WORD" }
|
|
||||||
assert_prompt(t, "some/nested/")
|
|
||||||
end)
|
|
||||||
|
|
||||||
it("will delete tailing space", function(t)
|
|
||||||
prompt.set "word "
|
|
||||||
input { "DELETE_WORD" }
|
|
||||||
assert_prompt(t, "")
|
|
||||||
end)
|
|
||||||
|
|
||||||
it("will leave a random space", function(t)
|
|
||||||
prompt.set "some word "
|
|
||||||
input { "DELETE_WORD" }
|
|
||||||
assert_prompt(t, "some ")
|
|
||||||
end)
|
|
||||||
|
|
||||||
local special_characters = { ".", "/", "^" }
|
|
||||||
for _, char in ipairs(special_characters) do
|
|
||||||
it(string.format("will stop at a %s", char), function(t)
|
|
||||||
prompt.set(string.format("key%sValue", char))
|
|
||||||
input { "DELETE_WORD" }
|
|
||||||
assert_prompt(t, string.format("key%s", char))
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
54
lua/ivy/register_backend.lua
Normal file
54
lua/ivy/register_backend.lua
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
---@class IvyBackend
|
||||||
|
---@field command string The command this backend will have
|
||||||
|
---@field items fun(input: string): { content: string }[] | string The callback function to get the items to select from
|
||||||
|
---@field callback fun(result: string, action: string) The callback function to run when a item is selected
|
||||||
|
---@field description string? The description of the backend, this will be used in the keymaps
|
||||||
|
---@field name string? The name of the backend, this will fallback to the command if its not set
|
||||||
|
---@field keymap string? The keymap to trigger this backend
|
||||||
|
|
||||||
|
---@class IvyBackendOptions
|
||||||
|
---@field command string The command this backend will have
|
||||||
|
---@field keymap string? The keymap to trigger this backend
|
||||||
|
|
||||||
|
---Register a new backend
|
||||||
|
---
|
||||||
|
---This will create all the commands and set all the keymaps for the backend
|
||||||
|
---@param backend IvyBackend
|
||||||
|
local register_backend_class = function(backend)
|
||||||
|
local user_command_options = { bang = true }
|
||||||
|
if backend.description ~= nil then
|
||||||
|
user_command_options.desc = backend.description
|
||||||
|
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)
|
||||||
|
|
||||||
|
if backend.keymap ~= nil then
|
||||||
|
vim.api.nvim_set_keymap("n", backend.keymap, "<cmd>" .. backend.command .. "<CR>", { nowait = true, silent = true })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
---@param backend IvyBackend | { ["1"]: string, ["2"]: IvyBackendOptions} | string The backend or backend module
|
||||||
|
---@param options IvyBackendOptions? The options for the backend, that will be merged with the backend
|
||||||
|
local register_backend = function(backend, options)
|
||||||
|
if type(backend[1]) == "string" then
|
||||||
|
options = backend[2]
|
||||||
|
backend = require(backend[1])
|
||||||
|
end
|
||||||
|
|
||||||
|
if type(backend) == "string" then
|
||||||
|
backend = require(backend)
|
||||||
|
end
|
||||||
|
|
||||||
|
if options then
|
||||||
|
for key, value in pairs(options) do
|
||||||
|
backend[key] = value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
register_backend_class(backend)
|
||||||
|
end
|
||||||
|
|
||||||
|
return register_backend
|
||||||
71
lua/ivy/register_backend_spec.lua
Normal file
71
lua/ivy/register_backend_spec.lua
Normal file
|
|
@ -0,0 +1,71 @@
|
||||||
|
local register_backend = require "ivy.register_backend"
|
||||||
|
|
||||||
|
local function get_command(name)
|
||||||
|
local command_iter = vim.api.nvim_get_commands {}
|
||||||
|
|
||||||
|
for _, cmd in pairs(command_iter) do
|
||||||
|
if cmd.name == name then
|
||||||
|
return cmd
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local function get_keymap(mode, rhs)
|
||||||
|
local keymap_iter = vim.api.nvim_get_keymap(mode)
|
||||||
|
for _, keymap in pairs(keymap_iter) do
|
||||||
|
if keymap.rhs == rhs then
|
||||||
|
return keymap
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
describe("register_backend", function()
|
||||||
|
after_each(function()
|
||||||
|
vim.api.nvim_del_user_command "IvyFd"
|
||||||
|
|
||||||
|
local keymap = get_keymap("n", "<Cmd>IvyFd<CR>")
|
||||||
|
if keymap then
|
||||||
|
vim.api.nvim_del_keymap("n", keymap.lhs)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("registers a backend from a string with the default options", function()
|
||||||
|
register_backend "ivy.backends.files"
|
||||||
|
|
||||||
|
local command = get_command "IvyFd"
|
||||||
|
assert.is_not_nil(command)
|
||||||
|
|
||||||
|
local keymap = get_keymap("n", "<Cmd>IvyFd<CR>")
|
||||||
|
assert.is_not_nil(keymap)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("allows you to override the keymap", function()
|
||||||
|
register_backend("ivy.backends.files", { keymap = "<C-p>" })
|
||||||
|
|
||||||
|
local keymap = get_keymap("n", "<Cmd>IvyFd<CR>")
|
||||||
|
assert(keymap ~= nil)
|
||||||
|
assert.are.equal("<C-P>", keymap.lhs)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("allows you to pass in a hole backend module", function()
|
||||||
|
register_backend(require "ivy.backends.files")
|
||||||
|
|
||||||
|
local command = get_command "IvyFd"
|
||||||
|
assert.is_not_nil(command)
|
||||||
|
|
||||||
|
local keymap = get_keymap("n", "<Cmd>IvyFd<CR>")
|
||||||
|
assert.is_not_nil(keymap)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("allows you to pass in a hole backend module", function()
|
||||||
|
register_backend { "ivy.backends.files", { keymap = "<C-p>" } }
|
||||||
|
|
||||||
|
local keymap = get_keymap("n", "<Cmd>IvyFd<CR>")
|
||||||
|
assert(keymap ~= nil)
|
||||||
|
assert.are.equal("<C-P>", keymap.lhs)
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
@ -99,9 +99,11 @@ end
|
||||||
utils.line_action = function()
|
utils.line_action = function()
|
||||||
return function(item)
|
return function(item)
|
||||||
local line = item:match "^%s+(%d+):"
|
local line = item:match "^%s+(%d+):"
|
||||||
|
if line ~= nil then
|
||||||
vim.cmd(line)
|
vim.cmd(line)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
utils.escape_file_name = function(input)
|
utils.escape_file_name = function(input)
|
||||||
local file, _ = string.gsub(input, "([$%]\\[])", "\\%1")
|
local file, _ = string.gsub(input, "([$%]\\[])", "\\%1")
|
||||||
|
|
|
||||||
11
lua/ivy/utils_escape_spec.lua
Normal file
11
lua/ivy/utils_escape_spec.lua
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
local utils = require "ivy.utils"
|
||||||
|
|
||||||
|
it("will escape a dollar in the file name", function()
|
||||||
|
local result = utils.escape_file_name "/path/to/$file/$name.lua"
|
||||||
|
assert.is_same(result, "/path/to/\\$file/\\$name.lua")
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("will escape a brackets in the file name", function()
|
||||||
|
local result = utils.escape_file_name "/path/to/[file]/[name].lua"
|
||||||
|
assert.is_same(result, "/path/to/\\[file\\]/\\[name\\].lua")
|
||||||
|
end)
|
||||||
28
lua/ivy/utils_line_action_spec.lua
Normal file
28
lua/ivy/utils_line_action_spec.lua
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
local utils = require "ivy.utils"
|
||||||
|
local line_action = utils.line_action()
|
||||||
|
|
||||||
|
describe("utils line_action", function()
|
||||||
|
before_each(function()
|
||||||
|
spy.on(vim, "cmd")
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("will run the line command", function()
|
||||||
|
line_action " 4: Some text"
|
||||||
|
|
||||||
|
assert.is_equal(#vim.cmd.calls, 1, "The `vim.cmd` function should be called once")
|
||||||
|
assert.spy(vim.cmd).was_called_with "4"
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("will run with more numbers", function()
|
||||||
|
line_action " 44: Some text"
|
||||||
|
|
||||||
|
assert.is_equal(#vim.cmd.calls, 1, "The `vim.cmd` function should be called once")
|
||||||
|
assert.spy(vim.cmd).was_called_with "44"
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("dose not run any action if no line is found", function()
|
||||||
|
line_action "Some text"
|
||||||
|
|
||||||
|
assert.spy(vim.cmd).was_not_called()
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
@ -1,39 +0,0 @@
|
||||||
local utils = require "ivy.utils"
|
|
||||||
local line_action = utils.line_action()
|
|
||||||
local vim_mock = require "ivy.vim_mock"
|
|
||||||
|
|
||||||
before_each(function()
|
|
||||||
vim_mock.reset()
|
|
||||||
end)
|
|
||||||
|
|
||||||
it("will run the line command", function(t)
|
|
||||||
line_action " 4: Some text"
|
|
||||||
|
|
||||||
if #vim_mock.commands ~= 1 then
|
|
||||||
t.error "`line_action` command length should be 1"
|
|
||||||
end
|
|
||||||
|
|
||||||
if vim_mock.commands[1] ~= "4" then
|
|
||||||
t.error "`line_action` command should be 4"
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
it("will run with more numbers", function(t)
|
|
||||||
line_action " 44: Some text"
|
|
||||||
|
|
||||||
if #vim_mock.commands ~= 1 then
|
|
||||||
t.error "`line_action` command length should be 1"
|
|
||||||
end
|
|
||||||
|
|
||||||
if vim_mock.commands[1] ~= "44" then
|
|
||||||
t.error "`line_action` command should be 44"
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
it("dose not run any action if no line is found", function(t)
|
|
||||||
line_action "Some text"
|
|
||||||
|
|
||||||
if #vim_mock.commands ~= 0 then
|
|
||||||
t.error "`line_action` command length should be 1"
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
@ -1,10 +1,5 @@
|
||||||
local utils = require "ivy.utils"
|
local utils = require "ivy.utils"
|
||||||
local vimgrep_action = utils.vimgrep_action()
|
local vimgrep_action = utils.vimgrep_action()
|
||||||
local vim_mock = require "ivy.vim_mock"
|
|
||||||
|
|
||||||
before_each(function()
|
|
||||||
vim_mock.reset()
|
|
||||||
end)
|
|
||||||
|
|
||||||
local test_data = {
|
local test_data = {
|
||||||
{
|
{
|
||||||
|
|
@ -20,37 +15,42 @@ local test_data = {
|
||||||
it = "will skip the line if its not matched",
|
it = "will skip the line if its not matched",
|
||||||
completion = "some/file.lua: This is some text",
|
completion = "some/file.lua: This is some text",
|
||||||
action = utils.actions.EDIT,
|
action = utils.actions.EDIT,
|
||||||
commands = { "edit some/file.lua" },
|
commands = { "buffer some/file.lua" },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
it = "will run the vsplit command",
|
it = "will run the vsplit command",
|
||||||
completion = "some/file.lua: This is some text",
|
completion = "some/file.lua: This is some text",
|
||||||
action = utils.actions.VSPLIT,
|
action = utils.actions.VSPLIT,
|
||||||
commands = { "vsplit some/file.lua" },
|
commands = { "vsplit | buffer some/file.lua" },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
it = "will run the split command",
|
it = "will run the split command",
|
||||||
completion = "some/file.lua: This is some text",
|
completion = "some/file.lua: This is some text",
|
||||||
action = utils.actions.SPLIT,
|
action = utils.actions.SPLIT,
|
||||||
commands = { "split some/file.lua" },
|
commands = { "split | buffer some/file.lua" },
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
describe("utils vimgrep_action", function()
|
||||||
|
before_each(function()
|
||||||
|
spy.on(vim, "cmd")
|
||||||
|
end)
|
||||||
|
|
||||||
|
after_each(function()
|
||||||
|
vim.cmd:revert()
|
||||||
|
end)
|
||||||
|
|
||||||
for i = 1, #test_data do
|
for i = 1, #test_data do
|
||||||
local data = test_data[i]
|
local data = test_data[i]
|
||||||
it(data.it, function(t)
|
it(data.it, function()
|
||||||
vimgrep_action(data.completion, data.action)
|
assert.is_true(#data.commands > 0, "You must assert that at least one command is run")
|
||||||
|
|
||||||
if #vim_mock.commands ~= #data.commands then
|
vimgrep_action(data.completion, data.action)
|
||||||
t.error("Incorrect number of commands run expected " .. #data.commands .. " but found " .. #vim_mock.commands)
|
assert.is_equal(#vim.cmd.calls, #data.commands, "The `vim.cmd` function should be called once")
|
||||||
end
|
|
||||||
|
|
||||||
for j = 1, #data.commands do
|
for j = 1, #data.commands do
|
||||||
if vim_mock.commands[j] ~= data.commands[j] then
|
assert.spy(vim.cmd).was_called_with(data.commands[j])
|
||||||
t.error(
|
|
||||||
"Incorrect command run expected '" .. data.commands[j] .. "' but found '" .. vim_mock.commands[j] .. "'"
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
end)
|
||||||
32
lua/ivy/window_spec.lua
Normal file
32
lua/ivy/window_spec.lua
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
local window = require "ivy.window"
|
||||||
|
local controller = require "ivy.controller"
|
||||||
|
|
||||||
|
describe("window", function()
|
||||||
|
before_each(function()
|
||||||
|
vim.cmd "highlight IvyMatch cterm=bold gui=bold"
|
||||||
|
window.initialize()
|
||||||
|
end)
|
||||||
|
|
||||||
|
after_each(function()
|
||||||
|
controller.destroy()
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("can initialize and destroy the window", function()
|
||||||
|
assert.is_equal(vim.api.nvim_get_current_buf(), window.buffer)
|
||||||
|
|
||||||
|
window.destroy()
|
||||||
|
assert.is_equal(nil, window.buffer)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("can set items", function()
|
||||||
|
window.set_items { { content = "Line one" } }
|
||||||
|
assert.is_equal("Line one", window.get_current_selection())
|
||||||
|
end)
|
||||||
|
|
||||||
|
it("will set the items when a string is passed in", function()
|
||||||
|
local items = table.concat({ "One", "Two", "Three" }, "\n")
|
||||||
|
window.set_items(items)
|
||||||
|
|
||||||
|
assert.is_equal(items, table.concat(vim.api.nvim_buf_get_lines(window.buffer, 0, -1, true), "\n"))
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
local vim_mock = require "ivy.vim_mock"
|
|
||||||
local window = require "ivy.window"
|
|
||||||
|
|
||||||
before_each(function()
|
|
||||||
vim_mock.reset()
|
|
||||||
end)
|
|
||||||
|
|
||||||
it("can initialize and destroy the window", function(t)
|
|
||||||
window.initialize()
|
|
||||||
|
|
||||||
t.assert_equal(10, window.get_buffer())
|
|
||||||
t.assert_equal(10, window.buffer)
|
|
||||||
|
|
||||||
window.destroy()
|
|
||||||
t.assert_equal(nil, window.buffer)
|
|
||||||
end)
|
|
||||||
|
|
||||||
it("can set items", function(t)
|
|
||||||
window.initialize()
|
|
||||||
|
|
||||||
window.set_items { { content = "Line one" } }
|
|
||||||
t.assert_equal("Line one", window.get_current_selection())
|
|
||||||
end)
|
|
||||||
|
|
||||||
it("will set the items when a string is passed in", function(t)
|
|
||||||
window.initialize()
|
|
||||||
|
|
||||||
local items = table.concat({ "One", "Two", "Three" }, "\n")
|
|
||||||
window.set_items(items)
|
|
||||||
|
|
||||||
local lines = table.concat(vim_mock.get_lines()[window.buffer], "\n")
|
|
||||||
t.assert_equal(items, lines)
|
|
||||||
end)
|
|
||||||
|
|
@ -5,26 +5,6 @@ local controller = require "ivy.controller"
|
||||||
-- luacheck: ignore
|
-- luacheck: ignore
|
||||||
vim.ivy = controller
|
vim.ivy = controller
|
||||||
|
|
||||||
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")
|
|
||||||
|
|
||||||
local user_command_options = { bang = true }
|
|
||||||
if backend.description ~= nil then
|
|
||||||
user_command_options.desc = backend.description
|
|
||||||
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)
|
|
||||||
|
|
||||||
if backend.keymap ~= nil then
|
|
||||||
vim.api.nvim_set_keymap("n", backend.keymap, "<cmd>" .. backend.command .. "<CR>", { nowait = true, silent = true })
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
vim.paste = (function(overridden)
|
vim.paste = (function(overridden)
|
||||||
return function(lines, phase)
|
return function(lines, phase)
|
||||||
local file_type = vim.api.nvim_buf_get_option(0, "filetype")
|
local file_type = vim.api.nvim_buf_get_option(0, "filetype")
|
||||||
|
|
@ -36,15 +16,4 @@ vim.paste = (function(overridden)
|
||||||
end
|
end
|
||||||
end)(vim.paste)
|
end)(vim.paste)
|
||||||
|
|
||||||
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")
|
|
||||||
|
|
||||||
if vim.fn.executable "rg" then
|
|
||||||
register_backend(require "ivy.backends.rg")
|
|
||||||
elseif vim.fn.executable "ag" then
|
|
||||||
register_backend(require "ivy.backends.ag")
|
|
||||||
end
|
|
||||||
|
|
||||||
vim.cmd "highlight IvyMatch cterm=bold gui=bold"
|
vim.cmd "highlight IvyMatch cterm=bold gui=bold"
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,11 @@ pub fn find_files(options: Options) -> Vec<String> {
|
||||||
builder.overrides(overrides);
|
builder.overrides(overrides);
|
||||||
|
|
||||||
for result in builder.build() {
|
for result in builder.build() {
|
||||||
let absolute_candidate = result.unwrap();
|
let absolute_candidate = match result {
|
||||||
|
Ok(absolute_candidate) => absolute_candidate,
|
||||||
|
Err(..) => continue,
|
||||||
|
};
|
||||||
|
|
||||||
let candidate_path = absolute_candidate.path().strip_prefix(base_path).unwrap();
|
let candidate_path = absolute_candidate.path().strip_prefix(base_path).unwrap();
|
||||||
if candidate_path.is_dir() {
|
if candidate_path.is_dir() {
|
||||||
continue;
|
continue;
|
||||||
|
|
|
||||||
8
scripts/busted.lua
Executable file
8
scripts/busted.lua
Executable file
|
|
@ -0,0 +1,8 @@
|
||||||
|
-- Script to run the busted cli tool. You can use this under nvim using be
|
||||||
|
-- below command. Any arguments can be passed in the same as the busted cli.
|
||||||
|
--
|
||||||
|
-- ```bash
|
||||||
|
-- nvim -l scripts/busted.lua
|
||||||
|
-- ```
|
||||||
|
vim.opt.rtp:append(vim.fn.getcwd())
|
||||||
|
require "busted.runner" { standalone = false }
|
||||||
Loading…
Reference in a new issue