Compare commits
3 Commits
6b4d9f0cda
...
722f05be61
Author | SHA1 | Date |
---|---|---|
|
722f05be61 | |
|
f6d7dc0db0 | |
|
f8ba2e587d |
|
@ -102,10 +102,12 @@ FileBufferPanel :: struct {
|
||||||
|
|
||||||
GrepPanel :: struct {
|
GrepPanel :: struct {
|
||||||
query_arena: mem.Arena,
|
query_arena: mem.Arena,
|
||||||
|
query_region: mem.Arena_Temp_Memory,
|
||||||
buffer: int,
|
buffer: int,
|
||||||
selected_result: int,
|
selected_result: int,
|
||||||
search_query: string,
|
search_query: string,
|
||||||
query_results: []GrepQueryResult,
|
query_results: []GrepQueryResult,
|
||||||
|
glyphs: GlyphBuffer,
|
||||||
}
|
}
|
||||||
|
|
||||||
GrepQueryResult :: struct {
|
GrepQueryResult :: struct {
|
||||||
|
|
|
@ -43,11 +43,6 @@ Selection :: struct {
|
||||||
end: Cursor,
|
end: Cursor,
|
||||||
}
|
}
|
||||||
|
|
||||||
Glyph :: struct {
|
|
||||||
codepoint: u8,
|
|
||||||
color: theme.PaletteColor,
|
|
||||||
}
|
|
||||||
|
|
||||||
FileBuffer :: struct {
|
FileBuffer :: struct {
|
||||||
allocator: mem.Allocator,
|
allocator: mem.Allocator,
|
||||||
|
|
||||||
|
@ -63,9 +58,7 @@ FileBuffer :: struct {
|
||||||
added_content: [dynamic]u8,
|
added_content: [dynamic]u8,
|
||||||
content_slices: [dynamic][]u8,
|
content_slices: [dynamic][]u8,
|
||||||
|
|
||||||
glyph_buffer_width: int,
|
glyphs: GlyphBuffer,
|
||||||
glyph_buffer_height: int,
|
|
||||||
glyph_buffer: [dynamic]Glyph,
|
|
||||||
|
|
||||||
input_buffer: [dynamic]u8,
|
input_buffer: [dynamic]u8,
|
||||||
}
|
}
|
||||||
|
@ -527,9 +520,12 @@ move_cursor_down :: proc(buffer: ^FileBuffer, amount: int = 1, cursor: Maybe(^Cu
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if it.hit_end {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
line_length := file_buffer_line_length(buffer, it.cursor.index);
|
line_length := file_buffer_line_length(buffer, it.cursor.index);
|
||||||
if it.cursor.col < line_length && it.cursor.col < current_col {
|
if it.cursor.col < line_length-1 && it.cursor.col < current_col {
|
||||||
for _ in iterate_file_buffer(&it) {
|
for _ in iterate_file_buffer(&it) {
|
||||||
if it.cursor.col >= line_length-1 || it.cursor.col >= current_col {
|
if it.cursor.col >= line_length-1 || it.cursor.col >= current_col {
|
||||||
break;
|
break;
|
||||||
|
@ -705,10 +701,7 @@ new_virtual_file_buffer :: proc(allocator: mem.Allocator) -> FileBuffer {
|
||||||
added_content = make([dynamic]u8, 0, 1024*1024),
|
added_content = make([dynamic]u8, 0, 1024*1024),
|
||||||
content_slices = make([dynamic][]u8, 0, 1024*1024),
|
content_slices = make([dynamic][]u8, 0, 1024*1024),
|
||||||
|
|
||||||
glyph_buffer_width = width,
|
glyphs = make_glyph_buffer(width, height),
|
||||||
glyph_buffer_height = height,
|
|
||||||
glyph_buffer = make([dynamic]Glyph, width*height, width*height),
|
|
||||||
|
|
||||||
input_buffer = make([dynamic]u8, 0, 1024),
|
input_buffer = make([dynamic]u8, 0, 1024),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -760,10 +753,7 @@ new_file_buffer :: proc(allocator: mem.Allocator, file_path: string, base_dir: s
|
||||||
added_content = make([dynamic]u8, 0, 1024*1024),
|
added_content = make([dynamic]u8, 0, 1024*1024),
|
||||||
content_slices = make([dynamic][]u8, 0, 1024*1024),
|
content_slices = make([dynamic][]u8, 0, 1024*1024),
|
||||||
|
|
||||||
glyph_buffer_width = width,
|
glyphs = make_glyph_buffer(width, height),
|
||||||
glyph_buffer_height = height,
|
|
||||||
glyph_buffer = make([dynamic]Glyph, width*height, width*height),
|
|
||||||
|
|
||||||
input_buffer = make([dynamic]u8, 0, 1024),
|
input_buffer = make([dynamic]u8, 0, 1024),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -809,11 +799,12 @@ next_buffer :: proc(state: ^State, prev_buffer: ^int) -> int {
|
||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: replace this with arena for the file buffer
|
||||||
free_file_buffer :: proc(buffer: ^FileBuffer) {
|
free_file_buffer :: proc(buffer: ^FileBuffer) {
|
||||||
delete(buffer.original_content);
|
delete(buffer.original_content);
|
||||||
delete(buffer.added_content);
|
delete(buffer.added_content);
|
||||||
delete(buffer.content_slices);
|
delete(buffer.content_slices);
|
||||||
delete(buffer.glyph_buffer);
|
delete(buffer.glyphs.buffer);
|
||||||
delete(buffer.input_buffer);
|
delete(buffer.input_buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -827,9 +818,9 @@ color_character :: proc(buffer: ^FileBuffer, start: Cursor, end: Cursor, palette
|
||||||
start.line -= buffer.top_line;
|
start.line -= buffer.top_line;
|
||||||
}
|
}
|
||||||
|
|
||||||
if end.line >= buffer.top_line + buffer.glyph_buffer_height {
|
if end.line >= buffer.top_line + buffer.glyphs.height {
|
||||||
end.line = buffer.glyph_buffer_height - 1;
|
end.line = buffer.glyphs.height - 1;
|
||||||
end.col = buffer.glyph_buffer_width - 1;
|
end.col = buffer.glyphs.width - 1;
|
||||||
} else {
|
} else {
|
||||||
end.line -= buffer.top_line;
|
end.line -= buffer.top_line;
|
||||||
}
|
}
|
||||||
|
@ -839,72 +830,19 @@ color_character :: proc(buffer: ^FileBuffer, start: Cursor, end: Cursor, palette
|
||||||
end_col := end.col;
|
end_col := end.col;
|
||||||
if j > start.line && j < end.line {
|
if j > start.line && j < end.line {
|
||||||
start_col = 0;
|
start_col = 0;
|
||||||
end_col = buffer.glyph_buffer_width;
|
end_col = buffer.glyphs.width;
|
||||||
} else if j < end.line {
|
} else if j < end.line {
|
||||||
end_col = buffer.glyph_buffer_width;
|
end_col = buffer.glyphs.width;
|
||||||
} else if j > start.line && j == end.line {
|
} else if j > start.line && j == end.line {
|
||||||
start_col = 0;
|
start_col = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
for i in start_col..<math.min(end_col+1, buffer.glyph_buffer_width) {
|
for i in start_col..<math.min(end_col+1, buffer.glyphs.width) {
|
||||||
buffer.glyph_buffer[i + j * buffer.glyph_buffer_width].color = palette_index;
|
buffer.glyphs.buffer[i + j * buffer.glyphs.width].color = palette_index;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
update_glyph_buffer :: proc(buffer: ^FileBuffer) {
|
|
||||||
for &glyph in buffer.glyph_buffer {
|
|
||||||
glyph = Glyph{};
|
|
||||||
}
|
|
||||||
|
|
||||||
begin := buffer.top_line;
|
|
||||||
rendered_col: int;
|
|
||||||
rendered_line: int;
|
|
||||||
|
|
||||||
it := new_file_buffer_iter(buffer);
|
|
||||||
for character in iterate_file_buffer(&it) {
|
|
||||||
if character == '\r' { continue; }
|
|
||||||
|
|
||||||
screen_line := rendered_line - begin;
|
|
||||||
// don't render past the screen
|
|
||||||
if rendered_line >= begin && screen_line >= buffer.glyph_buffer_height { break; }
|
|
||||||
|
|
||||||
// render INSERT mode text into glyph buffer
|
|
||||||
if len(buffer.input_buffer) > 0 && rendered_line == buffer.cursor.line && rendered_col >= buffer.cursor.col && rendered_col < buffer.cursor.col + len(buffer.input_buffer) {
|
|
||||||
for k in 0..<len(buffer.input_buffer) {
|
|
||||||
screen_line = rendered_line - begin;
|
|
||||||
|
|
||||||
if buffer.input_buffer[k] == '\n' {
|
|
||||||
rendered_col = 0;
|
|
||||||
rendered_line += 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if rendered_line >= begin && rendered_col < buffer.glyph_buffer_width {
|
|
||||||
buffer.glyph_buffer[rendered_col + screen_line * buffer.glyph_buffer_width].color = .Foreground;
|
|
||||||
buffer.glyph_buffer[rendered_col + screen_line * buffer.glyph_buffer_width].codepoint = buffer.input_buffer[k];
|
|
||||||
|
|
||||||
rendered_col += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
screen_line = rendered_line - begin;
|
|
||||||
|
|
||||||
if character == '\n' {
|
|
||||||
rendered_col = 0;
|
|
||||||
rendered_line += 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if rendered_line >= begin && rendered_col < buffer.glyph_buffer_width {
|
|
||||||
buffer.glyph_buffer[rendered_col + screen_line * buffer.glyph_buffer_width] = Glyph { codepoint = character, color = .Foreground };
|
|
||||||
}
|
|
||||||
|
|
||||||
rendered_col += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
draw_file_buffer :: proc(state: ^State, buffer: ^FileBuffer, x: int, y: int, show_line_numbers: bool = true) {
|
draw_file_buffer :: proc(state: ^State, buffer: ^FileBuffer, x: int, y: int, show_line_numbers: bool = true) {
|
||||||
update_glyph_buffer(buffer);
|
update_glyph_buffer(buffer);
|
||||||
|
|
||||||
|
@ -961,7 +899,8 @@ draw_file_buffer :: proc(state: ^State, buffer: ^FileBuffer, x: int, y: int, sho
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
for j in 0..<buffer.glyph_buffer_height {
|
// TODO: replace with glyph_buffer.draw_glyph_buffer
|
||||||
|
for j in 0..<buffer.glyphs.height {
|
||||||
text_y := y + state.source_font_height * j;
|
text_y := y + state.source_font_height * j;
|
||||||
|
|
||||||
if show_line_numbers {
|
if show_line_numbers {
|
||||||
|
@ -969,9 +908,9 @@ draw_file_buffer :: proc(state: ^State, buffer: ^FileBuffer, x: int, y: int, sho
|
||||||
}
|
}
|
||||||
|
|
||||||
line_length := 0;
|
line_length := 0;
|
||||||
for i in 0..<buffer.glyph_buffer_width {
|
for i in 0..<buffer.glyphs.width {
|
||||||
text_x := x + padding + i * state.source_font_width;
|
text_x := x + padding + i * state.source_font_width;
|
||||||
glyph := buffer.glyph_buffer[i + j * buffer.glyph_buffer_width];
|
glyph := buffer.glyphs.buffer[i + j * buffer.glyphs.width];
|
||||||
|
|
||||||
if glyph.codepoint == 0 { break; }
|
if glyph.codepoint == 0 { break; }
|
||||||
line_length += 1;
|
line_length += 1;
|
||||||
|
@ -1015,14 +954,14 @@ update_file_buffer_scroll :: proc(buffer: ^FileBuffer, cursor: Maybe(^Cursor) =
|
||||||
cursor = &buffer.cursor;
|
cursor = &buffer.cursor;
|
||||||
}
|
}
|
||||||
|
|
||||||
if cursor.?.line > (buffer.top_line + buffer.glyph_buffer_height - 5) {
|
if cursor.?.line > (buffer.top_line + buffer.glyphs.height - 5) {
|
||||||
buffer.top_line = math.max(cursor.?.line - buffer.glyph_buffer_height + 5, 0);
|
buffer.top_line = math.max(cursor.?.line - buffer.glyphs.height + 5, 0);
|
||||||
} else if cursor.?.line < (buffer.top_line + 5) {
|
} else if cursor.?.line < (buffer.top_line + 5) {
|
||||||
buffer.top_line = math.max(cursor.?.line - 5, 0);
|
buffer.top_line = math.max(cursor.?.line - 5, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// if buffer.cursor.line > (buffer.top_line + buffer.glyph_buffer_height - 5) {
|
// if buffer.cursor.line > (buffer.top_line + buffer.glyphs.height - 5) {
|
||||||
// buffer.top_line = math.max(buffer.cursor.line - buffer.glyph_buffer_height + 5, 0);
|
// buffer.top_line = math.max(buffer.cursor.line - buffer.glyphs.height + 5, 0);
|
||||||
// } else if buffer.cursor.line < (buffer.top_line + 5) {
|
// } else if buffer.cursor.line < (buffer.top_line + 5) {
|
||||||
// buffer.top_line = math.max(buffer.cursor.line - 5, 0);
|
// buffer.top_line = math.max(buffer.cursor.line - 5, 0);
|
||||||
// }
|
// }
|
||||||
|
|
|
@ -0,0 +1,139 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
import "core:fmt"
|
||||||
|
|
||||||
|
import "../theme"
|
||||||
|
|
||||||
|
GlyphBuffer :: struct {
|
||||||
|
buffer: []Glyph,
|
||||||
|
width: int,
|
||||||
|
height: int,
|
||||||
|
}
|
||||||
|
|
||||||
|
Glyph :: struct {
|
||||||
|
codepoint: u8,
|
||||||
|
color: theme.PaletteColor,
|
||||||
|
}
|
||||||
|
|
||||||
|
make_glyph_buffer :: proc(width, height: int, allocator := context.allocator) -> GlyphBuffer {
|
||||||
|
context.allocator = allocator
|
||||||
|
|
||||||
|
return GlyphBuffer {
|
||||||
|
width = width,
|
||||||
|
height = height,
|
||||||
|
buffer = make([]Glyph, width*height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
update_glyph_buffer_from_file_buffer :: proc(buffer: ^FileBuffer) {
|
||||||
|
for &glyph in buffer.glyphs.buffer {
|
||||||
|
glyph = Glyph{};
|
||||||
|
}
|
||||||
|
|
||||||
|
begin := buffer.top_line;
|
||||||
|
rendered_col: int;
|
||||||
|
rendered_line: int;
|
||||||
|
|
||||||
|
it := new_file_buffer_iter(buffer);
|
||||||
|
for character in iterate_file_buffer(&it) {
|
||||||
|
if character == '\r' { continue; }
|
||||||
|
|
||||||
|
screen_line := rendered_line - begin;
|
||||||
|
// don't render past the screen
|
||||||
|
if rendered_line >= begin && screen_line >= buffer.glyphs.height { break; }
|
||||||
|
|
||||||
|
// render INSERT mode text into glyph buffer
|
||||||
|
if len(buffer.input_buffer) > 0 && rendered_line == buffer.cursor.line && rendered_col >= buffer.cursor.col && rendered_col < buffer.cursor.col + len(buffer.input_buffer) {
|
||||||
|
for k in 0..<len(buffer.input_buffer) {
|
||||||
|
screen_line = rendered_line - begin;
|
||||||
|
|
||||||
|
if buffer.input_buffer[k] == '\n' {
|
||||||
|
rendered_col = 0;
|
||||||
|
rendered_line += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if rendered_line >= begin && rendered_col < buffer.glyphs.width {
|
||||||
|
buffer.glyphs.buffer[rendered_col + screen_line * buffer.glyphs.width].color = .Foreground;
|
||||||
|
buffer.glyphs.buffer[rendered_col + screen_line * buffer.glyphs.width].codepoint = buffer.input_buffer[k];
|
||||||
|
|
||||||
|
rendered_col += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
screen_line = rendered_line - begin;
|
||||||
|
|
||||||
|
if character == '\n' {
|
||||||
|
rendered_col = 0;
|
||||||
|
rendered_line += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if rendered_line >= begin && rendered_col < buffer.glyphs.width {
|
||||||
|
buffer.glyphs.buffer[rendered_col + screen_line * buffer.glyphs.width] = Glyph { codepoint = character, color = .Foreground };
|
||||||
|
}
|
||||||
|
|
||||||
|
rendered_col += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
update_glyph_buffer_from_bytes :: proc(glyphs: ^GlyphBuffer, data: []u8, top_line: int) {
|
||||||
|
for &glyph in glyphs.buffer {
|
||||||
|
glyph = Glyph{};
|
||||||
|
}
|
||||||
|
|
||||||
|
begin := top_line;
|
||||||
|
rendered_col: int;
|
||||||
|
rendered_line: int;
|
||||||
|
|
||||||
|
for character in data {
|
||||||
|
if character == '\r' { continue; }
|
||||||
|
|
||||||
|
screen_line := rendered_line - begin;
|
||||||
|
// don't render past the screen
|
||||||
|
if rendered_line >= begin && screen_line >= glyphs.height { break; }
|
||||||
|
|
||||||
|
screen_line = rendered_line - begin;
|
||||||
|
|
||||||
|
if character == '\n' {
|
||||||
|
rendered_col = 0;
|
||||||
|
rendered_line += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if rendered_line >= begin && rendered_col < glyphs.width {
|
||||||
|
glyphs.buffer[rendered_col + screen_line * glyphs.width] = Glyph { codepoint = character, color = .Foreground };
|
||||||
|
}
|
||||||
|
|
||||||
|
rendered_col += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
update_glyph_buffer :: proc{update_glyph_buffer_from_file_buffer, update_glyph_buffer_from_bytes}
|
||||||
|
|
||||||
|
draw_glyph_buffer :: proc(state: ^State, glyphs: ^GlyphBuffer, x: int, y: int, top_line: int, show_line_numbers: bool = true) {
|
||||||
|
padding := 0;
|
||||||
|
if show_line_numbers {
|
||||||
|
padding = state.source_font_width * 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
for j in 0..<glyphs.height {
|
||||||
|
text_y := y + state.source_font_height * j;
|
||||||
|
|
||||||
|
if show_line_numbers {
|
||||||
|
draw_text(state, fmt.tprintf("%d", top_line + j + 1), x, text_y);
|
||||||
|
}
|
||||||
|
|
||||||
|
line_length := 0;
|
||||||
|
for i in 0..<glyphs.width {
|
||||||
|
text_x := x + padding + i * state.source_font_width;
|
||||||
|
glyph := glyphs.buffer[i + j * glyphs.width];
|
||||||
|
|
||||||
|
if glyph.codepoint == 0 { break; }
|
||||||
|
line_length += 1;
|
||||||
|
|
||||||
|
draw_codepoint(state, rune(glyph.codepoint), text_x, text_y, glyph.color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
package input
|
package input
|
||||||
|
|
||||||
|
import "base:runtime"
|
||||||
import "core:log"
|
import "core:log"
|
||||||
|
|
||||||
import "vendor:sdl2"
|
import "vendor:sdl2"
|
||||||
|
@ -220,11 +221,16 @@ register_default_text_input_actions :: proc(input_map: ^core.InputActions) {
|
||||||
// TODO: add shift+o to insert newline above current one
|
// TODO: add shift+o to insert newline above current one
|
||||||
|
|
||||||
core.register_key_action(input_map, .O, proc(state: ^State) {
|
core.register_key_action(input_map, .O, proc(state: ^State) {
|
||||||
core.move_cursor_end_of_line(core.current_buffer(state), false);
|
if buffer := core.current_buffer(state); buffer != nil {
|
||||||
core.insert_content(core.current_buffer(state), []u8{'\n'});
|
core.move_cursor_end_of_line(buffer, false);
|
||||||
state.mode = .Insert;
|
runtime.clear(&buffer.input_buffer)
|
||||||
|
|
||||||
sdl2.StartTextInput();
|
append(&buffer.input_buffer, '\n')
|
||||||
|
|
||||||
|
state.mode = .Insert;
|
||||||
|
|
||||||
|
sdl2.StartTextInput();
|
||||||
|
}
|
||||||
}, "insert mode on newline");
|
}, "insert mode on newline");
|
||||||
|
|
||||||
// Copy-Paste
|
// Copy-Paste
|
||||||
|
|
|
@ -54,8 +54,8 @@ ui_font_height :: proc() -> i32 {
|
||||||
|
|
||||||
draw :: proc(state: ^State) {
|
draw :: proc(state: ^State) {
|
||||||
if buffer := core.current_buffer(state); buffer != nil {
|
if buffer := core.current_buffer(state); buffer != nil {
|
||||||
buffer.glyph_buffer_height = math.min(256, int((state.screen_height - state.source_font_height*2) / state.source_font_height)) + 1;
|
buffer.glyphs.height = math.min(256, int((state.screen_height - state.source_font_height*2) / state.source_font_height)) + 1;
|
||||||
buffer.glyph_buffer_width = math.min(256, int((state.screen_width - state.source_font_width) / state.source_font_width));
|
buffer.glyphs.width = math.min(256, int((state.screen_width - state.source_font_width) / state.source_font_width));
|
||||||
}
|
}
|
||||||
|
|
||||||
render_color := theme.get_palette_color(.Background);
|
render_color := theme.get_palette_color(.Background);
|
||||||
|
@ -168,8 +168,8 @@ ui_file_buffer :: proc(s: ^ui.State, buffer: ^FileBuffer) {
|
||||||
draw_func := proc(state: ^State, e: ui.UI_Element, user_data: rawptr) {
|
draw_func := proc(state: ^State, e: ui.UI_Element, user_data: rawptr) {
|
||||||
buffer := transmute(^FileBuffer)user_data;
|
buffer := transmute(^FileBuffer)user_data;
|
||||||
if buffer != nil {
|
if buffer != nil {
|
||||||
buffer.glyph_buffer_width = e.layout.size.x / state.source_font_width;
|
buffer.glyphs.width = e.layout.size.x / state.source_font_width;
|
||||||
buffer.glyph_buffer_height = e.layout.size.y / state.source_font_height + 1;
|
buffer.glyphs.height = e.layout.size.y / state.source_font_height + 1;
|
||||||
|
|
||||||
core.draw_file_buffer(state, buffer, e.layout.pos.x, e.layout.pos.y);
|
core.draw_file_buffer(state, buffer, e.layout.pos.x, e.layout.pos.y);
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,7 +120,9 @@ close :: proc(state: ^core.State, panel_id: int) {
|
||||||
util.delete(&state.panels, panel_id)
|
util.delete(&state.panels, panel_id)
|
||||||
|
|
||||||
// TODO: keep track of the last active panel instead of focusing back to the first one
|
// TODO: keep track of the last active panel instead of focusing back to the first one
|
||||||
state.current_panel = util.get_first_active_index(&state.panels).?
|
if first_active, ok := util.get_first_active_index(&state.panels).?; ok {
|
||||||
|
state.current_panel = first_active
|
||||||
|
}
|
||||||
|
|
||||||
core.reset_input_map(state)
|
core.reset_input_map(state)
|
||||||
}
|
}
|
||||||
|
@ -152,8 +154,8 @@ render_file_buffer :: proc(state: ^core.State, s: ^ui.State, buffer: ^core.FileB
|
||||||
draw_func := proc(state: ^core.State, e: ui.UI_Element, user_data: rawptr) {
|
draw_func := proc(state: ^core.State, e: ui.UI_Element, user_data: rawptr) {
|
||||||
buffer := transmute(^core.FileBuffer)user_data;
|
buffer := transmute(^core.FileBuffer)user_data;
|
||||||
if buffer != nil {
|
if buffer != nil {
|
||||||
buffer.glyph_buffer_width = e.layout.size.x / state.source_font_width;
|
buffer.glyphs.width = e.layout.size.x / state.source_font_width;
|
||||||
buffer.glyph_buffer_height = e.layout.size.y / state.source_font_height + 1;
|
buffer.glyphs.height = e.layout.size.y / state.source_font_height + 1;
|
||||||
|
|
||||||
core.draw_file_buffer(state, buffer, e.layout.pos.x, e.layout.pos.y);
|
core.draw_file_buffer(state, buffer, e.layout.pos.x, e.layout.pos.y);
|
||||||
}
|
}
|
||||||
|
@ -205,8 +207,8 @@ render_raw_buffer :: proc(state: ^core.State, s: ^ui.State, buffer: ^core.FileBu
|
||||||
draw_func := proc(state: ^core.State, e: ui.UI_Element, user_data: rawptr) {
|
draw_func := proc(state: ^core.State, e: ui.UI_Element, user_data: rawptr) {
|
||||||
buffer := transmute(^core.FileBuffer)user_data;
|
buffer := transmute(^core.FileBuffer)user_data;
|
||||||
if buffer != nil {
|
if buffer != nil {
|
||||||
buffer.glyph_buffer_width = e.layout.size.x / state.source_font_width;
|
buffer.glyphs.width = e.layout.size.x / state.source_font_width;
|
||||||
buffer.glyph_buffer_height = e.layout.size.y / state.source_font_height + 1;
|
buffer.glyphs.height = e.layout.size.y / state.source_font_height + 1;
|
||||||
|
|
||||||
core.draw_file_buffer(state, buffer, e.layout.pos.x, e.layout.pos.y, false);
|
core.draw_file_buffer(state, buffer, e.layout.pos.x, e.layout.pos.y, false);
|
||||||
}
|
}
|
||||||
|
@ -219,6 +221,24 @@ render_raw_buffer :: proc(state: ^core.State, s: ^ui.State, buffer: ^core.FileBu
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
render_glyph_buffer :: proc(state: ^core.State, s: ^ui.State, glyphs: ^core.GlyphBuffer) {
|
||||||
|
draw_func := proc(state: ^core.State, e: ui.UI_Element, user_data: rawptr) {
|
||||||
|
glyphs := transmute(^core.GlyphBuffer)user_data;
|
||||||
|
if glyphs != nil {
|
||||||
|
glyphs.width = e.layout.size.x / state.source_font_width;
|
||||||
|
glyphs.height = e.layout.size.y / state.source_font_height + 1;
|
||||||
|
|
||||||
|
core.draw_glyph_buffer(state, glyphs, e.layout.pos.x, e.layout.pos.y, 0, true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ui.open_element(s, ui.UI_Element_Kind_Custom{fn = draw_func, user_data = transmute(rawptr)glyphs}, {
|
||||||
|
kind = {ui.Grow{}, ui.Grow{}}
|
||||||
|
})
|
||||||
|
ui.close_element(s)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
make_file_buffer_panel :: proc(buffer_index: int) -> core.Panel {
|
make_file_buffer_panel :: proc(buffer_index: int) -> core.Panel {
|
||||||
input_map := core.new_input_map()
|
input_map := core.new_input_map()
|
||||||
|
|
||||||
|
@ -256,15 +276,19 @@ make_file_buffer_panel :: proc(buffer_index: int) -> core.Panel {
|
||||||
|
|
||||||
make_grep_panel :: proc(state: ^core.State) -> core.Panel {
|
make_grep_panel :: proc(state: ^core.State) -> core.Panel {
|
||||||
query_arena: mem.Arena
|
query_arena: mem.Arena
|
||||||
mem.arena_init(&query_arena, make([]u8, 1024*1024, state.ctx.allocator))
|
mem.arena_init(&query_arena, make([]u8, 1024*1024*2, state.ctx.allocator))
|
||||||
|
|
||||||
|
glyphs := core.make_glyph_buffer(256,256, allocator = mem.arena_allocator(&query_arena))
|
||||||
|
|
||||||
input_map := core.new_input_map()
|
input_map := core.new_input_map()
|
||||||
grep_input_buffer := core.new_virtual_file_buffer(context.allocator)
|
grep_input_buffer := core.new_virtual_file_buffer(context.allocator)
|
||||||
runtime.append(&state.buffers, grep_input_buffer)
|
runtime.append(&state.buffers, grep_input_buffer)
|
||||||
|
|
||||||
run_query :: proc(panel_state: ^core.GrepPanel, query: string, directory: string) {
|
run_query :: proc(panel_state: ^core.GrepPanel, query: string, directory: string) {
|
||||||
mem.arena_free_all(&panel_state.query_arena)
|
if panel_state.query_region.arena != nil {
|
||||||
panel_state.query_results = nil
|
mem.end_arena_temp_memory(panel_state.query_region)
|
||||||
|
}
|
||||||
|
panel_state.query_region = mem.begin_arena_temp_memory(&panel_state.query_arena)
|
||||||
|
|
||||||
context.allocator = mem.arena_allocator(&panel_state.query_arena)
|
context.allocator = mem.arena_allocator(&panel_state.query_arena)
|
||||||
|
|
||||||
|
@ -275,6 +299,13 @@ make_grep_panel :: proc(state: ^core.State) -> core.Panel {
|
||||||
|
|
||||||
panel_state.query_results = rs_grep_as_results(&rs_results)
|
panel_state.query_results = rs_grep_as_results(&rs_results)
|
||||||
free_grep_results(rs_results)
|
free_grep_results(rs_results)
|
||||||
|
|
||||||
|
panel_state.selected_result = 0
|
||||||
|
core.update_glyph_buffer_from_bytes(
|
||||||
|
&panel_state.glyphs,
|
||||||
|
transmute([]u8)panel_state.query_results[panel_state.selected_result].file_context,
|
||||||
|
panel_state.query_results[panel_state.selected_result].line,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
core.register_key_action(&input_map.mode[.Normal], .ENTER, proc(state: ^core.State) {
|
core.register_key_action(&input_map.mode[.Normal], .ENTER, proc(state: ^core.State) {
|
||||||
|
@ -308,6 +339,12 @@ make_grep_panel :: proc(state: ^core.State) -> core.Panel {
|
||||||
if panel_state, ok := ¤t_panel.panel_state.(core.GrepPanel); ok {
|
if panel_state, ok := ¤t_panel.panel_state.(core.GrepPanel); ok {
|
||||||
// TODO: bounds checking
|
// TODO: bounds checking
|
||||||
panel_state.selected_result -= 1
|
panel_state.selected_result -= 1
|
||||||
|
|
||||||
|
core.update_glyph_buffer_from_bytes(
|
||||||
|
&panel_state.glyphs,
|
||||||
|
transmute([]u8)panel_state.query_results[panel_state.selected_result].file_context,
|
||||||
|
panel_state.query_results[panel_state.selected_result].line,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, "move selection up");
|
}, "move selection up");
|
||||||
|
@ -318,6 +355,12 @@ make_grep_panel :: proc(state: ^core.State) -> core.Panel {
|
||||||
if panel_state, ok := ¤t_panel.panel_state.(core.GrepPanel); ok {
|
if panel_state, ok := ¤t_panel.panel_state.(core.GrepPanel); ok {
|
||||||
// TODO: bounds checking
|
// TODO: bounds checking
|
||||||
panel_state.selected_result += 1
|
panel_state.selected_result += 1
|
||||||
|
|
||||||
|
core.update_glyph_buffer_from_bytes(
|
||||||
|
&panel_state.glyphs,
|
||||||
|
transmute([]u8)panel_state.query_results[panel_state.selected_result].file_context,
|
||||||
|
panel_state.query_results[panel_state.selected_result].line,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, "move selection down");
|
}, "move selection down");
|
||||||
|
@ -338,6 +381,7 @@ make_grep_panel :: proc(state: ^core.State) -> core.Panel {
|
||||||
query_arena = query_arena,
|
query_arena = query_arena,
|
||||||
buffer = len(state.buffers)-1,
|
buffer = len(state.buffers)-1,
|
||||||
query_results = nil,
|
query_results = nil,
|
||||||
|
glyphs = glyphs,
|
||||||
},
|
},
|
||||||
input_map = input_map,
|
input_map = input_map,
|
||||||
buffer_proc = proc(state: ^core.State, panel_state: ^core.PanelState) -> (buffer: ^core.FileBuffer, ok: bool) {
|
buffer_proc = proc(state: ^core.State, panel_state: ^core.PanelState) -> (buffer: ^core.FileBuffer, ok: bool) {
|
||||||
|
@ -357,75 +401,74 @@ make_grep_panel :: proc(state: ^core.State) -> core.Panel {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
render_proc = proc(state: ^core.State, panel_state: ^core.PanelState) -> (ok: bool) {
|
render_proc = proc(state: ^core.State, panel_state: ^core.PanelState) -> (ok: bool) {
|
||||||
panel_state := panel_state.(core.GrepPanel) or_return;
|
if panel_state, ok := &panel_state.(core.GrepPanel); ok {
|
||||||
|
s := transmute(^ui.State)state.ui
|
||||||
|
|
||||||
s := transmute(^ui.State)state.ui
|
|
||||||
|
|
||||||
ui.open_element(s, nil, {
|
|
||||||
dir = .TopToBottom,
|
|
||||||
kind = {ui.Grow{}, ui.Grow{}}
|
|
||||||
})
|
|
||||||
{
|
|
||||||
// query results and file contents side-by-side
|
|
||||||
ui.open_element(s, nil, {
|
ui.open_element(s, nil, {
|
||||||
dir = .LeftToRight,
|
dir = .TopToBottom,
|
||||||
kind = {ui.Grow{}, ui.Grow{}}
|
kind = {ui.Grow{}, ui.Grow{}}
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
if panel_state.query_results != nil {
|
// query results and file contents side-by-side
|
||||||
// query results
|
ui.open_element(s, nil, {
|
||||||
ui.open_element(s, nil, {
|
dir = .LeftToRight,
|
||||||
dir = .TopToBottom,
|
kind = {ui.Grow{}, ui.Grow{}}
|
||||||
kind = {ui.Grow{}, ui.Grow{}}
|
})
|
||||||
})
|
{
|
||||||
{
|
if panel_state.query_results != nil {
|
||||||
for result, i in panel_state.query_results {
|
// query results
|
||||||
ui.open_element(s, nil, {
|
ui.open_element(s, nil, {
|
||||||
dir = .LeftToRight,
|
dir = .TopToBottom,
|
||||||
kind = {ui.Fit{}, ui.Fit{}},
|
kind = {ui.Grow{}, ui.Grow{}}
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
defer ui.close_element(s)
|
for result, i in panel_state.query_results {
|
||||||
|
ui.open_element(s, nil, {
|
||||||
|
dir = .LeftToRight,
|
||||||
|
kind = {ui.Fit{}, ui.Fit{}},
|
||||||
|
})
|
||||||
|
{
|
||||||
|
defer ui.close_element(s)
|
||||||
|
|
||||||
ui.open_element(s, fmt.tprintf("%v:%v: ", result.line, result.col), {})
|
ui.open_element(s, fmt.tprintf("%v:%v: ", result.line, result.col), {})
|
||||||
ui.close_element(s)
|
ui.close_element(s)
|
||||||
|
|
||||||
// TODO: when styling is implemented, make this look better
|
// TODO: when styling is implemented, make this look better
|
||||||
if panel_state.selected_result == i {
|
if panel_state.selected_result == i {
|
||||||
ui.open_element(s, fmt.tprintf("%s <--", result.file_path), {})
|
ui.open_element(s, fmt.tprintf("%s <--", result.file_path), {})
|
||||||
ui.close_element(s)
|
ui.close_element(s)
|
||||||
} else {
|
} else {
|
||||||
ui.open_element(s, result.file_path, {})
|
ui.open_element(s, result.file_path, {})
|
||||||
ui.close_element(s)
|
ui.close_element(s)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
ui.close_element(s)
|
||||||
ui.close_element(s)
|
|
||||||
|
|
||||||
// file contents
|
// file contents
|
||||||
selected_result := &panel_state.query_results[panel_state.selected_result]
|
selected_result := &panel_state.query_results[panel_state.selected_result]
|
||||||
ui.open_element(s, selected_result.file_context, {
|
render_glyph_buffer(state, s, &panel_state.glyphs)
|
||||||
kind = {ui.Grow{}, ui.Grow{}}
|
}
|
||||||
})
|
}
|
||||||
ui.close_element(s)
|
ui.close_element(s)
|
||||||
|
|
||||||
|
// text input
|
||||||
|
ui.open_element(s, nil, {
|
||||||
|
kind = {ui.Grow{}, ui.Exact(state.source_font_height)}
|
||||||
|
})
|
||||||
|
{
|
||||||
|
defer ui.close_element(s)
|
||||||
|
|
||||||
|
render_raw_buffer(state, s, &state.buffers[panel_state.buffer])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ui.close_element(s)
|
ui.close_element(s)
|
||||||
|
|
||||||
// text input
|
return true
|
||||||
ui.open_element(s, nil, {
|
|
||||||
kind = {ui.Grow{}, ui.Exact(state.source_font_height)}
|
|
||||||
})
|
|
||||||
{
|
|
||||||
defer ui.close_element(s)
|
|
||||||
|
|
||||||
render_raw_buffer(state, s, &state.buffers[panel_state.buffer])
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ui.close_element(s)
|
|
||||||
|
|
||||||
return true
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -49,7 +49,8 @@ impl Match {
|
||||||
let column = value.bytes_range_in_buffer().len() as u64;
|
let column = value.bytes_range_in_buffer().len() as u64;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
text: line,
|
// TODO: only return N-lines of context instead of the entire freakin' buffer
|
||||||
|
text: value.buffer().to_vec(),
|
||||||
path: path.unwrap_or_default(),
|
path: path.unwrap_or_default(),
|
||||||
line_number: value.line_number(),
|
line_number: value.line_number(),
|
||||||
column,
|
column,
|
||||||
|
|
|
@ -322,23 +322,48 @@ delete_across_slices :: 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)
|
@(test)
|
||||||
// insert_line_under_current :: proc(t: ^testing.T) {
|
move_down_next_line_has_shorter_length :: proc(t: ^testing.T) {
|
||||||
// e := new_test_editor()
|
e := new_test_editor()
|
||||||
// setup_empty_buffer(&e)
|
setup_empty_buffer(&e)
|
||||||
|
|
||||||
// buffer := &e.buffers[0]
|
is_ctrl_pressed := false
|
||||||
|
|
||||||
// inputted_text := "Hello, world!\nThis is a new line"
|
buffer := &e.buffers[0]
|
||||||
// expected_text := fmt.aprintf("%v\n", inputted_text)
|
|
||||||
// run_text_insertion(&e, inputted_text)
|
|
||||||
|
|
||||||
// expect_line_col(t, buffer.cursor, 1, 17)
|
run_text_insertion(&e, "012345678\n0")
|
||||||
// expect_cursor_index(t, buffer.cursor, 0, 31)
|
|
||||||
|
|
||||||
// contents := buffer_to_string(core.current_buffer(&e))
|
// Move up to the first line
|
||||||
// testing.expectf(t, contents == expected_text, "got '%v', expected '%v'", contents, expected_text)
|
run_input_multiple(&e, press_key(.K), 1)
|
||||||
// }
|
|
||||||
|
// Move to the end of the line
|
||||||
|
run_inputs(&e, []ArtificialInput{ press_key(.G), press_key(.L)})
|
||||||
|
|
||||||
|
// Move down to the second line
|
||||||
|
run_input_multiple(&e, press_key(.J), 1)
|
||||||
|
|
||||||
|
expect_line_col(t, buffer.cursor, 1, 0)
|
||||||
|
expect_cursor_index(t, buffer.cursor, 0, 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(test)
|
||||||
|
move_down_on_last_line :: proc(t: ^testing.T) {
|
||||||
|
e := new_test_editor()
|
||||||
|
setup_empty_buffer(&e)
|
||||||
|
|
||||||
|
is_ctrl_pressed := false
|
||||||
|
|
||||||
|
buffer := &e.buffers[0]
|
||||||
|
|
||||||
|
run_text_insertion(&e, "012345678")
|
||||||
|
|
||||||
|
// Try to move down
|
||||||
|
run_input_multiple(&e, press_key(.J), 1)
|
||||||
|
|
||||||
|
// Cursor should stay where it is
|
||||||
|
expect_line_col(t, buffer.cursor, 0, 8)
|
||||||
|
expect_cursor_index(t, buffer.cursor, 0, 8)
|
||||||
|
}
|
||||||
|
|
||||||
@(test)
|
@(test)
|
||||||
move_left_at_beginning_of_file :: proc(t: ^testing.T) {
|
move_left_at_beginning_of_file :: proc(t: ^testing.T) {
|
||||||
|
@ -478,6 +503,42 @@ move_to_beginning_of_line_from_start :: proc(t: ^testing.T) {
|
||||||
expect_cursor_index(t, buffer.cursor, 0, 0)
|
expect_cursor_index(t, buffer.cursor, 0, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(test)
|
||||||
|
insert_line_under_current :: proc(t: ^testing.T) {
|
||||||
|
e := new_test_editor()
|
||||||
|
setup_empty_buffer(&e)
|
||||||
|
|
||||||
|
buffer := &e.buffers[0]
|
||||||
|
|
||||||
|
initial_text := "Hello, world!\nThis is a new line"
|
||||||
|
run_text_insertion(&e, initial_text)
|
||||||
|
|
||||||
|
expected_text := "Hello, world!\nThis is the second line\nThis is a new line\n"
|
||||||
|
// ------------- ----------------------
|
||||||
|
// -------------------------
|
||||||
|
// 0 1 3
|
||||||
|
|
||||||
|
// Move cursor up onto the end of "Hello, world!"
|
||||||
|
run_input_multiple(&e, press_key(.K), 1)
|
||||||
|
|
||||||
|
// Insert line below and enter insert mode
|
||||||
|
run_input_multiple(&e, press_key(.O), 1)
|
||||||
|
|
||||||
|
// Technically the cursor is still on the first line, because the `input_buffer`
|
||||||
|
// has been modified but not the actual contents of the filebuffer
|
||||||
|
expect_line_col(t, buffer.cursor, 0, 13)
|
||||||
|
expect_cursor_index(t, buffer.cursor, 0, 13)
|
||||||
|
|
||||||
|
run_text_insertion(&e, "This is the second line")
|
||||||
|
|
||||||
|
expect_line_col(t, buffer.cursor, 1, 22)
|
||||||
|
expect_cursor_index(t, buffer.cursor, 1, 23)
|
||||||
|
|
||||||
|
contents := buffer_to_string(core.current_buffer(&e))
|
||||||
|
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) {
|
||||||
log.infof("running input: %v", input)
|
log.infof("running input: %v", input)
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue