refactor to have state and add test

This commit is contained in:
2026-04-26 21:29:38 -04:00
parent 0dc25adb8b
commit 8eaed029ea
4 changed files with 86 additions and 50 deletions

2
.gitignore vendored
View File

@@ -1,3 +1,5 @@
edit edit
*.ttf *.ttf
.build .build
main
test

109
edit.jai
View File

@@ -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; FPS :: 60;
MS_PER_FRAME :: 1000 / FPS; MS_PER_FRAME :: 1000 / FPS;
buffer: string; State :: struct {
lines: [..][..]u8; file_name: string;
line_height: int;
my_font: *Simp.Dynamic_Font; cursor_x: int;
cursor_y: int;
window_width := 800; window_width: int;
window_height := 600; window_height: int;
first_line := 0; buffer: string;
lines: [..][..]u8;
line_height: int;
first_line: int;
cursor_x := 0; my_font: *Simp.Dynamic_Font;
cursor_y := 0; dt: int;
}
file_name: string;
White :: Vector4.{1, 1, 1, 1}; White :: Vector4.{1, 1, 1, 1};
@@ -32,33 +45,32 @@ Special_Character_Lookup : [10]u8 : .[
xx #char "(", xx #char "(",
]; ];
main :: () { run_editor :: (file_name: string) {
args := get_command_line_arguments(); state: State;
assert(args.count == 2, "The file name must be passed as a CLI argument"); 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]; my_init_fonts(*state);
window_name := file_name;
window := create_window(window_width, window_height, window_name);
my_init_fonts();
Simp.set_render_target(window); Simp.set_render_target(window);
should_quit := false; should_quit := false;
prev := get_time(); 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 { while !should_quit {
reset_temporary_storage(); reset_temporary_storage();
now := get_time(); now := get_time();
dt := now - prev; state.dt = now - prev;
prev = now; prev = now;
Input.update_window_events(); Input.update_window_events();
handle_window_resizes(window); handle_window_resizes(*state, window);
for event : Input.events_this_frame { for event : Input.events_this_frame {
if event.type == .QUIT { if event.type == .QUIT {
@@ -70,13 +82,13 @@ main :: () {
if event.key_code == { if event.key_code == {
case .ESCAPE; should_quit = true; case .ESCAPE; should_quit = true;
case .BACKSPACE; handle_backspace(); case .BACKSPACE; handle_backspace(*state);
case .ENTER; handle_enter(); case .ENTER; handle_enter(*state);
case .ARROW_UP; #through; case .ARROW_UP; #through;
case .ARROW_DOWN; #through; case .ARROW_DOWN; #through;
case .ARROW_LEFT; #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" { if #char "A" <= event.key_code && event.key_code <= #char "z" {
@@ -88,22 +100,22 @@ main :: () {
if event.ctrl_pressed { if event.ctrl_pressed {
if event.key_code == { if event.key_code == {
case #char "S"; #through; case #char "S"; #through;
case #char "s"; handle_save(); case #char "s"; handle_save(*state);
} }
} else { } 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 { } else if 33 <= event.key_code && event.key_code <= 126 {
// NOTE: this doesn't quite work for some keys, like ` or [] // NOTE: this doesn't quite work for some keys, like ` or []
key := event.key_code; 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); Simp.clear_render_target(.0, .0, .0, 1);
render_edit_buffer(); render_edit_buffer(state);
Simp.swap_buffers(window); Simp.swap_buffers(window);
@@ -114,8 +126,8 @@ main :: () {
} }
} }
render_edit_buffer :: () { render_edit_buffer :: (using state: State) {
visible_lines := get_visible_lines(); visible_lines := get_visible_lines(state);
for line_buffer: visible_lines { for line_buffer: visible_lines {
Simp.set_shader_for_text(); Simp.set_shader_for_text();
@@ -154,13 +166,13 @@ render_edit_buffer :: () {
} }
} }
insert_character_at_cursor :: (char: u8) { insert_character_at_cursor :: (using state: *State, char: u8) {
line := *get_visible_lines()[cursor_y]; line := *get_visible_lines(state)[cursor_y];
array_insert_at(line, char, cursor_x); array_insert_at(line, char, cursor_x);
cursor_x += 1; cursor_x += 1;
} }
handle_save :: () { handle_save :: (using state: *State) {
out_builder: String_Builder; out_builder: String_Builder;
init_string_builder(*out_builder); init_string_builder(*out_builder);
@@ -177,7 +189,7 @@ handle_save :: () {
write_entire_file(out_file_name, content); 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() { for Input.get_window_resizes() {
Simp.update_window(it.window); Simp.update_window(it.window);
if it.window == window { if it.window == window {
@@ -187,13 +199,13 @@ handle_window_resizes :: (window: Window_Type) {
window_height = it.height; window_height = it.height;
if should_reinit { if should_reinit {
my_init_fonts(); my_init_fonts(state);
} }
} }
} }
} }
handle_enter :: () { handle_enter :: (using state: *State) {
cursor_line_index := cursor_y + first_line; cursor_line_index := cursor_y + first_line;
current_line := *lines[cursor_line_index]; current_line := *lines[cursor_line_index];
@@ -203,11 +215,11 @@ handle_enter :: () {
array_insert_at(*lines, new_line, cursor_line_index + 1); array_insert_at(*lines, new_line, cursor_line_index + 1);
current_line.count = cursor_x; current_line.count = cursor_x;
handle_arrow(.ARROW_DOWN); handle_arrow(state, .ARROW_DOWN);
cursor_x = 0; cursor_x = 0;
} }
handle_backspace :: () { handle_backspace :: (using state: *State) {
if cursor_x == 0 && cursor_y == 0 && first_line == 0 { if cursor_x == 0 && cursor_y == 0 && first_line == 0 {
return; return;
} }
@@ -219,7 +231,7 @@ handle_backspace :: () {
prev_count := prev_line.count; prev_count := prev_line.count;
array_add(prev_line, current_line.*); array_add(prev_line, current_line.*);
array_remove_at(*lines, cursor_line_index); array_remove_at(*lines, cursor_line_index);
handle_arrow(.ARROW_UP); handle_arrow(state, .ARROW_UP);
cursor_x = prev_count; cursor_x = prev_count;
} else { } else {
array_remove_at(current_line, cursor_x - 1); array_remove_at(current_line, cursor_x - 1);
@@ -227,8 +239,13 @@ handle_backspace :: () {
} }
} }
handle_arrow :: (key_code: Input.Key_Code) { handle_arrow :: (state: *State, key_code: Input.Key_Code) {
visible_lines := get_visible_lines(); 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 == { if key_code == {
case .ARROW_UP; case .ARROW_UP;
@@ -252,6 +269,7 @@ handle_arrow :: (key_code: Input.Key_Code) {
len := prev_line.count; len := prev_line.count;
cursor_y = max(0, cursor_y - 1); cursor_y = max(0, cursor_y - 1);
cursor_x = len; cursor_x = len;
} else if cursor_x != 0 && ctrl_pressed {
} else if cursor_x != 0 { } else if cursor_x != 0 {
cursor_x -= 1; cursor_x -= 1;
} }
@@ -261,13 +279,14 @@ handle_arrow :: (key_code: Input.Key_Code) {
if cursor_x == len { if cursor_x == len {
cursor_x = 0; cursor_x = 0;
cursor_y += 1; cursor_y += 1;
} else if ctrl_pressed {
} else { } else {
cursor_x += 1; cursor_x += 1;
} }
} }
} }
get_visible_lines :: () -> [][..]u8 { get_visible_lines :: (using state: State) -> [][..]u8 {
num_lines_in_screen := window_height / line_height - 1; num_lines_in_screen := window_height / line_height - 1;
return array_view(lines, first_line, num_lines_in_screen); return array_view(lines, first_line, num_lines_in_screen);
} }
@@ -328,7 +347,7 @@ get_time :: () -> s64 {
return val; 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); file_contents, ok := read_entire_file(file_path);
if !ok return ok; if !ok return ok;
@@ -348,7 +367,7 @@ read_file_lines :: (file_path: string) -> bool {
return ok; return ok;
} }
my_init_fonts :: () { my_init_fonts :: (using state: *State) {
line_height = 16; line_height = 16;
base_path := path_strip_filename(get_path_of_running_executable()); base_path := path_strip_filename(get_path_of_running_executable());

10
main.jai Normal file
View File

@@ -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";

5
test.jai Normal file
View File

@@ -0,0 +1,5 @@
main :: () {
}
#load "edit.jai";
#import "Basic";