chore: better ffi

This commit is contained in:
Ade Attwood 2022-08-28 17:05:14 +01:00
parent 39febd82e2
commit 75bd90ab4a
5 changed files with 50 additions and 27 deletions

View file

@ -7,9 +7,12 @@ local ffi = require "ffi"
local ivy_c = ffi.load(library_path) local ivy_c = ffi.load(library_path)
ffi.cdef [[ ffi.cdef [[
typedef struct { int score; const char* content; } match;
typedef struct { int len; match* matches; } match_list;
void ivy_init(const char*); void ivy_init(const char*);
int ivy_match(const char*, const char*); int ivy_match(const char*, const char*);
char* ivy_files(const char*, const char*); match_list* ivy_files(const char*, const char*);
]] ]]
local libivy = {} local libivy = {}
@ -23,7 +26,7 @@ libivy.ivy_match = function(pattern, text)
end end
libivy.ivy_files = function(pattern, base_dir) libivy.ivy_files = function(pattern, base_dir)
return ffi.string(ivy_c.ivy_files(pattern, base_dir)) return ivy_c.ivy_files(pattern, base_dir)
end end
return libivy return libivy

View file

@ -1,4 +1,5 @@
local libivy = require "ivy.libivy" local libivy = require "ivy.libivy"
local ffi = require "ffi"
it("should run a simple match", function(t) it("should run a simple match", function(t)
local score = libivy.ivy_match("term", "I am a serch term") local score = libivy.ivy_match("term", "I am a serch term")

View file

@ -1,3 +1,5 @@
local ffi = require "ffi"
-- Constent options that will be used for the keymaps -- Constent options that will be used for the keymaps
local opts = { noremap = true, silent = true, nowait = true } local opts = { noremap = true, silent = true, nowait = true }
@ -22,12 +24,12 @@ local function string_to_table(lines)
end end
local function set_items_string(buffer, lines) local function set_items_string(buffer, lines)
vim.api.nvim_buf_set_lines(buffer, 0, 9999, false, string_to_table(lines)) vim.api.nvim_buf_set_lines(buffer, 0, -1, false, string_to_table(lines))
end end
local function set_items_array(buffer, lines) local function set_items_array(buffer, lines)
if type(lines[1]) == "string" then if type(lines[1]) == "string" then
vim.api.nvim_buf_set_lines(buffer, 0, 9999, false, lines) vim.api.nvim_buf_set_lines(buffer, 0, -1, false, lines)
else else
for i = 1, #lines do for i = 1, #lines do
vim.api.nvim_buf_set_lines(buffer, i - 1, 9999, false, { lines[i][2] }) vim.api.nvim_buf_set_lines(buffer, i - 1, 9999, false, { lines[i][2] })
@ -112,7 +114,13 @@ window.update = function()
end end
window.set_items = function(items) window.set_items = function(items)
if #items == 0 then vim.api.nvim_buf_set_lines(window.get_buffer(), 0, -1, false, {})
if items.len ~= nil then
for i = 0, items.len - 1 do
vim.api.nvim_buf_set_lines(window.get_buffer(), i - 1, -1, false, { ffi.string(items.matches[i].content) })
end
elseif #items == 0 then
vim.api.nvim_buf_set_lines(window.get_buffer(), 0, 9999, false, { "-- No Items --" }) vim.api.nvim_buf_set_lines(window.get_buffer(), 0, 9999, false, { "-- No Items --" })
elseif type(items) == "string" then elseif type(items) == "string" then
set_items_string(window.get_buffer(), items) set_items_string(window.get_buffer(), items)

View file

@ -4,9 +4,15 @@ mod sorter;
use std::collections::HashMap; use std::collections::HashMap;
use std::ffi::CStr; use std::ffi::CStr;
use std::ffi::CString;
use std::os::raw::{c_char, c_int}; use std::os::raw::{c_char, c_int};
use std::sync::Mutex; use std::sync::Mutex;
use std::mem;
#[repr(C)]
pub struct FFiMatchList {
len: c_int,
matches: *mut sorter::Match
}
#[macro_use] #[macro_use]
extern crate lazy_static; extern crate lazy_static;
@ -22,6 +28,15 @@ fn to_string(input: *const c_char) -> String {
.to_string() .to_string()
} }
fn to_ffi_match_list(mut list: Vec<sorter::Match>) -> *const FFiMatchList {
list.shrink_to_fit();
let matches = list.as_mut_ptr();
let len: c_int = list.len().try_into().unwrap();
mem::forget(list);
return Box::into_raw(Box::new(FFiMatchList { len, matches }))
}
fn get_files(directory: &String) -> Vec<String> { fn get_files(directory: &String) -> Vec<String> {
let mut cache = GLOBAL_FILE_CACHE.lock().unwrap(); let mut cache = GLOBAL_FILE_CACHE.lock().unwrap();
if !cache.contains_key(directory) { if !cache.contains_key(directory) {
@ -55,32 +70,21 @@ pub fn inner_match(pattern: String, text: String) -> i32 {
} }
#[no_mangle] #[no_mangle]
pub extern "C" fn ivy_files(c_pattern: *const c_char, c_base_dir: *const c_char) -> *const c_char { pub extern "C" fn ivy_files(c_pattern: *const c_char, c_base_dir: *const c_char) -> *const FFiMatchList {
let pattern = to_string(c_pattern); let pattern = to_string(c_pattern);
let directory = to_string(c_base_dir); let directory = to_string(c_base_dir);
let output = inner_files(pattern, directory); return inner_files(pattern, directory);
CString::new(output).unwrap().into_raw()
} }
pub fn inner_files(pattern: String, base_dir: String) -> String { pub fn inner_files(pattern: String, base_dir: String) -> *const FFiMatchList {
let mut output = String::new();
// Bail out early if the pattern is empty; it's never going to find anything // Bail out early if the pattern is empty; it's never going to find anything
if pattern.is_empty() { if pattern.is_empty() {
return output; return to_ffi_match_list(Vec::new());
} }
let files = get_files(&base_dir); let files = get_files(&base_dir);
let sorter_options = sorter::Options::new(pattern); let sorter_options = sorter::Options::new(pattern);
to_ffi_match_list(sorter::sort_strings(sorter_options, files))
let files = sorter::sort_strings(sorter_options, files);
for file in files.iter() {
output.push_str(&file.content);
output.push('\n');
}
output
} }

View file

@ -1,11 +1,18 @@
use super::matcher; use super::matcher;
use rayon::prelude::*; use rayon::prelude::*;
use std::os::raw::{c_char, c_int};
use std::ffi::CString;
#[repr(C)]
pub struct Match { pub struct Match {
pub score: i64, pub score: c_int,
pub content: String, pub content: *const c_char
// pub score: i64,
// pub content: String,
} }
unsafe impl Send for Match {}
pub struct Options { pub struct Options {
pub pattern: String, pub pattern: String,
pub minimum_score: i64, pub minimum_score: i64,
@ -26,10 +33,10 @@ pub fn sort_strings(options: Options, strings: Vec<String>) -> Vec<Match> {
let mut matches = strings let mut matches = strings
.into_par_iter() .into_par_iter()
.map(|candidate| Match { .map(|candidate| Match {
score: matcher.score(candidate.as_str()), score: matcher.score(candidate.as_str()) as i32,
content: candidate, content: CString::new(candidate.clone().to_string()).unwrap().into_raw(),
}) })
.filter(|m| m.score > options.minimum_score) .filter(|m| m.score > options.minimum_score as i32)
.collect::<Vec<Match>>(); .collect::<Vec<Match>>();
matches.par_sort_unstable_by(|a, b| a.score.cmp(&b.score)); matches.par_sort_unstable_by(|a, b| a.score.cmp(&b.score));
matches matches