Compare commits

...

5 Commits

10 changed files with 300 additions and 195 deletions

View File

@ -137,8 +137,6 @@ 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
@ -171,7 +169,12 @@ yank_selection :: proc(state: ^State, buffer: ^FileBuffer) {
length := selection_length(buffer, selection)
state.yank_register.whole_line = false
state.yank_register.data = make([]u8, length)
err: runtime.Allocator_Error
state.yank_register.data, err = make([]u8, length)
if err != nil {
log.error("failed to allocate memory for yank register")
}
it := new_file_buffer_iter_with_cursor(buffer, selection.start)
@ -185,7 +188,7 @@ yank_selection :: proc(state: ^State, buffer: ^FileBuffer) {
}
paste_register :: proc(state: ^State, register: Register, buffer: ^FileBuffer) {
insert_content(buffer, register.data)
insert_content(buffer, register.data, reparse_buffer = true)
move_cursor_left(buffer)
}

View File

@ -55,8 +55,6 @@ FileBuffer :: struct {
history: FileHistory,
glyphs: GlyphBuffer,
input_buffer: [dynamic]u8,
}
BufferFlagSet :: bit_set[BufferFlags]
@ -712,7 +710,6 @@ new_virtual_file_buffer :: proc(allocator := context.allocator) -> FileBuffer {
history = make_history(),
glyphs = make_glyph_buffer(width, height),
input_buffer = make([dynamic]u8, 0, 1024),
};
return buffer;
@ -776,7 +773,6 @@ new_file_buffer :: proc(allocator: mem.Allocator, file_path: string, base_dir: s
history = make_history(content),
glyphs = make_glyph_buffer(width, height),
input_buffer = make([dynamic]u8, 0, 1024),
};
ts.parse_buffer(&buffer.tree, tree_sitter_file_buffer_input(&buffer))
@ -834,7 +830,6 @@ free_file_buffer :: proc(buffer: ^FileBuffer) {
ts.delete_state(&buffer.tree)
free_history(&buffer.history)
delete(buffer.glyphs.buffer)
delete(buffer.input_buffer)
}
color_character :: proc(buffer: ^FileBuffer, start: Cursor, end: Cursor, palette_index: theme.PaletteColor) {
@ -906,26 +901,6 @@ draw_file_buffer :: proc(state: ^State, buffer: ^FileBuffer, x, y, w, h: int, sh
draw_rect(state, start_sel_x, start_sel_y, state.source_font_width, state.source_font_height, .Green);
draw_rect(state, end_sel_x, end_sel_y, state.source_font_width, state.source_font_height, .Blue);
} else if state.mode == .Insert {
draw_rect(state, cursor_x, cursor_y, state.source_font_width, state.source_font_height, .Green);
num_line_break := 0;
line_length := 0;
for c in buffer.input_buffer {
if c == '\n' {
num_line_break += 1;
line_length = 0;
} else {
line_length += 1;
}
}
if num_line_break > 0 {
cursor_x = x + padding + line_length * state.source_font_width;
cursor_y = cursor_y + num_line_break * state.source_font_height;
} else {
cursor_x += line_length * state.source_font_width;
}
draw_rect(state, cursor_x, cursor_y, state.source_font_width, state.source_font_height, .Blue);
}
}
@ -1007,56 +982,101 @@ scroll_file_buffer :: proc(buffer: ^FileBuffer, dir: ScrollDir, cursor: Maybe(^C
}
}
insert_content :: proc(buffer: ^FileBuffer, to_be_inserted: []u8, append_to_end: bool = false) {
insert_content :: proc(buffer: ^FileBuffer, to_be_inserted: []u8, reparse_buffer: bool = false) {
if len(to_be_inserted) == 0 {
return;
}
buffer.flags += { .UnsavedChanges }
index := buffer.history.cursor.index if !append_to_end else new_piece_table_index_from_end(buffer_piece_table(buffer))
index := buffer.history.cursor.index
insert_text(buffer_piece_table(buffer), to_be_inserted, buffer.history.cursor.index)
if !append_to_end {
update_file_buffer_index_from_cursor(buffer);
move_cursor_right(buffer, false, amt = len(to_be_inserted) - 1);
}
update_file_buffer_index_from_cursor(buffer);
move_cursor_right(buffer, false, amt = len(to_be_inserted));
ts.parse_buffer(&buffer.tree, tree_sitter_file_buffer_input(buffer))
if reparse_buffer {
ts.parse_buffer(&buffer.tree, tree_sitter_file_buffer_input(buffer))
}
}
delete_content_from_buffer_cursor :: proc(buffer: ^FileBuffer, amount: int) {
if amount <= len(buffer.input_buffer) {
runtime.resize(&buffer.input_buffer, len(buffer.input_buffer)-amount);
} else {
buffer.flags += { .UnsavedChanges }
delete_content_from_buffer_cursor :: proc(buffer: ^FileBuffer, amount: int, reparse_buffer: bool = false) {
buffer.flags += { .UnsavedChanges }
amount := amount - len(buffer.input_buffer);
runtime.clear(&buffer.input_buffer);
// Calculate proper line/col values
it := new_file_buffer_iter_with_cursor(buffer, buffer.history.cursor);
iterate_file_buffer_reverse(&it)
// Calculate proper line/col values
it := new_file_buffer_iter_with_cursor(buffer, buffer.history.cursor);
iterate_file_buffer_reverse(&it)
delete_text(buffer_piece_table(buffer), &buffer.history.cursor.index)
delete_text(buffer_piece_table(buffer), &buffer.history.cursor.index)
buffer.history.cursor.line = it.cursor.line
buffer.history.cursor.col = it.cursor.col
buffer.history.cursor.line = it.cursor.line
buffer.history.cursor.col = it.cursor.col
if reparse_buffer {
ts.parse_buffer(&buffer.tree, tree_sitter_file_buffer_input(buffer))
}
ts.parse_buffer(&buffer.tree, tree_sitter_file_buffer_input(buffer))
}
delete_content_from_selection :: proc(buffer: ^FileBuffer, selection: ^Selection) {
delete_content_from_selection :: proc(buffer: ^FileBuffer, selection: ^Selection, reparse_buffer: bool = false) {
buffer.flags += { .UnsavedChanges }
selection^ = swap_selections(selection^)
delete_text_in_span(buffer_piece_table(buffer), &selection.start.index, &selection.end.index)
buffer.history.cursor.line = selection.start.line
buffer.history.cursor.col = selection.start.col
buffer.history.cursor.index = selection.start.index
ts.parse_buffer(&buffer.tree, tree_sitter_file_buffer_input(buffer))
if get_character_at_piece_table_index(buffer_piece_table(buffer), selection.start.index) == '\n' {
move_cursor_left(buffer)
}
if reparse_buffer {
ts.parse_buffer(&buffer.tree, tree_sitter_file_buffer_input(buffer))
}
}
delete_content :: proc{delete_content_from_buffer_cursor, delete_content_from_selection};
get_buffer_indent :: proc(buffer: ^FileBuffer, cursor: Maybe(Cursor) = nil) -> int {
cursor := cursor;
if cursor == nil {
cursor = buffer.history.cursor;
}
ptr_cursor := &cursor.?
move_cursor_start_of_line(buffer, ptr_cursor)
it := new_file_buffer_iter_with_cursor(buffer, ptr_cursor^);
iterate_file_buffer_until(&it, until_non_whitespace)
return it.cursor.col
}
buffer_to_string :: proc(buffer: ^FileBuffer, allocator := context.allocator) -> string {
context.allocator = allocator
length := 0
for chunk in buffer_piece_table(buffer).chunks {
length += len(chunk)
}
buffer_contents := make([]u8, length)
offset := 0
for chunk in buffer_piece_table(buffer).chunks {
for c in chunk {
buffer_contents[offset] = c
offset += 1
}
}
return string(buffer_contents[:len(buffer_contents)-1])
}
buffer_append_new_line :: proc(buffer: ^FileBuffer) {
}

View File

@ -61,25 +61,26 @@ update_glyph_buffer_from_file_buffer :: proc(buffer: ^FileBuffer, width, height:
// don't render past the screen
if rendered_line >= begin && screen_line >= buffer.glyphs.height { break; }
// render INSERT mode text into glyph buffer
if len(buffer.input_buffer) > 0 && rendered_line == buffer.history.cursor.line && rendered_col >= buffer.history.cursor.col && rendered_col < buffer.history.cursor.col + len(buffer.input_buffer) {
for k in 0..<len(buffer.input_buffer) {
screen_line = rendered_line - begin;
// NOTE: `input_buffer` doesn't exist anymore, but this is a nice reference for just inserting text within the glyph buffer
//
// if len(buffer.input_buffer) > 0 && rendered_line == buffer.history.cursor.line && rendered_col >= buffer.history.cursor.col && rendered_col < buffer.history.cursor.col + len(buffer.input_buffer) {
// for k in 0..<len(buffer.input_buffer) {
// screen_line = rendered_line - begin;
if buffer.input_buffer[k] == '\n' {
rendered_col = 0;
rendered_line += 1;
continue;
}
// if buffer.input_buffer[k] == '\n' {
// rendered_col = 0;
// rendered_line += 1;
// continue;
// }
if rendered_line >= begin && rendered_col < buffer.glyphs.width {
buffer.glyphs.buffer[rendered_col + screen_line * buffer.glyphs.width].color = .Foreground;
buffer.glyphs.buffer[rendered_col + screen_line * buffer.glyphs.width].codepoint = buffer.input_buffer[k];
// if rendered_line >= begin && rendered_col < buffer.glyphs.width {
// buffer.glyphs.buffer[rendered_col + screen_line * buffer.glyphs.width].color = .Foreground;
// buffer.glyphs.buffer[rendered_col + screen_line * buffer.glyphs.width].codepoint = buffer.input_buffer[k];
rendered_col += 1;
}
}
}
// rendered_col += 1;
// }
// }
// }
screen_line = rendered_line - begin;

View File

@ -32,10 +32,11 @@ new_logger :: proc(buffer: ^FileBuffer) -> runtime.Logger {
logger_proc :: proc(data: rawptr, level: runtime.Logger_Level, text: string, options: runtime.Logger_Options, location := #caller_location) {
buffer := cast(^FileBuffer)data;
if .Level in options {
insert_content(buffer, transmute([]u8)(Level_Header[level]), true);
}
// FIXME
// if .Level in options {
// insert_content(buffer, transmute([]u8)(Level_Header[level]), true);
// }
insert_content(buffer, transmute([]u8)(text), true);
insert_content(buffer, {'\n'}, true);
// insert_content(buffer, transmute([]u8)(text), true);
// insert_content(buffer, {'\n'}, true);
}

View File

@ -3,6 +3,8 @@ package core
PieceTable :: struct {
original_content: []u8,
added_content: [dynamic]u8,
// TODO: don't actually reference `added_content` and `original_content` via pointers, since they can be re-allocated
chunks: [dynamic][]u8,
}
@ -158,7 +160,19 @@ insert_text :: proc(t: ^PieceTable, to_be_inserted: []u8, index: PieceTableIndex
if index.char_index == 0 {
// insertion happening in beginning of content slice
inject_at(&t.chunks, index.chunk_index, inserted_slice);
if len(t.chunks) > 1 && index.chunk_index > 0 {
last_chunk_index := len(t.chunks[index.chunk_index-1])-1
if (&t.chunks[index.chunk_index-1][last_chunk_index]) == (&t.added_content[len(t.added_content)-1 - length]) {
start := len(t.added_content)-1 - last_chunk_index - length
t.chunks[index.chunk_index-1] = t.added_content[start:]
} else {
inject_at(&t.chunks, index.chunk_index, inserted_slice);
}
} else {
inject_at(&t.chunks, index.chunk_index, inserted_slice);
}
}
else {
// insertion is happening in middle of content slice

View File

@ -25,24 +25,6 @@ FileBuffer :: core.FileBuffer;
state := core.State {};
do_normal_mode :: proc(state: ^State, buffer: ^FileBuffer) {
}
do_insert_mode :: proc(state: ^State, buffer: ^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;
}
}
do_visual_mode :: proc(state: ^State, buffer: ^FileBuffer) {
}
ui_font_width :: proc() -> i32 {
return i32(state.source_font_width);
}
@ -286,6 +268,7 @@ main :: proc() {
log.error("SDL failed to initialize:", sdl2.GetError());
return;
}
defer sdl2.Quit()
if ttf.Init() < 0 {
log.error("SDL_TTF failed to initialize:", ttf.GetError());
@ -443,22 +426,49 @@ main :: proc() {
case .ESCAPE: {
state.mode = .Normal;
core.insert_content(buffer, buffer.input_buffer[:]);
runtime.clear(&buffer.input_buffer);
// core.insert_content(buffer, buffer.input_buffer[:]);
// runtime.clear(&buffer.input_buffer);
core.move_cursor_left(buffer)
sdl2.StopTextInput();
ts.parse_buffer(&buffer.tree, core.tree_sitter_file_buffer_input(buffer))
}
case .TAB: {
// TODO: change this to insert a tab character
for _ in 0..<4 {
append(&buffer.input_buffer, ' ');
// for _ in 0..<4 {
// append(&buffer.input_buffer, ' ');
// }
core.insert_content(buffer, transmute([]u8)string(" "))
if current_panel, ok := state.current_panel.?; ok {
if panel, ok := util.get(&state.panels, current_panel).?; ok && panel.on_buffer_input != nil {
panel->on_buffer_input(&state)
}
}
}
case .BACKSPACE: {
core.delete_content(buffer, 1);
if current_panel, ok := state.current_panel.?; ok {
if panel, ok := util.get(&state.panels, current_panel).?; ok && panel.on_buffer_input != nil {
panel->on_buffer_input(&state)
}
}
}
case .ENTER: {
append(&buffer.input_buffer, '\n');
indent := core.get_buffer_indent(buffer)
core.insert_content(buffer, []u8{'\n'})
for i in 0..<indent {
core.insert_content(buffer, []u8{' '})
}
if current_panel, ok := state.current_panel.?; ok {
if panel, ok := util.get(&state.panels, current_panel).?; ok && panel.on_buffer_input != nil {
panel->on_buffer_input(&state)
}
}
}
}
}
@ -470,8 +480,9 @@ main :: proc() {
break;
}
if char >= 32 && char <= 125 && len(buffer.input_buffer) < 1024-1 {
append(&buffer.input_buffer, u8(char));
if char >= 32 && char <= 125 {
// append(&buffer.input_buffer, u8(char));
core.insert_content(buffer, []u8{char})
}
}
@ -488,20 +499,6 @@ main :: proc() {
draw(&state);
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);
}
sdl2.Quit();
}

View File

@ -188,6 +188,25 @@ file_buffer_go_actions :: proc(input_map: ^core.InputActions) {
}, "move to end of line");
}
file_buffer_delete_actions :: proc(input_map: ^core.InputActions) {
core.register_key_action(input_map, .D, proc(state: ^core.State, user_data: rawptr) {
buffer := &(&(transmute(^core.Panel)user_data).type.(core.FileBufferPanel)).buffer
core.push_new_snapshot(&buffer.history)
buffer.selection = core.new_selection(buffer.history.cursor);
sel_cur := &(buffer.selection.?);
core.move_cursor_start_of_line(buffer, cursor = &sel_cur.start);
core.move_cursor_end_of_line(buffer, cursor = &sel_cur.end, stop_at_end = false);
core.delete_content_from_selection(buffer, sel_cur, reparse_buffer = true)
buffer.selection = nil;
core.reset_input_map(state)
}, "delete whole line");
}
file_buffer_input_actions :: proc(input_map: ^core.InputActions) {
// Cursor Movement
{
@ -273,6 +292,10 @@ file_buffer_input_actions :: proc(input_map: ^core.InputActions) {
file_buffer_go_actions(&go_actions);
core.register_key_action(input_map, .G, go_actions, "Go commands");
delete_actions := core.new_input_actions(show_help = true)
file_buffer_delete_actions(&delete_actions);
core.register_key_action(input_map, .D, delete_actions, "Delete commands");
core.register_key_action(input_map, .V, proc(state: ^core.State, user_data: rawptr) {
buffer := &(&(transmute(^core.Panel)user_data).type.(core.FileBufferPanel)).buffer
@ -428,7 +451,7 @@ file_buffer_visual_actions :: proc(input_map: ^core.InputActions) {
if state.yank_register.whole_line {
core.insert_content(buffer, []u8{'\n'});
core.paste_register(state, state.yank_register, buffer)
core.insert_content(buffer, []u8{'\n'});
core.insert_content(buffer, []u8{'\n'}, reparse_buffer = true);
} else {
core.paste_register(state, state.yank_register, buffer)
}
@ -479,10 +502,23 @@ file_buffer_text_input_actions :: proc(input_map: ^core.InputActions) {
core.push_new_snapshot(&buffer.history)
if buffer := buffer; buffer != nil {
core.move_cursor_end_of_line(buffer, false);
runtime.clear(&buffer.input_buffer)
core.move_cursor_end_of_line(buffer);
char := core.get_character_at_piece_table_index(core.buffer_piece_table(buffer), buffer.history.cursor.index)
indent := core.get_buffer_indent(buffer)
if char == '{' {
// TODO: update tab to be configurable
indent += 4
}
append(&buffer.input_buffer, '\n')
if char != '\n' {
core.move_cursor_right(buffer, stop_at_end = false)
}
core.insert_content(buffer, []u8{'\n'})
for i in 0..<indent {
core.insert_content(buffer, []u8{' '})
}
state.mode = .Insert;
@ -511,9 +547,8 @@ file_buffer_text_input_actions :: proc(input_map: ^core.InputActions) {
core.push_new_snapshot(&buffer.history)
if state.yank_register.whole_line {
core.move_cursor_end_of_line(buffer, false);
core.move_cursor_end_of_line(buffer, stop_at_end = false);
core.insert_content(buffer, []u8{'\n'});
core.move_cursor_right(buffer, false);
} else {
core.move_cursor_right(buffer)
}

View File

@ -21,7 +21,7 @@ open_grep_panel :: proc(state: ^core.State) {
}
make_grep_panel :: proc() -> core.Panel {
run_query :: proc(panel_state: ^core.GrepPanel, query: string, directory: string) {
run_query :: proc(panel_state: ^core.GrepPanel, buffer: ^core.FileBuffer, directory: string) {
if panel_state.query_region.arena != nil {
mem.end_arena_temp_memory(panel_state.query_region)
}
@ -30,7 +30,7 @@ make_grep_panel :: proc() -> core.Panel {
context.allocator = mem.arena_allocator(&panel_state.query_arena)
rs_results := grep(
strings.clone_to_cstring(query),
strings.clone_to_cstring(core.buffer_to_string(buffer)),
strings.clone_to_cstring(directory)
);
@ -142,7 +142,7 @@ make_grep_panel :: proc() -> core.Panel {
},
on_buffer_input = proc(panel: ^core.Panel, state: ^core.State) {
if panel_state, ok := &panel.type.(core.GrepPanel); ok {
run_query(panel_state, string(panel_state.buffer.input_buffer[:]), state.directory)
run_query(panel_state, &panel_state.buffer, state.directory)
}
},
render = proc(panel: ^core.Panel, state: ^core.State) -> (ok: bool) {
@ -183,7 +183,7 @@ make_grep_panel :: proc() -> core.Panel {
max_results := container_height / 16
for result, i in panel_state.query_results {
if i > max_results {
if i >= max_results {
break
}
@ -204,8 +204,14 @@ make_grep_panel :: proc() -> core.Panel {
style.background_color = .Background2
}
ui.open_element(s, result.file_path[len(state.directory):], {}, style)
ui.close_element(s)
if len(result.file_path) > 0 {
ui.open_element(s, result.file_path[len(state.directory):], {}, style)
ui.close_element(s)
} else {
style.background_color = .BrightRed
ui.open_element(s, "BAD FILE DIRECTORY", {}, style)
ui.close_element(s)
}
}
}
}

View File

@ -270,7 +270,6 @@ insert_before_slice :: proc(t: ^testing.T) {
run_text_insertion(&e, " rich")
expect_line_col(t, buffer.history.cursor, 0, 20)
expect_cursor_index(t, buffer.history.cursor, 2, 4)
contents := buffer_to_string(core.current_buffer(&e))
defer delete(contents)
@ -346,8 +345,7 @@ delete_in_slice :: proc(t: ^testing.T) {
run_input_multiple(&e, press_key(.BACKSPACE), 3)
run_input_multiple(&e, press_key(.ESCAPE), 1)
expect_line_col(t, buffer.history.cursor, 0, 17)
expect_cursor_index(t, buffer.history.cursor, 3, 0)
expect_line_col(t, buffer.history.cursor, 0, 16)
contents := buffer_to_string(core.current_buffer(&e))
defer delete(contents)
@ -387,15 +385,14 @@ delete_across_slices :: proc(t: ^testing.T) {
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)
run_input_multiple(&e, press_key(.L), 2)
// 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.history.cursor, 0, 16)
expect_cursor_index(t, buffer.history.cursor, 2, 0)
expect_line_col(t, buffer.history.cursor, 0, 15)
contents := buffer_to_string(core.current_buffer(&e))
defer delete(contents)
@ -632,14 +629,12 @@ append_end_of_line :: proc(t: ^testing.T) {
run_input_multiple(&e, press_key(.A), 1)
run_input_multiple(&e, press_key(.ESCAPE), 1)
expect_line_col(t, buffer.history.cursor, 0, 5)
expect_cursor_index(t, buffer.history.cursor, 1, 0)
expect_line_col(t, buffer.history.cursor, 0, 4)
run_input_multiple(&e, press_key(.A), 1)
run_input_multiple(&e, press_key(.ESCAPE), 1)
expect_line_col(t, buffer.history.cursor, 0, 5)
expect_cursor_index(t, buffer.history.cursor, 1, 0)
expect_line_col(t, buffer.history.cursor, 0, 4)
}
@(test)
@ -667,15 +662,11 @@ insert_line_under_current :: proc(t: ^testing.T) {
// Insert line below and enter insert mode
run_input_multiple(&e, press_key(.O), 1)
// Technically the cursor is still on the first line, because the `input_buffer`
// has been modified but not the actual contents of the filebuffer
expect_line_col(t, buffer.history.cursor, 0, 13)
expect_cursor_index(t, buffer.history.cursor, 0, 13)
expect_line_col(t, buffer.history.cursor, 1, 0)
run_text_insertion(&e, "This is the second line")
expect_line_col(t, buffer.history.cursor, 1, 22)
expect_cursor_index(t, buffer.history.cursor, 1, 23)
contents := buffer_to_string(core.current_buffer(&e))
defer delete(contents)
@ -716,6 +707,41 @@ yank_and_paste_whole_line :: proc(t: ^testing.T) {
testing.expectf(t, contents == expected_text, "got '%v', expected '%v'", contents, expected_text)
}
@(test)
select_and_delete_half_of_line_backwards :: proc(t: ^testing.T) {
e := new_test_editor()
setup_empty_buffer(&e)
defer {
panels.close(&e, 0)
delete_editor(&e)
}
buffer := core.current_buffer(&e)
initial_text := "Hello, world!\nThis is a new line"
run_text_insertion(&e, initial_text)
expected_text := "Hello\nThis is a new line\n"
// Move up to "Hello, world!"
run_input_multiple(&e, press_key(.K), 1)
// Move to end of line
run_inputs(&e, []ArtificialInput{ press_key(.G), press_key(.L)})
// Move to the end of 'Hello' and delete selection
run_input_multiple(&e, press_key(.V), 1)
run_input_multiple(&e, press_key(.H), 7)
run_input_multiple(&e, press_key(.D), 1)
expect_line_col(t, buffer.history.cursor, 0, 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)
}
run_editor_frame :: proc(state: ^core.State, input: ArtificialInput, is_ctrl_pressed: ^bool) {
{
run_key_action := proc(state: ^core.State, control_key_pressed: bool, key: core.Key) -> bool {
@ -777,21 +803,40 @@ run_editor_frame :: proc(state: ^core.State, input: ArtificialInput, is_ctrl_pre
#partial switch key.key {
case .ESCAPE: {
state.mode = .Normal;
core.insert_content(buffer, buffer.input_buffer[:]);
runtime.clear(&buffer.input_buffer);
core.move_cursor_left(buffer)
}
case .TAB: {
// TODO: change this to insert a tab character
for _ in 0..<4 {
append(&buffer.input_buffer, ' ');
core.insert_content(buffer, transmute([]u8)string(" "))
if current_panel, ok := state.current_panel.?; ok {
if panel, ok := util.get(&state.panels, current_panel).?; ok && panel.on_buffer_input != nil {
panel->on_buffer_input(state)
}
}
}
case .BACKSPACE: {
core.delete_content(buffer, 1);
if current_panel, ok := state.current_panel.?; ok {
if panel, ok := util.get(&state.panels, current_panel).?; ok && panel.on_buffer_input != nil {
panel->on_buffer_input(state)
}
}
}
case .ENTER: {
append(&buffer.input_buffer, '\n');
indent := core.get_buffer_indent(buffer)
core.insert_content(buffer, []u8{'\n'})
for i in 0..<indent {
core.insert_content(buffer, []u8{' '})
}
if current_panel, ok := state.current_panel.?; ok {
if panel, ok := util.get(&state.panels, current_panel).?; ok && panel.on_buffer_input != nil {
panel->on_buffer_input(state)
}
}
}
}
}
@ -804,8 +849,8 @@ run_editor_frame :: proc(state: ^core.State, input: ArtificialInput, is_ctrl_pre
break;
}
if char == '\n' || (char >= 32 && char <= 125 && len(buffer.input_buffer) < 1024-1) {
append(&buffer.input_buffer, u8(char));
if char == '\n' || (char >= 32 && char <= 125) {
core.insert_content(buffer, []u8{u8(char)})
}
}
@ -819,30 +864,5 @@ run_editor_frame :: proc(state: ^core.State, input: ArtificialInput, is_ctrl_pre
}
}
// 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);
}

50
todo.md
View File

@ -1,12 +1,13 @@
# Bugs
- Fix jumping forward a word jumping past consecutive brackets
- Odd scrolling behavior on small screen heights
- Scrolling past end/beginning of results panics
- Scrolling past end/beginning of grep results panics
- "change selection" not pushing snapshot
- Don't allow panel navigation in grep panel
# Visual QOL
- Split grep search results into a table to avoid funky unaligned text
# Planned Features
# TODO
- [ ] Jump List
- [x] Use grouped lifetimes exclusively for memory allocation/freeing
- [ ] Highlight which panel is currently active
@ -16,7 +17,6 @@
- [ ] 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
@ -29,26 +29,34 @@
- [ ] In-line errors
- [ ] Go-to Definition/
- [ ] Find references
- Re-implement lost features from Plugins
- [ ] Integrate tree-sitter
- [x] Syntax Highlighting
- [ ] Auto Setup Parsers
- [ ] Download parser
- [ ] Compile/"Install"
- [ ] Auto-indent?
- [ ] Bootleg Telescope
- [ ] Grepping Files
- [x] Query across project
- [x] Open file in new buffer
- [x] Open file in new buffer at found location
- [ ] Preview file with context
- [x] Show Context
- [ ] Properly show lines numbers
- [ ] Don't overlap result list with file preview
- [ ] Open Buffer Search
- [ ] Integrate tree-sitter
- [x] Syntax Highlighting
- [ ] Auto Setup Parsers
- [ ] Download parser
- [ ] Compile/"Install"
- [ ] Auto-indent
- [x] Infer indent with similar lines
- [x] Infer indent with C-style scopes (languages using '{')
- [ ] Infer indent inside multi-line function calls (again C-style)
- [ ] Somehow use tree-sitter to be language agnostic (not sure this is even possible with TS)
- [ ] Bootleg Telescope
- [ ] Grepping Files
- [x] Query across project
- [x] Open file in new buffer
- [x] Open file in new buffer at found location
- [ ] Preview file with context
- [x] Show Context
- [ ] Properly show lines numbers
- [ ] Don't overlap result list with file preview
- [ ] Open Buffer Search
- [ ] Workspace file search
- Re-write the UI (again)
- [x] New UI
- [ ] Styling
- [x] Background colors
- [x] Borders + Border Color
- [ ] Rounded corners?
- [ ] Gradients?
- Undo/Redo
- [x] Basic/Naive Undo/Redo
- [ ] Interface for undo-able actions