Move up down and multi cursor

This commit is contained in:
Krzosa Karol
2024-06-22 20:58:29 +02:00
parent 5ba9975c10
commit 48eca55a4c
2 changed files with 140 additions and 69 deletions

View File

@@ -29,16 +29,17 @@ struct Line {
Range range; Range range;
}; };
struct LineAndColumn {
Line line;
int64_t column;
};
struct Edit { struct Edit {
Range range; Range range;
String string; String string;
}; };
// :buffer_initialized // - Buffer should be initialized before use!
// @todo: it's very tempting to ensure that buffer is initialized
// then we know:
// - we always have at least one thing in lines array.
// - I think it would be probably easy enough to enforce
struct Buffer { struct Buffer {
Allocator allocator; Allocator allocator;
char *data[2]; char *data[2];
@@ -228,30 +229,6 @@ String CopyNullTerminated(Allocator allocator, Buffer &buffer, Range range) {
return result; return result;
} }
Line GetLine(Buffer &buffer, int64_t line) {
// :buffer_initialized
// @todo: do I enfore initialized state of the buffer +
// lines array maybe should always have something
Assert(buffer.lines.len);
line = Clamp(line, (int64_t)0, buffer.lines.len - 1);
Range range = buffer.lines[line];
Line result = {line, range};
return result;
}
Line FindLine(Buffer &buffer, int64_t pos) {
For(buffer.lines) {
// The program is doing '<= it.max' so as to include the new line.
// Otherwise this function wouldn't be able to find certain positions.
if (pos >= it.min && pos <= it.max) {
Line result = {buffer.lines.get_index(it), it};
return result;
}
}
return {};
}
int64_t AdjustUTF8Pos(String string, int64_t pos, int64_t direction) { int64_t AdjustUTF8Pos(String string, int64_t pos, int64_t direction) {
for (; pos >= 0 && pos < string.len;) { for (; pos >= 0 && pos < string.len;) {
if (IsUTF8ContinuationByte(string.data[pos])) { if (IsUTF8ContinuationByte(string.data[pos])) {
@@ -329,14 +306,14 @@ void Advance(BufferIter *iter) {
iter->item = GetUTF32(*iter->buffer, iter->pos, &iter->utf8_codepoint_size); iter->item = GetUTF32(*iter->buffer, iter->pos, &iter->utf8_codepoint_size);
} }
BufferIter Iterate(Buffer *buffer, Range range, int64_t direction = ITERATE_FORWARD) { BufferIter Iterate(Buffer &buffer, Range range, int64_t direction = ITERATE_FORWARD) {
Assert(direction == ITERATE_FORWARD || direction == ITERATE_BACKWARD); Assert(direction == ITERATE_FORWARD || direction == ITERATE_BACKWARD);
Assert(!IsUTF8ContinuationByte(GetChar(*buffer, range.min))); Assert(!IsUTF8ContinuationByte(GetChar(buffer, range.min)));
Assert(range.max >= range.min); Assert(range.max >= range.min);
range.min = Clamp(*buffer, range.min); range.min = Clamp(buffer, range.min);
range.max = Clamp(*buffer, range.max); range.max = Clamp(buffer, range.max);
BufferIter result = {buffer, range.min, range.max, direction}; BufferIter result = {&buffer, range.min, range.max, direction};
if (direction == ITERATE_BACKWARD) { if (direction == ITERATE_BACKWARD) {
result.end = range.min; result.end = range.min;
result.pos = range.max; result.pos = range.max;
@@ -346,6 +323,52 @@ BufferIter Iterate(Buffer *buffer, Range range, int64_t direction = ITERATE_FORW
return result; return result;
} }
Line GetLine(Buffer &buffer, int64_t line) {
Assert(buffer.lines.len);
line = Clamp(line, (int64_t)0, buffer.lines.len - 1);
Range range = buffer.lines[line];
Line result = {line, range};
return result;
}
Line FindLine(Buffer &buffer, int64_t pos) {
For(buffer.lines) {
// The program is doing '<= it.max' so as to include the new line.
// Otherwise this function wouldn't be able to find certain positions.
if (pos >= it.min && pos <= it.max) {
Line result = {buffer.lines.get_index(it), it};
return result;
}
}
return {};
}
LineAndColumn FindLineAndColumn(Buffer &buffer, int64_t pos) {
LineAndColumn result = {};
result.column = 1;
result.line = FindLine(buffer, pos);
for (BufferIter iter = Iterate(buffer, result.line.range); IsValid(iter); Advance(&iter)) {
if (iter.pos == pos) {
result.column = iter.codepoint_index;
break;
}
}
return result;
}
int64_t FindPos(Buffer &buffer, int64_t line_number, int64_t column) {
Line line = GetLine(buffer, line_number);
int64_t result = line.range.max;
for (BufferIter iter = Iterate(buffer, line.range); IsValid(iter); Advance(&iter)) {
if (iter.codepoint_index == column) {
result = iter.pos;
break;
}
}
return result;
}
void RunBufferTests() { void RunBufferTests() {
Scratch scratch; Scratch scratch;
{ {
@@ -407,7 +430,7 @@ void RunBufferTests() {
{ {
Array<char> s = {scratch}; Array<char> s = {scratch};
for (BufferIter iter = Iterate(&buffer, {0, 6}); IsValid(iter); Advance(&iter)) { for (BufferIter iter = Iterate(buffer, {0, 6}); IsValid(iter); Advance(&iter)) {
Assert(iter.item < 255); Assert(iter.item < 255);
s.add((char)iter.item); s.add((char)iter.item);
@@ -417,7 +440,7 @@ void RunBufferTests() {
} }
{ {
Array<char> s = {scratch}; Array<char> s = {scratch};
for (BufferIter iter = Iterate(&buffer, {0, 6}, ITERATE_BACKWARD); IsValid(iter); Advance(&iter)) { for (BufferIter iter = Iterate(buffer, {0, 6}, ITERATE_BACKWARD); IsValid(iter); Advance(&iter)) {
Assert(iter.item < 255); Assert(iter.item < 255);
s.add((char)iter.item); s.add((char)iter.item);
@@ -427,7 +450,7 @@ void RunBufferTests() {
} }
{ {
Array<char> s = {scratch}; Array<char> s = {scratch};
for (BufferIter iter = Iterate(&buffer, {0, buffer.len}); IsValid(iter); Advance(&iter)) { for (BufferIter iter = Iterate(buffer, {0, buffer.len}); IsValid(iter); Advance(&iter)) {
Assert(iter.item < 255); Assert(iter.item < 255);
s.add((char)iter.item); s.add((char)iter.item);
@@ -438,7 +461,7 @@ void RunBufferTests() {
} }
{ {
Array<char> s = {scratch}; Array<char> s = {scratch};
for (BufferIter iter = Iterate(&buffer, {0, buffer.len}, ITERATE_BACKWARD); IsValid(iter); Advance(&iter)) { for (BufferIter iter = Iterate(buffer, {0, buffer.len}, ITERATE_BACKWARD); IsValid(iter); Advance(&iter)) {
Assert(iter.item < 255); Assert(iter.item < 255);
s.add((char)iter.item); s.add((char)iter.item);

View File

@@ -69,15 +69,29 @@ void DrawString(Font font, String text, Vector2 position, float fontSize, float
} }
int64_t MoveRight(Buffer &buffer, int64_t pos) { int64_t MoveRight(Buffer &buffer, int64_t pos) {
int64_t result = pos + 1; pos = pos + 1;
result = AdjustUTF8Pos(buffer, result); pos = AdjustUTF8Pos(buffer, pos);
return result; Assert(pos >= 0 && pos <= buffer.len);
return pos;
} }
int64_t MoveLeft(Buffer &buffer, int64_t pos) { int64_t MoveLeft(Buffer &buffer, int64_t pos) {
int64_t result = pos - 1; pos = pos - 1;
result = AdjustUTF8Pos(buffer, result, -1); pos = AdjustUTF8Pos(buffer, pos, -1);
return result; Assert(pos >= 0 && pos <= buffer.len);
return pos;
}
int64_t MoveDown(Buffer &buffer, int64_t pos) {
LineAndColumn info = FindLineAndColumn(buffer, pos);
int64_t new_pos = FindPos(buffer, info.line.number + 1, info.column);
return new_pos;
}
int64_t MoveUp(Buffer &buffer, int64_t pos) {
LineAndColumn info = FindLineAndColumn(buffer, pos);
int64_t new_pos = FindPos(buffer, info.line.number - 1, info.column);
return new_pos;
} }
int main() { int main() {
@@ -99,8 +113,8 @@ int main() {
Window *window = &windows[0]; Window *window = &windows[0];
Buffer *buffer = &window->buffer; Buffer *buffer = &window->buffer;
InitBuffer(buffer); InitBuffer(buffer);
if (0) { if (1) {
for (int i = 0; i < 100; i += 1) { for (int i = 0; i < 5; i += 1) {
Array<Edit> edits = {FrameArena}; Array<Edit> edits = {FrameArena};
AddEdit(&edits, GetEnd(*buffer), Format(FrameArena, "line number: %d\n", i)); AddEdit(&edits, GetEnd(*buffer), Format(FrameArena, "line number: %d\n", i));
ApplyEdits(buffer, edits); ApplyEdits(buffer, edits);
@@ -108,6 +122,7 @@ int main() {
} }
window->cursors.add({}); window->cursors.add({});
window->cursors.add(GetLine(*buffer, 1).range);
} }
Vec2 camera_offset_world_to_render_units = {}; Vec2 camera_offset_world_to_render_units = {};
@@ -123,20 +138,6 @@ int main() {
focused_window->window_world_to_window_units.y -= mouse_wheel; focused_window->window_world_to_window_units.y -= mouse_wheel;
focused_window->window_world_to_window_units.y = ClampBottom(focused_window->window_world_to_window_units.y, 0.f); focused_window->window_world_to_window_units.y = ClampBottom(focused_window->window_world_to_window_units.y, 0.f);
for (int c = GetCharPressed(); c; c = GetCharPressed()) {
String string = "?";
UTF8Result utf8 = UTF32ToUTF8((uint32_t)c);
if (utf8.error == 0) {
string = {(char *)utf8.out_str, (int64_t)utf8.len};
}
Array<Edit> edits = {FrameArena};
For(focused_window->cursors) AddEdit(&edits, it, string);
ApplyEdits(&focused_window->buffer, edits);
For(focused_window->cursors)
it.max = it.min = MoveRight(focused_window->buffer, it.min);
}
if (IsKeyPressed(KEY_LEFT) || IsKeyPressedRepeat(KEY_LEFT)) { if (IsKeyPressed(KEY_LEFT) || IsKeyPressedRepeat(KEY_LEFT)) {
For(focused_window->cursors) { For(focused_window->cursors) {
it.max = it.min = MoveLeft(focused_window->buffer, it.min); it.max = it.min = MoveLeft(focused_window->buffer, it.min);
@@ -147,6 +148,53 @@ int main() {
it.max = it.min = MoveRight(focused_window->buffer, it.min); it.max = it.min = MoveRight(focused_window->buffer, it.min);
} }
} }
if (IsKeyPressed(KEY_DOWN) || IsKeyPressedRepeat(KEY_DOWN)) {
For(focused_window->cursors) {
it.max = it.min = MoveDown(focused_window->buffer, it.min);
}
}
if (IsKeyPressed(KEY_UP) || IsKeyPressedRepeat(KEY_UP)) {
For(focused_window->cursors) {
it.max = it.min = MoveUp(focused_window->buffer, it.min);
}
}
// Merge cursors that overlap
IterRemove(focused_window->cursors) {
IterRemovePrepare(focused_window->cursors);
ForItem(cursor, focused_window->cursors) {
if (&cursor == &it) continue;
if (cursor.min == it.min) {
remove_item = true;
break;
}
}
}
// Handle user input chars
for (int c = GetCharPressed(); c; c = GetCharPressed()) {
String string = "?";
UTF8Result utf8 = UTF32ToUTF8((uint32_t)c);
if (utf8.error == 0) {
string = {(char *)utf8.out_str, (int64_t)utf8.len};
}
Array<Edit> edits = {FrameArena};
For(focused_window->cursors) {
AddEdit(&edits, it, string);
}
ApplyEdits(&focused_window->buffer, edits);
For(focused_window->cursors) {
// Need to update current cursor and all cursors after it.
ForItem(cursor, focused_window->cursors) {
if (cursor.min >= it.min) {
cursor.min = cursor.max = MoveRight(focused_window->buffer, cursor.min);
}
}
}
}
} }
BeginDrawing(); BeginDrawing();
@@ -215,16 +263,16 @@ int main() {
if (font.texture.id == 0) font = GetFontDefault(); if (font.texture.id == 0) font = GetFontDefault();
float textOffsetX = 0.0f; float textOffsetX = 0.0f;
float scaleFactor = font_size / font.baseSize; // Character quad scaling factor float scaleFactor = font_size / font.baseSize; // Character quad scaling factor
for (int64_t i = 0;;) {
bool end_of_buffer = i == window.buffer.len; for (BufferIter iter = Iterate(window.buffer, line_range);; Advance(&iter)) {
bool new_line = i == line_range.max && i != window.buffer.len; bool end_of_buffer = iter.pos == window.buffer.len;
bool in_range = i < text.len; bool new_line = iter.pos == line_range.max;
bool in_range = IsValid(iter);
bool continue_looping = end_of_buffer || new_line || in_range; bool continue_looping = end_of_buffer || new_line || in_range;
if (!continue_looping) break; if (!continue_looping) break;
int codepointByteCount = 1; int codepoint = '\n';
int codepoint = '\n'; if (in_range) codepoint = iter.item;
if (in_range) codepoint = GetCodepointNext(&text.data[i], &codepointByteCount);
int index = GetGlyphIndex(font, codepoint); int index = GetGlyphIndex(font, codepoint);
GlyphInfo *glyph = font.glyphs + index; GlyphInfo *glyph = font.glyphs + index;
@@ -239,12 +287,12 @@ int main() {
// Clip everything that is outside the window and screen // Clip everything that is outside the window and screen
if (CheckCollisionRecs(cell_rectangle, ToRectangle(window_rect_in_render_units_clamped_to_screen))) { if (CheckCollisionRecs(cell_rectangle, ToRectangle(window_rect_in_render_units_clamped_to_screen))) {
row.cells.add({cell_rect, codepoint, line_range.min + i}); row.cells.add({cell_rect, codepoint, iter.pos});
row.rect.max = cell_rect.max; row.rect.max = cell_rect.max;
} }
textOffsetX += x_to_offset_by; textOffsetX += x_to_offset_by;
i += codepointByteCount; // Move text bytes counter to next codepoint if (end_of_buffer || new_line) break;
} }
if (row.cells.len) rows.add(row); if (row.cells.len) rows.add(row);