Tracking allocator, buffer tests
This commit is contained in:
@@ -12,4 +12,5 @@ set libs=../src/external/SDL/win32-static/SDL3-static.lib ../src/external/SDL/wi
|
||||
cl %flags% ../src/text_editor/text_editor.cpp -Fe:te.exe -I../src/external/SDL/include -I../src/external/lua/src -I../src/external/glad -I../src/ %libs% -link /SUBSYSTEM:WINDOWS /NODEFAULTLIB:LIBCMT /NODEFAULTLIB:MSVCRTD
|
||||
|
||||
copy te.exe ..\data\te.exe
|
||||
copy te.pdb ..\data\te.pdb
|
||||
echo written ..\data\te.exe
|
||||
|
||||
@@ -20,7 +20,9 @@ Things I like:
|
||||
- Configurable Open
|
||||
- Lua config files work pretty well
|
||||
|
||||
|
||||
Splits:
|
||||
- Buffer16.cpp / h
|
||||
- Block arena and refactor alloc
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -238,6 +238,8 @@ void *SystemAllocator_Alloc(void *object, int kind, void *p, size_t size) {
|
||||
Assert(result);
|
||||
} else if (kind == AllocatorKind_Deallocate) {
|
||||
free(p);
|
||||
} else {
|
||||
InvalidCodepath();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -246,3 +248,46 @@ Allocator GetSystemAllocator() {
|
||||
Allocator result = {SystemAllocator_Alloc};
|
||||
return result;
|
||||
}
|
||||
|
||||
struct MemoryRecord {
|
||||
size_t size;
|
||||
void *addr;
|
||||
bool deallocated;
|
||||
};
|
||||
Array<MemoryRecord> MemoryTrackingRecord;
|
||||
|
||||
|
||||
void *TrackingAllocatorProc(void *object, int kind, void *p, size_t size) {
|
||||
void *result = NULL;
|
||||
|
||||
if (kind == AllocatorKind_Allocate) {
|
||||
result = malloc(size);
|
||||
Add(&MemoryTrackingRecord, {size, result});
|
||||
Assert(result);
|
||||
} else if (kind == AllocatorKind_Deallocate) {
|
||||
free(p);
|
||||
|
||||
bool found = false;
|
||||
For(MemoryTrackingRecord){
|
||||
if (it.addr == p) {
|
||||
it.deallocated = true;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
Assert(found);
|
||||
} else {
|
||||
InvalidCodepath();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void TrackingAllocatorCheck() {
|
||||
For (MemoryTrackingRecord) {
|
||||
Assert(it.deallocated);
|
||||
}
|
||||
}
|
||||
|
||||
Allocator GetTrackingAllocator() {
|
||||
Allocator result = {TrackingAllocatorProc};
|
||||
return result;
|
||||
}
|
||||
@@ -1,94 +1,106 @@
|
||||
#define BUFFER_DEBUG 0
|
||||
|
||||
///////////////////////////////
|
||||
// helpers
|
||||
///////////////////////////////
|
||||
|
||||
Int GetSize(Range range) {
|
||||
API Range MakeRange(Int a, Int b) {
|
||||
Range result = {Min(a, b), Max(a, b)};
|
||||
return result;
|
||||
}
|
||||
|
||||
API Range MakeRange(Int a) {
|
||||
Range result = {a, a};
|
||||
return result;
|
||||
}
|
||||
|
||||
API Int GetSize(Range range) {
|
||||
Assert(range.max >= range.min);
|
||||
Int result = range.max - range.min;
|
||||
return result;
|
||||
}
|
||||
|
||||
Range Rng(Int a, Int b) {
|
||||
Range result = {Min(a, b), Max(a, b)};
|
||||
API bool AreEqual(Range a, Range b) {
|
||||
bool result = a.min == b.min && a.max == b.max;
|
||||
return result;
|
||||
}
|
||||
|
||||
Range Rng(Int a = 0) {
|
||||
Range result = {a, a};
|
||||
API bool AreOverlapping(Range a, Range b) {
|
||||
bool r1 = (a.max >= b.min && a.max <= b.max) || (a.min >= b.min && a.min <= b.max);
|
||||
bool r2 = (b.max >= a.min && b.max <= a.max) || (b.min >= a.min && b.min <= a.max);
|
||||
return r1 || r2;
|
||||
}
|
||||
|
||||
API bool InBounds(Range range, Int pos) {
|
||||
bool result = pos >= range.min && pos < range.max;
|
||||
return result;
|
||||
}
|
||||
|
||||
String16 GetString(Buffer *buffer, Range range = {0, INT64_MAX}) {
|
||||
range.min = Clamp(range.min, (Int)0, buffer->len);
|
||||
range.max = Clamp(range.max, (Int)0, buffer->len);
|
||||
String16 result = {(char16_t *)buffer->data + range.min, GetSize(range)};
|
||||
API Range operator-(Range a, Int value) {
|
||||
a.min -= value;
|
||||
a.max -= value;
|
||||
return a;
|
||||
}
|
||||
|
||||
API Range operator-=(Range &range, Int value) {
|
||||
range = range - value;
|
||||
return range;
|
||||
}
|
||||
|
||||
API Range operator+(Range a, Int value) {
|
||||
a.min += value;
|
||||
a.max += value;
|
||||
return a;
|
||||
}
|
||||
|
||||
API Range operator+=(Range &range, Int value) {
|
||||
range = range + value;
|
||||
return range;
|
||||
}
|
||||
|
||||
API Range GetBufferEndAsRange(Buffer *buffer) {
|
||||
Range result = {buffer->len, buffer->len};
|
||||
return result;
|
||||
}
|
||||
|
||||
String AllocCharString(Allocator allocator, Buffer *buffer, Range range = {0, INT64_MAX}) {
|
||||
String16 string16 = GetString(buffer, range);
|
||||
String result = ToString(allocator, string16);
|
||||
API Range GetBufferBeginAsRange(Buffer *buffer) {
|
||||
Range result = {0, 0};
|
||||
return result;
|
||||
}
|
||||
|
||||
Int Clamp(const Buffer *buffer, Int pos) {
|
||||
API Range GetRange(Buffer *buffer) {
|
||||
Range result = {0, buffer->len};
|
||||
return result;
|
||||
}
|
||||
|
||||
API Int Clamp(const Buffer *buffer, Int pos) {
|
||||
Int result = Clamp(pos, (Int)0, buffer->len);
|
||||
return result;
|
||||
}
|
||||
|
||||
Range Clamp(const Buffer *buffer, Range range) {
|
||||
API Range Clamp(const Buffer *buffer, Range range) {
|
||||
Range result = {};
|
||||
result.min = Clamp(buffer, range.min);
|
||||
result.max = Clamp(buffer, range.max);
|
||||
return result;
|
||||
}
|
||||
|
||||
Range GetEndAsRange(Buffer *buffer) {
|
||||
Range result = {buffer->len, buffer->len};
|
||||
return result;
|
||||
}
|
||||
|
||||
Range GetBeginAsRange(Buffer *buffer) {
|
||||
Range result = {0, 0};
|
||||
return result;
|
||||
}
|
||||
|
||||
Range GetRange(Buffer *buffer) {
|
||||
Range result = {0, buffer->len};
|
||||
return result;
|
||||
}
|
||||
|
||||
Int GetFront(Caret caret) {
|
||||
// Caret
|
||||
API Int GetFront(Caret caret) {
|
||||
Int result = caret.pos[caret.ifront];
|
||||
return result;
|
||||
}
|
||||
|
||||
Int GetBack(Caret caret) {
|
||||
API Int GetBack(Caret caret) {
|
||||
Int index = (caret.ifront + 1) % 2;
|
||||
Int result = caret.pos[index];
|
||||
return result;
|
||||
}
|
||||
|
||||
Caret MakeCaret(Int pos) {
|
||||
API Caret MakeCaret(Int pos) {
|
||||
Caret result = {};
|
||||
result.range.min = result.range.max = pos;
|
||||
return result;
|
||||
}
|
||||
|
||||
XY XYLine(Int line) {
|
||||
XY result = {};
|
||||
result.line = line;
|
||||
return result;
|
||||
}
|
||||
|
||||
char16_t GetChar(Buffer *buffer, Int pos) {
|
||||
if (pos >= 0 && pos < buffer->len) return buffer->str[pos];
|
||||
return 0;
|
||||
}
|
||||
|
||||
Caret MakeCaret(Int front, Int back) {
|
||||
API Caret MakeCaret(Int front, Int back) {
|
||||
Caret result = {};
|
||||
if (front >= back) {
|
||||
result.range.min = back;
|
||||
@@ -102,19 +114,19 @@ Caret MakeCaret(Int front, Int back) {
|
||||
return result;
|
||||
}
|
||||
|
||||
Caret SetBack(Caret caret, Int back) {
|
||||
API Caret SetBack(Caret caret, Int back) {
|
||||
Int front = GetFront(caret);
|
||||
Caret result = MakeCaret(front, back);
|
||||
return result;
|
||||
}
|
||||
|
||||
Caret SetFront(Caret caret, Int front) {
|
||||
API Caret SetFront(Caret caret, Int front) {
|
||||
Int back = GetBack(caret);
|
||||
Caret result = MakeCaret(front, back);
|
||||
return result;
|
||||
}
|
||||
|
||||
Caret SetFrontWithAnchor(Caret caret, Caret anchor, Int p) {
|
||||
API Caret SetFrontWithAnchor(Caret caret, Caret anchor, Int p) {
|
||||
if (anchor.range.min > p) {
|
||||
caret = MakeCaret(p, anchor.range.max);
|
||||
} else if (anchor.range.max < p) {
|
||||
@@ -125,60 +137,17 @@ Caret SetFrontWithAnchor(Caret caret, Caret anchor, Int p) {
|
||||
return caret;
|
||||
}
|
||||
|
||||
bool InBounds(const Buffer *buffer, Int pos) {
|
||||
bool result = pos >= 0 && pos < buffer->len;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool InBounds(Range range, Int pos) {
|
||||
bool result = pos >= range.min && pos < range.max;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool AreEqual(Range a, Range b) {
|
||||
bool result = a.min == b.min && a.max == b.max;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool AreEqual(Caret a, Caret b) {
|
||||
API bool AreEqual(Caret a, Caret b) {
|
||||
bool result = AreEqual(a.range, b.range) && a.ifront == b.ifront;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool AreOverlapping(Range a, Range b) {
|
||||
bool r1 = (a.max >= b.min && a.max <= b.max) || (a.min >= b.min && a.min <= b.max);
|
||||
bool r2 = (b.max >= a.min && b.max <= a.max) || (b.min >= a.min && b.min <= a.max);
|
||||
return r1 || r2;
|
||||
}
|
||||
|
||||
bool AreOverlapping(Caret a, Caret b) {
|
||||
API bool AreOverlapping(Caret a, Caret b) {
|
||||
bool result = AreOverlapping(a.range, b.range);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool InRange(Int a, Range b) {
|
||||
bool result = a >= b.min && a < b.max;
|
||||
return result;
|
||||
}
|
||||
|
||||
Range operator-(Range a, Int value) {
|
||||
a.min -= value;
|
||||
a.max -= value;
|
||||
return a;
|
||||
}
|
||||
|
||||
Range operator-=(Range &range, Int value) {
|
||||
range = range - value;
|
||||
return range;
|
||||
}
|
||||
|
||||
Int LastLine(Buffer *buffer) {
|
||||
Int result = buffer->line_starts.len - 1;
|
||||
return result;
|
||||
}
|
||||
|
||||
const Int LAST_LINE = INT64_MAX;
|
||||
Range GetLineRange(Buffer *buffer, Int line, Int *end_of_buffer = NULL) {
|
||||
API Range GetLineRange(Buffer *buffer, Int line, Int *end_of_buffer) {
|
||||
Range result = {buffer->line_starts[line], buffer->len};
|
||||
if (line + 1 < buffer->line_starts.len) {
|
||||
result.max = buffer->line_starts[line + 1];
|
||||
@@ -188,26 +157,60 @@ Range GetLineRange(Buffer *buffer, Int line, Int *end_of_buffer = NULL) {
|
||||
return result;
|
||||
}
|
||||
|
||||
Range GetLineRangeWithoutNL(Buffer *buffer, Int line) {
|
||||
API Range GetLineRangeWithoutNL(Buffer *buffer, Int line) {
|
||||
Int end_of_buffer = 0;
|
||||
Range line_range = GetLineRange(buffer, line, &end_of_buffer);
|
||||
line_range.max = line_range.max - 1 + end_of_buffer;
|
||||
return line_range;
|
||||
}
|
||||
|
||||
String16 GetLineString(Buffer *buffer, Int line, Int *end_of_buffer = NULL) {
|
||||
API String16 GetString(Buffer *buffer, Range range) {
|
||||
range.min = Clamp(range.min, (Int)0, buffer->len);
|
||||
range.max = Clamp(range.max, (Int)0, buffer->len);
|
||||
String16 result = {(char16_t *)buffer->data + range.min, GetSize(range)};
|
||||
return result;
|
||||
}
|
||||
|
||||
API String AllocCharString(Allocator allocator, Buffer *buffer, Range range) {
|
||||
String16 string16 = GetString(buffer, range);
|
||||
String result = ToString(allocator, string16);
|
||||
return result;
|
||||
}
|
||||
|
||||
API XY XYLine(Int line) {
|
||||
XY result = {};
|
||||
result.line = line;
|
||||
return result;
|
||||
}
|
||||
|
||||
API char16_t GetChar(Buffer *buffer, Int pos) {
|
||||
if (pos >= 0 && pos < buffer->len) return buffer->str[pos];
|
||||
return 0;
|
||||
}
|
||||
|
||||
API bool InBounds(const Buffer *buffer, Int pos) {
|
||||
bool result = pos >= 0 && pos < buffer->len;
|
||||
return result;
|
||||
}
|
||||
|
||||
API Int LastLine(Buffer *buffer) {
|
||||
Int result = buffer->line_starts.len - 1;
|
||||
return result;
|
||||
}
|
||||
|
||||
API String16 GetLineString(Buffer *buffer, Int line, Int *end_of_buffer) {
|
||||
Range range = GetLineRange(buffer, line, end_of_buffer);
|
||||
String16 string = GetString(buffer, range);
|
||||
return string;
|
||||
}
|
||||
|
||||
String16 GetLineStringWithoutNL(Buffer *buffer, Int line) {
|
||||
API String16 GetLineStringWithoutNL(Buffer *buffer, Int line) {
|
||||
Range range = GetLineRangeWithoutNL(buffer, line);
|
||||
String16 string = GetString(buffer, range);
|
||||
return string;
|
||||
}
|
||||
|
||||
Int PosToLine(Buffer *buffer, Int pos) {
|
||||
API Int PosToLine(Buffer *buffer, Int pos) {
|
||||
Add(&buffer->line_starts, buffer->len + 1);
|
||||
|
||||
// binary search
|
||||
@@ -237,7 +240,7 @@ Int PosToLine(Buffer *buffer, Int pos) {
|
||||
return result;
|
||||
}
|
||||
|
||||
XY PosToXY(Buffer *buffer, Int pos) {
|
||||
API XY PosToXY(Buffer *buffer, Int pos) {
|
||||
Int line = PosToLine(buffer, pos);
|
||||
Range line_range = GetLineRange(buffer, line);
|
||||
Int col = pos - line_range.min;
|
||||
@@ -245,14 +248,14 @@ XY PosToXY(Buffer *buffer, Int pos) {
|
||||
return result;
|
||||
}
|
||||
|
||||
Int XYToPos(Buffer *buffer, XY xy) {
|
||||
API Int XYToPos(Buffer *buffer, XY xy) {
|
||||
xy.line = Clamp(xy.line, (Int)0, buffer->line_starts.len - 1);
|
||||
Range line_range = GetLineRange(buffer, xy.line);
|
||||
Int pos = Clamp(xy.col + line_range.min, line_range.min, line_range.max);
|
||||
return pos;
|
||||
}
|
||||
|
||||
Int XYToPosWithoutNL(Buffer *buffer, XY xy) {
|
||||
API Int XYToPosWithoutNL(Buffer *buffer, XY xy) {
|
||||
xy.line = Clamp(xy.line, (Int)0, buffer->line_starts.len - 1);
|
||||
Int end_of_buffer = 0;
|
||||
Range line_range = GetLineRange(buffer, xy.line, &end_of_buffer);
|
||||
@@ -260,7 +263,7 @@ Int XYToPosWithoutNL(Buffer *buffer, XY xy) {
|
||||
return pos;
|
||||
}
|
||||
|
||||
Int XYToPosErrorOutOfBounds(Buffer *buffer, XY xy) {
|
||||
API Int XYToPosErrorOutOfBounds(Buffer *buffer, XY xy) {
|
||||
if (xy.line < 0 || xy.line >= buffer->line_starts.len) return -1;
|
||||
Range line_range = GetLineRange(buffer, xy.line);
|
||||
Int pos = xy.col + line_range.min;
|
||||
@@ -268,7 +271,7 @@ Int XYToPosErrorOutOfBounds(Buffer *buffer, XY xy) {
|
||||
return pos;
|
||||
}
|
||||
|
||||
Int GetWordStart(Buffer *buffer, Int pos) {
|
||||
API Int GetWordStart(Buffer *buffer, Int pos) {
|
||||
pos = Clamp(pos, (Int)0, buffer->len);
|
||||
for (Int i = pos - 1; i >= 0; i -= 1) {
|
||||
if (!IsWord(buffer->str[i])) break;
|
||||
@@ -277,7 +280,7 @@ Int GetWordStart(Buffer *buffer, Int pos) {
|
||||
return pos;
|
||||
}
|
||||
|
||||
Int GetWordEnd(Buffer *buffer, Int pos) {
|
||||
API Int GetWordEnd(Buffer *buffer, Int pos) {
|
||||
pos = Clamp(pos, (Int)0, buffer->len);
|
||||
for (Int i = pos;; i += 1) {
|
||||
pos = i;
|
||||
@@ -290,7 +293,7 @@ Int GetWordEnd(Buffer *buffer, Int pos) {
|
||||
return pos;
|
||||
}
|
||||
|
||||
bool IsLoadWord(char16_t w) {
|
||||
API bool IsLoadWord(char16_t w) {
|
||||
bool result = w == u'(' || w == u')' || w == u'/' || w == u'\\' || w == u':' || w == u'$' || w == u'_' || w == u'.' || w == u'@' || w == u',';
|
||||
if (!result) {
|
||||
result = !(IsSymbol(w) || IsWhitespace(w));
|
||||
@@ -298,7 +301,7 @@ bool IsLoadWord(char16_t w) {
|
||||
return result;
|
||||
}
|
||||
|
||||
Int GetLoadWordStart(Buffer *buffer, Int pos) {
|
||||
API Int GetLoadWordStart(Buffer *buffer, Int pos) {
|
||||
pos = Clamp(pos, (Int)0, buffer->len);
|
||||
for (Int i = pos - 1; i >= 0; i -= 1) {
|
||||
if (!IsLoadWord(buffer->str[i])) break;
|
||||
@@ -307,7 +310,7 @@ Int GetLoadWordStart(Buffer *buffer, Int pos) {
|
||||
return pos;
|
||||
}
|
||||
|
||||
Int GetLoadWordEnd(Buffer *buffer, Int pos) {
|
||||
API Int GetLoadWordEnd(Buffer *buffer, Int pos) {
|
||||
pos = Clamp(pos, (Int)0, buffer->len);
|
||||
for (Int i = pos;; i += 1) {
|
||||
pos = i;
|
||||
@@ -320,36 +323,12 @@ Int GetLoadWordEnd(Buffer *buffer, Int pos) {
|
||||
return pos;
|
||||
}
|
||||
|
||||
Range EncloseLoadWord(Buffer *buffer, Int pos) {
|
||||
API Range EncloseLoadWord(Buffer *buffer, Int pos) {
|
||||
Range result = {GetLoadWordStart(buffer, pos), GetLoadWordEnd(buffer, pos)};
|
||||
return result;
|
||||
}
|
||||
|
||||
Int GetExecWordStart(Buffer *buffer, Int pos) {
|
||||
pos = Clamp(pos, (Int)0, buffer->len);
|
||||
for (Int i = pos - 1; i >= 0; i -= 1) {
|
||||
if (IsWhitespace(buffer->str[i])) break;
|
||||
if (GetChar(buffer, i) == u'|' && GetChar(buffer, i + 1) == u'>') break;
|
||||
pos = i;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
Int GetExecWordEnd(Buffer *buffer, Int pos) {
|
||||
pos = Clamp(pos, (Int)0, buffer->len);
|
||||
for (Int i = pos;; i += 1) {
|
||||
pos = i;
|
||||
// this is because buffer end terminates the loop
|
||||
// too early and we cannot establish the proper range
|
||||
// semantics - proper max is one past last index
|
||||
if (!(i < buffer->len)) break;
|
||||
if (IsWhitespace(buffer->str[i])) break;
|
||||
if (GetChar(buffer, i - 1) == u'<' && GetChar(buffer, i) == u'|') break;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
Int GetNextWordEnd(Buffer *buffer, Int pos) {
|
||||
API Int GetNextWordEnd(Buffer *buffer, Int pos) {
|
||||
pos = Clamp(pos, (Int)0, buffer->len);
|
||||
char16_t prev = 0;
|
||||
for (Int i = pos;; i += 1) {
|
||||
@@ -367,7 +346,7 @@ Int GetNextWordEnd(Buffer *buffer, Int pos) {
|
||||
return result;
|
||||
}
|
||||
|
||||
Int GetPrevWordStart(Buffer *buffer, Int pos) {
|
||||
API Int GetPrevWordStart(Buffer *buffer, Int pos) {
|
||||
pos = Clamp(pos, (Int)0, buffer->len);
|
||||
char16_t prev = 0;
|
||||
Int i = pos - 1;
|
||||
@@ -383,43 +362,43 @@ Int GetPrevWordStart(Buffer *buffer, Int pos) {
|
||||
return result;
|
||||
}
|
||||
|
||||
Int GetLineStart(Buffer *buffer, Int pos) {
|
||||
API Int GetLineStart(Buffer *buffer, Int pos) {
|
||||
pos = Clamp(pos, (Int)0, buffer->len);
|
||||
Int line = PosToLine(buffer, pos);
|
||||
Range range = GetLineRangeWithoutNL(buffer, line);
|
||||
return range.min;
|
||||
}
|
||||
|
||||
Int GetLineEnd(Buffer *buffer, Int pos) {
|
||||
API Int GetLineEnd(Buffer *buffer, Int pos) {
|
||||
pos = Clamp(pos, (Int)0, buffer->len);
|
||||
Int line = PosToLine(buffer, pos);
|
||||
Range range = GetLineRangeWithoutNL(buffer, line);
|
||||
return range.max;
|
||||
}
|
||||
|
||||
Int GetFullLineStart(Buffer *buffer, Int pos) {
|
||||
API Int GetFullLineStart(Buffer *buffer, Int pos) {
|
||||
pos = Clamp(pos, (Int)0, buffer->len);
|
||||
Int line = PosToLine(buffer, pos);
|
||||
Range range = GetLineRange(buffer, line);
|
||||
return range.min;
|
||||
}
|
||||
|
||||
Int GetFullLineEnd(Buffer *buffer, Int pos, Int *eof = NULL) {
|
||||
API Int GetFullLineEnd(Buffer *buffer, Int pos, Int *eof) {
|
||||
pos = Clamp(pos, (Int)0, buffer->len);
|
||||
Int line = PosToLine(buffer, pos);
|
||||
Range range = GetLineRange(buffer, line, eof);
|
||||
return range.max;
|
||||
}
|
||||
|
||||
Int GetBufferEnd(Buffer *buffer) {
|
||||
API Int GetBufferEnd(Buffer *buffer) {
|
||||
return buffer->len;
|
||||
}
|
||||
|
||||
Int GetBufferStart(Buffer *buffer) {
|
||||
API Int GetBufferStart(Buffer *buffer) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
Int GetNextEmptyLineStart(Buffer *buffer, Int pos) {
|
||||
API Int GetNextEmptyLineStart(Buffer *buffer, Int pos) {
|
||||
Int result = pos;
|
||||
Int next_line = PosToLine(buffer, pos) + 1;
|
||||
for (Int line = next_line; line < buffer->line_starts.len; line += 1) {
|
||||
@@ -438,7 +417,7 @@ Int GetNextEmptyLineStart(Buffer *buffer, Int pos) {
|
||||
return result;
|
||||
}
|
||||
|
||||
Int GetPrevEmptyLineStart(Buffer *buffer, Int pos) {
|
||||
API Int GetPrevEmptyLineStart(Buffer *buffer, Int pos) {
|
||||
Int result = pos;
|
||||
Int next_line = PosToLine(buffer, pos) - 1;
|
||||
for (Int line = next_line; line >= 0; line -= 1) {
|
||||
@@ -457,12 +436,12 @@ Int GetPrevEmptyLineStart(Buffer *buffer, Int pos) {
|
||||
return result;
|
||||
}
|
||||
|
||||
Range EncloseWord(Buffer *buffer, Int pos) {
|
||||
API Range EncloseWord(Buffer *buffer, Int pos) {
|
||||
Range result = {GetWordStart(buffer, pos), GetWordEnd(buffer, pos)};
|
||||
return result;
|
||||
}
|
||||
|
||||
Int SkipSpaces(Buffer *buffer, Int seek) {
|
||||
API Int SkipSpaces(Buffer *buffer, Int seek) {
|
||||
for (; seek < buffer->len; seek += 1) {
|
||||
char16_t c = GetChar(buffer, seek);
|
||||
if (c != u' ') break;
|
||||
@@ -470,7 +449,7 @@ Int SkipSpaces(Buffer *buffer, Int seek) {
|
||||
return seek;
|
||||
}
|
||||
|
||||
Int FindScopeEnd(Buffer *buffer, Int seek, Int max_seek, char16_t open, char16_t close) {
|
||||
API Int FindScopeEnd(Buffer *buffer, Int seek, Int max_seek, char16_t open, char16_t close) {
|
||||
char16_t right = GetChar(buffer, seek);
|
||||
if (right == open) {
|
||||
int scope = 1;
|
||||
@@ -496,7 +475,7 @@ Int FindScopeEnd(Buffer *buffer, Int seek, Int max_seek, char16_t open, char16_t
|
||||
return seek;
|
||||
}
|
||||
|
||||
Range EncloseScope(Buffer *buffer, Int pos_min, Int pos_max, char16_t open, char16_t close) {
|
||||
API Range EncloseScope(Buffer *buffer, Int pos_min, Int pos_max, char16_t open, char16_t close) {
|
||||
Range result = {pos_min, pos_max};
|
||||
for (Int i = pos_min - 1; i >= 0; i -= 1) {
|
||||
if (buffer->str[i] == open) {
|
||||
@@ -513,33 +492,33 @@ Range EncloseScope(Buffer *buffer, Int pos_min, Int pos_max, char16_t open, char
|
||||
return result;
|
||||
}
|
||||
|
||||
Range EncloseLine(Buffer *buffer, Int pos) {
|
||||
API Range EncloseLine(Buffer *buffer, Int pos) {
|
||||
Range result = {GetLineStart(buffer, pos), GetLineEnd(buffer, pos)};
|
||||
return result;
|
||||
}
|
||||
|
||||
Range EncloseFullLine(Buffer *buffer, Int pos) {
|
||||
API Range EncloseFullLine(Buffer *buffer, Int pos) {
|
||||
Range result = {GetFullLineStart(buffer, pos), GetFullLineEnd(buffer, pos)};
|
||||
return result;
|
||||
}
|
||||
|
||||
Int OffsetByLine(Buffer *buffer, Int pos, Int line_offset) {
|
||||
API Int OffsetByLine(Buffer *buffer, Int pos, Int line_offset) {
|
||||
XY xy = PosToXY(buffer, pos);
|
||||
Int result = XYToPosWithoutNL(buffer, {xy.col, xy.line + line_offset});
|
||||
return result;
|
||||
}
|
||||
|
||||
Int GetNextChar(Buffer *buffer, Int pos) {
|
||||
API Int GetNextChar(Buffer *buffer, Int pos) {
|
||||
Int result = Clamp(pos + 1, (Int)0, buffer->len);
|
||||
return result;
|
||||
}
|
||||
|
||||
Int GetPrevChar(Buffer *buffer, Int pos) {
|
||||
API Int GetPrevChar(Buffer *buffer, Int pos) {
|
||||
Int result = Clamp(pos - 1, (Int)0, buffer->len);
|
||||
return result;
|
||||
}
|
||||
|
||||
Int GetLineIndent(Buffer *buffer, Int line) {
|
||||
API Int GetLineIndent(Buffer *buffer, Int line) {
|
||||
String16 string = GetLineStringWithoutNL(buffer, line);
|
||||
Int indent = 0;
|
||||
for (Int i = 0; i < string.len; i += 1) {
|
||||
@@ -552,23 +531,22 @@ Int GetLineIndent(Buffer *buffer, Int line) {
|
||||
return indent;
|
||||
}
|
||||
|
||||
Int GetIndentAtPos(Buffer *buffer, Int pos) {
|
||||
API Int GetIndentAtPos(Buffer *buffer, Int pos) {
|
||||
Int line = PosToLine(buffer, pos);
|
||||
Int result = GetLineIndent(buffer, line);
|
||||
return result;
|
||||
}
|
||||
|
||||
Range GetIndentRangeAtPos(Buffer *buffer, Int pos) {
|
||||
API Range GetIndentRangeAtPos(Buffer *buffer, Int pos) {
|
||||
Int line = PosToLine(buffer, pos);
|
||||
Int indent = GetLineIndent(buffer, line);
|
||||
Range range = GetLineRangeWithoutNL(buffer, line);
|
||||
return {range.min, range.min + indent};
|
||||
}
|
||||
|
||||
|
||||
Int FuzzyCloserWordBegin = 5;
|
||||
Int FuzzyConsecutiveMultiplier = 3;
|
||||
Int FuzzyRate(String16 string, String16 with) {
|
||||
const Int FuzzyCloserWordBegin = 5;
|
||||
const Int FuzzyConsecutiveMultiplier = 3;
|
||||
API Int FuzzyRate(String16 string, String16 with) {
|
||||
if (with.len == 0) return 0;
|
||||
Int points = 0;
|
||||
Int consecutive = 0;
|
||||
@@ -591,7 +569,7 @@ Int FuzzyRate(String16 string, String16 with) {
|
||||
return points;
|
||||
}
|
||||
|
||||
Array<FuzzyPair> FuzzySearchLines(Allocator allocator, Buffer *buffer, Int line_min, Int line_max, String16 needle) {
|
||||
API Array<FuzzyPair> FuzzySearchLines(Allocator allocator, Buffer *buffer, Int line_min, Int line_max, String16 needle) {
|
||||
if (line_min < 0 || line_min >= buffer->line_starts.len) return {};
|
||||
if (line_max < 0 || line_min > buffer->line_starts.len) return {};
|
||||
Array<FuzzyPair> ratings = {allocator};
|
||||
@@ -615,7 +593,7 @@ Array<FuzzyPair> FuzzySearchLines(Allocator allocator, Buffer *buffer, Int line_
|
||||
return ratings;
|
||||
}
|
||||
|
||||
Int FindRangeByPos(Array<Range> *ranges, Int pos) {
|
||||
API Int FindRangeByPos(Array<Range> *ranges, Int pos) {
|
||||
// binary search
|
||||
Int low = 0;
|
||||
Int high = ranges->len - 1;
|
||||
@@ -718,7 +696,7 @@ void RawValidateLineStarts(Buffer *buffer) {
|
||||
}
|
||||
}
|
||||
|
||||
void RawReplaceText(Buffer *buffer, Range range, String16 string) {
|
||||
API void RawReplaceText(Buffer *buffer, Range range, String16 string) {
|
||||
ProfileFunction();
|
||||
Assert(range.max >= range.min);
|
||||
Assert(range.max >= 0 && range.max <= buffer->len);
|
||||
@@ -750,20 +728,20 @@ void RawReplaceText(Buffer *buffer, Range range, String16 string) {
|
||||
#endif
|
||||
}
|
||||
|
||||
void RawAppend(Buffer *buffer, String16 string) {
|
||||
RawReplaceText(buffer, GetEndAsRange(buffer), string);
|
||||
API void RawAppend(Buffer *buffer, String16 string) {
|
||||
RawReplaceText(buffer, GetBufferEndAsRange(buffer), string);
|
||||
}
|
||||
|
||||
void RawAppend(Buffer *buffer, String string) {
|
||||
API void RawAppend(Buffer *buffer, String string) {
|
||||
Scratch scratch(buffer->line_starts.allocator);
|
||||
RawAppend(buffer, ToString16(scratch, string));
|
||||
}
|
||||
|
||||
void RawAppendf(Buffer *buffer, const char *fmt, ...) {
|
||||
API void RawAppendf(Buffer *buffer, const char *fmt, ...) {
|
||||
Scratch scratch(buffer->line_starts.allocator);
|
||||
STRING_FORMAT(scratch, fmt, string);
|
||||
String16 string16 = ToString16(scratch, string);
|
||||
RawReplaceText(buffer, GetEndAsRange(buffer), string16);
|
||||
RawReplaceText(buffer, GetBufferEndAsRange(buffer), string16);
|
||||
}
|
||||
|
||||
///////////////////////////////
|
||||
@@ -880,7 +858,7 @@ void MergeSort(int64_t Count, Edit *First, Edit *Temp) {
|
||||
}
|
||||
}
|
||||
|
||||
void ApplyEditsMultiCursor(Buffer *buffer, Array<Edit> edits) {
|
||||
API void ApplyEditsMultiCursor(Buffer *buffer, Array<Edit> edits) {
|
||||
ProfileFunction();
|
||||
#if BUFFER_DEBUG
|
||||
Assert(buffer->line_starts.len);
|
||||
@@ -930,7 +908,7 @@ void ApplyEditsMultiCursor(Buffer *buffer, Array<Edit> edits) {
|
||||
}
|
||||
}
|
||||
|
||||
void AddEdit(Array<Edit> *e, Range range, String16 string) {
|
||||
API void AddEdit(Array<Edit> *e, Range range, String16 string) {
|
||||
Add(e, {range, string});
|
||||
}
|
||||
|
||||
@@ -980,7 +958,7 @@ void SaveHistoryBeforeApplyEdits(Buffer *buffer, Array<HistoryEntry> *stack, Arr
|
||||
}
|
||||
}
|
||||
|
||||
void RedoEdit(Buffer *buffer, Array<Caret> *carets) {
|
||||
API void RedoEdit(Buffer *buffer, Array<Caret> *carets) {
|
||||
ProfileFunction();
|
||||
if (buffer->no_history) return;
|
||||
if (buffer->redo_stack.len <= 0) return;
|
||||
@@ -999,7 +977,7 @@ void RedoEdit(Buffer *buffer, Array<Caret> *carets) {
|
||||
Dealloc(&entry.edits);
|
||||
}
|
||||
|
||||
void UndoEdit(Buffer *buffer, Array<Caret> *carets) {
|
||||
API void UndoEdit(Buffer *buffer, Array<Caret> *carets) {
|
||||
ProfileFunction();
|
||||
if (buffer->no_history) return;
|
||||
if (buffer->undo_stack.len <= 0) return;
|
||||
@@ -1018,7 +996,7 @@ void UndoEdit(Buffer *buffer, Array<Caret> *carets) {
|
||||
Dealloc(&entry.edits);
|
||||
}
|
||||
|
||||
void DeallocHistoryEntries(Array<HistoryEntry> *entries) {
|
||||
API void DeallocHistoryEntries(Array<HistoryEntry> *entries) {
|
||||
For(*entries) {
|
||||
Dealloc(&it.carets);
|
||||
ForItem(edit, it.edits) Dealloc(it.edits.allocator, &edit.string.data);
|
||||
@@ -1031,15 +1009,12 @@ void ClearRedoStack(Buffer *buffer) {
|
||||
DeallocHistoryEntries(&buffer->redo_stack);
|
||||
}
|
||||
|
||||
void DeallocHistoryArray(Array<HistoryEntry> *entries) {
|
||||
API void DeallocHistoryArray(Array<HistoryEntry> *entries) {
|
||||
DeallocHistoryEntries(entries);
|
||||
Dealloc(entries);
|
||||
}
|
||||
|
||||
// @note: !!
|
||||
// We can invoke this before caret altering commands to save caret history
|
||||
// and then call some editing command to edit which is not going to save carets
|
||||
Array<Edit> BeginEdit(Allocator allocator, Buffer *buffer, Array<Caret> &carets) {
|
||||
API Array<Edit> BeginEdit(Allocator allocator, Buffer *buffer, Array<Caret> &carets) {
|
||||
Assert(buffer->edit_phase == 0 || buffer->edit_phase == 1);
|
||||
if (buffer->edit_phase == 0) {
|
||||
buffer->edit_phase += 1;
|
||||
@@ -1051,17 +1026,17 @@ Array<Edit> BeginEdit(Allocator allocator, Buffer *buffer, Array<Caret> &carets)
|
||||
return result;
|
||||
}
|
||||
|
||||
void SaveCaretHistoryBeforeBeginEdit(Buffer *buffer, Array<Caret> &carets) {
|
||||
API void SaveCaretHistoryBeforeBeginEdit(Buffer *buffer, Array<Caret> &carets) {
|
||||
BeginEdit({}, buffer, carets);
|
||||
}
|
||||
|
||||
void AssertRanges(Array<Caret> carets) {
|
||||
API void AssertRanges(Array<Caret> carets) {
|
||||
For(carets) {
|
||||
Assert(it.range.max >= it.range.min);
|
||||
}
|
||||
}
|
||||
|
||||
void AdjustCarets(Array<Edit> edits, Array<Caret> *carets) {
|
||||
API void AdjustCarets(Array<Edit> edits, Array<Caret> *carets) {
|
||||
Scratch scratch;
|
||||
Array<Caret> new_carets = TightCopy(scratch, *carets);
|
||||
ForItem(edit, edits) {
|
||||
@@ -1083,7 +1058,7 @@ void AdjustCarets(Array<Edit> edits, Array<Caret> *carets) {
|
||||
for (Int i = 0; i < carets->len; i += 1) carets->data[i] = new_carets[i];
|
||||
}
|
||||
|
||||
void EndEdit(Buffer *buffer, Array<Edit> *edits, Array<Caret> *carets, bool kill_selection) {
|
||||
API void EndEdit(Buffer *buffer, Array<Edit> *edits, Array<Caret> *carets, bool kill_selection) {
|
||||
ProfileFunction();
|
||||
|
||||
{
|
||||
@@ -1151,12 +1126,11 @@ void EndEdit(Buffer *buffer, Array<Edit> *edits, Array<Caret> *carets, bool kill
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Merge carets that overlap, this needs to be handled before any edits to
|
||||
// make sure overlapping edits won't happen.
|
||||
//
|
||||
// mouse_selection_anchor is special case for mouse handling !
|
||||
void MergeCarets(Buffer *buffer, Array<Caret> *carets) {
|
||||
API void MergeCarets(Buffer *buffer, Array<Caret> *carets) {
|
||||
ProfileFunction();
|
||||
For(*carets) it.range = Clamp(buffer, it.range);
|
||||
Caret first_caret = carets->data[0];
|
||||
@@ -1183,3 +1157,200 @@ void MergeCarets(Buffer *buffer, Array<Caret> *carets) {
|
||||
|
||||
Swap(&carets->data[first_caret_index], &carets->data[0]);
|
||||
}
|
||||
|
||||
API void InitBuffer(Allocator allocator, Buffer *buffer, BufferID id = {}, String name = "", Int size = 4096) {
|
||||
buffer->id = id;
|
||||
buffer->name = name;
|
||||
buffer->cap = size;
|
||||
buffer->data = AllocArray(allocator, U16, buffer->cap);
|
||||
buffer->line_starts.allocator = allocator;
|
||||
buffer->undo_stack.allocator = allocator;
|
||||
buffer->redo_stack.allocator = allocator;
|
||||
if (!buffer->no_line_starts) {
|
||||
Add(&buffer->line_starts, (Int)0);
|
||||
}
|
||||
}
|
||||
|
||||
API void DeinitBuffer(Buffer *buffer) {
|
||||
Assert(buffer->next == NULL);
|
||||
Assert(buffer->prev == NULL);
|
||||
Allocator allocator = buffer->line_starts.allocator;
|
||||
Dealloc(allocator, &buffer->data);
|
||||
Dealloc(&buffer->line_starts);
|
||||
DeallocHistoryArray(&buffer->undo_stack);
|
||||
DeallocHistoryArray(&buffer->redo_stack);
|
||||
}
|
||||
|
||||
void TestBuffer() {
|
||||
Scratch scratch;
|
||||
{
|
||||
Buffer buffer = {};
|
||||
InitBuffer(scratch, &buffer);
|
||||
Assert(buffer.line_starts.len == 1);
|
||||
String16 test_string = u"Thing itself";
|
||||
{
|
||||
RawReplaceText(&buffer, {}, test_string);
|
||||
Assert(buffer.cap == 4096);
|
||||
Assert(buffer.len == 12);
|
||||
String16 a = GetString(&buffer);
|
||||
Assert(a == test_string);
|
||||
Assert(buffer.line_starts.len == 1);
|
||||
Assert(buffer.line_starts[0] == 0);
|
||||
Assert(PosToLine(&buffer, 4) == 0);
|
||||
}
|
||||
{
|
||||
RawReplaceText(&buffer, {0, 5}, u"");
|
||||
Assert(buffer.cap == 4096);
|
||||
Assert(buffer.len == 12 - 5);
|
||||
String16 a = GetString(&buffer);
|
||||
Assert(a == u" itself");
|
||||
Assert(buffer.line_starts.len == 1);
|
||||
Assert(buffer.line_starts[0] == 0);
|
||||
Assert(PosToLine(&buffer, 4) == 0);
|
||||
}
|
||||
{
|
||||
RawReplaceText(&buffer, GetBufferEndAsRange(&buffer), u" and");
|
||||
Assert(buffer.cap == 4096);
|
||||
Assert(buffer.len == 12 - 5 + 4);
|
||||
String16 a = GetString(&buffer);
|
||||
Assert(a == u" itself and");
|
||||
Assert(buffer.line_starts.len == 1);
|
||||
Assert(buffer.line_starts[0] == 0);
|
||||
Assert(PosToLine(&buffer, 4) == 0);
|
||||
}
|
||||
{
|
||||
RawReplaceText(&buffer, GetRange(&buffer), u"");
|
||||
Assert(buffer.cap == 4096);
|
||||
Assert(buffer.len == 0);
|
||||
String16 a = GetString(&buffer);
|
||||
Assert(a == u"");
|
||||
Assert(buffer.line_starts.len == 1);
|
||||
Assert(buffer.line_starts[0] == 0);
|
||||
}
|
||||
{
|
||||
RawReplaceText(&buffer, GetBufferEndAsRange(&buffer), u"Memes and other\nthings");
|
||||
Assert(buffer.line_starts.len == 2);
|
||||
Assert(PosToLine(&buffer, 17) == 1);
|
||||
Assert(PosToLine(&buffer, 16) == 1);
|
||||
Assert(PosToLine(&buffer, 15) == 0);
|
||||
Assert(buffer.data[15] == L'\n');
|
||||
Assert(buffer.data[16] == L't');
|
||||
|
||||
RawReplaceText(&buffer, GetBufferBeginAsRange(&buffer), u"Things as is\nand stuff\n");
|
||||
Assert(buffer.line_starts.len == 4);
|
||||
Assert(PosToLine(&buffer, 12) == 0);
|
||||
Assert(buffer.data[12] == L'\n');
|
||||
Assert(PosToLine(&buffer, 13) == 1);
|
||||
Assert(PosToLine(&buffer, 21) == 1);
|
||||
Assert(PosToLine(&buffer, 22) == 1);
|
||||
Assert(buffer.data[22] == L'\n');
|
||||
Assert(PosToLine(&buffer, 23) == 2);
|
||||
Assert(PosToLine(&buffer, 37) == 2);
|
||||
Assert(PosToLine(&buffer, 38) == 2);
|
||||
Assert(buffer.data[38] == L'\n');
|
||||
Assert(PosToLine(&buffer, 39) == 3);
|
||||
Assert(buffer.data[39] == L't');
|
||||
|
||||
RawReplaceText(&buffer, GetBufferBeginAsRange(&buffer), u"a");
|
||||
Assert(buffer.line_starts.len == 4);
|
||||
Assert(PosToLine(&buffer, 13) == 0);
|
||||
Assert(PosToLine(&buffer, 14) == 1);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
Buffer buffer = {};
|
||||
InitBuffer(scratch, &buffer);
|
||||
RawReplaceText(&buffer, {}, u"Thing\nmeme");
|
||||
Assert(PosToLine(&buffer, 5) == 0);
|
||||
Assert(PosToLine(&buffer, 6) == 1);
|
||||
RawReplaceText(&buffer, {0, 1}, u"");
|
||||
Assert(PosToLine(&buffer, 4) == 0);
|
||||
Assert(PosToLine(&buffer, 5) == 1);
|
||||
RawReplaceText(&buffer, {4, 5}, u"");
|
||||
Assert(buffer.line_starts.len == 1);
|
||||
RawValidateLineStarts(&buffer);
|
||||
}
|
||||
|
||||
{
|
||||
Buffer buffer = {};
|
||||
InitBuffer(scratch, &buffer);
|
||||
RawReplaceText(&buffer, {}, u"Thing\nmeme");
|
||||
RawReplaceText(&buffer, {0, 5}, u"per\ncop");
|
||||
Assert(buffer.line_starts.len == (Int)3);
|
||||
Assert(PosToLine(&buffer, 3) == 0);
|
||||
Assert(PosToLine(&buffer, 4) == 1);
|
||||
RawValidateLineStarts(&buffer);
|
||||
|
||||
RawReplaceText(&buffer, {0, 8}, u"Thing\nmeme");
|
||||
RawReplaceText(&buffer, {0, 3}, u"Thing\nmeme");
|
||||
RawReplaceText(&buffer, {9, 13}, u"Thing\nmeme");
|
||||
RawReplaceText(&buffer, {4, 5}, u"Thing\nmeme\n\n\n");
|
||||
RawReplaceText(&buffer, {22, 23}, u"\n\n\nThing\nmeme\n\n\n");
|
||||
RawReplaceText(&buffer, {22, 23}, u"\n\n\nThing\nmeme\n\n\n");
|
||||
RawReplaceText(&buffer, {22, 23}, u"\n\n\nThing\nmeme\n\n\n");
|
||||
RawReplaceText(&buffer, {22, 23}, u"\n\n\nThing\nmeme\n\n\n");
|
||||
RawValidateLineStarts(&buffer);
|
||||
}
|
||||
|
||||
{
|
||||
Buffer buffer = {};
|
||||
InitBuffer(GetTrackingAllocator(), &buffer);
|
||||
RawReplaceText(&buffer, {}, u"Thing\nmeme");
|
||||
RawReplaceText(&buffer, GetBufferEndAsRange(&buffer), u"\nnewThing");
|
||||
DeinitBuffer(&buffer);
|
||||
TrackingAllocatorCheck();
|
||||
}
|
||||
|
||||
//
|
||||
{
|
||||
Buffer buffer = {};
|
||||
InitBuffer(GetTrackingAllocator(), &buffer);
|
||||
RawReplaceText(&buffer, {}, u"Testing\nthings");
|
||||
Array<Caret> carets = {scratch};
|
||||
Add(&carets, MakeCaret(0,7));
|
||||
Add(&carets, MakeCaret(8,9));
|
||||
Add(&carets, MakeCaret(GetBufferEnd(&buffer)));
|
||||
Array<Edit> edits = BeginEdit(scratch, &buffer, carets);
|
||||
MergeCarets(&buffer, &carets);
|
||||
AddEdit(&edits, {0, 7}, u"t");
|
||||
AddEdit(&edits, {8, 9}, u"T");
|
||||
AddEdit(&edits, GetBufferEndAsRange(&buffer), u"\nnewThing");
|
||||
EndEdit(&buffer, &edits, &carets, KILL_SELECTION);
|
||||
String16 s = GetString(&buffer);
|
||||
Assert(s == u"t\nThings\nnewThing");
|
||||
DeinitBuffer(&buffer);
|
||||
TrackingAllocatorCheck();
|
||||
}
|
||||
|
||||
// Make sure no_history and no line_starts properly makes sure of these
|
||||
{
|
||||
Buffer buffer = {};
|
||||
buffer.no_history = true;
|
||||
buffer.no_line_starts = true;
|
||||
InitBuffer(GetTrackingAllocator(), &buffer);
|
||||
RawReplaceText(&buffer, {}, u"Testing\nthings");
|
||||
Array<Caret> carets = {scratch};
|
||||
Add(&carets, MakeCaret(0,7));
|
||||
Add(&carets, MakeCaret(8,9));
|
||||
Add(&carets, MakeCaret(GetBufferEnd(&buffer)));
|
||||
Array<Edit> edits = BeginEdit(scratch, &buffer, carets);
|
||||
MergeCarets(&buffer, &carets);
|
||||
AddEdit(&edits, {0, 7}, u"t");
|
||||
AddEdit(&edits, {8, 9}, u"T");
|
||||
AddEdit(&edits, GetBufferEndAsRange(&buffer), u"\nnewThing");
|
||||
EndEdit(&buffer, &edits, &carets, KILL_SELECTION);
|
||||
String16 s = GetString(&buffer);
|
||||
Assert(s == u"t\nThings\nnewThing");
|
||||
Assert(buffer.line_starts.len == 0);
|
||||
Assert(buffer.line_starts.data == 0);
|
||||
Assert(buffer.redo_stack.len == 0);
|
||||
Assert(buffer.redo_stack.data == 0);
|
||||
Assert(buffer.undo_stack.len == 0);
|
||||
Assert(buffer.undo_stack.data == 0);
|
||||
DeinitBuffer(&buffer);
|
||||
TrackingAllocatorCheck();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -51,30 +51,113 @@ struct FuzzyPair {
|
||||
enum {
|
||||
KILL_SELECTION = 1,
|
||||
};
|
||||
constexpr Int LAST_LINE = INT64_MAX;
|
||||
|
||||
void SaveCaretHistoryBeforeBeginEdit(Buffer *buffer, Array<Caret> &carets);
|
||||
Array<Edit> BeginEdit(Allocator allocator, Buffer *buffer, Array<Caret> &carets);
|
||||
void EndEdit(Buffer *buffer, Array<Edit> *edits, Array<Caret> *carets, bool kill_selection);
|
||||
void AddEdit(Array<Edit> *e, Range range, String16 string);
|
||||
// @todo: buffer init, deinit
|
||||
API Range MakeRange(Int a, Int b);
|
||||
API Range MakeRange(Int a);
|
||||
API Int GetSize(Range range);
|
||||
API bool AreEqual(Range a, Range b);
|
||||
API bool AreOverlapping(Range a, Range b);
|
||||
API bool InBounds(Range range, Int pos);
|
||||
API Range operator-(Range a, Int value);
|
||||
API Range operator-=(Range &range, Int value);
|
||||
API Range operator+(Range a, Int value);
|
||||
API Range operator+=(Range &range, Int value);
|
||||
API Range GetBufferEndAsRange(Buffer *buffer);
|
||||
API Range GetBufferBeginAsRange(Buffer *buffer);
|
||||
API Range GetRange(Buffer *buffer);
|
||||
API Int Clamp(const Buffer *buffer, Int pos);
|
||||
API Range Clamp(const Buffer *buffer, Range range);
|
||||
|
||||
API Int GetFront(Caret caret);
|
||||
API Int GetBack(Caret caret);
|
||||
API Caret MakeCaret(Int pos);
|
||||
API Caret MakeCaret(Int front, Int back);
|
||||
API Caret SetBack(Caret caret, Int back);
|
||||
API Caret SetFront(Caret caret, Int front);
|
||||
API Caret SetFrontWithAnchor(Caret caret, Caret anchor, Int p);
|
||||
API bool AreEqual(Caret a, Caret b);
|
||||
API bool AreOverlapping(Caret a, Caret b);
|
||||
|
||||
API Range GetLineRange(Buffer *buffer, Int line, Int *end_of_buffer = NULL);
|
||||
API Range GetLineRangeWithoutNL(Buffer *buffer, Int line);
|
||||
API String16 GetString(Buffer *buffer, Range range = {0, INT64_MAX});
|
||||
API String AllocCharString(Allocator allocator, Buffer *buffer, Range range = {0, INT64_MAX});
|
||||
API String16 GetLineString(Buffer *buffer, Int line, Int *end_of_buffer = NULL);
|
||||
API String16 GetLineStringWithoutNL(Buffer *buffer, Int line);
|
||||
|
||||
API XY XYLine(Int line);
|
||||
API Int LastLine(Buffer *buffer);
|
||||
API Int PosToLine(Buffer *buffer, Int pos);
|
||||
API XY PosToXY(Buffer *buffer, Int pos);
|
||||
API Int XYToPos(Buffer *buffer, XY xy);
|
||||
API Int XYToPosWithoutNL(Buffer *buffer, XY xy);
|
||||
API Int XYToPosErrorOutOfBounds(Buffer *buffer, XY xy);
|
||||
|
||||
API char16_t GetChar(Buffer *buffer, Int pos);
|
||||
API bool InBounds(const Buffer *buffer, Int pos);
|
||||
API Int GetWordStart(Buffer *buffer, Int pos);
|
||||
API Int GetWordEnd(Buffer *buffer, Int pos);
|
||||
API bool IsLoadWord(char16_t w);
|
||||
API Int GetLoadWordStart(Buffer *buffer, Int pos);
|
||||
API Int GetLoadWordEnd(Buffer *buffer, Int pos);
|
||||
API Range EncloseLoadWord(Buffer *buffer, Int pos);
|
||||
API Int GetNextWordEnd(Buffer *buffer, Int pos);
|
||||
API Int GetPrevWordStart(Buffer *buffer, Int pos);
|
||||
API Int GetLineStart(Buffer *buffer, Int pos);
|
||||
API Int GetLineEnd(Buffer *buffer, Int pos);
|
||||
API Int GetFullLineStart(Buffer *buffer, Int pos);
|
||||
API Int GetFullLineEnd(Buffer *buffer, Int pos, Int *eof = NULL);
|
||||
API Int GetBufferEnd(Buffer *buffer);
|
||||
API Int GetBufferStart(Buffer *buffer);
|
||||
API Int GetNextEmptyLineStart(Buffer *buffer, Int pos);
|
||||
API Int GetPrevEmptyLineStart(Buffer *buffer, Int pos);
|
||||
API Range EncloseWord(Buffer *buffer, Int pos);
|
||||
API Int SkipSpaces(Buffer *buffer, Int seek);
|
||||
API Int FindScopeEnd(Buffer *buffer, Int seek, Int max_seek, char16_t open, char16_t close);
|
||||
API Range EncloseScope(Buffer *buffer, Int pos_min, Int pos_max, char16_t open, char16_t close);
|
||||
API Range EncloseLine(Buffer *buffer, Int pos);
|
||||
API Range EncloseFullLine(Buffer *buffer, Int pos);
|
||||
API Int OffsetByLine(Buffer *buffer, Int pos, Int line_offset);
|
||||
API Int GetNextChar(Buffer *buffer, Int pos);
|
||||
API Int GetPrevChar(Buffer *buffer, Int pos);
|
||||
API Int GetLineIndent(Buffer *buffer, Int line);
|
||||
API Int GetIndentAtPos(Buffer *buffer, Int pos);
|
||||
API Range GetIndentRangeAtPos(Buffer *buffer, Int pos);
|
||||
|
||||
API Int FuzzyRate(String16 string, String16 with);
|
||||
API Array<FuzzyPair> FuzzySearchLines(Allocator allocator, Buffer *buffer, Int line_min, Int line_max, String16 needle);
|
||||
API Int FindRangeByPos(Array<Range> *ranges, Int pos);
|
||||
|
||||
// These don't handle history, just raw operations on buffer memory
|
||||
API void RawReplaceText(Buffer *buffer, Range range, String16 string);
|
||||
API void RawAppend(Buffer *buffer, String16 string);
|
||||
API void RawAppend(Buffer *buffer, String string);
|
||||
API void RawAppendf(Buffer *buffer, const char *fmt, ...);
|
||||
|
||||
// We can invoke SaveCaretHistoryBeforeBeginEdit(which is basically BeginEdit with
|
||||
// different name before caret altering commands to save caret history
|
||||
// and then call some editing command to edit which is not going to save carets
|
||||
API void SaveCaretHistoryBeforeBeginEdit(Buffer *buffer, Array<Caret> &carets);
|
||||
API Array<Edit> BeginEdit(Allocator allocator, Buffer *buffer, Array<Caret> &carets);
|
||||
API void EndEdit(Buffer *buffer, Array<Edit> *edits, Array<Caret> *carets, bool kill_selection);
|
||||
API void AddEdit(Array<Edit> *e, Range range, String16 string);
|
||||
|
||||
// Merge carets that overlap, this needs to be handled before any edits to
|
||||
// make sure overlapping edits won't happen.
|
||||
//
|
||||
// mouse_selection_anchor is special case for mouse handling !
|
||||
void MergeCarets(Buffer *buffer, Array<Caret> *carets);
|
||||
API void MergeCarets(Buffer *buffer, Array<Caret> *carets);
|
||||
|
||||
// Adjusts caret copies after edit to make them not move after for example
|
||||
// a bar modification. Sometimes works, sometimes doesn't, depends, not an all solving tool.
|
||||
// For example in case of ReopenBuffer, when we select and replace entire buffer, it didn't quite work.
|
||||
void AdjustCarets(Array<Edit> edits, Array<Caret> *carets);
|
||||
API void AdjustCarets(Array<Edit> edits, Array<Caret> *carets);
|
||||
API void AssertRanges(Array<Caret> carets);
|
||||
|
||||
void RedoEdit(Buffer *buffer, Array<Caret> *carets);
|
||||
void UndoEdit(Buffer *buffer, Array<Caret> *carets);
|
||||
API void RedoEdit(Buffer *buffer, Array<Caret> *carets);
|
||||
API void UndoEdit(Buffer *buffer, Array<Caret> *carets);
|
||||
|
||||
// Raw buffer operations
|
||||
void RawReplaceText(Buffer *buffer, Range range, String16 string);
|
||||
void RawAppend(Buffer *buffer, String16 string);
|
||||
void RawAppend(Buffer *buffer, String string);
|
||||
void RawAppendf(Buffer *buffer, const char *fmt, ...);
|
||||
|
||||
// @todo: buffer init, deinit
|
||||
API void DeallocHistoryArray(Array<HistoryEntry> *entries);
|
||||
API void DeallocHistoryEntries(Array<HistoryEntry> *entries);
|
||||
@@ -174,12 +174,12 @@ void Command_Append(View *view, String16 string, bool scroll_to_end_if_cursor_on
|
||||
Add(&view_info, vi);
|
||||
}
|
||||
|
||||
Command_SelectRangeOneCursor(view, GetEndAsRange(buffer));
|
||||
Command_SelectRangeOneCursor(view, GetBufferEndAsRange(buffer));
|
||||
Command_Replace(view, string);
|
||||
|
||||
For (view_info) {
|
||||
if (it.scroll_to_end) {
|
||||
it.view->carets[0] = MakeCaret(GetEndAsRange(buffer).min);
|
||||
it.view->carets[0] = MakeCaret(GetBufferEndAsRange(buffer).min);
|
||||
} else {
|
||||
Dealloc(&it.view->carets);
|
||||
it.view->carets = it.carets;
|
||||
@@ -204,8 +204,10 @@ void ReportErrorf(const char *fmt, ...) {
|
||||
STRING_FORMAT(scratch, fmt, string);
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error!", string.data, NULL);
|
||||
View *view = GetView(NullViewID);
|
||||
if (view) {
|
||||
Command_Appendf(view, "%S\n", string);
|
||||
ActiveWindow = NullWindowID;
|
||||
}
|
||||
}
|
||||
|
||||
void ReportConsolef(const char *fmt, ...) {
|
||||
@@ -427,9 +429,9 @@ void Command_MoveLine(View *view, int direction) {
|
||||
|
||||
AddEdit(&edits, lines_to_move_range, {});
|
||||
if (direction == DIR_DOWN) {
|
||||
AddEdit(&edits, Rng(next_line_end), string);
|
||||
AddEdit(&edits, MakeRange(next_line_end), string);
|
||||
} else {
|
||||
AddEdit(&edits, Rng(prev_line_start), string);
|
||||
AddEdit(&edits, MakeRange(prev_line_start), string);
|
||||
}
|
||||
|
||||
Add(&saved_xy, {PosToXY(buffer, GetFront(it)), PosToXY(buffer, GetBack(it))});
|
||||
@@ -481,7 +483,7 @@ void Command_DuplicateLine(View *view, int direction) {
|
||||
String16 string = Copy16(scratch, GetString(buffer, range));
|
||||
|
||||
Int pos = direction == DIR_UP ? range.min : range.max;
|
||||
AddEdit(&edits, Rng(pos), string);
|
||||
AddEdit(&edits, MakeRange(pos), string);
|
||||
}
|
||||
EndEdit(buffer, &edits, &view->carets, !KILL_SELECTION);
|
||||
|
||||
@@ -945,13 +947,13 @@ void Command_FuzzySort(View *view, String16 needle) {
|
||||
For(ratings) {
|
||||
String16 s = GetLineStringWithoutNL(buffer, it.index);
|
||||
if (s.len == 0) continue;
|
||||
RawReplaceText(temp_buffer, GetEndAsRange(temp_buffer), s);
|
||||
RawReplaceText(temp_buffer, GetEndAsRange(temp_buffer), u"\n");
|
||||
RawReplaceText(temp_buffer, GetBufferEndAsRange(temp_buffer), s);
|
||||
RawReplaceText(temp_buffer, GetBufferEndAsRange(temp_buffer), u"\n");
|
||||
}
|
||||
|
||||
Command_SelectEntireBuffer(view);
|
||||
Command_Replace(view, GetString(temp_buffer));
|
||||
Command_SelectRangeOneCursor(view, Rng(0));
|
||||
Command_SelectRangeOneCursor(view, MakeRange(0));
|
||||
}
|
||||
|
||||
void ReopenBuffer(Buffer *buffer) {
|
||||
@@ -1081,7 +1083,7 @@ void Command_ListCode(String dir = WorkDir) {
|
||||
ListFilesRecursive(main.buffer, dir);
|
||||
main.view->fuzzy_search = true;
|
||||
main.view->update_scroll = true;
|
||||
Command_SelectRangeOneCursor(main.view, GetEndAsRange(main.buffer));
|
||||
Command_SelectRangeOneCursor(main.view, GetBufferEndAsRange(main.buffer));
|
||||
}
|
||||
|
||||
int Lua_ListCode(lua_State *L) {
|
||||
@@ -1205,7 +1207,7 @@ int Lua_Cmd(lua_State *L) {
|
||||
BSet set = GetConsoleSet();
|
||||
main.window->active_goto_list = set.view->id;
|
||||
main.window->goto_list_pos = set.buffer->len;
|
||||
Command_SelectRangeOneCursor(set.view, Rng(set.buffer->len));
|
||||
Command_SelectRangeOneCursor(set.view, MakeRange(set.buffer->len));
|
||||
Command_BeginJump(&set);
|
||||
Exec(set.view->id, true, cmd, working_dir);
|
||||
Command_EndJump(set);
|
||||
@@ -1234,7 +1236,7 @@ void Command_ListBuffers() {
|
||||
}
|
||||
main.view->fuzzy_search = true;
|
||||
main.view->update_scroll = true;
|
||||
Command_SelectRangeOneCursor(main.view, GetEndAsRange(main.buffer));
|
||||
Command_SelectRangeOneCursor(main.view, GetBufferEndAsRange(main.buffer));
|
||||
}
|
||||
|
||||
int Lua_ListBuffers(lua_State *L) {
|
||||
|
||||
@@ -431,7 +431,7 @@ void OnCommand(Event event) {
|
||||
if (ShiftPress(SDLK_PAGEUP)) {
|
||||
Command_MoveCursorsByPageSize(active.window, DIR_UP, SHIFT_PRESS);
|
||||
} else if (CtrlPress(SDLK_PAGEUP)) {
|
||||
Command_SelectRangeOneCursor(active.view, Rng(0));
|
||||
Command_SelectRangeOneCursor(active.view, MakeRange(0));
|
||||
} else if (Press(SDLK_PAGEUP)) {
|
||||
Command_MoveCursorsByPageSize(active.window, DIR_UP);
|
||||
}
|
||||
@@ -439,7 +439,7 @@ void OnCommand(Event event) {
|
||||
if (ShiftPress(SDLK_PAGEDOWN)) {
|
||||
Command_MoveCursorsByPageSize(active.window, DIR_DOWN, SHIFT_PRESS);
|
||||
} else if (CtrlPress(SDLK_PAGEDOWN)) {
|
||||
Command_SelectRangeOneCursor(active.view, Rng(active.buffer->len));
|
||||
Command_SelectRangeOneCursor(active.view, MakeRange(active.buffer->len));
|
||||
} else if (Press(SDLK_PAGEDOWN)) {
|
||||
Command_MoveCursorsByPageSize(active.window, DIR_DOWN);
|
||||
}
|
||||
@@ -575,10 +575,10 @@ void OnCommand(Event event) {
|
||||
For(IterateInReverse(&ratings)) {
|
||||
String16 s = GetLineStringWithoutNL(active.buffer, it.index);
|
||||
if (s.len == 0) continue;
|
||||
RawReplaceText(temp_buffer, GetEndAsRange(temp_buffer), s);
|
||||
RawReplaceText(temp_buffer, GetEndAsRange(temp_buffer), u"\n");
|
||||
RawReplaceText(temp_buffer, GetBufferEndAsRange(temp_buffer), s);
|
||||
RawReplaceText(temp_buffer, GetBufferEndAsRange(temp_buffer), u"\n");
|
||||
}
|
||||
RawReplaceText(temp_buffer, GetEndAsRange(temp_buffer), last_line_string);
|
||||
RawReplaceText(temp_buffer, GetBufferEndAsRange(temp_buffer), last_line_string);
|
||||
|
||||
Caret caret = active.view->carets[0];
|
||||
Command_SelectEntireBuffer(active.view);
|
||||
|
||||
@@ -390,6 +390,12 @@ int main(int argc, char **argv)
|
||||
BeginProfiler();
|
||||
InitScratch();
|
||||
|
||||
if (1) {
|
||||
TestBuffer();
|
||||
ReportErrorf("Testing DONE\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if !OS_WINDOWS
|
||||
for (int i = 0; environ[i]; i += 1) {
|
||||
Add(&Enviroment, Copy(GetSystemAllocator(), environ[i]));
|
||||
|
||||
@@ -107,7 +107,7 @@ void ReplaceTitleBarData(Window *window) {
|
||||
|
||||
// add separator at the end of buffer
|
||||
if (!found_separator) {
|
||||
Command_SelectRangeOneCursor(title.view, GetEndAsRange(title.buffer));
|
||||
Command_SelectRangeOneCursor(title.view, GetBufferEndAsRange(title.buffer));
|
||||
Array<Edit> edits = Command_ReplaceEx(scratch, title.view, u" |");
|
||||
AdjustCarets(edits, &caret_copy);
|
||||
}
|
||||
@@ -127,7 +127,7 @@ void ReplaceTitleBarData(Window *window) {
|
||||
if (string_to_replace != string) {
|
||||
Command_SelectRangeOneCursor(title.view, replace_range);
|
||||
Array<Edit> edits = Command_ReplaceEx(scratch, title.view, string);
|
||||
Command_SelectRangeOneCursor(title.view, Rng(0));
|
||||
Command_SelectRangeOneCursor(title.view, MakeRange(0));
|
||||
AdjustCarets(edits, &caret_copy);
|
||||
}
|
||||
}
|
||||
@@ -112,13 +112,13 @@ bool ToggleVisibility(WindowID window_id) {
|
||||
|
||||
void LoadBigText(Buffer *buffer, int size = 5000000) {
|
||||
for (int i = 0; i < size; i += 1) {
|
||||
RawReplaceText(buffer, GetEndAsRange(buffer), u"Line number or something of the sort which is here or there or maybe somewhere else\n");
|
||||
RawReplaceText(buffer, GetBufferEndAsRange(buffer), u"Line number or something of the sort which is here or there or maybe somewhere else\n");
|
||||
}
|
||||
}
|
||||
|
||||
void LoadBigLine(Buffer *buffer, int size = 5000000) {
|
||||
for (int i = 0; i < size; i += 1) {
|
||||
RawReplaceText(buffer, GetEndAsRange(buffer), u"Line number or something of the sort which is here or there or maybe somewhere else | ");
|
||||
RawReplaceText(buffer, GetBufferEndAsRange(buffer), u"Line number or something of the sort which is here or there or maybe somewhere else | ");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,7 +132,7 @@ void LoadTextA(Buffer *buffer) {
|
||||
for (int i = 0; i < 1000; i += 1) {
|
||||
String s = Format(scratch, "line1: %d line2: %d line3: %d line4: %d line5: %d line6: %d line1: %d line2: %d line3: %d line4: %d line5: %d line6: %d\r\n", i, i, i, i, i, i, i, i, i, i, i, i);
|
||||
String16 s16 = ToString16(scratch, s);
|
||||
RawReplaceText(buffer, GetEndAsRange(buffer), s16);
|
||||
RawReplaceText(buffer, GetBufferEndAsRange(buffer), s16);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ https://www.lua.org/manual/5.4/
|
||||
Scratch scratch;
|
||||
RawReplaceText(buffer, {}, ToString16(scratch, text));
|
||||
|
||||
// RawReplaceText(buffer, GetEndAsRange(buffer), ToString16(scratch, text));
|
||||
// RawReplaceText(buffer, GetBufferEndAsRange(buffer), ToString16(scratch, text));
|
||||
}
|
||||
|
||||
void InitWindows() {
|
||||
|
||||
Reference in New Issue
Block a user