Compare commits
	
		
			5 Commits 
		
	
	
		
			381062518e
			...
			a70da837fa
		
	
	| Author | SHA1 | Date | 
|---|---|---|
|  | a70da837fa | |
|  | ad5e4f85fd | |
|  | dab095e88d | |
|  | c9991fb0ad | |
|  | e3d41ccad6 | 
|  | @ -137,8 +137,6 @@ current_buffer :: proc(state: ^State) -> ^FileBuffer { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| yank_whole_line :: proc(state: ^State, buffer: ^FileBuffer) { | yank_whole_line :: proc(state: ^State, buffer: ^FileBuffer) { | ||||||
|     context.allocator = buffer.allocator |  | ||||||
| 
 |  | ||||||
|     if state.yank_register.data != nil { |     if state.yank_register.data != nil { | ||||||
|         delete(state.yank_register.data) |         delete(state.yank_register.data) | ||||||
|         state.yank_register.data = nil |         state.yank_register.data = nil | ||||||
|  | @ -171,7 +169,12 @@ yank_selection :: proc(state: ^State, buffer: ^FileBuffer) { | ||||||
|     length := selection_length(buffer, selection) |     length := selection_length(buffer, selection) | ||||||
| 
 | 
 | ||||||
|     state.yank_register.whole_line = false |     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) |     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) { | 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) |     move_cursor_left(buffer) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -55,8 +55,6 @@ FileBuffer :: struct { | ||||||
| 
 | 
 | ||||||
|     history: FileHistory, |     history: FileHistory, | ||||||
|     glyphs: GlyphBuffer, |     glyphs: GlyphBuffer, | ||||||
| 
 |  | ||||||
|     input_buffer: [dynamic]u8, |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| BufferFlagSet :: bit_set[BufferFlags] | BufferFlagSet :: bit_set[BufferFlags] | ||||||
|  | @ -712,7 +710,6 @@ new_virtual_file_buffer :: proc(allocator := context.allocator) -> FileBuffer { | ||||||
|         history = make_history(), |         history = make_history(), | ||||||
| 
 | 
 | ||||||
|         glyphs = make_glyph_buffer(width, height), |         glyphs = make_glyph_buffer(width, height), | ||||||
|         input_buffer = make([dynamic]u8, 0, 1024), |  | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|     return buffer; |     return buffer; | ||||||
|  | @ -776,7 +773,6 @@ new_file_buffer :: proc(allocator: mem.Allocator, file_path: string, base_dir: s | ||||||
|             history = make_history(content), |             history = make_history(content), | ||||||
| 
 | 
 | ||||||
|             glyphs = make_glyph_buffer(width, height), |             glyphs = make_glyph_buffer(width, height), | ||||||
|             input_buffer = make([dynamic]u8, 0, 1024), |  | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         ts.parse_buffer(&buffer.tree, tree_sitter_file_buffer_input(&buffer)) |         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) |     ts.delete_state(&buffer.tree) | ||||||
|     free_history(&buffer.history) |     free_history(&buffer.history) | ||||||
|     delete(buffer.glyphs.buffer) |     delete(buffer.glyphs.buffer) | ||||||
|     delete(buffer.input_buffer) |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| color_character :: proc(buffer: ^FileBuffer, start: Cursor, end: Cursor, palette_index: theme.PaletteColor) { | 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, 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); |             draw_rect(state, end_sel_x, end_sel_y, state.source_font_width, state.source_font_height, .Blue); | ||||||
|         } else if state.mode == .Insert { |         } 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); |             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 { |     if len(to_be_inserted) == 0 { | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|     buffer.flags += { .UnsavedChanges } |     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) |     insert_text(buffer_piece_table(buffer), to_be_inserted, buffer.history.cursor.index) | ||||||
| 
 | 
 | ||||||
|     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); |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|     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) { | delete_content_from_buffer_cursor :: proc(buffer: ^FileBuffer, amount: int, reparse_buffer: bool = false) { | ||||||
|     if amount <= len(buffer.input_buffer) { |     buffer.flags += { .UnsavedChanges } | ||||||
|         runtime.resize(&buffer.input_buffer, len(buffer.input_buffer)-amount); |  | ||||||
|     } else { |  | ||||||
|         buffer.flags += { .UnsavedChanges } |  | ||||||
| 
 | 
 | ||||||
|         amount := amount - len(buffer.input_buffer); |     // Calculate proper line/col values | ||||||
|         runtime.clear(&buffer.input_buffer); |     it := new_file_buffer_iter_with_cursor(buffer, buffer.history.cursor); | ||||||
|  |     iterate_file_buffer_reverse(&it) | ||||||
| 
 | 
 | ||||||
|         // Calculate proper line/col values |     delete_text(buffer_piece_table(buffer), &buffer.history.cursor.index) | ||||||
|         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) |     buffer.history.cursor.line = it.cursor.line | ||||||
|  |     buffer.history.cursor.col = it.cursor.col | ||||||
| 
 | 
 | ||||||
|         buffer.history.cursor.line = it.cursor.line |     if reparse_buffer { | ||||||
|         buffer.history.cursor.col = it.cursor.col |         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 } |     buffer.flags += { .UnsavedChanges } | ||||||
| 
 | 
 | ||||||
|     selection^ = swap_selections(selection^) |     selection^ = swap_selections(selection^) | ||||||
|     delete_text_in_span(buffer_piece_table(buffer), &selection.start.index, &selection.end.index) |     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 |     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}; | 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) { | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -61,25 +61,26 @@ update_glyph_buffer_from_file_buffer :: proc(buffer: ^FileBuffer, width, height: | ||||||
|         // don't render past the screen |         // don't render past the screen | ||||||
|         if rendered_line >= begin && screen_line >= buffer.glyphs.height { break; } |         if rendered_line >= begin && screen_line >= buffer.glyphs.height { break; } | ||||||
| 
 | 
 | ||||||
|         // render INSERT mode text into glyph buffer |         // 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) { |         // 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) { | ||||||
|                 screen_line = rendered_line - begin; |         //     for k in 0..<len(buffer.input_buffer) { | ||||||
|  |         //         screen_line = rendered_line - begin; | ||||||
| 
 | 
 | ||||||
|                 if buffer.input_buffer[k] == '\n' { |         //         if buffer.input_buffer[k] == '\n' { | ||||||
|                     rendered_col = 0; |         //             rendered_col = 0; | ||||||
|                     rendered_line += 1; |         //             rendered_line += 1; | ||||||
|                     continue; |         //             continue; | ||||||
|                 } |         //         } | ||||||
| 
 | 
 | ||||||
|                 if rendered_line >= begin && rendered_col < buffer.glyphs.width { |         //         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].color = .Foreground; | ||||||
|                     buffer.glyphs.buffer[rendered_col + screen_line * buffer.glyphs.width].codepoint = buffer.input_buffer[k]; |         //             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; |         screen_line = rendered_line - begin; | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -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) { | logger_proc :: proc(data: rawptr, level: runtime.Logger_Level, text: string, options: runtime.Logger_Options, location := #caller_location) { | ||||||
|     buffer := cast(^FileBuffer)data; |     buffer := cast(^FileBuffer)data; | ||||||
| 
 | 
 | ||||||
|    if .Level in options { |     // FIXME | ||||||
|        insert_content(buffer, transmute([]u8)(Level_Header[level]), true); |     // if .Level in options { | ||||||
|    }  |     //     insert_content(buffer, transmute([]u8)(Level_Header[level]), true); | ||||||
|  |     // }  | ||||||
| 
 | 
 | ||||||
|    insert_content(buffer, transmute([]u8)(text), true); |     // insert_content(buffer, transmute([]u8)(text), true); | ||||||
|    insert_content(buffer, {'\n'}, true); |     // insert_content(buffer, {'\n'}, true); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -3,6 +3,8 @@ package core | ||||||
| PieceTable :: struct { | PieceTable :: struct { | ||||||
|     original_content: []u8, |     original_content: []u8, | ||||||
|     added_content: [dynamic]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, |     chunks: [dynamic][]u8, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -158,7 +160,19 @@ insert_text :: proc(t: ^PieceTable, to_be_inserted: []u8, index: PieceTableIndex | ||||||
|     if index.char_index == 0 { |     if index.char_index == 0 { | ||||||
|         // insertion happening in beginning of content slice |         // 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 { |     else { | ||||||
|         // insertion is happening in middle of content slice |         // insertion is happening in middle of content slice | ||||||
|  |  | ||||||
|  | @ -25,24 +25,6 @@ FileBuffer :: core.FileBuffer; | ||||||
| 
 | 
 | ||||||
| state := core.State {}; | 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 { | ui_font_width :: proc() -> i32 { | ||||||
|     return i32(state.source_font_width); |     return i32(state.source_font_width); | ||||||
| } | } | ||||||
|  | @ -286,6 +268,7 @@ main :: proc() { | ||||||
|         log.error("SDL failed to initialize:", sdl2.GetError()); |         log.error("SDL failed to initialize:", sdl2.GetError()); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |     defer sdl2.Quit() | ||||||
| 
 | 
 | ||||||
|     if ttf.Init() < 0 { |     if ttf.Init() < 0 { | ||||||
|         log.error("SDL_TTF failed to initialize:", ttf.GetError()); |         log.error("SDL_TTF failed to initialize:", ttf.GetError()); | ||||||
|  | @ -443,22 +426,49 @@ main :: proc() { | ||||||
|                                     case .ESCAPE: { |                                     case .ESCAPE: { | ||||||
|                                         state.mode = .Normal; |                                         state.mode = .Normal; | ||||||
| 
 | 
 | ||||||
|                                         core.insert_content(buffer, buffer.input_buffer[:]); |                                         // core.insert_content(buffer, buffer.input_buffer[:]); | ||||||
|                                         runtime.clear(&buffer.input_buffer); |                                         // runtime.clear(&buffer.input_buffer); | ||||||
|  |                                         core.move_cursor_left(buffer) | ||||||
| 
 | 
 | ||||||
|                                         sdl2.StopTextInput(); |                                         sdl2.StopTextInput(); | ||||||
|  | 
 | ||||||
|  |                                         ts.parse_buffer(&buffer.tree, core.tree_sitter_file_buffer_input(buffer)) | ||||||
|                                     } |                                     } | ||||||
|                                     case .TAB: { |                                     case .TAB: { | ||||||
|                                         // TODO: change this to insert a tab character |                                         // TODO: change this to insert a tab character | ||||||
|                                         for _ in 0..<4 { |                                         // for _ in 0..<4 { | ||||||
|                                             append(&buffer.input_buffer, ' '); |                                         //     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: { |                                     case .BACKSPACE: { | ||||||
|                                         core.delete_content(buffer, 1); |                                         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: { |                                     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; |                                     break; | ||||||
|                                 } |                                 } | ||||||
| 
 | 
 | ||||||
|                                 if char >= 32 && char <= 125 && len(buffer.input_buffer) < 1024-1 { |                                 if char >= 32 && char <= 125 { | ||||||
|                                     append(&buffer.input_buffer, u8(char)); |                                     // append(&buffer.input_buffer, u8(char)); | ||||||
|  |                                     core.insert_content(buffer, []u8{char}) | ||||||
|                                 } |                                 } | ||||||
|                             } |                             } | ||||||
| 
 | 
 | ||||||
|  | @ -488,20 +499,6 @@ main :: proc() { | ||||||
| 
 | 
 | ||||||
|         draw(&state); |         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); |         runtime.free_all(context.temp_allocator); | ||||||
|     } |     } | ||||||
| 
 |  | ||||||
|     sdl2.Quit(); |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -188,6 +188,25 @@ file_buffer_go_actions :: proc(input_map: ^core.InputActions) { | ||||||
|     }, "move to end of line"); |     }, "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) { | file_buffer_input_actions :: proc(input_map: ^core.InputActions) { | ||||||
|     // Cursor Movement |     // Cursor Movement | ||||||
|     { |     { | ||||||
|  | @ -273,6 +292,10 @@ file_buffer_input_actions :: proc(input_map: ^core.InputActions) { | ||||||
|     file_buffer_go_actions(&go_actions); |     file_buffer_go_actions(&go_actions); | ||||||
|     core.register_key_action(input_map, .G, go_actions, "Go commands"); |     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) { |     core.register_key_action(input_map, .V, proc(state: ^core.State, user_data: rawptr) { | ||||||
|         buffer := &(&(transmute(^core.Panel)user_data).type.(core.FileBufferPanel)).buffer |         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 { |             if state.yank_register.whole_line { | ||||||
|                 core.insert_content(buffer, []u8{'\n'}); |                 core.insert_content(buffer, []u8{'\n'}); | ||||||
|                 core.paste_register(state, state.yank_register, buffer) |                 core.paste_register(state, state.yank_register, buffer) | ||||||
|                 core.insert_content(buffer, []u8{'\n'}); |                 core.insert_content(buffer, []u8{'\n'}, reparse_buffer = true); | ||||||
|             } else { |             } else { | ||||||
|                 core.paste_register(state, state.yank_register, buffer) |                 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) |         core.push_new_snapshot(&buffer.history) | ||||||
| 
 | 
 | ||||||
|         if buffer := buffer; buffer != nil { |         if buffer := buffer; buffer != nil { | ||||||
|             core.move_cursor_end_of_line(buffer, false); |             core.move_cursor_end_of_line(buffer); | ||||||
|             runtime.clear(&buffer.input_buffer) |  | ||||||
|              |              | ||||||
|             append(&buffer.input_buffer, '\n') |             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 | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             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; |             state.mode = .Insert; | ||||||
| 
 | 
 | ||||||
|  | @ -511,9 +547,8 @@ file_buffer_text_input_actions :: proc(input_map: ^core.InputActions) { | ||||||
|             core.push_new_snapshot(&buffer.history) |             core.push_new_snapshot(&buffer.history) | ||||||
| 
 | 
 | ||||||
|             if state.yank_register.whole_line { |             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.insert_content(buffer, []u8{'\n'}); | ||||||
|                 core.move_cursor_right(buffer, false); |  | ||||||
|             } else { |             } else { | ||||||
|                 core.move_cursor_right(buffer) |                 core.move_cursor_right(buffer) | ||||||
|             } |             } | ||||||
|  |  | ||||||
|  | @ -21,7 +21,7 @@ open_grep_panel :: proc(state: ^core.State) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| make_grep_panel :: proc() -> core.Panel { | 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 { |         if panel_state.query_region.arena != nil { | ||||||
|             mem.end_arena_temp_memory(panel_state.query_region) |             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) |         context.allocator = mem.arena_allocator(&panel_state.query_arena) | ||||||
| 
 | 
 | ||||||
|         rs_results := grep( |         rs_results := grep( | ||||||
|             strings.clone_to_cstring(query), |             strings.clone_to_cstring(core.buffer_to_string(buffer)), | ||||||
|             strings.clone_to_cstring(directory) |             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) { |         on_buffer_input = proc(panel: ^core.Panel, state: ^core.State) { | ||||||
|             if panel_state, ok := &panel.type.(core.GrepPanel); ok { |             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) { |         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 |                                 max_results := container_height / 16 | ||||||
| 
 | 
 | ||||||
|                                 for result, i in panel_state.query_results { |                                 for result, i in panel_state.query_results { | ||||||
|                                     if i > max_results { |                                     if i >= max_results { | ||||||
|                                         break |                                         break | ||||||
|                                     } |                                     } | ||||||
| 
 | 
 | ||||||
|  | @ -204,8 +204,14 @@ make_grep_panel :: proc() -> core.Panel { | ||||||
|                                             style.background_color = .Background2 |                                             style.background_color = .Background2 | ||||||
|                                         } |                                         } | ||||||
| 
 | 
 | ||||||
|                                         ui.open_element(s, result.file_path[len(state.directory):], {}, style) |                                         if len(result.file_path) > 0 { | ||||||
|                                         ui.close_element(s) |                                             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) | ||||||
|  |                                         } | ||||||
|                                     } |                                     } | ||||||
|                                 } |                                 } | ||||||
|                             } |                             } | ||||||
|  |  | ||||||
|  | @ -270,7 +270,6 @@ insert_before_slice :: proc(t: ^testing.T) { | ||||||
|     run_text_insertion(&e, " rich") |     run_text_insertion(&e, " rich") | ||||||
| 
 | 
 | ||||||
|     expect_line_col(t, buffer.history.cursor, 0, 20) |     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)) |     contents := buffer_to_string(core.current_buffer(&e)) | ||||||
|     defer delete(contents) |     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(.BACKSPACE), 3) | ||||||
|     run_input_multiple(&e, press_key(.ESCAPE), 1) |     run_input_multiple(&e, press_key(.ESCAPE), 1) | ||||||
| 
 | 
 | ||||||
|     expect_line_col(t, buffer.history.cursor, 0, 17) |     expect_line_col(t, buffer.history.cursor, 0, 16) | ||||||
|     expect_cursor_index(t, buffer.history.cursor, 3, 0) |  | ||||||
| 
 | 
 | ||||||
|     contents := buffer_to_string(core.current_buffer(&e)) |     contents := buffer_to_string(core.current_buffer(&e)) | ||||||
|     defer delete(contents) |     defer delete(contents) | ||||||
|  | @ -387,15 +385,14 @@ delete_across_slices :: proc(t: ^testing.T) { | ||||||
|     run_input_multiple(&e, press_key(.ESCAPE), 1) |     run_input_multiple(&e, press_key(.ESCAPE), 1) | ||||||
| 
 | 
 | ||||||
|     // Move right, passed the 'h' on to the space before 'world!' |     // 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 |     // Remove the ' h', which consists of two content slices | ||||||
|     run_input_multiple(&e, press_key(.I), 1) |     run_input_multiple(&e, press_key(.I), 1) | ||||||
|     run_input_multiple(&e, press_key(.BACKSPACE), 2) |     run_input_multiple(&e, press_key(.BACKSPACE), 2) | ||||||
|     run_input_multiple(&e, press_key(.ESCAPE), 1) |     run_input_multiple(&e, press_key(.ESCAPE), 1) | ||||||
| 
 | 
 | ||||||
|     expect_line_col(t, buffer.history.cursor, 0, 16) |     expect_line_col(t, buffer.history.cursor, 0, 15) | ||||||
|     expect_cursor_index(t, buffer.history.cursor, 2, 0) |  | ||||||
| 
 | 
 | ||||||
|     contents := buffer_to_string(core.current_buffer(&e)) |     contents := buffer_to_string(core.current_buffer(&e)) | ||||||
|     defer delete(contents) |     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(.A), 1) | ||||||
|     run_input_multiple(&e, press_key(.ESCAPE), 1) |     run_input_multiple(&e, press_key(.ESCAPE), 1) | ||||||
| 
 | 
 | ||||||
|     expect_line_col(t, buffer.history.cursor, 0, 5) |     expect_line_col(t, buffer.history.cursor, 0, 4) | ||||||
|     expect_cursor_index(t, buffer.history.cursor, 1, 0) |  | ||||||
| 
 | 
 | ||||||
|     run_input_multiple(&e, press_key(.A), 1) |     run_input_multiple(&e, press_key(.A), 1) | ||||||
|     run_input_multiple(&e, press_key(.ESCAPE), 1) |     run_input_multiple(&e, press_key(.ESCAPE), 1) | ||||||
| 
 | 
 | ||||||
|     expect_line_col(t, buffer.history.cursor, 0, 5) |     expect_line_col(t, buffer.history.cursor, 0, 4) | ||||||
|     expect_cursor_index(t, buffer.history.cursor, 1, 0) |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @(test) | @(test) | ||||||
|  | @ -667,15 +662,11 @@ insert_line_under_current :: proc(t: ^testing.T) { | ||||||
|     // Insert line below and enter insert mode |     // Insert line below and enter insert mode | ||||||
|     run_input_multiple(&e, press_key(.O), 1) |     run_input_multiple(&e, press_key(.O), 1) | ||||||
| 
 | 
 | ||||||
|     // Technically the cursor is still on the first line, because the `input_buffer` |     expect_line_col(t, buffer.history.cursor, 1, 0) | ||||||
|     // 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) |  | ||||||
| 
 | 
 | ||||||
|     run_text_insertion(&e, "This is the second line") |     run_text_insertion(&e, "This is the second line") | ||||||
| 
 | 
 | ||||||
|     expect_line_col(t, buffer.history.cursor, 1, 22) |     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)) |     contents := buffer_to_string(core.current_buffer(&e)) | ||||||
|     defer delete(contents) |     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) |     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_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 { |         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 { |                             #partial switch key.key { | ||||||
|                                 case .ESCAPE: { |                                 case .ESCAPE: { | ||||||
|                                     state.mode = .Normal; |                                     state.mode = .Normal; | ||||||
| 
 |                                     core.move_cursor_left(buffer) | ||||||
|                                     core.insert_content(buffer, buffer.input_buffer[:]); |  | ||||||
|                                     runtime.clear(&buffer.input_buffer); |  | ||||||
|                                 } |                                 } | ||||||
|                                 case .TAB: { |                                 case .TAB: { | ||||||
|                                     // TODO: change this to insert a tab character |                                     // TODO: change this to insert a tab character | ||||||
|                                     for _ in 0..<4 { |                                     core.insert_content(buffer, transmute([]u8)string("    ")) | ||||||
|                                         append(&buffer.input_buffer, ' '); | 
 | ||||||
|  |                                     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: { |                                 case .BACKSPACE: { | ||||||
|                                     core.delete_content(buffer, 1); |                                     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: { |                                 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; |                             break; | ||||||
|                         } |                         } | ||||||
| 
 | 
 | ||||||
|                         if char == '\n' || (char >= 32 && char <= 125 && len(buffer.input_buffer) < 1024-1) { |                         if char == '\n' || (char >= 32 && char <= 125) { | ||||||
|                             append(&buffer.input_buffer, u8(char)); |                             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); |     runtime.free_all(context.temp_allocator); | ||||||
| } | } | ||||||
|  |  | ||||||
							
								
								
									
										50
									
								
								todo.md
								
								
								
								
							
							
						
						
									
										50
									
								
								todo.md
								
								
								
								
							|  | @ -1,12 +1,13 @@ | ||||||
| # Bugs | # Bugs | ||||||
| - 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 | - Scrolling past end/beginning of grep results panics | ||||||
| - Scrolling past end/beginning of results panics | - "change selection" not pushing snapshot | ||||||
|  | - Don't allow panel navigation in grep panel | ||||||
| 
 | 
 | ||||||
| # Visual QOL | # Visual QOL | ||||||
| - Split grep search results into a table to avoid funky unaligned text | - Split grep search results into a table to avoid funky unaligned text | ||||||
| 
 | 
 | ||||||
| # Planned Features | # TODO | ||||||
| - [ ] Jump List | - [ ] Jump List | ||||||
| - [x] Use grouped lifetimes exclusively for memory allocation/freeing | - [x] Use grouped lifetimes exclusively for memory allocation/freeing | ||||||
| - [ ] Highlight which panel is currently active | - [ ] Highlight which panel is currently active | ||||||
|  | @ -16,7 +17,6 @@ | ||||||
|     - [ ] Finish writing tests for all current user actions |     - [ ] Finish writing tests for all current user actions | ||||||
| - Vim-like Macro replays | - Vim-like Macro replays | ||||||
| - [ ] Simple File Search (vim /) | - [ ] Simple File Search (vim /) | ||||||
| - [ ] Auto-indent |  | ||||||
| - Modify input system to allow for keybinds that take input | - Modify input system to allow for keybinds that take input | ||||||
|     - Vim's f and F movement commands |     - Vim's f and F movement commands | ||||||
|     - Vim's r command |     - Vim's r command | ||||||
|  | @ -29,26 +29,34 @@ | ||||||
|         - [ ] In-line errors |         - [ ] In-line errors | ||||||
|     - [ ] Go-to Definition/ |     - [ ] Go-to Definition/ | ||||||
|     - [ ] Find references |     - [ ] Find references | ||||||
| - Re-implement lost features from Plugins | - [ ] Integrate tree-sitter | ||||||
|     - [ ] Integrate tree-sitter |     - [x] Syntax Highlighting | ||||||
|         - [x] Syntax Highlighting |     - [ ] Auto Setup Parsers | ||||||
|         - [ ] Auto Setup Parsers |         - [ ] Download parser | ||||||
|             - [ ] Download parser |         - [ ] Compile/"Install" | ||||||
|             - [ ] Compile/"Install" |     - [ ] Auto-indent | ||||||
|         - [ ] Auto-indent? |         - [x] Infer indent with similar lines | ||||||
|     - [ ] Bootleg Telescope |         - [x] Infer indent with C-style scopes (languages using '{') | ||||||
|         - [ ] Grepping Files |         - [ ] Infer indent inside multi-line function calls (again C-style) | ||||||
|             - [x] Query across project |         - [ ] Somehow use tree-sitter to be language agnostic (not sure this is even possible with TS) | ||||||
|             - [x] Open file in new buffer | - [ ] Bootleg Telescope | ||||||
|             - [x] Open file in new buffer at found location |     - [ ] Grepping Files | ||||||
|             - [ ] Preview file with context |         - [x] Query across project | ||||||
|                 - [x] Show Context |         - [x] Open file in new buffer | ||||||
|                 - [ ] Properly show lines numbers |         - [x] Open file in new buffer at found location | ||||||
|                 - [ ] Don't overlap result list with file preview |         - [ ] Preview file with context | ||||||
|         - [ ] Open Buffer Search |             - [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) | - Re-write the UI (again) | ||||||
|     - [x] New UI |     - [x] New UI | ||||||
|     - [ ] Styling |     - [ ] Styling | ||||||
|  |         - [x] Background colors | ||||||
|  |         - [x] Borders + Border Color | ||||||
|  |         - [ ] Rounded corners? | ||||||
|  |         - [ ] Gradients? | ||||||
| - Undo/Redo | - Undo/Redo | ||||||
|     - [x] Basic/Naive Undo/Redo |     - [x] Basic/Naive Undo/Redo | ||||||
|     - [ ] Interface for undo-able actions |     - [ ] Interface for undo-able actions | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue