Undo redo
This commit is contained in:
@@ -444,7 +444,7 @@ void BoundedAdd(Array<T> *arr, T item) {
|
|||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
T *Alloc(Array<T> *arr, T item) {
|
T *Alloc(Array<T> *arr, T item) {
|
||||||
TryGrowing();
|
TryGrowing(arr);
|
||||||
T *ref = arr->data + arr->len++;
|
T *ref = arr->data + arr->len++;
|
||||||
ref[0] = item;
|
ref[0] = item;
|
||||||
return ref;
|
return ref;
|
||||||
|
|||||||
@@ -162,7 +162,6 @@ void ReplaceText(Buffer *buffer, Range range, String16 string) {
|
|||||||
Assert(range.max >= range.min);
|
Assert(range.max >= range.min);
|
||||||
Assert(range.max >= 0 && range.max <= buffer->len);
|
Assert(range.max >= 0 && range.max <= buffer->len);
|
||||||
Assert(range.min >= 0 && range.min <= buffer->len);
|
Assert(range.min >= 0 && range.min <= buffer->len);
|
||||||
buffer->change_id += 1;
|
|
||||||
|
|
||||||
Int size_to_remove = range.max - range.min;
|
Int size_to_remove = range.max - range.min;
|
||||||
Int size_to_add = string.len;
|
Int size_to_add = string.len;
|
||||||
|
|||||||
@@ -1,15 +1,3 @@
|
|||||||
// @todo: gap buffer to improve speed of inserting on big files with only one cursor?
|
|
||||||
struct Buffer {
|
|
||||||
union {
|
|
||||||
U16 *data;
|
|
||||||
wchar_t *str;
|
|
||||||
};
|
|
||||||
Int len;
|
|
||||||
Int cap;
|
|
||||||
Array<Int> line_starts;
|
|
||||||
Int change_id;
|
|
||||||
};
|
|
||||||
|
|
||||||
union Range {
|
union Range {
|
||||||
struct {
|
struct {
|
||||||
Int min;
|
Int min;
|
||||||
@@ -35,3 +23,24 @@ struct Edit {
|
|||||||
Range range;
|
Range range;
|
||||||
String16 string;
|
String16 string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// @idea: maybe redo tree?
|
||||||
|
struct HistoryEntry {
|
||||||
|
Array<Edit> edits;
|
||||||
|
Array<Caret> carets;
|
||||||
|
};
|
||||||
|
|
||||||
|
// @todo: gap buffer to improve speed of inserting on big files with only one cursor?
|
||||||
|
struct Buffer {
|
||||||
|
union {
|
||||||
|
U16 *data;
|
||||||
|
wchar_t *str;
|
||||||
|
};
|
||||||
|
Int len;
|
||||||
|
Int cap;
|
||||||
|
Array<Int> line_starts;
|
||||||
|
|
||||||
|
Array<HistoryEntry> undo_stack;
|
||||||
|
Array<HistoryEntry> redo_stack;
|
||||||
|
int debug_edit_phase;
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,48 +1,32 @@
|
|||||||
// @todo:
|
void SaveHistoryBeforeMergeCursor(Buffer *buffer, Array<HistoryEntry> *stack, Array<Caret> &carets) {
|
||||||
// @idea: maybe redo tree?
|
HistoryEntry *entry = Alloc(stack);
|
||||||
struct HistoryEntry {
|
entry->carets = TightCopy(GetSystemAllocator(), carets);
|
||||||
Array<Edit> edits;
|
|
||||||
Array<Caret> carets;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct History {
|
|
||||||
Array<HistoryEntry> undo_stack;
|
|
||||||
Array<HistoryEntry> redo_stack;
|
|
||||||
int debug_edit_phase;
|
|
||||||
};
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
void SaveHistoryBeforeMergeCursor(Window *window, Array<HistoryEntry> *stack) {
|
|
||||||
ProfileFunction();
|
|
||||||
HistoryEntry *entry = stack->alloc();
|
|
||||||
Allocator sys_allocator = GetSystemAllocator();
|
|
||||||
entry->cursors = window->cursors.tight_copy(sys_allocator);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SaveHistoryBeforeApplyEdits(Window *window, Array<HistoryEntry> *stack, Array<Edit> edits) {
|
void SaveHistoryBeforeApplyEdits(Buffer *buffer, Array<HistoryEntry> *stack, Array<Edit> &edits) {
|
||||||
ProfileFunction();
|
ProfileFunction();
|
||||||
HistoryEntry *entry = stack->last();
|
HistoryEntry *entry = GetLast(*stack);
|
||||||
Allocator sys_allocator = GetSystemAllocator();
|
Allocator sys_allocator = GetSystemAllocator();
|
||||||
entry->edits = edits.tight_copy(sys_allocator);
|
entry->edits = TightCopy(sys_allocator, edits);
|
||||||
|
|
||||||
// Make reverse edits
|
// Make reverse edits
|
||||||
For(entry->edits) {
|
For(entry->edits) {
|
||||||
Range new_range = {it.range.min, it.range.min + it.string.len};
|
Range new_range = {it.range.min, it.range.min + it.string.len};
|
||||||
String string = GetString(window->buffer, it.range);
|
String16 string = GetString(*buffer, it.range);
|
||||||
it.string = Copy(sys_allocator, string);
|
it.string = Copy(sys_allocator, string);
|
||||||
it.range = new_range;
|
it.range = new_range;
|
||||||
}
|
}
|
||||||
|
|
||||||
Array<Edit> temp_edits = entry->edits.tight_copy(sys_allocator);
|
Scratch scratch;
|
||||||
defer { temp_edits.dealloc(); };
|
Array<Edit> temp_edits = TightCopy(scratch, entry->edits);
|
||||||
|
|
||||||
// Fix reverse edits
|
// Fix reverse edits
|
||||||
ForItem(edit, edits) {
|
ForItem(edit, edits) {
|
||||||
int64_t remove_size = GetRangeSize(edit.range);
|
Int remove_size = GetSize(edit.range);
|
||||||
int64_t insert_size = edit.string.len;
|
Int insert_size = edit.string.len;
|
||||||
int64_t offset = insert_size - remove_size;
|
Int offset = insert_size - remove_size;
|
||||||
|
|
||||||
for (int64_t i = 0; i < entry->edits.len; i += 1) {
|
for (Int i = 0; i < entry->edits.len; i += 1) {
|
||||||
Edit &new_edit = entry->edits.data[i];
|
Edit &new_edit = entry->edits.data[i];
|
||||||
Edit &old_edit = temp_edits.data[i];
|
Edit &old_edit = temp_edits.data[i];
|
||||||
if (old_edit.range.min > edit.range.min) {
|
if (old_edit.range.min > edit.range.min) {
|
||||||
@@ -53,96 +37,94 @@ void SaveHistoryBeforeApplyEdits(Window *window, Array<HistoryEntry> *stack, Arr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RedoEdit(Window *window) {
|
void RedoEdit(Buffer *buffer, Array<Caret> *carets) {
|
||||||
ProfileFunction();
|
ProfileFunction();
|
||||||
if (window->history.redo_stack.len <= 0) return;
|
if (buffer->redo_stack.len <= 0) return;
|
||||||
|
|
||||||
HistoryEntry entry = window->history.redo_stack.pop();
|
HistoryEntry entry = Pop(&buffer->redo_stack);
|
||||||
|
|
||||||
SaveHistoryBeforeMergeCursor(window, &window->history.undo_stack);
|
SaveHistoryBeforeMergeCursor(buffer, &buffer->undo_stack, *carets);
|
||||||
SaveHistoryBeforeApplyEdits(window, &window->history.undo_stack, entry.edits);
|
SaveHistoryBeforeApplyEdits(buffer, &buffer->undo_stack, entry.edits);
|
||||||
_ApplyEdits(&window->buffer, entry.edits);
|
_ApplyEdits(buffer, entry.edits);
|
||||||
|
|
||||||
window->cursors.dealloc();
|
Dealloc(carets);
|
||||||
window->cursors = entry.cursors;
|
*carets = entry.carets;
|
||||||
window->not_regen_layout = false;
|
|
||||||
|
|
||||||
Allocator sys_allocator = GetSystemAllocator();
|
Allocator sys_allocator = GetSystemAllocator();
|
||||||
For(entry.edits) Dealloc(sys_allocator, &it.string.data);
|
For(entry.edits) Dealloc(sys_allocator, &it.string.data);
|
||||||
entry.edits.dealloc();
|
Dealloc(&entry.edits);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UndoEdit(Window *window) {
|
void UndoEdit(Buffer *buffer, Array<Caret> *carets) {
|
||||||
ProfileFunction();
|
ProfileFunction();
|
||||||
if (window->history.undo_stack.len <= 0) return;
|
if (buffer->undo_stack.len <= 0) return;
|
||||||
|
|
||||||
HistoryEntry entry = window->history.undo_stack.pop();
|
HistoryEntry entry = Pop(&buffer->undo_stack);
|
||||||
|
|
||||||
SaveHistoryBeforeMergeCursor(window, &window->history.redo_stack);
|
SaveHistoryBeforeMergeCursor(buffer, &buffer->redo_stack, *carets);
|
||||||
SaveHistoryBeforeApplyEdits(window, &window->history.redo_stack, entry.edits);
|
SaveHistoryBeforeApplyEdits(buffer, &buffer->redo_stack, entry.edits);
|
||||||
_ApplyEdits(&window->buffer, entry.edits);
|
_ApplyEdits(buffer, entry.edits);
|
||||||
|
|
||||||
window->cursors.dealloc();
|
Dealloc(carets);
|
||||||
window->cursors = entry.cursors;
|
*carets = entry.carets;
|
||||||
window->not_regen_layout = false;
|
|
||||||
|
|
||||||
Allocator sys_allocator = GetSystemAllocator();
|
Allocator sys_allocator = GetSystemAllocator();
|
||||||
For(entry.edits) Dealloc(sys_allocator, &it.string.data);
|
For(entry.edits) Dealloc(sys_allocator, &it.string.data);
|
||||||
entry.edits.dealloc();
|
Dealloc(&entry.edits);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BeforeEdit(Window *window) {
|
void ApplyEdits(Buffer *buffer, Array<Edit> &edits) {
|
||||||
Assert(window->history.debug_edit_phase == 0);
|
ProfileFunction();
|
||||||
window->history.debug_edit_phase += 1;
|
Assert(buffer->debug_edit_phase == 1);
|
||||||
Assert(window->cursors.len);
|
buffer->debug_edit_phase += 1;
|
||||||
SaveHistoryBeforeMergeCursor(window, &window->history.undo_stack);
|
SaveHistoryBeforeApplyEdits(buffer, &buffer->undo_stack, edits);
|
||||||
|
_ApplyEdits(buffer, edits);
|
||||||
|
}
|
||||||
|
|
||||||
// Clear redo stack
|
void ClearRedoStack(Buffer *buffer) {
|
||||||
{
|
ForItem(entry, buffer->redo_stack) {
|
||||||
For(window->history.redo_stack) {
|
Dealloc(&entry.carets);
|
||||||
it.cursors.dealloc();
|
ForItem(edit, entry.edits) Dealloc(entry.edits.allocator, &edit.string.data);
|
||||||
ForItem(edit, it.edits) Dealloc(it.edits.allocator, &edit.string.data);
|
Dealloc(&entry.edits);
|
||||||
it.edits.dealloc();
|
|
||||||
}
|
|
||||||
window->history.redo_stack.dealloc();
|
|
||||||
}
|
}
|
||||||
MergeCursors(window);
|
buffer->redo_stack.len = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ApplyEdits(Window *window, Array<Edit> edits) {
|
void BeforeEdit(Buffer *buffer, Array<Caret> &carets) {
|
||||||
ProfileFunction();
|
Assert(buffer->debug_edit_phase == 0);
|
||||||
Assert(window->history.debug_edit_phase == 1);
|
buffer->debug_edit_phase += 1;
|
||||||
window->history.debug_edit_phase += 1;
|
Assert(carets.len);
|
||||||
SaveHistoryBeforeApplyEdits(window, &window->history.undo_stack, edits);
|
SaveHistoryBeforeMergeCursor(buffer, &buffer->undo_stack, carets);
|
||||||
_ApplyEdits(&window->buffer, edits);
|
ClearRedoStack(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AfterEdit(Window *window, Array<Edit> edits) {
|
void AfterEdit(Buffer *buffer, Array<Edit> *edits, Array<Caret> *carets) {
|
||||||
ProfileFunction();
|
ProfileFunction();
|
||||||
Assert(window->history.debug_edit_phase == 2);
|
Assert(buffer->debug_edit_phase == 2);
|
||||||
window->history.debug_edit_phase -= 2;
|
buffer->debug_edit_phase -= 2;
|
||||||
|
HistoryEntry *entry = GetLast(buffer->undo_stack);
|
||||||
|
|
||||||
HistoryEntry *entry = window->history.undo_stack.last();
|
#if 1
|
||||||
Assert(entry->cursors.len);
|
Assert(entry->carets.len);
|
||||||
Assert(entry->edits.len);
|
Assert(entry->edits.len);
|
||||||
|
for (Int i = 0; i < edits->len - 1; i += 1) {
|
||||||
for (int64_t i = 0; i < edits.len - 1; i += 1) {
|
Assert(edits->data[i].range.min <= edits->data[i + 1].range.min);
|
||||||
Assert(edits[i].range.min <= edits[i + 1].range.min);
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
//
|
//
|
||||||
// Offset all cursors by edits
|
// Offset all carets by edits
|
||||||
//
|
//
|
||||||
Scratch scratch;
|
Scratch scratch;
|
||||||
Array<Cursor> new_cursors = window->cursors.tight_copy(scratch);
|
Array<Caret> new_carets = TightCopy(scratch, *carets);
|
||||||
ForItem(edit, edits) {
|
ForItem(edit, *edits) {
|
||||||
int64_t remove_size = GetRangeSize(edit.range);
|
Int remove_size = GetSize(edit.range);
|
||||||
int64_t insert_size = edit.string.len;
|
Int insert_size = edit.string.len;
|
||||||
int64_t offset = insert_size - remove_size;
|
Int offset = insert_size - remove_size;
|
||||||
|
|
||||||
for (int64_t i = 0; i < window->cursors.len; i += 1) {
|
for (Int i = 0; i < carets->len; i += 1) {
|
||||||
Cursor &old_cursor = window->cursors.data[i];
|
Caret &old_cursor = carets->data[i];
|
||||||
Cursor &new_cursor = new_cursors.data[i];
|
Caret &new_cursor = new_carets.data[i];
|
||||||
if (old_cursor.range.min == edit.range.min) {
|
if (old_cursor.range.min == edit.range.min) {
|
||||||
new_cursor.range.min = new_cursor.range.max = new_cursor.range.min + insert_size;
|
new_cursor.range.min = new_cursor.range.max = new_cursor.range.min + insert_size;
|
||||||
} else if (old_cursor.range.min > edit.range.min) {
|
} else if (old_cursor.range.min > edit.range.min) {
|
||||||
@@ -150,11 +132,9 @@ void AfterEdit(Window *window, Array<Edit> edits) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (int64_t i = 0; i < window->cursors.len; i += 1) window->cursors[i] = new_cursors[i];
|
for (Int i = 0; i < carets->len; i += 1) carets->data[i] = new_carets[i];
|
||||||
|
|
||||||
// Make sure all cursors are in range
|
// Make sure all carets are in range
|
||||||
For(window->cursors) it.range = Clamp(window->buffer, it.range);
|
// @todo: remove?
|
||||||
|
For(*carets) it.range = Clamp(*buffer, it.range);
|
||||||
window->not_regen_layout = false;
|
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
@@ -5,7 +5,7 @@ void MergeCarets(Array<Caret> *_carets, Range *mouse_selection_anchor = NULL) {
|
|||||||
// Merge carets that overlap, this needs to be handled before any edits to
|
// Merge carets 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.
|
||||||
|
|
||||||
// @optimize @refactor: this is retarded, I hit so many array removal bugs here, without allocation please
|
// @optimize @refactor: this is retarded, I hit so many array removal bugs here
|
||||||
Array<Caret *> deleted_carets = {scratch};
|
Array<Caret *> deleted_carets = {scratch};
|
||||||
ForItem(caret, carets) {
|
ForItem(caret, carets) {
|
||||||
if (Contains(deleted_carets, &caret)) goto end_of_caret_loop;
|
if (Contains(deleted_carets, &caret)) goto end_of_caret_loop;
|
||||||
@@ -19,21 +19,22 @@ void MergeCarets(Array<Caret> *_carets, Range *mouse_selection_anchor = NULL) {
|
|||||||
caret.range.max = Max(caret.range.max, it.range.max);
|
caret.range.max = Max(caret.range.max, it.range.max);
|
||||||
caret.range.min = Min(caret.range.min, it.range.min);
|
caret.range.min = Min(caret.range.min, it.range.min);
|
||||||
if (mouse_selection_anchor) *mouse_selection_anchor = caret.range;
|
if (mouse_selection_anchor) *mouse_selection_anchor = caret.range;
|
||||||
break;
|
goto end_of_caret_loop;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
end_of_caret_loop:;
|
end_of_caret_loop:;
|
||||||
}
|
}
|
||||||
|
|
||||||
Array<Caret> new_carets = {carets.allocator};
|
Array<Caret> new_carets = {scratch};
|
||||||
For(carets) {
|
For(carets) {
|
||||||
if (Contains(deleted_carets, &it) == false) {
|
if (Contains(deleted_carets, &it) == false) {
|
||||||
Add(&new_carets, it);
|
Add(&new_carets, it);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Assert(new_carets.len <= carets.len);
|
Assert(new_carets.len <= carets.len);
|
||||||
Dealloc(&carets);
|
|
||||||
carets = new_carets;
|
carets.len = 0;
|
||||||
|
For(new_carets) Add(&carets, it);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MergeSort(int64_t Count, Edit *First, Edit *Temp) {
|
void MergeSort(int64_t Count, Edit *First, Edit *Temp) {
|
||||||
@@ -91,7 +92,7 @@ void MergeSort(int64_t Count, Edit *First, Edit *Temp) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ApplyEdits(Buffer *buffer, Array<Edit> edits) {
|
void _ApplyEdits(Buffer *buffer, Array<Edit> edits) {
|
||||||
ProfileFunction();
|
ProfileFunction();
|
||||||
#if BUFFER_DEBUG
|
#if BUFFER_DEBUG
|
||||||
Assert(buffer->line_starts.len);
|
Assert(buffer->line_starts.len);
|
||||||
@@ -157,7 +158,7 @@ void TestBufferMultiCaret() {
|
|||||||
AddEdit(&edits, {0, 7}, L"t");
|
AddEdit(&edits, {0, 7}, L"t");
|
||||||
AddEdit(&edits, {8, 9}, L"T");
|
AddEdit(&edits, {8, 9}, L"T");
|
||||||
AddEdit(&edits, GetEndAsRange(buffer), L"\nnewThing");
|
AddEdit(&edits, GetEndAsRange(buffer), L"\nnewThing");
|
||||||
ApplyEdits(&buffer, edits);
|
_ApplyEdits(&buffer, edits);
|
||||||
String16 s = GetString(buffer);
|
String16 s = GetString(buffer);
|
||||||
Assert(s == L"t\nThings\nnewThing");
|
Assert(s == L"t\nThings\nnewThing");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,11 +26,9 @@
|
|||||||
#include "view_draw.cpp"
|
#include "view_draw.cpp"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
- Shify + Alt + Down - make a cursor below
|
|
||||||
- Ctrl + Z, Ctrl + C - Undo redo history
|
- Ctrl + Z, Ctrl + C - Undo redo history
|
||||||
- Ctrl + D - create new cursor at next occurence of word
|
- Ctrl + D - create new cursor at next occurence of word
|
||||||
- Ctrl + X, Ctrl + C, Ctrl + V - Copy paste
|
- Ctrl + X, Ctrl + C, Ctrl + V - Copy paste
|
||||||
- Mouse anchor point and double click
|
|
||||||
- Scrollbars
|
- Scrollbars
|
||||||
- Line numbers
|
- Line numbers
|
||||||
- file info bar at bottom (line, column, line endings)
|
- file info bar at bottom (line, column, line endings)
|
||||||
|
|||||||
@@ -1,41 +1,16 @@
|
|||||||
void AfterEdit(View *view, Array<Edit> edits) {
|
|
||||||
//
|
|
||||||
// Offset all cursors by edits
|
|
||||||
//
|
|
||||||
Scratch scratch;
|
|
||||||
Array<Caret> new_cursors = TightCopy(scratch, view->carets);
|
|
||||||
ForItem(edit, edits) {
|
|
||||||
Int remove_size = GetSize(edit.range);
|
|
||||||
Int insert_size = edit.string.len;
|
|
||||||
Int offset = insert_size - remove_size;
|
|
||||||
|
|
||||||
for (Int i = 0; i < view->carets.len; i += 1) {
|
|
||||||
Caret &old_cursor = view->carets.data[i];
|
|
||||||
Caret &new_cursor = new_cursors.data[i];
|
|
||||||
if (old_cursor.range.min == edit.range.min) {
|
|
||||||
new_cursor.range.min = new_cursor.range.max = new_cursor.range.min + insert_size;
|
|
||||||
} else if (old_cursor.range.min > edit.range.min) {
|
|
||||||
new_cursor.range.min = new_cursor.range.max = new_cursor.range.min + offset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (Int i = 0; i < view->carets.len; i += 1) view->carets[i] = new_cursors[i];
|
|
||||||
|
|
||||||
// Make sure all cursors are in range
|
|
||||||
For(view->carets) it.range = Clamp(*view->buffer, it.range);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Command_Replace(View *view, String16 string) {
|
void Command_Replace(View *view, String16 string) {
|
||||||
Scratch scratch;
|
Scratch scratch;
|
||||||
|
BeforeEdit(view->buffer, view->carets);
|
||||||
MergeCarets(&view->carets);
|
MergeCarets(&view->carets);
|
||||||
Array<Edit> edits = {scratch};
|
Array<Edit> edits = {scratch};
|
||||||
For(view->carets) AddEdit(&edits, it.range, string);
|
For(view->carets) AddEdit(&edits, it.range, string);
|
||||||
ApplyEdits(view->buffer, edits);
|
ApplyEdits(view->buffer, edits);
|
||||||
AfterEdit(view, edits);
|
AfterEdit(view->buffer, &edits, &view->carets);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Command_DuplicateLine(View *view, int direction) {
|
void Command_DuplicateLine(View *view, int direction) {
|
||||||
Assert(direction == DIR_UP || direction == DIR_DOWN);
|
Assert(direction == DIR_UP || direction == DIR_DOWN);
|
||||||
|
BeforeEdit(view->buffer, view->carets);
|
||||||
For(view->carets) it = MakeCaret(GetFront(it));
|
For(view->carets) it = MakeCaret(GetFront(it));
|
||||||
MergeCarets(&view->carets);
|
MergeCarets(&view->carets);
|
||||||
|
|
||||||
@@ -49,7 +24,7 @@ void Command_DuplicateLine(View *view, int direction) {
|
|||||||
AddEdit(&edits, Rng(pos), string);
|
AddEdit(&edits, Rng(pos), string);
|
||||||
}
|
}
|
||||||
ApplyEdits(view->buffer, edits);
|
ApplyEdits(view->buffer, edits);
|
||||||
AfterEdit(view, edits);
|
AfterEdit(view->buffer, &edits, &view->carets);
|
||||||
|
|
||||||
For(view->carets) it = MakeCaret(MovePos(*view->buffer, it.range.min, direction, false));
|
For(view->carets) it = MakeCaret(MovePos(*view->buffer, it.range.min, direction, false));
|
||||||
}
|
}
|
||||||
@@ -241,6 +216,12 @@ void HandleKeybindings(View *_view) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (CtrlShiftPress(KEY_Z)) {
|
||||||
|
RedoEdit(&buf, &view.carets);
|
||||||
|
} else if (CtrlPress(KEY_Z)) {
|
||||||
|
UndoEdit(&buf, &view.carets);
|
||||||
|
}
|
||||||
|
|
||||||
if (ShiftPress(KEY_PAGE_UP)) {
|
if (ShiftPress(KEY_PAGE_UP)) {
|
||||||
Command_MoveCursorsByPageSize(&view, DIR_UP, SHIFT_PRESSED);
|
Command_MoveCursorsByPageSize(&view, DIR_UP, SHIFT_PRESSED);
|
||||||
} else if (Press(KEY_PAGE_UP)) {
|
} else if (Press(KEY_PAGE_UP)) {
|
||||||
|
|||||||
Reference in New Issue
Block a user