From 8eaed029eafc9dfe2311098c6aa6b47e7de96f31 Mon Sep 17 00:00:00 2001 From: Grant Horner Date: Sun, 26 Apr 2026 21:29:38 -0400 Subject: [PATCH] refactor to have state and add test --- .gitignore | 2 + edit.jai | 119 +++++++++++++++++++++++++++++++---------------------- main.jai | 10 +++++ test.jai | 5 +++ 4 files changed, 86 insertions(+), 50 deletions(-) create mode 100644 main.jai create mode 100644 test.jai diff --git a/.gitignore b/.gitignore index 80cab65..53f7aec 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ edit *.ttf .build +main +test \ No newline at end of file diff --git a/edit.jai b/edit.jai index 8b8721c..9c8eda4 100644 --- a/edit.jai +++ b/edit.jai @@ -1,21 +1,34 @@ +// Todos: +// - move by word +// - select text +// - copy selected text +// - cut selected text +// - move line up +// - move line down +// - duplicate line +// - refactor to make cursor_x be aligned with characters themselves + + FPS :: 60; MS_PER_FRAME :: 1000 / FPS; -buffer: string; -lines: [..][..]u8; -line_height: int; +State :: struct { + file_name: string; + + cursor_x: int; + cursor_y: int; + + window_width: int; + window_height: int; + + buffer: string; + lines: [..][..]u8; + line_height: int; + first_line: int; -my_font: *Simp.Dynamic_Font; - -window_width := 800; -window_height := 600; - -first_line := 0; - -cursor_x := 0; -cursor_y := 0; - -file_name: string; + my_font: *Simp.Dynamic_Font; + dt: int; +} White :: Vector4.{1, 1, 1, 1}; @@ -32,33 +45,32 @@ Special_Character_Lookup : [10]u8 : .[ xx #char "(", ]; -main :: () { - args := get_command_line_arguments(); - assert(args.count == 2, "The file name must be passed as a CLI argument"); +run_editor :: (file_name: string) { + state: State; + state.window_width = 800; + state.window_height = 600; + state.file_name = file_name; + window := create_window(state.window_width, state.window_height, state.file_name); - file_name = args[1]; - window_name := file_name; - window := create_window(window_width, window_height, window_name); - - my_init_fonts(); + my_init_fonts(*state); Simp.set_render_target(window); should_quit := false; prev := get_time(); - assert(read_file_lines(file_name), "Must be able to read file!"); + assert(read_file_lines(*state, state.file_name), "Must be able to read file!"); while !should_quit { reset_temporary_storage(); now := get_time(); - dt := now - prev; + state.dt = now - prev; prev = now; Input.update_window_events(); - handle_window_resizes(window); + handle_window_resizes(*state, window); for event : Input.events_this_frame { if event.type == .QUIT { @@ -70,13 +82,13 @@ main :: () { if event.key_code == { case .ESCAPE; should_quit = true; - case .BACKSPACE; handle_backspace(); - case .ENTER; handle_enter(); + case .BACKSPACE; handle_backspace(*state); + case .ENTER; handle_enter(*state); case .ARROW_UP; #through; case .ARROW_DOWN; #through; case .ARROW_LEFT; #through; - case .ARROW_RIGHT; handle_arrow(event.key_code); + case .ARROW_RIGHT; handle_arrow(*state, event); } if #char "A" <= event.key_code && event.key_code <= #char "z" { @@ -88,22 +100,22 @@ main :: () { if event.ctrl_pressed { if event.key_code == { case #char "S"; #through; - case #char "s"; handle_save(); + case #char "s"; handle_save(*state); } } else { - insert_character_at_cursor(cast(u8) key); + insert_character_at_cursor(*state, cast(u8) key); } } else if 33 <= event.key_code && event.key_code <= 126 { // NOTE: this doesn't quite work for some keys, like ` or [] key := event.key_code; - insert_character_at_cursor(cast(u8) key); + insert_character_at_cursor(*state, cast(u8) key); } } } Simp.clear_render_target(.0, .0, .0, 1); - render_edit_buffer(); + render_edit_buffer(state); Simp.swap_buffers(window); @@ -114,8 +126,8 @@ main :: () { } } -render_edit_buffer :: () { - visible_lines := get_visible_lines(); +render_edit_buffer :: (using state: State) { + visible_lines := get_visible_lines(state); for line_buffer: visible_lines { Simp.set_shader_for_text(); @@ -154,13 +166,13 @@ render_edit_buffer :: () { } } -insert_character_at_cursor :: (char: u8) { - line := *get_visible_lines()[cursor_y]; +insert_character_at_cursor :: (using state: *State, char: u8) { + line := *get_visible_lines(state)[cursor_y]; array_insert_at(line, char, cursor_x); cursor_x += 1; } -handle_save :: () { +handle_save :: (using state: *State) { out_builder: String_Builder; init_string_builder(*out_builder); @@ -177,7 +189,7 @@ handle_save :: () { write_entire_file(out_file_name, content); } -handle_window_resizes :: (window: Window_Type) { +handle_window_resizes :: (using state: *State, window: Window_Type) { for Input.get_window_resizes() { Simp.update_window(it.window); if it.window == window { @@ -187,13 +199,13 @@ handle_window_resizes :: (window: Window_Type) { window_height = it.height; if should_reinit { - my_init_fonts(); + my_init_fonts(state); } } } } -handle_enter :: () { +handle_enter :: (using state: *State) { cursor_line_index := cursor_y + first_line; current_line := *lines[cursor_line_index]; @@ -203,11 +215,11 @@ handle_enter :: () { array_insert_at(*lines, new_line, cursor_line_index + 1); current_line.count = cursor_x; - handle_arrow(.ARROW_DOWN); + handle_arrow(state, .ARROW_DOWN); cursor_x = 0; } -handle_backspace :: () { +handle_backspace :: (using state: *State) { if cursor_x == 0 && cursor_y == 0 && first_line == 0 { return; } @@ -219,7 +231,7 @@ handle_backspace :: () { prev_count := prev_line.count; array_add(prev_line, current_line.*); array_remove_at(*lines, cursor_line_index); - handle_arrow(.ARROW_UP); + handle_arrow(state, .ARROW_UP); cursor_x = prev_count; } else { array_remove_at(current_line, cursor_x - 1); @@ -227,8 +239,13 @@ handle_backspace :: () { } } -handle_arrow :: (key_code: Input.Key_Code) { - visible_lines := get_visible_lines(); +handle_arrow :: (state: *State, key_code: Input.Key_Code) { + handle_arrow(state, .{key_code=key_code}); +} + +handle_arrow :: (using state: *State, using event: Input.Event) { + visible_lines := get_visible_lines(state); + if key_code == { case .ARROW_UP; @@ -252,22 +269,24 @@ handle_arrow :: (key_code: Input.Key_Code) { len := prev_line.count; cursor_y = max(0, cursor_y - 1); cursor_x = len; + } else if cursor_x != 0 && ctrl_pressed { } else if cursor_x != 0 { cursor_x -= 1; - } + } case .ARROW_RIGHT; line := visible_lines[cursor_y]; len := line.count; if cursor_x == len { cursor_x = 0; cursor_y += 1; + } else if ctrl_pressed { } else { cursor_x += 1; - } + } } } -get_visible_lines :: () -> [][..]u8 { +get_visible_lines :: (using state: State) -> [][..]u8 { num_lines_in_screen := window_height / line_height - 1; return array_view(lines, first_line, num_lines_in_screen); } @@ -328,7 +347,7 @@ get_time :: () -> s64 { return val; } -read_file_lines :: (file_path: string) -> bool { +read_file_lines :: (using state: *State, file_path: string) -> bool { file_contents, ok := read_entire_file(file_path); if !ok return ok; @@ -348,7 +367,7 @@ read_file_lines :: (file_path: string) -> bool { return ok; } -my_init_fonts :: () { +my_init_fonts :: (using state: *State) { line_height = 16; base_path := path_strip_filename(get_path_of_running_executable()); diff --git a/main.jai b/main.jai new file mode 100644 index 0000000..4fa2691 --- /dev/null +++ b/main.jai @@ -0,0 +1,10 @@ +main :: () { + args := get_command_line_arguments(); + assert(args.count == 2, "The file name must be passed as a CLI argument"); + + file_name := args[1]; + run_editor(file_name); +} + +#load "edit.jai"; +#import "Basic"; diff --git a/test.jai b/test.jai new file mode 100644 index 0000000..e6a1165 --- /dev/null +++ b/test.jai @@ -0,0 +1,5 @@ +main :: () { +} + +#load "edit.jai"; +#import "Basic";