Fix multiple cursors crashes by sorting cursors by min range
This commit is contained in:
@@ -95,6 +95,13 @@ T Clamp(T value, T min, T max) {
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void Swap(T *a, T *b) {
|
||||||
|
T temp = *a;
|
||||||
|
*a = *b;
|
||||||
|
*b = temp;
|
||||||
|
}
|
||||||
|
|
||||||
inline bool IsPowerOf2(size_t x) {
|
inline bool IsPowerOf2(size_t x) {
|
||||||
size_t result = (((x) & ((x)-1)) == 0);
|
size_t result = (((x) & ((x)-1)) == 0);
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@@ -131,6 +131,7 @@ Cursor ChangeFront(Cursor cursor, int64_t front) {
|
|||||||
void AddEdit(Array<Edit> *edits, Range range, String string) {
|
void AddEdit(Array<Edit> *edits, Range range, String string) {
|
||||||
edits->add({range, string});
|
edits->add({range, string});
|
||||||
}
|
}
|
||||||
|
// SortKey = range.min
|
||||||
|
|
||||||
bool InBounds(const Buffer &buffer, int64_t pos) {
|
bool InBounds(const Buffer &buffer, int64_t pos) {
|
||||||
bool result = pos >= 0 && pos < buffer.len;
|
bool result = pos >= 0 && pos < buffer.len;
|
||||||
@@ -153,7 +154,63 @@ String GetString(const Buffer &buffer, Range range = {0, INT64_MAX}) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MergeSort(int64_t Count, Edit *First, Edit *Temp) {
|
||||||
|
// SortKey = range.min
|
||||||
|
if (Count == 1) {
|
||||||
|
// NOTE(casey): No work to do.
|
||||||
|
} else if (Count == 2) {
|
||||||
|
Edit *EntryA = First;
|
||||||
|
Edit *EntryB = First + 1;
|
||||||
|
if (EntryA->range.min > EntryB->range.min) {
|
||||||
|
Swap(EntryA, EntryB);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
int64_t Half0 = Count / 2;
|
||||||
|
int64_t Half1 = Count - Half0;
|
||||||
|
|
||||||
|
Assert(Half0 >= 1);
|
||||||
|
Assert(Half1 >= 1);
|
||||||
|
|
||||||
|
Edit *InHalf0 = First;
|
||||||
|
Edit *InHalf1 = First + Half0;
|
||||||
|
Edit *End = First + Count;
|
||||||
|
|
||||||
|
MergeSort(Half0, InHalf0, Temp);
|
||||||
|
MergeSort(Half1, InHalf1, Temp);
|
||||||
|
|
||||||
|
Edit *ReadHalf0 = InHalf0;
|
||||||
|
Edit *ReadHalf1 = InHalf1;
|
||||||
|
|
||||||
|
Edit *Out = Temp;
|
||||||
|
for (int64_t Index = 0;
|
||||||
|
Index < Count;
|
||||||
|
++Index) {
|
||||||
|
if (ReadHalf0 == InHalf1) {
|
||||||
|
*Out++ = *ReadHalf1++;
|
||||||
|
} else if (ReadHalf1 == End) {
|
||||||
|
*Out++ = *ReadHalf0++;
|
||||||
|
} else if (ReadHalf0->range.min < ReadHalf1->range.min) {
|
||||||
|
*Out++ = *ReadHalf0++;
|
||||||
|
} else {
|
||||||
|
*Out++ = *ReadHalf1++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Assert(Out == (Temp + Count));
|
||||||
|
Assert(ReadHalf0 == InHalf1);
|
||||||
|
Assert(ReadHalf1 == End);
|
||||||
|
|
||||||
|
// TODO(casey): Not really necessary if we ping-pong
|
||||||
|
for (int64_t Index = 0;
|
||||||
|
Index < Count;
|
||||||
|
++Index) {
|
||||||
|
First[Index] = Temp[Index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ApplyEdits(Buffer *buffer, Array<Edit> edits) {
|
void ApplyEdits(Buffer *buffer, Array<Edit> edits) {
|
||||||
|
Scratch scratch((Arena *)buffer->allocator.object);
|
||||||
|
|
||||||
Assert(edits.len);
|
Assert(edits.len);
|
||||||
int64_t size_to_delete = 0;
|
int64_t size_to_delete = 0;
|
||||||
int64_t size_to_insert = 0;
|
int64_t size_to_insert = 0;
|
||||||
@@ -180,6 +237,10 @@ void ApplyEdits(Buffer *buffer, Array<Edit> edits) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// We need to sort from lowest to higest based on range.min
|
||||||
|
Array<Edit> edits_copy = edits.copy(scratch);
|
||||||
|
MergeSort(edits.len, edits_copy.data, edits.data);
|
||||||
|
|
||||||
int64_t len_offset = size_to_insert - size_to_delete;
|
int64_t len_offset = size_to_insert - size_to_delete;
|
||||||
int64_t allocated_size_required = Max((int64_t)0, len_offset);
|
int64_t allocated_size_required = Max((int64_t)0, len_offset);
|
||||||
if (buffer->len + allocated_size_required > buffer->cap) {
|
if (buffer->len + allocated_size_required > buffer->cap) {
|
||||||
@@ -199,13 +260,12 @@ void ApplyEdits(Buffer *buffer, Array<Edit> edits) {
|
|||||||
int srci = buffer->bi;
|
int srci = buffer->bi;
|
||||||
int dsti = (buffer->bi + 1) % 2;
|
int dsti = (buffer->bi + 1) % 2;
|
||||||
|
|
||||||
Scratch scratch((Arena *)buffer->allocator.object);
|
|
||||||
Array<Edit> writes = {scratch};
|
Array<Edit> writes = {scratch};
|
||||||
int64_t prev_source = 0;
|
int64_t prev_source = 0;
|
||||||
int64_t prev_dest = 0;
|
int64_t prev_dest = 0;
|
||||||
|
|
||||||
For(edits) {
|
For(edits) {
|
||||||
TraceLog(LOG_DEBUG, "edit dsti: %d, srci: %d, range: %lld to %lld, string: '%.*s'\n", dsti, srci, (long long)it.range.min, (long long)it.range.max, FmtString(it.string));
|
TraceLog(LOG_DEBUG, "edit dsti: %d, srci: %d, range: %lld to %lld, string: '%.*s'", dsti, srci, (long long)it.range.min, (long long)it.range.max, Min(5, (int)it.string.len), it.string.data);
|
||||||
Range source_range = {prev_source, it.range.min};
|
Range source_range = {prev_source, it.range.min};
|
||||||
if (GetRangeSize(source_range) != 0) {
|
if (GetRangeSize(source_range) != 0) {
|
||||||
String source_string = {};
|
String source_string = {};
|
||||||
@@ -241,7 +301,9 @@ void ApplyEdits(Buffer *buffer, Array<Edit> edits) {
|
|||||||
|
|
||||||
int64_t new_buffer_len = 0;
|
int64_t new_buffer_len = 0;
|
||||||
For(writes) {
|
For(writes) {
|
||||||
TraceLog(LOG_DEBUG, "write dsti: %d, srci: %d, range: %lld to %lld, string: '%.*s'\n", dsti, srci, (long long)it.range.min, (long long)it.range.max, FmtString(it.string));
|
Assert(it.range.min >= 0);
|
||||||
|
Assert(it.range.max >= 0);
|
||||||
|
TraceLog(LOG_DEBUG, "write dsti: %d, srci: %d, range: %lld to %lld, string: '%.*s'", dsti, srci, (long long)it.range.min, (long long)it.range.max, Min(5, (int)it.string.len), it.string.data);
|
||||||
memcpy(buffer->data[dsti] + new_buffer_len, it.string.data, it.string.len);
|
memcpy(buffer->data[dsti] + new_buffer_len, it.string.data, it.string.len);
|
||||||
new_buffer_len += it.string.len;
|
new_buffer_len += it.string.len;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -231,14 +231,26 @@ int main() {
|
|||||||
}
|
}
|
||||||
if (IsKeyPressed(KEY_DOWN) || IsKeyPressedRepeat(KEY_DOWN)) {
|
if (IsKeyPressed(KEY_DOWN) || IsKeyPressedRepeat(KEY_DOWN)) {
|
||||||
For(focused_window->cursors) {
|
For(focused_window->cursors) {
|
||||||
|
if (IsKeyDown(KEY_LEFT_SHIFT)) {
|
||||||
|
int64_t front = GetFront(it);
|
||||||
|
front = MoveDown(focused_window->buffer, front);
|
||||||
|
it = ChangeFront(it, front);
|
||||||
|
} else {
|
||||||
it.range.max = it.range.min = MoveDown(focused_window->buffer, it.range.min);
|
it.range.max = it.range.min = MoveDown(focused_window->buffer, it.range.min);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (IsKeyPressed(KEY_UP) || IsKeyPressedRepeat(KEY_UP)) {
|
if (IsKeyPressed(KEY_UP) || IsKeyPressedRepeat(KEY_UP)) {
|
||||||
For(focused_window->cursors) {
|
For(focused_window->cursors) {
|
||||||
|
if (IsKeyDown(KEY_LEFT_SHIFT)) {
|
||||||
|
int64_t front = GetFront(it);
|
||||||
|
front = MoveUp(focused_window->buffer, front);
|
||||||
|
it = ChangeFront(it, front);
|
||||||
|
} else {
|
||||||
it.range.max = it.range.min = MoveUp(focused_window->buffer, it.range.min);
|
it.range.max = it.range.min = MoveUp(focused_window->buffer, it.range.min);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Merge cursors that overlap, this needs to be handled before any edits to
|
// Merge cursors that overlap, this needs to be handled before any edits to
|
||||||
// make sure overlapping edits won't happen.
|
// make sure overlapping edits won't happen.
|
||||||
@@ -426,10 +438,15 @@ int main() {
|
|||||||
if (CheckCollisionPointRec(mouse_in_render_units, ToRectangle(cell.rect))) {
|
if (CheckCollisionPointRec(mouse_in_render_units, ToRectangle(cell.rect))) {
|
||||||
DrawRectangleRec(ToRectangle(cell.rect), {0, 0, 255, 40});
|
DrawRectangleRec(ToRectangle(cell.rect), {0, 0, 255, 40});
|
||||||
if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) {
|
if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) {
|
||||||
|
if (IsKeyDown(KEY_LEFT_CONTROL)) {
|
||||||
|
window.cursors.add({cell.pos, cell.pos});
|
||||||
|
} else {
|
||||||
window.cursors.clear();
|
window.cursors.clear();
|
||||||
window.cursors.add({cell.pos, cell.pos});
|
window.cursors.add({cell.pos, cell.pos});
|
||||||
|
}
|
||||||
} else if (IsMouseButtonDown(MOUSE_BUTTON_LEFT)) {
|
} else if (IsMouseButtonDown(MOUSE_BUTTON_LEFT)) {
|
||||||
window.cursors[0] = ChangeBack(window.cursors[0], cell.pos);
|
Cursor *c = window.cursors.last();
|
||||||
|
*c = ChangeBack(*c, cell.pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user