refactor to have state and add test
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,3 +1,5 @@
|
|||||||
edit
|
edit
|
||||||
*.ttf
|
*.ttf
|
||||||
.build
|
.build
|
||||||
|
main
|
||||||
|
test
|
||||||
113
edit.jai
113
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;
|
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,22 +269,24 @@ 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;
|
||||||
}
|
}
|
||||||
case .ARROW_RIGHT;
|
case .ARROW_RIGHT;
|
||||||
line := visible_lines[cursor_y];
|
line := visible_lines[cursor_y];
|
||||||
len := line.count;
|
len := line.count;
|
||||||
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
10
main.jai
Normal 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";
|
||||||
Reference in New Issue
Block a user