diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 2163ad4..7422053 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -1,70 +1,88 @@
name: CI
on:
push: { branches: ["0.x"] }
pull_request: { branches: ["0.x"] }
jobs:
luacheck:
name: Luacheck
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install luarocks
run: sudo apt update && sudo apt install -y luarocks
- name: Install luacheck
run: sudo luarocks install luacheck
- name: Run luacheck
run: luacheck .
stylua:
name: StyLua
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Run stylua
- uses: JohnnyMorganz/stylua-action@v3.0.0
+ uses: JohnnyMorganz/stylua-action@v4.0.0
with:
token: ${{ secrets.GITHUB_TOKEN }}
version: latest
args: --check .
cargo-format:
name: Cargo Format
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up rust
uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt
- name: Lint
run: cargo fmt --check
test:
name: Build and test
+ strategy:
+ matrix:
+ nvim-version: ["v0.9.5", "stable", "nightly"]
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up rust
uses: dtolnay/rust-toolchain@stable
- 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
run: cargo build --release
- - name: Test
- run: find lua -name "*_test.lua" | xargs luajit scripts/test.lua
+ - name: Run tests
+ 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
diff --git a/.luacheckrc b/.luacheckrc
index 337ac8e..2d46e75 100644
--- a/.luacheckrc
+++ b/.luacheckrc
@@ -1,18 +1,21 @@
-- Rerun tests only if their modification time changed.
cache = true
std = luajit
codes = true
self = false
-- Global objects defined by the C code
read_globals = {
"vim",
- "it",
"after",
"after_each",
+ "assert",
"before",
"before_each",
+ "describe",
+ "it",
+ "spy",
}
diff --git a/Cargo.lock b/Cargo.lock
index 60d34ec..17ec5d4 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1,753 +1,753 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "aho-corasick"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
dependencies = [
"memchr",
]
[[package]]
name = "anes"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
[[package]]
name = "anstyle"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d"
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bstr"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "542f33a8835a0884b006a0c3df3dadd99c0c3f296ed26c2fdc8028e01ad6230c"
dependencies = [
"memchr",
"serde",
]
[[package]]
name = "bumpalo"
version = "3.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d"
[[package]]
name = "cast"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
[[package]]
name = "cc"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "ciborium"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0c137568cc60b904a7724001b35ce2630fd00d5d84805fbb608ab89509d788f"
dependencies = [
"ciborium-io",
"ciborium-ll",
"serde",
]
[[package]]
name = "ciborium-io"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "346de753af073cc87b52b2083a506b38ac176a44cfb05497b622e27be899b369"
[[package]]
name = "ciborium-ll"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "213030a2b5a4e0c0892b6652260cf6ccac84827b83a85a534e178e3906c4cf1b"
dependencies = [
"ciborium-io",
"half",
]
[[package]]
name = "clap"
version = "4.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93aae7a4192245f70fe75dd9157fc7b4a5bf53e88d30bd4396f7d8f9284d5acc"
dependencies = [
"clap_builder",
]
[[package]]
name = "clap_builder"
version = "4.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f423e341edefb78c9caba2d9c7f7687d0e72e89df3ce3394554754393ac3990"
dependencies = [
"anstyle",
"bitflags",
"clap_lex",
]
[[package]]
name = "clap_lex"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b"
[[package]]
name = "criterion"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f"
dependencies = [
"anes",
"cast",
"ciborium",
"clap",
"criterion-plot",
"is-terminal",
"itertools",
"num-traits",
"once_cell",
"oorandom",
"plotters",
"rayon",
"regex",
"serde",
"serde_derive",
"serde_json",
"tinytemplate",
"walkdir",
]
[[package]]
name = "criterion-plot"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1"
dependencies = [
"cast",
"itertools",
]
[[package]]
name = "crossbeam-deque"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
dependencies = [
"cfg-if",
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "045ebe27666471bb549370b4b0b3e51b07f56325befa4284db65fc89c02511b1"
dependencies = [
"autocfg",
"cfg-if",
"crossbeam-utils",
"memoffset",
"once_cell",
"scopeguard",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc"
dependencies = [
"cfg-if",
"once_cell",
]
[[package]]
name = "either"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
[[package]]
name = "errno"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a"
dependencies = [
"errno-dragonfly",
"libc",
"windows-sys",
]
[[package]]
name = "errno-dragonfly"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
dependencies = [
"cc",
"libc",
]
[[package]]
name = "fuzzy-matcher"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54614a3312934d066701a80f20f15fa3b56d67ac7722b39eea5b4c9dd1d66c94"
dependencies = [
"thread_local",
]
[[package]]
name = "globset"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1"
dependencies = [
"aho-corasick",
"bstr",
"log",
"regex-automata",
"regex-syntax 0.8.2",
]
[[package]]
name = "half"
version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
[[package]]
name = "hermit-abi"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
[[package]]
name = "ignore"
version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b46810df39e66e925525d6e38ce1e7f6e1d208f72dc39757880fcb66e2c58af1"
dependencies = [
"crossbeam-deque",
"globset",
"log",
"memchr",
"regex-automata",
"same-file",
"walkdir",
"winapi-util",
]
[[package]]
name = "io-lifetimes"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
dependencies = [
"hermit-abi",
"libc",
"windows-sys",
]
[[package]]
name = "is-terminal"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f"
dependencies = [
"hermit-abi",
"io-lifetimes",
"rustix",
"windows-sys",
]
[[package]]
name = "itertools"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8bf247779e67a9082a4790b45e71ac7cfd1321331a5c856a74a9faebdab78d0"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754"
[[package]]
name = "ivy"
version = "0.0.1"
dependencies = [
"criterion",
"fuzzy-matcher",
"ignore",
"rayon",
]
[[package]]
name = "js-sys"
version = "0.3.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47"
dependencies = [
"wasm-bindgen",
]
[[package]]
name = "libc"
version = "0.2.144"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1"
[[package]]
name = "linux-raw-sys"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
[[package]]
name = "log"
version = "0.4.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
[[package]]
name = "memchr"
version = "2.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
[[package]]
name = "memoffset"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
dependencies = [
"autocfg",
]
[[package]]
name = "num-traits"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
dependencies = [
"autocfg",
]
[[package]]
name = "once_cell"
version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f7254b99e31cad77da24b08ebf628882739a608578bb1bcdfc1f9c21260d7c0"
[[package]]
name = "oorandom"
version = "11.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
[[package]]
name = "plotters"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2538b639e642295546c50fcd545198c9d64ee2a38620a628724a3b266d5fbf97"
dependencies = [
"num-traits",
"plotters-backend",
"plotters-svg",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "plotters-backend"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "193228616381fecdc1224c62e96946dfbc73ff4384fba576e052ff8c1bea8142"
[[package]]
name = "plotters-svg"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9a81d2759aae1dae668f783c308bc5c8ebd191ff4184aaa1b37f65a6ae5a56f"
dependencies = [
"plotters-backend",
]
[[package]]
name = "proc-macro2"
version = "1.0.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rayon"
-version = "1.8.1"
+version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa7237101a77a10773db45d62004a272517633fbcc3df19d96455ede1122e051"
+checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
dependencies = [
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
version = "1.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
dependencies = [
"crossbeam-deque",
"crossbeam-utils",
]
[[package]]
name = "regex"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b"
dependencies = [
"regex-syntax 0.6.27",
]
[[package]]
name = "regex-automata"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax 0.8.2",
]
[[package]]
name = "regex-syntax"
version = "0.6.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244"
[[package]]
name = "regex-syntax"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
[[package]]
name = "rustix"
version = "0.37.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acf8729d8542766f1b2cf77eb034d52f40d375bb8b615d0b147089946e16613d"
dependencies = [
"bitflags",
"errno",
"io-lifetimes",
"libc",
"linux-raw-sys",
"windows-sys",
]
[[package]]
name = "ryu"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
[[package]]
name = "same-file"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
dependencies = [
"winapi-util",
]
[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "serde"
version = "1.0.144"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.144"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94ed3a816fb1d101812f83e789f888322c34e291f894f19590dc310963e87a00"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "syn"
version = "1.0.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thread_local"
version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180"
dependencies = [
"once_cell",
]
[[package]]
name = "tinytemplate"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc"
dependencies = [
"serde",
"serde_json",
]
[[package]]
name = "unicode-ident"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd"
[[package]]
name = "walkdir"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee"
dependencies = [
"same-file",
"winapi-util",
]
[[package]]
name = "wasm-bindgen"
version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c"
dependencies = [
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
[[package]]
name = "web-sys"
version = "0.3.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f"
dependencies = [
"js-sys",
"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]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
[[package]]
name = "windows_i686_gnu"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
[[package]]
name = "windows_i686_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
diff --git a/Cargo.toml b/Cargo.toml
index cf70aca..d36ee36 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,31 +1,31 @@
[package]
name = "ivy"
version = "0.0.1"
edition = "2021"
[lib]
name = "ivyrs"
crate-type = ["cdylib", "rlib"]
path = "rust/lib.rs"
[dependencies]
ignore = "0.4.22"
fuzzy-matcher = "0.3.7"
-rayon = "1.8.1"
+rayon = "1.10.0"
[dev-dependencies]
criterion = "0.5.1"
[profile.release]
opt-level = 3
[profile.bench]
debug = true
[[bench]]
name = "ivy_match"
harness = false
[[bench]]
name = "ivy_files"
harness = false
diff --git a/README.md b/README.md
index e9e8d1e..f048609 100644
--- a/README.md
+++ b/README.md
@@ -1,190 +1,248 @@
-

+
An [ivy-mode](https://github.com/abo-abo/swiper#ivy) port to neovim. Ivy is a
generic completion mechanism for ~~Emacs~~ Nvim
## Installation
### Manually
```sh
git clone https://github.com/AdeAttwood/ivy.nvim ~/.config/nvim/pack/bundle/start/ivy.nvim
```
### Plugin managers
-TODO: Add docs in the plugin managers I don't use any
+Using [lazy.nvim](https://github.com/folke/lazy.nvim)
+```lua
+{
+ "AdeAttwood/ivy.nvim",
+ build = "cargo build --release",
+},
+```
+
+TODO: Add more plugin managers
+
+### Setup / Configuration
+
+Ivy can be configured with minimal config that will give you all the defaults
+provided by Ivy.
+
+```lua
+require('ivy').setup()
+```
+
+With Ivy you can configure your own backends.
+
+```lua
+require('ivy').setup {
+ backends = {
+ -- A backend module that will be registered
+ "ivy.backends.buffers",
+ -- Using a table so you can configure a custom keymap overriding the
+ -- default one.
+ { "ivy.backends.files", { keymap = "" } }
+ },
+}
+```
+
+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
For the native searching, you will need to compile the shard library. You can
do that by running the below command in the root of the plugin.
```sh
cargo build --release
```
You will need to have the rust toolchain installed. You can find more about
that [here](https://www.rust-lang.org/tools/install)
If you get a linker error you may need to install `build-essential` to get
`ld`. This is a common issue if you are running the [benchmarks](#benchmarks)
in a VM
```
error: linker `cc` not found
|
= 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
-### 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 |
-| ------------------ | ----------- | ----------------------------------------------------------- |
-| IvyFd | \p | Find files in your project with a custom rust file finder |
-| IvyAg | \/ | Find content in files using the silver searcher |
-| IvyBuffers | \b | Search though open buffers |
-| IvyLines | | Search the lines in the current buffer |
-| IvyWorkspaceSymbol | | Search for workspace symbols using the lsp workspace/symbol |
+| Module | Command | Key Map | Description |
+| ------------------------------------ | ------------------ | ----------- | ----------------------------------------------------------- |
+| `ivy.backends.files` | IvyFd | \p | Find files in your project with a custom rust file finder |
+| `ivy.backends.ag` | IvyAg | \/ | Find content in files using the silver searcher |
+| `ivy.backends.rg` | IvyRg | \/ | Find content in files using ripgrep cli tool |
+| `ivy.backends.buffers` | IvyBuffers | \b | Search though open buffers |
+| `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
Action can be run on selected candidates provide functionality
-| Action | Description |
-| -------------- | ------------------------------------------------------------------------------ |
-| Complete | Run the completion function, usually this will be opening a file |
-| Peek | Run the completion function on a selection, but don't close the results window |
-| Vertical Split | Run the completion function in a new vertical split |
-| Split | Run the completion function in a new split |
+| Action | Key Map | Description |
+| -------------- | ----------- | ------------------------------------------------------------------------------ |
+| Complete | \ |Run the completion function, usually this will be opening a file |
+| Vertical Split | \ |Run the completion function in a new vertical split |
+| Split | \ |Run the completion function in a new split |
+| Destroy | \ |Close the results window |
+| Clear | \ |Clear the results window |
+| Delete word | \ |Delete the word under the cursor |
+| Next | \ |Move to the next candidate |
+| Previous | \ |Move to the previous candidate |
+| Next Checkpoint| \ |Move to the next candidate and keep Ivy open and focussed |
+| Previous Checkpoint| \|Move to the previous candidate and keep Ivy open and focussed |
+
+Add your own keymaps for an action by adding a `ftplugin/ivy.lua` file in your config.
+Just add a simple keymap like this:
+
+```lua
+vim.api.nvim_set_keymap( "n", "", "lua vim.ivy.destroy()", { noremap = true, silent = true, nowait = true })
+```
+
## API
### ivy.run
The `ivy.run` function is the core function in the plugin, it will launch the
completion window and display the items from your items function. When the
users accept one of the candidates with an [action](#actions), it will call the
callback function to in most cases open the item in the desired location.
```lua
---@param name string
---@param items fun(input: string): { content: string }[] | string
---@param callback fun(result: string, action: string)
vim.ivy.run = function(name, items, callback) end
```
#### Name `string`
The name is the display name for the command and will be the name of the buffer
in the completion window
#### Items `fun(input: string): { content: string }[] | string`
The items function is a function that will return the candidates to display in
the completion window. This can return a string where each line will be a
completion item. Or an array of tables where the `content` will be the
completion item.
#### Callback `fun(result: string, action: string)`
The function that will run when the user selects a completion item. Generally
this will open the item in the desired location. For example, in the file
finder with will open the file in a new buffer. If the user selects the
vertical split action it will open the buffer in a new `vsplit`
#### Example
```lua
vim.ivy.run(
-- The name given to the results window and displayed to the user
"Title",
-- Call back function to get all the candidates that will be displayed in
-- the results window, The `input` will be passed in, so you can filter
-- your results with the value from the prompt
function(input)
return {
{ content = "One" },
{ content = "Two" },
{ content = "Three" },
}
end,
- -- Action callback that will be called on the completion or peek actions.
+ -- Action callback that will be called on the completion or checkpoint actions.
-- The currently selected item is passed in as the result.
function(result) vim.cmd("edit " .. result) end
)
```
## Benchmarks
Benchmarks are of various tasks that ivy will do. The purpose of the benchmarks
are to give us a baseline on where to start when trying to optimize performance
in the matching and sorting, not to put ivy against other tools. When starting
to optimize, you will probably need to get a baseline on your hardware.
There are fixtures provided that will create the directory structure of the
[kubernetes](https://github.com/kubernetes/kubernetes) source code, from
somewhere around commit sha 985c9202ccd250a5fe22c01faf0d8f83d804b9f3. This will
create a directory tree of 23511 files a relative large source tree to get a
good idea of performance. To create the source tree under
`/tmp/ivy-trees/kubernetes` run the following command. This will need to be run
for the benchmarks to run.
```bash
# Create the source trees
bash ./scripts/fixtures.bash
# Run the benchmark script
luajit ./scripts/benchmark.lua
```
Current benchmark status running on a `e2-standard-2` 2 vCPU + 8 GB memory VM
running on GCP.
IvyRs (Lua)
| Name | Total | Average | Min | Max |
| ---------------------------- | ------------- | ------------- | ------------- | ------------- |
| ivy_match(file.lua) 1000000x | 04.153531 (s) | 00.000004 (s) | 00.000003 (s) | 00.002429 (s) |
| ivy_files(kubernetes) 100x | 03.526795 (s) | 00.035268 (s) | 00.021557 (s) | 00.037127 (s) |
IvyRs (Criterion)
| Name | Min | Mean | Max |
| --------------------- | --------- | --------- | --------- |
| ivy_files(kubernetes) | 19.727 ms | 19.784 ms | 19.842 ms |
| ivy_match(file.lua) | 2.6772 µs | 2.6822 µs | 2.6873 µs |
CPP
| Name | Total | Average | Min | Max |
| ---------------------------- | ------------- | ------------- | ------------- | ------------- |
| ivy_match(file.lua) 1000000x | 01.855197 (s) | 00.000002 (s) | 00.000001 (s) | 00.000177 (s) |
| ivy_files(kubernetes) 100x | 14.696396 (s) | 00.146964 (s) | 00.056604 (s) | 00.168478 (s) |
## Other stuff you might like
- [ivy-mode](https://github.com/abo-abo/swiper#ivy) - An emacs package that was the inspiration for this nvim plugin
- [Command-T](https://github.com/wincent/command-t) - Vim plugin I used before I started this one
- [telescope.nvim](https://github.com/nvim-telescope/telescope.nvim) - Another competition plugin, lots of people are using
diff --git a/lua/ivy/config.lua b/lua/ivy/config.lua
new file mode 100644
index 0000000..8ff1e6a
--- /dev/null
+++ b/lua/ivy/config.lua
@@ -0,0 +1,33 @@
+local config_mt = {}
+config_mt.__index = config_mt
+
+function config_mt:get_in(config, key_table)
+ local current_value = config
+ for _, key in ipairs(key_table) do
+ if current_value == nil then
+ return nil
+ end
+
+ current_value = current_value[key]
+ end
+
+ return current_value
+end
+
+function config_mt:get(key_table)
+ return self:get_in(self.user_config, key_table) or self:get_in(self.default_config, key_table)
+end
+
+local config = { user_config = {} }
+
+config.default_config = {
+ backends = {
+ "ivy.backends.buffers",
+ "ivy.backends.files",
+ "ivy.backends.lines",
+ "ivy.backends.rg",
+ "ivy.backends.lsp-workspace-symbols",
+ },
+}
+
+return setmetatable(config, config_mt)
diff --git a/lua/ivy/config_spec.lua b/lua/ivy/config_spec.lua
new file mode 100644
index 0000000..7ef8cd8
--- /dev/null
+++ b/lua/ivy/config_spec.lua
@@ -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)
diff --git a/lua/ivy/controller_spec.lua b/lua/ivy/controller_spec.lua
new file mode 100644
index 0000000..6556cee
--- /dev/null
+++ b/lua/ivy/controller_spec.lua
@@ -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)
diff --git a/lua/ivy/controller_test.lua b/lua/ivy/controller_test.lua
deleted file mode 100644
index 9865142..0000000
--- a/lua/ivy/controller_test.lua
+++ /dev/null
@@ -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)
diff --git a/lua/ivy/init.lua b/lua/ivy/init.lua
new file mode 100644
index 0000000..58672ff
--- /dev/null
+++ b/lua/ivy/init.lua
@@ -0,0 +1,32 @@
+local controller = require "ivy.controller"
+local config = require "ivy.config"
+local register_backend = require "ivy.register_backend"
+
+local ivy = {}
+ivy.run = controller.run
+ivy.register_backend = register_backend
+
+-- Private variable to check if ivy has been setup, this is to prevent multiple
+-- setups of ivy. This is only exposed for testing purposes.
+---@private
+ivy.has_setup = false
+
+---@class IvySetupOptions
+---@field backends (IvyBackend | { ["1"]: string, ["2"]: IvyBackendOptions} | string)[]
+
+---@param user_config IvySetupOptions
+function ivy.setup(user_config)
+ if ivy.has_setup then
+ return
+ end
+
+ config.user_config = user_config or {}
+
+ for _, backend in ipairs(config:get { "backends" } or {}) do
+ register_backend(backend)
+ end
+
+ ivy.has_setup = true
+end
+
+return ivy
diff --git a/lua/ivy/init_spec.lua b/lua/ivy/init_spec.lua
new file mode 100644
index 0000000..f8ea91b
--- /dev/null
+++ b/lua/ivy/init_spec.lua
@@ -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)
diff --git a/lua/ivy/libivy_spec.lua b/lua/ivy/libivy_spec.lua
new file mode 100644
index 0000000..8faf503
--- /dev/null
+++ b/lua/ivy/libivy_spec.lua
@@ -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)
diff --git a/lua/ivy/libivy_test.lua b/lua/ivy/libivy_test.lua
deleted file mode 100644
index fe18455..0000000
--- a/lua/ivy/libivy_test.lua
+++ /dev/null
@@ -1,46 +0,0 @@
-local libivy = require "ivy.libivy"
-
-it("should run a simple match", function(t)
- local score = libivy.ivy_match("term", "I am a serch term")
-
- if score <= 0 then
- t.error("Score should not be less than 0 found " .. score)
- end
-end)
-
-it("should find a dot file", function(t)
- local current_dir = libivy.ivy_cwd()
- local results = libivy.ivy_files(".github/workflows/ci.yml", current_dir)
-
- if results.length ~= 2 then
- t.error("Incorrect number of results found " .. results.length)
- end
-
- if results[2].content ~= ".github/workflows/ci.yml" then
- t.error("Invalid matches: " .. results[2].content)
- end
-end)
-
-it("will allow you to access the length via the metatable", function(t)
- local current_dir = libivy.ivy_cwd()
- local results = libivy.ivy_files(".github/workflows/ci.yml", current_dir)
-
- local mt = getmetatable(results)
-
- if results.length ~= mt.__len(results) then
- t.error "The `length` property does not match the __len metamethod"
- end
-end)
-
-it("will create an iterator", function(t)
- local iter = libivy.ivy_files(".github/workflows/ci.yml", libivy.ivy_cwd())
- local mt = getmetatable(iter)
-
- if type(mt["__index"]) ~= "function" then
- t.error "The iterator does not have an __index metamethod"
- end
-
- if type(mt["__len"]) ~= "function" then
- t.error "The iterator does not have an __len metamethod"
- end
-end)
diff --git a/lua/ivy/matcher_spec.lua b/lua/ivy/matcher_spec.lua
new file mode 100644
index 0000000..7128a2c
--- /dev/null
+++ b/lua/ivy/matcher_spec.lua
@@ -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)
diff --git a/lua/ivy/matcher_test.lua b/lua/ivy/matcher_test.lua
deleted file mode 100644
index f80f565..0000000
--- a/lua/ivy/matcher_test.lua
+++ /dev/null
@@ -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)
diff --git a/lua/ivy/prompt_spec.lua b/lua/ivy/prompt_spec.lua
new file mode 100644
index 0000000..452a091
--- /dev/null
+++ b/lua/ivy/prompt_spec.lua
@@ -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)
diff --git a/lua/ivy/prompt_test.lua b/lua/ivy/prompt_test.lua
deleted file mode 100644
index ee834b6..0000000
--- a/lua/ivy/prompt_test.lua
+++ /dev/null
@@ -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
diff --git a/lua/ivy/register_backend.lua b/lua/ivy/register_backend.lua
new file mode 100644
index 0000000..f314588
--- /dev/null
+++ b/lua/ivy/register_backend.lua
@@ -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, "" .. backend.command .. "", { 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
diff --git a/lua/ivy/register_backend_spec.lua b/lua/ivy/register_backend_spec.lua
new file mode 100644
index 0000000..ab51e4e
--- /dev/null
+++ b/lua/ivy/register_backend_spec.lua
@@ -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", "IvyFd")
+ 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", "IvyFd")
+ assert.is_not_nil(keymap)
+ end)
+
+ it("allows you to override the keymap", function()
+ register_backend("ivy.backends.files", { keymap = "" })
+
+ local keymap = get_keymap("n", "IvyFd")
+ assert(keymap ~= nil)
+ assert.are.equal("", 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", "IvyFd")
+ assert.is_not_nil(keymap)
+ end)
+
+ it("allows you to pass in a hole backend module", function()
+ register_backend { "ivy.backends.files", { keymap = "" } }
+
+ local keymap = get_keymap("n", "IvyFd")
+ assert(keymap ~= nil)
+ assert.are.equal("", keymap.lhs)
+ end)
+end)
diff --git a/lua/ivy/utils.lua b/lua/ivy/utils.lua
index 910e57f..40489a8 100644
--- a/lua/ivy/utils.lua
+++ b/lua/ivy/utils.lua
@@ -1,111 +1,113 @@
local utils = {}
-- A list of all of the actions defined by ivy. The callback function can
-- implement as many of them as necessary. As a minimum it should implement the
-- "EDIT" action that is called on the default complete.
utils.actions = {
EDIT = "EDIT",
CHECKPOINT = "CHECKPOINT",
VSPLIT = "VSPLIT",
SPLIT = "SPLIT",
}
utils.command_map = {
[utils.actions.EDIT] = "edit",
[utils.actions.CHECKPOINT] = "edit",
[utils.actions.VSPLIT] = "vsplit",
[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)
if min == nil then
min = 3
end
return function(input)
-- Dont run the commands unless we have somting to search that wont
-- return a ton of results or on some commands the command files with
-- no search term
if #input < min then
return "-- Please type more than " .. min .. " chars --"
end
-- TODO(ade): Think if we want to start escaping the command here. I
-- dont know if its causing issues while trying to use regex especially
-- with word boundaries `input:gsub("'", "\\'"):gsub('"', '\\"')`
local handle = io.popen(command .. " " .. input .. " 2>&1 || true")
if handle == nil then
return {}
end
local results = {}
for line in handle:lines() do
table.insert(results, { content = line })
end
handle:close()
return results
end
end
utils.vimgrep_action = function()
return function(item, action)
-- Match file and line form vimgrep style commands
local file = item:match "([^:]+):"
local line = item:match ":(%d+):"
-- Cant do anything if we cant find a file to go to
if file == nil then
return
end
utils.file_action()(file, action)
if line ~= nil then
vim.cmd(line)
end
end
end
utils.file_action = function()
return function(file, action)
if file == nil then
return
end
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
vim.api.nvim_err_writeln("[IVY] The file action is unable the handel the action " .. action)
return
end
vim.cmd(command .. " " .. utils.escape_file_name(file))
end
end
utils.line_action = function()
return function(item)
local line = item:match "^%s+(%d+):"
- vim.cmd(line)
+ if line ~= nil then
+ vim.cmd(line)
+ end
end
end
utils.escape_file_name = function(input)
local file, _ = string.gsub(input, "([$%]\\[])", "\\%1")
return file
end
return utils
diff --git a/lua/ivy/utils_escape_spec.lua b/lua/ivy/utils_escape_spec.lua
new file mode 100644
index 0000000..7a47331
--- /dev/null
+++ b/lua/ivy/utils_escape_spec.lua
@@ -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)
diff --git a/lua/ivy/utils_line_action_spec.lua b/lua/ivy/utils_line_action_spec.lua
new file mode 100644
index 0000000..8e4484e
--- /dev/null
+++ b/lua/ivy/utils_line_action_spec.lua
@@ -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)
diff --git a/lua/ivy/utils_line_action_test.lua b/lua/ivy/utils_line_action_test.lua
deleted file mode 100644
index f68810e..0000000
--- a/lua/ivy/utils_line_action_test.lua
+++ /dev/null
@@ -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)
diff --git a/lua/ivy/utils_vimgrep_action_test.lua b/lua/ivy/utils_vimgrep_action_spec.lua
similarity index 50%
rename from lua/ivy/utils_vimgrep_action_test.lua
rename to lua/ivy/utils_vimgrep_action_spec.lua
index 0c08c09..51cb55d 100644
--- a/lua/ivy/utils_vimgrep_action_test.lua
+++ b/lua/ivy/utils_vimgrep_action_spec.lua
@@ -1,56 +1,56 @@
local utils = require "ivy.utils"
local vimgrep_action = utils.vimgrep_action()
-local vim_mock = require "ivy.vim_mock"
-
-before_each(function()
- vim_mock.reset()
-end)
local test_data = {
{
it = "will edit some file and goto the line",
completion = "some/file.lua:2: This is some text",
action = utils.actions.EDIT,
commands = {
"edit some/file.lua",
"2",
},
},
{
it = "will skip the line if its not matched",
completion = "some/file.lua: This is some text",
action = utils.actions.EDIT,
- commands = { "edit some/file.lua" },
+ commands = { "buffer some/file.lua" },
},
{
it = "will run the vsplit command",
completion = "some/file.lua: This is some text",
action = utils.actions.VSPLIT,
- commands = { "vsplit some/file.lua" },
+ commands = { "vsplit | buffer some/file.lua" },
},
{
it = "will run the split command",
completion = "some/file.lua: This is some text",
action = utils.actions.SPLIT,
- commands = { "split some/file.lua" },
+ commands = { "split | buffer some/file.lua" },
},
}
-for i = 1, #test_data do
- local data = test_data[i]
- it(data.it, function(t)
- vimgrep_action(data.completion, data.action)
+describe("utils vimgrep_action", function()
+ before_each(function()
+ spy.on(vim, "cmd")
+ end)
+
+ after_each(function()
+ vim.cmd:revert()
+ end)
+
+ for i = 1, #test_data do
+ 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")
- if #vim_mock.commands ~= #data.commands then
- t.error("Incorrect number of commands run expected " .. #data.commands .. " but found " .. #vim_mock.commands)
- end
+ 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
- if vim_mock.commands[j] ~= data.commands[j] then
- t.error(
- "Incorrect command run expected '" .. data.commands[j] .. "' but found '" .. vim_mock.commands[j] .. "'"
- )
+ for j = 1, #data.commands do
+ assert.spy(vim.cmd).was_called_with(data.commands[j])
end
- end
- end)
-end
+ end)
+ end
+end)
diff --git a/lua/ivy/window_spec.lua b/lua/ivy/window_spec.lua
new file mode 100644
index 0000000..873504a
--- /dev/null
+++ b/lua/ivy/window_spec.lua
@@ -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)
diff --git a/lua/ivy/window_test.lua b/lua/ivy/window_test.lua
deleted file mode 100644
index dcad637..0000000
--- a/lua/ivy/window_test.lua
+++ /dev/null
@@ -1,33 +0,0 @@
-local vim_mock = require "ivy.vim_mock"
-local window = require "ivy.window"
-
-before_each(function()
- vim_mock.reset()
-end)
-
-it("can initialize and destroy the window", function(t)
- window.initialize()
-
- t.assert_equal(10, window.get_buffer())
- t.assert_equal(10, window.buffer)
-
- window.destroy()
- t.assert_equal(nil, window.buffer)
-end)
-
-it("can set items", function(t)
- window.initialize()
-
- window.set_items { { content = "Line one" } }
- t.assert_equal("Line one", window.get_current_selection())
-end)
-
-it("will set the items when a string is passed in", function(t)
- window.initialize()
-
- local items = table.concat({ "One", "Two", "Three" }, "\n")
- window.set_items(items)
-
- local lines = table.concat(vim_mock.get_lines()[window.buffer], "\n")
- t.assert_equal(items, lines)
-end)
diff --git a/plugin/ivy.lua b/plugin/ivy.lua
index 383e866..cab4b9a 100644
--- a/plugin/ivy.lua
+++ b/plugin/ivy.lua
@@ -1,50 +1,19 @@
local controller = require "ivy.controller"
-- Put the controller in to the vim global so we can access it in mappings
-- better without requires. You can call controller commands like `vim.ivy.xxx`.
-- luacheck: ignore
vim.ivy = controller
-local register_backend = function(backend)
- assert(backend.command, "The backend must have a command")
- assert(backend.items, "The backend must have a items function")
- assert(backend.callback, "The backend must have a callback function")
-
- local user_command_options = { bang = true }
- if backend.description ~= nil then
- user_command_options.desc = backend.description
- end
-
- local name = backend.name or backend.command
- vim.api.nvim_create_user_command(backend.command, function()
- vim.ivy.run(name, backend.items, backend.callback)
- end, user_command_options)
-
- if backend.keymap ~= nil then
- vim.api.nvim_set_keymap("n", backend.keymap, "" .. backend.command .. "", { nowait = true, silent = true })
- end
-end
-
vim.paste = (function(overridden)
return function(lines, phase)
local file_type = vim.api.nvim_buf_get_option(0, "filetype")
if file_type == "ivy" then
vim.ivy.paste()
else
overridden(lines, phase)
end
end
end)(vim.paste)
-register_backend(require "ivy.backends.buffers")
-register_backend(require "ivy.backends.files")
-register_backend(require "ivy.backends.lines")
-register_backend(require "ivy.backends.lsp-workspace-symbols")
-
-if vim.fn.executable "rg" then
- register_backend(require "ivy.backends.rg")
-elseif vim.fn.executable "ag" then
- register_backend(require "ivy.backends.ag")
-end
-
vim.cmd "highlight IvyMatch cterm=bold gui=bold"
diff --git a/rust/finder.rs b/rust/finder.rs
index f3baa97..37eff2a 100644
--- a/rust/finder.rs
+++ b/rust/finder.rs
@@ -1,39 +1,43 @@
use ignore::{overrides::OverrideBuilder, WalkBuilder};
use std::fs;
pub struct Options {
pub directory: String,
}
pub fn find_files(options: Options) -> Vec {
let mut files: Vec = Vec::new();
let base_path = &fs::canonicalize(options.directory).unwrap();
let mut builder = WalkBuilder::new(base_path);
// Search for hidden files and directories
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
// is no way to handel errors in the rust library
let mut override_builder = OverrideBuilder::new("");
override_builder.add("!.git").unwrap();
override_builder.add("!.sl").unwrap();
let overrides = override_builder.build().unwrap();
builder.overrides(overrides);
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();
if candidate_path.is_dir() {
continue;
}
files.push(candidate_path.to_str().unwrap().to_string());
}
files
}
diff --git a/scripts/busted.lua b/scripts/busted.lua
new file mode 100755
index 0000000..753c0e6
--- /dev/null
+++ b/scripts/busted.lua
@@ -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 }