From 75bd90ab4afcf7298bbfc1674da81a2ed6114030 Mon Sep 17 00:00:00 2001 From: Ade Attwood Date: Sun, 28 Aug 2022 17:05:14 +0100 Subject: [PATCH] chore: better ffi --- lua/ivy/libivy.lua | 7 +++++-- lua/ivy/libivy_test.lua | 1 + lua/ivy/window.lua | 14 +++++++++++--- rust/lib.rs | 38 +++++++++++++++++++++----------------- rust/sorter.rs | 17 ++++++++++++----- 5 files changed, 50 insertions(+), 27 deletions(-) diff --git a/lua/ivy/libivy.lua b/lua/ivy/libivy.lua index 0b00c2f..cc162ad 100644 --- a/lua/ivy/libivy.lua +++ b/lua/ivy/libivy.lua @@ -7,9 +7,12 @@ local ffi = require "ffi" local ivy_c = ffi.load(library_path) ffi.cdef [[ + typedef struct { int score; const char* content; } match; + typedef struct { int len; match* matches; } match_list; + void ivy_init(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 = {} @@ -23,7 +26,7 @@ libivy.ivy_match = function(pattern, text) end 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 return libivy diff --git a/lua/ivy/libivy_test.lua b/lua/ivy/libivy_test.lua index 28fb527..9ecf50a 100644 --- a/lua/ivy/libivy_test.lua +++ b/lua/ivy/libivy_test.lua @@ -1,4 +1,5 @@ local libivy = require "ivy.libivy" +local ffi = require "ffi" it("should run a simple match", function(t) local score = libivy.ivy_match("term", "I am a serch term") diff --git a/lua/ivy/window.lua b/lua/ivy/window.lua index addefc1..53a2ca2 100644 --- a/lua/ivy/window.lua +++ b/lua/ivy/window.lua @@ -1,3 +1,5 @@ +local ffi = require "ffi" + -- Constent options that will be used for the keymaps local opts = { noremap = true, silent = true, nowait = true } @@ -22,12 +24,12 @@ local function string_to_table(lines) end 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 local function set_items_array(buffer, lines) 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 for i = 1, #lines do vim.api.nvim_buf_set_lines(buffer, i - 1, 9999, false, { lines[i][2] }) @@ -112,7 +114,13 @@ window.update = function() end 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 --" }) elseif type(items) == "string" then set_items_string(window.get_buffer(), items) diff --git a/rust/lib.rs b/rust/lib.rs index e2aca28..5ca802d 100644 --- a/rust/lib.rs +++ b/rust/lib.rs @@ -4,9 +4,15 @@ mod sorter; use std::collections::HashMap; use std::ffi::CStr; -use std::ffi::CString; use std::os::raw::{c_char, c_int}; use std::sync::Mutex; +use std::mem; + +#[repr(C)] +pub struct FFiMatchList { + len: c_int, + matches: *mut sorter::Match +} #[macro_use] extern crate lazy_static; @@ -22,6 +28,15 @@ fn to_string(input: *const c_char) -> String { .to_string() } +fn to_ffi_match_list(mut list: Vec) -> *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 { let mut cache = GLOBAL_FILE_CACHE.lock().unwrap(); if !cache.contains_key(directory) { @@ -55,32 +70,21 @@ pub fn inner_match(pattern: String, text: String) -> i32 { } #[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 directory = to_string(c_base_dir); - let output = inner_files(pattern, directory); - - CString::new(output).unwrap().into_raw() + return inner_files(pattern, directory); } -pub fn inner_files(pattern: String, base_dir: String) -> String { - let mut output = String::new(); - +pub fn inner_files(pattern: String, base_dir: String) -> *const FFiMatchList { // Bail out early if the pattern is empty; it's never going to find anything if pattern.is_empty() { - return output; + return to_ffi_match_list(Vec::new()); } let files = get_files(&base_dir); let sorter_options = sorter::Options::new(pattern); - - let files = sorter::sort_strings(sorter_options, files); - for file in files.iter() { - output.push_str(&file.content); - output.push('\n'); - } - - output + to_ffi_match_list(sorter::sort_strings(sorter_options, files)) } diff --git a/rust/sorter.rs b/rust/sorter.rs index f81f622..3009830 100644 --- a/rust/sorter.rs +++ b/rust/sorter.rs @@ -1,11 +1,18 @@ use super::matcher; use rayon::prelude::*; +use std::os::raw::{c_char, c_int}; +use std::ffi::CString; +#[repr(C)] pub struct Match { - pub score: i64, - pub content: String, + pub score: c_int, + pub content: *const c_char + // pub score: i64, + // pub content: String, } +unsafe impl Send for Match {} + pub struct Options { pub pattern: String, pub minimum_score: i64, @@ -26,10 +33,10 @@ pub fn sort_strings(options: Options, strings: Vec) -> Vec { let mut matches = strings .into_par_iter() .map(|candidate| Match { - score: matcher.score(candidate.as_str()), - content: candidate, + score: matcher.score(candidate.as_str()) as i32, + 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::>(); matches.par_sort_unstable_by(|a, b| a.score.cmp(&b.score)); matches