Compare commits

...

58 commits
docs ... 0.x

Author SHA1 Message Date
renovate[bot]
67ca10505d chore(deps): update practically/conventional-tools:1.x
All checks were successful
CI / StyLua (push) Successful in 39s
CI / Luacheck (push) Successful in 57s
CI / Cargo Format (push) Successful in 1m3s
Conventional Tools Commitlint / Commitlint (push) Successful in 1m45s
CI / Build and test (push) Successful in 3m51s
Update the docker digest to a3e9869
2024-12-14 13:53:03 +00:00
16a791e8a6 chore: update dependencies
All checks were successful
CI / Luacheck (pull_request) Successful in 47s
CI / StyLua (pull_request) Successful in 41s
CI / Cargo Format (pull_request) Successful in 1m8s
Conventional Tools Commitlint / Commitlint (pull_request) Successful in 25s
CI / Build and test (pull_request) Successful in 4m42s
CI / StyLua (push) Successful in 34s
CI / Luacheck (push) Successful in 47s
CI / Cargo Format (push) Successful in 1m0s
Conventional Tools Commitlint / Commitlint (push) Successful in 21s
CI / Build and test (push) Successful in 3m29s
Summary:

run `cargo update`

Test Plan:

CI and manual testing
2024-10-27 18:19:10 +00:00
0f364c140d fix: add no require git to rg command backend
Summary:

When using this backend in a directory that is not a git repository, it will
search though all the file. This is mostly a problem when using an alternative
source control like sapling.

When using this backend neovim will just hang until the rg command is done.
When searching node_modules you can imagine how long that takes. This does not
actually fix the issue however, it does make it search though less files.

Test Plan:

This has been used manually on a sapling repo. I have also added
`--no-require-git` to most of the rg defaults in other applications.
2024-10-27 17:28:54 +00:00
6e45d75a1e feat: allow users to configure the window keymaps
All checks were successful
CI / StyLua (pull_request) Successful in 31s
CI / Luacheck (pull_request) Successful in 52s
CI / Cargo Format (pull_request) Successful in 49s
Conventional Tools Commitlint / Commitlint (pull_request) Successful in 13s
CI / Build and test (pull_request) Successful in 2m50s
CI / StyLua (push) Successful in 44s
CI / Luacheck (push) Successful in 1m4s
CI / Cargo Format (push) Successful in 1m12s
Conventional Tools Commitlint / Commitlint (push) Successful in 17s
CI / Build and test (push) Successful in 6m4s
Summary:

Now you can use the setup configuration to overwrite and override the keymaps
in the ivy window. Each of the actions have now been givin a "key", you can use
the keys to run action on keymaps defined in the config. They will all be
registered in the buffer and run when pressed.

The readme has been updated to document how do this.

Test Plan:

We have the tests that still run. It has been tested manually and gone though
QA before getting merged.
2024-10-27 09:28:33 +00:00
renovate[bot]
cfa7e29ae4 fix(deps): update rust crate ignore to v0.4.23
All checks were successful
CI / Luacheck (push) Successful in 27s
CI / StyLua (push) Successful in 13s
CI / Cargo Format (push) Successful in 21s
CI / Build and test (push) Successful in 1m59s
Conventional Tools Commitlint / Commitlint (push) Successful in 5s
Reviewed By: AdeAttwood
Imported From: https://github.com/AdeAttwood/ivy.nvim/pull/93
2024-09-16 19:49:49 +01:00
ded926a4a6 chore: make public api consistent
All checks were successful
CI / Luacheck (pull_request) Successful in 20s
CI / StyLua (pull_request) Successful in 12s
CI / Cargo Format (pull_request) Successful in 18s
CI / Build and test (pull_request) Successful in 2m2s
Conventional Tools Commitlint / Commitlint (pull_request) Successful in 4s
CI / Luacheck (push) Successful in 22s
CI / StyLua (push) Successful in 12s
CI / Cargo Format (push) Successful in 18s
CI / Build and test (push) Successful in 1m52s
Conventional Tools Commitlint / Commitlint (push) Successful in 5s
Summary:

Right now we have two ways to access the public api we have `require('ivy')`
and `vim.ivy. Each way has a different api that will cause some confusion.

Now both apis are the same so anyone that wants to integrate with ivy can do so
without having to figure out what one they need to use.

Test Plan:

The unit tests cover most of the work, I have also been using this locally for
quite some time now with now issues.
2024-09-03 19:34:17 +01:00
f910955ad1 ci: run stylua via npx rather than via action
All checks were successful
CI / Luacheck (push) Successful in 22s
CI / StyLua (push) Successful in 12s
CI / Cargo Format (push) Successful in 19s
CI / Build and test (push) Successful in 1m50s
Conventional Tools Commitlint / Commitlint (push) Successful in 5s
Summary:

This is so we don't need the GITHUB_TOKEN that can not be added in forgejo. I
am giving it a go and the `GITHUB_` prefix is reserved for secrets. There are
also issues with the action in a forgejo environment due to the API not
resolving to github.

This now should work on both platforms. So we can maintain github compatibility
for any pull requests that may come that way.

Test Plan:

Run the CI on github and forgejo, this will need to be a manual process to
validate this works.

Pull Request: #2
2024-09-03 19:20:09 +01:00
Ádran Farias Carnavale
7efb98baab
docs: installation instructions with mini.deps
This commit introduces a brief guide about how to install this plugin
using the `mini.deps` package manager

ref: https://github.com/echasnovski/mini.deps
2024-07-29 08:55:54 +01:00
ShaolunWang
f3585c8cde feat: support windows
Summary:
Add windows support by checking `dll` instead of `so` or `dylib` on windows in `libivy.lua`

Author: @ShaolunWang
Imported From: https://github.com/AdeAttwood/ivy.nvim/pull/90

Test Plan: None provided provided by the author.

Differential Revision: https://ph.baln.co.uk/D7
2024-07-24 16:32:15 +01:00
fef45c2d7e chore: add script for a quick integration test
Summary:
This is just a little script that will run a search and print out the timings
for each of the searches that ivy will preform.

For example running

```
nvim -l ./scripts/integration.lua /path/to/repo test 2> /dev/null
```

will output

```
t       0.074737
te      0.016494
tes     0.018898
test    0.017214
```

Test Plan:
Does not really require testing, this is a little helper script for
development. If it does not work on another machine then we can cross that
bridge when we come to it.

Reviewers: AdeAttwood

Reviewed By: AdeAttwood

Differential Revision: https://ph.baln.co.uk/D2
2024-07-21 12:06:32 +01:00
57d652cb9d ci: run tests on more versions of nvim
Summary:

Right now we are only running the tests on the nightly build of nvim. I would
like to try and maintain support for a few releases. One of the goals of this
project is to be stable. This means trying our best to maintain BC, for this I
have setup running the tests on older versions of nvim.

Right now we have:

- *nightly* Make sure we support the new releases, we can hopefully fix issues
  before this gets releases.
- *stable* This is the main version we support
- *v0.9.5* Maintain the old stable, some OSs like Ubuntu lack behind, would be
  nice to maintain that.

Test Plan:

CI, I have done a quick run before finalizing the change
2024-07-02 20:50:58 +01:00
e121ba7a9f fix: panic on files you don't have access to
Summary:

When you are in a project that has files the user cannot read, ivy's finder
will panic will a permission denied error. This can quite easily happen when
using docker on a project, it's quite common to mount a directory into a docker
container that is run as a different user I.E. MySQL. The other example I can
think of is when you are running tests in a container, the test output may be
owned by the containers' user.

When running ivy this is an example of the kind of panic you would get.

```
thread '<unnamed>' panicked at rust/finder.rs:34:41:
  called `Result::unwrap()` on an `Err` value: WithPath {
    path: "/tmp/workspace/mysql/#innodb_redo", err: Io(
      Custom {
        kind: PermissionDenied, error: Error {
          depth: 2, inner: Io {
            path: Some("/tmp/workspace/mysql/#innodb_redo"),
            err: Os {
              code: 13,
              kind: PermissionDenied,
              message: "Permission denied"
            }
          }
        }
      }
    )
  }
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
fatal runtime error: Rust panics must be rethrown
```

Ref: #83

Test Plan:

This has been tested locally, right now we don't have any unit tests for this.
We may setup some more testing in the future. This was a little hard to
recreate with out docker and mysql.
2024-06-30 15:06:56 +01:00
9af13d0031 docs: add setup config and backends to the readme
Summary:

Now we have a setup function with some config we better tell ppl about it. This
add the setup and config options into the readme.

It also replaces the "Command" with "Backends" to make it a little easier to
understand what needs to be configured. The backend concept has only just been
exposed publicly recently. It now makes since rename this section to line up
with the config.

Test Plan:

N / A
2024-06-30 15:00:56 +01:00
72f3fcac4e docs: remove 'peek' from the docs
Summary:

When we were flushing out the docs, we found some inconsistency in the naming
of checkpoint and peek. They were referring to the same thing.

This removes the word 'peek' in favor of 'checkpoint'

Test Plan:

N / A
2024-06-30 15:00:56 +01:00
a2287f1179 ci: update the test command for better output
Summary:

Now we are running the tests in neovim we get some output printed to stderr.
This is not helpful in the test output, and obfuscate errors.

This throws any unneeded output to `/dev/null` so we can focus of the tests
that have failed. It also moves over to using TAP output for a more descriptive
out put in CI.

Test Plan:

CI, running the test will validate this
2024-06-30 15:00:28 +01:00
Arne Van Maele
611aa54902 feat: add custom keymap section 2024-06-27 21:22:07 +01:00
Arne Van Maele
d19bdc4dfd feat: add keymaps for actions 2024-06-27 21:22:07 +01:00
d6d782b584 feat: setup configuration with default fallbacks
Summary:

Now the users configuration and the default configuration is separated. This
will make it easier to setup ivy with some defaults. Right now we only have the
backends configurable however, it looks like we will be making other components
of ivy configurable.

To use this you can now call `ivy.setup` with no parameters and you will get
the default config. You can also call it with a partial config and if the
option is not found in the users config it will fallback to the users config
value.

Test Plan:

Manual testing and with unit tests in CI
2024-06-27 21:12:37 +01:00
f4d9b67370 feat: implement a setup function
Summary:

Now when using ivy.nvim you will need to call the `setup` function. This will
need to register any backends you want to use. This is an example config, this
can be put into a plugin in `~/.config/nvim/plugin/ivy.lua` for example.

```lua
require('ivy').setup {
  backends = {
    "ivy.backends.buffers",
    "ivy.backends.files",
  },
}
```

If you are using Lazy you can use the `config` directly to call the setup
function.

```lua
return {
  "AdeAttwood/ivy.nvim",
  build = "cargo build --release",
  config = {
    backends = {
      "ivy.backends.buffers",
      "ivy.backends.files",
    }
  }
}
```

The `setup` function can only be called once, if its called a second time any
backends or config will not be used. The module 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")
```

