test harness
parent
8c352355de
commit
6b4d9f0cda
3
Makefile
3
Makefile
|
@ -8,3 +8,6 @@ editor: grep src/**/*.odin
|
||||||
|
|
||||||
grep:
|
grep:
|
||||||
cargo build --manifest-path "src/pkg/grep_lib/Cargo.toml"
|
cargo build --manifest-path "src/pkg/grep_lib/Cargo.toml"
|
||||||
|
|
||||||
|
test: src/**/*.odin
|
||||||
|
odin test src/tests/ -all-packages -debug -out:bin/test_runner
|
|
@ -1073,7 +1073,7 @@ insert_content :: proc(buffer: ^FileBuffer, to_be_inserted: []u8, append_to_end:
|
||||||
|
|
||||||
if !append_to_end {
|
if !append_to_end {
|
||||||
update_file_buffer_index_from_cursor(buffer);
|
update_file_buffer_index_from_cursor(buffer);
|
||||||
move_cursor_right(buffer, false, amt = len(to_be_inserted));
|
move_cursor_right(buffer, false, amt = len(to_be_inserted) - 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -320,7 +320,7 @@ main :: proc() {
|
||||||
} else {
|
} else {
|
||||||
buffer := core.new_virtual_file_buffer(context.allocator);
|
buffer := core.new_virtual_file_buffer(context.allocator);
|
||||||
|
|
||||||
util.append_static_list(&state.panels, panels.make_file_buffer_panel(len(state.buffers)))
|
panels.open(&state, panels.make_file_buffer_panel(len(state.buffers)))
|
||||||
runtime.append(&state.buffers, buffer);
|
runtime.append(&state.buffers, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,622 @@
|
||||||
|
package tests
|
||||||
|
|
||||||
|
import "base:runtime"
|
||||||
|
import "core:testing"
|
||||||
|
import "core:fmt"
|
||||||
|
import "core:mem"
|
||||||
|
import "core:log"
|
||||||
|
|
||||||
|
import "../core"
|
||||||
|
import "../panels"
|
||||||
|
import "../util"
|
||||||
|
|
||||||
|
new_test_editor :: proc() -> core.State {
|
||||||
|
state := core.State {
|
||||||
|
ctx = context,
|
||||||
|
screen_width = 640,
|
||||||
|
screen_height = 480,
|
||||||
|
source_font_width = 8,
|
||||||
|
source_font_height = 16,
|
||||||
|
|
||||||
|
panels = util.make_static_list(core.Panel, 128),
|
||||||
|
|
||||||
|
directory = "test_directory",
|
||||||
|
};
|
||||||
|
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer_to_string :: proc(buffer: ^core.FileBuffer) -> string {
|
||||||
|
if buffer == nil {
|
||||||
|
log.error("nil buffer")
|
||||||
|
}
|
||||||
|
|
||||||
|
length := 0
|
||||||
|
for content_slice in buffer.content_slices {
|
||||||
|
length += len(content_slice)
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer_contents := make([]u8, length)
|
||||||
|
|
||||||
|
offset := 0
|
||||||
|
for content_slice in buffer.content_slices {
|
||||||
|
for c in content_slice {
|
||||||
|
buffer_contents[offset] = c
|
||||||
|
offset += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(buffer_contents)
|
||||||
|
}
|
||||||
|
|
||||||
|
ArtificialInput :: union {
|
||||||
|
ArtificialKey,
|
||||||
|
ArtificialTextInput,
|
||||||
|
}
|
||||||
|
|
||||||
|
ArtificialKey :: struct {
|
||||||
|
is_down: bool,
|
||||||
|
key: core.Key,
|
||||||
|
}
|
||||||
|
|
||||||
|
ArtificialTextInput :: struct {
|
||||||
|
text: string,
|
||||||
|
}
|
||||||
|
|
||||||
|
press_key :: proc(key: core.Key) -> ArtificialKey {
|
||||||
|
return ArtificialKey {
|
||||||
|
is_down = true,
|
||||||
|
key = key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
release_key :: proc(key: core.Key) -> ArtificialKey {
|
||||||
|
return ArtificialKey {
|
||||||
|
is_down = false,
|
||||||
|
key = key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
input_text :: proc(text: string) -> ArtificialTextInput {
|
||||||
|
return ArtificialTextInput {
|
||||||
|
text = text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
core.reset_input_map(state)
|
||||||
|
}
|
||||||
|
|
||||||
|
run_inputs :: proc(state: ^core.State, inputs: []ArtificialInput) {
|
||||||
|
is_ctrl_pressed := false
|
||||||
|
|
||||||
|
for input in inputs {
|
||||||
|
run_editor_frame(state, input, &is_ctrl_pressed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
run_input_multiple :: proc(state: ^core.State, input: ArtificialInput, amount: int) {
|
||||||
|
is_ctrl_pressed := false
|
||||||
|
|
||||||
|
for _ in 0..<amount {
|
||||||
|
run_editor_frame(state, input, &is_ctrl_pressed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
run_text_insertion :: proc(state: ^core.State, text: string) {
|
||||||
|
is_ctrl_pressed := false
|
||||||
|
|
||||||
|
inputs := []ArtificialInput {
|
||||||
|
press_key(.I),
|
||||||
|
input_text(text),
|
||||||
|
press_key(.ESCAPE),
|
||||||
|
}
|
||||||
|
|
||||||
|
for input in inputs {
|
||||||
|
run_editor_frame(state, input, &is_ctrl_pressed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
expect_line_col :: proc(t: ^testing.T, cursor: core.Cursor, line, col: int) {
|
||||||
|
testing.expect_value(t, cursor.line, line)
|
||||||
|
testing.expect_value(t, cursor.col, col)
|
||||||
|
}
|
||||||
|
|
||||||
|
expect_cursor_index :: proc(t: ^testing.T, cursor: core.Cursor, slice_index, content_index: int) {
|
||||||
|
testing.expect_value(t, cursor.index.slice_index, slice_index)
|
||||||
|
testing.expect_value(t, cursor.index.content_index, content_index)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(test)
|
||||||
|
insert_from_empty_no_newlines :: proc(t: ^testing.T) {
|
||||||
|
e := new_test_editor()
|
||||||
|
setup_empty_buffer(&e)
|
||||||
|
|
||||||
|
buffer := &e.buffers[0]
|
||||||
|
|
||||||
|
inputted_text := "Hello, world!"
|
||||||
|
expected_text := fmt.aprintf("%v\n", inputted_text)
|
||||||
|
run_text_insertion(&e, inputted_text)
|
||||||
|
|
||||||
|
expect_line_col(t, buffer.cursor, 0, 12)
|
||||||
|
expect_cursor_index(t, buffer.cursor, 0, 12)
|
||||||
|
|
||||||
|
contents := buffer_to_string(core.current_buffer(&e))
|
||||||
|
testing.expectf(t, contents == expected_text, "got '%v', expected '%v'", contents, expected_text)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(test)
|
||||||
|
insert_from_empty_with_newline :: proc(t: ^testing.T) {
|
||||||
|
e := new_test_editor()
|
||||||
|
setup_empty_buffer(&e)
|
||||||
|
|
||||||
|
buffer := &e.buffers[0]
|
||||||
|
|
||||||
|
inputted_text := "Hello, world!\nThis is a new line"
|
||||||
|
expected_text := fmt.aprintf("%v\n", inputted_text)
|
||||||
|
run_text_insertion(&e, inputted_text)
|
||||||
|
|
||||||
|
expect_line_col(t, buffer.cursor, 1, 17)
|
||||||
|
expect_cursor_index(t, buffer.cursor, 0, 31)
|
||||||
|
|
||||||
|
contents := buffer_to_string(core.current_buffer(&e))
|
||||||
|
testing.expectf(t, contents == expected_text, "got '%v', expected '%v'", contents, expected_text)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(test)
|
||||||
|
insert_in_between_text :: proc(t: ^testing.T) {
|
||||||
|
e := new_test_editor()
|
||||||
|
setup_empty_buffer(&e)
|
||||||
|
|
||||||
|
buffer := &e.buffers[0]
|
||||||
|
|
||||||
|
inputted_text := "Hello, world!"
|
||||||
|
expected_text := "Hello, beautiful world!\n"
|
||||||
|
|
||||||
|
run_text_insertion(&e, inputted_text)
|
||||||
|
|
||||||
|
// Move the cursor to the space in between 'Hello,' and 'world!'
|
||||||
|
run_input_multiple(&e, press_key(.H), 6)
|
||||||
|
|
||||||
|
run_text_insertion(&e, " beautiful")
|
||||||
|
|
||||||
|
expect_line_col(t, buffer.cursor, 0, 15)
|
||||||
|
expect_cursor_index(t, buffer.cursor, 1, 9)
|
||||||
|
|
||||||
|
contents := buffer_to_string(core.current_buffer(&e))
|
||||||
|
testing.expectf(t, contents == expected_text, "got '%v', expected '%v'", contents, expected_text)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(test)
|
||||||
|
insert_before_slice_at_beginning_of_file :: proc(t: ^testing.T) {
|
||||||
|
e := new_test_editor()
|
||||||
|
setup_empty_buffer(&e)
|
||||||
|
|
||||||
|
buffer := &e.buffers[0]
|
||||||
|
|
||||||
|
inputted_text := "Hello, world!"
|
||||||
|
expected_text := "Well, Hello, beautiful world!\n"
|
||||||
|
|
||||||
|
run_text_insertion(&e, inputted_text)
|
||||||
|
|
||||||
|
// Move the cursor to the space in between 'Hello,' and 'world!'
|
||||||
|
run_input_multiple(&e, press_key(.H), 6)
|
||||||
|
run_text_insertion(&e, " beautiful")
|
||||||
|
|
||||||
|
// Move to beginning of line (and hence the file)
|
||||||
|
run_inputs(&e, []ArtificialInput{ press_key(.G), press_key(.H)})
|
||||||
|
run_text_insertion(&e, "Well, ")
|
||||||
|
|
||||||
|
expect_line_col(t, buffer.cursor, 0, 5)
|
||||||
|
expect_cursor_index(t, buffer.cursor, 0, 5)
|
||||||
|
|
||||||
|
contents := buffer_to_string(core.current_buffer(&e))
|
||||||
|
testing.expectf(t, contents == expected_text, "got '%v', expected '%v'", contents, expected_text)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(test)
|
||||||
|
insert_before_slice :: proc(t: ^testing.T) {
|
||||||
|
e := new_test_editor()
|
||||||
|
setup_empty_buffer(&e)
|
||||||
|
|
||||||
|
buffer := &e.buffers[0]
|
||||||
|
|
||||||
|
inputted_text := "Hello, world!"
|
||||||
|
expected_text := "Hello, beautiful rich world!\n"
|
||||||
|
|
||||||
|
run_text_insertion(&e, inputted_text)
|
||||||
|
|
||||||
|
// Move the cursor to the space in between 'Hello,' and 'world!'
|
||||||
|
run_input_multiple(&e, press_key(.H), 6)
|
||||||
|
run_text_insertion(&e, " beautiful")
|
||||||
|
|
||||||
|
// Move right to the start of the slice of ' world!'
|
||||||
|
run_input_multiple(&e, press_key(.L), 1)
|
||||||
|
|
||||||
|
run_text_insertion(&e, " rich")
|
||||||
|
|
||||||
|
expect_line_col(t, buffer.cursor, 0, 20)
|
||||||
|
expect_cursor_index(t, buffer.cursor, 2, 4)
|
||||||
|
|
||||||
|
contents := buffer_to_string(core.current_buffer(&e))
|
||||||
|
testing.expectf(t, contents == expected_text, "got '%v', expected '%v'", contents, expected_text)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(test)
|
||||||
|
delete_in_slice :: proc(t: ^testing.T) {
|
||||||
|
e := new_test_editor()
|
||||||
|
setup_empty_buffer(&e)
|
||||||
|
|
||||||
|
buffer := &e.buffers[0]
|
||||||
|
|
||||||
|
inputted_text := "Hello, world!"
|
||||||
|
expected_text := "Hello, beautiful h world!\n"
|
||||||
|
// ------ - ---------
|
||||||
|
// ---------- -
|
||||||
|
// 0 1 234
|
||||||
|
|
||||||
|
run_text_insertion(&e, inputted_text)
|
||||||
|
|
||||||
|
// Move the cursor to the space in between 'Hello,' and 'world!'
|
||||||
|
run_input_multiple(&e, press_key(.H), 6)
|
||||||
|
run_text_insertion(&e, " beautiful")
|
||||||
|
|
||||||
|
// Move right to the start of the slice of ' world!'
|
||||||
|
run_input_multiple(&e, press_key(.L), 1)
|
||||||
|
run_text_insertion(&e, " rich")
|
||||||
|
|
||||||
|
run_input_multiple(&e, press_key(.I), 1)
|
||||||
|
run_input_multiple(&e, press_key(.BACKSPACE), 3)
|
||||||
|
run_input_multiple(&e, press_key(.ESCAPE), 1)
|
||||||
|
|
||||||
|
expect_line_col(t, buffer.cursor, 0, 17)
|
||||||
|
expect_cursor_index(t, buffer.cursor, 3, 0)
|
||||||
|
|
||||||
|
contents := buffer_to_string(core.current_buffer(&e))
|
||||||
|
testing.expectf(t, contents == expected_text, "got '%v', expected '%v'", contents, expected_text)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(test)
|
||||||
|
delete_across_slices :: proc(t: ^testing.T) {
|
||||||
|
e := new_test_editor()
|
||||||
|
setup_empty_buffer(&e)
|
||||||
|
|
||||||
|
buffer := &e.buffers[0]
|
||||||
|
|
||||||
|
inputted_text := "Hello, world!"
|
||||||
|
expected_text := "Hello, beautiful world!\n"
|
||||||
|
// ------ ---------
|
||||||
|
// ----------
|
||||||
|
// 0 1 2
|
||||||
|
|
||||||
|
run_text_insertion(&e, inputted_text)
|
||||||
|
|
||||||
|
// Move the cursor to the space in between 'Hello,' and 'world!'
|
||||||
|
run_input_multiple(&e, press_key(.H), 6)
|
||||||
|
run_text_insertion(&e, " beautiful")
|
||||||
|
|
||||||
|
// Move right to the start of the slice of ' world!'
|
||||||
|
run_input_multiple(&e, press_key(.L), 1)
|
||||||
|
run_text_insertion(&e, " rich")
|
||||||
|
|
||||||
|
run_input_multiple(&e, press_key(.I), 1)
|
||||||
|
run_input_multiple(&e, press_key(.BACKSPACE), 3)
|
||||||
|
run_input_multiple(&e, press_key(.ESCAPE), 1)
|
||||||
|
|
||||||
|
// Move right, passed the 'h' on to the space before 'world!'
|
||||||
|
run_input_multiple(&e, press_key(.L), 1)
|
||||||
|
|
||||||
|
// Remove the ' h', which consists of two content slices
|
||||||
|
run_input_multiple(&e, press_key(.I), 1)
|
||||||
|
run_input_multiple(&e, press_key(.BACKSPACE), 2)
|
||||||
|
run_input_multiple(&e, press_key(.ESCAPE), 1)
|
||||||
|
|
||||||
|
expect_line_col(t, buffer.cursor, 0, 16)
|
||||||
|
expect_cursor_index(t, buffer.cursor, 2, 0)
|
||||||
|
|
||||||
|
contents := buffer_to_string(core.current_buffer(&e))
|
||||||
|
testing.expectf(t, contents == expected_text, "got '%v', expected '%v'", contents, expected_text)
|
||||||
|
}
|
||||||
|
|
||||||
|
// @(test)
|
||||||
|
// insert_line_under_current :: proc(t: ^testing.T) {
|
||||||
|
// e := new_test_editor()
|
||||||
|
// setup_empty_buffer(&e)
|
||||||
|
|
||||||
|
// buffer := &e.buffers[0]
|
||||||
|
|
||||||
|
// inputted_text := "Hello, world!\nThis is a new line"
|
||||||
|
// expected_text := fmt.aprintf("%v\n", inputted_text)
|
||||||
|
// run_text_insertion(&e, inputted_text)
|
||||||
|
|
||||||
|
// expect_line_col(t, buffer.cursor, 1, 17)
|
||||||
|
// expect_cursor_index(t, buffer.cursor, 0, 31)
|
||||||
|
|
||||||
|
// contents := buffer_to_string(core.current_buffer(&e))
|
||||||
|
// testing.expectf(t, contents == expected_text, "got '%v', expected '%v'", contents, expected_text)
|
||||||
|
// }
|
||||||
|
|
||||||
|
@(test)
|
||||||
|
move_left_at_beginning_of_file :: proc(t: ^testing.T) {
|
||||||
|
e := new_test_editor()
|
||||||
|
setup_empty_buffer(&e)
|
||||||
|
|
||||||
|
buffer := &e.buffers[0]
|
||||||
|
|
||||||
|
run_text_insertion(&e, "01234")
|
||||||
|
// Move cursor from --------^
|
||||||
|
// to ------------------^
|
||||||
|
run_input_multiple(&e, press_key(.H), 4)
|
||||||
|
|
||||||
|
expect_line_col(t, buffer.cursor, 0, 0)
|
||||||
|
expect_cursor_index(t, buffer.cursor, 0, 0)
|
||||||
|
|
||||||
|
// Try to move before the beginning of the file
|
||||||
|
run_input_multiple(&e, press_key(.H), 1)
|
||||||
|
|
||||||
|
// Should stay the same
|
||||||
|
expect_line_col(t, buffer.cursor, 0, 0)
|
||||||
|
expect_cursor_index(t, buffer.cursor, 0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(test)
|
||||||
|
move_right_at_end_of_file :: proc(t: ^testing.T) {
|
||||||
|
e := new_test_editor()
|
||||||
|
setup_empty_buffer(&e)
|
||||||
|
|
||||||
|
is_ctrl_pressed := false
|
||||||
|
|
||||||
|
buffer := &e.buffers[0]
|
||||||
|
|
||||||
|
run_text_insertion(&e, "01234")
|
||||||
|
|
||||||
|
expect_line_col(t, buffer.cursor, 0, 4)
|
||||||
|
expect_cursor_index(t, buffer.cursor, 0, 4)
|
||||||
|
|
||||||
|
// Try to move after the end of the file
|
||||||
|
run_input_multiple(&e, press_key(.L), 1)
|
||||||
|
|
||||||
|
// Should stay the same
|
||||||
|
expect_line_col(t, buffer.cursor, 0, 4)
|
||||||
|
expect_cursor_index(t, buffer.cursor, 0, 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(test)
|
||||||
|
move_to_end_of_line_from_end :: proc(t: ^testing.T) {
|
||||||
|
e := new_test_editor()
|
||||||
|
setup_empty_buffer(&e)
|
||||||
|
|
||||||
|
is_ctrl_pressed := false
|
||||||
|
|
||||||
|
buffer := &e.buffers[0]
|
||||||
|
|
||||||
|
run_text_insertion(&e, "01234\n01234")
|
||||||
|
|
||||||
|
// Move up to the first line
|
||||||
|
run_input_multiple(&e, press_key(.K), 1)
|
||||||
|
|
||||||
|
// Move to the end of the line
|
||||||
|
run_inputs(&e, []ArtificialInput{ press_key(.G), press_key(.L)})
|
||||||
|
|
||||||
|
expect_line_col(t, buffer.cursor, 0, 4)
|
||||||
|
expect_cursor_index(t, buffer.cursor, 0, 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(test)
|
||||||
|
move_to_end_of_line_from_middle :: proc(t: ^testing.T) {
|
||||||
|
e := new_test_editor()
|
||||||
|
setup_empty_buffer(&e)
|
||||||
|
|
||||||
|
is_ctrl_pressed := false
|
||||||
|
|
||||||
|
buffer := &e.buffers[0]
|
||||||
|
|
||||||
|
run_text_insertion(&e, "01234\n01234")
|
||||||
|
|
||||||
|
// Move up to the first line
|
||||||
|
run_input_multiple(&e, press_key(.K), 1)
|
||||||
|
|
||||||
|
// Move into the middle of the line
|
||||||
|
run_input_multiple(&e, press_key(.H), 2)
|
||||||
|
|
||||||
|
// Move to the end of the line
|
||||||
|
run_inputs(&e, []ArtificialInput{ press_key(.G), press_key(.L)})
|
||||||
|
|
||||||
|
expect_line_col(t, buffer.cursor, 0, 4)
|
||||||
|
expect_cursor_index(t, buffer.cursor, 0, 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(test)
|
||||||
|
move_to_beginning_of_line_from_middle :: proc(t: ^testing.T) {
|
||||||
|
e := new_test_editor()
|
||||||
|
setup_empty_buffer(&e)
|
||||||
|
|
||||||
|
is_ctrl_pressed := false
|
||||||
|
|
||||||
|
buffer := &e.buffers[0]
|
||||||
|
|
||||||
|
run_text_insertion(&e, "01234\n01234")
|
||||||
|
|
||||||
|
// Move up to the first line
|
||||||
|
run_input_multiple(&e, press_key(.K), 1)
|
||||||
|
|
||||||
|
// Move into the middle of the line
|
||||||
|
run_input_multiple(&e, press_key(.H), 2)
|
||||||
|
|
||||||
|
// Move to the beginning of the line
|
||||||
|
run_inputs(&e, []ArtificialInput{ press_key(.G), press_key(.H)})
|
||||||
|
|
||||||
|
expect_line_col(t, buffer.cursor, 0, 0)
|
||||||
|
expect_cursor_index(t, buffer.cursor, 0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(test)
|
||||||
|
move_to_beginning_of_line_from_start :: proc(t: ^testing.T) {
|
||||||
|
e := new_test_editor()
|
||||||
|
setup_empty_buffer(&e)
|
||||||
|
|
||||||
|
is_ctrl_pressed := false
|
||||||
|
|
||||||
|
buffer := &e.buffers[0]
|
||||||
|
|
||||||
|
run_text_insertion(&e, "01234\n01234")
|
||||||
|
|
||||||
|
// Move up to the first line
|
||||||
|
run_input_multiple(&e, press_key(.K), 1)
|
||||||
|
|
||||||
|
// Move to the start of the line
|
||||||
|
run_input_multiple(&e, press_key(.H), 4)
|
||||||
|
|
||||||
|
// Move to the beginning of the line
|
||||||
|
run_inputs(&e, []ArtificialInput{ press_key(.G), press_key(.H)})
|
||||||
|
|
||||||
|
expect_line_col(t, buffer.cursor, 0, 0)
|
||||||
|
expect_cursor_index(t, buffer.cursor, 0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 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);
|
||||||
|
return true;
|
||||||
|
case core.InputActions:
|
||||||
|
state.current_input_map = &(&state.current_input_map.ctrl_key_actions[key]).action.(core.InputActions)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if action, exists := state.current_input_map.key_actions[key]; exists {
|
||||||
|
switch value in action.action {
|
||||||
|
case core.EditorAction:
|
||||||
|
value(state);
|
||||||
|
return true;
|
||||||
|
case core.InputActions:
|
||||||
|
state.current_input_map = &(&state.current_input_map.key_actions[key]).action.(core.InputActions)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.info("current_input_map is null")
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
is_ctrl_pressed^ = true;
|
||||||
|
} else {
|
||||||
|
run_key_action(state, is_ctrl_pressed^, key.key)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if key.key == .LCTRL {
|
||||||
|
is_ctrl_pressed^ = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case .Insert: {
|
||||||
|
log.info("it's insert mode")
|
||||||
|
|
||||||
|
buffer := core.current_buffer(state);
|
||||||
|
|
||||||
|
if key, ok := input.(ArtificialKey); ok {
|
||||||
|
if key.is_down {
|
||||||
|
// TODO: make this work properly
|
||||||
|
if true || !run_key_action(state, is_ctrl_pressed^, key.key) {
|
||||||
|
#partial switch key.key {
|
||||||
|
case .ESCAPE: {
|
||||||
|
state.mode = .Normal;
|
||||||
|
|
||||||
|
core.insert_content(buffer, buffer.input_buffer[:]);
|
||||||
|
runtime.clear(&buffer.input_buffer);
|
||||||
|
}
|
||||||
|
case .TAB: {
|
||||||
|
// TODO: change this to insert a tab character
|
||||||
|
for _ in 0..<4 {
|
||||||
|
append(&buffer.input_buffer, ' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case .BACKSPACE: {
|
||||||
|
core.delete_content(buffer, 1);
|
||||||
|
}
|
||||||
|
case .ENTER: {
|
||||||
|
append(&buffer.input_buffer, '\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: share this with the main application
|
||||||
|
do_insert_mode :: proc(state: ^core.State, buffer: ^core.FileBuffer) {
|
||||||
|
key := 0;
|
||||||
|
|
||||||
|
for key > 0 {
|
||||||
|
if key >= 32 && key <= 125 && len(buffer.input_buffer) < 1024-1 {
|
||||||
|
append(&buffer.input_buffer, u8(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
key = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch state.mode {
|
||||||
|
case .Normal:
|
||||||
|
// buffer := core.current_buffer(state);
|
||||||
|
// do_normal_mode(state, buffer);
|
||||||
|
case .Insert:
|
||||||
|
buffer := core.current_buffer(state);
|
||||||
|
do_insert_mode(state, buffer);
|
||||||
|
case .Visual:
|
||||||
|
// buffer := core.current_buffer(state);
|
||||||
|
// do_visual_mode(state, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
runtime.free_all(context.temp_allocator);
|
||||||
|
}
|
18
todo.md
18
todo.md
|
@ -2,22 +2,27 @@
|
||||||
- Fix crash when cursor is over a new-line
|
- Fix crash when cursor is over a new-line
|
||||||
- Fix jumping forward a word jumping past consecutive brackets
|
- Fix jumping forward a word jumping past consecutive brackets
|
||||||
- Odd scrolling behavior on small screen heights
|
- Odd scrolling behavior on small screen heights
|
||||||
|
- Closing the only panel crashes
|
||||||
|
|
||||||
# Planned Features
|
# Planned Features
|
||||||
|
- Testing Harness
|
||||||
|
- [x] Replay user inputs and assert buffer contents/changes
|
||||||
|
- [ ] Finish writing tests for all current user actions
|
||||||
|
- Vim-like Macro replays
|
||||||
|
- [ ] Simple File Search (vim /)
|
||||||
|
- [ ] Auto-indent
|
||||||
|
- Modify input system to allow for keybinds that take input
|
||||||
|
- Vim's f and F movement commands
|
||||||
|
- Vim's r command
|
||||||
- Save/Load files
|
- Save/Load files
|
||||||
- [x] Save
|
- [x] Save
|
||||||
- [ ] Load when changed on disk
|
- [ ] Load when changed on disk
|
||||||
- [ ] Simple File Search (vim /)
|
|
||||||
- [ ] Auto-indent
|
|
||||||
- Testing Harness
|
|
||||||
- [ ] Replay user inputs and assert buffer contents/changes
|
|
||||||
- LSP Integration
|
- LSP Integration
|
||||||
- [ ] Language Server Configurations
|
- [ ] Language Server Configurations
|
||||||
- [ ] Diagnostics
|
- [ ] Diagnostics
|
||||||
- [ ] In-line errors
|
- [ ] In-line errors
|
||||||
- [ ] Go-to Definition/
|
- [ ] Go-to Definition/
|
||||||
- [ ] Find references
|
- [ ] Find references
|
||||||
- Vim-like Macro replays
|
|
||||||
- Re-implement lost features from Plugins
|
- Re-implement lost features from Plugins
|
||||||
- [ ] Syntax Highlighting
|
- [ ] Syntax Highlighting
|
||||||
- [ ] Integrate tree-sitter
|
- [ ] Integrate tree-sitter
|
||||||
|
@ -45,9 +50,6 @@
|
||||||
- [ ] Change inside delimiter
|
- [ ] Change inside delimiter
|
||||||
- Virtual Whitespace
|
- Virtual Whitespace
|
||||||
- Allow any-sized tabs
|
- Allow any-sized tabs
|
||||||
- Modify input system to allow for keybinds that take input
|
|
||||||
- Vim's f and F movement commands
|
|
||||||
- Vim's r command
|
|
||||||
- Command Search and Execution
|
- Command Search and Execution
|
||||||
- Refactor to remove generics added specifically for plugins
|
- Refactor to remove generics added specifically for plugins
|
||||||
- Palette based UI?
|
- Palette based UI?
|
||||||
|
|
Loading…
Reference in New Issue