re-write grep plugin built-in + move things around
parent
97326b54f8
commit
06d9750cd2
|
@ -10,6 +10,10 @@
|
|||
"features": {
|
||||
"./odin-feature": {
|
||||
"version": "latest"
|
||||
},
|
||||
"ghcr.io/devcontainers/features/rust:1": {
|
||||
"version": "1.88.0",
|
||||
"profile": "default"
|
||||
}
|
||||
}
|
||||
}
|
9
Makefile
9
Makefile
|
@ -1,5 +1,10 @@
|
|||
export RUSTFLAGS=-C target-feature=-avx2
|
||||
|
||||
all: editor
|
||||
|
||||
editor: src/**/*.odin
|
||||
editor: grep src/**/*.odin
|
||||
mkdir -p bin
|
||||
odin build src/ -out:bin/editor -debug
|
||||
odin build src/ -out:bin/editor -debug
|
||||
|
||||
grep:
|
||||
cargo build --manifest-path "src/pkg/grep_lib/Cargo.toml"
|
||||
|
|
|
@ -2,6 +2,7 @@ package core
|
|||
|
||||
import "base:runtime"
|
||||
import "base:intrinsics"
|
||||
import "core:mem"
|
||||
import "core:reflect"
|
||||
import "core:fmt"
|
||||
import "core:log"
|
||||
|
@ -10,6 +11,8 @@ import lua "vendor:lua/5.4"
|
|||
|
||||
import "../util"
|
||||
|
||||
HardcodedFontPath :: "bin/JetBrainsMono-Regular.ttf";
|
||||
|
||||
Mode :: enum {
|
||||
Normal,
|
||||
Insert,
|
||||
|
@ -43,7 +46,6 @@ State :: struct {
|
|||
|
||||
log_buffer: FileBuffer,
|
||||
|
||||
input_map: InputMap,
|
||||
current_input_map: ^InputActions,
|
||||
|
||||
commands: EditorCommandList,
|
||||
|
@ -72,11 +74,16 @@ EditorCommandArgument :: union #no_nil {
|
|||
|
||||
PanelRenderProc :: proc(state: ^State, panel_state: ^PanelState) -> (ok: bool)
|
||||
PanelBufferProc :: proc(state: ^State, panel_state: ^PanelState) -> (buffer: ^FileBuffer, ok: bool)
|
||||
PanelBufferInputProc :: proc(state: ^State, panel_state: ^PanelState)
|
||||
PanelDropProc :: proc(state: ^State, panel_state: ^PanelState)
|
||||
Panel :: struct {
|
||||
is_floating: bool,
|
||||
panel_state: PanelState,
|
||||
input_map: InputMap,
|
||||
buffer_proc: PanelBufferProc,
|
||||
on_buffer_input_proc: PanelBufferInputProc,
|
||||
render_proc: PanelRenderProc,
|
||||
drop: PanelDropProc,
|
||||
}
|
||||
|
||||
PanelState :: union {
|
||||
|
@ -89,13 +96,15 @@ FileBufferPanel :: struct {
|
|||
}
|
||||
|
||||
GrepPanel :: struct {
|
||||
query_arena: mem.Arena,
|
||||
buffer: int,
|
||||
selected_result: int,
|
||||
search_query: string,
|
||||
query_results: []GrepQueryResult,
|
||||
selected_result: int,
|
||||
}
|
||||
|
||||
GrepQueryResult :: struct {
|
||||
file_context: string,
|
||||
file_path: string,
|
||||
line: int,
|
||||
col: int,
|
||||
|
@ -121,6 +130,16 @@ current_buffer :: proc(state: ^State) -> ^FileBuffer {
|
|||
return &state.buffers[state.current_buffer];
|
||||
}
|
||||
|
||||
reset_input_map_from_state_mode :: proc(state: ^State) {
|
||||
reset_input_map_from_mode(state, state.mode)
|
||||
}
|
||||
reset_input_map_from_mode :: proc(state: ^State, mode: Mode) {
|
||||
if current_panel, ok := util.get(&state.panels, state.current_panel.? or_else -1).?; ok {
|
||||
state.current_input_map = ¤t_panel.input_map.mode[mode]
|
||||
}
|
||||
}
|
||||
reset_input_map :: proc{reset_input_map_from_mode, reset_input_map_from_state_mode}
|
||||
|
||||
buffer_from_index :: proc(state: ^State, buffer_index: int) -> ^FileBuffer {
|
||||
if buffer_index == -2 {
|
||||
return &state.log_buffer;
|
||||
|
|
|
@ -0,0 +1,196 @@
|
|||
package input
|
||||
|
||||
import "core:log"
|
||||
|
||||
import "vendor:sdl2"
|
||||
|
||||
import "../core"
|
||||
|
||||
State :: core.State
|
||||
|
||||
register_default_go_actions :: proc(input_map: ^core.InputActions) {
|
||||
core.register_key_action(input_map, .H, proc(state: ^State) {
|
||||
core.move_cursor_start_of_line(core.current_buffer(state));
|
||||
core.reset_input_map(state)
|
||||
}, "move to beginning of line");
|
||||
core.register_key_action(input_map, .L, proc(state: ^State) {
|
||||
core.move_cursor_end_of_line(core.current_buffer(state));
|
||||
core.reset_input_map(state)
|
||||
}, "move to end of line");
|
||||
}
|
||||
|
||||
register_default_input_actions :: proc(input_map: ^core.InputActions) {
|
||||
// Cursor Movement
|
||||
{
|
||||
core.register_key_action(input_map, .W, proc(state: ^State) {
|
||||
core.move_cursor_forward_start_of_word(core.current_buffer(state));
|
||||
}, "move forward one word");
|
||||
core.register_key_action(input_map, .E, proc(state: ^State) {
|
||||
core.move_cursor_forward_end_of_word(core.current_buffer(state));
|
||||
}, "move forward to end of word");
|
||||
|
||||
core.register_key_action(input_map, .B, proc(state: ^State) {
|
||||
core.move_cursor_backward_start_of_word(core.current_buffer(state));
|
||||
}, "move backward one word");
|
||||
|
||||
core.register_key_action(input_map, .K, proc(state: ^State) {
|
||||
core.move_cursor_up(core.current_buffer(state));
|
||||
}, "move up one line");
|
||||
core.register_key_action(input_map, .J, proc(state: ^State) {
|
||||
core.move_cursor_down(core.current_buffer(state));
|
||||
}, "move down one line");
|
||||
core.register_key_action(input_map, .H, proc(state: ^State) {
|
||||
core.move_cursor_left(core.current_buffer(state));
|
||||
}, "move left one char");
|
||||
core.register_key_action(input_map, .L, proc(state: ^State) {
|
||||
core.move_cursor_right(core.current_buffer(state));
|
||||
}, "move right one char");
|
||||
|
||||
core.register_ctrl_key_action(input_map, .U, proc(state: ^State) {
|
||||
core.scroll_file_buffer(core.current_buffer(state), .Up);
|
||||
}, "scroll buffer up");
|
||||
core.register_ctrl_key_action(input_map, .D, proc(state: ^State) {
|
||||
core.scroll_file_buffer(core.current_buffer(state), .Down);
|
||||
}, "scroll buffer up");
|
||||
}
|
||||
|
||||
// Scale font size
|
||||
{
|
||||
core.register_ctrl_key_action(input_map, .MINUS, proc(state: ^State) {
|
||||
if state.source_font_height > 16 {
|
||||
state.source_font_height -= 2;
|
||||
state.source_font_width = state.source_font_height / 2;
|
||||
|
||||
state.font_atlas = core.gen_font_atlas(state, core.HardcodedFontPath);
|
||||
}
|
||||
log.debug(state.source_font_height);
|
||||
}, "increase font size");
|
||||
core.register_ctrl_key_action(input_map, .EQUAL, proc(state: ^State) {
|
||||
state.source_font_height += 2;
|
||||
state.source_font_width = state.source_font_height / 2;
|
||||
|
||||
state.font_atlas = core.gen_font_atlas(state, core.HardcodedFontPath);
|
||||
}, "decrease font size");
|
||||
}
|
||||
|
||||
core.register_key_action(input_map, .G, core.new_input_actions(), "Go commands");
|
||||
register_default_go_actions(&(&input_map.key_actions[.G]).action.(core.InputActions));
|
||||
|
||||
core.register_key_action(input_map, .V, proc(state: ^State) {
|
||||
state.mode = .Visual;
|
||||
core.reset_input_map(state)
|
||||
|
||||
core.current_buffer(state).selection = core.new_selection(core.current_buffer(state).cursor);
|
||||
}, "enter visual mode");
|
||||
|
||||
}
|
||||
|
||||
register_default_visual_actions :: proc(input_map: ^core.InputActions) {
|
||||
core.register_key_action(input_map, .ESCAPE, proc(state: ^State) {
|
||||
state.mode = .Normal;
|
||||
core.reset_input_map(state)
|
||||
|
||||
core.current_buffer(state).selection = nil;
|
||||
core.update_file_buffer_scroll(core.current_buffer(state))
|
||||
}, "exit visual mode");
|
||||
|
||||
// Cursor Movement
|
||||
{
|
||||
core.register_key_action(input_map, .W, proc(state: ^State) {
|
||||
sel_cur := &(core.current_buffer(state).selection.?);
|
||||
|
||||
core.move_cursor_forward_start_of_word(core.current_buffer(state), cursor = &sel_cur.end);
|
||||
}, "move forward one word");
|
||||
core.register_key_action(input_map, .E, proc(state: ^State) {
|
||||
sel_cur := &(core.current_buffer(state).selection.?);
|
||||
|
||||
core.move_cursor_forward_end_of_word(core.current_buffer(state), cursor = &sel_cur.end);
|
||||
}, "move forward to end of word");
|
||||
|
||||
core.register_key_action(input_map, .B, proc(state: ^State) {
|
||||
sel_cur := &(core.current_buffer(state).selection.?);
|
||||
|
||||
core.move_cursor_backward_start_of_word(core.current_buffer(state), cursor = &sel_cur.end);
|
||||
}, "move backward one word");
|
||||
|
||||
core.register_key_action(input_map, .K, proc(state: ^State) {
|
||||
sel_cur := &(core.current_buffer(state).selection.?);
|
||||
|
||||
core.move_cursor_up(core.current_buffer(state), cursor = &sel_cur.end);
|
||||
}, "move up one line");
|
||||
core.register_key_action(input_map, .J, proc(state: ^State) {
|
||||
sel_cur := &(core.current_buffer(state).selection.?);
|
||||
|
||||
core.move_cursor_down(core.current_buffer(state), cursor = &sel_cur.end);
|
||||
}, "move down one line");
|
||||
core.register_key_action(input_map, .H, proc(state: ^State) {
|
||||
sel_cur := &(core.current_buffer(state).selection.?);
|
||||
|
||||
core.move_cursor_left(core.current_buffer(state), cursor = &sel_cur.end);
|
||||
}, "move left one char");
|
||||
core.register_key_action(input_map, .L, proc(state: ^State) {
|
||||
sel_cur := &(core.current_buffer(state).selection.?);
|
||||
|
||||
core.move_cursor_right(core.current_buffer(state), cursor = &sel_cur.end);
|
||||
}, "move right one char");
|
||||
|
||||
core.register_ctrl_key_action(input_map, .U, proc(state: ^State) {
|
||||
sel_cur := &(core.current_buffer(state).selection.?);
|
||||
|
||||
core.scroll_file_buffer(core.current_buffer(state), .Up, cursor = &sel_cur.end);
|
||||
}, "scroll buffer up");
|
||||
core.register_ctrl_key_action(input_map, .D, proc(state: ^State) {
|
||||
sel_cur := &(core.current_buffer(state).selection.?);
|
||||
|
||||
core.scroll_file_buffer(core.current_buffer(state), .Down, cursor = &sel_cur.end);
|
||||
}, "scroll buffer up");
|
||||
}
|
||||
|
||||
// Text Modification
|
||||
{
|
||||
core.register_key_action(input_map, .D, proc(state: ^State) {
|
||||
sel_cur := &(core.current_buffer(state).selection.?);
|
||||
|
||||
core.delete_content(core.current_buffer(state), sel_cur);
|
||||
core.current_buffer(state).selection = nil;
|
||||
core.update_file_buffer_scroll(core.current_buffer(state))
|
||||
|
||||
state.mode = .Normal
|
||||
core.reset_input_map(state)
|
||||
}, "delete selection");
|
||||
|
||||
core.register_key_action(input_map, .C, proc(state: ^State) {
|
||||
sel_cur := &(core.current_buffer(state).selection.?);
|
||||
|
||||
core.delete_content(core.current_buffer(state), sel_cur);
|
||||
core.current_buffer(state).selection = nil;
|
||||
core.update_file_buffer_scroll(core.current_buffer(state))
|
||||
|
||||
state.mode = .Insert
|
||||
core.reset_input_map(state, core.Mode.Normal)
|
||||
sdl2.StartTextInput();
|
||||
}, "change selection");
|
||||
}
|
||||
}
|
||||
|
||||
register_default_text_input_actions :: proc(input_map: ^core.InputActions) {
|
||||
core.register_key_action(input_map, .I, proc(state: ^State) {
|
||||
state.mode = .Insert;
|
||||
sdl2.StartTextInput();
|
||||
}, "enter insert mode");
|
||||
core.register_key_action(input_map, .A, proc(state: ^State) {
|
||||
core.move_cursor_right(core.current_buffer(state), false);
|
||||
state.mode = .Insert;
|
||||
sdl2.StartTextInput();
|
||||
}, "enter insert mode after character (append)");
|
||||
|
||||
// TODO: add shift+o to insert newline above current one
|
||||
|
||||
core.register_key_action(input_map, .O, proc(state: ^State) {
|
||||
core.move_cursor_end_of_line(core.current_buffer(state), false);
|
||||
core.insert_content(core.current_buffer(state), []u8{'\n'});
|
||||
state.mode = .Insert;
|
||||
|
||||
sdl2.StartTextInput();
|
||||
}, "insert mode on newline");
|
||||
}
|
251
src/main.odin
251
src/main.odin
|
@ -19,8 +19,6 @@ import "panels"
|
|||
import "theme"
|
||||
import "ui"
|
||||
|
||||
HardcodedFontPath :: "bin/JetBrainsMono-Regular.ttf";
|
||||
|
||||
State :: core.State;
|
||||
FileBuffer :: core.FileBuffer;
|
||||
|
||||
|
@ -47,202 +45,6 @@ do_insert_mode :: proc(state: ^State, buffer: ^FileBuffer) {
|
|||
do_visual_mode :: proc(state: ^State, buffer: ^FileBuffer) {
|
||||
}
|
||||
|
||||
register_default_leader_actions :: proc(input_map: ^core.InputActions) {
|
||||
core.register_key_action(input_map, .Q, proc(state: ^State) {
|
||||
state.current_input_map = &state.input_map.mode[state.mode];
|
||||
}, "close this help");
|
||||
}
|
||||
|
||||
register_default_go_actions :: proc(input_map: ^core.InputActions) {
|
||||
core.register_key_action(input_map, .H, proc(state: ^State) {
|
||||
core.move_cursor_start_of_line(core.current_buffer(state));
|
||||
state.current_input_map = &state.input_map.mode[state.mode];
|
||||
}, "move to beginning of line");
|
||||
core.register_key_action(input_map, .L, proc(state: ^State) {
|
||||
core.move_cursor_end_of_line(core.current_buffer(state));
|
||||
state.current_input_map = &state.input_map.mode[state.mode];
|
||||
}, "move to end of line");
|
||||
}
|
||||
|
||||
register_default_input_actions :: proc(input_map: ^core.InputActions) {
|
||||
// Cursor Movement
|
||||
{
|
||||
core.register_key_action(input_map, .W, proc(state: ^State) {
|
||||
core.move_cursor_forward_start_of_word(core.current_buffer(state));
|
||||
}, "move forward one word");
|
||||
core.register_key_action(input_map, .E, proc(state: ^State) {
|
||||
core.move_cursor_forward_end_of_word(core.current_buffer(state));
|
||||
}, "move forward to end of word");
|
||||
|
||||
core.register_key_action(input_map, .B, proc(state: ^State) {
|
||||
core.move_cursor_backward_start_of_word(core.current_buffer(state));
|
||||
}, "move backward one word");
|
||||
|
||||
core.register_key_action(input_map, .K, proc(state: ^State) {
|
||||
core.move_cursor_up(core.current_buffer(state));
|
||||
}, "move up one line");
|
||||
core.register_key_action(input_map, .J, proc(state: ^State) {
|
||||
core.move_cursor_down(core.current_buffer(state));
|
||||
}, "move down one line");
|
||||
core.register_key_action(input_map, .H, proc(state: ^State) {
|
||||
core.move_cursor_left(core.current_buffer(state));
|
||||
}, "move left one char");
|
||||
core.register_key_action(input_map, .L, proc(state: ^State) {
|
||||
core.move_cursor_right(core.current_buffer(state));
|
||||
}, "move right one char");
|
||||
|
||||
core.register_ctrl_key_action(input_map, .U, proc(state: ^State) {
|
||||
core.scroll_file_buffer(core.current_buffer(state), .Up);
|
||||
}, "scroll buffer up");
|
||||
core.register_ctrl_key_action(input_map, .D, proc(state: ^State) {
|
||||
core.scroll_file_buffer(core.current_buffer(state), .Down);
|
||||
}, "scroll buffer up");
|
||||
}
|
||||
|
||||
// Scale font size
|
||||
{
|
||||
core.register_ctrl_key_action(input_map, .MINUS, proc(state: ^State) {
|
||||
if state.source_font_height > 16 {
|
||||
state.source_font_height -= 2;
|
||||
state.source_font_width = state.source_font_height / 2;
|
||||
|
||||
state.font_atlas = core.gen_font_atlas(state, HardcodedFontPath);
|
||||
}
|
||||
log.debug(state.source_font_height);
|
||||
}, "increase font size");
|
||||
core.register_ctrl_key_action(input_map, .EQUAL, proc(state: ^State) {
|
||||
state.source_font_height += 2;
|
||||
state.source_font_width = state.source_font_height / 2;
|
||||
|
||||
state.font_atlas = core.gen_font_atlas(state, HardcodedFontPath);
|
||||
}, "decrease font size");
|
||||
}
|
||||
|
||||
core.register_key_action(input_map, .SPACE, core.new_input_actions(), "leader commands");
|
||||
register_default_leader_actions(&(&input_map.key_actions[.SPACE]).action.(core.InputActions));
|
||||
|
||||
core.register_key_action(input_map, .G, core.new_input_actions(), "Go commands");
|
||||
register_default_go_actions(&(&input_map.key_actions[.G]).action.(core.InputActions));
|
||||
|
||||
core.register_key_action(&state.input_map.mode[.Normal], .V, proc(state: ^State) {
|
||||
state.mode = .Visual;
|
||||
state.current_input_map = &state.input_map.mode[.Visual];
|
||||
|
||||
core.current_buffer(state).selection = core.new_selection(core.current_buffer(state).cursor);
|
||||
}, "enter visual mode");
|
||||
|
||||
}
|
||||
|
||||
register_default_visual_actions :: proc(input_map: ^core.InputActions) {
|
||||
core.register_key_action(input_map, .ESCAPE, proc(state: ^State) {
|
||||
state.mode = .Normal;
|
||||
state.current_input_map = &state.input_map.mode[.Normal];
|
||||
|
||||
core.current_buffer(state).selection = nil;
|
||||
core.update_file_buffer_scroll(core.current_buffer(state))
|
||||
}, "exit visual mode");
|
||||
|
||||
// Cursor Movement
|
||||
{
|
||||
core.register_key_action(input_map, .W, proc(state: ^State) {
|
||||
sel_cur := &(core.current_buffer(state).selection.?);
|
||||
|
||||
core.move_cursor_forward_start_of_word(core.current_buffer(state), cursor = &sel_cur.end);
|
||||
}, "move forward one word");
|
||||
core.register_key_action(input_map, .E, proc(state: ^State) {
|
||||
sel_cur := &(core.current_buffer(state).selection.?);
|
||||
|
||||
core.move_cursor_forward_end_of_word(core.current_buffer(state), cursor = &sel_cur.end);
|
||||
}, "move forward to end of word");
|
||||
|
||||
core.register_key_action(input_map, .B, proc(state: ^State) {
|
||||
sel_cur := &(core.current_buffer(state).selection.?);
|
||||
|
||||
core.move_cursor_backward_start_of_word(core.current_buffer(state), cursor = &sel_cur.end);
|
||||
}, "move backward one word");
|
||||
|
||||
core.register_key_action(input_map, .K, proc(state: ^State) {
|
||||
sel_cur := &(core.current_buffer(state).selection.?);
|
||||
|
||||
core.move_cursor_up(core.current_buffer(state), cursor = &sel_cur.end);
|
||||
}, "move up one line");
|
||||
core.register_key_action(input_map, .J, proc(state: ^State) {
|
||||
sel_cur := &(core.current_buffer(state).selection.?);
|
||||
|
||||
core.move_cursor_down(core.current_buffer(state), cursor = &sel_cur.end);
|
||||
}, "move down one line");
|
||||
core.register_key_action(input_map, .H, proc(state: ^State) {
|
||||
sel_cur := &(core.current_buffer(state).selection.?);
|
||||
|
||||
core.move_cursor_left(core.current_buffer(state), cursor = &sel_cur.end);
|
||||
}, "move left one char");
|
||||
core.register_key_action(input_map, .L, proc(state: ^State) {
|
||||
sel_cur := &(core.current_buffer(state).selection.?);
|
||||
|
||||
core.move_cursor_right(core.current_buffer(state), cursor = &sel_cur.end);
|
||||
}, "move right one char");
|
||||
|
||||
core.register_ctrl_key_action(input_map, .U, proc(state: ^State) {
|
||||
sel_cur := &(core.current_buffer(state).selection.?);
|
||||
|
||||
core.scroll_file_buffer(core.current_buffer(state), .Up, cursor = &sel_cur.end);
|
||||
}, "scroll buffer up");
|
||||
core.register_ctrl_key_action(input_map, .D, proc(state: ^State) {
|
||||
sel_cur := &(core.current_buffer(state).selection.?);
|
||||
|
||||
core.scroll_file_buffer(core.current_buffer(state), .Down, cursor = &sel_cur.end);
|
||||
}, "scroll buffer up");
|
||||
}
|
||||
|
||||
// Text Modification
|
||||
{
|
||||
core.register_key_action(input_map, .D, proc(state: ^State) {
|
||||
sel_cur := &(core.current_buffer(state).selection.?);
|
||||
|
||||
core.delete_content(core.current_buffer(state), sel_cur);
|
||||
core.current_buffer(state).selection = nil;
|
||||
core.update_file_buffer_scroll(core.current_buffer(state))
|
||||
|
||||
state.mode = .Normal
|
||||
state.current_input_map = &state.input_map.mode[.Normal];
|
||||
}, "delete selection");
|
||||
|
||||
core.register_key_action(input_map, .C, proc(state: ^State) {
|
||||
sel_cur := &(core.current_buffer(state).selection.?);
|
||||
|
||||
core.delete_content(core.current_buffer(state), sel_cur);
|
||||
core.current_buffer(state).selection = nil;
|
||||
core.update_file_buffer_scroll(core.current_buffer(state))
|
||||
|
||||
state.mode = .Insert
|
||||
state.current_input_map = &state.input_map.mode[.Normal];
|
||||
sdl2.StartTextInput();
|
||||
}, "change selection");
|
||||
}
|
||||
}
|
||||
|
||||
register_default_text_input_actions :: proc(input_map: ^core.InputActions) {
|
||||
core.register_key_action(input_map, .I, proc(state: ^State) {
|
||||
state.mode = .Insert;
|
||||
sdl2.StartTextInput();
|
||||
}, "enter insert mode");
|
||||
core.register_key_action(input_map, .A, proc(state: ^State) {
|
||||
core.move_cursor_right(core.current_buffer(state), false);
|
||||
state.mode = .Insert;
|
||||
sdl2.StartTextInput();
|
||||
}, "enter insert mode after character (append)");
|
||||
|
||||
// TODO: add shift+o to insert newline above current one
|
||||
|
||||
core.register_key_action(input_map, .O, proc(state: ^State) {
|
||||
core.move_cursor_end_of_line(core.current_buffer(state), false);
|
||||
core.insert_content(core.current_buffer(state), []u8{'\n'});
|
||||
state.mode = .Insert;
|
||||
|
||||
sdl2.StartTextInput();
|
||||
}, "insert mode on newline");
|
||||
}
|
||||
|
||||
ui_font_width :: proc() -> i32 {
|
||||
return i32(state.source_font_width);
|
||||
}
|
||||
|
@ -269,11 +71,11 @@ draw :: proc(state: ^State) {
|
|||
kind = {ui.Grow{}, ui.Grow{}},
|
||||
})
|
||||
{
|
||||
for i in 0..<state.panels.len {
|
||||
panel := &state.panels.data[i]
|
||||
|
||||
if panel.render_proc != nil {
|
||||
panel.render_proc(state, &panel.panel_state)
|
||||
for i in 0..<len(state.panels.data) {
|
||||
if panel, ok := util.get(&state.panels, i).?; ok {
|
||||
if panel.render_proc != nil {
|
||||
panel.render_proc(state, &panel.panel_state)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -282,7 +84,8 @@ draw :: proc(state: ^State) {
|
|||
ui.compute_layout_2(new_ui)
|
||||
ui.draw(new_ui, state)
|
||||
|
||||
if state.mode != .Insert && state.current_input_map != &state.input_map.mode[state.mode] {
|
||||
// TODO: figure out when to not show the input help menu
|
||||
if state.mode != .Insert { // && state.current_input_map != &state.input_map.mode[state.mode] {
|
||||
longest_description := 0;
|
||||
for key, action in state.current_input_map.key_actions {
|
||||
if len(action.description) > longest_description {
|
||||
|
@ -406,7 +209,6 @@ main :: proc() {
|
|||
screen_height = 480,
|
||||
source_font_width = 8,
|
||||
source_font_height = 16,
|
||||
input_map = core.new_input_map(),
|
||||
commands = make(core.EditorCommandList),
|
||||
command_arena = mem.arena_allocator(&_command_arena),
|
||||
|
||||
|
@ -429,11 +231,7 @@ main :: proc() {
|
|||
mem.scratch_allocator_init(&scratch, 1024*1024);
|
||||
scratch_alloc = mem.scratch_allocator(&scratch);
|
||||
|
||||
state.current_input_map = &state.input_map.mode[.Normal];
|
||||
register_default_input_actions(&state.input_map.mode[.Normal]);
|
||||
register_default_visual_actions(&state.input_map.mode[.Visual]);
|
||||
|
||||
register_default_text_input_actions(&state.input_map.mode[.Normal]);
|
||||
core.reset_input_map(&state)
|
||||
|
||||
// core.register_editor_command(
|
||||
// &state.commands,
|
||||
|
@ -482,6 +280,7 @@ main :: proc() {
|
|||
"Opens a new scratch buffer",
|
||||
proc(state: ^State) {
|
||||
buffer := core.new_virtual_file_buffer(context.allocator);
|
||||
util.append_static_list(&state.panels, panels.make_file_buffer_panel(len(state.buffers)))
|
||||
runtime.append(&state.buffers, buffer);
|
||||
}
|
||||
)
|
||||
|
@ -500,13 +299,7 @@ main :: proc() {
|
|||
if args, ok := core.attempt_read_command_args(Args, state.command_args[:]); ok {
|
||||
log.info("attempting to open file", args.file_path)
|
||||
|
||||
buffer, err := core.new_file_buffer(context.allocator, args.file_path, state.directory);
|
||||
if err.type != .None {
|
||||
log.error("Failed to create file buffer:", err);
|
||||
return;
|
||||
}
|
||||
|
||||
runtime.append(&state.buffers, buffer);
|
||||
panels.open_file_buffer_in_new_panel(state, args.file_path)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
@ -522,14 +315,7 @@ main :: proc() {
|
|||
|
||||
if len(os.args) > 1 {
|
||||
for arg in os.args[1:] {
|
||||
buffer, err := core.new_file_buffer(context.allocator, arg, state.directory);
|
||||
if err.type != .None {
|
||||
log.error("Failed to create file buffer:", err);
|
||||
continue;
|
||||
}
|
||||
|
||||
util.append_static_list(&state.panels, panels.make_file_buffer_panel(len(state.buffers)))
|
||||
runtime.append(&state.buffers, buffer);
|
||||
panels.open_file_buffer_in_new_panel(&state, arg)
|
||||
}
|
||||
} else {
|
||||
buffer := core.new_virtual_file_buffer(context.allocator);
|
||||
|
@ -538,9 +324,6 @@ main :: proc() {
|
|||
runtime.append(&state.buffers, buffer);
|
||||
}
|
||||
|
||||
util.append_static_list(&state.panels, panels.make_grep_panel(&state))
|
||||
state.current_panel = state.panels.len-1
|
||||
|
||||
if sdl2.Init({.VIDEO}) < 0 {
|
||||
log.error("SDL failed to initialize:", sdl2.GetError());
|
||||
return;
|
||||
|
@ -578,7 +361,7 @@ main :: proc() {
|
|||
log.error("Failed to create renderer:", sdl2.GetError());
|
||||
return;
|
||||
}
|
||||
state.font_atlas = core.gen_font_atlas(&state, HardcodedFontPath);
|
||||
state.font_atlas = core.gen_font_atlas(&state, core.HardcodedFontPath);
|
||||
defer {
|
||||
if state.font_atlas.font != nil {
|
||||
ttf.CloseFont(state.font_atlas.font);
|
||||
|
@ -608,10 +391,6 @@ main :: proc() {
|
|||
|
||||
control_key_pressed: bool;
|
||||
for !state.should_close {
|
||||
if current_panel, ok := util.get(&state.panels, state.current_panel.? or_else -1).?; ok {
|
||||
state.current_input_map = ¤t_panel.input_map.mode[state.mode]
|
||||
}
|
||||
|
||||
{
|
||||
// ui_context.last_mouse_left_down = ui_context.mouse_left_down;
|
||||
// ui_context.last_mouse_right_down = ui_context.mouse_right_down;
|
||||
|
@ -733,6 +512,12 @@ main :: proc() {
|
|||
append(&buffer.input_buffer, u8(char));
|
||||
}
|
||||
}
|
||||
|
||||
if current_panel, ok := state.current_panel.?; ok {
|
||||
if panel, ok := util.get(&state.panels, current_panel).?; ok && panel.on_buffer_input_proc != nil {
|
||||
panel.on_buffer_input_proc(&state, &panel.panel_state)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +1,103 @@
|
|||
package panels
|
||||
|
||||
import "base:runtime"
|
||||
import "core:mem"
|
||||
import "core:path/filepath"
|
||||
import "core:fmt"
|
||||
import "core:strings"
|
||||
import "core:log"
|
||||
|
||||
import "vendor:sdl2"
|
||||
|
||||
import "../core"
|
||||
import "../input"
|
||||
import "../util"
|
||||
import "../ui"
|
||||
|
||||
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 ---
|
||||
free_grep_results :: proc(results: RS_GrepResults) ---
|
||||
}
|
||||
|
||||
RS_GrepResults :: struct {
|
||||
results: [^]RS_GrepResult,
|
||||
len: u32,
|
||||
}
|
||||
RS_GrepResult :: struct {
|
||||
line_number: u64,
|
||||
column: u64,
|
||||
|
||||
text_len: u32,
|
||||
path_len: u32,
|
||||
|
||||
text: [^]u8,
|
||||
path: [^]u8,
|
||||
}
|
||||
|
||||
@(private)
|
||||
rs_grep_as_results :: proc(results: ^RS_GrepResults, allocator := context.allocator) -> []core.GrepQueryResult {
|
||||
query_results := make([]core.GrepQueryResult, results.len)
|
||||
|
||||
for i in 0..<results.len {
|
||||
r := results.results[i]
|
||||
|
||||
query_results[i] = core.GrepQueryResult {
|
||||
file_context = strings.clone_from_ptr(r.text, int(r.text_len), allocator) or_continue,
|
||||
file_path = strings.clone_from_ptr(r.path, int(r.path_len), allocator) or_continue,
|
||||
line = int(r.line_number),
|
||||
col = int(r.column),
|
||||
}
|
||||
}
|
||||
|
||||
return query_results
|
||||
}
|
||||
|
||||
// NOTE: odd that this is here, but I don't feel like thinking of a better dep-tree to fix it
|
||||
register_default_leader_actions :: proc(input_map: ^core.InputActions) {
|
||||
core.register_key_action(input_map, .Q, proc(state: ^core.State) {
|
||||
core.reset_input_map(state)
|
||||
}, "close this help");
|
||||
|
||||
core.register_key_action(input_map, .R, proc(state: ^core.State) {
|
||||
open(state, make_grep_panel(state))
|
||||
}, "Grep Workspace")
|
||||
}
|
||||
|
||||
open :: proc(state: ^core.State, panel: core.Panel, make_active: bool = true) {
|
||||
if panel_id, ok := util.append_static_list(&state.panels, panel).?; ok && make_active {
|
||||
state.current_panel = panel_id
|
||||
|
||||
core.reset_input_map(state)
|
||||
}
|
||||
}
|
||||
|
||||
close :: proc(state: ^core.State, panel_id: int) {
|
||||
if panel, ok := util.get(&state.panels, panel_id).?; ok {
|
||||
if panel.drop != nil {
|
||||
panel.drop(state, &panel.panel_state)
|
||||
}
|
||||
|
||||
util.delete(&state.panels, panel_id)
|
||||
|
||||
core.reset_input_map(state)
|
||||
}
|
||||
}
|
||||
|
||||
open_file_buffer_in_new_panel :: proc(state: ^core.State, file_path: string) {
|
||||
buffer, err := core.new_file_buffer(context.allocator, file_path, state.directory);
|
||||
if err.type != .None {
|
||||
log.error("Failed to create file buffer:", err);
|
||||
return;
|
||||
}
|
||||
|
||||
buffer_index := len(state.buffers)
|
||||
runtime.append(&state.buffers, buffer);
|
||||
|
||||
open(state, make_file_buffer_panel(buffer_index))
|
||||
}
|
||||
|
||||
render_file_buffer :: proc(state: ^core.State, s: ^ui.State, buffer: ^core.FileBuffer) {
|
||||
draw_func := proc(state: ^core.State, e: ui.UI_Element, user_data: rawptr) {
|
||||
buffer := transmute(^core.FileBuffer)user_data;
|
||||
|
@ -64,10 +152,24 @@ render_raw_buffer :: proc(state: ^core.State, s: ^ui.State, buffer: ^core.FileBu
|
|||
}
|
||||
|
||||
make_file_buffer_panel :: proc(buffer_index: int) -> core.Panel {
|
||||
input_map := core.new_input_map()
|
||||
|
||||
leader_actions := core.new_input_actions()
|
||||
register_default_leader_actions(&leader_actions);
|
||||
core.register_key_action(&input_map.mode[.Normal], .SPACE, leader_actions, "leader commands");
|
||||
|
||||
input.register_default_input_actions(&input_map.mode[.Normal]);
|
||||
input.register_default_visual_actions(&input_map.mode[.Visual]);
|
||||
input.register_default_text_input_actions(&input_map.mode[.Normal]);
|
||||
|
||||
return core.Panel {
|
||||
panel_state = core.FileBufferPanel { buffer_index = buffer_index },
|
||||
// TODO: move the input registration from main.odin to here
|
||||
input_map = core.new_input_map(),
|
||||
input_map = input_map,
|
||||
buffer_proc = proc(state: ^core.State, panel_state: ^core.PanelState) -> (buffer: ^core.FileBuffer, ok: bool) {
|
||||
panel_state := panel_state.(core.FileBufferPanel) or_return;
|
||||
|
||||
return &state.buffers[panel_state.buffer_index], true
|
||||
},
|
||||
render_proc = proc(state: ^core.State, panel_state: ^core.PanelState) -> (ok: bool) {
|
||||
panel_state := panel_state.(core.FileBufferPanel) or_return;
|
||||
s := transmute(^ui.State)state.ui
|
||||
|
@ -81,10 +183,44 @@ make_file_buffer_panel :: proc(buffer_index: int) -> core.Panel {
|
|||
}
|
||||
|
||||
make_grep_panel :: proc(state: ^core.State) -> core.Panel {
|
||||
query_arena: mem.Arena
|
||||
mem.arena_init(&query_arena, make([]u8, 1024*1024))
|
||||
|
||||
input_map := core.new_input_map()
|
||||
grep_input_buffer := core.new_virtual_file_buffer(context.allocator)
|
||||
runtime.append(&state.buffers, grep_input_buffer)
|
||||
|
||||
run_query :: proc(panel_state: ^core.GrepPanel, query: string, directory: string) {
|
||||
mem.arena_free_all(&panel_state.query_arena)
|
||||
panel_state.query_results = nil
|
||||
|
||||
rs_results := grep(
|
||||
strings.clone_to_cstring(query, allocator = context.temp_allocator),
|
||||
strings.clone_to_cstring(directory, allocator = context.temp_allocator)
|
||||
);
|
||||
|
||||
panel_state.query_results = rs_grep_as_results(&rs_results, mem.arena_allocator(&panel_state.query_arena))
|
||||
free_grep_results(rs_results)
|
||||
}
|
||||
|
||||
core.register_key_action(&input_map.mode[.Normal], .ENTER, proc(state: ^core.State) {
|
||||
if current_panel, ok := util.get(&state.panels, state.current_panel.? or_else -1).?; ok {
|
||||
this_panel := state.current_panel.?
|
||||
|
||||
if panel_state, ok := ¤t_panel.panel_state.(core.GrepPanel); ok {
|
||||
if panel_state.query_results != nil {
|
||||
selected_result := &panel_state.query_results[panel_state.selected_result]
|
||||
|
||||
open_file_buffer_in_new_panel(state, selected_result.file_path)
|
||||
|
||||
mem.arena_free_all(&panel_state.query_arena)
|
||||
panel_state.query_results = nil
|
||||
|
||||
close(state, this_panel)
|
||||
}
|
||||
}
|
||||
}
|
||||
}, "Open File");
|
||||
core.register_key_action(&input_map.mode[.Normal], .I, proc(state: ^core.State) {
|
||||
state.mode = .Insert;
|
||||
sdl2.StartTextInput();
|
||||
|
@ -114,29 +250,18 @@ make_grep_panel :: proc(state: ^core.State) -> core.Panel {
|
|||
state.mode = .Normal;
|
||||
sdl2.StopTextInput();
|
||||
}, "exit insert mode");
|
||||
core.register_key_action(&input_map.mode[.Insert], .ENTER, proc(state: ^core.State) {
|
||||
state.mode = .Normal;
|
||||
sdl2.StopTextInput();
|
||||
}, "search");
|
||||
core.register_key_action(&input_map.mode[.Normal], .ESCAPE, proc(state: ^core.State) {
|
||||
if state.current_panel != nil {
|
||||
close(state, state.current_panel.?)
|
||||
}
|
||||
}, "close panel");
|
||||
|
||||
|
||||
results := make([]core.GrepQueryResult, 4)
|
||||
results[0] = core.GrepQueryResult {
|
||||
file_path = "src/main.odin"
|
||||
}
|
||||
results[1] = core.GrepQueryResult {
|
||||
file_path = "src/core/core.odin"
|
||||
}
|
||||
results[2] = core.GrepQueryResult {
|
||||
file_path = "src/panels/panels.odin"
|
||||
}
|
||||
results[3] = core.GrepQueryResult {
|
||||
file_path = "src/core/gfx.odin"
|
||||
}
|
||||
|
||||
return core.Panel {
|
||||
panel_state = core.GrepPanel {
|
||||
query_arena = query_arena,
|
||||
buffer = len(state.buffers)-1,
|
||||
query_results = results,
|
||||
query_results = nil,
|
||||
},
|
||||
input_map = input_map,
|
||||
buffer_proc = proc(state: ^core.State, panel_state: ^core.PanelState) -> (buffer: ^core.FileBuffer, ok: bool) {
|
||||
|
@ -144,28 +269,75 @@ make_grep_panel :: proc(state: ^core.State) -> core.Panel {
|
|||
|
||||
return &state.buffers[panel_state.buffer], true
|
||||
},
|
||||
on_buffer_input_proc = proc(state: ^core.State, panel_state: ^core.PanelState) {
|
||||
if panel_state, ok := &panel_state.(core.GrepPanel); ok {
|
||||
buffer := &state.buffers[panel_state.buffer]
|
||||
run_query(panel_state, string(buffer.input_buffer[:]), state.directory)
|
||||
}
|
||||
},
|
||||
drop = proc(state: ^core.State, panel_state: ^core.PanelState) {
|
||||
if panel_state, ok := &panel_state.(core.GrepPanel); ok {
|
||||
delete(panel_state.query_arena.data)
|
||||
}
|
||||
},
|
||||
render_proc = proc(state: ^core.State, panel_state: ^core.PanelState) -> (ok: bool) {
|
||||
panel_state := panel_state.(core.GrepPanel) or_return;
|
||||
|
||||
s := transmute(^ui.State)state.ui
|
||||
|
||||
ui.open_element(s, nil, {
|
||||
dir = .TopToBottom,
|
||||
kind = {ui.Grow{}, ui.Grow{}}
|
||||
})
|
||||
{
|
||||
defer ui.close_element(s)
|
||||
// query results and file contents side-by-side
|
||||
ui.open_element(s, nil, {
|
||||
dir = .LeftToRight,
|
||||
kind = {ui.Grow{}, ui.Grow{}}
|
||||
})
|
||||
{
|
||||
if panel_state.query_results != nil {
|
||||
// query results
|
||||
ui.open_element(s, nil, {
|
||||
dir = .TopToBottom,
|
||||
kind = {ui.Grow{}, ui.Grow{}}
|
||||
})
|
||||
{
|
||||
for result, i in panel_state.query_results {
|
||||
ui.open_element(s, nil, {
|
||||
dir = .LeftToRight,
|
||||
kind = {ui.Fit{}, ui.Fit{}},
|
||||
})
|
||||
{
|
||||
defer ui.close_element(s)
|
||||
|
||||
for result, i in panel_state.query_results {
|
||||
// TODO: when styling is implemented, make this look better
|
||||
if panel_state.selected_result == i {
|
||||
ui.open_element(s, fmt.tprintf("%s <--", result.file_path), {})
|
||||
ui.open_element(s, fmt.tprintf("%v:%v: ", result.line, result.col), {})
|
||||
ui.close_element(s)
|
||||
|
||||
// TODO: when styling is implemented, make this look better
|
||||
if panel_state.selected_result == i {
|
||||
ui.open_element(s, fmt.tprintf("%s <--", result.file_path), {})
|
||||
ui.close_element(s)
|
||||
} else {
|
||||
ui.open_element(s, result.file_path, {})
|
||||
ui.close_element(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ui.close_element(s)
|
||||
} else {
|
||||
ui.open_element(s, result.file_path, {})
|
||||
|
||||
// file contents
|
||||
selected_result := &panel_state.query_results[panel_state.selected_result]
|
||||
ui.open_element(s, selected_result.file_context, {
|
||||
kind = {ui.Grow{}, ui.Grow{}}
|
||||
})
|
||||
ui.close_element(s)
|
||||
}
|
||||
}
|
||||
ui.close_element(s)
|
||||
|
||||
// text input
|
||||
ui.open_element(s, nil, {
|
||||
kind = {ui.Grow{}, ui.Exact(state.source_font_height)}
|
||||
})
|
||||
|
@ -175,6 +347,7 @@ make_grep_panel :: proc(state: ^core.State) -> core.Panel {
|
|||
render_raw_buffer(state, s, &state.buffers[panel_state.buffer])
|
||||
}
|
||||
}
|
||||
ui.close_element(s)
|
||||
|
||||
return true
|
||||
}
|
||||
|
|
|
@ -0,0 +1,381 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bstr"
|
||||
version = "1.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
|
||||
|
||||
[[package]]
|
||||
name = "encoding_rs"
|
||||
version = "0.8.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "encoding_rs_io"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1cc3c5651fb62ab8aa3103998dade57efdd028544bd300516baa31840c252a83"
|
||||
dependencies = [
|
||||
"encoding_rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "globset"
|
||||
version = "0.4.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "54a1028dfc5f5df5da8a56a73e6c153c9a9708ec57232470703592a3f18e49f5"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"bstr",
|
||||
"log",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "grep"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"grep 0.3.2",
|
||||
"termcolor",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "grep"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "308ae749734e28d749a86f33212c7b756748568ce332f970ac1d9cd8531f32e6"
|
||||
dependencies = [
|
||||
"grep-cli",
|
||||
"grep-matcher",
|
||||
"grep-printer",
|
||||
"grep-regex",
|
||||
"grep-searcher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "grep-cli"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47f1288f0e06f279f84926fa4c17e3fcd2a22b357927a82f2777f7be26e4cec0"
|
||||
dependencies = [
|
||||
"bstr",
|
||||
"globset",
|
||||
"libc",
|
||||
"log",
|
||||
"termcolor",
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "grep-matcher"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47a3141a10a43acfedc7c98a60a834d7ba00dfe7bec9071cbfc19b55b292ac02"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "grep-printer"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c112110ae4a891aa4d83ab82ecf734b307497d066f437686175e83fbd4e013fe"
|
||||
dependencies = [
|
||||
"bstr",
|
||||
"grep-matcher",
|
||||
"grep-searcher",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "grep-regex"
|
||||
version = "0.1.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9edd147c7e3296e7a26bd3a81345ce849557d5a8e48ed88f736074e760f91f7e"
|
||||
dependencies = [
|
||||
"bstr",
|
||||
"grep-matcher",
|
||||
"log",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "grep-searcher"
|
||||
version = "0.1.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9b6c14b3fc2e0a107d6604d3231dec0509e691e62447104bc385a46a7892cda"
|
||||
dependencies = [
|
||||
"bstr",
|
||||
"encoding_rs",
|
||||
"encoding_rs_io",
|
||||
"grep-matcher",
|
||||
"log",
|
||||
"memchr",
|
||||
"memmap2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.174"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
|
||||
|
||||
[[package]]
|
||||
name = "memmap2"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.95"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.219"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.219"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.140"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.104"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
|
||||
dependencies = [
|
||||
"same-file",
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||
dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
|
@ -0,0 +1,12 @@
|
|||
[package]
|
||||
name = "grep"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[lib]
|
||||
crate-type = ["staticlib"]
|
||||
|
||||
[dependencies]
|
||||
grep = "0.3.2"
|
||||
termcolor = "1.4.1"
|
||||
walkdir = "2.5.0"
|
|
@ -0,0 +1,208 @@
|
|||
use std::{
|
||||
error::Error,
|
||||
ffi::{CStr, CString, OsString},
|
||||
path::Path,
|
||||
str::FromStr,
|
||||
sync::mpsc::{Receiver, Sender},
|
||||
thread,
|
||||
};
|
||||
|
||||
use grep::{
|
||||
regex::RegexMatcherBuilder,
|
||||
searcher::{BinaryDetection, SearcherBuilder, Sink, SinkError},
|
||||
};
|
||||
use std::sync::mpsc::channel;
|
||||
use walkdir::WalkDir;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum SimpleSinkError {
|
||||
StandardError,
|
||||
NoLine,
|
||||
BadString,
|
||||
}
|
||||
|
||||
impl SinkError for SimpleSinkError {
|
||||
fn error_message<T: std::fmt::Display>(message: T) -> Self {
|
||||
eprintln!("{message}");
|
||||
|
||||
Self::StandardError
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Match {
|
||||
text: Vec<u8>,
|
||||
path: String,
|
||||
line_number: Option<u64>,
|
||||
column: u64,
|
||||
}
|
||||
impl Match {
|
||||
fn from_sink_match_with_path(
|
||||
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;
|
||||
|
||||
Ok(Self {
|
||||
text: line,
|
||||
path: path.unwrap_or_default(),
|
||||
line_number: value.line_number(),
|
||||
column,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
struct SimpleSink {
|
||||
current_path: Option<String>,
|
||||
matches: Vec<Match>,
|
||||
}
|
||||
impl Sink for SimpleSink {
|
||||
type Error = SimpleSinkError;
|
||||
|
||||
fn matched(
|
||||
&mut self,
|
||||
_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(),
|
||||
)?);
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
|
||||
fn search(pattern: &str, paths: &[&str]) -> Result<SimpleSink, 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::default();
|
||||
for path in paths {
|
||||
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) => {
|
||||
eprintln!("{}", err);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
if !dent.file_type().is_file() {
|
||||
continue;
|
||||
}
|
||||
sink.current_path = Some(dent.path().to_string_lossy().into());
|
||||
|
||||
let result = searcher.search_path(&matcher, dent.path(), &mut sink);
|
||||
|
||||
if let Err(err) = result {
|
||||
eprintln!("{}: {:?}", dent.path().display(), err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(sink)
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct GrepResult {
|
||||
line_number: u64,
|
||||
column: u64,
|
||||
|
||||
text_len: u32,
|
||||
path_len: u32,
|
||||
|
||||
text: *const u8,
|
||||
path: *const u8,
|
||||
}
|
||||
|
||||
impl From<Match> for GrepResult {
|
||||
fn from(value: Match) -> Self {
|
||||
Self {
|
||||
line_number: value.line_number.unwrap_or(1),
|
||||
column: value.column,
|
||||
// this won't totally bite me later
|
||||
text_len: value.text.len() as u32,
|
||||
path_len: value.path.len() as u32,
|
||||
text: Box::leak(value.text.into_boxed_slice()).as_ptr(),
|
||||
path: Box::leak(value.path.into_bytes().into_boxed_slice()).as_ptr(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl From<GrepResult> for Match {
|
||||
fn from(value: GrepResult) -> Self {
|
||||
unsafe {
|
||||
let text = std::slice::from_raw_parts(value.text, value.text_len as usize).to_vec();
|
||||
|
||||
let path = std::slice::from_raw_parts(value.path, value.path_len as usize).to_vec();
|
||||
let path = String::from_utf8_unchecked(path);
|
||||
|
||||
Self {
|
||||
text,
|
||||
path,
|
||||
line_number: Some(value.line_number),
|
||||
column: value.column,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct GrepResults {
|
||||
results: *const GrepResult,
|
||||
len: u32,
|
||||
}
|
||||
|
||||
// NOTE(pcleavelin): for some reason the current odin compiler (2025-04 as of this comment) is providing an extra argument (I assume a pointer to the odin context struct)
|
||||
#[unsafe(no_mangle)]
|
||||
extern "C" fn grep(
|
||||
// _: *const std::ffi::c_char,
|
||||
pattern: *const std::ffi::c_char,
|
||||
directory: *const std::ffi::c_char,
|
||||
) -> GrepResults {
|
||||
let (pattern, directory) = unsafe {
|
||||
(
|
||||
CStr::from_ptr(pattern).to_string_lossy(),
|
||||
CStr::from_ptr(directory).to_string_lossy(),
|
||||
)
|
||||
};
|
||||
|
||||
println!("pattern: '{pattern}', directory: '{directory}'");
|
||||
|
||||
let boxed = search(&pattern, &[&directory])
|
||||
.into_iter()
|
||||
.map(|sink| sink.matches.into_iter())
|
||||
.flatten()
|
||||
.map(Into::into)
|
||||
.collect::<Vec<_>>()
|
||||
.into_boxed_slice();
|
||||
|
||||
let len = boxed.len() as u32;
|
||||
|
||||
GrepResults {
|
||||
results: Box::leak(boxed).as_ptr(),
|
||||
len,
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
extern "C" fn free_grep_results(_: GrepResults) { }
|
|
@ -106,7 +106,7 @@ close_element :: proc(state: ^State, loc := #caller_location) -> UI_Layout {
|
|||
switch v in e.kind {
|
||||
case UI_Element_Kind_Text: {
|
||||
// FIXME: properly use font size
|
||||
e.layout.size.x = len(v) * 9
|
||||
e.layout.size.x = len(v) * 10
|
||||
}
|
||||
case UI_Element_Kind_Image: {
|
||||
// TODO
|
||||
|
@ -230,11 +230,19 @@ grow_children :: proc(state: ^State, index: int) {
|
|||
case .RightToLeft: fallthrough
|
||||
case .LeftToRight: {
|
||||
children_size.x += child.layout.size.x
|
||||
|
||||
if children_size.y < child.layout.size.y {
|
||||
children_size.y = child.layout.size.y
|
||||
}
|
||||
}
|
||||
|
||||
case .BottomToTop: fallthrough
|
||||
case .TopToBottom: {
|
||||
children_size.y += child.layout.size.y
|
||||
|
||||
if children_size.x < child.layout.size.x {
|
||||
children_size.x = child.layout.size.x
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,25 +3,31 @@ package util
|
|||
import "base:runtime"
|
||||
|
||||
StaticList :: struct($T: typeid) {
|
||||
data: []T,
|
||||
len: int,
|
||||
data: []StaticListSlot(T),
|
||||
}
|
||||
|
||||
append_static_list :: proc(list: ^StaticList($T), value: T) -> bool {
|
||||
if list.len >= len(list.data) {
|
||||
return false
|
||||
StaticListSlot :: struct($T: typeid) {
|
||||
active: bool,
|
||||
data: T,
|
||||
}
|
||||
|
||||
append_static_list :: proc(list: ^StaticList($T), value: T) -> Maybe(int) {
|
||||
for i in 0..<len(list.data) {
|
||||
if !list.data[i].active {
|
||||
list.data[i].active = true
|
||||
list.data[i].data = value
|
||||
|
||||
return i
|
||||
}
|
||||
}
|
||||
|
||||
list.data[list.len] = value
|
||||
list.len += 1
|
||||
|
||||
return true
|
||||
return nil
|
||||
}
|
||||
append :: proc{append_static_list}
|
||||
|
||||
make_static_list :: proc($T: typeid, len: int) -> StaticList(T) {
|
||||
list := StaticList(T) {
|
||||
data = runtime.make_slice([]T, len)
|
||||
data = runtime.make_slice([]StaticListSlot(T), len)
|
||||
}
|
||||
|
||||
return list
|
||||
|
@ -30,11 +36,23 @@ make_static_list :: proc($T: typeid, len: int) -> StaticList(T) {
|
|||
make :: proc{make_static_list}
|
||||
|
||||
get_static_list_elem :: proc(list: ^StaticList($T), index: int) -> Maybe(^T) {
|
||||
if index >= list.len {
|
||||
if index < 0 || index >= len(list.data) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &list.data[index]
|
||||
if list.data[index].active {
|
||||
return &list.data[index].data
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
get :: proc{get_static_list_elem}
|
||||
get :: proc{get_static_list_elem}
|
||||
|
||||
delete_static_list_elem :: proc(list: ^StaticList($T), index: int) {
|
||||
if index >= 0 && index < len(list.data) {
|
||||
list.data[index].active = false
|
||||
}
|
||||
}
|
||||
|
||||
delete :: proc{delete_static_list_elem}
|
Loading…
Reference in New Issue