As well as the `register_backend` the core `run`function is exposed. With this
exposed we should be able to build anything we want.

```lua
vim.ivy.run(
  "Title",
  function(input)
    return {
      { content = "One" },
      { content = "Two" },
      { content = "Three" },
    }
  end,
  function(result) vim.cmd("edit " .. result) end
)
```

Test Plan:

Not much to test in this one, it has been tested locally on my config that does
not use any plugin managers, also a sandbox Lazy env using `NVIM_APPNAME`
NVIM_APPNAME
2024-06-27 21:12:37 +01:00
91b6db9d76 feat: split out register backend so it can be used as an external API
Summary:

Split out the register backend function from being a private function. A
backend is a table that will define all of the attributes for a run function.
The attributes are as follows:

- **command** The name of the command that will be registered as a vim user command.
- **items** The callback function that will be passed to the run function that will gather the items for selection
- **callback** The callback function run when an item is selected

The following are optional:

- **keymap** The string passed to `nvim_set_keymap`, this will always be registered in normal mode
- **description** The text description that will be used in the user command
- **name** The name of the backend, this will fallback to the command if its not set

It will also allow to register a backend in a couple of different ways:

- With a backend module table
- With a backend module name
- With a backend module name and override options

This will look for for a lua module `ivy.backends.files`. The module will be
required and registered as a backend.

```lua
register_backend "ivy.backends.files"
```

This will do the same with the module string however, before the backend is
registered the keymap will be overridden

```lua
register_backend({ "ivy.backends.file", { keymap = "<C-p>" } })
```

Test Plan:

CI / Manual testing locally
2024-06-27 21:12:37 +01:00
4803045d4e test: remove all the old tests
Summary:

Removes all the old tests that are not using busted. They have now been
replaced with the busted ones.

Test Plan:

CI
2024-06-16 17:23:45 +01:00
cb4f5860ef ci: setup the new tests in github actions
Summary:

This runs the new tests in our existing CI. We will remove the old tests later
when we know everything runs together so we know everything works.

Test Plan:

CI
2024-06-16 17:17:16 +01:00
37cad71291 test: move the suite over to busted
Summary:

Right now we are using a custom test runner. This move the suite over to
busted, this will make things much more maintainable going forward. The two
main reasons for moving are.

1) The custom runner as some bugs, when running assertions we are not getting
the correct results.

2) All of the busted mocking features. We can use spy and mock, this will allow
us to remove the nvim_mock. This file is not amazing and confuses the lsp
often.

Test Plan:

CI
2024-06-16 17:17:16 +01:00
Arne Van Maele
d7b368355f feat: add Lazy.nvim installation to the readme 2024-06-12 07:40:11 +01:00
renovate[bot]
829bb008b8 fix(deps): update rust crate rayon to 1.10.0 2024-03-25 08:48:35 +00:00
renovate[bot]
6ffdb7795a fix(deps): update rust crate rayon to 1.9.0 2024-02-28 09:21:29 +00:00
cf3e7e3044 fix: opening file with square brackets in them
Summary:

Fixes an issue where you could not open files that were already open with ivy.
If the file path contains a square brackets and that file is already loaded
into a buffer, ivy will throw an error when trying to open it via "files" or
"buffers".

This is an issue with the file escaping before we try and cal `buffer` passing
in the file path to go to the buffer rather than open a new buffer.

This is common with JS frameworks like next js for parameters in file based
routing.

Test Plan:

Test have been added for this change. I have also added tests for the dollar
that was previously handled.
2024-02-13 09:27:09 +00:00
renovate[bot]
6dd5323380 chore(deps): update johnnymorganz/stylua-action action to v4 2024-02-13 09:26:58 +00:00
c32698cf25 feat: don't search in sapling source control directories
This file should be treated as the .git dir and not show up in the candidates
list for files.
2024-01-17 21:15:24 +00:00
renovate[bot]
e5f00bfb1e fix(deps): update rust crate rayon to 1.8.1 2024-01-17 21:14:17 +00:00
7ecc6f1226 feat: don't require a git directory to use .gitignore files
When we are searching we don't want to have to initialize git repo to use a
.gitignore file. This should just ignore any file in a .gitignore always
2024-01-17 21:02:27 +00:00
renovate[bot]
f48e712a4f fix(deps): update rust crate ignore to 0.4.22 2024-01-08 08:00:54 +00:00
342fb8f301 ci: move away from the unsupported rust actions 2023-12-19 21:07:39 +00:00
2429fe725c perf: move to filter map from map and filter
Instead of doing two passes of all candidates using map then a filter,
this uses `filter_map` so we are only doing one pass for the candidates.
This alone is quite a significant improvement of ~7%

Output of `./scripts/bench 0.x`

Benchmark 1: 0.x
  Time (mean ± σ):      2.373 s ±  0.138 s    [User: 10.617 s, System: 1.697 s]
  Range (min … max):    2.124 s …  2.577 s    10 runs

Benchmark 2: HEAD
  Time (mean ± σ):      2.206 s ±  0.133 s    [User: 10.061 s, System: 1.811 s]
  Range (min … max):    1.940 s …  2.433 s    10 runs

Summary
  HEAD ran
    1.08 ± 0.09 times faster than 0.x

-------------------------------------
The percentage difference is -7.00%
-------------------------------------
2023-12-02 17:27:10 +00:00
renovate[bot]
6a57a15e4b fix(deps): update rust crate ignore to 0.4.21 2023-12-02 16:41:32 +00:00
9f9e4a2023 perf: add lua iteration to decrease loops in lua
Move some of the iteration in to loa and access the values by the index
to reduce the number of loops we need todo to get items into teh results
buffer.

Currently the flow is:
  1) Filter and sort the candidates in rust
  2) Convert to a string and pass to lua
  3) Split the string and add them as lines in a buffer in lua

Now the flow is:
  1) Filter and sort the candidates in rust
  2) Loop over an iterator in lua
  3) Pass each item to lua as a pointer by the index

This removes quite a bit of the work that is needed to get the data into
lua as a table. We are first removing the loop that will join the
results vector into one string. Then we will remove the copy of this
string into lua. We will then finally remove the loop to split the
string and create a table from it in lua. All of this ends up in a 12%
speed up.

Output for `./scripts/bench 0.x`

Benchmark 1: HEAD
  Time (mean ± σ):      2.667 s ±  0.065 s    [User: 8.537 s, System: 1.420 s]
  Range (min … max):    2.588 s …  2.767 s    10 runs

Benchmark 2: 0.x
  Time (mean ± σ):      2.337 s ±  0.150 s    [User: 9.564 s, System: 1.648 s]
  Range (min … max):    2.161 s …  2.529 s    10 runs

Summary
  HEAD ran
    1.14 ± 0.08 times faster than 0.x

