diff --git a/src/core/core.odin b/src/core/core.odin index 63be5b4..f3f7c9f 100644 --- a/src/core/core.odin +++ b/src/core/core.odin @@ -102,10 +102,12 @@ FileBufferPanel :: struct { GrepPanel :: struct { query_arena: mem.Arena, + query_region: mem.Arena_Temp_Memory, buffer: int, selected_result: int, search_query: string, query_results: []GrepQueryResult, + glyphs: GlyphBuffer, } GrepQueryResult :: struct { diff --git a/src/core/file_buffer.odin b/src/core/file_buffer.odin index 88c10f5..e24005c 100644 --- a/src/core/file_buffer.odin +++ b/src/core/file_buffer.odin @@ -43,11 +43,6 @@ Selection :: struct { end: Cursor, } -Glyph :: struct { - codepoint: u8, - color: theme.PaletteColor, -} - FileBuffer :: struct { allocator: mem.Allocator, @@ -63,9 +58,7 @@ FileBuffer :: struct { added_content: [dynamic]u8, content_slices: [dynamic][]u8, - glyph_buffer_width: int, - glyph_buffer_height: int, - glyph_buffer: [dynamic]Glyph, + glyphs: GlyphBuffer, input_buffer: [dynamic]u8, } @@ -708,10 +701,7 @@ new_virtual_file_buffer :: proc(allocator: mem.Allocator) -> FileBuffer { added_content = make([dynamic]u8, 0, 1024*1024), content_slices = make([dynamic][]u8, 0, 1024*1024), - glyph_buffer_width = width, - glyph_buffer_height = height, - glyph_buffer = make([dynamic]Glyph, width*height, width*height), - + glyphs = make_glyph_buffer(width, height), input_buffer = make([dynamic]u8, 0, 1024), }; @@ -763,10 +753,7 @@ new_file_buffer :: proc(allocator: mem.Allocator, file_path: string, base_dir: s added_content = make([dynamic]u8, 0, 1024*1024), content_slices = make([dynamic][]u8, 0, 1024*1024), - glyph_buffer_width = width, - glyph_buffer_height = height, - glyph_buffer = make([dynamic]Glyph, width*height, width*height), - + glyphs = make_glyph_buffer(width, height), input_buffer = make([dynamic]u8, 0, 1024), }; @@ -812,11 +799,12 @@ next_buffer :: proc(state: ^State, prev_buffer: ^int) -> int { return index; } +// TODO: replace this with arena for the file buffer free_file_buffer :: proc(buffer: ^FileBuffer) { delete(buffer.original_content); delete(buffer.added_content); delete(buffer.content_slices); - delete(buffer.glyph_buffer); + delete(buffer.glyphs.buffer); delete(buffer.input_buffer); } @@ -830,9 +818,9 @@ color_character :: proc(buffer: ^FileBuffer, start: Cursor, end: Cursor, palette start.line -= buffer.top_line; } - if end.line >= buffer.top_line + buffer.glyph_buffer_height { - end.line = buffer.glyph_buffer_height - 1; - end.col = buffer.glyph_buffer_width - 1; + if end.line >= buffer.top_line + buffer.glyphs.height { + end.line = buffer.glyphs.height - 1; + end.col = buffer.glyphs.width - 1; } else { end.line -= buffer.top_line; } @@ -842,72 +830,19 @@ color_character :: proc(buffer: ^FileBuffer, start: Cursor, end: Cursor, palette end_col := end.col; if j > start.line && j < end.line { start_col = 0; - end_col = buffer.glyph_buffer_width; + end_col = buffer.glyphs.width; } else if j < end.line { - end_col = buffer.glyph_buffer_width; + end_col = buffer.glyphs.width; } else if j > start.line && j == end.line { start_col = 0; } - for i in start_col..= begin && screen_line >= buffer.glyph_buffer_height { break; } - - // render INSERT mode text into glyph buffer - if len(buffer.input_buffer) > 0 && rendered_line == buffer.cursor.line && rendered_col >= buffer.cursor.col && rendered_col < buffer.cursor.col + len(buffer.input_buffer) { - for k in 0..= begin && rendered_col < buffer.glyph_buffer_width { - buffer.glyph_buffer[rendered_col + screen_line * buffer.glyph_buffer_width].color = .Foreground; - buffer.glyph_buffer[rendered_col + screen_line * buffer.glyph_buffer_width].codepoint = buffer.input_buffer[k]; - - rendered_col += 1; - } - } - } - - screen_line = rendered_line - begin; - - if character == '\n' { - rendered_col = 0; - rendered_line += 1; - continue; - } - - if rendered_line >= begin && rendered_col < buffer.glyph_buffer_width { - buffer.glyph_buffer[rendered_col + screen_line * buffer.glyph_buffer_width] = Glyph { codepoint = character, color = .Foreground }; - } - - rendered_col += 1; - } -} - draw_file_buffer :: proc(state: ^State, buffer: ^FileBuffer, x: int, y: int, show_line_numbers: bool = true) { update_glyph_buffer(buffer); @@ -964,7 +899,8 @@ draw_file_buffer :: proc(state: ^State, buffer: ^FileBuffer, x: int, y: int, sho draw_rect(state, cursor_x, cursor_y, state.source_font_width, state.source_font_height, .Blue); } - for j in 0.. (buffer.top_line + buffer.glyph_buffer_height - 5) { - buffer.top_line = math.max(cursor.?.line - buffer.glyph_buffer_height + 5, 0); + if cursor.?.line > (buffer.top_line + buffer.glyphs.height - 5) { + buffer.top_line = math.max(cursor.?.line - buffer.glyphs.height + 5, 0); } else if cursor.?.line < (buffer.top_line + 5) { buffer.top_line = math.max(cursor.?.line - 5, 0); } - // if buffer.cursor.line > (buffer.top_line + buffer.glyph_buffer_height - 5) { - // buffer.top_line = math.max(buffer.cursor.line - buffer.glyph_buffer_height + 5, 0); + // if buffer.cursor.line > (buffer.top_line + buffer.glyphs.height - 5) { + // buffer.top_line = math.max(buffer.cursor.line - buffer.glyphs.height + 5, 0); // } else if buffer.cursor.line < (buffer.top_line + 5) { // buffer.top_line = math.max(buffer.cursor.line - 5, 0); // } diff --git a/src/core/glyph_buffer.odin b/src/core/glyph_buffer.odin new file mode 100644 index 0000000..cf5c213 --- /dev/null +++ b/src/core/glyph_buffer.odin @@ -0,0 +1,139 @@ +package core + +import "core:fmt" + +import "../theme" + +GlyphBuffer :: struct { + buffer: []Glyph, + width: int, + height: int, +} + +Glyph :: struct { + codepoint: u8, + color: theme.PaletteColor, +} + +make_glyph_buffer :: proc(width, height: int, allocator := context.allocator) -> GlyphBuffer { + context.allocator = allocator + + return GlyphBuffer { + width = width, + height = height, + buffer = make([]Glyph, width*height) + } +} + +update_glyph_buffer_from_file_buffer :: proc(buffer: ^FileBuffer) { + for &glyph in buffer.glyphs.buffer { + glyph = Glyph{}; + } + + begin := buffer.top_line; + rendered_col: int; + rendered_line: int; + + it := new_file_buffer_iter(buffer); + for character in iterate_file_buffer(&it) { + if character == '\r' { continue; } + + screen_line := rendered_line - begin; + // don't render past the screen + if rendered_line >= begin && screen_line >= buffer.glyphs.height { break; } + + // render INSERT mode text into glyph buffer + if len(buffer.input_buffer) > 0 && rendered_line == buffer.cursor.line && rendered_col >= buffer.cursor.col && rendered_col < buffer.cursor.col + len(buffer.input_buffer) { + for k in 0..= begin && rendered_col < buffer.glyphs.width { + buffer.glyphs.buffer[rendered_col + screen_line * buffer.glyphs.width].color = .Foreground; + buffer.glyphs.buffer[rendered_col + screen_line * buffer.glyphs.width].codepoint = buffer.input_buffer[k]; + + rendered_col += 1; + } + } + } + + screen_line = rendered_line - begin; + + if character == '\n' { + rendered_col = 0; + rendered_line += 1; + continue; + } + + if rendered_line >= begin && rendered_col < buffer.glyphs.width { + buffer.glyphs.buffer[rendered_col + screen_line * buffer.glyphs.width] = Glyph { codepoint = character, color = .Foreground }; + } + + rendered_col += 1; + } +} + +update_glyph_buffer_from_bytes :: proc(glyphs: ^GlyphBuffer, data: []u8, top_line: int) { + for &glyph in glyphs.buffer { + glyph = Glyph{}; + } + + begin := top_line; + rendered_col: int; + rendered_line: int; + + for character in data { + if character == '\r' { continue; } + + screen_line := rendered_line - begin; + // don't render past the screen + if rendered_line >= begin && screen_line >= glyphs.height { break; } + + screen_line = rendered_line - begin; + + if character == '\n' { + rendered_col = 0; + rendered_line += 1; + continue; + } + + if rendered_line >= begin && rendered_col < glyphs.width { + glyphs.buffer[rendered_col + screen_line * glyphs.width] = Glyph { codepoint = character, color = .Foreground }; + } + + rendered_col += 1; + } +} + +update_glyph_buffer :: proc{update_glyph_buffer_from_file_buffer, update_glyph_buffer_from_bytes} + +draw_glyph_buffer :: proc(state: ^State, glyphs: ^GlyphBuffer, x: int, y: int, top_line: int, show_line_numbers: bool = true) { + padding := 0; + if show_line_numbers { + padding = state.source_font_width * 5; + } + + for j in 0.. i32 { draw :: proc(state: ^State) { if buffer := core.current_buffer(state); buffer != nil { - buffer.glyph_buffer_height = math.min(256, int((state.screen_height - state.source_font_height*2) / state.source_font_height)) + 1; - buffer.glyph_buffer_width = math.min(256, int((state.screen_width - state.source_font_width) / state.source_font_width)); + buffer.glyphs.height = math.min(256, int((state.screen_height - state.source_font_height*2) / state.source_font_height)) + 1; + buffer.glyphs.width = math.min(256, int((state.screen_width - state.source_font_width) / state.source_font_width)); } render_color := theme.get_palette_color(.Background); @@ -168,8 +168,8 @@ ui_file_buffer :: proc(s: ^ui.State, buffer: ^FileBuffer) { draw_func := proc(state: ^State, e: ui.UI_Element, user_data: rawptr) { buffer := transmute(^FileBuffer)user_data; if buffer != nil { - buffer.glyph_buffer_width = e.layout.size.x / state.source_font_width; - buffer.glyph_buffer_height = e.layout.size.y / state.source_font_height + 1; + buffer.glyphs.width = e.layout.size.x / state.source_font_width; + buffer.glyphs.height = e.layout.size.y / state.source_font_height + 1; core.draw_file_buffer(state, buffer, e.layout.pos.x, e.layout.pos.y); } diff --git a/src/panels/panels.odin b/src/panels/panels.odin index 7d96582..e2b217c 100644 --- a/src/panels/panels.odin +++ b/src/panels/panels.odin @@ -120,7 +120,9 @@ close :: proc(state: ^core.State, panel_id: int) { util.delete(&state.panels, panel_id) // TODO: keep track of the last active panel instead of focusing back to the first one - state.current_panel = util.get_first_active_index(&state.panels).? + if first_active, ok := util.get_first_active_index(&state.panels).?; ok { + state.current_panel = first_active + } core.reset_input_map(state) } @@ -152,8 +154,8 @@ render_file_buffer :: proc(state: ^core.State, s: ^ui.State, buffer: ^core.FileB draw_func := proc(state: ^core.State, e: ui.UI_Element, user_data: rawptr) { buffer := transmute(^core.FileBuffer)user_data; if buffer != nil { - buffer.glyph_buffer_width = e.layout.size.x / state.source_font_width; - buffer.glyph_buffer_height = e.layout.size.y / state.source_font_height + 1; + buffer.glyphs.width = e.layout.size.x / state.source_font_width; + buffer.glyphs.height = e.layout.size.y / state.source_font_height + 1; core.draw_file_buffer(state, buffer, e.layout.pos.x, e.layout.pos.y); } @@ -205,8 +207,8 @@ render_raw_buffer :: proc(state: ^core.State, s: ^ui.State, buffer: ^core.FileBu draw_func := proc(state: ^core.State, e: ui.UI_Element, user_data: rawptr) { buffer := transmute(^core.FileBuffer)user_data; if buffer != nil { - buffer.glyph_buffer_width = e.layout.size.x / state.source_font_width; - buffer.glyph_buffer_height = e.layout.size.y / state.source_font_height + 1; + buffer.glyphs.width = e.layout.size.x / state.source_font_width; + buffer.glyphs.height = e.layout.size.y / state.source_font_height + 1; core.draw_file_buffer(state, buffer, e.layout.pos.x, e.layout.pos.y, false); } @@ -219,6 +221,24 @@ render_raw_buffer :: proc(state: ^core.State, s: ^ui.State, buffer: ^core.FileBu } +render_glyph_buffer :: proc(state: ^core.State, s: ^ui.State, glyphs: ^core.GlyphBuffer) { + draw_func := proc(state: ^core.State, e: ui.UI_Element, user_data: rawptr) { + glyphs := transmute(^core.GlyphBuffer)user_data; + if glyphs != nil { + glyphs.width = e.layout.size.x / state.source_font_width; + glyphs.height = e.layout.size.y / state.source_font_height + 1; + + core.draw_glyph_buffer(state, glyphs, e.layout.pos.x, e.layout.pos.y, 0, true); + } + }; + + ui.open_element(s, ui.UI_Element_Kind_Custom{fn = draw_func, user_data = transmute(rawptr)glyphs}, { + kind = {ui.Grow{}, ui.Grow{}} + }) + ui.close_element(s) + +} + make_file_buffer_panel :: proc(buffer_index: int) -> core.Panel { input_map := core.new_input_map() @@ -256,15 +276,19 @@ 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, state.ctx.allocator)) + mem.arena_init(&query_arena, make([]u8, 1024*1024*2, state.ctx.allocator)) + + glyphs := core.make_glyph_buffer(256,256, allocator = mem.arena_allocator(&query_arena)) 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 + 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) @@ -275,6 +299,13 @@ make_grep_panel :: proc(state: ^core.State) -> core.Panel { panel_state.query_results = rs_grep_as_results(&rs_results) free_grep_results(rs_results) + + panel_state.selected_result = 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, + ) } core.register_key_action(&input_map.mode[.Normal], .ENTER, proc(state: ^core.State) { @@ -308,6 +339,12 @@ make_grep_panel :: proc(state: ^core.State) -> core.Panel { if panel_state, ok := ¤t_panel.panel_state.(core.GrepPanel); ok { // TODO: bounds checking panel_state.selected_result -= 1 + + 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, + ) } } }, "move selection up"); @@ -318,6 +355,12 @@ make_grep_panel :: proc(state: ^core.State) -> core.Panel { if panel_state, ok := ¤t_panel.panel_state.(core.GrepPanel); ok { // TODO: bounds checking panel_state.selected_result += 1 + + 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, + ) } } }, "move selection down"); @@ -338,6 +381,7 @@ make_grep_panel :: proc(state: ^core.State) -> core.Panel { query_arena = query_arena, buffer = len(state.buffers)-1, query_results = nil, + glyphs = glyphs, }, input_map = input_map, buffer_proc = proc(state: ^core.State, panel_state: ^core.PanelState) -> (buffer: ^core.FileBuffer, ok: bool) { @@ -357,75 +401,74 @@ make_grep_panel :: proc(state: ^core.State) -> core.Panel { } }, render_proc = proc(state: ^core.State, panel_state: ^core.PanelState) -> (ok: bool) { - panel_state := panel_state.(core.GrepPanel) or_return; + if panel_state, ok := &panel_state.(core.GrepPanel); ok { + s := transmute(^ui.State)state.ui - 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, + dir = .TopToBottom, 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) + // 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) + 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) + // 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) + 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) + // file contents + selected_result := &panel_state.query_results[panel_state.selected_result] + render_glyph_buffer(state, s, &panel_state.glyphs) + } + } + 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) - // 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]) - } + return true } - ui.close_element(s) - return true + return false } } } \ No newline at end of file diff --git a/src/pkg/grep_lib/src/lib.rs b/src/pkg/grep_lib/src/lib.rs index 02f8328..98548bf 100644 --- a/src/pkg/grep_lib/src/lib.rs +++ b/src/pkg/grep_lib/src/lib.rs @@ -49,7 +49,8 @@ impl Match { let column = value.bytes_range_in_buffer().len() as u64; Ok(Self { - text: line, + // 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,