diff --git a/src/core/core.odin b/src/core/core.odin index 9662d49..67bd60d 100644 --- a/src/core/core.odin +++ b/src/core/core.odin @@ -136,6 +136,8 @@ current_buffer :: proc(state: ^State) -> ^FileBuffer { } yank_whole_line :: proc(state: ^State, buffer: ^FileBuffer) { + context.allocator = buffer.allocator + if state.yank_register.data != nil { delete(state.yank_register.data) state.yank_register.data = nil diff --git a/src/core/file_buffer.odin b/src/core/file_buffer.odin index 0cf522b..1e90419 100644 --- a/src/core/file_buffer.odin +++ b/src/core/file_buffer.odin @@ -825,8 +825,8 @@ color_character :: proc(buffer: ^FileBuffer, start: Cursor, end: Cursor, palette } draw_file_buffer :: proc(state: ^State, buffer: ^FileBuffer, x, y, w, h: int, show_line_numbers: bool = true, show_cursor: bool = true) { - glyph_width := math.min(256, int((w - state.source_font_width) / state.source_font_width)); - glyph_height := math.min(256, int((h - state.source_font_height*2) / state.source_font_height)) + 1; + glyph_width := math.min(256, int(w / state.source_font_width)); + glyph_height := math.min(256, int(h / state.source_font_height)) + 1; update_glyph_buffer(buffer, glyph_width, glyph_height); @@ -845,7 +845,7 @@ draw_file_buffer :: proc(state: ^State, buffer: ^FileBuffer, x, y, w, h: int, sh if show_cursor { if state.mode == .Normal { draw_rect(state, cursor_x, cursor_y, state.source_font_width, state.source_font_height, .Background4); - } else if state.mode == .Visual { + } else if state.mode == .Visual && buffer.selection != nil { start_sel_x := x + padding + buffer.selection.?.start.col * state.source_font_width; start_sel_y := y + buffer.selection.?.start.line * state.source_font_height; @@ -937,7 +937,9 @@ update_file_buffer_scroll :: proc(buffer: ^FileBuffer, cursor: Maybe(^Cursor) = cursor = &buffer.history.cursor; } - if cursor.?.line > (buffer.top_line + buffer.glyphs.height - 5) { + if buffer.glyphs.height < 5 { + buffer.top_line = cursor.?.line + } else 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); diff --git a/src/core/history.odin b/src/core/history.odin index 760f904..2e06d89 100644 --- a/src/core/history.odin +++ b/src/core/history.odin @@ -1,8 +1,11 @@ package core import "core:log" +import "core:mem" FileHistory :: struct { + allocator: mem.Allocator, + piece_table: PieceTable, cursor: Cursor, @@ -20,6 +23,7 @@ make_history_with_data :: proc(initial_data: []u8, starting_capacity: int = 1024 context.allocator = allocator return FileHistory { + allocator = allocator, piece_table = make_piece_table(initial_data, starting_capacity = starting_capacity), snapshots = make([]Snapshot, starting_capacity), next = 0, @@ -31,6 +35,7 @@ make_history_empty :: proc(starting_capacity: int = 1024, allocator := context.a context.allocator = allocator return FileHistory { + allocator = allocator, piece_table = make_piece_table(starting_capacity = starting_capacity), snapshots = make([]Snapshot, starting_capacity), next = 0, @@ -54,6 +59,8 @@ free_history :: proc(history: ^FileHistory) { } push_new_snapshot :: proc(history: ^FileHistory) { + context.allocator = history.allocator + if history.snapshots[history.next].chunks != nil { delete(history.snapshots[history.next].chunks) } @@ -65,6 +72,8 @@ push_new_snapshot :: proc(history: ^FileHistory) { } pop_snapshot :: proc(history: ^FileHistory, make_new_snapshot: bool = false) { + context.allocator = history.allocator + new_next, _ := next_indexes(history, backward = true) if new_next == history.next do return @@ -81,6 +90,8 @@ pop_snapshot :: proc(history: ^FileHistory, make_new_snapshot: bool = false) { } recover_snapshot :: proc(history: ^FileHistory) { + context.allocator = history.allocator + new_next, _ := next_indexes(history) if history.snapshots[new_next].chunks == nil do return history.next = new_next diff --git a/src/panels/file_buffer.odin b/src/panels/file_buffer.odin index f863ab9..4f30f33 100644 --- a/src/panels/file_buffer.odin +++ b/src/panels/file_buffer.odin @@ -84,9 +84,6 @@ 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.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, e.layout.size.x, e.layout.size.y); } }; diff --git a/src/panels/grep.odin b/src/panels/grep.odin index 31ba9e8..2c140d9 100644 --- a/src/panels/grep.odin +++ b/src/panels/grep.odin @@ -58,7 +58,12 @@ make_grep_panel :: proc() -> core.Panel { panel_state := &panel.type.(core.GrepPanel) - mem.arena_init(&panel_state.query_arena, make([]u8, 1024*1024*2)) + arena_bytes, err := make([]u8, 1024*1024*2) + if err != nil { + log.errorf("failed to allocate arena for grep panel: '%v'", err) + return + } + mem.arena_init(&panel_state.query_arena, arena_bytes) panel.input_map = core.new_input_map() panel_state.glyphs = core.make_glyph_buffer(256,256) @@ -215,7 +220,7 @@ make_grep_panel :: proc() -> core.Panel { { defer ui.close_element(s) - // render_raw_buffer(state, s, &state.buffers[panel_state.buffer]) + render_raw_buffer(state, s, &panel_state.buffer) } } ui.close_element(s) @@ -275,9 +280,6 @@ 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.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, e.layout.size.x, e.layout.size.y, false); } }; diff --git a/src/panels/panels.odin b/src/panels/panels.odin index 33ac9b1..df8e378 100644 --- a/src/panels/panels.odin +++ b/src/panels/panels.odin @@ -66,7 +66,14 @@ open :: proc(state: ^core.State, panel: core.Panel, make_active: bool = true) -> panel.id = panel_id state.current_panel = panel_id - mem.arena_init(&panel.arena, make([]u8, 1024*1024*4)) + arena_bytes, err := make([]u8, 1024*1024*8) + if err != nil { + log.errorf("failed to allocate memory for panel: '%v'", err) + util.delete(&state.panels, panel_id) + return + } + + mem.arena_init(&panel.arena, arena_bytes) panel.allocator = mem.arena_allocator(&panel.arena) panel->create(state) diff --git a/src/tests/tests.odin b/src/tests/tests.odin index db036a8..ec15be7 100644 --- a/src/tests/tests.odin +++ b/src/tests/tests.odin @@ -26,6 +26,10 @@ new_test_editor :: proc() -> core.State { return state } +delete_editor :: proc(e: ^core.State) { + util.delete(&e.panels) +} + buffer_to_string :: proc(buffer: ^core.FileBuffer) -> string { if buffer == nil { log.error("nil buffer") @@ -84,9 +88,7 @@ input_text :: proc(text: string) -> ArtificialTextInput { } setup_empty_buffer :: proc(state: ^core.State) { - buffer := core.new_virtual_file_buffer(context.allocator); - panels.open(state, panels.make_file_buffer_panel(len(state.buffers))) - runtime.append(&state.buffers, buffer); + panels.open(state, panels.make_file_buffer_panel("")) core.reset_input_map(state) } @@ -135,8 +137,12 @@ expect_cursor_index :: proc(t: ^testing.T, cursor: core.Cursor, chunk_index, cha insert_from_empty_no_newlines :: proc(t: ^testing.T) { e := new_test_editor() setup_empty_buffer(&e) + defer { + panels.close(&e, 0) + delete_editor(&e) + } - buffer := &e.buffers[0] + buffer := core.current_buffer(&e) inputted_text := "Hello, world!" expected_text := fmt.aprintf("%v\n", inputted_text) @@ -146,6 +152,8 @@ insert_from_empty_no_newlines :: proc(t: ^testing.T) { expect_cursor_index(t, buffer.history.cursor, 0, 12) contents := buffer_to_string(core.current_buffer(&e)) + defer delete(contents) + testing.expectf(t, contents == expected_text, "got '%v', expected '%v'", contents, expected_text) } @@ -153,8 +161,12 @@ insert_from_empty_no_newlines :: proc(t: ^testing.T) { insert_from_empty_with_newline :: proc(t: ^testing.T) { e := new_test_editor() setup_empty_buffer(&e) + defer { + panels.close(&e, 0) + delete_editor(&e) + } - buffer := &e.buffers[0] + buffer := core.current_buffer(&e) inputted_text := "Hello, world!\nThis is a new line" expected_text := fmt.aprintf("%v\n", inputted_text) @@ -164,6 +176,8 @@ insert_from_empty_with_newline :: proc(t: ^testing.T) { expect_cursor_index(t, buffer.history.cursor, 0, 31) contents := buffer_to_string(core.current_buffer(&e)) + defer delete(contents) + testing.expectf(t, contents == expected_text, "got '%v', expected '%v'", contents, expected_text) } @@ -171,8 +185,12 @@ insert_from_empty_with_newline :: proc(t: ^testing.T) { insert_in_between_text :: proc(t: ^testing.T) { e := new_test_editor() setup_empty_buffer(&e) + defer { + panels.close(&e, 0) + delete_editor(&e) + } - buffer := &e.buffers[0] + buffer := core.current_buffer(&e) inputted_text := "Hello, world!" expected_text := "Hello, beautiful world!\n" @@ -188,6 +206,8 @@ insert_in_between_text :: proc(t: ^testing.T) { expect_cursor_index(t, buffer.history.cursor, 1, 9) contents := buffer_to_string(core.current_buffer(&e)) + defer delete(contents) + testing.expectf(t, contents == expected_text, "got '%v', expected '%v'", contents, expected_text) } @@ -195,8 +215,12 @@ insert_in_between_text :: proc(t: ^testing.T) { insert_before_slice_at_beginning_of_file :: proc(t: ^testing.T) { e := new_test_editor() setup_empty_buffer(&e) + defer { + panels.close(&e, 0) + delete_editor(&e) + } - buffer := &e.buffers[0] + buffer := core.current_buffer(&e) inputted_text := "Hello, world!" expected_text := "Well, Hello, beautiful world!\n" @@ -215,6 +239,8 @@ insert_before_slice_at_beginning_of_file :: proc(t: ^testing.T) { expect_cursor_index(t, buffer.history.cursor, 0, 5) contents := buffer_to_string(core.current_buffer(&e)) + defer delete(contents) + testing.expectf(t, contents == expected_text, "got '%v', expected '%v'", contents, expected_text) } @@ -222,8 +248,12 @@ insert_before_slice_at_beginning_of_file :: proc(t: ^testing.T) { insert_before_slice :: proc(t: ^testing.T) { e := new_test_editor() setup_empty_buffer(&e) + defer { + panels.close(&e, 0) + delete_editor(&e) + } - buffer := &e.buffers[0] + buffer := core.current_buffer(&e) inputted_text := "Hello, world!" expected_text := "Hello, beautiful rich world!\n" @@ -243,6 +273,8 @@ insert_before_slice :: proc(t: ^testing.T) { expect_cursor_index(t, buffer.history.cursor, 2, 4) contents := buffer_to_string(core.current_buffer(&e)) + defer delete(contents) + testing.expectf(t, contents == expected_text, "got '%v', expected '%v'", contents, expected_text) } @@ -250,8 +282,12 @@ insert_before_slice :: proc(t: ^testing.T) { delete_last_content_slice_beginning_of_file :: proc(t: ^testing.T) { e := new_test_editor() setup_empty_buffer(&e) + defer { + panels.close(&e, 0) + delete_editor(&e) + } - buffer := &e.buffers[0] + buffer := core.current_buffer(&e) run_text_insertion(&e, "Hello, world!") @@ -283,8 +319,12 @@ delete_last_content_slice_beginning_of_file :: proc(t: ^testing.T) { delete_in_slice :: proc(t: ^testing.T) { e := new_test_editor() setup_empty_buffer(&e) + defer { + panels.close(&e, 0) + delete_editor(&e) + } - buffer := &e.buffers[0] + buffer := core.current_buffer(&e) inputted_text := "Hello, world!" expected_text := "Hello, beautiful h world!\n" @@ -310,6 +350,8 @@ delete_in_slice :: proc(t: ^testing.T) { expect_cursor_index(t, buffer.history.cursor, 3, 0) contents := buffer_to_string(core.current_buffer(&e)) + defer delete(contents) + testing.expectf(t, contents == expected_text, "got '%v', expected '%v'", contents, expected_text) } @@ -317,8 +359,12 @@ delete_in_slice :: proc(t: ^testing.T) { delete_across_slices :: proc(t: ^testing.T) { e := new_test_editor() setup_empty_buffer(&e) + defer { + panels.close(&e, 0) + delete_editor(&e) + } - buffer := &e.buffers[0] + buffer := core.current_buffer(&e) inputted_text := "Hello, world!" expected_text := "Hello, beautiful world!\n" @@ -352,6 +398,8 @@ delete_across_slices :: proc(t: ^testing.T) { expect_cursor_index(t, buffer.history.cursor, 2, 0) contents := buffer_to_string(core.current_buffer(&e)) + defer delete(contents) + testing.expectf(t, contents == expected_text, "got '%v', expected '%v'", contents, expected_text) } @@ -359,10 +407,14 @@ delete_across_slices :: proc(t: ^testing.T) { move_down_next_line_has_shorter_length :: proc(t: ^testing.T) { e := new_test_editor() setup_empty_buffer(&e) + defer { + panels.close(&e, 0) + delete_editor(&e) + } is_ctrl_pressed := false - buffer := &e.buffers[0] + buffer := core.current_buffer(&e) run_text_insertion(&e, "012345678\n0") @@ -383,10 +435,14 @@ move_down_next_line_has_shorter_length :: proc(t: ^testing.T) { move_down_on_last_line :: proc(t: ^testing.T) { e := new_test_editor() setup_empty_buffer(&e) + defer { + panels.close(&e, 0) + delete_editor(&e) + } is_ctrl_pressed := false - buffer := &e.buffers[0] + buffer := core.current_buffer(&e) run_text_insertion(&e, "012345678") @@ -402,8 +458,12 @@ move_down_on_last_line :: proc(t: ^testing.T) { move_left_at_beginning_of_file :: proc(t: ^testing.T) { e := new_test_editor() setup_empty_buffer(&e) + defer { + panels.close(&e, 0) + delete_editor(&e) + } - buffer := &e.buffers[0] + buffer := core.current_buffer(&e) run_text_insertion(&e, "01234") // Move cursor from --------^ @@ -425,10 +485,14 @@ move_left_at_beginning_of_file :: proc(t: ^testing.T) { move_right_at_end_of_file :: proc(t: ^testing.T) { e := new_test_editor() setup_empty_buffer(&e) + defer { + panels.close(&e, 0) + delete_editor(&e) + } is_ctrl_pressed := false - buffer := &e.buffers[0] + buffer := core.current_buffer(&e) run_text_insertion(&e, "01234") @@ -447,10 +511,14 @@ move_right_at_end_of_file :: proc(t: ^testing.T) { move_to_end_of_line_from_end :: proc(t: ^testing.T) { e := new_test_editor() setup_empty_buffer(&e) + defer { + panels.close(&e, 0) + delete_editor(&e) + } is_ctrl_pressed := false - buffer := &e.buffers[0] + buffer := core.current_buffer(&e) run_text_insertion(&e, "01234\n01234") @@ -468,10 +536,14 @@ move_to_end_of_line_from_end :: proc(t: ^testing.T) { move_to_end_of_line_from_middle :: proc(t: ^testing.T) { e := new_test_editor() setup_empty_buffer(&e) + defer { + panels.close(&e, 0) + delete_editor(&e) + } is_ctrl_pressed := false - buffer := &e.buffers[0] + buffer := core.current_buffer(&e) run_text_insertion(&e, "01234\n01234") @@ -492,10 +564,14 @@ move_to_end_of_line_from_middle :: proc(t: ^testing.T) { move_to_beginning_of_line_from_middle :: proc(t: ^testing.T) { e := new_test_editor() setup_empty_buffer(&e) + defer { + panels.close(&e, 0) + delete_editor(&e) + } is_ctrl_pressed := false - buffer := &e.buffers[0] + buffer := core.current_buffer(&e) run_text_insertion(&e, "01234\n01234") @@ -516,10 +592,14 @@ move_to_beginning_of_line_from_middle :: proc(t: ^testing.T) { move_to_beginning_of_line_from_start :: proc(t: ^testing.T) { e := new_test_editor() setup_empty_buffer(&e) + defer { + panels.close(&e, 0) + delete_editor(&e) + } is_ctrl_pressed := false - buffer := &e.buffers[0] + buffer := core.current_buffer(&e) run_text_insertion(&e, "01234\n01234") @@ -540,8 +620,12 @@ move_to_beginning_of_line_from_start :: proc(t: ^testing.T) { append_end_of_line :: proc(t: ^testing.T) { e := new_test_editor() setup_empty_buffer(&e) + defer { + panels.close(&e, 0) + delete_editor(&e) + } - buffer := &e.buffers[0] + buffer := core.current_buffer(&e) run_text_insertion(&e, "hello") @@ -562,8 +646,12 @@ append_end_of_line :: proc(t: ^testing.T) { insert_line_under_current :: proc(t: ^testing.T) { e := new_test_editor() setup_empty_buffer(&e) + defer { + panels.close(&e, 0) + delete_editor(&e) + } - buffer := &e.buffers[0] + buffer := core.current_buffer(&e) initial_text := "Hello, world!\nThis is a new line" run_text_insertion(&e, initial_text) @@ -590,6 +678,8 @@ insert_line_under_current :: proc(t: ^testing.T) { expect_cursor_index(t, buffer.history.cursor, 1, 23) contents := buffer_to_string(core.current_buffer(&e)) + defer delete(contents) + testing.expectf(t, contents == expected_text, "got '%v', expected '%v'", contents, expected_text) } @@ -597,8 +687,12 @@ insert_line_under_current :: proc(t: ^testing.T) { yank_and_paste_whole_line :: proc(t: ^testing.T) { e := new_test_editor() setup_empty_buffer(&e) + defer { + panels.close(&e, 0) + delete_editor(&e) + } - buffer := &e.buffers[0] + buffer := core.current_buffer(&e) initial_text := "Hello, world!\nThis is a new line" run_text_insertion(&e, initial_text) @@ -617,22 +711,22 @@ yank_and_paste_whole_line :: proc(t: ^testing.T) { expect_line_col(t, buffer.history.cursor, 1, 0) contents := buffer_to_string(core.current_buffer(&e)) + defer delete(contents) + testing.expectf(t, contents == expected_text, "got '%v', expected '%v'", contents, expected_text) } run_editor_frame :: proc(state: ^core.State, input: ArtificialInput, is_ctrl_pressed: ^bool) { - log.infof("running input: %v", input) - { run_key_action := proc(state: ^core.State, control_key_pressed: bool, key: core.Key) -> bool { - log.info("key_action") + if current_panel, ok := state.current_panel.?; ok { + panel := util.get(&state.panels, current_panel).? - if state.current_input_map != nil { if control_key_pressed { if action, exists := state.current_input_map.ctrl_key_actions[key]; exists { switch value in action.action { case core.EditorAction: - value(state); + value(state, panel); return true; case core.InputActions: state.current_input_map = &(&state.current_input_map.ctrl_key_actions[key]).action.(core.InputActions) @@ -643,7 +737,7 @@ run_editor_frame :: proc(state: ^core.State, input: ArtificialInput, is_ctrl_pre if action, exists := state.current_input_map.key_actions[key]; exists { switch value in action.action { case core.EditorAction: - value(state); + value(state, panel); return true; case core.InputActions: state.current_input_map = &(&state.current_input_map.key_actions[key]).action.(core.InputActions) @@ -651,8 +745,6 @@ run_editor_frame :: proc(state: ^core.State, input: ArtificialInput, is_ctrl_pre } } } - } else { - log.info("current_input_map is null") } return false @@ -661,8 +753,6 @@ run_editor_frame :: proc(state: ^core.State, input: ArtificialInput, is_ctrl_pre switch state.mode { case .Visual: fallthrough case .Normal: { - log.info("it's normal/visual mode") - if key, ok := input.(ArtificialKey); ok { if key.is_down { if key.key == .LCTRL { @@ -678,8 +768,6 @@ run_editor_frame :: proc(state: ^core.State, input: ArtificialInput, is_ctrl_pre } } case .Insert: { - log.info("it's insert mode") - buffer := core.current_buffer(state); if key, ok := input.(ArtificialKey); ok { @@ -710,24 +798,20 @@ run_editor_frame :: proc(state: ^core.State, input: ArtificialInput, is_ctrl_pre } } - log.info("before text input") if text_input, ok := input.(ArtificialTextInput); ok { - log.infof("attempting to append '%v' to buffer", text_input) - for char in text_input.text { if char < 1 { break; } if char == '\n' || (char >= 32 && char <= 125 && len(buffer.input_buffer) < 1024-1) { - log.infof("appening '%v' to buffer", char) 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) + if panel, ok := util.get(&state.panels, current_panel).?; ok && panel.on_buffer_input != nil { + panel->on_buffer_input(state) } } } diff --git a/src/tree_sitter/ts.odin b/src/tree_sitter/ts.odin index 08c8e14..9d26299 100644 --- a/src/tree_sitter/ts.odin +++ b/src/tree_sitter/ts.odin @@ -230,6 +230,10 @@ delete_state :: proc(state: ^State) { } parse_buffer :: proc(state: ^State, input: Input) { + if state.parser == nil { + return + } + old_tree := state.tree if old_tree != nil { defer tree_delete(old_tree) diff --git a/src/util/list.odin b/src/util/list.odin index 81e88f1..20b656c 100644 --- a/src/util/list.odin +++ b/src/util/list.odin @@ -11,7 +11,7 @@ StaticListSlot :: struct($T: typeid) { data: T, } -append_static_list :: proc(list: ^StaticList($T), value: T) -> (id: int, panel: ^T, ok: bool) { +append_static_list :: proc(list: ^StaticList($T), value: T) -> (id: int, item: ^T, ok: bool) { for i in 0..