Working on rendering

This commit is contained in:
Krzosa Karol
2024-06-22 08:35:11 +02:00
parent a329bc2424
commit 94555d2a60
11 changed files with 258 additions and 150 deletions

View File

@@ -9,7 +9,8 @@ much better then the rest.
First of all you can represent the cursor at the end of the buffer
by doing: <buffer_len, buffer_len>. This action in itself doesn't
select anything, the formation doesn't force you to index the
buffer and so on, it's reduced to a pure position.
buffer and so on, it's reduced to a pure position. In the end
you can nicely represent cursors in select mode and non-select mode.
This property of being able to represent pure positions makes
it possible to clamp the values to the same range <0, buffer_len>,
@@ -23,11 +24,21 @@ struct Range {
// <0,4> = 0,1,2,3
};
struct Line {
int64_t number;
Range range;
};
struct Edit {
Range range;
String string;
};
// :buffer_initialized
// @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 {
Allocator allocator;
char *data[2];
@@ -47,54 +58,62 @@ Range GetRange(const Buffer &buffer) {
return result;
}
int64_t Clamp(Buffer *buffer, int64_t pos) {
int64_t result = Clamp(pos, (int64_t)0, buffer->len);
int64_t Clamp(const Buffer &buffer, int64_t pos) {
int64_t result = Clamp(pos, (int64_t)0, buffer.len);
return result;
}
Range Clamp(Buffer *buffer, Range range) {
Range Clamp(const Buffer &buffer, Range range) {
Range result = {};
result.min = Clamp(buffer, range.min);
result.max = Clamp(buffer, range.max);
return result;
}
Range GetEnd(const Buffer &buffer) {
Range range = {buffer.len, buffer.len};
return range;
}
void AddEdit(Array<Edit> *edits, Range range, String string) {
edits->add({range, string});
}
bool InBounds(Buffer *buffer, int64_t pos) {
bool result = pos >= 0 && pos < buffer->len;
bool InBounds(const Buffer &buffer, int64_t pos) {
bool result = pos >= 0 && pos < buffer.len;
return result;
}
char GetChar(Buffer *buffer, int64_t pos) {
char GetChar(const Buffer &buffer, int64_t pos) {
if (!InBounds(buffer, pos)) return 0;
return buffer->data[buffer->bi][pos];
return buffer.data[buffer.bi][pos];
}
char *GetCharP(Buffer *buffer, int64_t pos) {
char *GetCharP(const Buffer &buffer, int64_t pos) {
if (!InBounds(buffer, pos)) return 0;
return buffer->data[buffer->bi] + pos;
return buffer.data[buffer.bi] + pos;
}
String GetString(Buffer *buffer, Range range) {
String GetString(const Buffer &buffer, Range range = {0, INT64_MAX}) {
range = Clamp(buffer, range);
String result = {GetCharP(buffer, range.min), GetRangeSize(range)};
return result;
}
void ApplyEdits(Buffer *buffer, Array<Edit> edits) {
Assert(edits.len);
int64_t size_to_delete = 0;
int64_t size_to_insert = 0;
For(edits) {
it.range.min = Clamp(buffer, it.range.min);
it.range.max = Clamp(buffer, it.range.max);
Assert(it.range.min >= 0);
Assert(it.range.max >= it.range.min);
Assert(it.range.max <= buffer->len);
size_to_delete += GetRangeSize(it.range);
size_to_insert += it.string.len;
}
#if DEBUG_BUILD
// Make sure edit ranges don't overlap
ForItem(it1, edits) {
ForItem(it2, edits) {
if (&it1 == &it2) continue;
@@ -176,6 +195,9 @@ void ApplyEdits(Buffer *buffer, Array<Edit> edits) {
buffer->len = new_buffer_len;
// Update lines
// Make sure we always have one line, even if the buffer is empty,
// this way we can nicely clamp things without worrying of getting
// a negative when doing (len - 1).
{
String delimiter = "\n";
String string = {buffer->data[dsti], buffer->len};
@@ -193,9 +215,46 @@ void ApplyEdits(Buffer *buffer, Array<Edit> edits) {
}
}
int64_t AdjustUTF8Pos(Buffer *buffer, int64_t pos, int64_t direction = 1) {
void InitBuffer(Buffer *buffer) {
Scratch scratch;
Array<Edit> edits = {};
AddEdit(&edits, {}, "");
ApplyEdits(buffer, edits);
}
String CopyNullTerminated(Allocator allocator, Buffer &buffer, Range range) {
String buffer_string = GetString(buffer, range);
String result = Copy(allocator, buffer_string);
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(const Buffer &buffer, int64_t pos, int64_t direction = 1) {
int64_t result = pos;
for (; result >= 0 && result < buffer->len;) {
for (; result >= 0 && result < buffer.len;) {
char c = GetChar(buffer, pos);
if (IsUTF8ContinuationByte(c)) {
result += direction;
@@ -206,13 +265,13 @@ int64_t AdjustUTF8Pos(Buffer *buffer, int64_t pos, int64_t direction = 1) {
return result;
}
uint32_t GetUTF32(Buffer *buffer, int64_t pos, int64_t *codepoint_size) {
uint32_t GetUTF32(Buffer &buffer, int64_t pos, int64_t *codepoint_size) {
if (!InBounds(buffer, pos)) {
return 0;
}
char *p = GetCharP(buffer, pos);
int64_t max = buffer->len - pos;
int64_t max = buffer.len - pos;
UTF32Result utf32 = UTF8ToUTF32(p, (int)max);
Assert(utf32.error == 0);
@@ -245,8 +304,8 @@ bool IsValid(const BufferIter &iter) {
}
if (result) {
Assert(!IsUTF8ContinuationByte(GetChar(iter.buffer, iter.pos)));
Assert(InBounds(iter.buffer, iter.pos));
Assert(!IsUTF8ContinuationByte(GetChar(*iter.buffer, iter.pos)));
Assert(InBounds(*iter.buffer, iter.pos));
}
return result;
}
@@ -258,19 +317,19 @@ void Advance(BufferIter *iter) {
if (iter->direction == ITERATE_FORWARD) {
iter->pos += iter->utf8_codepoint_size;
} else {
iter->pos = AdjustUTF8Pos(iter->buffer, iter->pos - 1, ITERATE_BACKWARD);
iter->pos = AdjustUTF8Pos(*iter->buffer, iter->pos - 1, ITERATE_BACKWARD);
}
if (!IsValid(*iter)) return;
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) {
Assert(direction == ITERATE_FORWARD || direction == ITERATE_BACKWARD);
Assert(!IsUTF8ContinuationByte(GetChar(buffer, range.min)));
Assert(!IsUTF8ContinuationByte(GetChar(*buffer, range.min)));
Assert(range.max >= range.min);
range.min = Clamp(buffer, range.min);
range.max = Clamp(buffer, range.max);
range.min = Clamp(*buffer, range.min);
range.max = Clamp(*buffer, range.max);
BufferIter result = {buffer, range.min, range.max, direction};
if (direction == ITERATE_BACKWARD) {
@@ -292,12 +351,20 @@ void RunBufferTests() {
String string = {buffer.data[buffer.bi], buffer.len};
Assert(string == "Things and other things");
Assert(buffer.lines.len == 1);
Assert(GetString(&buffer, buffer.lines[0]) == "Things and other things");
Assert(GetString(buffer, buffer.lines[0]) == "Things and other things");
edits.clear();
AddEdit(&edits, {1000, 1000}, " memes");
AddEdit(&edits, GetEnd(buffer), " memes");
ApplyEdits(&buffer, edits);
Assert(GetString(&buffer, buffer.lines[0]) == "Things and other things memes");
Assert(GetString(buffer, buffer.lines[0]) == "Things and other things memes");
}
{
Buffer buffer = {scratch};
Array<Edit> edits = {scratch};
edits.add({});
ApplyEdits(&buffer, edits);
Assert("" == GetString(buffer));
Assert(buffer.lines.len == 1);
}
{
Buffer buffer = {scratch};
@@ -317,7 +384,7 @@ void RunBufferTests() {
String string = {buffer.data[buffer.bi], buffer.len};
Assert(string == "Memes dna BigOther things");
Assert(buffer.lines.len == 1);
Assert(GetString(&buffer, buffer.lines[0]) == "Memes dna BigOther things");
Assert(GetString(buffer, buffer.lines[0]) == "Memes dna BigOther things");
}
{
Buffer buffer = {scratch};
@@ -329,9 +396,9 @@ void RunBufferTests() {
});
ApplyEdits(&buffer, edits);
Assert(buffer.lines.len == 3);
Assert(GetString(&buffer, buffer.lines[1]) == "Things and other things");
Assert(GetString(&buffer, buffer.lines[0]) == "Things and other things");
Assert(GetString(&buffer, buffer.lines[2]) == "");
Assert(GetString(buffer, buffer.lines[1]) == "Things and other things");
Assert(GetString(buffer, buffer.lines[0]) == "Things and other things");
Assert(GetString(buffer, buffer.lines[2]) == "");
{
Array<char> s = {scratch};
@@ -361,7 +428,7 @@ void RunBufferTests() {
s.add((char)iter.item);
}
String str = {s.data, s.len};
String b = {GetCharP(&buffer, 0), buffer.len};
String b = {GetCharP(buffer, 0), buffer.len};
Assert(str == b);
}
{
@@ -372,7 +439,7 @@ void RunBufferTests() {
s.add((char)iter.item);
}
String str = {s.data, s.len};
String b = {GetCharP(&buffer, 0), buffer.len};
String b = {GetCharP(buffer, 0), buffer.len};
Assert(str.len == b.len);
}
}
@@ -391,11 +458,28 @@ void RunBufferTests() {
for (int i = 0; i < iters; i += 1) {
ApplyEdits(&buffer, edits);
for (int64_t j = 0; j < i; j += 1) {
String string = GetString(&buffer, {edits[0].string.len * j, edits[0].string.len * (j + 1)});
String string = GetString(buffer, {edits[0].string.len * j, edits[0].string.len * (j + 1)});
Assert(string == edits[0].string);
}
}
Assert(edits[0].string.len * iters == buffer.len);
Assert(buffer.lines.len == iters * 2 + 1);
Line l0 = FindLine(buffer, 4);
Assert(l0.number == 0);
Assert(l0.range.min == 0);
Assert(l0.range.max < 30);
Line l1 = FindLine(buffer, 30);
Assert(l1.number == 1);
Assert(l1.range.min > 20);
Assert(l1.range.max < 50);
Assert(l1.range.max == GetLine(buffer, 1).range.max);
// Make sure there are no gaps
for (int64_t i = 100; i < 600; i += 1) {
Line l2 = FindLine(buffer, i);
Assert(l2.number > 0);
}
}
}