-------------------------------------
The percentage difference is -12.00%
-------------------------------------
2023-12-02 16:40:58 +00:00
d707a6e15b chore: add bench script to benchmark two commits
This will quickly benchmark your current commit against a commit you
pass in. This will output the percentage difference so you can quickly
get an idea in the performance changes in your current work
2023-12-01 21:40:54 +00:00
renovate[bot]
421c9b5999 fix(deps): update rust crate rayon to 1.8.0 2023-10-22 17:35:24 +01:00
renovate[bot]
675521cc19 chore(deps): update actions/checkout action to v4 2023-10-22 17:29:20 +01:00
c562190829 refactor: remove lazy_static
once_cell has now been merged into rust core. This removes the
lazy_static dependency and migrates over to the built in `OnceLock`.

Its always good to remove dependencies where possible, this also give us
a preference for the built in `OnceLock`

```
Benchmark 1: chore: add benchmark for `set_items`
  Time (mean ± σ):      6.327 s ±  0.199 s    [User: 15.316 s, System: 1.323 s]
  Range (min … max):    6.087 s …  6.712 s    10 runs

Benchmark 2: refactor: remove lazy_static
  Time (mean ± σ):      6.171 s ±  0.251 s    [User: 15.223 s, System: 1.382 s]
  Range (min … max):    5.910 s …  6.776 s    10 runs

Summary
  'refactor: remove lazy_static' ran
    1.03 ± 0.05 times faster than 'chore: add benchmark for `set_items`'
```
2023-10-22 17:23:28 +01:00
6af2b5011b chore: add benchmark for set_items 2023-10-22 17:23:28 +01:00
renovate[bot]
1c412bffa9 chore(deps): update practically/conventional-tools to 647d6e4 2023-06-25 05:28:35 -07:00
5a0f037b71 feat: add the paste functionality into the ivy prompt
Now when you paste and you are in an ivy buffer the paste will be added
to the prompt not into the completion window. You can use your usual
paste key binding I.E. <SHIFT>+<INSERT> <CTRL>+<SHIFT>+<V>

Ref: #11
2023-06-13 01:19:24 -07:00
0bd6770da4 fix: use package.searchpath to find libivyrs
Now we are useing the package.searchpath to find the libivyrs.so or
libivyrs.dylib. This is so it will load the correct library or the OS
you are on. This fixes the issues with loading the library on macOS.

Ref: #57
2023-06-13 01:19:11 -07:00
renovate[bot]
de7f9a2292 chore(deps): update rust crate criterion to 0.5.1 2023-06-09 09:38:06 -07:00
100723f141 chore: remove dependabot 2023-05-23 09:26:37 +01:00
027819e15f feat: add ripgrep backend
`rg` will now be used over `ag` if it is available. This is because `rg`
is a faster alternative reduces lag when searching larger codebases.
`ag` is also now conditionally loaded if the command is available.
2023-05-23 09:24:36 +01:00
renovate[bot]
92aa2c3433 chore(deps): update johnnymorganz/stylua-action action to v3 2023-05-23 09:23:59 +01:00
56dced41e0 fix: opening terminal buffers
Now when trying to open files and buffers it will use the `buffer`
command when there is an existing buffer with the same name. This will
allow us to open non file buffers like terminals and log buffers.

By using the `bufnr` function to get the existing buffers, it will also
work for buffers that have been renamed with `title` command

Ref: #31
2023-04-26 07:26:12 +01:00
2a72162150 style: run stylua 2023-04-26 07:24:22 +01:00
9017b2c322 perf: return a table of results from the command finder
When reading the output of a command we are now reading the stream line
by line and building the table of results. This will save us a loop of
the results later, if we returned a string we need to split the string
before adding each line to the completion buffer because nvim only
supports replacing one line at a time.
2023-04-26 07:24:22 +01:00
10a3117aae fix: never let a shell command fail
This makes the terminal go really funkie and sometimes crash when
printing the error messages to the current process stdout where nvim is
running.

