Compare commits
71 commits
issue-temp
...
0.x
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
67ca10505d | ||
| 16a791e8a6 | |||
| 0f364c140d | |||
| 6e45d75a1e | |||
|
|
cfa7e29ae4 | ||
| ded926a4a6 | |||
| f910955ad1 | |||
|
|
7efb98baab | ||
|
|
f3585c8cde | ||
| fef45c2d7e | |||
| 57d652cb9d | |||
| e121ba7a9f | |||
| 9af13d0031 | |||
| 72f3fcac4e | |||
| a2287f1179 | |||
|
|
611aa54902 | ||
|
|
d19bdc4dfd | ||
| d6d782b584 | |||
| f4d9b67370 | |||
| 91b6db9d76 | |||
| 4803045d4e | |||
| cb4f5860ef | |||
| 37cad71291 | |||
|
|
d7b368355f | ||
|
|
829bb008b8 | ||
|
|
6ffdb7795a | ||
| cf3e7e3044 | |||
|
|
6dd5323380 | ||
| c32698cf25 | |||
|
|
e5f00bfb1e | ||
| 7ecc6f1226 | |||
|
|
f48e712a4f | ||
| 342fb8f301 | |||
| 2429fe725c | |||
|
|
6a57a15e4b | ||
| 9f9e4a2023 | |||
| d707a6e15b | |||
|
|
421c9b5999 | ||
|
|
675521cc19 | ||
| c562190829 | |||
| 6af2b5011b | |||
|
|
1c412bffa9 | ||
| 5a0f037b71 | |||
| 0bd6770da4 | |||
|
|
de7f9a2292 | ||
| 100723f141 | |||
| 027819e15f | |||
|
|
92aa2c3433 | ||
| 56dced41e0 | |||
| 2a72162150 | |||
| 9017b2c322 | |||
| 10a3117aae | |||
| ff7c28490d | |||
| 149d12e824 | |||
|
|
4e2c2381ef | ||
|
|
e553517f7a | ||
| 2032d0748b | |||
| 4dd8927ea2 | |||
| a69d84dd25 | |||
| 3a21d8e2b0 | |||
| 04d04fc96a | |||
| 51a72513f4 | |||
| 45068e759d | |||
| 43d7e11c8b | |||
|
|
0d528d3b1c | ||
| 4dace131b8 | |||
|
|
877adb42b4 | ||
| b57abf3787 | |||
| 1af1f0e252 | |||
| 3d8d55a146 | |||
|
|
8b75c9b536 |
65 changed files with 1655 additions and 1549 deletions
|
|
@ -1,3 +0,0 @@
|
|||
Language: Cpp
|
||||
BasedOnStyle: Google
|
||||
ColumnLimit: 0
|
||||
20
.clang-tidy
20
.clang-tidy
|
|
@ -1,20 +0,0 @@
|
|||
Checks: '
|
||||
-*,
|
||||
google-*,
|
||||
-google-runtime-references,
|
||||
-google-readability-avoid-underscore-in-googletest-name,
|
||||
llvm-include-order,
|
||||
llvm-namespace-comment,
|
||||
misc-throw-by-value-catch-by-reference,
|
||||
modernize*,
|
||||
-modernize-use-trailing-return-type,
|
||||
readability-container-size-empty,
|
||||
'
|
||||
|
||||
WarningsAsErrors: '*'
|
||||
|
||||
HeaderFilterRegex: './src/**/*'
|
||||
|
||||
CheckOptions:
|
||||
- key: google-readability-braces-around-statements.ShortStatementLines
|
||||
value: '3'
|
||||
24
.github/ISSUE_TEMPLATE/BUG_REPORT.md
vendored
Normal file
24
.github/ISSUE_TEMPLATE/BUG_REPORT.md
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
---
|
||||
name: "\U0001F41B Bug report"
|
||||
about: Create a report to help us improve
|
||||
labels: 'bug'
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- Please add a detailed description of the issue you are having -->
|
||||
|
||||
## Expected Behaviour
|
||||
|
||||
<!-- Please describe the way you think this should work -->
|
||||
|
||||
## Actual Behaviour
|
||||
|
||||
<!-- Please describe the way it is currently working -->
|
||||
|
||||
## Steps to Reproduce the Problem
|
||||
|
||||
<!-- Please add all the steps you need to take to reproduce the issue -->
|
||||
|
||||
1)
|
||||
2)
|
||||
33
.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md
vendored
Normal file
33
.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
---
|
||||
name: "\U0001F680 Feature request"
|
||||
about: Suggest an idea for this project
|
||||
labels: 'enhancement'
|
||||
---
|
||||
|
||||
|
||||
## Problem to solve
|
||||
|
||||
<!-- What is the user problem you are trying to solve with this issue? -->
|
||||
|
||||
## Proposal
|
||||
|
||||
<!--
|
||||
|
||||
Use this section to explain the feature and how it will work. It can be helpful
|
||||
to add technical details and design proposals.
|
||||
|
||||
-->
|
||||
|
||||
## Further details
|
||||
|
||||
<!--
|
||||
|
||||
Include use cases, benefits, goals, or any other details that will help us
|
||||
understand the problem better.
|
||||
|
||||
-->
|
||||
|
||||
## Links / References
|
||||
|
||||
<!-- Add any links or references to support this request. -->
|
||||
|
||||
20
.github/dependabot.yml
vendored
20
.github/dependabot.yml
vendored
|
|
@ -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'
|
||||
81
.github/workflows/ci.yml
vendored
81
.github/workflows/ci.yml
vendored
|
|
@ -5,26 +5,12 @@ on:
|
|||
pull_request: { branches: ["0.x"] }
|
||||
|
||||
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:
|
||||
name: Luacheck
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install luarocks
|
||||
run: sudo apt update && sudo apt install -y luarocks
|
||||
|
|
@ -40,58 +26,59 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Run stylua
|
||||
uses: JohnnyMorganz/stylua-action@v2.0.0
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
version: latest
|
||||
args: --check .
|
||||
|
||||
clang-format:
|
||||
name: Clang Format
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install Dependencies
|
||||
run: sudo apt-get update && sudo apt-get install --no-install-recommends -y clang-format findutils
|
||||
|
||||
- name: Run clang format
|
||||
run: find ./cpp -name "*.cpp" -o -name "*.hpp" | xargs clang-format -Werror --dry-run
|
||||
run: npx @johnnymorganz/stylua-bin --check .
|
||||
|
||||
cargo-format:
|
||||
name: Cargo Format
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Run cargo format
|
||||
uses: actions-rs/cargo@v1
|
||||
- name: Set up rust
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
command: fmt
|
||||
args: --all -- --check
|
||||
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@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install rust toolchain
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
- 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
|
||||
|
|
|
|||
21
.github/workflows/ct-commitlint.yml
vendored
Normal file
21
.github/workflows/ct-commitlint.yml
vendored
Normal 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
|
||||
|
|
@ -10,9 +10,12 @@ self = false
|
|||
read_globals = {
|
||||
"vim",
|
||||
|
||||
"it",
|
||||
"after",
|
||||
"after_each",
|
||||
"assert",
|
||||
"before",
|
||||
"before_each",
|
||||
"describe",
|
||||
"it",
|
||||
"spy",
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,60 +0,0 @@
|
|||
cmake_minimum_required(VERSION 3.16)
|
||||
set(PROJECT_VERSION_NAME "v0.0.1")
|
||||
|
||||
# Split and sanatize the project version so it can be uses as pars and used as
|
||||
# the project version "v1.1.1" is not a valida version number
|
||||
string(REPLACE "v" "" PROJECT_VERSION ${PROJECT_VERSION_NAME})
|
||||
string(REPLACE "." ";" VERSION_LIST ${PROJECT_VERSION})
|
||||
list(GET VERSION_LIST 0 PROJECT_VERSION_MAJOR)
|
||||
list(GET VERSION_LIST 1 PROJECT_VERSION_MINOR)
|
||||
list(GET VERSION_LIST 2 PROJECT_VERSION_PATCH)
|
||||
|
||||
project ("Ivy" VERSION ${PROJECT_VERSION})
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
# Set the build type if its not test
|
||||
if(NOT CMAKE_BUILD_TYPE)
|
||||
set(CMAKE_BUILD_TYPE "Release")
|
||||
endif()
|
||||
|
||||
# Ensure the build type is valid
|
||||
if(NOT "${CMAKE_BUILD_TYPE}" STREQUAL "Debug" AND
|
||||
NOT "${CMAKE_BUILD_TYPE}" STREQUAL "Release" AND
|
||||
NOT "${CMAKE_BUILD_TYPE}" STREQUAL "MinSizeRel" AND
|
||||
NOT "${CMAKE_BUILD_TYPE}" STREQUAL "RelWithDebInfo")
|
||||
message(FATAL_ERROR "Unknown build type \"${CMAKE_BUILD_TYPE}\". Allowed values are Debug, Release, RelWithDebInfo, and MinSizeRel.")
|
||||
endif()
|
||||
|
||||
# detect operating system and host processor
|
||||
message(STATUS "We are on a ${CMAKE_SYSTEM_NAME} system")
|
||||
message(STATUS "The host processor is ${CMAKE_HOST_SYSTEM_PROCESSOR}")
|
||||
|
||||
# Place binaries and libraries according to GNU standards. For example
|
||||
# executables created with `add_executable` will be built into the `bin`
|
||||
# directory
|
||||
include(GNUInstallDirs)
|
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR})
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR})
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_BINDIR})
|
||||
|
||||
# Set the default compiler flags for GNU
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES GNU)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wunreachable-code -Wno-unknown-pragmas -Wno-sign-compare -Wwrite-strings -Wno-unused")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g3")
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG")
|
||||
endif()
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
file(GLOB_RECURSE IVY_HEADER "${CMAKE_CURRENT_LIST_DIR}/cpp/*.hpp")
|
||||
file(GLOB_RECURSE IVY_SOURCE "${CMAKE_CURRENT_LIST_DIR}/cpp/*.cpp")
|
||||
list(FILTER IVY_SOURCE EXCLUDE REGEX "_test\\.cpp$")
|
||||
list(FILTER IVY_SOURCE EXCLUDE REGEX "cli\\.cpp$")
|
||||
|
||||
add_library(ivy SHARED ${IVY_SOURCE} ${IVY_HEADER})
|
||||
target_link_libraries(ivy Threads::Threads)
|
||||
|
||||
add_executable(ivycli ${IVY_SOURCE} ${IVY_HEADER} ${CMAKE_CURRENT_LIST_DIR}/cpp/cli.cpp)
|
||||
target_link_libraries(ivycli Threads::Threads)
|
||||
480
Cargo.lock
generated
480
Cargo.lock
generated
|
|
@ -4,9 +4,9 @@ version = 3
|
|||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.19"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e"
|
||||
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
|
@ -18,42 +18,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
name = "anstyle"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
checksum = "8365de52b16c035ff4fcafe0092ba9390540e3e352870ac09933bebcaa2c8c56"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
version = "1.4.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"
|
||||
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||
|
||||
[[package]]
|
||||
name = "bstr"
|
||||
version = "0.2.17"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223"
|
||||
checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.11.0"
|
||||
version = "3.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d"
|
||||
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
|
||||
|
||||
[[package]]
|
||||
name = "cast"
|
||||
|
|
@ -69,9 +59,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|||
|
||||
[[package]]
|
||||
name = "ciborium"
|
||||
version = "0.2.0"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0c137568cc60b904a7724001b35ce2630fd00d5d84805fbb608ab89509d788f"
|
||||
checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e"
|
||||
dependencies = [
|
||||
"ciborium-io",
|
||||
"ciborium-ll",
|
||||
|
|
@ -80,15 +70,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "ciborium-io"
|
||||
version = "0.2.0"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "346de753af073cc87b52b2083a506b38ac176a44cfb05497b622e27be899b369"
|
||||
checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757"
|
||||
|
||||
[[package]]
|
||||
name = "ciborium-ll"
|
||||
version = "0.2.0"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "213030a2b5a4e0c0892b6652260cf6ccac84827b83a85a534e178e3906c4cf1b"
|
||||
checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9"
|
||||
dependencies = [
|
||||
"ciborium-io",
|
||||
"half",
|
||||
|
|
@ -96,40 +86,44 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "3.2.22"
|
||||
version = "4.5.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86447ad904c7fb335a790c9d7fe3d0d971dc523b8ccd1561a520de9a85302750"
|
||||
checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8"
|
||||
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",
|
||||
"indexmap",
|
||||
"textwrap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.2.4"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
|
||||
dependencies = [
|
||||
"os_str_bytes",
|
||||
]
|
||||
checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
|
||||
|
||||
[[package]]
|
||||
name = "criterion"
|
||||
version = "0.4.0"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7c76e09c1aae2bc52b3d2f29e13c6572553b30c4aa1b8a49fd70de6412654cb"
|
||||
checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f"
|
||||
dependencies = [
|
||||
"anes",
|
||||
"atty",
|
||||
"cast",
|
||||
"ciborium",
|
||||
"clap",
|
||||
"criterion-plot",
|
||||
"is-terminal",
|
||||
"itertools",
|
||||
"lazy_static",
|
||||
"num-traits",
|
||||
"once_cell",
|
||||
"oorandom",
|
||||
"plotters",
|
||||
"rayon",
|
||||
|
|
@ -151,62 +145,42 @@ dependencies = [
|
|||
"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]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.2"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc"
|
||||
checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.9.10"
|
||||
version = "0.9.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "045ebe27666471bb549370b4b0b3e51b07f56325befa4284db65fc89c02511b1"
|
||||
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
"memoffset",
|
||||
"once_cell",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.11"
|
||||
version = "0.8.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
]
|
||||
checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80"
|
||||
|
||||
[[package]]
|
||||
name = "crunchy"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.8.0"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
|
||||
|
||||
[[package]]
|
||||
name = "fuzzy-matcher"
|
||||
|
|
@ -219,80 +193,74 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "globset"
|
||||
version = "0.4.9"
|
||||
version = "0.4.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a1e17342619edbc21a964c2afbeb6c820c6a2560032872f397bb97ea127bd0a"
|
||||
checksum = "15f1ce686646e7f1e19bf7d5533fe443a45dbfb990e00629110797578b42fb19"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"bstr",
|
||||
"fnv",
|
||||
"log",
|
||||
"regex",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "half"
|
||||
version = "1.8.2"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
|
||||
|
||||
[[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"
|
||||
checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"cfg-if",
|
||||
"crunchy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ignore"
|
||||
version = "0.4.18"
|
||||
name = "hermit-abi"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "713f1b139373f96a2e0ce3ac931cd01ee973c3c5dd7c40c0c2efe96ad2b6751d"
|
||||
checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc"
|
||||
|
||||
[[package]]
|
||||
name = "ignore"
|
||||
version = "0.4.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d89fd380afde86567dfba715db065673989d6253f42b88179abd3eae47bda4b"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
"crossbeam-deque",
|
||||
"globset",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"memchr",
|
||||
"regex",
|
||||
"regex-automata",
|
||||
"same-file",
|
||||
"thread_local",
|
||||
"walkdir",
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.9.1"
|
||||
name = "is-terminal"
|
||||
version = "0.4.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
|
||||
checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"hashbrown",
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.10.4"
|
||||
version = "0.10.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8bf247779e67a9082a4790b45e71ac7cfd1321331a5c856a74a9faebdab78d0"
|
||||
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.3"
|
||||
version = "1.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754"
|
||||
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
||||
|
||||
[[package]]
|
||||
name = "ivy"
|
||||
|
|
@ -301,97 +269,62 @@ dependencies = [
|
|||
"criterion",
|
||||
"fuzzy-matcher",
|
||||
"ignore",
|
||||
"lazy_static",
|
||||
"rayon",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.60"
|
||||
version = "0.3.72"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47"
|
||||
checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.132"
|
||||
version = "0.2.161"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5"
|
||||
checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.17"
|
||||
version = "0.4.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.5.0"
|
||||
version = "2.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.15"
|
||||
version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
|
||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||
dependencies = [
|
||||
"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]]
|
||||
name = "once_cell"
|
||||
version = "1.14.0"
|
||||
version = "1.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f7254b99e31cad77da24b08ebf628882739a608578bb1bcdfc1f9c21260d7c0"
|
||||
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
|
||||
|
||||
[[package]]
|
||||
name = "oorandom"
|
||||
version = "11.1.3"
|
||||
version = "11.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
|
||||
|
||||
[[package]]
|
||||
name = "os_str_bytes"
|
||||
version = "6.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff"
|
||||
checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9"
|
||||
|
||||
[[package]]
|
||||
name = "plotters"
|
||||
version = "0.3.4"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2538b639e642295546c50fcd545198c9d64ee2a38620a628724a3b266d5fbf97"
|
||||
checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
"plotters-backend",
|
||||
|
|
@ -402,65 +335,74 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "plotters-backend"
|
||||
version = "0.3.4"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "193228616381fecdc1224c62e96946dfbc73ff4384fba576e052ff8c1bea8142"
|
||||
checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a"
|
||||
|
||||
[[package]]
|
||||
name = "plotters-svg"
|
||||
version = "0.3.3"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f9a81d2759aae1dae668f783c308bc5c8ebd191ff4184aaa1b37f65a6ae5a56f"
|
||||
checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670"
|
||||
dependencies = [
|
||||
"plotters-backend",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.43"
|
||||
version = "1.0.89"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab"
|
||||
checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.21"
|
||||
version = "1.0.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
|
||||
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.6.0"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e060280438193c554f654141c9ea9417886713b7acd75974c85b18a69a88e0b"
|
||||
checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa"
|
||||
dependencies = [
|
||||
"crossbeam-deque",
|
||||
"either",
|
||||
"rayon-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.10.1"
|
||||
version = "1.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cac410af5d00ab6884528b4ab69d1e8e146e8d471201800fa1b4524126de6ad3"
|
||||
checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2"
|
||||
dependencies = [
|
||||
"crossbeam-channel",
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils",
|
||||
"num_cpus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.6.0"
|
||||
version = "1.11.1"
|
||||
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 = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
|
|
@ -469,15 +411,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.27"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244"
|
||||
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.11"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
|
||||
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
|
|
@ -488,26 +430,20 @@ 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"
|
||||
version = "1.0.213"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860"
|
||||
checksum = "3ea7893ff5e2466df8d720bb615088341b295f849602c6956047f8f80f0e9bc1"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.144"
|
||||
version = "1.0.213"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94ed3a816fb1d101812f83e789f888322c34e291f894f19590dc310963e87a00"
|
||||
checksum = "7e85ad2009c50b58e87caa8cd6dac16bdf511bbfb7af6c33df902396aa480fa5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
@ -516,38 +452,34 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.85"
|
||||
version = "1.0.132"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44"
|
||||
checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.99"
|
||||
version = "2.0.85"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13"
|
||||
checksum = "5023162dfcd14ef8f32034d8bcd4cc5ddc61ef7a247c024a33e24e1f24d21b56"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "949517c0cf1bf4ee812e2e07e08ab448e3ae0d23472aee8a06c985f0c8815b16"
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "1.1.4"
|
||||
version = "1.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180"
|
||||
checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
|
|
@ -563,36 +495,36 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.4"
|
||||
version = "1.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd"
|
||||
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.3.2"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
|
||||
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
|
||||
dependencies = [
|
||||
"same-file",
|
||||
"winapi",
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.83"
|
||||
version = "0.2.95"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268"
|
||||
checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.83"
|
||||
version = "0.2.95"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142"
|
||||
checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
|
|
@ -605,9 +537,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.83"
|
||||
version = "0.2.95"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810"
|
||||
checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
|
|
@ -615,9 +547,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.83"
|
||||
version = "0.2.95"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c"
|
||||
checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
@ -628,47 +560,107 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.83"
|
||||
version = "0.2.95"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
|
||||
checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d"
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.60"
|
||||
version = "0.3.72"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f"
|
||||
checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112"
|
||||
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"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
||||
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
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"
|
||||
|
|
|
|||
|
|
@ -9,13 +9,12 @@ crate-type = ["cdylib", "rlib"]
|
|||
path = "rust/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
ignore = "0.4.18"
|
||||
ignore = "0.4.22"
|
||||
fuzzy-matcher = "0.3.7"
|
||||
lazy_static = "1.4.0"
|
||||
rayon = "1.6.0"
|
||||
rayon = "1.10.0"
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = "0.4.0"
|
||||
criterion = "0.5.1"
|
||||
|
||||
[profile.release]
|
||||
opt-level = 3
|
||||
|
|
|
|||
185
README.md
185
README.md
|
|
@ -1,6 +1,6 @@
|
|||
<div align="center">
|
||||
|
||||
<img src="assets/logo.svg" alt="ivy.vim" />
|
||||
<img src="assets/logo.svg" alt="ivy.vim" />
|
||||
|
||||
<br />
|
||||
<br />
|
||||
|
|
@ -20,7 +20,90 @@ git clone https://github.com/AdeAttwood/ivy.nvim ~/.config/nvim/pack/bundle/star
|
|||
|
||||
### 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
|
||||
|
||||
|
|
@ -44,32 +127,88 @@ 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 | \<leader\>p | Find files in your project with the fd cli file finder |
|
||||
| IvyAg | \<leader\>/ | Find content in files using the silver searcher |
|
||||
| IvyBuffers | \<leader\>b | Search though open buffers |
|
||||
| IvyLines | | Search the lines in the current buffer |
|
||||
| Module | Command | Default Key Map | Description |
|
||||
| ------------------------------------ | ------------------ | --------------- | ----------------------------------------------------------- |
|
||||
| `ivy.backends.files` | IvyFd | \<leader\>p | Find files in your project with a custom rust file finder |
|
||||
| `ivy.backends.ag` | IvyAg | \<leader\>/ | Find content in files using the silver searcher |
|
||||
| `ivy.backends.rg` | IvyRg | \<leader\>/ | Find content in files using ripgrep cli tool |
|
||||
| `ivy.backends.buffers` | IvyBuffers | \<leader\>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 | Default Key Map | Description |
|
||||
| ------------------- | --------------------- | --------------- | ---------------------------------------------------------------- |
|
||||
| Complete | `complete` | \<CR\> | Run the completion function, usually this will be opening a file |
|
||||
| Vertical Split | `vsplit` | \<C-v\> | Run the completion function in a new vertical split |
|
||||
| Split | `split` | \<C-s\> | 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
|
||||
|
||||
### 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
|
||||
|
|
@ -77,10 +216,18 @@ Action can be run on selected candidates provide functionality
|
|||
-- Call back function to get all the candidates that will be displayed in
|
||||
-- the results window, The `input` will be passed in, so you can filter
|
||||
-- your results with the value from the prompt
|
||||
function(input) return { "One", "Two", Three } end,
|
||||
-- Action callback that will be called on the completion or peek actions.
|
||||
function(input)
|
||||
return {
|
||||
{ content = "One" },
|
||||
{ content = "Two" },
|
||||
{ content = "Three" },
|
||||
}
|
||||
end,
|
||||
-- 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
|
||||
function(result)
|
||||
vim.cmd("edit " .. result)
|
||||
end
|
||||
)
|
||||
```
|
||||
|
||||
|
|
|
|||
35
cpp/cli.cpp
35
cpp/cli.cpp
|
|
@ -1,35 +0,0 @@
|
|||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
#include <regex>
|
||||
#include <string>
|
||||
|
||||
#include "./file_scanner.hpp"
|
||||
#include "./sorter.hpp"
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
std::vector<std::string> args;
|
||||
args.reserve(argc);
|
||||
// Skip the first argument because that will be the programme name.
|
||||
for (int i = 1; i < argc; i++) {
|
||||
args.emplace_back(argv[i]);
|
||||
}
|
||||
|
||||
if (args.empty()) {
|
||||
std::cout << "Missing required search term" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto base_dir = std::filesystem::current_path();
|
||||
std::string search = args.at(0);
|
||||
|
||||
auto sorter = ivy::Sorter(search);
|
||||
auto scanner = ivy::FileScanner(base_dir);
|
||||
|
||||
std::regex pattern("([" + search + "])");
|
||||
for (ivy::Match const& match : sorter.sort(scanner.scan())) {
|
||||
std::cout << match.score << " " << std::regex_replace(match.content, pattern, "\033[1m$&\033[0m") << std::endl;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace ivy {
|
||||
class FileScanner {
|
||||
std::string m_base_dir;
|
||||
|
||||
public:
|
||||
explicit FileScanner(const std::string base_dir) : m_base_dir(base_dir) {}
|
||||
|
||||
std::vector<std::string> scan() {
|
||||
std::vector<std::string> results;
|
||||
for (const fs::directory_entry& dir_entry : fs::recursive_directory_iterator(m_base_dir)) {
|
||||
fs::path path = dir_entry.path();
|
||||
|
||||
// TODO(ade): sort out some kind of ignore thing. This will be needed
|
||||
// when we start adding wildcard ignore functionality
|
||||
if (path.string().find(".git") != std::string::npos) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (dir_entry.is_regular_file()) {
|
||||
results.emplace_back(fs::relative(path, m_base_dir));
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
};
|
||||
} // namespace ivy
|
||||
|
|
@ -1,212 +0,0 @@
|
|||
// LICENSE
|
||||
//
|
||||
// This software is dual-licensed to the public domain and under the following
|
||||
// license: you are granted a perpetual, irrevocable license to copy, modify,
|
||||
// publish, and distribute this file as you see fit.
|
||||
//
|
||||
// VERSION
|
||||
// 0.2.0 (2017-02-18) Scored matches perform exhaustive search for best score
|
||||
// 0.1.0 (2016-03-28) Initial release
|
||||
//
|
||||
// AUTHOR
|
||||
// Forrest Smith
|
||||
//
|
||||
// NOTES
|
||||
// Compiling
|
||||
// You MUST add '#define FTS_FUZZY_MATCH_IMPLEMENTATION' before including this header in ONE source file to create implementation.
|
||||
//
|
||||
// fuzzy_match_simple(...)
|
||||
// Returns true if each character in pattern is found sequentially within str
|
||||
//
|
||||
// fuzzy_match(...)
|
||||
// Returns true if pattern is found AND calculates a score.
|
||||
// Performs exhaustive search via recursion to find all possible matches and match with highest score.
|
||||
// Scores values have no intrinsic meaning. Possible score range is not normalized and varies with pattern.
|
||||
// Recursion is limited internally (default=10) to prevent degenerate cases (pattern="aaaaaa" str="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
|
||||
// Uses uint8_t for match indices. Therefore patterns are limited to 256 characters.
|
||||
// Score system should be tuned for YOUR use case. Words, sentences, file names, or method names all prefer different tuning.
|
||||
|
||||
#ifndef FTS_FUZZY_MATCH_H
|
||||
#define FTS_FUZZY_MATCH_H
|
||||
|
||||
#include <ctype.h> // ::tolower, ::toupper
|
||||
|
||||
#include <cstdint> // uint8_t
|
||||
#include <cstdio>
|
||||
#include <cstring> // memcpy
|
||||
#include <iostream>
|
||||
|
||||
// Public interface
|
||||
namespace fts {
|
||||
static bool fuzzy_match_simple(char const* pattern, char const* str);
|
||||
static bool fuzzy_match(char const* pattern, char const* str, int& outScore);
|
||||
static bool fuzzy_match(char const* pattern, char const* str, int& outScore, uint8_t* matches, int maxMatches);
|
||||
} // namespace fts
|
||||
|
||||
#ifdef FTS_FUZZY_MATCH_IMPLEMENTATION
|
||||
namespace fts {
|
||||
|
||||
// Forward declarations for "private" implementation
|
||||
namespace fuzzy_internal {
|
||||
static bool fuzzy_match_recursive(const char* pattern, const char* str, int& outScore, const char* strBegin,
|
||||
uint8_t const* srcMatches, uint8_t* newMatches, int maxMatches, int nextMatch,
|
||||
int& recursionCount, int recursionLimit);
|
||||
}
|
||||
|
||||
// Public interface
|
||||
static bool fuzzy_match_simple(char const* pattern, char const* str) {
|
||||
while (*pattern != '\0' && *str != '\0') {
|
||||
if (tolower(*pattern) == tolower(*str))
|
||||
++pattern;
|
||||
++str;
|
||||
}
|
||||
|
||||
return *pattern == '\0' ? true : false;
|
||||
}
|
||||
|
||||
static bool fuzzy_match(char const* pattern, char const* str, int& outScore) {
|
||||
uint8_t matches[256];
|
||||
return fuzzy_match(pattern, str, outScore, matches, sizeof(matches));
|
||||
}
|
||||
|
||||
static bool fuzzy_match(char const* pattern, char const* str, int& outScore, uint8_t* matches, int maxMatches) {
|
||||
int recursionCount = 0;
|
||||
int recursionLimit = 10;
|
||||
|
||||
return fuzzy_internal::fuzzy_match_recursive(pattern, str, outScore, str, nullptr, matches, maxMatches, 0, recursionCount, recursionLimit);
|
||||
}
|
||||
|
||||
// Private implementation
|
||||
static bool fuzzy_internal::fuzzy_match_recursive(const char* pattern, const char* str, int& outScore,
|
||||
const char* strBegin, uint8_t const* srcMatches, uint8_t* matches, int maxMatches,
|
||||
int nextMatch, int& recursionCount, int recursionLimit) {
|
||||
// Count recursions
|
||||
++recursionCount;
|
||||
if (recursionCount >= recursionLimit)
|
||||
return false;
|
||||
|
||||
// Detect end of strings
|
||||
if (*pattern == '\0' || *str == '\0')
|
||||
return false;
|
||||
|
||||
// Recursion params
|
||||
bool recursiveMatch = false;
|
||||
uint8_t bestRecursiveMatches[256];
|
||||
int bestRecursiveScore = 0;
|
||||
|
||||
// Loop through pattern and str looking for a match
|
||||
bool first_match = true;
|
||||
while (*pattern != '\0' && *str != '\0') {
|
||||
// Found match. We assume a space in the pattern is match so we can match paths better
|
||||
if (tolower(*pattern) == tolower(*str) || *pattern == ' ') {
|
||||
// Supplied matches buffer was too short
|
||||
if (nextMatch >= maxMatches) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// "Copy-on-Write" srcMatches into matches
|
||||
if (first_match && srcMatches) {
|
||||
memcpy(matches, srcMatches, nextMatch);
|
||||
first_match = false;
|
||||
}
|
||||
|
||||
// Recursive call that "skips" this match
|
||||
uint8_t recursiveMatches[256];
|
||||
int recursiveScore;
|
||||
if (fuzzy_match_recursive(pattern, str + 1, recursiveScore, strBegin, matches, recursiveMatches, sizeof(recursiveMatches), nextMatch, recursionCount, recursionLimit)) {
|
||||
// Pick best recursive score
|
||||
if (!recursiveMatch || recursiveScore > bestRecursiveScore) {
|
||||
memcpy(bestRecursiveMatches, recursiveMatches, 256);
|
||||
bestRecursiveScore = recursiveScore;
|
||||
}
|
||||
recursiveMatch = true;
|
||||
}
|
||||
|
||||
// Advance
|
||||
matches[nextMatch++] = (uint8_t)(str - strBegin);
|
||||
++pattern;
|
||||
}
|
||||
++str;
|
||||
}
|
||||
|
||||
// Determine if full pattern was matched
|
||||
bool matched = *pattern == '\0' ? true : false;
|
||||
|
||||
// Calculate score
|
||||
if (matched) {
|
||||
const int sequential_bonus = 15; // bonus for adjacent matches
|
||||
const int separator_bonus = 30; // bonus if match occurs after a separator
|
||||
const int camel_bonus = 30; // bonus if match is uppercase and prev is lower
|
||||
const int first_letter_bonus = 15; // bonus if the first letter is matched
|
||||
|
||||
const int leading_letter_penalty = -5; // penalty applied for every letter in str before the first match
|
||||
const int max_leading_letter_penalty = -15; // maximum penalty for leading letters
|
||||
const int unmatched_letter_penalty = -1; // penalty for every letter that doesn't matter
|
||||
|
||||
// Iterate str to end
|
||||
while (*str != '\0')
|
||||
++str;
|
||||
|
||||
// Initialize score
|
||||
outScore = 100;
|
||||
|
||||
// Apply leading letter penalty
|
||||
int penalty = leading_letter_penalty * matches[0];
|
||||
if (penalty < max_leading_letter_penalty)
|
||||
penalty = max_leading_letter_penalty;
|
||||
outScore += penalty;
|
||||
|
||||
// Apply unmatched penalty
|
||||
int unmatched = (int)(str - strBegin) - nextMatch;
|
||||
outScore += unmatched_letter_penalty * unmatched;
|
||||
|
||||
// Apply ordering bonuses
|
||||
for (int i = 0; i < nextMatch; ++i) {
|
||||
uint8_t currIdx = matches[i];
|
||||
|
||||
if (i > 0) {
|
||||
uint8_t prevIdx = matches[i - 1];
|
||||
|
||||
// Sequential
|
||||
if (currIdx == (prevIdx + 1))
|
||||
outScore += sequential_bonus;
|
||||
}
|
||||
|
||||
// Check for bonuses based on neighbor character value
|
||||
if (currIdx > 0) {
|
||||
// Camel case
|
||||
char neighbor = strBegin[currIdx - 1];
|
||||
char curr = strBegin[currIdx];
|
||||
if (::islower(neighbor) && ::isupper(curr))
|
||||
outScore += camel_bonus;
|
||||
|
||||
// Separator
|
||||
bool neighborSeparator = neighbor == '_' || neighbor == ' ' || neighbor == '/' || neighbor == '-';
|
||||
if (neighborSeparator)
|
||||
outScore += separator_bonus;
|
||||
} else {
|
||||
// First letter
|
||||
outScore += first_letter_bonus;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return best result
|
||||
if (recursiveMatch && (!matched || bestRecursiveScore > outScore)) {
|
||||
// Recursive score is better than "this"
|
||||
memcpy(matches, bestRecursiveMatches, maxMatches);
|
||||
outScore = bestRecursiveScore;
|
||||
return true;
|
||||
} else if (matched) {
|
||||
// "this" score is better than recursive
|
||||
return true;
|
||||
} else {
|
||||
// no match
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} // namespace fts
|
||||
|
||||
#endif // FTS_FUZZY_MATCH_IMPLEMENTATION
|
||||
|
||||
#endif // FTS_FUZZY_MATCH_H
|
||||
|
|
@ -1,198 +0,0 @@
|
|||
// Copyright 2017-2018 ccls Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// https://github.com/MaskRay/ccls/blob/master/src/fuzzy_match.cc
|
||||
|
||||
#include "fuzzy_match.hpp"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
namespace ivy {
|
||||
namespace {
|
||||
enum CharClass { Other,
|
||||
Lower,
|
||||
Upper };
|
||||
enum CharRole { None,
|
||||
Tail,
|
||||
Head };
|
||||
|
||||
CharClass getCharClass(int c) {
|
||||
if (islower(c))
|
||||
return Lower;
|
||||
if (isupper(c))
|
||||
return Upper;
|
||||
return Other;
|
||||
}
|
||||
|
||||
void calculateRoles(std::string_view s, int roles[], int *class_set) {
|
||||
if (s.empty()) {
|
||||
*class_set = 0;
|
||||
return;
|
||||
}
|
||||
CharClass pre = Other, cur = getCharClass(s[0]), suc;
|
||||
*class_set = 1 << cur;
|
||||
auto fn = [&]() {
|
||||
if (cur == Other)
|
||||
return None;
|
||||
// U(U)L is Head while U(U)U is Tail
|
||||
return pre == Other || (cur == Upper && (pre == Lower || suc == Lower))
|
||||
? Head
|
||||
: Tail;
|
||||
};
|
||||
for (size_t i = 0; i < s.size() - 1; i++) {
|
||||
suc = getCharClass(s[i + 1]);
|
||||
*class_set |= 1 << suc;
|
||||
roles[i] = fn();
|
||||
pre = cur;
|
||||
cur = suc;
|
||||
}
|
||||
roles[s.size() - 1] = fn();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int FuzzyMatcher::missScore(int j, bool last) {
|
||||
int s = -3;
|
||||
if (last)
|
||||
s -= 10;
|
||||
if (text_role[j] == Head)
|
||||
s -= 10;
|
||||
return s;
|
||||
}
|
||||
|
||||
int FuzzyMatcher::matchScore(int i, int j, bool last) {
|
||||
int s = 0;
|
||||
// Case matching.
|
||||
if (pat[i] == text[j]) {
|
||||
s++;
|
||||
// pat contains uppercase letters or prefix matching.
|
||||
if ((pat_set & 1 << Upper) || i == j)
|
||||
s++;
|
||||
}
|
||||
if (pat_role[i] == Head) {
|
||||
if (text_role[j] == Head)
|
||||
s += 30;
|
||||
else if (text_role[j] == Tail)
|
||||
s -= 10;
|
||||
}
|
||||
// Matching a tail while previous char wasn't matched.
|
||||
if (text_role[j] == Tail && i && !last)
|
||||
s -= 30;
|
||||
// First char of pat matches a tail.
|
||||
if (i == 0 && text_role[j] == Tail)
|
||||
s -= 40;
|
||||
return s;
|
||||
}
|
||||
|
||||
FuzzyMatcher::FuzzyMatcher(std::string_view pattern, int sensitivity) {
|
||||
calculateRoles(pattern, pat_role, &pat_set);
|
||||
if (sensitivity == 1)
|
||||
sensitivity = pat_set & 1 << Upper ? 2 : 0;
|
||||
case_sensitivity = sensitivity;
|
||||
size_t n = 0;
|
||||
for (size_t i = 0; i < pattern.size(); i++)
|
||||
if (pattern[i] != ' ') {
|
||||
pat += pattern[i];
|
||||
low_pat[n] = (char)::tolower(pattern[i]);
|
||||
pat_role[n] = pat_role[i];
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
int FuzzyMatcher::match(std::string_view text, bool strict) {
|
||||
if (pat.empty() != text.empty())
|
||||
return kMinScore;
|
||||
int n = int(text.size());
|
||||
if (n > kMaxText)
|
||||
return kMinScore + 1;
|
||||
this->text = text;
|
||||
for (int i = 0; i < n; i++)
|
||||
low_text[i] = (char)::tolower(text[i]);
|
||||
calculateRoles(text, text_role, &text_set);
|
||||
if (strict && n && !!pat_role[0] != !!text_role[0])
|
||||
return kMinScore;
|
||||
dp[0][0][0] = dp[0][0][1] = 0;
|
||||
for (int j = 0; j < n; j++) {
|
||||
dp[0][j + 1][0] = dp[0][j][0] + missScore(j, false);
|
||||
dp[0][j + 1][1] = kMinScore * 2;
|
||||
}
|
||||
for (int i = 0; i < int(pat.size()); i++) {
|
||||
int(*pre)[2] = dp[i & 1];
|
||||
int(*cur)[2] = dp[(i + 1) & 1];
|
||||
cur[i][0] = cur[i][1] = kMinScore;
|
||||
for (int j = i; j < n; j++) {
|
||||
cur[j + 1][0] = std::max(cur[j][0] + missScore(j, false),
|
||||
cur[j][1] + missScore(j, true));
|
||||
// For the first char of pattern, apply extra restriction to filter bad
|
||||
// candidates (e.g. |int| in |PRINT|)
|
||||
cur[j + 1][1] = (case_sensitivity ? pat[i] == text[j]
|
||||
: low_pat[i] == low_text[j] &&
|
||||
(i || text_role[j] != Tail ||
|
||||
pat[i] == text[j]))
|
||||
? std::max(pre[j][0] + matchScore(i, j, false),
|
||||
pre[j][1] + matchScore(i, j, true))
|
||||
: kMinScore * 2;
|
||||
}
|
||||
}
|
||||
|
||||
// Enumerate the end position of the match in str. Each removed trailing
|
||||
// character has a penulty.
|
||||
int ret = kMinScore;
|
||||
for (int j = pat.size(); j <= n; j++)
|
||||
ret = std::max(ret, dp[pat.size() & 1][j][1] - 2 * (n - j));
|
||||
return ret;
|
||||
}
|
||||
} // namespace ivy
|
||||
|
||||
#if 0
|
||||
TEST_SUITE("fuzzy_match") {
|
||||
bool Ranks(std::string_view pat, std::vector<const char*> texts) {
|
||||
FuzzyMatcher fuzzy(pat, 0);
|
||||
std::vector<int> scores;
|
||||
for (auto text : texts)
|
||||
scores.push_back(fuzzy.Match(text));
|
||||
bool ret = true;
|
||||
for (size_t i = 0; i < texts.size() - 1; i++)
|
||||
if (scores[i] < scores[i + 1]) {
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
if (!ret) {
|
||||
for (size_t i = 0; i < texts.size(); i++)
|
||||
printf("%s %d ", texts[i], scores[i]);
|
||||
puts("");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
TEST_CASE("test") {
|
||||
FuzzyMatcher fuzzy("", 0);
|
||||
CHECK(fuzzy.Match("") == 0);
|
||||
CHECK(fuzzy.Match("aaa") < 0);
|
||||
|
||||
// case
|
||||
CHECK(Ranks("monad", {"monad", "Monad", "mONAD"}));
|
||||
// initials
|
||||
CHECK(Ranks("ab", {"ab", "aoo_boo", "acb"}));
|
||||
CHECK(Ranks("CC", {"CamelCase", "camelCase", "camelcase"}));
|
||||
CHECK(Ranks("cC", {"camelCase", "CamelCase", "camelcase"}));
|
||||
CHECK(Ranks("c c", {"camelCase", "camel case", "CamelCase", "camelcase",
|
||||
"camel ace"}));
|
||||
CHECK(Ranks("Da.Te",
|
||||
{"Data.Text", "Data.Text.Lazy", "Data.Aeson.Encoding.text"}));
|
||||
CHECK(Ranks("foo bar.h", {"foo/bar.h", "foobar.h"}));
|
||||
// prefix
|
||||
CHECK(Ranks("is", {"isIEEE", "inSuf"}));
|
||||
// shorter
|
||||
CHECK(Ranks("ma", {"map", "many", "maximum"}));
|
||||
CHECK(Ranks("print", {"printf", "sprintf"}));
|
||||
// score(PRINT) = kMinScore
|
||||
CHECK(Ranks("ast", {"ast", "AST", "INT_FAST16_MAX"}));
|
||||
// score(PRINT) > kMinScore
|
||||
CHECK(Ranks("Int", {"int", "INT", "PRINT"}));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
// Copyright 2017-2018 ccls Authors
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <climits>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
namespace ivy {
|
||||
class FuzzyMatcher {
|
||||
public:
|
||||
constexpr static int kMaxPat = 100;
|
||||
constexpr static int kMaxText = 200;
|
||||
// Negative but far from INT_MIN so that intermediate results are hard to
|
||||
// overflow.
|
||||
constexpr static int kMinScore = INT_MIN / 4;
|
||||
|
||||
// 0: case-insensitive
|
||||
// 1: case-folded, i.e. insensitive if no input character is uppercase.
|
||||
// 2: case-sensitive
|
||||
FuzzyMatcher(std::string_view pattern, int case_sensitivity);
|
||||
int match(std::string_view text, bool strict);
|
||||
|
||||
private:
|
||||
int case_sensitivity;
|
||||
std::string pat;
|
||||
std::string_view text;
|
||||
int pat_set, text_set;
|
||||
char low_pat[kMaxPat], low_text[kMaxText];
|
||||
int pat_role[kMaxPat], text_role[kMaxText];
|
||||
int dp[2][kMaxText + 1][2];
|
||||
|
||||
int matchScore(int i, int j, bool last);
|
||||
int missScore(int j, bool last);
|
||||
};
|
||||
} // namespace ivy
|
||||
44
cpp/lib.cpp
44
cpp/lib.cpp
|
|
@ -1,44 +0,0 @@
|
|||
#include <cstring>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#define FTS_FUZZY_MATCH_IMPLEMENTATION
|
||||
#include "./file_scanner.hpp"
|
||||
#include "./fts_fuzzy_match.hpp"
|
||||
#include "./match.hpp"
|
||||
#include "./sorter.hpp"
|
||||
|
||||
namespace ivy {
|
||||
static std::map<std::string, std::vector<std::string>> file_cache;
|
||||
}; // namespace ivy
|
||||
|
||||
extern "C" void ivy_init(const char* dir) {
|
||||
auto scanner = ivy::FileScanner(dir);
|
||||
ivy::file_cache[std::string(dir)] = scanner.scan();
|
||||
}
|
||||
|
||||
extern "C" int ivy_match(const char* pattern, const char* text) {
|
||||
int score = 0;
|
||||
fts::fuzzy_match(pattern, text, score);
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
extern "C" char* ivy_files(const char* search, const char* base_dir) {
|
||||
if (!ivy::file_cache.count(base_dir)) {
|
||||
auto scanner = ivy::FileScanner(base_dir);
|
||||
ivy::file_cache[std::string(base_dir)] = scanner.scan();
|
||||
}
|
||||
|
||||
auto sorter = ivy::Sorter(search);
|
||||
|
||||
// TODO(ade): Sort out how this memory is freed. I am assuming its in lua
|
||||
// land via ffi
|
||||
auto* s = new std::string();
|
||||
for (ivy::Match const& match : sorter.sort(ivy::file_cache.at(base_dir))) {
|
||||
s->append(match.content + "\n");
|
||||
}
|
||||
|
||||
return s->data();
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace ivy {
|
||||
|
||||
struct Match {
|
||||
int score;
|
||||
std::string content;
|
||||
};
|
||||
|
||||
static bool sort_match(const Match& a, const Match& b) { return a.score < b.score; }
|
||||
|
||||
} // namespace ivy
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#define FTS_FUZZY_MATCH_IMPLEMENTATION
|
||||
#include "./fts_fuzzy_match.hpp"
|
||||
#include "./match.hpp"
|
||||
#include "./thread_pool.hpp"
|
||||
|
||||
namespace ivy {
|
||||
|
||||
class Sorter {
|
||||
ivy::ThreadPool m_thread_pool;
|
||||
|
||||
std::string m_term;
|
||||
|
||||
std::mutex m_matches_lock;
|
||||
std::vector<Match> m_matches;
|
||||
|
||||
inline void add_entry(const std::string& file) {
|
||||
int score = 0;
|
||||
fts::fuzzy_match(m_term.c_str(), file.c_str(), score);
|
||||
|
||||
if (score > 50) {
|
||||
std::unique_lock<std::mutex> lock(m_matches_lock);
|
||||
m_matches.emplace_back(Match{score, std::move(file)});
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
explicit Sorter(std::string_view term) : m_term(term) {}
|
||||
~Sorter() { m_thread_pool.shutdown(); }
|
||||
|
||||
inline std::vector<Match> sort(const std::vector<std::string>& list) {
|
||||
for (const std::string& item : list) {
|
||||
m_thread_pool.push([&item, this]() { add_entry(item); });
|
||||
}
|
||||
|
||||
while (!m_thread_pool.empty()) {
|
||||
// Wait for all of the jobs to be finished
|
||||
}
|
||||
|
||||
std::sort(m_matches.begin(), m_matches.end(), sort_match);
|
||||
return m_matches;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace ivy
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
// Copyright 2021 Practically.io All rights reserved
|
||||
//
|
||||
// Use of this source is governed by a BSD-style
|
||||
// licence that can be found in the LICENCE file or at
|
||||
// https://www.practically.io/copyright/
|
||||
|
||||
#include "thread_pool.hpp"
|
||||
|
||||
namespace ivy {
|
||||
void ThreadPool::run_job() {
|
||||
std::function<void()> job;
|
||||
while (true) {
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_queue_lock);
|
||||
m_condition.wait(lock, [&]() { return !m_queue.empty() || m_stop; });
|
||||
if (m_queue.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
job = m_queue.front();
|
||||
m_queue.pop();
|
||||
}
|
||||
|
||||
job();
|
||||
|
||||
{
|
||||
// Only decrement the job count when the job has finished running.
|
||||
std::unique_lock<std::mutex> lock(m_count_lock);
|
||||
m_job_count--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ThreadPool::create_threads(unsigned int thread_count) {
|
||||
for (int i = 0; i < thread_count; i++) {
|
||||
m_threads.emplace_back(std::thread([this] { run_job(); }));
|
||||
}
|
||||
}
|
||||
|
||||
void ThreadPool::push(std::function<void()> job) {
|
||||
{
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_count_lock);
|
||||
m_job_count++;
|
||||
}
|
||||
|
||||
std::unique_lock<std::mutex> lock(m_queue_lock);
|
||||
m_queue.push(job);
|
||||
}
|
||||
|
||||
m_condition.notify_one();
|
||||
}
|
||||
|
||||
bool ThreadPool::empty() {
|
||||
std::unique_lock<std::mutex> lock(m_count_lock);
|
||||
return m_job_count == 0;
|
||||
}
|
||||
|
||||
void ThreadPool::shutdown() {
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_queue_lock);
|
||||
m_stop = true;
|
||||
}
|
||||
|
||||
m_condition.notify_all();
|
||||
for (auto &thread : m_threads) {
|
||||
thread.join();
|
||||
}
|
||||
}
|
||||
} // namespace ivy
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
// Copyright 2021 Practically.io All rights reserved
|
||||
//
|
||||
// Use of this source is governed by a BSD-style
|
||||
// licence that can be found in the LICENCE file or at
|
||||
// https://www.practically.io/copyright/
|
||||
#pragma once
|
||||
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <queue>
|
||||
#include <thread>
|
||||
|
||||
namespace ivy {
|
||||
// Basic thread pool implementation to run callbacks distributed across
|
||||
// specified number of threads
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// ivy::ThreadPool thread_pool;
|
||||
// for (int i = 0; i < 10; i++) {
|
||||
// thread_pool.push([i]() {
|
||||
// std::cout << "The number is " << i << std::endl;
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// thread_pool.shutdown();
|
||||
//
|
||||
class ThreadPool {
|
||||
bool m_stop = false;
|
||||
// Need to track the number of jobs that need to be processed separately
|
||||
// because we cant rely on the queue length to check if pool has finished all
|
||||
// the jobs. It dose not take into account the jobs that have already been
|
||||
// picked up by a thread.
|
||||
int m_job_count = 0;
|
||||
std::mutex m_queue_lock;
|
||||
|
||||
std::queue<std::function<void()>> m_queue;
|
||||
std::mutex m_count_lock;
|
||||
|
||||
std::vector<std::thread> m_threads;
|
||||
std::condition_variable m_condition;
|
||||
|
||||
void run_job();
|
||||
void create_threads(unsigned int thread_count);
|
||||
|
||||
public:
|
||||
// Create a new thread pool with the maximum number of threads you can have on
|
||||
// the current machine
|
||||
ThreadPool() { create_threads(std::thread::hardware_concurrency()); }
|
||||
// Create a thread pool that will use the specified number of threads
|
||||
explicit ThreadPool(unsigned int thread_count) {
|
||||
create_threads(thread_count);
|
||||
}
|
||||
// Push a call back function into the queue that will be run on the thread
|
||||
// pool as some time.
|
||||
void push(std::function<void()>);
|
||||
// Tests to see if there is any jobs that still need to be processed by the
|
||||
// queue
|
||||
bool empty();
|
||||
// Shuts down the thread pool and waits for the queue to be empty. This must
|
||||
// be called when all of the jobs have been pushed into the queue. This is a
|
||||
// blocking operation and will not exit until the queue is empty and all of
|
||||
// the pushed jobs have been handled.
|
||||
void shutdown();
|
||||
};
|
||||
} // namespace ivy
|
||||
12
lua/ivy/backends/ag.lua
Normal file
12
lua/ivy/backends/ag.lua
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
local utils = require "ivy.utils"
|
||||
|
||||
local ag = {
|
||||
name = "AG",
|
||||
command = "IvyAg",
|
||||
description = "Run ag to search for content in files",
|
||||
keymap = "<leader>/",
|
||||
items = utils.command_finder "ag",
|
||||
callback = utils.vimgrep_action(),
|
||||
}
|
||||
|
||||
return ag
|
||||
38
lua/ivy/backends/buffers.lua
Normal file
38
lua/ivy/backends/buffers.lua
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
local libivy = require "ivy.libivy"
|
||||
local utils = require "ivy.utils"
|
||||
|
||||
local function items(input)
|
||||
local list = {}
|
||||
local buffers = vim.api.nvim_list_bufs()
|
||||
for index = 1, #buffers do
|
||||
local buffer = buffers[index]
|
||||
-- Get the relative path from the current working directory. We need to
|
||||
-- substring +2 to remove the `/` from the start of the path to give us a
|
||||
-- true relative path
|
||||
local buffer_name = vim.api.nvim_buf_get_name(buffer):sub(#vim.fn.getcwd() + 2, -1)
|
||||
local file_type = vim.api.nvim_buf_get_option(buffer, "filetype")
|
||||
if vim.api.nvim_buf_is_loaded(buffer) and file_type ~= "ivy" and #buffer_name > 0 then
|
||||
local score = libivy.ivy_match(input, buffer_name)
|
||||
if score > -200 or #input == 0 then
|
||||
table.insert(list, { score = score, content = buffer_name })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
table.sort(list, function(a, b)
|
||||
return a.score < b.score
|
||||
end)
|
||||
|
||||
return list
|
||||
end
|
||||
|
||||
local buffers = {
|
||||
name = "Buffers",
|
||||
command = "IvyBuffers",
|
||||
description = "List all of the current open buffers",
|
||||
keymap = "<leader>b",
|
||||
items = items,
|
||||
callback = utils.file_action(),
|
||||
}
|
||||
|
||||
return buffers
|
||||
17
lua/ivy/backends/files.lua
Normal file
17
lua/ivy/backends/files.lua
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
local libivy = require "ivy.libivy"
|
||||
local utils = require "ivy.utils"
|
||||
|
||||
local function items(term)
|
||||
return libivy.ivy_files(term, vim.fn.getcwd())
|
||||
end
|
||||
|
||||
local files = {
|
||||
name = "Files",
|
||||
command = "IvyFd",
|
||||
description = "Find files in the project",
|
||||
keymap = "<leader>p",
|
||||
items = items,
|
||||
callback = utils.file_action(),
|
||||
}
|
||||
|
||||
return files
|
||||
32
lua/ivy/backends/lines.lua
Normal file
32
lua/ivy/backends/lines.lua
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
local utils = require "ivy.utils"
|
||||
local libivy = require "ivy.libivy"
|
||||
|
||||
local function items(input)
|
||||
local list = {}
|
||||
|
||||
local lines = vim.api.nvim_buf_get_lines(vim.ivy.origin(), 0, -1, false)
|
||||
for index = 1, #lines do
|
||||
local line = lines[index]
|
||||
local score = libivy.ivy_match(input, line)
|
||||
if score > -200 then
|
||||
local prefix = string.rep(" ", 4 - #tostring(index)) .. index .. ": "
|
||||
table.insert(list, { score = score, content = prefix .. line })
|
||||
end
|
||||
end
|
||||
|
||||
table.sort(list, function(a, b)
|
||||
return a.score < b.score
|
||||
end)
|
||||
|
||||
return list
|
||||
end
|
||||
|
||||
local lines = {
|
||||
name = "Lines",
|
||||
command = "IvyLines",
|
||||
description = "Search though the lines in the current buffer",
|
||||
items = items,
|
||||
callback = utils.line_action(),
|
||||
}
|
||||
|
||||
return lines
|
||||
40
lua/ivy/backends/lsp-workspace-symbols.lua
Normal file
40
lua/ivy/backends/lsp-workspace-symbols.lua
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
local window = require "ivy.window"
|
||||
local utils = require "ivy.utils"
|
||||
|
||||
local previous_results = {}
|
||||
local function set_items(items)
|
||||
window.set_items(items)
|
||||
previous_results = items
|
||||
end
|
||||
|
||||
local function items(input)
|
||||
local buffer_number = window.origin_buffer
|
||||
local cwd = vim.fn.getcwd()
|
||||
local results = {}
|
||||
vim.lsp.buf_request(buffer_number, "workspace/symbol", { query = input }, function(err, server_result, _, _)
|
||||
if err ~= nil then
|
||||
set_items { content = "-- There was an error with workspace/symbol --" }
|
||||
return
|
||||
end
|
||||
local locations = vim.lsp.util.symbols_to_items(server_result or {}, buffer_number) or {}
|
||||
for index = 1, #locations do
|
||||
local item = locations[index]
|
||||
local relative_path = item.filename:sub(#cwd + 2, -1)
|
||||
table.insert(results, { content = relative_path .. ":" .. item.lnum .. ": " .. item.text })
|
||||
end
|
||||
|
||||
set_items(results)
|
||||
end)
|
||||
|
||||
return previous_results
|
||||
end
|
||||
|
||||
local lsp_workspace_symbols = {
|
||||
name = "WorkspaceSymbols",
|
||||
command = "IvyWorkspaceSymbols",
|
||||
description = "Search for workspace symbols using the lsp workspace/symbol",
|
||||
items = items,
|
||||
callback = utils.vimgrep_action(),
|
||||
}
|
||||
|
||||
return lsp_workspace_symbols
|
||||
12
lua/ivy/backends/rg.lua
Normal file
12
lua/ivy/backends/rg.lua
Normal 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
48
lua/ivy/config.lua
Normal 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
27
lua/ivy/config_spec.lua
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
local config = require "ivy.config"
|
||||
|
||||
describe("config", function()
|
||||
before_each(function()
|
||||
config.user_config = {}
|
||||
end)
|
||||
|
||||
it("gets the first item when there is only default values", function()
|
||||
local first_backend = config:get { "backends", 1 }
|
||||
assert.is_equal("ivy.backends.buffers", first_backend)
|
||||
end)
|
||||
|
||||
it("returns nil if we access a key that is not a valid config item", function()
|
||||
assert.is_nil(config:get { "not", "a", "thing" })
|
||||
end)
|
||||
|
||||
it("returns the users overridden config value", function()
|
||||
config.user_config = { backends = { "ivy.my.backend" } }
|
||||
local first_backend = config:get { "backends", 1 }
|
||||
assert.is_equal("ivy.my.backend", first_backend)
|
||||
end)
|
||||
|
||||
it("returns a nested value", function()
|
||||
config.user_config = { some = { nested = "value" } }
|
||||
assert.is_equal(config:get { "some", "nested" }, "value")
|
||||
end)
|
||||
end)
|
||||
|
|
@ -3,7 +3,6 @@ local prompt = require "ivy.prompt"
|
|||
local utils = require "ivy.utils"
|
||||
|
||||
local controller = {}
|
||||
controller.action = utils.actions
|
||||
|
||||
controller.items = nil
|
||||
controller.callback = nil
|
||||
|
|
@ -30,6 +29,10 @@ controller.search = function(value)
|
|||
controller.update(prompt.text())
|
||||
end
|
||||
|
||||
controller.paste = function()
|
||||
controller.search(prompt.text() .. vim.fn.getreg "+p")
|
||||
end
|
||||
|
||||
controller.update = function(text)
|
||||
vim.schedule(function()
|
||||
window.set_items(controller.items(text))
|
||||
|
|
@ -37,7 +40,7 @@ controller.update = function(text)
|
|||
if #text > 0 then
|
||||
-- Escape characters so they do not throw an error when vim tries to use
|
||||
-- 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 .. "]'")
|
||||
end
|
||||
end)
|
||||
|
|
@ -52,7 +55,7 @@ end
|
|||
|
||||
controller.checkpoint = function()
|
||||
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)
|
||||
end
|
||||
|
||||
|
|
|
|||
57
lua/ivy/controller_spec.lua
Normal file
57
lua/ivy/controller_spec.lua
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
local window = require "ivy.window"
|
||||
local controller = require "ivy.controller"
|
||||
|
||||
describe("controller", function()
|
||||
before_each(function()
|
||||
vim.cmd "highlight IvyMatch cterm=bold gui=bold"
|
||||
window.initialize()
|
||||
end)
|
||||
|
||||
after_each(function()
|
||||
controller.destroy()
|
||||
end)
|
||||
|
||||
it("will run the completion", function()
|
||||
controller.run("Testing", function()
|
||||
return { { content = "Some content" } }
|
||||
end, function()
|
||||
return {}
|
||||
end)
|
||||
|
||||
-- Run all the scheduled tasks
|
||||
vim.wait(0)
|
||||
|
||||
local lines = vim.api.nvim_buf_get_lines(0, 0, -1, true)
|
||||
assert.is_equal(#lines, 1)
|
||||
assert.is_equal(lines[1], "Some content")
|
||||
end)
|
||||
|
||||
it("will not try and highlight the buffer if there is nothing to highlight", function()
|
||||
spy.on(vim, "cmd")
|
||||
|
||||
controller.items = function()
|
||||
return { { content = "Hello" } }
|
||||
end
|
||||
|
||||
controller.update ""
|
||||
|
||||
vim.wait(0)
|
||||
|
||||
assert.spy(vim.cmd).was_called_with "syntax clear IvyMatch"
|
||||
assert.spy(vim.cmd).was_not_called_with "syntax match IvyMatch '[H]'"
|
||||
end)
|
||||
|
||||
it("will escape a - when passing it to be highlighted", function()
|
||||
spy.on(vim, "cmd")
|
||||
|
||||
controller.items = function()
|
||||
return { { content = "Hello" } }
|
||||
end
|
||||
|
||||
controller.update "some-file"
|
||||
|
||||
vim.wait(0)
|
||||
|
||||
assert.spy(vim.cmd).was_called_with "syntax match IvyMatch '[some\\-file]'"
|
||||
end)
|
||||
end)
|
||||
|
|
@ -1,51 +0,0 @@
|
|||
local vim_mock = require "ivy.vim_mock"
|
||||
local window = require "ivy.window"
|
||||
local controller = require "ivy.controller"
|
||||
|
||||
-- The number of the mock buffer where all the test completions gets put
|
||||
local buffer_number = 10
|
||||
|
||||
before_each(function()
|
||||
vim_mock.reset()
|
||||
window.initialize()
|
||||
end)
|
||||
|
||||
after_each(function()
|
||||
controller.destroy()
|
||||
end)
|
||||
|
||||
it("will run", function(t)
|
||||
controller.run("Testing", function()
|
||||
return { { content = "Some content" } }
|
||||
end, function()
|
||||
return {}
|
||||
end)
|
||||
|
||||
local lines = vim_mock.get_lines()
|
||||
local completion_lines = lines[buffer_number]
|
||||
|
||||
t.assert_equal(#completion_lines, 1)
|
||||
t.assert_equal(completion_lines[1], "Some content")
|
||||
end)
|
||||
|
||||
it("will not try and highlight the buffer if there is nothing to highlight", function(t)
|
||||
controller.items = function()
|
||||
return { { content = "Hello" } }
|
||||
end
|
||||
|
||||
controller.update ""
|
||||
local commands = vim_mock.get_commands()
|
||||
t.assert_equal(#commands, 1)
|
||||
end)
|
||||
|
||||
it("will escape a - when passing it to be highlighted", function(t)
|
||||
controller.items = function()
|
||||
return { { content = "Hello" } }
|
||||
end
|
||||
|
||||
controller.update "some-file"
|
||||
local commands = vim_mock.get_commands()
|
||||
local syntax_command = commands[2]
|
||||
|
||||
t.assert_equal("syntax match IvyMatch '[some\\-file]'", syntax_command)
|
||||
end)
|
||||
49
lua/ivy/init.lua
Normal file
49
lua/ivy/init.lua
Normal 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
30
lua/ivy/init_spec.lua
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
local ivy = require "ivy"
|
||||
local config = require "ivy.config"
|
||||
|
||||
describe("ivy.setup", function()
|
||||
before_each(function()
|
||||
ivy.has_setup = false
|
||||
config.user_config = {}
|
||||
end)
|
||||
|
||||
it("sets the users config options", function()
|
||||
ivy.setup { backends = { "ivy.backends.files" } }
|
||||
assert.is_equal("ivy.backends.files", config:get { "backends", 1 })
|
||||
end)
|
||||
|
||||
it("will not reconfigure if its called twice", function()
|
||||
ivy.setup { backends = { "ivy.backends.files" } }
|
||||
ivy.setup { backends = { "some.backend" } }
|
||||
assert.is_equal("ivy.backends.files", config:get { "backends", 1 })
|
||||
end)
|
||||
|
||||
it("does not crash if you don't pass in any params to the setup function", function()
|
||||
ivy.setup()
|
||||
assert.is_equal("ivy.backends.buffers", config:get { "backends", 1 })
|
||||
end)
|
||||
|
||||
it("will fallback if the key is not set at all in the users config", function()
|
||||
ivy.setup { some_key = "some_value" }
|
||||
assert.is_equal("ivy.backends.buffers", config:get { "backends", 1 })
|
||||
end)
|
||||
end)
|
||||
|
|
@ -1,18 +1,65 @@
|
|||
local library_path = (function()
|
||||
local dirname = string.sub(debug.getinfo(1).source, 2, #"/fzf_lib.lua" * -1)
|
||||
return dirname .. "/../../target/release/libivyrs.so"
|
||||
local root = string.sub(debug.getinfo(1).source, 2, #"/libivy.lua" * -1)
|
||||
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)()
|
||||
|
||||
local ffi = require "ffi"
|
||||
local ivy_c = ffi.load(library_path)
|
||||
local ok, ivy_c = pcall(ffi.load, library_path)
|
||||
if not ok then
|
||||
vim.api.nvim_err_writeln(
|
||||
"libivyrs.so not found! Please ensure you have complied the shared library."
|
||||
.. " For more info refer to the documentation, https://github.com/AdeAttwood/ivy.nvim#compiling"
|
||||
)
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
ffi.cdef [[
|
||||
void ivy_init(const char*);
|
||||
char* ivy_cwd();
|
||||
int ivy_match(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 = {}
|
||||
|
||||
libivy.ivy_init = function(dir)
|
||||
|
|
@ -28,7 +75,12 @@ libivy.ivy_match = function(pattern, text)
|
|||
end
|
||||
|
||||
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
|
||||
|
||||
return libivy
|
||||
|
|
|
|||
36
lua/ivy/libivy_spec.lua
Normal file
36
lua/ivy/libivy_spec.lua
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
require "busted.runner"()
|
||||
|
||||
local libivy = require "ivy.libivy"
|
||||
|
||||
describe("libivy", function()
|
||||
it("should run a simple match", function()
|
||||
local score = libivy.ivy_match("term", "I am a serch term")
|
||||
|
||||
assert.is_true(score > 0)
|
||||
end)
|
||||
|
||||
it("should find a dot file", function()
|
||||
local current_dir = libivy.ivy_cwd()
|
||||
local results = libivy.ivy_files(".github/workflows/ci.yml", current_dir)
|
||||
|
||||
assert.is_equal(2, results.length, "Incorrect number of results found")
|
||||
assert.is_equal(".github/workflows/ci.yml", results[2].content, "Invalid matches")
|
||||
end)
|
||||
|
||||
it("will allow you to access the length via the metatable", function()
|
||||
local current_dir = libivy.ivy_cwd()
|
||||
local results = libivy.ivy_files(".github/workflows/ci.yml", current_dir)
|
||||
|
||||
local mt = getmetatable(results)
|
||||
|
||||
assert.is_equal(results.length, mt.__len(results), "The `length` property does not match the __len metamethod")
|
||||
end)
|
||||
|
||||
it("will create an iterator", function()
|
||||
local iter = libivy.ivy_files(".github/workflows/ci.yml", libivy.ivy_cwd())
|
||||
local mt = getmetatable(iter)
|
||||
|
||||
assert.is_equal(type(mt["__index"]), "function")
|
||||
assert.is_equal(type(mt["__len"]), "function")
|
||||
end)
|
||||
end)
|
||||
|
|
@ -1,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
29
lua/ivy/matcher_spec.lua
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
local libivy = require "ivy.libivy"
|
||||
|
||||
-- Helper function to test a that string `one` has a higher match score than
|
||||
-- string `two`. If string `one` has a lower score than string `two` a string
|
||||
-- will be returned that can be used in body of an error. If not then `nil` is
|
||||
-- returned and all is good.
|
||||
local match_test = function(term, one, two)
|
||||
local score_one = libivy.ivy_match(term, one)
|
||||
local score_two = libivy.ivy_match(term, two)
|
||||
|
||||
assert.is_true(
|
||||
score_one > score_two,
|
||||
("The score of %s (%d) ranked higher than %s (%d)"):format(one, score_one, two, score_two)
|
||||
)
|
||||
end
|
||||
|
||||
describe("ivy matcher", function()
|
||||
it("should match path separator", function()
|
||||
match_test("file", "some/file.lua", "somefile.lua")
|
||||
end)
|
||||
|
||||
-- it("should match pattern with spaces", function()
|
||||
-- match_test("so fi", "some/file.lua", "somefile.lua")
|
||||
-- end)
|
||||
|
||||
it("should match the start of a string", function()
|
||||
match_test("file", "file.lua", "somefile.lua")
|
||||
end)
|
||||
end)
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
local libivy = require "ivy.libivy"
|
||||
|
||||
-- Helper function to test a that string `one` has a higher match score than
|
||||
-- string `two`. If string `one` has a lower score than string `two` a string
|
||||
-- will be returned that can be used in body of an error. If not then `nil` is
|
||||
-- returned and all is good.
|
||||
local match_test = function(term, one, two)
|
||||
local score_one = libivy.ivy_match(term, one)
|
||||
local score_two = libivy.ivy_match(term, two)
|
||||
|
||||
if score_one < score_two then
|
||||
return one .. " should be ranked higher than " .. two
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
it("sould match path separator", function(t)
|
||||
local result = match_test("file", "some/file.lua", "somefile.lua")
|
||||
if result then
|
||||
t.error(result)
|
||||
end
|
||||
end)
|
||||
|
||||
it("sould match pattern with spaces", function(t)
|
||||
local result = match_test("so fi", "some/file.lua", "somefile.lua")
|
||||
if result then
|
||||
t.error(result)
|
||||
end
|
||||
end)
|
||||
|
||||
it("sould match the start of a string", function(t)
|
||||
local result = match_test("file", "file.lua", "somefile.lua")
|
||||
if result then
|
||||
t.error(result)
|
||||
end
|
||||
end)
|
||||
|
|
@ -1,6 +1,23 @@
|
|||
-- The prefix that will be before the search text for the user
|
||||
local prompt_prefix = ">> "
|
||||
|
||||
-- Gets the suffix to delete from some text biased on what happens in a bash
|
||||
-- prompt. If the text dose not end in a letter then the last word and all of
|
||||
-- the tailing special characters will be returned. If the text dose end in a
|
||||
-- letter then only the last word will be returned leaving the special
|
||||
-- characters that are before the last word. For example
|
||||
--
|
||||
-- `some word` -> `some `
|
||||
-- `some word` -> `some `
|
||||
-- `some word ` -> `some `
|
||||
local function get_delete_suffix(text)
|
||||
if text:match "([A-Za-z]+)$" == nil then
|
||||
return text:match "([A-Za-z]+[^A-Za-z]+)$"
|
||||
end
|
||||
|
||||
return text:match "([A-Za-z]+)$"
|
||||
end
|
||||
|
||||
local prompt = {}
|
||||
|
||||
prompt.suffix = ""
|
||||
|
|
@ -9,6 +26,7 @@ prompt.value = ""
|
|||
prompt.text = function()
|
||||
return prompt.value .. prompt.suffix
|
||||
end
|
||||
|
||||
prompt.update = function()
|
||||
vim.api.nvim_echo({
|
||||
{ prompt_prefix, "None" },
|
||||
|
|
@ -32,9 +50,12 @@ prompt.input = function(char)
|
|||
prompt.suffix = prompt.suffix:sub(2, -1)
|
||||
end
|
||||
elseif char == "DELETE_WORD" then
|
||||
prompt.value = prompt.value:match "(.*)%s+.*$"
|
||||
if prompt.value == nil then
|
||||
local suffix = get_delete_suffix(prompt.value)
|
||||
|
||||
if suffix == nil then
|
||||
prompt.value = ""
|
||||
else
|
||||
prompt.value = prompt.value:sub(1, #prompt.value - #suffix)
|
||||
end
|
||||
elseif char == "\\\\" then
|
||||
prompt.value = prompt.value .. "\\"
|
||||
|
|
|
|||
91
lua/ivy/prompt_spec.lua
Normal file
91
lua/ivy/prompt_spec.lua
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
local prompt = require "ivy.prompt"
|
||||
|
||||
-- Input a list of strings into the prompt
|
||||
local input = function(input_table)
|
||||
for index = 1, #input_table do
|
||||
prompt.input(input_table[index])
|
||||
end
|
||||
end
|
||||
|
||||
describe("prompt", function()
|
||||
before_each(function()
|
||||
prompt.destroy()
|
||||
end)
|
||||
|
||||
it("starts with empty text", function()
|
||||
assert.is_same(prompt.text(), "")
|
||||
end)
|
||||
|
||||
it("can input some text", function()
|
||||
input { "A", "d", "e" }
|
||||
assert.is_same(prompt.text(), "Ade")
|
||||
end)
|
||||
|
||||
it("can delete a char", function()
|
||||
input { "A", "d", "e", "BACKSPACE" }
|
||||
assert.is_same(prompt.text(), "Ad")
|
||||
end)
|
||||
|
||||
it("will reset the text", function()
|
||||
input { "A", "d", "e" }
|
||||
prompt.set "New"
|
||||
assert.is_same(prompt.text(), "New")
|
||||
end)
|
||||
|
||||
it("can move around the a word", function()
|
||||
input { "P", "r", "o", "p", "t", "LEFT", "LEFT", "LEFT", "RIGHT", "m" }
|
||||
assert.is_same(prompt.text(), "Prompt")
|
||||
end)
|
||||
|
||||
it("can delete a word", function()
|
||||
prompt.set "Ade Attwood"
|
||||
input { "DELETE_WORD" }
|
||||
|
||||
assert.is_same(prompt.text(), "Ade ")
|
||||
end)
|
||||
|
||||
it("can delete a word in the middle and leave the cursor at that word", function()
|
||||
prompt.set "Ade middle A"
|
||||
input { "LEFT", "LEFT", "DELETE_WORD", "a" }
|
||||
|
||||
assert.is_same(prompt.text(), "Ade a A")
|
||||
end)
|
||||
|
||||
it("will delete the space and the word if the last word is single space", function()
|
||||
prompt.set "some.thing "
|
||||
input { "DELETE_WORD" }
|
||||
|
||||
assert.is_same(prompt.text(), "some.")
|
||||
end)
|
||||
|
||||
it("will only delete one word from path", function()
|
||||
prompt.set "some/nested/path"
|
||||
input { "DELETE_WORD" }
|
||||
|
||||
assert.is_same(prompt.text(), "some/nested/")
|
||||
end)
|
||||
|
||||
it("will delete tailing space", function()
|
||||
prompt.set "word "
|
||||
input { "DELETE_WORD" }
|
||||
|
||||
assert.is_same(prompt.text(), "")
|
||||
end)
|
||||
|
||||
it("will leave a random space", function()
|
||||
prompt.set "some word "
|
||||
input { "DELETE_WORD" }
|
||||
|
||||
assert.is_same(prompt.text(), "some ")
|
||||
end)
|
||||
|
||||
local special_characters = { ".", "/", "^" }
|
||||
for _, char in ipairs(special_characters) do
|
||||
it(string.format("will stop at a %s", char), function()
|
||||
prompt.set(string.format("key%sValue", char))
|
||||
input { "DELETE_WORD" }
|
||||
|
||||
assert.is_same(prompt.text(), string.format("key%s", char))
|
||||
end)
|
||||
end
|
||||
end)
|
||||
|
|
@ -1,61 +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)
|
||||
54
lua/ivy/register_backend.lua
Normal file
54
lua/ivy/register_backend.lua
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
---@class IvyBackend
|
||||
---@field command string The command this backend will have
|
||||
---@field items fun(input: string): { content: string }[] | string The callback function to get the items to select from
|
||||
---@field callback fun(result: string, action: string) The callback function to run when a item is selected
|
||||
---@field description string? The description of the backend, this will be used in the keymaps
|
||||
---@field name string? The name of the backend, this will fallback to the command if its not set
|
||||
---@field keymap string? The keymap to trigger this backend
|
||||
|
||||
---@class IvyBackendOptions
|
||||
---@field command string The command this backend will have
|
||||
---@field keymap string? The keymap to trigger this backend
|
||||
|
||||
---Register a new backend
|
||||
---
|
||||
---This will create all the commands and set all the keymaps for the backend
|
||||
---@param backend IvyBackend
|
||||
local register_backend_class = function(backend)
|
||||
local user_command_options = { bang = true }
|
||||
if backend.description ~= nil then
|
||||
user_command_options.desc = backend.description
|
||||
end
|
||||
|
||||
local name = backend.name or backend.command
|
||||
vim.api.nvim_create_user_command(backend.command, function()
|
||||
vim.ivy.run(name, backend.items, backend.callback)
|
||||
end, user_command_options)
|
||||
|
||||
if backend.keymap ~= nil then
|
||||
vim.api.nvim_set_keymap("n", backend.keymap, "<cmd>" .. backend.command .. "<CR>", { nowait = true, silent = true })
|
||||
end
|
||||
end
|
||||
|
||||
---@param backend IvyBackend | { ["1"]: string, ["2"]: IvyBackendOptions} | string The backend or backend module
|
||||
---@param options IvyBackendOptions? The options for the backend, that will be merged with the backend
|
||||
local register_backend = function(backend, options)
|
||||
if type(backend[1]) == "string" then
|
||||
options = backend[2]
|
||||
backend = require(backend[1])
|
||||
end
|
||||
|
||||
if type(backend) == "string" then
|
||||
backend = require(backend)
|
||||
end
|
||||
|
||||
if options then
|
||||
for key, value in pairs(options) do
|
||||
backend[key] = value
|
||||
end
|
||||
end
|
||||
|
||||
register_backend_class(backend)
|
||||
end
|
||||
|
||||
return register_backend
|
||||
71
lua/ivy/register_backend_spec.lua
Normal file
71
lua/ivy/register_backend_spec.lua
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
local register_backend = require "ivy.register_backend"
|
||||
|
||||
local function get_command(name)
|
||||
local command_iter = vim.api.nvim_get_commands {}
|
||||
|
||||
for _, cmd in pairs(command_iter) do
|
||||
if cmd.name == name then
|
||||
return cmd
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
local function get_keymap(mode, rhs)
|
||||
local keymap_iter = vim.api.nvim_get_keymap(mode)
|
||||
for _, keymap in pairs(keymap_iter) do
|
||||
if keymap.rhs == rhs then
|
||||
return keymap
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
describe("register_backend", function()
|
||||
after_each(function()
|
||||
vim.api.nvim_del_user_command "IvyFd"
|
||||
|
||||
local keymap = get_keymap("n", "<Cmd>IvyFd<CR>")
|
||||
if keymap then
|
||||
vim.api.nvim_del_keymap("n", keymap.lhs)
|
||||
end
|
||||
end)
|
||||
|
||||
it("registers a backend from a string with the default options", function()
|
||||
register_backend "ivy.backends.files"
|
||||
|
||||
local command = get_command "IvyFd"
|
||||
assert.is_not_nil(command)
|
||||
|
||||
local keymap = get_keymap("n", "<Cmd>IvyFd<CR>")
|
||||
assert.is_not_nil(keymap)
|
||||
end)
|
||||
|
||||
it("allows you to override the keymap", function()
|
||||
register_backend("ivy.backends.files", { keymap = "<C-p>" })
|
||||
|
||||
local keymap = get_keymap("n", "<Cmd>IvyFd<CR>")
|
||||
assert(keymap ~= nil)
|
||||
assert.are.equal("<C-P>", keymap.lhs)
|
||||
end)
|
||||
|
||||
it("allows you to pass in a hole backend module", function()
|
||||
register_backend(require "ivy.backends.files")
|
||||
|
||||
local command = get_command "IvyFd"
|
||||
assert.is_not_nil(command)
|
||||
|
||||
local keymap = get_keymap("n", "<Cmd>IvyFd<CR>")
|
||||
assert.is_not_nil(keymap)
|
||||
end)
|
||||
|
||||
it("allows you to pass in a hole backend module", function()
|
||||
register_backend { "ivy.backends.files", { keymap = "<C-p>" } }
|
||||
|
||||
local keymap = get_keymap("n", "<Cmd>IvyFd<CR>")
|
||||
assert(keymap ~= nil)
|
||||
assert.are.equal("<C-P>", keymap.lhs)
|
||||
end)
|
||||
end)
|
||||
|
|
@ -17,6 +17,13 @@ utils.command_map = {
|
|||
[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
|
||||
|
|
@ -33,14 +40,18 @@ utils.command_finder = function(command, min)
|
|||
-- 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")
|
||||
local handle = io.popen(command .. " " .. input .. " 2>&1 || true")
|
||||
if handle == nil then
|
||||
return {}
|
||||
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
|
||||
|
||||
|
|
@ -68,21 +79,35 @@ utils.file_action = function()
|
|||
return
|
||||
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
|
||||
vim.api.nvim_err_writeln("[IVY] The file action is unable the handel the action " .. action)
|
||||
return
|
||||
end
|
||||
|
||||
vim.cmd(command .. " " .. file)
|
||||
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
|
||||
|
|
|
|||
11
lua/ivy/utils_escape_spec.lua
Normal file
11
lua/ivy/utils_escape_spec.lua
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
local utils = require "ivy.utils"
|
||||
|
||||
it("will escape a dollar in the file name", function()
|
||||
local result = utils.escape_file_name "/path/to/$file/$name.lua"
|
||||
assert.is_same(result, "/path/to/\\$file/\\$name.lua")
|
||||
end)
|
||||
|
||||
it("will escape a brackets in the file name", function()
|
||||
local result = utils.escape_file_name "/path/to/[file]/[name].lua"
|
||||
assert.is_same(result, "/path/to/\\[file\\]/\\[name\\].lua")
|
||||
end)
|
||||
28
lua/ivy/utils_line_action_spec.lua
Normal file
28
lua/ivy/utils_line_action_spec.lua
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
local utils = require "ivy.utils"
|
||||
local line_action = utils.line_action()
|
||||
|
||||
describe("utils line_action", function()
|
||||
before_each(function()
|
||||
spy.on(vim, "cmd")
|
||||
end)
|
||||
|
||||
it("will run the line command", function()
|
||||
line_action " 4: Some text"
|
||||
|
||||
assert.is_equal(#vim.cmd.calls, 1, "The `vim.cmd` function should be called once")
|
||||
assert.spy(vim.cmd).was_called_with "4"
|
||||
end)
|
||||
|
||||
it("will run with more numbers", function()
|
||||
line_action " 44: Some text"
|
||||
|
||||
assert.is_equal(#vim.cmd.calls, 1, "The `vim.cmd` function should be called once")
|
||||
assert.spy(vim.cmd).was_called_with "44"
|
||||
end)
|
||||
|
||||
it("dose not run any action if no line is found", function()
|
||||
line_action "Some text"
|
||||
|
||||
assert.spy(vim.cmd).was_not_called()
|
||||
end)
|
||||
end)
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
local utils = require "ivy.utils"
|
||||
local line_action = utils.line_action()
|
||||
local vim_mock = require "ivy.vim_mock"
|
||||
|
||||
before_each(function()
|
||||
vim_mock.reset()
|
||||
end)
|
||||
|
||||
it("will run the line command", function(t)
|
||||
line_action " 4: Some text"
|
||||
|
||||
if #vim_mock.commands ~= 1 then
|
||||
t.error "`line_action` command length should be 1"
|
||||
end
|
||||
|
||||
if vim_mock.commands[1] ~= "4" then
|
||||
t.error "`line_action` command should be 4"
|
||||
end
|
||||
end)
|
||||
|
||||
it("will run with more numbers", function(t)
|
||||
line_action " 44: Some text"
|
||||
|
||||
if #vim_mock.commands ~= 1 then
|
||||
t.error "`line_action` command length should be 1"
|
||||
end
|
||||
|
||||
if vim_mock.commands[1] ~= "44" then
|
||||
t.error "`line_action` command should be 44"
|
||||
end
|
||||
end)
|
||||
|
||||
it("dose not run any action if no line is found", function(t)
|
||||
line_action "Some text"
|
||||
|
||||
if #vim_mock.commands ~= 0 then
|
||||
t.error "`line_action` command length should be 1"
|
||||
end
|
||||
end)
|
||||
|
|
@ -1,10 +1,5 @@
|
|||
local utils = require "ivy.utils"
|
||||
local vimgrep_action = utils.vimgrep_action()
|
||||
local vim_mock = require "ivy.vim_mock"
|
||||
|
||||
before_each(function()
|
||||
vim_mock.reset()
|
||||
end)
|
||||
|
||||
local test_data = {
|
||||
{
|
||||
|
|
@ -20,37 +15,42 @@ local test_data = {
|
|||
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)
|
||||
|
||||
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
|
||||
describe("utils vimgrep_action", function()
|
||||
before_each(function()
|
||||
spy.on(vim, "cmd")
|
||||
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)
|
||||
|
|
@ -75,6 +75,11 @@ mock.reset = function()
|
|||
return lines
|
||||
end,
|
||||
},
|
||||
fn = {
|
||||
bufnr = function()
|
||||
return -1
|
||||
end,
|
||||
},
|
||||
schedule = function(callback)
|
||||
callback()
|
||||
end,
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
local config = require "ivy.config"
|
||||
|
||||
-- Constent options that will be used for the keymaps
|
||||
local opts = { noremap = true, silent = true, nowait = true }
|
||||
|
||||
|
|
@ -21,12 +23,47 @@ local function string_to_table(lines)
|
|||
return matches
|
||||
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 = {}
|
||||
|
||||
window.index = 0
|
||||
window.origin = nil
|
||||
window.window = nil
|
||||
window.buffer = nil
|
||||
window.origin_buffer = nil
|
||||
|
||||
window.initialize = function()
|
||||
window.make_buffer()
|
||||
|
|
@ -34,6 +71,7 @@ end
|
|||
|
||||
window.make_buffer = function()
|
||||
window.origin = vim.api.nvim_get_current_win()
|
||||
window.origin_buffer = vim.api.nvim_win_get_buf(0)
|
||||
|
||||
vim.api.nvim_command "botright split new"
|
||||
window.buffer = vim.api.nvim_win_get_buf(0)
|
||||
|
|
@ -57,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)
|
||||
end
|
||||
|
||||
vim.api.nvim_buf_set_keymap(window.buffer, "n", "<C-c>", "<cmd>lua vim.ivy.destroy()<CR>", opts)
|
||||
vim.api.nvim_buf_set_keymap(window.buffer, "n", "<C-u>", "<cmd>lua vim.ivy.search('')<CR>", opts)
|
||||
vim.api.nvim_buf_set_keymap(window.buffer, "n", "<C-n>", "<cmd>lua vim.ivy.next()<CR>", opts)
|
||||
vim.api.nvim_buf_set_keymap(window.buffer, "n", "<C-p>", "<cmd>lua vim.ivy.previous()<CR>", opts)
|
||||
vim.api.nvim_buf_set_keymap(window.buffer, "n", "<C-M-n>", "<cmd>lua vim.ivy.next(); vim.ivy.checkpoint()<CR>", opts)
|
||||
vim.api.nvim_buf_set_keymap(
|
||||
window.buffer,
|
||||
"n",
|
||||
"<C-M-p>",
|
||||
"<cmd>lua vim.ivy.previous(); vim.ivy.checkpoint()<CR>",
|
||||
opts
|
||||
)
|
||||
vim.api.nvim_buf_set_keymap(window.buffer, "n", "<CR>", "<cmd>lua vim.ivy.complete(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)
|
||||
local mappings = config:get { "mappings" }
|
||||
assert(mappings, "The mappings key is missing from the config, something has gone horribly wrong")
|
||||
for key, value in pairs(mappings) do
|
||||
if callbacks[value] == nil then
|
||||
error("The mapping '" .. value .. "' is not a valid ivy callback")
|
||||
end
|
||||
|
||||
vim.api.nvim_buf_set_keymap(window.buffer, "n", key, callbacks[value], opts)
|
||||
end
|
||||
end
|
||||
|
||||
window.get_current_selection = function()
|
||||
|
|
@ -104,15 +132,17 @@ window.set_items = function(items)
|
|||
items = string_to_table(items)
|
||||
end
|
||||
|
||||
local items_length = get_items_length(items)
|
||||
|
||||
-- TODO(ade): Validate the items are in the correct format. This also need to
|
||||
-- come with some descriptive messages and possible help.
|
||||
|
||||
-- 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 --" } }
|
||||
end
|
||||
|
||||
local items_length = #items
|
||||
window.index = items_length - 1
|
||||
|
||||
for index = 1, items_length do
|
||||
|
|
@ -128,6 +158,8 @@ window.set_items = function(items)
|
|||
|
||||
vim.api.nvim_win_set_height(window.window, line_count)
|
||||
window.update()
|
||||
|
||||
call_gc(items)
|
||||
end
|
||||
|
||||
window.destroy = function()
|
||||
|
|
@ -138,6 +170,7 @@ window.destroy = function()
|
|||
window.buffer = nil
|
||||
window.window = nil
|
||||
window.origin = nil
|
||||
window.origin_buffer = nil
|
||||
window.index = 0
|
||||
end
|
||||
|
||||
|
|
|
|||
32
lua/ivy/window_spec.lua
Normal file
32
lua/ivy/window_spec.lua
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
local window = require "ivy.window"
|
||||
local controller = require "ivy.controller"
|
||||
|
||||
describe("window", function()
|
||||
before_each(function()
|
||||
vim.cmd "highlight IvyMatch cterm=bold gui=bold"
|
||||
window.initialize()
|
||||
end)
|
||||
|
||||
after_each(function()
|
||||
controller.destroy()
|
||||
end)
|
||||
|
||||
it("can initialize and destroy the window", function()
|
||||
assert.is_equal(vim.api.nvim_get_current_buf(), window.buffer)
|
||||
|
||||
window.destroy()
|
||||
assert.is_equal(nil, window.buffer)
|
||||
end)
|
||||
|
||||
it("can set items", function()
|
||||
window.set_items { { content = "Line one" } }
|
||||
assert.is_equal("Line one", window.get_current_selection())
|
||||
end)
|
||||
|
||||
it("will set the items when a string is passed in", function()
|
||||
local items = table.concat({ "One", "Two", "Three" }, "\n")
|
||||
window.set_items(items)
|
||||
|
||||
assert.is_equal(items, table.concat(vim.api.nvim_buf_get_lines(window.buffer, 0, -1, true), "\n"))
|
||||
end)
|
||||
end)
|
||||
|
|
@ -1,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)
|
||||
|
|
@ -1,73 +1,19 @@
|
|||
local controller = require "ivy.controller"
|
||||
local utils = require "ivy.utils"
|
||||
local libivy = require "ivy.libivy"
|
||||
local api = require "ivy"
|
||||
|
||||
-- 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
|
||||
vim.ivy = api
|
||||
|
||||
vim.api.nvim_create_user_command("IvyAg", function()
|
||||
vim.ivy.run("AG", utils.command_finder "ag", utils.vimgrep_action())
|
||||
end, { bang = true, desc = "Run ag to search for content in files" })
|
||||
|
||||
vim.api.nvim_create_user_command("IvyFd", function()
|
||||
vim.ivy.run("Files", function(term)
|
||||
return libivy.ivy_files(term, vim.fn.getcwd())
|
||||
end, utils.file_action())
|
||||
end, { bang = true, desc = "Find files in the project" })
|
||||
|
||||
vim.api.nvim_create_user_command("IvyBuffers", function()
|
||||
vim.ivy.run("Buffers", function(input)
|
||||
local list = {}
|
||||
local buffers = vim.api.nvim_list_bufs()
|
||||
for index = 1, #buffers do
|
||||
local buffer = buffers[index]
|
||||
-- Get the relative path from the current working directory. We need to
|
||||
-- substring +2 to remove the `/` from the start of the path to give us a
|
||||
-- true relative path
|
||||
local buffer_name = vim.api.nvim_buf_get_name(buffer):sub(#vim.fn.getcwd() + 2, -1)
|
||||
local file_type = vim.api.nvim_buf_get_option(buffer, "filetype")
|
||||
if vim.api.nvim_buf_is_loaded(buffer) and file_type ~= "ivy" and #buffer_name > 0 then
|
||||
local score = libivy.ivy_match(input, buffer_name)
|
||||
if score > -200 or #input == 0 then
|
||||
table.insert(list, { score = score, content = buffer_name })
|
||||
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
|
||||
|
||||
table.sort(list, function(a, b)
|
||||
return a.score < b.score
|
||||
end)
|
||||
|
||||
return list
|
||||
end, utils.file_action())
|
||||
end, { bang = true, desc = "List all of the current open buffers" })
|
||||
|
||||
vim.api.nvim_create_user_command("IvyLines", function()
|
||||
vim.ivy.run("Lines", function(input)
|
||||
local list = {}
|
||||
|
||||
local lines = vim.api.nvim_buf_get_lines(vim.ivy.origin(), 0, -1, false)
|
||||
for index = 1, #lines do
|
||||
local line = lines[index]
|
||||
local score = libivy.ivy_match(input, line)
|
||||
if score > -200 then
|
||||
local prefix = string.rep(" ", 4 - #tostring(index)) .. index .. ": "
|
||||
table.insert(list, { score = score, content = prefix .. line })
|
||||
end
|
||||
end
|
||||
|
||||
table.sort(list, function(a, b)
|
||||
return a.score < b.score
|
||||
end)
|
||||
|
||||
return list
|
||||
end, utils.line_action())
|
||||
end, { bang = true, desc = "List all of the current open buffers" })
|
||||
|
||||
vim.api.nvim_set_keymap("n", "<leader>b", "<cmd>IvyBuffers<CR>", { nowait = true, silent = true })
|
||||
vim.api.nvim_set_keymap("n", "<leader>p", "<cmd>IvyFd<CR>", { nowait = true, silent = true })
|
||||
vim.api.nvim_set_keymap("n", "<leader>/", "<cmd>IvyAg<CR>", { nowait = true, silent = true })
|
||||
end
|
||||
end)(vim.paste)
|
||||
|
||||
vim.cmd "highlight IvyMatch cterm=bold gui=bold"
|
||||
|
|
|
|||
5
post-merge.sample
Executable file
5
post-merge.sample
Executable 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
6
renovate.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": [
|
||||
"config:base"
|
||||
]
|
||||
}
|
||||
|
|
@ -10,18 +10,27 @@ pub fn find_files(options: Options) -> Vec<String> {
|
|||
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;
|
||||
|
|
|
|||
102
rust/lib.rs
102
rust/lib.rs
|
|
@ -7,12 +7,38 @@ use std::ffi::CStr;
|
|||
use std::ffi::CString;
|
||||
use std::os::raw::{c_char, c_int};
|
||||
use std::sync::Mutex;
|
||||
use std::sync::OnceLock;
|
||||
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
// A store to the singleton instance of the ivy struct. This must not be accessed directly it must
|
||||
// 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! {
|
||||
static ref GLOBAL_FILE_CACHE: Mutex<HashMap<String, Vec<String>>> = Mutex::new(HashMap::new());
|
||||
struct Ivy {
|
||||
// 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 {
|
||||
|
|
@ -23,15 +49,17 @@ fn to_string(input: *const c_char) -> String {
|
|||
}
|
||||
|
||||
fn get_files(directory: &String) -> Vec<String> {
|
||||
let mut cache = GLOBAL_FILE_CACHE.lock().unwrap();
|
||||
if !cache.contains_key(directory) {
|
||||
let mut ivy = Ivy::global().lock().unwrap();
|
||||
if !ivy.file_cache.contains_key(directory) {
|
||||
let finder_options = finder::Options {
|
||||
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]
|
||||
|
|
@ -61,6 +89,64 @@ pub fn inner_match(pattern: String, text: String) -> 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]
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -17,8 +17,7 @@ impl Matcher {
|
|||
|
||||
pub fn score(&self, text: &str) -> i64 {
|
||||
self.matcher
|
||||
.fuzzy_indices(text, &self.pattern)
|
||||
.map(|(score, _indices)| score)
|
||||
.fuzzy_match(text, &self.pattern)
|
||||
.unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,12 +25,19 @@ pub fn sort_strings(options: Options, strings: Vec<String>) -> Vec<Match> {
|
|||
|
||||
let mut matches = strings
|
||||
.into_par_iter()
|
||||
.map(|candidate| Match {
|
||||
score: matcher.score(candidate.as_str()),
|
||||
content: candidate,
|
||||
.filter_map(|candidate| {
|
||||
let score = matcher.score(candidate.as_str());
|
||||
if score < options.minimum_score {
|
||||
None
|
||||
} else {
|
||||
Some(Match {
|
||||
score,
|
||||
content: candidate,
|
||||
})
|
||||
}
|
||||
})
|
||||
.filter(|m| m.score > options.minimum_score)
|
||||
.collect::<Vec<Match>>();
|
||||
|
||||
matches.par_sort_unstable_by(|a, b| a.score.cmp(&b.score));
|
||||
matches
|
||||
}
|
||||
|
|
|
|||
24
scripts/bench
Executable file
24
scripts/bench
Executable 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 ""
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
package.path = "lua/?.lua;" .. package.path
|
||||
local libivy = require "ivy.libivy"
|
||||
local vim_mock = require "ivy.vim_mock"
|
||||
local window = require "ivy.window"
|
||||
|
||||
local benchmark = function(name, n, callback)
|
||||
local status = {
|
||||
|
|
@ -25,7 +27,7 @@ local benchmark = function(name, n, callback)
|
|||
|
||||
print(
|
||||
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,
|
||||
status.running_total,
|
||||
status.running_total / n,
|
||||
|
|
@ -35,8 +37,8 @@ local benchmark = function(name, n, callback)
|
|||
)
|
||||
end
|
||||
|
||||
print "| Name | Total | Average | Min | Max |"
|
||||
print "|--------------------------------|---------------|---------------|---------------|---------------|"
|
||||
print "| Name | Total | Average | Min | Max |"
|
||||
print "|-------------------------------------------|---------------|---------------|---------------|---------------|"
|
||||
|
||||
benchmark("ivy_match(file.lua) 1000000x", 1000000, function()
|
||||
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()
|
||||
libivy.ivy_files("file.go", "/tmp/ivy-trees/kubernetes")
|
||||
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
8
scripts/busted.lua
Executable file
|
|
@ -0,0 +1,8 @@
|
|||
-- Script to run the busted cli tool. You can use this under nvim using be
|
||||
-- below command. Any arguments can be passed in the same as the busted cli.
|
||||
--
|
||||
-- ```bash
|
||||
-- nvim -l scripts/busted.lua
|
||||
-- ```
|
||||
vim.opt.rtp:append(vim.fn.getcwd())
|
||||
require "busted.runner" { standalone = false }
|
||||
30
scripts/integration.lua
Normal file
30
scripts/integration.lua
Normal 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
|
||||
Loading…
Reference in a new issue