run grep in a different thread to not block UI
parent
670ae631f5
commit
73b35dfece
|
@ -1,10 +1,18 @@
|
|||
use std::{error::Error, ffi::OsString, path::Path, str::FromStr};
|
||||
use std::{
|
||||
error::Error,
|
||||
ffi::OsString,
|
||||
path::Path,
|
||||
str::FromStr,
|
||||
sync::mpsc::{Receiver, Sender},
|
||||
thread,
|
||||
};
|
||||
|
||||
use grep::{
|
||||
regex::RegexMatcher,
|
||||
regex::{RegexMatcher, RegexMatcherBuilder},
|
||||
searcher::{BinaryDetection, SearcherBuilder, Sink, SinkError},
|
||||
};
|
||||
use plugin_rs_bindings::{Buffer, Closure, Hook, InputMap, Key, PaletteColor, Plugin};
|
||||
use std::sync::mpsc::channel;
|
||||
use walkdir::WalkDir;
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -73,7 +81,10 @@ impl Sink for SimpleSink {
|
|||
}
|
||||
|
||||
fn search(pattern: &str, paths: &[OsString]) -> Result<SimpleSink, Box<dyn Error>> {
|
||||
let matcher = RegexMatcher::new_line_matcher(pattern)?;
|
||||
let matcher = RegexMatcherBuilder::new()
|
||||
.case_smart(true)
|
||||
.fixed_strings(true)
|
||||
.build(pattern)?;
|
||||
let mut searcher = SearcherBuilder::new()
|
||||
.binary_detection(BinaryDetection::quit(b'\x00'))
|
||||
.line_number(true)
|
||||
|
@ -81,7 +92,15 @@ fn search(pattern: &str, paths: &[OsString]) -> Result<SimpleSink, Box<dyn Error
|
|||
|
||||
let mut sink = SimpleSink::default();
|
||||
for path in paths {
|
||||
for result in WalkDir::new(path) {
|
||||
for result in WalkDir::new(path).into_iter().filter_entry(|dent| {
|
||||
if dent.file_type().is_dir()
|
||||
&& (dent.path().ends_with("target") || dent.path().ends_with(".git"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
}) {
|
||||
let dent = match result {
|
||||
Ok(dent) => dent,
|
||||
Err(err) => {
|
||||
|
@ -105,18 +124,24 @@ fn search(pattern: &str, paths: &[OsString]) -> Result<SimpleSink, Box<dyn Error
|
|||
Ok(sink)
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
enum Message {
|
||||
Search((String, Vec<OsString>)),
|
||||
Quit,
|
||||
}
|
||||
|
||||
struct GrepWindow {
|
||||
sink: Option<SimpleSink>,
|
||||
selected_match: usize,
|
||||
top_index: usize,
|
||||
input_buffer: Option<Buffer>,
|
||||
|
||||
tx: Sender<Message>,
|
||||
rx: Receiver<SimpleSink>,
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn OnInitialize(plugin: Plugin) {
|
||||
println!("Grep Plugin Initialized");
|
||||
|
||||
plugin.register_hook(Hook::BufferInput, on_buffer_input);
|
||||
plugin.register_input_group(
|
||||
None,
|
||||
|
@ -126,11 +151,17 @@ pub extern "C" fn OnInitialize(plugin: Plugin) {
|
|||
input_map,
|
||||
Key::R,
|
||||
Closure!((plugin: Plugin) => {
|
||||
let (window_tx, thread_rx) = channel();
|
||||
let (thread_tx, window_rx) = channel();
|
||||
create_search_thread(thread_tx, thread_rx);
|
||||
|
||||
let window = GrepWindow {
|
||||
selected_match: 0,
|
||||
top_index: 0,
|
||||
input_buffer: Some(plugin.buffer_table.open_virtual_buffer()),
|
||||
sink: None,
|
||||
tx: window_tx,
|
||||
rx: window_rx,
|
||||
};
|
||||
|
||||
plugin.create_window(window, Closure!((plugin: Plugin, input_map: InputMap) => {
|
||||
|
@ -212,6 +243,24 @@ pub extern "C" fn OnInitialize(plugin: Plugin) {
|
|||
);
|
||||
}
|
||||
|
||||
fn create_search_thread(tx: Sender<SimpleSink>, rx: Receiver<Message>) {
|
||||
thread::spawn(move || {
|
||||
while let Ok(message) = rx.recv() {
|
||||
match message {
|
||||
Message::Search((pattern, paths)) => {
|
||||
if let Ok(sink) = search(&pattern, &paths) {
|
||||
if let Err(err) = tx.send(sink) {
|
||||
eprintln!("error getting grep results: {err:?}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
Message::Quit => return,
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn OnExit(_plugin: Plugin) {
|
||||
println!("Grep Plugin Exiting");
|
||||
|
@ -239,6 +288,13 @@ extern "C" fn draw_window(plugin: Plugin, window: *const std::ffi::c_void) {
|
|||
let directory = Path::new(dir.as_ref());
|
||||
|
||||
(plugin.draw_rect)(x, y, width, height, PaletteColor::Background4);
|
||||
(plugin.draw_rect)(
|
||||
x + font_width,
|
||||
y + font_height,
|
||||
width - font_width * 2,
|
||||
height - font_height * 3,
|
||||
PaletteColor::Background3,
|
||||
);
|
||||
|
||||
if let Some(buffer) = window.input_buffer {
|
||||
(plugin.draw_rect)(
|
||||
|
@ -258,12 +314,17 @@ extern "C" fn draw_window(plugin: Plugin, window: *const std::ffi::c_void) {
|
|||
);
|
||||
}
|
||||
|
||||
if let Ok(sink) = window.rx.try_recv() {
|
||||
window.sink = Some(sink);
|
||||
}
|
||||
|
||||
if let Some(sink) = &window.sink {
|
||||
if !sink.matches.is_empty() {
|
||||
let num_mats_to_draw = std::cmp::min(
|
||||
(sink.matches.len() - window.top_index) as i32,
|
||||
(height - font_height * 2) / (font_height) - 1,
|
||||
);
|
||||
let max_mat_length = (width - font_width * 2) / font_width;
|
||||
|
||||
for (i, mat) in sink.matches[window.top_index..].iter().enumerate() {
|
||||
let index = i + window.top_index;
|
||||
|
@ -281,17 +342,24 @@ extern "C" fn draw_window(plugin: Plugin, window: *const std::ffi::c_void) {
|
|||
let matched_text = String::from_utf8_lossy(&mat.text);
|
||||
let text = match mat.line_number {
|
||||
Some(line_number) => format!(
|
||||
"{} - {}:{}:{}: {}\0",
|
||||
index, relative_file_path, line_number, mat.column, matched_text
|
||||
"{}:{}:{}: {}",
|
||||
relative_file_path, line_number, mat.column, matched_text
|
||||
),
|
||||
None => format!("{}:{}: {}\0", relative_file_path, mat.column, matched_text),
|
||||
None => format!("{}:{}: {}", relative_file_path, mat.column, matched_text),
|
||||
};
|
||||
let text = if text.len() > max_mat_length as usize {
|
||||
text.as_str().split_at(max_mat_length as usize).0
|
||||
} else {
|
||||
&text
|
||||
};
|
||||
|
||||
let text = format!("{text}\0");
|
||||
|
||||
if index == window.selected_match {
|
||||
(plugin.draw_rect)(
|
||||
x + font_width,
|
||||
y + font_height + ((index - window.top_index) as i32) * font_height,
|
||||
(text.len() as i32) * font_width,
|
||||
(text.chars().count() as i32) * font_width,
|
||||
font_height,
|
||||
PaletteColor::Background2,
|
||||
);
|
||||
|
@ -320,11 +388,18 @@ extern "C" fn on_buffer_input(plugin: Plugin, buffer: Buffer) {
|
|||
if let Some(buffer_info) = plugin.buffer_table.get_buffer_info(buffer) {
|
||||
if let Some(input) = buffer_info.input.try_as_str() {
|
||||
let directory = OsString::from_str(plugin.get_current_directory().as_ref());
|
||||
window.sink = match directory {
|
||||
Ok(dir) => search(&input, &[dir]).ok(),
|
||||
|
||||
match directory {
|
||||
Ok(dir) => {
|
||||
if let Err(err) = window
|
||||
.tx
|
||||
.send(Message::Search((input.to_string(), vec![dir])))
|
||||
{
|
||||
eprintln!("failed to grep: {err:?}");
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
eprintln!("failed to parse directory");
|
||||
None
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -335,6 +410,8 @@ extern "C" fn on_buffer_input(plugin: Plugin, buffer: Buffer) {
|
|||
|
||||
extern "C" fn free_window(plugin: Plugin, window: *const std::ffi::c_void) {
|
||||
let mut window = unsafe { Box::<GrepWindow>::from_raw(window as *mut GrepWindow) };
|
||||
let _ = window.tx.send(Message::Quit);
|
||||
|
||||
if let Some(buffer) = window.input_buffer {
|
||||
plugin.buffer_table.free_virtual_buffer(buffer);
|
||||
window.input_buffer = None;
|
||||
|
|
|
@ -800,7 +800,7 @@ draw_file_buffer :: proc(state: ^State, buffer: ^FileBuffer, x: int, y: int, fon
|
|||
text_y := y + state.source_font_height * j;
|
||||
|
||||
if show_line_numbers {
|
||||
raylib.DrawTextEx(font, raylib.TextFormat("%d", begin + j + 1), raylib.Vector2 { f32(x), f32(text_y) }, f32(state.source_font_height), 0, theme.get_palette_raylib_color(.Background3));
|
||||
raylib.DrawTextEx(font, raylib.TextFormat("%d", begin + j + 1), raylib.Vector2 { f32(x), f32(text_y) }, f32(state.source_font_height), 0, theme.get_palette_raylib_color(.Background4));
|
||||
}
|
||||
|
||||
for i in 0..<buffer.glyph_buffer_width {
|
||||
|
|
|
@ -354,17 +354,16 @@ main :: proc() {
|
|||
draw_text = proc "c" (text: cstring, x: f32, y: f32, color: theme.PaletteColor) {
|
||||
context = state.ctx;
|
||||
|
||||
raylib.DrawTextEx(
|
||||
text := string(text);
|
||||
for codepoint, index in text {
|
||||
raylib.DrawTextCodepoint(
|
||||
state.font,
|
||||
text,
|
||||
raylib.Vector2 {
|
||||
x,
|
||||
y,
|
||||
},
|
||||
rune(codepoint),
|
||||
raylib.Vector2 { x + f32(index * state.source_font_width), y },
|
||||
f32(state.source_font_height),
|
||||
0,
|
||||
theme.get_palette_raylib_color(color)
|
||||
);
|
||||
}
|
||||
},
|
||||
draw_buffer_from_index = proc "c" (buffer_index: int, x: int, y: int, glyph_buffer_width: int, glyph_buffer_height: int, show_line_numbers: bool) {
|
||||
context = state.ctx;
|
||||
|
@ -729,7 +728,8 @@ main :: proc() {
|
|||
}
|
||||
|
||||
// Load plugins
|
||||
filepath.walk(filepath.join({ state.directory, "bin" }), load_plugin, transmute(rawptr)&state);
|
||||
// TODO(pcleavelin): Get directory of binary instead of shells current working directory
|
||||
filepath.walk(filepath.join({ os.get_current_directory(), "bin" }), load_plugin, transmute(rawptr)&state);
|
||||
|
||||
for plugin in state.plugins {
|
||||
if plugin.on_initialize != nil {
|
||||
|
|
Loading…
Reference in New Issue