Now the error output is sent to the `popen` output and displayed in the
ivy completion buffer without messing with the current process io.
2023-04-26 07:24:22 +01:00
ff7c28490d fix: escape \ when running shell commands
This is causing an issue when running shell commands though ivy and
trying to escape chars is in strings or regexes.
2023-04-26 07:24:22 +01:00
149d12e824 ci: move to the dockerhub conventional-tools update 2023-04-18 20:55:07 +01:00
renovate[bot]
4e2c2381ef chore: add renovate.json 2023-03-27 20:48:16 +01:00
dependabot[bot]
e553517f7a chore: bump rayon from 1.6.1 to 1.7.0
Bumps [rayon](https://github.com/rayon-rs/rayon) from 1.6.1 to 1.7.0.
- [Release notes](https://github.com/rayon-rs/rayon/releases)
- [Changelog](https://github.com/rayon-rs/rayon/blob/master/RELEASES.md)
- [Commits](https://github.com/rayon-rs/rayon/compare/rayon-core-v1.6.1...rayon-core-v1.7.0)

---
updated-dependencies:
- dependency-name: rayon
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-03-06 16:23:16 +00:00
2032d0748b docs: add section about post-merge compiling 2023-03-03 07:22:08 +00:00
4dd8927ea2 docs: fix the ivy.run example 2023-03-03 07:22:08 +00:00
44 changed files with 1377 additions and 702 deletions

View file

@ -1,20 +0,0 @@
version: 2
updates:
- package-ecosystem: cargo
directory: '/'
schedule:
interval: daily
commit-message:
prefix: 'chore:'
assignees:
- 'AdeAttwood'
- package-ecosystem: github-actions
directory: '/'
schedule:
interval: daily
commit-message:
prefix: 'chore:'
assignees:
- 'AdeAttwood'

View file

@ -5,26 +5,12 @@ on:
pull_request: { branches: ["0.x"] } pull_request: { branches: ["0.x"] }
jobs: jobs:
commits:
name: Commitlint
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 1000
- name: Lint commits
uses: docker://registry.k1.zportal.co.uk/practically-oss/conventional-tools:0.x
with:
args: conventional-tools commitlint -l1 -f39febd82e236a9c79f5b408e98cbd20410f11e9e
luacheck: luacheck:
name: Luacheck name: Luacheck
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v4
- name: Install luarocks - name: Install luarocks
run: sudo apt update && sudo apt install -y luarocks run: sudo apt update && sudo apt install -y luarocks
@ -40,45 +26,59 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v4
- name: Run stylua - name: Run stylua
uses: JohnnyMorganz/stylua-action@v2.0.0 run: npx @johnnymorganz/stylua-bin --check .
with:
token: ${{ secrets.GITHUB_TOKEN }}
version: latest
args: --check .
cargo-format: cargo-format:
name: Cargo Format name: Cargo Format
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v4
- name: Run cargo format - name: Set up rust
uses: actions-rs/cargo@v1 uses: dtolnay/rust-toolchain@stable
with: with:
command: fmt components: rustfmt
args: --all -- --check
- name: Lint
run: cargo fmt --check
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
uses: actions/checkout@v3 uses: actions/checkout@v4
- name: Install rust toolchain - name: Set up rust
uses: actions-rs/toolchain@v1 uses: dtolnay/rust-toolchain@stable
with:
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

21
.github/workflows/ct-commitlint.yml vendored Normal file
View file

@ -0,0 +1,21 @@
name: Conventional Tools Commitlint
on:
push: { branches: ["0.x"] }
pull_request: { branches: ["0.x"] }
jobs:
commits:
name: Commitlint
runs-on: ubuntu-latest
container: practically/conventional-tools:1.x@sha256:a3e98697743d8801c694b92553da733aff0fbae6bf92876b13c92343a569f049
steps:
- name: Checkout
uses: actions/checkout@v4
with: {fetch-depth: 1000}
- name: Git safe.directory
run: git config --global --add safe.directory $PWD
- name: Lint commits
run: conventional-tools commitlint -l1 -f39febd82e236a9c79f5b408e98cbd20410f11e9e

View file

@ -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",
} }

477
Cargo.lock generated
View file

@ -4,9 +4,9 @@ version = 3
[[package]] [[package]]
name = "aho-corasick" name = "aho-corasick"
version = "0.7.19" version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e" checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
dependencies = [ dependencies = [
"memchr", "memchr",
] ]
@ -18,33 +18,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
[[package]] [[package]]
name = "atty" name = "anstyle"
version = "0.2.14" version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" checksum = "8365de52b16c035ff4fcafe0092ba9390540e3e352870ac09933bebcaa2c8c56"
dependencies = [
"hermit-abi",
"libc",
"winapi",
]
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.1.0" version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]] [[package]]
name = "bstr" name = "bstr"
version = "1.1.0" 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 = "b45ea9b00a7b3f2988e9a65ad3917e62123c38dba709b666506207be96d1790b" checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c"
dependencies = [ dependencies = [
"memchr", "memchr",
"serde", "serde",
@ -52,9 +41,9 @@ dependencies = [
[[package]] [[package]]
name = "bumpalo" name = "bumpalo"
version = "3.11.0" version = "3.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
[[package]] [[package]]
name = "cast" name = "cast"
@ -70,9 +59,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]] [[package]]
name = "ciborium" name = "ciborium"
version = "0.2.0" version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0c137568cc60b904a7724001b35ce2630fd00d5d84805fbb608ab89509d788f" checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e"
dependencies = [ dependencies = [
"ciborium-io", "ciborium-io",
"ciborium-ll", "ciborium-ll",
@ -81,15 +70,15 @@ dependencies = [
[[package]] [[package]]
name = "ciborium-io" name = "ciborium-io"
version = "0.2.0" version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "346de753af073cc87b52b2083a506b38ac176a44cfb05497b622e27be899b369" checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757"
[[package]] [[package]]
name = "ciborium-ll" name = "ciborium-ll"
version = "0.2.0" version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "213030a2b5a4e0c0892b6652260cf6ccac84827b83a85a534e178e3906c4cf1b" checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9"
dependencies = [ dependencies = [
"ciborium-io", "ciborium-io",
"half", "half",
@ -97,40 +86,44 @@ dependencies = [
[[package]] [[package]]
name = "clap" name = "clap"
version = "3.2.22" version = "4.5.20"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86447ad904c7fb335a790c9d7fe3d0d971dc523b8ccd1561a520de9a85302750" checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8"
dependencies = [ dependencies = [
"bitflags", "clap_builder",
]
[[package]]
name = "clap_builder"
version = "4.5.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54"
dependencies = [
"anstyle",
"clap_lex", "clap_lex",
"indexmap",
"textwrap",
] ]
[[package]] [[package]]
name = "clap_lex" name = "clap_lex"
version = "0.2.4" version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
dependencies = [
"os_str_bytes",
]
[[package]] [[package]]
name = "criterion" name = "criterion"
version = "0.4.0" version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7c76e09c1aae2bc52b3d2f29e13c6572553b30c4aa1b8a49fd70de6412654cb" checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f"
dependencies = [ dependencies = [
"anes", "anes",
"atty",
"cast", "cast",
"ciborium", "ciborium",
"clap", "clap",
"criterion-plot", "criterion-plot",
"is-terminal",
"itertools", "itertools",
"lazy_static",
"num-traits", "num-traits",
"once_cell",
"oorandom", "oorandom",
"plotters", "plotters",
"rayon", "rayon",
@ -152,62 +145,42 @@ dependencies = [
"itertools", "itertools",
] ]
[[package]]
name = "crossbeam-channel"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521"
dependencies = [
"cfg-if",
"crossbeam-utils",
]
[[package]] [[package]]
name = "crossbeam-deque" name = "crossbeam-deque"
version = "0.8.2" version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
dependencies = [ dependencies = [
"cfg-if",
"crossbeam-epoch", "crossbeam-epoch",
"crossbeam-utils", "crossbeam-utils",
] ]
[[package]] [[package]]
name = "crossbeam-epoch" name = "crossbeam-epoch"
version = "0.9.10" version = "0.9.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "045ebe27666471bb549370b4b0b3e51b07f56325befa4284db65fc89c02511b1" checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
dependencies = [ dependencies = [
"autocfg",
"cfg-if",
"crossbeam-utils", "crossbeam-utils",
"memoffset",
"once_cell",
"scopeguard",
] ]
[[package]] [[package]]
name = "crossbeam-utils" name = "crossbeam-utils"
version = "0.8.11" version = "0.8.20"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc" checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
dependencies = [
"cfg-if", [[package]]
"once_cell", name = "crunchy"
] version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
[[package]] [[package]]
name = "either" name = "either"
version = "1.8.0" version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]] [[package]]
name = "fuzzy-matcher" name = "fuzzy-matcher"
@ -220,79 +193,74 @@ dependencies = [
[[package]] [[package]]
name = "globset" name = "globset"
version = "0.4.10" version = "0.4.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "029d74589adefde59de1a0c4f4732695c32805624aec7b68d91503d4dba79afc" checksum = "15f1ce686646e7f1e19bf7d5533fe443a45dbfb990e00629110797578b42fb19"
dependencies = [ dependencies = [
"aho-corasick", "aho-corasick",
"bstr", "bstr",
"fnv",
"log", "log",
"regex", "regex-automata",
"regex-syntax",
] ]
[[package]] [[package]]
name = "half" name = "half"
version = "1.8.2" version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888"
[[package]]
name = "hashbrown"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [ dependencies = [
"libc", "cfg-if",
"crunchy",
] ]
[[package]] [[package]]
name = "ignore" name = "hermit-abi"
version = "0.4.20" version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbe7873dab538a9a44ad79ede1faf5f30d49f9a5c883ddbab48bce81b64b7492" checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc"
[[package]]
name = "ignore"
version = "0.4.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d89fd380afde86567dfba715db065673989d6253f42b88179abd3eae47bda4b"
dependencies = [ dependencies = [
"crossbeam-deque",
"globset", "globset",
"lazy_static",
"log", "log",
"memchr", "memchr",
"regex", "regex-automata",
"same-file", "same-file",
"thread_local",
"walkdir", "walkdir",
"winapi-util", "winapi-util",
] ]
[[package]] [[package]]
name = "indexmap" name = "is-terminal"
version = "1.9.1" version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b"
dependencies = [ dependencies = [
"autocfg", "hermit-abi",
"hashbrown", "libc",
"windows-sys 0.52.0",
] ]
[[package]] [[package]]
name = "itertools" name = "itertools"
version = "0.10.4" version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8bf247779e67a9082a4790b45e71ac7cfd1321331a5c856a74a9faebdab78d0" checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
dependencies = [ dependencies = [
"either", "either",
] ]
[[package]] [[package]]
name = "itoa" name = "itoa"
version = "1.0.3" version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
[[package]] [[package]]
name = "ivy" name = "ivy"
@ -301,97 +269,62 @@ dependencies = [
"criterion", "criterion",
"fuzzy-matcher", "fuzzy-matcher",
"ignore", "ignore",
"lazy_static",
"rayon", "rayon",
] ]
[[package]] [[package]]
name = "js-sys" name = "js-sys"
version = "0.3.60" version = "0.3.72"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9"
dependencies = [ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.132" version = "0.2.161"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5" checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1"
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.17" version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
dependencies = [
"cfg-if",
]
[[package]] [[package]]
name = "memchr" name = "memchr"
version = "2.5.0" version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "memoffset"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
dependencies = [
"autocfg",
]
[[package]] [[package]]
name = "num-traits" name = "num-traits"
version = "0.2.15" version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [ dependencies = [
"autocfg", "autocfg",
] ]
[[package]]
name = "num_cpus"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"
dependencies = [
"hermit-abi",
"libc",
]
[[package]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.14.0" version = "1.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f7254b99e31cad77da24b08ebf628882739a608578bb1bcdfc1f9c21260d7c0" checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
[[package]] [[package]]
name = "oorandom" name = "oorandom"
version = "11.1.3" version = "11.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9"
[[package]]
name = "os_str_bytes"
version = "6.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff"
[[package]] [[package]]
name = "plotters" name = "plotters"
version = "0.3.4" version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2538b639e642295546c50fcd545198c9d64ee2a38620a628724a3b266d5fbf97" checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747"
dependencies = [ dependencies = [
"num-traits", "num-traits",
"plotters-backend", "plotters-backend",
@ -402,42 +335,42 @@ dependencies = [
[[package]] [[package]]
name = "plotters-backend" name = "plotters-backend"
version = "0.3.4" version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "193228616381fecdc1224c62e96946dfbc73ff4384fba576e052ff8c1bea8142" checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a"
[[package]] [[package]]
name = "plotters-svg" name = "plotters-svg"
version = "0.3.3" version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9a81d2759aae1dae668f783c308bc5c8ebd191ff4184aaa1b37f65a6ae5a56f" checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670"
dependencies = [ dependencies = [
"plotters-backend", "plotters-backend",
] ]
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.43" version = "1.0.89"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab" checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.21" version = "1.0.37"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]] [[package]]
name = "rayon" name = "rayon"
version = "1.6.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 = "6db3a213adf02b3bcfd2d3846bb41cb22857d131789e01df434fb7e7bc0759b7" checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
dependencies = [ dependencies = [
"either", "either",
"rayon-core", "rayon-core",
@ -445,21 +378,31 @@ dependencies = [
[[package]] [[package]]
name = "rayon-core" name = "rayon-core"
version = "1.10.1" version = "1.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cac410af5d00ab6884528b4ab69d1e8e146e8d471201800fa1b4524126de6ad3" checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
dependencies = [ dependencies = [
"crossbeam-channel",
"crossbeam-deque", "crossbeam-deque",
"crossbeam-utils", "crossbeam-utils",
"num_cpus",
] ]
[[package]] [[package]]
name = "regex" name = "regex"
version = "1.6.0" version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3"
dependencies = [ dependencies = [
"aho-corasick", "aho-corasick",
"memchr", "memchr",
@ -468,15 +411,15 @@ dependencies = [
[[package]] [[package]]
name = "regex-syntax" name = "regex-syntax"
version = "0.6.27" version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
[[package]] [[package]]
name = "ryu" name = "ryu"
version = "1.0.11" version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
[[package]] [[package]]
name = "same-file" name = "same-file"
@ -487,26 +430,20 @@ dependencies = [
"winapi-util", "winapi-util",
] ]
[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.144" version = "1.0.213"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860" checksum = "3ea7893ff5e2466df8d720bb615088341b295f849602c6956047f8f80f0e9bc1"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.144" version = "1.0.213"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94ed3a816fb1d101812f83e789f888322c34e291f894f19590dc310963e87a00" checksum = "7e85ad2009c50b58e87caa8cd6dac16bdf511bbfb7af6c33df902396aa480fa5"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -515,38 +452,34 @@ dependencies = [
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.85" version = "1.0.132"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44" checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03"
dependencies = [ dependencies = [
"itoa", "itoa",
"memchr",
"ryu", "ryu",
"serde", "serde",
] ]
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.99" version = "2.0.85"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13" checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "textwrap"
version = "0.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16"
[[package]] [[package]]
name = "thread_local" name = "thread_local"
version = "1.1.4" version = "1.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
dependencies = [ dependencies = [
"cfg-if",
"once_cell", "once_cell",
] ]
@ -562,36 +495,36 @@ dependencies = [
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.4" version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd" checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
[[package]] [[package]]
name = "walkdir" name = "walkdir"
version = "2.3.2" version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
dependencies = [ dependencies = [
"same-file", "same-file",
"winapi",
"winapi-util", "winapi-util",
] ]
[[package]] [[package]]
name = "wasm-bindgen" name = "wasm-bindgen"
version = "0.2.83" version = "0.2.95"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"once_cell",
"wasm-bindgen-macro", "wasm-bindgen-macro",
] ]
[[package]] [[package]]
name = "wasm-bindgen-backend" name = "wasm-bindgen-backend"
version = "0.2.83" version = "0.2.95"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358"
dependencies = [ dependencies = [
"bumpalo", "bumpalo",
"log", "log",
@ -604,9 +537,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-macro" name = "wasm-bindgen-macro"
version = "0.2.83" version = "0.2.95"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56"
dependencies = [ dependencies = [
"quote", "quote",
"wasm-bindgen-macro-support", "wasm-bindgen-macro-support",
@ -614,9 +547,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-macro-support" name = "wasm-bindgen-macro-support"
version = "0.2.83" version = "0.2.95"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -627,47 +560,107 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-shared" name = "wasm-bindgen-shared"
version = "0.2.83" version = "0.2.95"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d"
[[package]] [[package]]
name = "web-sys" name = "web-sys"
version = "0.3.60" version = "0.3.72"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112"
dependencies = [ dependencies = [
"js-sys", "js-sys",
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]] [[package]]
name = "winapi-util" name = "winapi-util"
version = "0.1.5" version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
dependencies = [ dependencies = [
"winapi", "windows-sys 0.59.0",
] ]
[[package]] [[package]]
name = "winapi-x86_64-pc-windows-gnu" name = "windows-sys"
version = "0.4.0" version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-sys"
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"

View file

@ -9,13 +9,12 @@ crate-type = ["cdylib", "rlib"]
path = "rust/lib.rs" path = "rust/lib.rs"
[dependencies] [dependencies]
ignore = "0.4.20" ignore = "0.4.22"
fuzzy-matcher = "0.3.7" fuzzy-matcher = "0.3.7"
lazy_static = "1.4.0" rayon = "1.10.0"
rayon = "1.6.1"
[dev-dependencies] [dev-dependencies]
criterion = "0.4.0" criterion = "0.5.1"
[profile.release] [profile.release]
opt-level = 3 opt-level = 3

160
README.md
View file

@ -1,6 +1,6 @@
<div align="center"> <div align="center">
<img src="assets/logo.svg" alt="ivy.vim" /> <img src="assets/logo.svg" alt="ivy.vim" />
<br /> <br />
<br /> <br />
@ -20,7 +20,90 @@ 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",
},
```
Using [mini.deps](https://github.com/echasnovski/mini.deps)
```lua
local deps = require "mini.deps"
deps.later(function() -- Or `deps.now` if you want this to be loaded immediately
local build = function(args)
local obj = vim
.system(
{ "cargo", "build", "--release", string.format("%s%s%s", "--manifest-path=", args.path, "/Cargo.toml") },
{ text = true }
)
:wait()
vim.print(vim.inspect(obj))
end
deps.add {
source = "AdeAttwood/ivy.nvim",
hooks = {
post_install = build,
post_checkout = build,
},
}
end)
```
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>" } },
},
-- Set mappings of your own, you can use the action `key` to define the
-- action you want.
mappings = {
["<CR>"] = "complete"
}
}
```
When defining config overrides in the setup function, this will overwrite any
default config, not merge it. To merge the configuration you can use the
`vim.tbl_extend` use the default configuration and add any extra.
```lua
require("ivy").setup {
mappings = vim.tbl_extend("force", config:get { "mappings" }, {
["<esc>"] = "destroy",
}),
}
```
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
@ -44,30 +127,50 @@ error: linker `cc` not found
= note: No such file or directory (os error 2) = note: No such file or directory (os error 2)
``` ```
To configure auto compiling you can use the [`post-merge`](./post-merge.sample)
git hook to compile the library automatically when you update the package. This
works well if you are managing your plugins via git. For an example
installation you can run the following command. **NOTE:** This will overwrite
any post-merge hooks you have already installed.
```sh
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 | Default 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 | Default Key Map | Description |
| -------------- | ------------------------------------------------------------------------------ | | ------------------- | --------------------- | --------------- | ---------------------------------------------------------------- |
| Complete | Run the completion function, usually this will be opening a file | | Complete | `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 | `vsplit` | \<C-v\> | Run the completion function in a new vertical split |
| Vertical Split | Run the completion function in a new vertical split | | Split | `split` | \<C-s\> | Run the completion function in a new split |
| Split | Run the completion function in a new split | | Destroy | `destroy` | \<C-c\> | Close the results window |
| Clear | `clear` | \<C-u\> | Clear the results window |
| Delete word | `delete_word` | \<C-w\> | Delete the word under the cursor |
| Next | `next` | \<C-n\> | Move to the next candidate |
| Previous | `previous` | \<C-p\> | Move to the previous candidate |
| Next Checkpoint | `next_checkpoint` | \<C-M-n\> | Move to the next candidate and keep Ivy open and focussed |
| Previous Checkpoint | `previous_checkpoint` | \<C-M-n\> | Move to the previous candidate and keep Ivy open and focussed |
## API ## API
@ -107,13 +210,6 @@ vertical split action it will open the buffer in a new `vsplit`
#### Example #### Example
```lua ```lua
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)
vim.ivy.run( vim.ivy.run(
-- The name given to the results window and displayed to the user -- The name given to the results window and displayed to the user
"Title", "Title",
@ -127,19 +223,11 @@ 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)
return { end
{ 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/rg.lua Normal file
View file

@ -0,0 +1,12 @@
local utils = require "ivy.utils"
local rg = {
name = "RG",
command = "IvyRg",
description = "Run ripgrep to search for content in files",
keymap = "<leader>/",
items = utils.command_finder "rg --no-require-git --max-columns 200 --vimgrep --",
callback = utils.vimgrep_action(),
}
return rg

48
lua/ivy/config.lua Normal file
View file

@ -0,0 +1,48 @@
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",
},
mappings = {
["<C-c>"] = "destroy",
["<C-u>"] = "clear",
["<C-n>"] = "next",
["<C-p>"] = "previous",
["<C-M-n>"] = "next_checkpoint",
["<C-M-p>"] = "previous_checkpoint",
["<CR>"] = "complete",
["<C-v>"] = "vsplit",
["<C-s>"] = "split",
["<BS>"] = "backspace",
["<Left>"] = "left",
["<Right>"] = "right",
["<C-w>"] = "delete_word",
},
}
return setmetatable(config, config_mt)

27
lua/ivy/config_spec.lua Normal file
View 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)

View file

@ -3,7 +3,6 @@ local prompt = require "ivy.prompt"
local utils = require "ivy.utils" local utils = require "ivy.utils"
local controller = {} local controller = {}
controller.action = utils.actions
controller.items = nil controller.items = nil
controller.callback = nil controller.callback = nil
@ -30,6 +29,10 @@ controller.search = function(value)
controller.update(prompt.text()) controller.update(prompt.text())
end end
controller.paste = function()
controller.search(prompt.text() .. vim.fn.getreg "+p")
end
controller.update = function(text) controller.update = function(text)
vim.schedule(function() vim.schedule(function()
window.set_items(controller.items(text)) window.set_items(controller.items(text))
@ -37,7 +40,7 @@ controller.update = function(text)
if #text > 0 then if #text > 0 then
-- Escape characters so they do not throw an error when vim tries to use -- Escape characters so they do not throw an error when vim tries to use
-- the "text" as a regex -- the "text" as a regex
local escaped_text = string.gsub(text, "([-/])", "\\%1") local escaped_text = string.gsub(text, "([-/\\])", "\\%1")
vim.cmd("syntax match IvyMatch '[" .. escaped_text .. "]'") vim.cmd("syntax match IvyMatch '[" .. escaped_text .. "]'")
end end
end) end)
@ -52,7 +55,7 @@ end
controller.checkpoint = function() controller.checkpoint = function()
vim.api.nvim_set_current_win(window.origin) vim.api.nvim_set_current_win(window.origin)
controller.callback(window.get_current_selection(), controller.action.CHECKPOINT) controller.callback(window.get_current_selection(), utils.actions.CHECKPOINT)
vim.api.nvim_set_current_win(window.window) vim.api.nvim_set_current_win(window.window)
end end

View 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)

View file

@ -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)

49
lua/ivy/init.lua Normal file
View file

@ -0,0 +1,49 @@
local controller = require "ivy.controller"
local libivy = require "ivy.libivy"
local config = require "ivy.config"
local utils = require "ivy.utils"
local register_backend = require "ivy.register_backend"
local ivy = {}
ivy.action = utils.actions
ivy.utils = utils
ivy.match = libivy.ivy_match
ivy.run = controller.run
ivy.register_backend = register_backend
ivy.checkpoint = controller.checkpoint
ivy.paste = controller.paste
ivy.complete = controller.complete
ivy.destroy = controller.destroy
ivy.input = controller.input
ivy.next = controller.next
ivy.previous = controller.previous
ivy.search = controller.search
-- 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
View 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)

View file

@ -1,6 +1,22 @@
local library_path = (function() local library_path = (function()
local dirname = string.sub(debug.getinfo(1).source, 2, #"/fzf_lib.lua" * -1) local root = string.sub(debug.getinfo(1).source, 2, #"/libivy.lua" * -1)
return dirname .. "/../../target/release/libivyrs.so" local release_path = root .. "../../target/release"
local current_vim_version = vim.version()
local minimum_supported_version = vim.version.parse "0.9.5"
local is_windows
if vim.version.gt(current_vim_version, minimum_supported_version) then
is_windows = vim.uv.os_uname().sysname == "Windows_NT"
else
is_windows = vim.loop.os_uname().sysname == "Windows_NT"
end
if is_windows then
return package.searchpath("ivyrs", release_path .. "/?.dll;")
else
return package.searchpath("libivyrs", release_path .. "/?.so;" .. release_path .. "/?.dylib;")
end
end)() end)()
local ffi = require "ffi" local ffi = require "ffi"
@ -19,8 +35,31 @@ ffi.cdef [[
char* ivy_cwd(); char* ivy_cwd();
int ivy_match(const char*, const char*); int ivy_match(const char*, const char*);
char* ivy_files(const char*, const char*); char* ivy_files(const char*, const char*);
int ivy_files_iter(const char*, const char*);
int ivy_files_iter_len(int);
char* ivy_files_iter_at(int, int);
void ivy_files_iter_delete(int);
]] ]]
local iter_mt = {
__len = function(self)
return self.length
end,
__index = function(self, index)
-- Pass in our index -1. This will map lua's one based indexing to zero
-- based indexing that we are using in the rust lib.
local item = ffi.string(ivy_c.ivy_files_iter_at(self.id, index - 1))
return { content = item }
end,
__newindex = function(_, _, _)
error("attempt to update a read-only table", 2)
end,
__gc = function(self)
ivy_c.ivy_files_iter_delete(self.id)
end,
}
local libivy = {} local libivy = {}
libivy.ivy_init = function(dir) libivy.ivy_init = function(dir)
@ -36,7 +75,12 @@ libivy.ivy_match = function(pattern, text)
end end
libivy.ivy_files = function(pattern, base_dir) libivy.ivy_files = function(pattern, base_dir)
return ffi.string(ivy_c.ivy_files(pattern, base_dir)) local iter_id = ivy_c.ivy_files_iter(pattern, base_dir)
local iter_len = ivy_c.ivy_files_iter_len(iter_id)
local iter = { id = iter_id, length = iter_len }
setmetatable(iter, iter_mt)
return iter
end end
return libivy return libivy

36
lua/ivy/libivy_spec.lua Normal file
View 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)

View file

@ -1,18 +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 matches = libivy.ivy_files("ci.yml", current_dir)
if matches ~= ".github/workflows/ci.yml\n" then
t.error("Invalid matches: " .. matches)
end
end)

29
lua/ivy/matcher_spec.lua Normal file
View 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)

View file

@ -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
View 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)

View file

@ -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

View 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

View 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)

View file

@ -17,6 +17,13 @@ utils.command_map = {
[utils.actions.SPLIT] = "split", [utils.actions.SPLIT] = "split",
} }
utils.existing_command_map = {
[utils.actions.EDIT] = "buffer",
[utils.actions.CHECKPOINT] = "buffer",
[utils.actions.VSPLIT] = "vsplit | buffer",
[utils.actions.SPLIT] = "split | buffer",
}
utils.command_finder = function(command, min) utils.command_finder = function(command, min)
if min == nil then if min == nil then
min = 3 min = 3
@ -33,14 +40,18 @@ utils.command_finder = function(command, min)
-- TODO(ade): Think if we want to start escaping the command here. I -- 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 -- dont know if its causing issues while trying to use regex especially
-- with word boundaries `input:gsub("'", "\\'"):gsub('"', '\\"')` -- with word boundaries `input:gsub("'", "\\'"):gsub('"', '\\"')`
local handle = io.popen(command .. " " .. input .. " 2>&1") local handle = io.popen(command .. " " .. input .. " 2>&1 || true")
if handle == nil then if handle == nil then
return {} return {}
end end
local result = handle:read "*a"
handle:close()
return result local results = {}
for line in handle:lines() do
table.insert(results, { content = line })
end
handle:close()
return results
end end
end end
@ -68,7 +79,14 @@ utils.file_action = function()
return return
end end
local command = utils.command_map[action] local buffer_number = vim.fn.bufnr(file)
local command
if buffer_number > -1 then
command = utils.existing_command_map[action]
else
command = utils.command_map[action]
end
if command == nil then if command == nil then
vim.api.nvim_err_writeln("[IVY] The file action is unable the handel the action " .. action) vim.api.nvim_err_writeln("[IVY] The file action is unable the handel the action " .. action)
return return
@ -81,12 +99,15 @@ 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+):"
vim.cmd(line) if line ~= nil then
vim.cmd(line)
end
end end
end end
utils.escape_file_name = function(input) utils.escape_file_name = function(input)
return string.gsub(input, "([$])", "\\%1") local file, _ = string.gsub(input, "([$%]\\[])", "\\%1")
return file
end end
return utils return utils

View 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)

View 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)

View file

@ -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)

View file

@ -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" },
}, },
} }
for i = 1, #test_data do describe("utils vimgrep_action", function()
local data = test_data[i] before_each(function()
it(data.it, function(t) spy.on(vim, "cmd")
vimgrep_action(data.completion, data.action)
if #vim_mock.commands ~= #data.commands then
t.error("Incorrect number of commands run expected " .. #data.commands .. " but found " .. #vim_mock.commands)
end
for j = 1, #data.commands do
if vim_mock.commands[j] ~= data.commands[j] then
t.error(
"Incorrect command run expected '" .. data.commands[j] .. "' but found '" .. vim_mock.commands[j] .. "'"
)
end
end
end) end)
end
after_each(function()
vim.cmd:revert()
end)
for i = 1, #test_data do
local data = test_data[i]
it(data.it, function()
assert.is_true(#data.commands > 0, "You must assert that at least one command is run")
vimgrep_action(data.completion, data.action)
assert.is_equal(#vim.cmd.calls, #data.commands, "The `vim.cmd` function should be called once")
for j = 1, #data.commands do
assert.spy(vim.cmd).was_called_with(data.commands[j])
end
end)
end
end)

View file

@ -75,6 +75,11 @@ mock.reset = function()
return lines return lines
end, end,
}, },
fn = {
bufnr = function()
return -1
end,
},
schedule = function(callback) schedule = function(callback)
callback() callback()
end, end,

View file

@ -1,3 +1,5 @@
local config = require "ivy.config"
-- Constent options that will be used for the keymaps -- Constent options that will be used for the keymaps
local opts = { noremap = true, silent = true, nowait = true } local opts = { noremap = true, silent = true, nowait = true }
@ -21,6 +23,40 @@ local function string_to_table(lines)
return matches return matches
end end
local function get_items_length(items)
local mt = getmetatable(items)
if mt ~= nil and mt.__len ~= nil then
return mt.__len(items)
end
return #items
end
local function call_gc(items)
local mt = getmetatable(items)
if mt ~= nil and mt.__gc ~= nil then
return mt.__gc(items)
end
end
local callbacks = {
destroy = "<cmd>lua vim.ivy.destroy()<CR>",
clear = "<cmd>lua vim.ivy.search('')<CR>",
next = "<cmd>lua vim.ivy.next()<CR>",
previous = "<cmd>lua vim.ivy.previous()<CR>",
next_checkpoint = "<cmd>lua vim.ivy.next(); vim.ivy.checkpoint()<CR>",
previous_checkpoint = "<cmd>lua vim.ivy.previous(); vim.ivy.checkpoint()<CR>",
complete = "<cmd>lua vim.ivy.complete(vim.ivy.action.EDIT)<CR>",
vsplit = "<cmd>lua vim.ivy.complete(vim.ivy.action.VSPLIT)<CR>",
split = "<cmd>lua vim.ivy.complete(vim.ivy.action.SPLIT)<CR>",
backspace = "<cmd>lua vim.ivy.input('BACKSPACE')<CR>",
left = "<cmd>lua vim.ivy.input('LEFT')<CR>",
right = "<cmd>lua vim.ivy.input('RIGHT')<CR>",
delete_word = "<cmd>lua vim.ivy.input('DELETE_WORD')<CR>",
}
local window = {} local window = {}
window.index = 0 window.index = 0
@ -59,25 +95,15 @@ window.make_buffer = function()
vim.api.nvim_buf_set_keymap(window.buffer, "n", chars[index], "<cmd>lua vim.ivy.input('" .. char .. "')<CR>", opts) vim.api.nvim_buf_set_keymap(window.buffer, "n", chars[index], "<cmd>lua vim.ivy.input('" .. char .. "')<CR>", opts)
end end
vim.api.nvim_buf_set_keymap(window.buffer, "n", "<C-c>", "<cmd>lua vim.ivy.destroy()<CR>", opts) local mappings = config:get { "mappings" }
vim.api.nvim_buf_set_keymap(window.buffer, "n", "<C-u>", "<cmd>lua vim.ivy.search('')<CR>", opts) assert(mappings, "The mappings key is missing from the config, something has gone horribly wrong")
vim.api.nvim_buf_set_keymap(window.buffer, "n", "<C-n>", "<cmd>lua vim.ivy.next()<CR>", opts) for key, value in pairs(mappings) do
vim.api.nvim_buf_set_keymap(window.buffer, "n", "<C-p>", "<cmd>lua vim.ivy.previous()<CR>", opts) if callbacks[value] == nil then
vim.api.nvim_buf_set_keymap(window.buffer, "n", "<C-M-n>", "<cmd>lua vim.ivy.next(); vim.ivy.checkpoint()<CR>", opts) error("The mapping '" .. value .. "' is not a valid ivy callback")
vim.api.nvim_buf_set_keymap( end
window.buffer,
"n", vim.api.nvim_buf_set_keymap(window.buffer, "n", key, callbacks[value], opts)
"<C-M-p>", end
"<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(vim.ivy.action.EDIT)<CR>", opts)
vim.api.nvim_buf_set_keymap(window.buffer, "n", "<C-v>", "<cmd>lua vim.ivy.complete(vim.ivy.action.VSPLIT)<CR>", opts)
vim.api.nvim_buf_set_keymap(window.buffer, "n", "<C-s>", "<cmd>lua vim.ivy.complete(vim.ivy.action.SPLIT)<CR>", opts)
vim.api.nvim_buf_set_keymap(window.buffer, "n", "<BS>", "<cmd>lua vim.ivy.input('BACKSPACE')<CR>", opts)
vim.api.nvim_buf_set_keymap(window.buffer, "n", "<Left>", "<cmd>lua vim.ivy.input('LEFT')<CR>", opts)
vim.api.nvim_buf_set_keymap(window.buffer, "n", "<Right>", "<cmd>lua vim.ivy.input('RIGHT')<CR>", opts)
vim.api.nvim_buf_set_keymap(window.buffer, "n", "<C-w>", "<cmd>lua vim.ivy.input('DELETE_WORD')<CR>", opts)
end end
window.get_current_selection = function() window.get_current_selection = function()
@ -106,15 +132,17 @@ window.set_items = function(items)
items = string_to_table(items) items = string_to_table(items)
end end
local items_length = get_items_length(items)
-- TODO(ade): Validate the items are in the correct format. This also need to -- TODO(ade): Validate the items are in the correct format. This also need to
-- come with some descriptive messages and possible help. -- come with some descriptive messages and possible help.
-- Display no items text if there are no items to dispaly -- Display no items text if there are no items to dispaly
if #items == 0 then if items_length == 0 then
items_length = 1
items = { { content = "-- No Items --" } } items = { { content = "-- No Items --" } }
end end
local items_length = #items
window.index = items_length - 1 window.index = items_length - 1
for index = 1, items_length do for index = 1, items_length do
@ -130,6 +158,8 @@ window.set_items = function(items)
vim.api.nvim_win_set_height(window.window, line_count) vim.api.nvim_win_set_height(window.window, line_count)
window.update() window.update()
call_gc(items)
end end
window.destroy = function() window.destroy = function()

32
lua/ivy/window_spec.lua Normal file
View 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)

View file

@ -1,23 +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)

View file

@ -1,34 +1,19 @@
local controller = require "ivy.controller" local api = require "ivy"
-- Put the controller in to the vim global so we can access it in mappings -- 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`. -- better without requires. You can call controller commands like `vim.ivy.xxx`.
-- luacheck: ignore -- luacheck: ignore
vim.ivy = controller vim.ivy = api
local register_backend = function(backend) vim.paste = (function(overridden)
assert(backend.command, "The backend must have a command") return function(lines, phase)
assert(backend.items, "The backend must have a items function") local file_type = vim.api.nvim_buf_get_option(0, "filetype")
assert(backend.callback, "The backend must have a callback function") if file_type == "ivy" then
vim.ivy.paste()
local user_command_options = { bang = true } else
if backend.description ~= nil then overridden(lines, phase)
user_command_options.desc = backend.description end
end end
end)(vim.paste)
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
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" vim.cmd "highlight IvyMatch cterm=bold gui=bold"

5
post-merge.sample Executable file
View file

@ -0,0 +1,5 @@
#! /bin/bash
# Automatically compile libivy.so when we pull down the repo.
# https://github.com/AdeAttwood/ivy.nvim#compiling
cargo build --release

6
renovate.json Normal file
View file

@ -0,0 +1,6 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:base"
]
}

View file

@ -10,18 +10,27 @@ pub fn find_files(options: Options) -> Vec<String> {
let base_path = &fs::canonicalize(options.directory).unwrap(); let base_path = &fs::canonicalize(options.directory).unwrap();
let mut builder = WalkBuilder::new(base_path); let mut builder = WalkBuilder::new(base_path);
// Search for hidden files and directories
builder.hidden(false); builder.hidden(false);
// Don't require a git repo to use .gitignore files. We want to use the .gitignore files
// wherever we are
builder.require_git(false);
// TODO(ade): Remove unwraps and find a good way to get the errors into the UI. Currently there // TODO(ade): Remove unwraps and find a good way to get the errors into the UI. Currently there
// is no way to handel errors in the rust library // is no way to handel errors in the rust library
let mut override_builder = OverrideBuilder::new(""); let mut override_builder = OverrideBuilder::new("");
override_builder.add("!.git").unwrap(); override_builder.add("!.git").unwrap();
override_builder.add("!.sl").unwrap();
let overrides = override_builder.build().unwrap(); let overrides = override_builder.build().unwrap();
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;

View file

@ -7,12 +7,38 @@ use std::ffi::CStr;
use std::ffi::CString; use std::ffi::CString;
use std::os::raw::{c_char, c_int}; use std::os::raw::{c_char, c_int};
use std::sync::Mutex; use std::sync::Mutex;
use std::sync::OnceLock;
#[macro_use] // A store to the singleton instance of the ivy struct. This must not be accessed directly it must
extern crate lazy_static; // be use via the Ivy::global() function. Accessing this directly may cause a panic if its been
// initialized correctly.
static INSTANCE: OnceLock<Mutex<Ivy>> = OnceLock::new();
lazy_static! { struct Ivy {
static ref GLOBAL_FILE_CACHE: Mutex<HashMap<String, Vec<String>>> = Mutex::new(HashMap::new()); // The file cache so we don't have to keep iterating the filesystem. The map key is the root
// directory that has been search and the value an a vector containing all of the files that as
// in the root. The value will be relative from the root.
pub file_cache: HashMap<String, Vec<String>>,
// The sequence number of the last iterator created. This will use as a pointer value to the
// iterator so we can access it though lua and rust without having to copy strings.
pub iter_sequence: i32,
// A store of all the iterators that have been created. The key is the sequence number and the
// value is the vector of matches that were matched in the search.
pub iter_map: HashMap<i32, Vec<CString>>,
}
impl Ivy {
// Get the global instance of the ivy struct. This will initialize the struct if it has not
// initialized yet.
pub fn global() -> &'static Mutex<Ivy> {
INSTANCE.get_or_init(|| {
Mutex::new(Ivy {
file_cache: HashMap::new(),
iter_sequence: 0,
iter_map: HashMap::new(),
})
})
}
} }
fn to_string(input: *const c_char) -> String { fn to_string(input: *const c_char) -> String {
@ -23,15 +49,17 @@ fn to_string(input: *const c_char) -> String {
} }
fn get_files(directory: &String) -> Vec<String> { fn get_files(directory: &String) -> Vec<String> {
let mut cache = GLOBAL_FILE_CACHE.lock().unwrap(); let mut ivy = Ivy::global().lock().unwrap();
if !cache.contains_key(directory) { if !ivy.file_cache.contains_key(directory) {
let finder_options = finder::Options { let finder_options = finder::Options {
directory: directory.clone(), directory: directory.clone(),
}; };
cache.insert(directory.clone(), finder::find_files(finder_options));
ivy.file_cache
.insert(directory.clone(), finder::find_files(finder_options));
} }
return cache.get(directory).unwrap().to_vec(); return ivy.file_cache.get(directory).unwrap().to_vec();
} }
#[no_mangle] #[no_mangle]
@ -61,6 +89,64 @@ pub fn inner_match(pattern: String, text: String) -> i32 {
m.score(text.as_str()) as i32 m.score(text.as_str()) as i32
} }
// Create a new iterator that will iterate over all the files in the given directory that match a
// pattern. It will return the pointer to the iterator so it can be retrieve later. The iterator
// can be deleted with `ivy_files_iter_delete`
#[no_mangle]
pub extern "C" fn ivy_files_iter(c_pattern: *const c_char, c_base_dir: *const c_char) -> i32 {
let directory = to_string(c_base_dir);
let pattern = to_string(c_pattern);
let files = get_files(&directory);
let mut ivy = Ivy::global().lock().unwrap();
// Convert the matches into CStrings so we can pass the pointers out while still maintaining
// ownership. If we didn't do this the CString would be dropped and the pointer would be freed
// while its being used externally.
let sorter_options = sorter::Options::new(pattern);
let matches = sorter::sort_strings(sorter_options, files)
.into_iter()
.map(|m| CString::new(m.content.as_str()).unwrap())
.collect::<Vec<CString>>();
ivy.iter_sequence += 1;
let new_sequence = ivy.iter_sequence;
ivy.iter_map.insert(new_sequence, matches);
new_sequence
}
// Delete the iterator with the given id. This will free the memory used by the iterator that was
// created with `ivy_files_iter`
#[no_mangle]
pub extern "C" fn ivy_files_iter_delete(iter_id: i32) {
let mut ivy = Ivy::global().lock().unwrap();
ivy.iter_map.remove(&iter_id);
}
// Returns the length of a given iterator. This will return the number of items that were matched
// when the iterator was created with `ivy_files_iter`
#[no_mangle]
pub extern "C" fn ivy_files_iter_len(iter_id: i32) -> i32 {
let ivy = Ivy::global().lock().unwrap();
let items = ivy.iter_map.get(&iter_id).unwrap();
items.len() as i32
}
// Returns the item at the given index in the iterator. This will return the full match that was
// given in the iterator. This will return a pointer to the string so it can be used in lua.
#[no_mangle]
pub extern "C" fn ivy_files_iter_at(iter_id: i32, index: i32) -> *const c_char {
let ivy = Ivy::global().lock().unwrap();
let items = ivy.iter_map.get(&iter_id).unwrap();
let item = items.get(index as usize).unwrap();
item.as_ptr()
}
#[no_mangle] #[no_mangle]
pub extern "C" fn ivy_files(c_pattern: *const c_char, c_base_dir: *const c_char) -> *const c_char { pub extern "C" fn ivy_files(c_pattern: *const c_char, c_base_dir: *const c_char) -> *const c_char {
let pattern = to_string(c_pattern); let pattern = to_string(c_pattern);

View file

@ -17,8 +17,7 @@ impl Matcher {
pub fn score(&self, text: &str) -> i64 { pub fn score(&self, text: &str) -> i64 {
self.matcher self.matcher
.fuzzy_indices(text, &self.pattern) .fuzzy_match(text, &self.pattern)
.map(|(score, _indices)| score)
.unwrap_or_default() .unwrap_or_default()
} }
} }

View file

@ -25,12 +25,19 @@ pub fn sort_strings(options: Options, strings: Vec<String>) -> Vec<Match> {
let mut matches = strings let mut matches = strings
.into_par_iter() .into_par_iter()
.map(|candidate| Match { .filter_map(|candidate| {
score: matcher.score(candidate.as_str()), let score = matcher.score(candidate.as_str());
content: candidate, if score < options.minimum_score {
None
} else {
Some(Match {
score,
content: candidate,
})
}
}) })
.filter(|m| m.score > options.minimum_score)
.collect::<Vec<Match>>(); .collect::<Vec<Match>>();
matches.par_sort_unstable_by(|a, b| a.score.cmp(&b.score)); matches.par_sort_unstable_by(|a, b| a.score.cmp(&b.score));
matches matches
} }

24
scripts/bench Executable file
View file

@ -0,0 +1,24 @@
#!/bin/bash
target_rev="$(git rev-parse $1)"
current_rev="$(git rev-parse HEAD)"
current_branch="$(git rev-parse --abbrev-ref HEAD)"
trap "git checkout "$current_branch"" EXIT
hyperfine \
--style full \
--warmup 3 \
--export-json /tmp/bench.json \
--parameter-list rev "${target_rev},${current_rev}" \
--prepare "git checkout {rev} && cargo build --release" -n '{rev}' 'luajit ./scripts/benchmark.lua'
old_value=$(cat /tmp/bench.json | jq '.results[0].mean')
new_value=$(cat /tmp/bench.json | jq '.results[1].mean')
percentage_difference=$(echo "scale=2; (($new_value - $old_value) / $old_value) * 100" | bc)
echo ""
echo "-------------------------------------"
echo "The percentage difference is $percentage_difference%"
echo "-------------------------------------"
echo ""

View file

@ -1,5 +1,7 @@
package.path = "lua/?.lua;" .. package.path package.path = "lua/?.lua;" .. package.path
local libivy = require "ivy.libivy" local libivy = require "ivy.libivy"
local vim_mock = require "ivy.vim_mock"
local window = require "ivy.window"
local benchmark = function(name, n, callback) local benchmark = function(name, n, callback)
local status = { local status = {
@ -25,7 +27,7 @@ local benchmark = function(name, n, callback)
print( print(
string.format( string.format(
"| %-30s | %09.6f (s) | %09.6f (s) | %09.6f (s) | %09.6f (s) |", "| %-41s | %09.6f (s) | %09.6f (s) | %09.6f (s) | %09.6f (s) |",
name, name,
status.running_total, status.running_total,
status.running_total / n, status.running_total / n,
@ -35,8 +37,8 @@ local benchmark = function(name, n, callback)
) )
end end
print "| Name | Total | Average | Min | Max |" print "| Name | Total | Average | Min | Max |"
print "|--------------------------------|---------------|---------------|---------------|---------------|" print "|-------------------------------------------|---------------|---------------|---------------|---------------|"
benchmark("ivy_match(file.lua) 1000000x", 1000000, function() benchmark("ivy_match(file.lua) 1000000x", 1000000, function()
libivy.ivy_match("file.lua", "some/long/path/to/file/file.lua") libivy.ivy_match("file.lua", "some/long/path/to/file/file.lua")
@ -46,3 +48,17 @@ libivy.ivy_init "/tmp/ivy-trees/kubernetes"
benchmark("ivy_files(kubernetes) 100x", 100, function() benchmark("ivy_files(kubernetes) 100x", 100, function()
libivy.ivy_files("file.go", "/tmp/ivy-trees/kubernetes") libivy.ivy_files("file.go", "/tmp/ivy-trees/kubernetes")
end) end)
-- Mock the vim API so we can run `vim.` functions. Override the
-- `nvim_buf_set_lines` function, this is so very slow. It saves all of the
-- lines so we can assert on them in the tests. For benchmarking we don't need
-- any of this, we can't control the vim internals.
vim_mock.reset()
_G.vim.api.nvim_buf_set_lines = function() end
window.initialize()
benchmark("ivy_files_with_set_items(kubernetes) 100x", 100, function()
local items = libivy.ivy_files(".go", "/tmp/ivy-trees/kubernetes")
window.set_items(items)
end)

8
scripts/busted.lua Executable file
View 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 }

30
scripts/integration.lua Normal file
View file

@ -0,0 +1,30 @@
vim.opt.rtp:append(vim.fn.getcwd())
local ivy = require "ivy"
local prompt = require "ivy.prompt"
require "plugin.ivy"
if #vim.v.argv ~= 5 then
print "[ERROR] Expected 5 arguments"
print " Usage: nvim -l ./scripts/integration.lua <directory> <search>"
return
end
ivy.setup()
vim.fn.chdir(vim.v.argv[4])
print("Working in " .. vim.fn.getcwd())
vim.cmd "IvyFd"
for _, value in pairs(vim.split(vim.v.argv[5], "")) do
local start_time = os.clock()
vim.ivy.input(value)
vim.wait(0)
local running_time = os.clock() - start_time
io.stdout:write(prompt.text() .. "\t" .. running_time .. "\n")
end