Compare commits
No commits in common. "a70da837fa92b0214ad9a042f68e8101abf59c6f" and "381062518e1ab4f9b91c86b689c79e14344b971a" have entirely different histories.
a70da837fa
...
381062518e
|
|
@ -137,6 +137,8 @@ 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
|
||||||
|
|
@ -169,12 +171,7 @@ 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)
|
||||||
|
|
||||||
|
|
@ -188,7 +185,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, reparse_buffer = true)
|
insert_content(buffer, register.data)
|
||||||
move_cursor_left(buffer)
|
move_cursor_left(buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,8 @@ FileBuffer :: struct {
|
||||||
|
|
||||||
history: FileHistory,
|
history: FileHistory,
|
||||||
glyphs: GlyphBuffer,
|
glyphs: GlyphBuffer,
|
||||||
|
|
||||||
|
input_buffer: [dynamic]u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
BufferFlagSet :: bit_set[BufferFlags]
|
BufferFlagSet :: bit_set[BufferFlags]
|
||||||
|
|
@ -710,6 +712,7 @@ 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;
|
||||||
|
|
@ -773,6 +776,7 @@ 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))
|
||||||
|
|
@ -830,6 +834,7 @@ 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) {
|
||||||
|
|
@ -901,6 +906,26 @@ 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -982,101 +1007,56 @@ scroll_file_buffer :: proc(buffer: ^FileBuffer, dir: ScrollDir, cursor: Maybe(^C
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
insert_content :: proc(buffer: ^FileBuffer, to_be_inserted: []u8, reparse_buffer: bool = false) {
|
insert_content :: proc(buffer: ^FileBuffer, to_be_inserted: []u8, append_to_end: 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
|
index := buffer.history.cursor.index if !append_to_end else new_piece_table_index_from_end(buffer_piece_table(buffer))
|
||||||
|
|
||||||
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)
|
||||||
|
|
||||||
update_file_buffer_index_from_cursor(buffer);
|
if !append_to_end {
|
||||||
move_cursor_right(buffer, false, amt = len(to_be_inserted));
|
update_file_buffer_index_from_cursor(buffer);
|
||||||
|
move_cursor_right(buffer, false, amt = len(to_be_inserted) - 1);
|
||||||
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_buffer_cursor :: proc(buffer: ^FileBuffer, amount: int, reparse_buffer: bool = false) {
|
delete_content_from_buffer_cursor :: proc(buffer: ^FileBuffer, amount: int) {
|
||||||
buffer.flags += { .UnsavedChanges }
|
if amount <= len(buffer.input_buffer) {
|
||||||
|
runtime.resize(&buffer.input_buffer, len(buffer.input_buffer)-amount);
|
||||||
|
} else {
|
||||||
|
buffer.flags += { .UnsavedChanges }
|
||||||
|
|
||||||
// Calculate proper line/col values
|
amount := amount - len(buffer.input_buffer);
|
||||||
it := new_file_buffer_iter_with_cursor(buffer, buffer.history.cursor);
|
runtime.clear(&buffer.input_buffer);
|
||||||
iterate_file_buffer_reverse(&it)
|
|
||||||
|
|
||||||
delete_text(buffer_piece_table(buffer), &buffer.history.cursor.index)
|
// Calculate proper line/col values
|
||||||
|
it := new_file_buffer_iter_with_cursor(buffer, buffer.history.cursor);
|
||||||
|
iterate_file_buffer_reverse(&it)
|
||||||
|
|
||||||
buffer.history.cursor.line = it.cursor.line
|
delete_text(buffer_piece_table(buffer), &buffer.history.cursor.index)
|
||||||
buffer.history.cursor.col = it.cursor.col
|
|
||||||
|
|
||||||
if reparse_buffer {
|
buffer.history.cursor.line = it.cursor.line
|
||||||
ts.parse_buffer(&buffer.tree, tree_sitter_file_buffer_input(buffer))
|
buffer.history.cursor.col = it.cursor.col
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ts.parse_buffer(&buffer.tree, tree_sitter_file_buffer_input(buffer))
|
||||||
}
|
}
|
||||||
|
|
||||||
delete_content_from_selection :: proc(buffer: ^FileBuffer, selection: ^Selection, reparse_buffer: bool = false) {
|
delete_content_from_selection :: proc(buffer: ^FileBuffer, selection: ^Selection) {
|
||||||
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
|
||||||
|
|
||||||
if get_character_at_piece_table_index(buffer_piece_table(buffer), selection.start.index) == '\n' {
|
ts.parse_buffer(&buffer.tree, tree_sitter_file_buffer_input(buffer))
|
||||||
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,26 +61,25 @@ 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; }
|
||||||
|
|
||||||
// NOTE: `input_buffer` doesn't exist anymore, but this is a nice reference for just inserting text within the glyph buffer
|
// 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) {
|
||||||
// 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) {
|
||||||
// for k in 0..<len(buffer.input_buffer) {
|
screen_line = rendered_line - begin;
|
||||||
// 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,11 +32,10 @@ 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;
|
||||||
|
|
||||||
// FIXME
|
if .Level in options {
|
||||||
// if .Level in options {
|
insert_content(buffer, transmute([]u8)(Level_Header[level]), true);
|
||||||
// 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,8 +3,6 @@ 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,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -160,19 +158,7 @@ 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
|
||||||
|
|
||||||
if len(t.chunks) > 1 && index.chunk_index > 0 {
|
inject_at(&t.chunks, index.chunk_index, inserted_slice);
|
||||||
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,6 +25,24 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
@ -268,7 +286,6 @@ 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());
|
||||||
|
|
@ -426,49 +443,22 @@ 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: {
|
||||||
indent := core.get_buffer_indent(buffer)
|
append(&buffer.input_buffer, '\n');
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -480,9 +470,8 @@ main :: proc() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if char >= 32 && char <= 125 {
|
if char >= 32 && char <= 125 && len(buffer.input_buffer) < 1024-1 {
|
||||||
// append(&buffer.input_buffer, u8(char));
|
append(&buffer.input_buffer, u8(char));
|
||||||
core.insert_content(buffer, []u8{char})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -499,6 +488,20 @@ 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,25 +188,6 @@ 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
|
||||||
{
|
{
|
||||||
|
|
@ -292,10 +273,6 @@ 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
|
||||||
|
|
||||||
|
|
@ -451,7 +428,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'}, reparse_buffer = true);
|
core.insert_content(buffer, []u8{'\n'});
|
||||||
} else {
|
} else {
|
||||||
core.paste_register(state, state.yank_register, buffer)
|
core.paste_register(state, state.yank_register, buffer)
|
||||||
}
|
}
|
||||||
|
|
@ -502,23 +479,10 @@ 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);
|
core.move_cursor_end_of_line(buffer, false);
|
||||||
|
runtime.clear(&buffer.input_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
|
|
||||||
}
|
|
||||||
|
|
||||||
if char != '\n' {
|
append(&buffer.input_buffer, '\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;
|
||||||
|
|
||||||
|
|
@ -547,8 +511,9 @@ 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, stop_at_end = false);
|
core.move_cursor_end_of_line(buffer, 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, buffer: ^core.FileBuffer, directory: string) {
|
run_query :: proc(panel_state: ^core.GrepPanel, query: string, 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(core.buffer_to_string(buffer)),
|
strings.clone_to_cstring(query),
|
||||||
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, &panel_state.buffer, state.directory)
|
run_query(panel_state, string(panel_state.buffer.input_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,14 +204,8 @@ make_grep_panel :: proc() -> core.Panel {
|
||||||
style.background_color = .Background2
|
style.background_color = .Background2
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(result.file_path) > 0 {
|
ui.open_element(s, result.file_path[len(state.directory):], {}, style)
|
||||||
ui.open_element(s, result.file_path[len(state.directory):], {}, style)
|
ui.close_element(s)
|
||||||
ui.close_element(s)
|
|
||||||
} else {
|
|
||||||
style.background_color = .BrightRed
|
|
||||||
ui.open_element(s, "BAD FILE DIRECTORY", {}, style)
|
|
||||||
ui.close_element(s)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -270,6 +270,7 @@ 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)
|
||||||
|
|
@ -345,7 +346,8 @@ 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, 16)
|
expect_line_col(t, buffer.history.cursor, 0, 17)
|
||||||
|
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)
|
||||||
|
|
@ -385,14 +387,15 @@ 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), 2)
|
run_input_multiple(&e, press_key(.L), 1)
|
||||||
|
|
||||||
// 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, 15)
|
expect_line_col(t, buffer.history.cursor, 0, 16)
|
||||||
|
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)
|
||||||
|
|
@ -629,12 +632,14 @@ 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, 4)
|
expect_line_col(t, buffer.history.cursor, 0, 5)
|
||||||
|
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, 4)
|
expect_line_col(t, buffer.history.cursor, 0, 5)
|
||||||
|
expect_cursor_index(t, buffer.history.cursor, 1, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
@(test)
|
@(test)
|
||||||
|
|
@ -662,11 +667,15 @@ 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)
|
||||||
|
|
||||||
expect_line_col(t, buffer.history.cursor, 1, 0)
|
// 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)
|
||||||
|
|
||||||
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)
|
||||||
|
|
@ -707,41 +716,6 @@ 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 {
|
||||||
|
|
@ -803,40 +777,21 @@ 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
|
||||||
core.insert_content(buffer, transmute([]u8)string(" "))
|
for _ in 0..<4 {
|
||||||
|
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: {
|
||||||
indent := core.get_buffer_indent(buffer)
|
append(&buffer.input_buffer, '\n');
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -849,8 +804,8 @@ run_editor_frame :: proc(state: ^core.State, input: ArtificialInput, is_ctrl_pre
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if char == '\n' || (char >= 32 && char <= 125) {
|
if char == '\n' || (char >= 32 && char <= 125 && len(buffer.input_buffer) < 1024-1) {
|
||||||
core.insert_content(buffer, []u8{u8(char)})
|
append(&buffer.input_buffer, u8(char));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -864,5 +819,30 @@ 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,13 +1,12 @@
|
||||||
# Bugs
|
# Bugs
|
||||||
- Fix jumping forward a word jumping past consecutive brackets
|
- Fix jumping forward a word jumping past consecutive brackets
|
||||||
- Scrolling past end/beginning of grep results panics
|
- Odd scrolling behavior on small screen heights
|
||||||
- "change selection" not pushing snapshot
|
- Scrolling past end/beginning of results panics
|
||||||
- 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
|
||||||
|
|
||||||
# TODO
|
# Planned Features
|
||||||
- [ ] 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
|
||||||
|
|
@ -17,6 +16,7 @@
|
||||||
- [ ] 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,34 +29,26 @@
|
||||||
- [ ] In-line errors
|
- [ ] In-line errors
|
||||||
- [ ] Go-to Definition/
|
- [ ] Go-to Definition/
|
||||||
- [ ] Find references
|
- [ ] Find references
|
||||||
- [ ] Integrate tree-sitter
|
- Re-implement lost features from Plugins
|
||||||
- [x] Syntax Highlighting
|
- [ ] Integrate tree-sitter
|
||||||
- [ ] Auto Setup Parsers
|
- [x] Syntax Highlighting
|
||||||
- [ ] Download parser
|
- [ ] Auto Setup Parsers
|
||||||
- [ ] Compile/"Install"
|
- [ ] Download parser
|
||||||
- [ ] Auto-indent
|
- [ ] Compile/"Install"
|
||||||
- [x] Infer indent with similar lines
|
- [ ] Auto-indent?
|
||||||
- [x] Infer indent with C-style scopes (languages using '{')
|
- [ ] Bootleg Telescope
|
||||||
- [ ] Infer indent inside multi-line function calls (again C-style)
|
- [ ] Grepping Files
|
||||||
- [ ] Somehow use tree-sitter to be language agnostic (not sure this is even possible with TS)
|
- [x] Query across project
|
||||||
- [ ] Bootleg Telescope
|
- [x] Open file in new buffer
|
||||||
- [ ] Grepping Files
|
- [x] Open file in new buffer at found location
|
||||||
- [x] Query across project
|
- [ ] Preview file with context
|
||||||
- [x] Open file in new buffer
|
- [x] Show Context
|
||||||
- [x] Open file in new buffer at found location
|
- [ ] Properly show lines numbers
|
||||||
- [ ] Preview file with context
|
- [ ] Don't overlap result list with file preview
|
||||||
- [x] Show Context
|
- [ ] Open Buffer Search
|
||||||
- [ ] 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