1
0
mirror of https://github.com/SpaceVim/SpaceVim.git synced 2025-04-13 17:30:40 +08:00

feat(fzf): add bundle telescope fzf extension

This commit is contained in:
wsdjeg 2022-06-02 21:44:41 +08:00
parent 297f58477b
commit 237ec35fd7
20 changed files with 2930 additions and 13 deletions

View File

@ -50,6 +50,7 @@ function! SpaceVim#layers#telescope#plugins() abort
call add(plugins, [g:_spacevim_root_dir . 'bundle/telescope-menu', {'merged' : 0}])
call add(plugins, [g:_spacevim_root_dir . 'bundle/telescope-ctags-outline.nvim', {'merged' : 0}])
call add(plugins, [g:_spacevim_root_dir . 'bundle/neoyank.vim', { 'merged' : 0}])
call add(plugins, [g:_spacevim_root_dir . 'bundle/telescope-fzf-native.nvim', { 'merged' : 0}])
return plugins
endfunction

View File

@ -0,0 +1,7 @@
AllowShortFunctionsOnASingleLine: false
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
IndentCaseLabels: false
SortIncludes: false
ColumnLimit: 80
IndentWidth: 2

View File

@ -0,0 +1 @@
github: Conni2461

View File

@ -0,0 +1,88 @@
name: CI
on: [push, pull_request]
jobs:
gcc:
name: c build and tests
runs-on: ${{ matrix.os }}
strategy:
matrix:
include:
- os: ubuntu-20.04
compiler: gcc
- os: ubuntu-20.04
compiler: clang
- os: macos-10.15
compiler: gcc
- os: macos-10.15
compiler: clang
steps:
- uses: actions/checkout@v2
- name: Prepare
env:
CC: ${{ matrix.compiler }}
run: |
cc --version
git clone https://github.com/Conni2461/examiner
cd examiner
make && sudo make install
- name: Build
env:
CC: ${{ matrix.compiler }}
LD_LIBRARY_PATH: /usr/lib:/usr/local/lib
run: make
- name: Tests
env:
CC: ${{ matrix.compiler }}
LD_LIBRARY_PATH: /usr/lib:/usr/local/lib
run: make test
windows:
name: windows
runs-on: windows-2019
steps:
- uses: actions/checkout@v2
- uses: lukka/get-cmake@latest
- name: Build
run: |
cmake -S. -Bbuild -DCMAKE_BUILD_TYPE=Release
cmake --build build --config Release
cmake --install build --prefix build
nvim-tests:
name: nvim-tests
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-20.04, macos-10.15]
include:
- os: ubuntu-20.04
url: https://github.com/neovim/neovim/releases/download/nightly/nvim-linux64.tar.gz
- os: macos-10.15
url: https://github.com/neovim/neovim/releases/download/nightly/nvim-macos.tar.gz
steps:
- uses: actions/checkout@v2
- run: date +%F > todays-date
- name: Restore cache for today's nightly.
uses: actions/cache@v2
with:
path: _neovim
key: ${{ matrix.os }}-${{ hashFiles('todays-date') }}
- name: Prepare
run: |
test -d _neovim || {
mkdir -p _neovim
curl -sL ${{ matrix.url }} | tar xzf - --strip-components=1 -C "${PWD}/_neovim"
}
mkdir -p ~/.local/share/nvim/site/pack/vendor/start
git clone --depth 1 https://github.com/nvim-lua/plenary.nvim ~/.local/share/nvim/site/pack/vendor/start/plenary.nvim
ln -s $(pwd) ~/.local/share/nvim/site/pack/vendor/start
- name: Build
run: make
- name: Tests
run: |
export PATH="${PWD}/_neovim/bin:${PATH}"
export VIM="${PWD}/_neovim/share/nvim/runtime"
nvim --version
make ntest

View File

@ -0,0 +1,42 @@
name: Linting and style checking
on: [push, pull_request]
jobs:
lint:
name: Lint
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- name: Prepare
run: |
sudo apt-get update
sudo apt-get install luarocks
sudo luarocks install luacheck
- name: Lint
run: make lint
clangformat:
name: clangformat
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- name: Prepare clang-format
run: |
sudo apt-get update
sudo apt-get install clang-format
- name: Format
run: make format
stylua:
name: stylua
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- uses: JohnnyMorganz/stylua-action@1.0.0
with:
token: ${{ secrets.GITHUB_TOKEN }}
# CLI arguments
args: --color always --check lua/

View File

@ -0,0 +1,4 @@
build/
.cache/
compile_commands.json

View File

@ -0,0 +1,16 @@
-- cache false so i don't need sudo upstream
cache = false
std = luajit
codes = true
self = false
-- Glorious list of warnings: https://luacheck.readthedocs.io/en/stable/warnings.html
ignore = {
"212", -- Unused argument, In the case of callback function, _arg_name is easier to understand than _, so this option is set to off.
"122", -- Indirectly setting a readonly global
}
-- Global objects defined by the C code
read_globals = {
"vim",
}

View File

@ -0,0 +1,6 @@
column_width = 120
line_endings = "Unix"
indent_type = "Spaces"
indent_width = 2
quote_style = "AutoPreferDouble"
call_parentheses = "None"

View File

@ -0,0 +1,20 @@
cmake_minimum_required(VERSION 3.2)
project(fzf C)
add_library(${PROJECT_NAME} SHARED "src/fzf.c")
set_target_properties(${PROJECT_NAME} PROPERTIES
PREFIX "lib"
C_STANDARD 99
WINDOWS_EXPORT_ALL_SYMBOLS ON
)
target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src)
if (CMAKE_C_COMPILER_ID STREQUAL "MSVC")
target_compile_options(${PROJECT_NAME} PRIVATE /W4)
else ()
target_compile_options(${PROJECT_NAME} PRIVATE -Wall)
endif ()
install(TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_BINARY_DIR})

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 Simon Hauser
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,40 @@
CFLAGS = -Wall -Werror -fpic -std=gnu99
ifeq ($(OS),Windows_NT)
MKD = -mkdir
RM = cmd /C rmdir /Q /S
CC = gcc
TARGET := libfzf.dll
else
MKD = mkdir -p
RM = rm -rf
TARGET := libfzf.so
endif
all: build/$(TARGET)
build/$(TARGET): src/fzf.c src/fzf.h
$(MKD) build
$(CC) -O3 $(CFLAGS) -shared src/fzf.c -o build/$(TARGET)
build/test: build/$(TARGET) test/test.c
$(CC) -Og -ggdb3 $(CFLAGS) test/test.c -o build/test -I./src -L./build -lfzf -lexaminer
.PHONY: lint format clangdhappy clean test ntest
lint:
luacheck lua
format:
clang-format --style=file --dry-run -Werror src/fzf.c src/fzf.h test/test.c
test: build/test
@LD_LIBRARY_PATH=${PWD}/build:${PWD}/examiner/build:${LD_LIBRARY_PATH} ./build/test
ntest:
nvim --headless --noplugin -u test/minrc.vim -c "PlenaryBustedDirectory test/ { minimal_init = './test/minrc.vim' }"
clangdhappy:
compiledb make
clean:
$(RM) build

View File

