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::{
|
use grep::{
|
||||||
regex::RegexMatcher,
|
regex::{RegexMatcher, RegexMatcherBuilder},
|
||||||
searcher::{BinaryDetection, SearcherBuilder, Sink, SinkError},
|
searcher::{BinaryDetection, SearcherBuilder, Sink, SinkError},
|
||||||
};
|
};
|
||||||
use plugin_rs_bindings::{Buffer, Closure, Hook, InputMap, Key, PaletteColor, Plugin};
|
use plugin_rs_bindings::{Buffer, Closure, Hook, InputMap, Key, PaletteColor, Plugin};
|
||||||
|
use std::sync::mpsc::channel;
|
||||||
use walkdir::WalkDir;
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -73,7 +81,10 @@ impl Sink for SimpleSink {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn search(pattern: &str, paths: &[OsString]) -> Result<SimpleSink, Box<dyn Error>> {
|
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()
|
let mut searcher = SearcherBuilder::new()
|
||||||
.binary_detection(BinaryDetection::quit(b'\x00'))
|
.binary_detection(BinaryDetection::quit(b'\x00'))
|
||||||
.line_number(true)
|
.line_number(true)
|
||||||
|
@ -81,7 +92,15 @@ fn search(pattern: &str, paths: &[OsString]) -> Result<SimpleSink, Box<dyn Error
|
||||||
|
|
||||||
let mut sink = SimpleSink::default();
|
let mut sink = SimpleSink::default();
|
||||||
for path in paths {
|
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 {
|
let dent = match result {
|
||||||
Ok(dent) => dent,
|
Ok(dent) => dent,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
|
@ -105,18 +124,24 @@ fn search(pattern: &str, paths: &[OsString]) -> Result<SimpleSink, Box<dyn Error
|
||||||
Ok(sink)
|
Ok(sink)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
enum Message {
|
||||||
|
Search((String, Vec<OsString>)),
|
||||||
|
Quit,
|
||||||
|
}
|
||||||
|
|
||||||
struct GrepWindow {
|
struct GrepWindow {
|
||||||
sink: Option<SimpleSink>,
|
sink: Option<SimpleSink>,
|
||||||
selected_match: usize,
|
selected_match: usize,
|
||||||
top_index: usize,
|
top_index: usize,
|
||||||
input_buffer: Option<Buffer>,
|
input_buffer: Option<Buffer>,
|
||||||
|
|
||||||
|
tx: Sender<Message>,
|
||||||
|
rx: Receiver<SimpleSink>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn OnInitialize(plugin: Plugin) {
|
pub extern "C" fn OnInitialize(plugin: Plugin) {
|
||||||
println!("Grep Plugin Initialized");
|
println!("Grep Plugin Initialized");
|
||||||
|
|
||||||
plugin.register_hook(Hook::BufferInput, on_buffer_input);
|
plugin.register_hook(Hook::BufferInput, on_buffer_input);
|
||||||
plugin.register_input_group(
|
plugin.register_input_group(
|
||||||
None,
|
None,
|
||||||
|
@ -126,11 +151,17 @@ pub extern "C" fn OnInitialize(plugin: Plugin) {
|
||||||
input_map,
|
input_map,
|
||||||
Key::R,
|
Key::R,
|
||||||
Closure!((plugin: Plugin) => {
|
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 {
|
let window = GrepWindow {
|
||||||
selected_match: 0,
|
selected_match: 0,
|
||||||
top_index: 0,
|
top_index: 0,
|
||||||
input_buffer: Some(plugin.buffer_table.open_virtual_buffer()),
|
input_buffer: Some(plugin.buffer_table.open_virtual_buffer()),
|
||||||
sink: None,
|
sink: None,
|
||||||
|
tx: window_tx,
|
||||||
|
rx: window_rx,
|
||||||
};
|
};
|
||||||
|
|
||||||
plugin.create_window(window, Closure!((plugin: Plugin, input_map: InputMap) => {
|
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]
|
#[no_mangle]
|
||||||
pub extern "C" fn OnExit(_plugin: Plugin) {
|
pub extern "C" fn OnExit(_plugin: Plugin) {
|
||||||
println!("Grep Plugin Exiting");
|
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());
|
let directory = Path::new(dir.as_ref());
|
||||||
|
|
||||||
(plugin.draw_rect)(x, y, width, height, PaletteColor::Background4);
|
(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 {
|
if let Some(buffer) = window.input_buffer {
|
||||||
(plugin.draw_rect)(
|
(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 let Some(sink) = &window.sink {
|
||||||
if !sink.matches.is_empty() {
|
if !sink.matches.is_empty() {
|
||||||
let num_mats_to_draw = std::cmp::min(
|
let num_mats_to_draw = std::cmp::min(
|
||||||
(sink.matches.len() - window.top_index) as i32,
|
(sink.matches.len() - window.top_index) as i32,
|
||||||
(height - font_height * 2) / (font_height) - 1,
|
(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() {
|
for (i, mat) in sink.matches[window.top_index..].iter().enumerate() {
|
||||||
let index = i + window.top_index;
|
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 matched_text = String::from_utf8_lossy(&mat.text);
|
||||||
let text = match mat.line_number {
|
let text = match mat.line_number {
|
||||||
Some(line_number) => format!(
|
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 {
|
if index == window.selected_match {
|
||||||
(plugin.draw_rect)(
|
(plugin.draw_rect)(
|
||||||
x + font_width,
|
x + font_width,
|
||||||
y + font_height + ((index - window.top_index) as i32) * font_height,
|
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,
|
font_height,
|
||||||
PaletteColor::Background2,
|
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(buffer_info) = plugin.buffer_table.get_buffer_info(buffer) {
|
||||||
if let Some(input) = buffer_info.input.try_as_str() {
|
if let Some(input) = buffer_info.input.try_as_str() {
|
||||||
let directory = OsString::from_str(plugin.get_current_directory().as_ref());
|
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(_) => {
|
Err(_) => {
|
||||||
eprintln!("failed to parse directory");
|
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) {
|
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 mut window = unsafe { Box::<GrepWindow>::from_raw(window as *mut GrepWindow) };
|
||||||
|
let _ = window.tx.send(Message::Quit);
|
||||||
|
|
||||||
if let Some(buffer) = window.input_buffer {
|
if let Some(buffer) = window.input_buffer {
|
||||||
plugin.buffer_table.free_virtual_buffer(buffer);
|
plugin.buffer_table.free_virtual_buffer(buffer);
|
||||||
window.input_buffer = None;
|
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;
|
text_y := y + state.source_font_height * j;
|
||||||
|
|
||||||
if show_line_numbers {
|
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 {
|
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) {
|
draw_text = proc "c" (text: cstring, x: f32, y: f32, color: theme.PaletteColor) {
|
||||||
context = state.ctx;
|
context = state.ctx;
|
||||||
|
|
||||||
raylib.DrawTextEx(
|
text := string(text);
|
||||||
|
for codepoint, index in text {
|
||||||
|
raylib.DrawTextCodepoint(
|
||||||
state.font,
|
state.font,
|
||||||
text,
|
rune(codepoint),
|
||||||
raylib.Vector2 {
|
raylib.Vector2 { x + f32(index * state.source_font_width), y },
|
||||||
x,
|
|
||||||
y,
|
|
||||||
},
|
|
||||||
f32(state.source_font_height),
|
f32(state.source_font_height),
|
||||||
0,
|
|
||||||
theme.get_palette_raylib_color(color)
|
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) {
|
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;
|
context = state.ctx;
|
||||||
|
@ -729,7 +728,8 @@ main :: proc() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load plugins
|
// 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 {
|
for plugin in state.plugins {
|
||||||
if plugin.on_initialize != nil {
|
if plugin.on_initialize != nil {
|
||||||
|
|
Loading…
Reference in New Issue