Compare commits
2 Commits
a70da837fa
...
79dafaf1a0
Author | SHA1 | Date |
---|---|---|
|
79dafaf1a0 | |
|
39a26c3c01 |
1
Makefile
1
Makefile
|
@ -7,6 +7,7 @@ editor: grep src/**/*.odin
|
|||
odin build src/ -out:bin/editor -debug
|
||||
|
||||
grep:
|
||||
cargo fmt --manifest-path "src/pkg/grep_lib/Cargo.toml"
|
||||
cargo build --manifest-path "src/pkg/grep_lib/Cargo.toml"
|
||||
|
||||
test: src/**/*.odin
|
||||
|
|
|
@ -103,6 +103,13 @@ FileBufferPanel :: struct {
|
|||
buffer: FileBuffer,
|
||||
viewed_symbol: Maybe(string),
|
||||
|
||||
search_buffer: FileBuffer,
|
||||
query_arena: mem.Arena,
|
||||
query_region: mem.Arena_Temp_Memory,
|
||||
query_results: []GrepQueryResult,
|
||||
selected_result: int,
|
||||
is_searching: bool,
|
||||
|
||||
// only used for initialization
|
||||
file_path: string,
|
||||
line, col: int,
|
||||
|
|
|
@ -97,6 +97,22 @@ file_buffer_end :: proc(buffer: ^FileBuffer) -> Cursor {
|
|||
};
|
||||
}
|
||||
|
||||
FileBufferIterResult :: struct {
|
||||
character: u8,
|
||||
done: bool,
|
||||
}
|
||||
|
||||
iterate_file_buffer_c :: proc "c" (it: ^FileBufferIter) -> FileBufferIterResult {
|
||||
context = runtime.default_context()
|
||||
|
||||
character, _, cond := iterate_file_buffer(it)
|
||||
|
||||
return FileBufferIterResult {
|
||||
character = character,
|
||||
done = !cond,
|
||||
}
|
||||
}
|
||||
|
||||
iterate_file_buffer :: proc(it: ^FileBufferIter) -> (character: u8, idx: PieceTableIndex, cond: bool) {
|
||||
character, idx, cond = iterate_piece_table_iter(&it.piter)
|
||||
|
||||
|
@ -405,6 +421,27 @@ file_buffer_line_length :: proc(buffer: ^FileBuffer, index: PieceTableIndex) ->
|
|||
return line_length;
|
||||
}
|
||||
|
||||
move_cursor_to_location :: proc(buffer: ^FileBuffer, line, col: int, cursor: Maybe(^Cursor) = nil) {
|
||||
cursor := cursor;
|
||||
|
||||
if cursor == nil {
|
||||
cursor = &buffer.history.cursor;
|
||||
}
|
||||
|
||||
it := new_file_buffer_iter(buffer);
|
||||
for _ in iterate_file_buffer(&it) {
|
||||
if (it.cursor.line == line && it.cursor.col >= col) || it.cursor.line > line {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
cursor.?^ = it.cursor
|
||||
|
||||
update_file_buffer_scroll(buffer, cursor)
|
||||
|
||||
buffer.last_col = cursor.?.col
|
||||
}
|
||||
|
||||
move_cursor_start_of_line :: proc(buffer: ^FileBuffer, cursor: Maybe(^Cursor) = nil) {
|
||||
cursor := cursor;
|
||||
|
||||
|
@ -541,13 +578,13 @@ move_cursor_left :: proc(buffer: ^FileBuffer, cursor: Maybe(^Cursor) = nil) {
|
|||
cursor = &buffer.history.cursor;
|
||||
}
|
||||
|
||||
buffer.last_col = cursor.?.col
|
||||
|
||||
if cursor.?.col > 0 {
|
||||
it := new_file_buffer_iter_with_cursor(buffer, cursor.?^);
|
||||
iterate_file_buffer_reverse(&it);
|
||||
cursor.?^ = it.cursor;
|
||||
}
|
||||
|
||||
buffer.last_col = cursor.?.col
|
||||
}
|
||||
|
||||
move_cursor_right :: proc(buffer: ^FileBuffer, stop_at_end: bool = true, amt: int = 1, cursor: Maybe(^Cursor) = nil) {
|
||||
|
@ -557,8 +594,6 @@ move_cursor_right :: proc(buffer: ^FileBuffer, stop_at_end: bool = true, amt: in
|
|||
cursor = &buffer.history.cursor;
|
||||
}
|
||||
|
||||
buffer.last_col = cursor.?.col
|
||||
|
||||
it := new_file_buffer_iter_with_cursor(buffer, cursor.?^);
|
||||
line_length := file_buffer_line_length(buffer, it.cursor.index);
|
||||
|
||||
|
@ -568,6 +603,8 @@ move_cursor_right :: proc(buffer: ^FileBuffer, stop_at_end: bool = true, amt: in
|
|||
cursor.?^ = it.cursor;
|
||||
}
|
||||
}
|
||||
|
||||
buffer.last_col = cursor.?.col
|
||||
}
|
||||
|
||||
move_cursor_forward_start_of_word :: proc(buffer: ^FileBuffer, cursor: Maybe(^Cursor) = nil) {
|
||||
|
|
|
@ -102,6 +102,20 @@ recover_snapshot :: proc(history: ^FileHistory) {
|
|||
history.cursor = history.snapshots[history.next].cursor
|
||||
}
|
||||
|
||||
first_snapshot :: proc(history: ^FileHistory) {
|
||||
context.allocator = history.allocator
|
||||
|
||||
new_next :: 0
|
||||
|
||||
if history.snapshots[new_next].chunks == nil do return
|
||||
history.next = new_next
|
||||
|
||||
delete(history.piece_table.chunks)
|
||||
|
||||
history.piece_table.chunks = clone_chunk(history.snapshots[history.next].chunks)
|
||||
history.cursor = history.snapshots[history.next].cursor
|
||||
}
|
||||
|
||||
clone_chunk :: proc(chunks: [dynamic][]u8) -> [dynamic][]u8 {
|
||||
new_chunks := make([dynamic][]u8, len(chunks), len(chunks))
|
||||
|
||||
|
|
|
@ -3,6 +3,8 @@ package panels
|
|||
import "base:runtime"
|
||||
import "core:log"
|
||||
import "core:fmt"
|
||||
import "core:mem"
|
||||
import "core:strings"
|
||||
import "core:path/filepath"
|
||||
|
||||
import "vendor:sdl2"
|
||||
|
@ -12,6 +14,26 @@ import "../core"
|
|||
import "../ui"
|
||||
|
||||
make_file_buffer_panel :: proc(file_path: string, line: int = 0, col: int = 0) -> core.Panel {
|
||||
run_query :: proc(panel_state: ^core.FileBufferPanel, buffer: ^core.FileBuffer) {
|
||||
if panel_state.query_region.arena != nil {
|
||||
mem.end_arena_temp_memory(panel_state.query_region)
|
||||
}
|
||||
panel_state.query_region = mem.begin_arena_temp_memory(&panel_state.query_arena)
|
||||
|
||||
context.allocator = mem.arena_allocator(&panel_state.query_arena)
|
||||
|
||||
it := core.new_file_buffer_iter(buffer)
|
||||
|
||||
rs_results := grep_buffer(
|
||||
strings.clone_to_cstring(core.buffer_to_string(&panel_state.search_buffer)),
|
||||
&it,
|
||||
core.iterate_file_buffer_c
|
||||
);
|
||||
|
||||
panel_state.selected_result = 0
|
||||
panel_state.query_results = rs_grep_as_results(&rs_results)
|
||||
}
|
||||
|
||||
return core.Panel {
|
||||
type = core.FileBufferPanel {
|
||||
file_path = file_path,
|
||||
|
@ -27,7 +49,16 @@ make_file_buffer_panel :: proc(file_path: string, line: int = 0, col: int = 0) -
|
|||
context.allocator = panel.allocator
|
||||
|
||||
panel_state := &panel.type.(core.FileBufferPanel)
|
||||
|
||||
arena_bytes, err := make([]u8, 1024*1024*2)
|
||||
if err != nil {
|
||||
log.errorf("failed to allocate arena for file buffer panel: '%v'", err)
|
||||
return
|
||||
}
|
||||
mem.arena_init(&panel_state.query_arena, arena_bytes)
|
||||
|
||||
panel.input_map = core.new_input_map()
|
||||
panel_state.search_buffer = core.new_virtual_file_buffer(panel.allocator)
|
||||
|
||||
if len(panel_state.file_path) == 0 {
|
||||
panel_state.buffer = core.new_virtual_file_buffer(panel.allocator)
|
||||
|
@ -62,31 +93,77 @@ make_file_buffer_panel :: proc(file_path: string, line: int = 0, col: int = 0) -
|
|||
buffer = proc(panel: ^core.Panel, state: ^core.State) -> (buffer: ^core.FileBuffer, ok: bool) {
|
||||
panel_state := &panel.type.(core.FileBufferPanel)
|
||||
|
||||
return &panel_state.buffer, true
|
||||
if panel_state.is_searching {
|
||||
return &panel_state.search_buffer, true
|
||||
} else {
|
||||
return &panel_state.buffer, true
|
||||
}
|
||||
},
|
||||
on_buffer_input = proc(panel: ^core.Panel, state: ^core.State) {
|
||||
panel_state := &panel.type.(core.FileBufferPanel)
|
||||
run_query(panel_state, &panel_state.buffer)
|
||||
|
||||
if len(panel_state.query_results) > 0 {
|
||||
for result, i in panel_state.query_results {
|
||||
cursor := panel_state.buffer.history.cursor
|
||||
|
||||
if result.line >= cursor.line || (result.line == cursor.line && result.col >= cursor.col) {
|
||||
core.move_cursor_to_location(&panel_state.buffer, result.line, result.col)
|
||||
break
|
||||
}
|
||||
|
||||
if i == len(panel_state.query_results)-1 {
|
||||
result := panel_state.query_results[0]
|
||||
core.move_cursor_to_location(&panel_state.buffer, result.line, result.col)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
render = proc(panel: ^core.Panel, state: ^core.State) -> (ok: bool) {
|
||||
panel_state := &panel.type.(core.FileBufferPanel)
|
||||
|
||||
s := transmute(^ui.State)state.ui
|
||||
render_file_buffer(state, s, &panel_state.buffer)
|
||||
|
||||
if viewed_symbol, ok := panel_state.viewed_symbol.?; ok {
|
||||
ui.open_element(s, nil,
|
||||
{
|
||||
dir = .TopToBottom,
|
||||
kind = {ui.Fit{}, ui.Fit{}},
|
||||
floating = true,
|
||||
},
|
||||
style = {
|
||||
background_color = .Background2,
|
||||
},
|
||||
)
|
||||
ui.open_element(s, nil,
|
||||
{
|
||||
ui.open_element(s, viewed_symbol, {})
|
||||
dir = .TopToBottom,
|
||||
kind = {ui.Grow{}, ui.Grow{}},
|
||||
},
|
||||
)
|
||||
{
|
||||
render_file_buffer(state, s, &panel_state.buffer)
|
||||
if panel_state.is_searching {
|
||||
ui.open_element(s, nil,
|
||||
{
|
||||
dir = .TopToBottom,
|
||||
kind = {ui.Grow{}, ui.Exact(state.source_font_height)},
|
||||
},
|
||||
)
|
||||
{
|
||||
render_raw_buffer(state, s, &panel_state.search_buffer)
|
||||
}
|
||||
ui.close_element(s)
|
||||
}
|
||||
|
||||
if viewed_symbol, ok := panel_state.viewed_symbol.?; ok {
|
||||
ui.open_element(s, nil,
|
||||
{
|
||||
dir = .TopToBottom,
|
||||
kind = {ui.Fit{}, ui.Fit{}},
|
||||
floating = true,
|
||||
},
|
||||
style = {
|
||||
background_color = .Background2,
|
||||
},
|
||||
)
|
||||
{
|
||||
ui.open_element(s, viewed_symbol, {})
|
||||
ui.close_element(s)
|
||||
}
|
||||
ui.close_element(s)
|
||||
}
|
||||
ui.close_element(s)
|
||||
}
|
||||
ui.close_element(s)
|
||||
|
||||
return true
|
||||
}
|
||||
|
@ -115,6 +192,11 @@ render_file_buffer :: proc(state: ^core.State, s: ^ui.State, buffer: ^core.FileB
|
|||
{
|
||||
kind = {ui.Grow{}, ui.Grow{}}
|
||||
},
|
||||
style = {
|
||||
border = {.Left, .Right, .Top, .Bottom},
|
||||
border_color = .Background4,
|
||||
background_color = .Background1,
|
||||
},
|
||||
)
|
||||
ui.close_element(s)
|
||||
|
||||
|
@ -296,6 +378,20 @@ file_buffer_input_actions :: proc(input_map: ^core.InputActions) {
|
|||
file_buffer_delete_actions(&delete_actions);
|
||||
core.register_key_action(input_map, .D, delete_actions, "Delete commands");
|
||||
|
||||
core.register_key_action(input_map, .SLASH, proc(state: ^core.State, user_data: rawptr) {
|
||||
panel_state := &(transmute(^core.Panel)user_data).type.(core.FileBufferPanel)
|
||||
|
||||
core.first_snapshot(&panel_state.search_buffer.history)
|
||||
core.push_new_snapshot(&panel_state.search_buffer.history)
|
||||
|
||||
core.reset_input_map(state)
|
||||
|
||||
state.mode = .Insert;
|
||||
sdl2.StartTextInput();
|
||||
|
||||
panel_state.is_searching = true
|
||||
}, "search buffer")
|
||||
|
||||
core.register_key_action(input_map, .V, proc(state: ^core.State, user_data: rawptr) {
|
||||
buffer := &(&(transmute(^core.Panel)user_data).type.(core.FileBufferPanel)).buffer
|
||||
|
||||
|
@ -308,10 +404,35 @@ file_buffer_input_actions :: proc(input_map: ^core.InputActions) {
|
|||
core.register_key_action(input_map, .ESCAPE, proc(state: ^core.State, user_data: rawptr) {
|
||||
panel := transmute(^core.Panel)user_data
|
||||
panel_state := &panel.type.(core.FileBufferPanel)
|
||||
buffer := &panel_state.buffer
|
||||
|
||||
if panel_state.is_searching {
|
||||
panel_state.is_searching = false
|
||||
sdl2.StopTextInput()
|
||||
}
|
||||
|
||||
panel_state.viewed_symbol = nil
|
||||
});
|
||||
|
||||
core.register_key_action(input_map, .N, proc(state: ^core.State, user_data: rawptr) {
|
||||
panel := transmute(^core.Panel)user_data
|
||||
panel_state := &panel.type.(core.FileBufferPanel)
|
||||
|
||||
if len(panel_state.query_results) > 0 {
|
||||
for result, i in panel_state.query_results {
|
||||
cursor := panel_state.buffer.history.cursor
|
||||
|
||||
if result.line > cursor.line || (result.line == cursor.line && result.col > cursor.col) {
|
||||
core.move_cursor_to_location(&panel_state.buffer, result.line, result.col)
|
||||
break
|
||||
}
|
||||
|
||||
if i == len(panel_state.query_results)-1 {
|
||||
result := panel_state.query_results[0]
|
||||
core.move_cursor_to_location(&panel_state.buffer, result.line, result.col)
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
file_buffer_visual_actions :: proc(input_map: ^core.InputActions) {
|
||||
|
|
|
@ -29,20 +29,26 @@ make_grep_panel :: proc() -> core.Panel {
|
|||
|
||||
context.allocator = mem.arena_allocator(&panel_state.query_arena)
|
||||
|
||||
rs_results := grep(
|
||||
strings.clone_to_cstring(core.buffer_to_string(buffer)),
|
||||
strings.clone_to_cstring(directory)
|
||||
);
|
||||
search_query := core.buffer_to_string(buffer)
|
||||
if len(search_query) > 0 {
|
||||
rs_results := grep(
|
||||
strings.clone_to_cstring(search_query),
|
||||
strings.clone_to_cstring(directory)
|
||||
);
|
||||
|
||||
panel_state.query_results = rs_grep_as_results(&rs_results)
|
||||
panel_state.query_results = rs_grep_as_results(&rs_results)
|
||||
|
||||
panel_state.selected_result = 0
|
||||
if len(panel_state.query_results) > 0 {
|
||||
core.update_glyph_buffer_from_bytes(
|
||||
&panel_state.glyphs,
|
||||
transmute([]u8)panel_state.query_results[panel_state.selected_result].file_context,
|
||||
panel_state.query_results[panel_state.selected_result].line,
|
||||
)
|
||||
panel_state.selected_result = 0
|
||||
if len(panel_state.query_results) > 0 {
|
||||
core.update_glyph_buffer_from_bytes(
|
||||
&panel_state.glyphs,
|
||||
transmute([]u8)panel_state.query_results[panel_state.selected_result].file_context,
|
||||
panel_state.query_results[panel_state.selected_result].line,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
panel_state.selected_result = 0
|
||||
panel_state.query_results = nil
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -259,6 +265,7 @@ foreign import grep_lib "../pkg/grep_lib/target/debug/libgrep.a"
|
|||
@(default_calling_convention = "c")
|
||||
foreign grep_lib {
|
||||
grep :: proc (pattern: cstring, directory: cstring) -> RS_GrepResults ---
|
||||
grep_buffer :: proc (pattern: cstring, it: ^core.FileBufferIter, func: proc "c" (it: ^core.FileBufferIter) -> core.FileBufferIterResult) -> RS_GrepResults ---
|
||||
free_grep_results :: proc(results: RS_GrepResults) ---
|
||||
}
|
||||
|
||||
|
@ -283,7 +290,7 @@ rs_grep_as_results :: proc(results: ^RS_GrepResults, allocator := context.alloca
|
|||
|
||||
query_results := make([]core.GrepQueryResult, results.len)
|
||||
|
||||
for i in 0..<results.len {
|
||||
for i in 0..<len(query_results) {
|
||||
r := results.results[i]
|
||||
|
||||
query_results[i] = core.GrepQueryResult {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use std::{
|
||||
error::Error,
|
||||
ffi::CStr,
|
||||
ffi::{CStr, c_void},
|
||||
};
|
||||
|
||||
use grep::{
|
||||
|
@ -33,32 +33,41 @@ struct Match {
|
|||
}
|
||||
impl Match {
|
||||
fn from_sink_match_with_path(
|
||||
pattern: &str,
|
||||
value: &grep::searcher::SinkMatch<'_>,
|
||||
path: Option<String>,
|
||||
) -> Result<Self, SimpleSinkError> {
|
||||
let line = value
|
||||
.lines()
|
||||
.next()
|
||||
.ok_or(SimpleSinkError::NoLine)?
|
||||
.to_vec();
|
||||
let column = value.bytes_range_in_buffer().len() as u64;
|
||||
) -> Result<Vec<Self>, SimpleSinkError> {
|
||||
let line = String::from_utf8_lossy(value.lines().next().ok_or(SimpleSinkError::NoLine)?);
|
||||
|
||||
Ok(Self {
|
||||
// TODO: only return N-lines of context instead of the entire freakin' buffer
|
||||
text: value.buffer().to_vec(),
|
||||
path: path.unwrap_or_default(),
|
||||
line_number: value.line_number(),
|
||||
column,
|
||||
})
|
||||
Ok(line
|
||||
.match_indices(pattern)
|
||||
.into_iter()
|
||||
.map(|(index, _)| Self {
|
||||
text: value.buffer().to_vec(),
|
||||
path: path.clone().unwrap_or_default(),
|
||||
line_number: value.line_number(),
|
||||
column: index as u64 + 1,
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
struct SimpleSink {
|
||||
#[derive(Debug)]
|
||||
struct SimpleSink<'a> {
|
||||
search_pattern: &'a str,
|
||||
current_path: Option<String>,
|
||||
matches: Vec<Match>,
|
||||
}
|
||||
impl Sink for SimpleSink {
|
||||
impl<'a> SimpleSink<'a> {
|
||||
fn new(pattern: &'a str) -> Self {
|
||||
Self {
|
||||
search_pattern: pattern,
|
||||
current_path: None,
|
||||
matches: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Sink for SimpleSink<'_> {
|
||||
type Error = SimpleSinkError;
|
||||
|
||||
fn matched(
|
||||
|
@ -66,16 +75,47 @@ impl Sink for SimpleSink {
|
|||
_searcher: &grep::searcher::Searcher,
|
||||
mat: &grep::searcher::SinkMatch<'_>,
|
||||
) -> Result<bool, Self::Error> {
|
||||
self.matches.push(Match::from_sink_match_with_path(
|
||||
mat,
|
||||
self.current_path.clone(),
|
||||
)?);
|
||||
let mut matches =
|
||||
Match::from_sink_match_with_path(self.search_pattern, mat, self.current_path.clone())?;
|
||||
|
||||
self.matches.append(&mut matches);
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
|
||||
fn search(pattern: &str, paths: &[&str]) -> Result<SimpleSink, Box<dyn Error>> {
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy)]
|
||||
struct FileBufferIter(*const c_void);
|
||||
|
||||
#[repr(C)]
|
||||
struct FileBufferIterResult {
|
||||
character: u8,
|
||||
done: bool,
|
||||
}
|
||||
|
||||
struct BufferIter {
|
||||
iter: FileBufferIter,
|
||||
iter_func: extern "C" fn(it: FileBufferIter) -> FileBufferIterResult,
|
||||
}
|
||||
|
||||
impl std::io::Read for BufferIter {
|
||||
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
||||
let result = (self.iter_func)(self.iter);
|
||||
|
||||
if result.done || buf.len() < 1 {
|
||||
return Ok(0);
|
||||
} else {
|
||||
buf[0] = result.character;
|
||||
return Ok(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn search_buffer<'a>(
|
||||
pattern: &'a str,
|
||||
buffer: BufferIter,
|
||||
) -> Result<SimpleSink<'a>, Box<dyn Error>> {
|
||||
let matcher = RegexMatcherBuilder::new()
|
||||
.case_smart(true)
|
||||
.fixed_strings(true)
|
||||
|
@ -85,7 +125,26 @@ fn search(pattern: &str, paths: &[&str]) -> Result<SimpleSink, Box<dyn Error>> {
|
|||
.line_number(true)
|
||||
.build();
|
||||
|
||||
let mut sink = SimpleSink::default();
|
||||
let mut sink = SimpleSink::new(pattern);
|
||||
let result = searcher.search_reader(matcher, buffer, &mut sink);
|
||||
if let Err(err) = result {
|
||||
eprintln!("{:?}", err);
|
||||
}
|
||||
|
||||
Ok(sink)
|
||||
}
|
||||
|
||||
fn search<'a>(pattern: &'a str, paths: &[&str]) -> Result<SimpleSink<'a>, Box<dyn Error>> {
|
||||
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)
|
||||
.build();
|
||||
|
||||
let mut sink = SimpleSink::new(pattern);
|
||||
for path in paths {
|
||||
for result in WalkDir::new(path).into_iter().filter_entry(|dent| {
|
||||
if dent.file_type().is_dir()
|
||||
|
@ -147,9 +206,17 @@ impl From<Match> for GrepResult {
|
|||
impl From<GrepResult> for Match {
|
||||
fn from(value: GrepResult) -> Self {
|
||||
unsafe {
|
||||
let text = Box::from_raw(std::slice::from_raw_parts_mut(value.text as *mut _, value.text_len as usize)).to_vec();
|
||||
let text = Box::from_raw(std::slice::from_raw_parts_mut(
|
||||
value.text as *mut _,
|
||||
value.text_len as usize,
|
||||
))
|
||||
.to_vec();
|
||||
|
||||
let path = Box::from_raw(std::slice::from_raw_parts_mut(value.path as *mut _, value.path_len as usize)).to_vec();
|
||||
let path = Box::from_raw(std::slice::from_raw_parts_mut(
|
||||
value.path as *mut _,
|
||||
value.path_len as usize,
|
||||
))
|
||||
.to_vec();
|
||||
let path = String::from_utf8_unchecked(path);
|
||||
|
||||
Self {
|
||||
|
@ -182,8 +249,6 @@ extern "C" fn grep(
|
|||
)
|
||||
};
|
||||
|
||||
println!("pattern: '{pattern}', directory: '{directory}'");
|
||||
|
||||
let boxed = search(&pattern, &[&directory])
|
||||
.into_iter()
|
||||
.map(|sink| sink.matches.into_iter())
|
||||
|
@ -200,10 +265,43 @@ extern "C" fn grep(
|
|||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
extern "C" fn grep_buffer(
|
||||
pattern: *const std::ffi::c_char,
|
||||
it: FileBufferIter,
|
||||
iter_func: extern "C" fn(it: FileBufferIter) -> FileBufferIterResult,
|
||||
) -> GrepResults {
|
||||
let pattern = unsafe { CStr::from_ptr(pattern).to_string_lossy() };
|
||||
|
||||
let boxed = search_buffer(
|
||||
&pattern,
|
||||
BufferIter {
|
||||
iter: it,
|
||||
iter_func,
|
||||
},
|
||||
)
|
||||
.into_iter()
|
||||
.map(|sink| sink.matches.into_iter())
|
||||
.flatten()
|
||||
.map(|v| GrepResult::from(v))
|
||||
.collect::<Vec<_>>()
|
||||
.into_boxed_slice();
|
||||
|
||||
let len = boxed.len() as u32;
|
||||
|
||||
GrepResults {
|
||||
results: Box::into_raw(boxed) as _,
|
||||
len,
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
extern "C" fn free_grep_results(results: GrepResults) {
|
||||
unsafe {
|
||||
let mut array = std::slice::from_raw_parts_mut(results.results as *mut GrepResult, results.len as usize);
|
||||
let mut array = std::slice::from_raw_parts_mut(
|
||||
results.results as *mut GrepResult,
|
||||
results.len as usize,
|
||||
);
|
||||
let array = Box::from_raw(array);
|
||||
|
||||
for v in array {
|
||||
|
|
|
@ -451,9 +451,9 @@ draw :: proc(state: ^State, core_state: ^core.State) {
|
|||
if .Right in e.style.border {
|
||||
core.draw_line(
|
||||
core_state,
|
||||
e.layout.pos.x + e.layout.size.x,
|
||||
e.layout.pos.x + e.layout.size.x - 1,
|
||||
e.layout.pos.y,
|
||||
e.layout.pos.x + e.layout.size.x,
|
||||
e.layout.pos.x + e.layout.size.x - 1,
|
||||
e.layout.pos.y + e.layout.size.y,
|
||||
e.style.border_color,
|
||||
)
|
||||
|
|
2
todo.md
2
todo.md
|
@ -17,6 +17,8 @@
|
|||
- [ ] Finish writing tests for all current user actions
|
||||
- Vim-like Macro replays
|
||||
- [ ] Simple File Search (vim /)
|
||||
- [x] Forward Search
|
||||
- [ ] Backward Search
|
||||
- Modify input system to allow for keybinds that take input
|
||||
- Vim's f and F movement commands
|
||||
- Vim's r command
|
||||
|
|
Loading…
Reference in New Issue