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.. 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 }, 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 buffer := &state.buffers[panel_state.buffer_index] render_file_buffer(state, s, buffer) return true } } } 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(); }, "enter insert mode"); core.register_key_action(&input_map.mode[.Normal], .K, proc(state: ^core.State) { // NOTE: this is really jank, should probably update the input // action stuff to allow panels to be passed into these handlers if current_panel, ok := util.get(&state.panels, state.current_panel.? or_else -1).?; ok { if panel_state, ok := ¤t_panel.panel_state.(core.GrepPanel); ok { // TODO: bounds checking panel_state.selected_result -= 1 } } }, "move selection up"); core.register_key_action(&input_map.mode[.Normal], .J, proc(state: ^core.State) { // NOTE: this is really jank, should probably update the input // action stuff to allow panels to be passed into these handlers if current_panel, ok := util.get(&state.panels, state.current_panel.? or_else -1).?; ok { if panel_state, ok := ¤t_panel.panel_state.(core.GrepPanel); ok { // TODO: bounds checking panel_state.selected_result += 1 } } }, "move selection down"); core.register_key_action(&input_map.mode[.Insert], .ESCAPE, proc(state: ^core.State) { state.mode = .Normal; sdl2.StopTextInput(); }, "exit insert mode"); 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"); return core.Panel { panel_state = core.GrepPanel { query_arena = query_arena, buffer = len(state.buffers)-1, query_results = nil, }, input_map = input_map, buffer_proc = proc(state: ^core.State, panel_state: ^core.PanelState) -> (buffer: ^core.FileBuffer, ok: bool) { panel_state := panel_state.(core.GrepPanel) or_return; 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{}} }) { // 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) 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) // 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)} }) { defer ui.close_element(s) render_raw_buffer(state, s, &state.buffers[panel_state.buffer]) } } ui.close_element(s) return true } } }