@ -0,0 +1,167 @@
# telescope-fzf-native.nvim
**fzf-native** is a `c` port of **[fzf][fzf]**. It only covers the algorithm and
implements few functions to support calculating the score.
This means that the [fzf syntax](https://github.com/junegunn/fzf#search-syntax)
is supported:
| Token | Match type | Description |
| --------- | -------------------------- | ------------------------------------ |
| `sbtrkt` | fuzzy-match | Items that match `sbtrkt` |
| `'wild` | exact-match (quoted) | Items that include `wild` |
| `^music` | prefix-exact-match | Items that start with `music` |
| `.mp3$` | suffix-exact-match | Items that end with `.mp3` |
| `!fire` | inverse-exact-match | Items that do not include `fire` |
| `!^music` | inverse-prefix-exact-match | Items that do not start with `music` |
| `!.mp3$` | inverse-suffix-exact-match | Items that do not end with `.mp3` |
A single bar character term acts as an OR operator. For example, the following
query matches entries that start with `core` and end with either `go`, `rb`,
or `py`.
```
^core go$ | rb$ | py$
```
This is an advantage over the more simpler `fzy` algorithm, which is also
available for telescope (as native component or as lua component).
## Installation
To get **fzf-native** working, you need to build it with either `cmake` or `make`. As of now, we do not ship binaries.
Both install methods will be supported going forward.
### CMake (Windows, Linux, MacOS)
This requires:
- CMake, and the Microsoft C++ Build Tools on Windows
- CMake, make, and GCC or Clang on Linux and MacOS
#### vim-plug
```viml
Plug 'nvim-telescope/telescope-fzf-native.nvim', { 'do': 'cmake -S. -Bbuild -DCMAKE_BUILD_TYPE=Release && cmake --build build --config Release && cmake --install build --prefix build' }
```
#### packer.nvim
```lua
use {'nvim-telescope/telescope-fzf-native.nvim', run = 'cmake -S. -Bbuild -DCMAKE_BUILD_TYPE=Release && cmake --build build --config Release && cmake --install build --prefix build' }
```
### Make (Linux, MacOS, Windows with MinGW)
This requires `gcc` or `clang` and `make`
#### vim-plug
```viml
Plug 'nvim-telescope/telescope-fzf-native.nvim', { 'do': 'make' }
```
#### packer.nvim
```lua
use {'nvim-telescope/telescope-fzf-native.nvim', run = 'make' }
```
## Telescope Setup and Configuration:
```lua
-- You dont need to set any of these options. These are the default ones. Only
-- the loading is important
require('telescope').setup {
extensions = {
fzf = {
fuzzy = true, -- false will only do exact matching
override_generic_sorter = true, -- override the generic sorter
override_file_sorter = true, -- override the file sorter
case_mode = "smart_case", -- or "ignore_case" or "respect_case"
-- the default case_mode is "smart_case"
}
}
}
-- To get fzf loaded and working with telescope, you need to call
-- load_extension, somewhere after setup function:
require('telescope').load_extension('fzf')
```
## Developer Interface
This section is only addressed towards developers who plan to use the library
(c or lua bindings).
This section is not addressed towards users of the telescope extension.
### C Interface
```c
fzf_slab_t *slab = fzf_make_default_slab();
/* fzf_case_mode enum : CaseSmart = 0, CaseIgnore, CaseRespect
* normalize bool : always set to false because its not implemented yet.
* This is reserved for future use
* pattern char* : pattern you want to match. e.g. "src | lua !.c$
* fuzzy bool : enable or disable fuzzy matching
*/
fzf_pattern_t *pattern = fzf_parse_pattern(CaseSmart, false, "src | lua !.c$", true);
/* you can get the score/position for as many items as you want */
int score = fzf_get_score(line, pattern, slab);
fzf_position_t *pos = fzf_get_positions(line, pattern, slab);
fzf_free_positions(pos);
fzf_free_pattern(pattern);
fzf_free_slab(slab);
```
### Lua Interface
```lua
local fzf = require('fzf_lib')
local slab = fzf.allocate_slab()
-- pattern: string
-- case_mode: number with 0 = smart_case, 1 = ignore_case, 2 = respect_case
-- fuzzy: enable or disable fuzzy matching. default true
local pattern_obj = fzf.parse_pattern(pattern, case_mode, fuzzy)
-- you can get the score/position for as many items as you want
-- line: string
-- score: number
local score = fzf.get_score(line, pattern_obj, slab)
-- table (does not have to be freed)
local pos = fzf.get_pos(line, pattern_obj, slab)
fzf.free_pattern(pattern_obj)
fzf.free_slab(slab)
```
## Disclaimer
This projects implements **[fzf][fzf]** algorithm in c. So there might be
differences in matching. I don't guarantee completeness.
### TODO
Stuff still missing that is present in **[fzf][fzf]**.
- [ ] normalize
- [ ] case for unicode (i don't think this works currently)
## Benchmark
Comparison with fzy-native and fzy-lua with a table containing 240201 file
strings. It calculated the score and position (if score > 0) for each of these
strings with the pattern that is listed below:
![benchmark 1](https://raw.githubusercontent.com/wiki/nvim-telescope/telescope.nvim/imgs/bench1.png)
![benchmark 2](https://raw.githubusercontent.com/wiki/nvim-telescope/telescope.nvim/imgs/bench2.png)
## Credit
All credit for the algorithm goes to junegunn and his work on **[fzf][fzf]**.
This is merely a c fork distributed under MIT for telescope.
[fzf]: https://github.com/junegunn/fzf

View File

@ -0,0 +1,85 @@
local ffi = require "ffi"
local library_path = (function()
local dirname = string.sub(debug.getinfo(1).source, 2, #"/fzf_lib.lua" * -1)
if package.config:sub(1, 1) == "\\" then
return dirname .. "../build/libfzf.dll"
else
return dirname .. "../build/libfzf.so"
end
end)()
local native = ffi.load(library_path)
ffi.cdef [[
typedef struct {} fzf_i16_t;
typedef struct {} fzf_i32_t;
typedef struct {
fzf_i16_t I16;
fzf_i32_t I32;
} fzf_slab_t;
typedef struct {} fzf_term_set_t;
typedef struct {
fzf_term_set_t **ptr;
size_t size;
size_t cap;
} fzf_pattern_t;
typedef struct {
uint32_t *data;
size_t size;
size_t cap;
} fzf_position_t;
fzf_position_t *fzf_get_positions(const char *text, fzf_pattern_t *pattern, fzf_slab_t *slab);
void fzf_free_positions(fzf_position_t *pos);
int32_t fzf_get_score(const char *text, fzf_pattern_t *pattern, fzf_slab_t *slab);
fzf_pattern_t *fzf_parse_pattern(int32_t case_mode, bool normalize, char *pattern, bool fuzzy);
void fzf_free_pattern(fzf_pattern_t *pattern);
fzf_slab_t *fzf_make_default_slab(void);
void fzf_free_slab(fzf_slab_t *slab);
]]
local fzf = {}
fzf.get_score = function(input, pattern_struct, slab)
return native.fzf_get_score(input, pattern_struct, slab)
end
fzf.get_pos = function(input, pattern_struct, slab)
local pos = native.fzf_get_positions(input, pattern_struct, slab)
if pos == nil then
return
end
local res = {}
for i = 1, tonumber(pos.size) do
res[i] = pos.data[i - 1] + 1
end
native.fzf_free_positions(pos)
return res
end
fzf.parse_pattern = function(pattern, case_mode, fuzzy)
case_mode = case_mode == nil and 0 or case_mode
fuzzy = fuzzy == nil and true or fuzzy
local c_str = ffi.new("char[?]", #pattern + 1)
ffi.copy(c_str, pattern)
return native.fzf_parse_pattern(case_mode, false, c_str, fuzzy)
end
fzf.free_pattern = function(p)
native.fzf_free_pattern(p)
end
fzf.allocate_slab = function()
return native.fzf_make_default_slab()
end
fzf.free_slab = function(s)
native.fzf_free_slab(s)
end
return fzf

View File

@ -0,0 +1,148 @@
local fzf = require "fzf_lib"
local sorters = require "telescope.sorters"
local case_enum = setmetatable({
["smart_case"] = 0,
["ignore_case"] = 1,
["respect_case"] = 2,
}, {
__index = function(_, k)
error(string.format("%s is not a valid case mode", k))
end,
__newindex = function()
error "Don't set new things"
end,
})
local get_fzf_sorter = function(opts)
local case_mode = case_enum[opts.case_mode]
local fuzzy_mode = opts.fuzzy == nil and true or opts.fuzzy
local post_or = false
local post_inv = false
local post_escape = false
local get_struct = function(self, prompt)
local struct = self.state.prompt_cache[prompt]
if not struct then
struct = fzf.parse_pattern(prompt, case_mode, fuzzy_mode)
self.state.prompt_cache[prompt] = struct
end
return struct
end
local clear_filter_fun = function(self, prompt)
local filter = "^(" .. self._delimiter .. "(%S+)" .. "[" .. self._delimiter .. "%s]" .. ")"
local matched = prompt:match(filter)
if matched == nil then
return prompt
end
return prompt:sub(#matched + 1, -1)
end
return sorters.Sorter:new {
init = function(self)
self.state.slab = fzf.allocate_slab()
self.state.prompt_cache = {}
if self.filter_function then
self.__highlight_prefilter = clear_filter_fun
end
end,
destroy = function(self)
for _, v in pairs(self.state.prompt_cache) do
fzf.free_pattern(v)
end
self.state.prompt_cache = {}
if self.state.slab ~= nil then
fzf.free_slab(self.state.slab)
self.state.slab = nil
end
end,
start = function(self, prompt)
local last = prompt:sub(-1, -1)
if last == "|" then
self._discard_state.filtered = {}
post_or = true
elseif last == " " and post_or then
self._discard_state.filtered = {}
elseif post_or then
self._discard_state.filtered = {}
post_or = false
else
post_or = false
end
if last == "\\" and not post_escape then
self._discard_state.filtered = {}
post_escape = true
else
self._discard_state.filtered = {}
post_escape = false
end
if last == "!" and not post_inv then
post_inv = true
self._discard_state.filtered = {}
elseif post_inv then
self._discard_state.filtered = {}
elseif post_inv and " " then
post_inv = false
end
end,
discard = true,
scoring_function = function(self, prompt, line)
local obj = get_struct(self, prompt)
local score = fzf.get_score(line, obj, self.state.slab)
if score == 0 then
return -1
else
return 1 / score
end
end,
highlighter = function(self, prompt, display)
if self.__highlight_prefilter then
prompt = self:__highlight_prefilter(prompt)
end
return fzf.get_pos(display, get_struct(self, prompt), self.state.slab)
end,
}
end
local fast_extend = function(opts, conf)
local ret = {}
ret.case_mode = vim.F.if_nil(opts.case_mode, conf.case_mode)
ret.fuzzy = vim.F.if_nil(opts.fuzzy, conf.fuzzy)
return ret
end
return require("telescope").register_extension {
setup = function(ext_config, config)
local override_file = vim.F.if_nil(ext_config.override_file_sorter, true)
local override_generic = vim.F.if_nil(ext_config.override_generic_sorter, true)
local conf = {}
conf.case_mode = vim.F.if_nil(ext_config.case_mode, "smart_case")
conf.fuzzy = vim.F.if_nil(ext_config.fuzzy, true)
if override_file then
config.file_sorter = function(opts)
opts = opts or {}
return get_fzf_sorter(fast_extend(opts, conf))
end
end
if override_generic then
config.generic_sorter = function(opts)
opts = opts or {}
return get_fzf_sorter(fast_extend(opts, conf))
end
end
end,
exports = {
native_fzf_sorter = function(opts)
return get_fzf_sorter(opts or { case_mode = "smart_case", fuzzy = true })
end,
},
}

1270
bundle/telescope-fzf-native.nvim/src/fzf.c vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,111 @@
#ifndef FZF_H_
#define FZF_H_
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
typedef struct {
int16_t *data;
size_t size;
size_t cap;
bool allocated;
} fzf_i16_t;
typedef struct {
int32_t *data;
size_t size;
size_t cap;
bool allocated;
} fzf_i32_t;
typedef struct {
uint32_t *data;
size_t size;
size_t cap;
} fzf_position_t;
typedef struct {
int32_t start;
int32_t end;
int32_t score;
} fzf_result_t;
typedef struct {
fzf_i16_t I16;
fzf_i32_t I32;
} fzf_slab_t;
typedef struct {
size_t size_16;
size_t size_32;
} fzf_slab_config_t;
typedef struct {
const char *data;
size_t size;
} fzf_string_t;
typedef fzf_result_t (*fzf_algo_t)(bool, bool, fzf_string_t *, fzf_string_t *,
fzf_position_t *, fzf_slab_t *);
typedef enum { CaseSmart = 0, CaseIgnore, CaseRespect } fzf_case_types;
typedef struct {
fzf_algo_t fn;
bool inv;
char *ptr;
void *text;
bool case_sensitive;
} fzf_term_t;
typedef struct {
fzf_term_t *ptr;
size_t size;
size_t cap;
} fzf_term_set_t;
typedef struct {
fzf_term_set_t **ptr;
size_t size;
size_t cap;
bool only_inv;
} fzf_pattern_t;
fzf_result_t fzf_fuzzy_match_v1(bool case_sensitive, bool normalize,
fzf_string_t *text, fzf_string_t *pattern,
fzf_position_t *pos, fzf_slab_t *slab);
fzf_result_t fzf_fuzzy_match_v2(bool case_sensitive, bool normalize,
fzf_string_t *text, fzf_string_t *pattern,
fzf_position_t *pos, fzf_slab_t *slab);
fzf_result_t fzf_exact_match_naive(bool case_sensitive, bool normalize,
fzf_string_t *text, fzf_string_t *pattern,
fzf_position_t *pos, fzf_slab_t *slab);
fzf_result_t fzf_prefix_match(bool case_sensitive, bool normalize,
fzf_string_t *text, fzf_string_t *pattern,
fzf_position_t *pos, fzf_slab_t *slab);
fzf_result_t fzf_suffix_match(bool case_sensitive, bool normalize,
fzf_string_t *text, fzf_string_t *pattern,
fzf_position_t *pos, fzf_slab_t *slab);
fzf_result_t fzf_equal_match(bool case_sensitive, bool normalize,
fzf_string_t *text, fzf_string_t *pattern,
fzf_position_t *pos, fzf_slab_t *slab);
/* interface */
fzf_pattern_t *fzf_parse_pattern(fzf_case_types case_mode, bool normalize,
char *pattern, bool fuzzy);
void fzf_free_pattern(fzf_pattern_t *pattern);
int32_t fzf_get_score(const char *text, fzf_pattern_t *pattern,
fzf_slab_t *slab);
fzf_position_t *fzf_pos_array(size_t len);
fzf_position_t *fzf_get_positions(const char *text, fzf_pattern_t *pattern,
fzf_slab_t *slab);
void fzf_free_positions(fzf_position_t *pos);
fzf_slab_t *fzf_make_slab(fzf_slab_config_t config);
fzf_slab_t *fzf_make_default_slab(void);
void fzf_free_slab(fzf_slab_t *slab);
#endif // FZF_H_

View File

@ -0,0 +1,111 @@
local fzf = require "fzf_lib"
local eq = assert.are.same
local is_nil = assert.is_nil
describe("fzf", function()
local slab = fzf.allocate_slab()
it("can get the score for simple pattern", function()
local p = fzf.parse_pattern("fzf", 0)
eq(80, fzf.get_score("src/fzf", p, slab))
eq(0, fzf.get_score("asdf", p, slab))
eq(54, fzf.get_score("fasdzasdf", p, slab))
fzf.free_pattern(p)
end)
it("can get the score for or pattern", function()
local p = fzf.parse_pattern("lua | src | 'doc | ^asdfasdf | file$", 0)
eq(80, fzf.get_score("src/fzf.c", p, slab))
eq(0, fzf.get_score("build/libfzf", p, slab))
eq(80, fzf.get_score("lua/fzf_lib.lua", p, slab))
eq(80, fzf.get_score("doc/fzf.txt", p, slab))
eq(0, fzf.get_score("daonc/fzf.txt", p, slab))
eq(200, fzf.get_score("asdfasdf", p, slab))
eq(0, fzf.get_score("noasdfasdf", p, slab))
eq(104, fzf.get_score("not_file", p, slab))
eq(0, fzf.get_score("not_file.txt", p, slab))
fzf.free_pattern(p)
end)
it("can get the score for and pattern", function()
local p = fzf.parse_pattern("fzf !lib", 0)
eq(80, fzf.get_score("src/fzf.c", p, slab))
eq(0, fzf.get_score("lua/fzf_lib.lua", p, slab))
eq(0, fzf.get_score("build/libfzf", p, slab))
fzf.free_pattern(p)
local p = fzf.parse_pattern("fzf src c", 0)
eq(192, fzf.get_score("src/fzf.c", p, slab))
eq(0, fzf.get_score("lua/fzf_lib.lua", p, slab))
eq(0, fzf.get_score("build/libfzf", p, slab))
fzf.free_pattern(p)
end)
it("can get the score for patterns with escaped space", function()
local p = fzf.parse_pattern("\\ ", 0)
eq(32, fzf.get_score("src file", p, slab))
eq(0, fzf.get_score("src_file", p, slab))
eq(32, fzf.get_score("another another file", p, slab))
fzf.free_pattern(p)
end)
it("can get the score for issue 11", function()
local p = fzf.parse_pattern("feature/1337-some-times-i-have-a-lot-of-hyphens", 0)
eq(1136, fzf.get_score("feature/1337-some-times-i-have-a-lot-of-hyphens", p, slab))
fzf.free_pattern(p)
end)
it("can get the pos for simple pattern", function()
local p = fzf.parse_pattern("fzf", 0)
eq({ 7, 6, 5 }, fzf.get_pos("src/fzf", p, slab))
is_nil(fzf.get_pos("asdf", p, slab))
eq({ 9, 5, 1 }, fzf.get_pos("fasdzasdf", p, slab))
fzf.free_pattern(p)
end)
it("can get the pos for or pattern", function()
local p = fzf.parse_pattern("lua | src | 'doc | ^asdfasdf | file$", 0)
eq({ 3, 2, 1 }, fzf.get_pos("src/fzf.c", p, slab))
is_nil(fzf.get_pos("build/libfzf", p, slab))
eq({ 3, 2, 1 }, fzf.get_pos("lua/fzf_lib.lua", p, slab))
eq({ 1, 2, 3 }, fzf.get_pos("doc/fzf.txt", p, slab))
is_nil(fzf.get_pos("daonc/fzf.txt", p, slab))
eq({ 1, 2, 3, 4, 5, 6, 7, 8 }, fzf.get_pos("asdfasdf", p, slab))
is_nil(fzf.get_pos("noasdfasdf", p, slab))
eq({ 5, 6, 7, 8 }, fzf.get_pos("not_file", p, slab))
is_nil(fzf.get_pos("not_file.txt", p, slab))
fzf.free_pattern(p)
end)
it("can get the pos for and pattern", function()
local p = fzf.parse_pattern("fzf !lib", 0)
eq({ 7, 6, 5 }, fzf.get_pos("src/fzf.c", p, slab))
is_nil(fzf.get_pos("lua/fzf_lib.lua", p, slab))
is_nil(fzf.get_pos("build/libfzf", p, slab))
fzf.free_pattern(p)
p = fzf.parse_pattern("fzf src c", 0)
eq({ 7, 6, 5, 3, 2, 1, 9 }, fzf.get_pos("src/fzf.c", p, slab))
is_nil(fzf.get_pos("lua/fzf_lib.lua", p, slab))
is_nil(fzf.get_pos("build/libfzf", p, slab))
fzf.free_pattern(p)
end)
it("can get the pos for patterns with escaped space", function()
local p = fzf.parse_pattern("\\ ", 0)
eq({ 4 }, fzf.get_pos("src file", p, slab))
is_nil(fzf.get_pos("src_file", p, slab))
eq({ 8 }, fzf.get_pos("another another file", p, slab))
fzf.free_pattern(p)
end)
it("can get the pos for issue 11", function()
local p = fzf.parse_pattern("feature/1337-some-times-i-have-a-lot-of-hyphens", 0)
local expected = {}
for i = 47, 1, -1 do
table.insert(expected, i)
end
eq(expected, fzf.get_pos("feature/1337-some-times-i-have-a-lot-of-hyphens", p, slab))
fzf.free_pattern(p)
end)
fzf.free_slab(slab)
end)

View File

@ -0,0 +1,4 @@
set rtp+=.
set rtp+=../plenary.nvim/
runtime! plugin/plenary.vim

View File

@ -0,0 +1,771 @@
#include "fzf.h"
#include <examiner.h>
#include <stdlib.h>
#include <string.h>
typedef enum {
ScoreMatch = 16,
ScoreGapStart = -3,
ScoreGapExtension = -1,
BonusBoundary = ScoreMatch / 2,
BonusNonWord = ScoreMatch / 2,
BonusCamel123 = BonusBoundary + ScoreGapExtension,
BonusConsecutive = -(ScoreGapStart + ScoreGapExtension),
BonusFirstCharMultiplier = 2,
} score_t;
#define call_alg(alg, case, txt, pat, assert_block) \
{ \
fzf_position_t *pos = fzf_pos_array(0); \
fzf_result_t res = alg(case, false, txt, pat, pos, NULL); \
assert_block; \
fzf_free_positions(pos); \
} \
{ \
fzf_position_t *pos = fzf_pos_array(0); \
fzf_slab_t *slab = fzf_make_default_slab(); \
fzf_result_t res = alg(case, false, txt, pat, pos, slab); \
assert_block; \
fzf_free_positions(pos); \
fzf_free_slab(slab); \
}
static int8_t max_i8(int8_t a, int8_t b) {
return a > b ? a : b;
}
#define MATCH_WRAPPER(nn, og) \
fzf_result_t nn(bool case_sensitive, bool normalize, const char *text, \
const char *pattern, fzf_position_t *pos, \
fzf_slab_t *slab) { \
fzf_string_t input = {.data = text, .size = strlen(text)}; \
fzf_string_t pattern_wrap = {.data = pattern, .size = strlen(pattern)}; \
return og(case_sensitive, normalize, &input, &pattern_wrap, pos, slab); \
}
MATCH_WRAPPER(fuzzy_match_v2, fzf_fuzzy_match_v2);
MATCH_WRAPPER(fuzzy_match_v1, fzf_fuzzy_match_v1);
MATCH_WRAPPER(exact_match_naive, fzf_exact_match_naive);
MATCH_WRAPPER(prefix_match, fzf_prefix_match);
MATCH_WRAPPER(suffix_match, fzf_suffix_match);
MATCH_WRAPPER(equal_match, fzf_equal_match);
// TODO(conni2461): Implement normalize and test it here
TEST(FuzzyMatchV2, case1) {
call_alg(fuzzy_match_v2, true, "So Danco Samba", "So", {
ASSERT_EQ(0, res.start);
ASSERT_EQ(2, res.end);
ASSERT_EQ(56, res.score);
ASSERT_EQ(2, pos->size);
ASSERT_EQ(1, pos->data[0]);
ASSERT_EQ(0, pos->data[1]);
});
}
TEST(FuzzyMatchV2, case2) {
call_alg(fuzzy_match_v2, false, "So Danco Samba", "sodc", {
ASSERT_EQ(0, res.start);
ASSERT_EQ(7, res.end);
ASSERT_EQ(89, res.score);
ASSERT_EQ(4, pos->size);
ASSERT_EQ(6, pos->data[0]);
ASSERT_EQ(3, pos->data[1]);
ASSERT_EQ(1, pos->data[2]);
ASSERT_EQ(0, pos->data[3]);
});
}
TEST(FuzzyMatchV2, case3) {
call_alg(fuzzy_match_v2, false, "Danco", "danco", {
ASSERT_EQ(0, res.start);
ASSERT_EQ(5, res.end);
ASSERT_EQ(128, res.score);
ASSERT_EQ(5, pos->size);
ASSERT_EQ(4, pos->data[0]);
ASSERT_EQ(3, pos->data[1]);
ASSERT_EQ(2, pos->data[2]);
ASSERT_EQ(1, pos->data[3]);
ASSERT_EQ(0, pos->data[4]);
});
}
TEST(FuzzyMatchV2, case4) {
call_alg(fuzzy_match_v2, false, "fooBarbaz1", "obz", {
ASSERT_EQ(2, res.start);
ASSERT_EQ(9, res.end);
int expected_score =
ScoreMatch * 3 + BonusCamel123 + ScoreGapStart + ScoreGapExtension * 3;
ASSERT_EQ(expected_score, res.score);
});
}
TEST(FuzzyMatchV2, case5) {
call_alg(fuzzy_match_v2, false, "foo bar baz", "fbb", {
ASSERT_EQ(0, res.start);
ASSERT_EQ(9, res.end);
int expected_score =
ScoreMatch * 3 + BonusBoundary * BonusFirstCharMultiplier +
BonusBoundary * 2 + 2 * ScoreGapStart + 4 * ScoreGapExtension;
ASSERT_EQ(expected_score, res.score);
});
}
TEST(FuzzyMatchV2, case6) {
call_alg(fuzzy_match_v2, false, "/AutomatorDocument.icns", "rdoc", {
ASSERT_EQ(9, res.start);
ASSERT_EQ(13, res.end);
int expected_score = ScoreMatch * 4 + BonusCamel123 + BonusConsecutive * 2;
ASSERT_EQ(expected_score, res.score);
});
}
TEST(FuzzyMatchV2, case7) {
call_alg(fuzzy_match_v2, false, "/man1/zshcompctl.1", "zshc", {
ASSERT_EQ(6, res.start);
ASSERT_EQ(10, res.end);
int expected_score = ScoreMatch * 4 +
BonusBoundary * BonusFirstCharMultiplier +
BonusBoundary * 3;
ASSERT_EQ(expected_score, res.score);
});
}
TEST(FuzzyMatchV2, case8) {
call_alg(fuzzy_match_v2, false, "/.oh-my-zsh/cache", "zshc", {
ASSERT_EQ(8, res.start);
ASSERT_EQ(13, res.end);
int expected_score = ScoreMatch * 4 +
BonusBoundary * BonusFirstCharMultiplier +
BonusBoundary * 3 + ScoreGapStart;
ASSERT_EQ(expected_score, res.score);
});
}
TEST(FuzzyMatchV2, case9) {
call_alg(fuzzy_match_v2, false, "ab0123 456", "12356", {
ASSERT_EQ(3, res.start);
ASSERT_EQ(10, res.end);
int expected_score = ScoreMatch * 5 + BonusConsecutive * 3 + ScoreGapStart +
ScoreGapExtension;
ASSERT_EQ(expected_score, res.score);
});
}
TEST(FuzzyMatchV2, case10) {
call_alg(fuzzy_match_v2, false, "abc123 456", "12356", {
ASSERT_EQ(3, res.start);
ASSERT_EQ(10, res.end);
int expected_score = ScoreMatch * 5 +
BonusCamel123 * BonusFirstCharMultiplier +
BonusCamel123 * 2 + BonusConsecutive + ScoreGapStart +
ScoreGapExtension;
ASSERT_EQ(expected_score, res.score);
});
}
TEST(FuzzyMatchV2, case11) {
call_alg(fuzzy_match_v2, false, "foo/bar/baz", "fbb", {
ASSERT_EQ(0, res.start);
ASSERT_EQ(9, res.end);
int expected_score =
ScoreMatch * 3 + BonusBoundary * BonusFirstCharMultiplier +
BonusBoundary * 2 + 2 * ScoreGapStart + 4 * ScoreGapExtension;
ASSERT_EQ(expected_score, res.score);
});
}
TEST(FuzzyMatchV2, case12) {
call_alg(fuzzy_match_v2, false, "fooBarBaz", "fbb", {
ASSERT_EQ(0, res.start);
ASSERT_EQ(7, res.end);
int expected_score =
ScoreMatch * 3 + BonusBoundary * BonusFirstCharMultiplier +
BonusCamel123 * 2 + 2 * ScoreGapStart + 2 * ScoreGapExtension;
ASSERT_EQ(expected_score, res.score);
});
}
TEST(FuzzyMatchV2, case13) {
call_alg(fuzzy_match_v2, false, "foo barbaz", "fbb", {
ASSERT_EQ(0, res.start);
ASSERT_EQ(8, res.end);
int expected_score =
ScoreMatch * 3 + BonusBoundary * BonusFirstCharMultiplier +
BonusBoundary + ScoreGapStart * 2 + ScoreGapExtension * 3;
ASSERT_EQ(expected_score, res.score);
});
}
TEST(FuzzyMatchV2, case14) {
call_alg(fuzzy_match_v2, false, "fooBar Baz", "foob", {
ASSERT_EQ(0, res.start);
ASSERT_EQ(4, res.end);
int expected_score = ScoreMatch * 4 +
BonusBoundary * BonusFirstCharMultiplier +
BonusBoundary * 3;
ASSERT_EQ(expected_score, res.score);
});
}
TEST(FuzzyMatchV2, case15) {
call_alg(fuzzy_match_v2, false, "xFoo-Bar Baz", "foo-b", {
ASSERT_EQ(1, res.start);
ASSERT_EQ(6, res.end);
int expected_score = ScoreMatch * 5 +
BonusCamel123 * BonusFirstCharMultiplier +
BonusCamel123 * 2 + BonusNonWord + BonusBoundary;
ASSERT_EQ(expected_score, res.score);
});
}
TEST(FuzzyMatchV2, case16) {
call_alg(fuzzy_match_v2, true, "fooBarbaz", "oBz", {
ASSERT_EQ(2, res.start);
ASSERT_EQ(9, res.end);
int expected_score =
ScoreMatch * 3 + BonusCamel123 + ScoreGapStart + ScoreGapExtension * 3;
ASSERT_EQ(expected_score, res.score);
});
}
TEST(FuzzyMatchV2, case17) {
call_alg(fuzzy_match_v2, true, "Foo/Bar/Baz", "FBB", {
ASSERT_EQ(0, res.start);
ASSERT_EQ(9, res.end);
int expected_score = ScoreMatch * 3 +
BonusBoundary * (BonusFirstCharMultiplier + 2) +
ScoreGapStart * 2 + ScoreGapExtension * 4;
ASSERT_EQ(expected_score, res.score);
});
}
TEST(FuzzyMatchV2, case18) {
call_alg(fuzzy_match_v2, true, "FooBarBaz", "FBB", {
ASSERT_EQ(0, res.start);
ASSERT_EQ(7, res.end);
int expected_score =
ScoreMatch * 3 + BonusBoundary * BonusFirstCharMultiplier +
BonusCamel123 * 2 + ScoreGapStart * 2 + ScoreGapExtension * 2;
ASSERT_EQ(expected_score, res.score);
});
}
TEST(FuzzyMatchV2, case19) {
call_alg(fuzzy_match_v2, true, "FooBar Baz", "FooB", {
ASSERT_EQ(0, res.start);
ASSERT_EQ(4, res.end);
int expected_score =
ScoreMatch * 4 + BonusBoundary * BonusFirstCharMultiplier +
BonusBoundary * 2 + max_i8(BonusCamel123, BonusBoundary);
ASSERT_EQ(expected_score, res.score);
});
}
TEST(FuzzyMatchV2, case20) {
call_alg(fuzzy_match_v2, true, "foo-bar", "o-ba", {
ASSERT_EQ(2, res.start);
ASSERT_EQ(6, res.end);
int expected_score = ScoreMatch * 4 + BonusBoundary * 3;
ASSERT_EQ(expected_score, res.score);
});
}
TEST(FuzzyMatchV2, case21) {
call_alg(fuzzy_match_v2, true, "fooBarbaz", "oBZ", {
ASSERT_EQ(-1, res.start);
ASSERT_EQ(-1, res.end);
ASSERT_EQ(0, res.score);
});
}
TEST(FuzzyMatchV2, case22) {
call_alg(fuzzy_match_v2, true, "Foo Bar Baz", "fbb", {
ASSERT_EQ(-1, res.start);
ASSERT_EQ(-1, res.end);
ASSERT_EQ(0, res.score);
});
}
TEST(FuzzyMatchV2, case23) {
call_alg(fuzzy_match_v2, true, "fooBarbaz", "fooBarbazz", {
ASSERT_EQ(-1, res.start);
ASSERT_EQ(-1, res.end);
ASSERT_EQ(0, res.score);
});
}
TEST(FuzzyMatchV1, case1) {
call_alg(fuzzy_match_v1, true, "So Danco Samba", "So", {
ASSERT_EQ(0, res.start);
ASSERT_EQ(2, res.end);
ASSERT_EQ(56, res.score);
ASSERT_EQ(2, pos->size);
ASSERT_EQ(0, pos->data[0]);
ASSERT_EQ(1, pos->data[1]);
});
}
TEST(FuzzyMatchV1, case2) {
call_alg(fuzzy_match_v1, false, "So Danco Samba", "sodc", {
ASSERT_EQ(0, res.start);
ASSERT_EQ(7, res.end);
ASSERT_EQ(89, res.score);
ASSERT_EQ(4, pos->size);
ASSERT_EQ(0, pos->data[0]);
ASSERT_EQ(1, pos->data[1]);
ASSERT_EQ(3, pos->data[2]);
ASSERT_EQ(6, pos->data[3]);
});
}
TEST(FuzzyMatchV1, case3) {
call_alg(fuzzy_match_v1, false, "Danco", "danco", {
ASSERT_EQ(0, res.start);
ASSERT_EQ(5, res.end);
ASSERT_EQ(128, res.score);
ASSERT_EQ(5, pos->size);
ASSERT_EQ(0, pos->data[0]);
ASSERT_EQ(1, pos->data[1]);
ASSERT_EQ(2, pos->data[2]);
ASSERT_EQ(3, pos->data[3]);
ASSERT_EQ(4, pos->data[4]);
});
}
TEST(ExactMatch, case1) {
call_alg(exact_match_naive, true, "So Danco Samba", "So", {
ASSERT_EQ(0, res.start);
ASSERT_EQ(2, res.end);
ASSERT_EQ(56, res.score);
});
}
TEST(ExactMatch, case2) {
call_alg(exact_match_naive, false, "So Danco Samba", "sodc", {
ASSERT_EQ(-1, res.start);
ASSERT_EQ(-1, res.end);
ASSERT_EQ(0, res.score);
});
}
TEST(ExactMatch, case3) {
call_alg(exact_match_naive, false, "Danco", "danco", {
ASSERT_EQ(0, res.start);
ASSERT_EQ(5, res.end);
ASSERT_EQ(128, res.score);
});
}
TEST(PrefixMatch, case1) {
call_alg(prefix_match, true, "So Danco Samba", "So", {
ASSERT_EQ(0, res.start);
ASSERT_EQ(2, res.end);
ASSERT_EQ(56, res.score);
});
}
TEST(PrefixMatch, case2) {
call_alg(prefix_match, false, "So Danco Samba", "sodc", {
ASSERT_EQ(-1, res.start);
ASSERT_EQ(-1, res.end);
ASSERT_EQ(0, res.score);
});
}
TEST(PrefixMatch, case3) {
call_alg(prefix_match, false, "Danco", "danco", {
ASSERT_EQ(0, res.start);
ASSERT_EQ(5, res.end);
ASSERT_EQ(128, res.score);
});
}
TEST(SuffixMatch, case1) {
call_alg(suffix_match, true, "So Danco Samba", "So", {
ASSERT_EQ(-1, res.start);
ASSERT_EQ(-1, res.end);
ASSERT_EQ(0, res.score);
});
}
TEST(SuffixMatch, case2) {
call_alg(suffix_match, false, "So Danco Samba", "sodc", {
ASSERT_EQ(-1, res.start);
ASSERT_EQ(-1, res.end);
ASSERT_EQ(0, res.score);
});
}
TEST(SuffixMatch, case3) {
call_alg(suffix_match, false, "Danco", "danco", {
ASSERT_EQ(0, res.start);
ASSERT_EQ(5, res.end);
ASSERT_EQ(128, res.score);
});
}
TEST(EqualMatch, case1) {
call_alg(equal_match, true, "So Danco Samba", "So", {
ASSERT_EQ(-1, res.start);
ASSERT_EQ(-1, res.end);
ASSERT_EQ(0, res.score);
});
}
TEST(EqualMatch, case2) {
call_alg(equal_match, false, "So Danco Samba", "sodc", {
ASSERT_EQ(-1, res.start);
ASSERT_EQ(-1, res.end);
ASSERT_EQ(0, res.score);
});
}
TEST(EqualMatch, case3) {
call_alg(equal_match, false, "Danco", "danco", {
ASSERT_EQ(0, res.start);
ASSERT_EQ(5, res.end);
ASSERT_EQ(128, res.score);
});
}
TEST(PatternParsing, empty) {
fzf_pattern_t *pat = fzf_parse_pattern(CaseSmart, false, "", true);
ASSERT_EQ(0, pat->size);
ASSERT_EQ(0, pat->cap);
ASSERT_FALSE(pat->only_inv);
fzf_free_pattern(pat);
}
TEST(PatternParsing, simple) {
fzf_pattern_t *pat = fzf_parse_pattern(CaseSmart, false, "lua", true);
ASSERT_EQ(1, pat->size);
ASSERT_EQ(1, pat->cap);
ASSERT_FALSE(pat->only_inv);
ASSERT_EQ(1, pat->ptr[0]->size);
ASSERT_EQ(1, pat->ptr[0]->cap);
ASSERT_EQ((void *)fzf_fuzzy_match_v2, pat->ptr[0]->ptr[0].fn);
ASSERT_EQ("lua", ((fzf_string_t *)(pat->ptr[0]->ptr[0].text))->data);
ASSERT_FALSE(pat->ptr[0]->ptr[0].case_sensitive);
fzf_free_pattern(pat);
}
TEST(PatternParsing, withEscapedSpace) {
fzf_pattern_t *pat = fzf_parse_pattern(CaseSmart, false, "file\\ ", true);
ASSERT_EQ(1, pat->size);
ASSERT_EQ(1, pat->cap);
ASSERT_FALSE(pat->only_inv);
ASSERT_EQ(1, pat->ptr[0]->size);
ASSERT_EQ(1, pat->ptr[0]->cap);
ASSERT_EQ((void *)fzf_fuzzy_match_v2, pat->ptr[0]->ptr[0].fn);
ASSERT_EQ("file ", ((fzf_string_t *)(pat->ptr[0]->ptr[0].text))->data);
ASSERT_FALSE(pat->ptr[0]->ptr[0].case_sensitive);
fzf_free_pattern(pat);
}
TEST(PatternParsing, withComplexEscapedSpace) {
fzf_pattern_t *pat =
fzf_parse_pattern(CaseSmart, false, "file\\ with\\ space", true);
ASSERT_EQ(1, pat->size);
ASSERT_EQ(1, pat->cap);
ASSERT_FALSE(pat->only_inv);
ASSERT_EQ(1, pat->ptr[0]->size);
ASSERT_EQ(1, pat->ptr[0]->cap);
ASSERT_EQ((void *)fzf_fuzzy_match_v2, pat->ptr[0]->ptr[0].fn);
ASSERT_EQ("file with space",
((fzf_string_t *)(pat->ptr[0]->ptr[0].text))->data);
ASSERT_FALSE(pat->ptr[0]->ptr[0].case_sensitive);
fzf_free_pattern(pat);
}
TEST(PatternParsing, withEscapedSpaceAndNormalSpace) {
fzf_pattern_t *pat = fzf_parse_pattern(CaseSmart, false, "file\\ new", true);
ASSERT_EQ(2, pat->size);
ASSERT_EQ(2, pat->cap);
ASSERT_FALSE(pat->only_inv);
ASSERT_EQ(1, pat->ptr[0]->size);
ASSERT_EQ(1, pat->ptr[0]->cap);
ASSERT_EQ(1, pat->ptr[1]->size);
ASSERT_EQ(1, pat->ptr[1]->cap);
ASSERT_EQ((void *)fzf_fuzzy_match_v2, pat->ptr[0]->ptr[0].fn);
ASSERT_EQ("file ", ((fzf_string_t *)(pat->ptr[0]->ptr[0].text))->data);
ASSERT_FALSE(pat->ptr[0]->ptr[0].case_sensitive);
ASSERT_EQ((void *)fzf_fuzzy_match_v2, pat->ptr[1]->ptr[0].fn);
ASSERT_EQ("new", ((fzf_string_t *)(pat->ptr[1]->ptr[0].text))->data);
ASSERT_FALSE(pat->ptr[1]->ptr[0].case_sensitive);
fzf_free_pattern(pat);
}
TEST(PatternParsing, invert) {
fzf_pattern_t *pat = fzf_parse_pattern(CaseSmart, false, "!Lua", true);
ASSERT_EQ(1, pat->size);
ASSERT_EQ(1, pat->cap);
ASSERT_TRUE(pat->only_inv);
ASSERT_EQ(1, pat->ptr[0]->size);
ASSERT_EQ(1, pat->ptr[0]->cap);
ASSERT_EQ((void *)fzf_exact_match_naive, pat->ptr[0]->ptr[0].fn);
ASSERT_EQ("Lua", ((fzf_string_t *)(pat->ptr[0]->ptr[0].text))->data);
ASSERT_TRUE(pat->ptr[0]->ptr[0].case_sensitive);
ASSERT_TRUE(pat->ptr[0]->ptr[0].inv);
fzf_free_pattern(pat);
}
TEST(PatternParsing, invertMultiple) {
fzf_pattern_t *pat = fzf_parse_pattern(CaseSmart, false, "!fzf !test", true);
ASSERT_EQ(2, pat->size);
ASSERT_EQ(2, pat->cap);
ASSERT_TRUE(pat->only_inv);
ASSERT_EQ(1, pat->ptr[0]->size);
ASSERT_EQ(1, pat->ptr[0]->cap);
ASSERT_EQ(1, pat->ptr[1]->size);
ASSERT_EQ(1, pat->ptr[1]->cap);
ASSERT_EQ((void *)fzf_exact_match_naive, pat->ptr[0]->ptr[0].fn);
ASSERT_EQ("fzf", ((fzf_string_t *)(pat->ptr[0]->ptr[0].text))->data);
ASSERT_FALSE(pat->ptr[0]->ptr[0].case_sensitive);
ASSERT_TRUE(pat->ptr[0]->ptr[0].inv);
ASSERT_EQ((void *)fzf_exact_match_naive, pat->ptr[1]->ptr[0].fn);
ASSERT_EQ("test", ((fzf_string_t *)(pat->ptr[1]->ptr[0].text))->data);
ASSERT_FALSE(pat->ptr[1]->ptr[0].case_sensitive);
ASSERT_TRUE(pat->ptr[1]->ptr[0].inv);
fzf_free_pattern(pat);
}
TEST(PatternParsing, smartCase) {
fzf_pattern_t *pat = fzf_parse_pattern(CaseSmart, false, "Lua", true);
ASSERT_EQ(1, pat->size);
ASSERT_EQ(1, pat->cap);
ASSERT_FALSE(pat->only_inv);
ASSERT_EQ(1, pat->ptr[0]->size);
ASSERT_EQ(1, pat->ptr[0]->cap);
ASSERT_EQ((void *)fzf_fuzzy_match_v2, pat->ptr[0]->ptr[0].fn);
ASSERT_EQ("Lua", ((fzf_string_t *)(pat->ptr[0]->ptr[0].text))->data);
ASSERT_TRUE(pat->ptr[0]->ptr[0].case_sensitive);
fzf_free_pattern(pat);
}
TEST(PatternParsing, simpleOr) {
fzf_pattern_t *pat = fzf_parse_pattern(CaseSmart, false, "'src | ^Lua", true);
ASSERT_EQ(1, pat->size);
ASSERT_EQ(1, pat->cap);
ASSERT_FALSE(pat->only_inv);
ASSERT_EQ(2, pat->ptr[0]->size);
ASSERT_EQ(2, pat->ptr[0]->cap);
ASSERT_EQ((void *)fzf_exact_match_naive, pat->ptr[0]->ptr[0].fn);
ASSERT_EQ("src", ((fzf_string_t *)(pat->ptr[0]->ptr[0].text))->data);
ASSERT_FALSE(pat->ptr[0]->ptr[0].case_sensitive);
ASSERT_EQ((void *)fzf_prefix_match, pat->ptr[0]->ptr[1].fn);
ASSERT_EQ("Lua", ((fzf_string_t *)(pat->ptr[0]->ptr[1].text))->data);
ASSERT_TRUE(pat->ptr[0]->ptr[1].case_sensitive);
fzf_free_pattern(pat);
}
TEST(PatternParsing, complexAnd) {
fzf_pattern_t *pat = fzf_parse_pattern(CaseSmart, false,
".lua$ 'previewer !'term !asdf", true);
ASSERT_EQ(4, pat->size);
ASSERT_EQ(4, pat->cap);
ASSERT_FALSE(pat->only_inv);
ASSERT_EQ(1, pat->ptr[0]->size);
ASSERT_EQ(1, pat->ptr[0]->cap);
ASSERT_EQ(1, pat->ptr[1]->size);
ASSERT_EQ(1, pat->ptr[1]->cap);
ASSERT_EQ(1, pat->ptr[2]->size);
ASSERT_EQ(1, pat->ptr[2]->cap);
ASSERT_EQ(1, pat->ptr[3]->size);
ASSERT_EQ(1, pat->ptr[3]->cap);
ASSERT_EQ((void *)fzf_suffix_match, pat->ptr[0]->ptr[0].fn);
ASSERT_EQ(".lua", ((fzf_string_t *)(pat->ptr[0]->ptr[0].text))->data);
ASSERT_FALSE(pat->ptr[0]->ptr[0].case_sensitive);
ASSERT_EQ((void *)fzf_exact_match_naive, pat->ptr[1]->ptr[0].fn);
ASSERT_EQ("previewer", ((fzf_string_t *)(pat->ptr[1]->ptr[0].text))->data);
ASSERT_EQ(0, pat->ptr[1]->ptr[0].case_sensitive);
ASSERT_EQ((void *)fzf_fuzzy_match_v2, pat->ptr[2]->ptr[0].fn);
ASSERT_EQ("term", ((fzf_string_t *)(pat->ptr[2]->ptr[0].text))->data);
ASSERT_FALSE(pat->ptr[2]->ptr[0].case_sensitive);
ASSERT_TRUE(pat->ptr[2]->ptr[0].inv);
ASSERT_EQ((void *)fzf_exact_match_naive, pat->ptr[3]->ptr[0].fn);
ASSERT_EQ("asdf", ((fzf_string_t *)(pat->ptr[3]->ptr[0].text))->data);
ASSERT_FALSE(pat->ptr[3]->ptr[0].case_sensitive);
ASSERT_TRUE(pat->ptr[3]->ptr[0].inv);
fzf_free_pattern(pat);
}
static void score_wrapper(char *pattern, char **input, int *expected) {
fzf_slab_t *slab = fzf_make_default_slab();
fzf_pattern_t *pat = fzf_parse_pattern(CaseSmart, false, pattern, true);
for (size_t i = 0; input[i] != NULL; ++i) {
ASSERT_EQ(expected[i], fzf_get_score(input[i], pat, slab));
}
fzf_free_pattern(pat);
fzf_free_slab(slab);
}
TEST(ScoreIntegration, simple) {
char *input[] = {"fzf", "main.c", "src/fzf", "fz/noooo", NULL};
int expected[] = {0, 1, 0, 1};
score_wrapper("!fzf", input, expected);
}
TEST(ScoreIntegration, invertAnd) {
char *input[] = {"src/fzf.c", "README.md", "lua/asdf", "test/test.c", NULL};
int expected[] = {0, 1, 1, 0};
score_wrapper("!fzf !test", input, expected);
}
TEST(ScoreIntegration, withEscapedSpace) {
char *input[] = {"file ", "file lua", "lua", NULL};
int expected[] = {0, 200, 0};
score_wrapper("file\\ lua", input, expected);
}
TEST(ScoreIntegration, onlyEscapedSpace) {
char *input[] = {"file with space", "file lua", "lua", "src", "test", NULL};
int expected[] = {32, 32, 0, 0, 0};
score_wrapper("\\ ", input, expected);
}
TEST(ScoreIntegration, simpleOr) {
char *input[] = {"src/fzf.h", "README.md", "build/fzf",
"lua/fzf_lib.lua", "Lua/fzf_lib.lua", NULL};
int expected[] = {80, 0, 0, 0, 80};
score_wrapper("'src | ^Lua", input, expected);
}
TEST(ScoreIntegration, complexTerm) {
char *input[] = {"lua/random_previewer", "README.md",
"previewers/utils.lua", "previewers/buffer.lua",
"previewers/term.lua", NULL};
int expected[] = {0, 0, 328, 328, 0};
score_wrapper(".lua$ 'previewer !'term", input, expected);
}
static void pos_wrapper(char *pattern, char **input, int **expected) {
fzf_slab_t *slab = fzf_make_default_slab();
fzf_pattern_t *pat = fzf_parse_pattern(CaseSmart, false, pattern, true);
for (size_t i = 0; input[i] != NULL; ++i) {
fzf_position_t *pos = fzf_get_positions(input[i], pat, slab);
if (!pos) {
ASSERT_EQ((void *)pos, expected[i]);
continue;
}
// Verify that the size is correct
if (expected[i]) {
ASSERT_EQ(-1, expected[i][pos->size]);
} else {
ASSERT_EQ(0, pos->size);
}
ASSERT_EQ_MEM(expected[i], pos->data, pos->size * sizeof(pos->data[0]));
fzf_free_positions(pos);
}
fzf_free_pattern(pat);
fzf_free_slab(slab);
}
TEST(PosIntegration, simple) {
char *input[] = {"src/fzf.c", "src/fzf.h",
"lua/fzf_lib.lua", "lua/telescope/_extensions/fzf.lua",
"README.md", NULL};
int match1[] = {6, 5, 4, -1};
int match2[] = {6, 5, 4, -1};
int match3[] = {6, 5, 4, -1};
int match4[] = {28, 27, 26, -1};
int *expected[] = {match1, match2, match3, match4, NULL};
pos_wrapper("fzf", input, expected);
}
TEST(PosIntegration, invert) {
char *input[] = {"fzf", "main.c", "src/fzf", "fz/noooo", NULL};
int *expected[] = {NULL, NULL, NULL, NULL, NULL};
pos_wrapper("!fzf", input, expected);
}
TEST(PosIntegration, andWithSecondInvert) {
char *input[] = {"src/fzf.c", "lua/fzf_lib.lua", "build/libfzf", NULL};
int match1[] = {6, 5, 4, -1};
int *expected[] = {match1, NULL, NULL};
pos_wrapper("fzf !lib", input, expected);
}
TEST(PosIntegration, andAllInvert) {
char *input[] = {"src/fzf.c", "README.md", "lua/asdf", "test/test.c", NULL};
int *expected[] = {NULL, NULL, NULL, NULL};
pos_wrapper("!fzf !test", input, expected);
}
TEST(PosIntegration, withEscapedSpace) {
char *input[] = {"file ", "file lua", "lua", NULL};
int match1[] = {7, 6, 5, 4, 3, 2, 1, 0, -1};
int *expected[] = {NULL, match1, NULL};
pos_wrapper("file\\ lua", input, expected);
}
TEST(PosIntegration, onlyEscapedSpace) {
char *input[] = {"file with space", "lul lua", "lua", "src", "test", NULL};
int match1[] = {4, -1};
int match2[] = {3, -1};
int *expected[] = {match1, match2, NULL, NULL, NULL};
pos_wrapper("\\ ", input, expected);
}
TEST(PosIntegration, simpleOr) {
char *input[] = {"src/fzf.h", "README.md", "build/fzf",
"lua/fzf_lib.lua", "Lua/fzf_lib.lua", NULL};
int match1[] = {0, 1, 2, -1};
int match2[] = {0, 1, 2, -1};
int *expected[] = {match1, NULL, NULL, NULL, match2};
pos_wrapper("'src | ^Lua", input, expected);
}
TEST(PosIntegration, orMemLeak) {
char *input[] = {"src/fzf.h", NULL};
int match1[] = {2, 1, 0, -1};
int *expected[] = {match1};
pos_wrapper("src | src", input, expected);
}
TEST(PosIntegration, complexTerm) {
char *input[] = {"lua/random_previewer", "README.md",
"previewers/utils.lua", "previewers/buffer.lua",
"previewers/term.lua", NULL};
int match1[] = {16, 17, 18, 19, 0, 1, 2, 3, 4, 5, 6, 7, 8, -1};
int match2[] = {17, 18, 19, 20, 0, 1, 2, 3, 4, 5, 6, 7, 8, -1};
int *expected[] = {NULL, NULL, match1, match2, NULL};
pos_wrapper(".lua$ 'previewer !'term", input, expected);
}
int main(int argc, char **argv) {
exam_init(argc, argv);
return exam_run();
}

View File

@ -3,25 +3,29 @@ lua require('telescope').load_extension('messages')
lua require('telescope').load_extension('project')
lua require('telescope').load_extension('scriptnames')
lua require('telescope').load_extension('neoyank')
if filereadable(g:_spacevim_root_dir . 'bundle/telescope-fzf-native.nvim/build/libfzf.so')
\ || filereadable(g:_spacevim_root_dir . 'bundle/telescope-fzf-native.nvim/build/libfzf.dll')
lua require('telescope').load_extension('fzf')
endif
lua <<EOF
local actions = require("telescope.actions")
require("telescope").setup{
defaults = {
mappings = {
i = {
-- the default key binding should same as other fuzzy finder layer
-- tab move to next
["<C-j>"] = actions.move_selection_next,
["<Tab>"] = actions.move_selection_next,
["<C-k>"] = actions.move_selection_previous,
["<S-Tab>"] = actions.move_selection_previous,
["<Esc>"] = actions.close,
["<C-h>"] = "which_key"
defaults = {
mappings = {
i = {
-- the default key binding should same as other fuzzy finder layer
-- tab move to next
["<C-j>"] = actions.move_selection_next,
["<Tab>"] = actions.move_selection_next,
["<C-k>"] = actions.move_selection_previous,
["<S-Tab>"] = actions.move_selection_previous,
["<Esc>"] = actions.close,
["<C-h>"] = "which_key"
},
},
sorting_strategy = "ascending",
layout_config = {
prompt_position = "bottom"
layout_config = {
prompt_position = "bottom"
}
}
}