diff --git a/.ignore b/.ignore index 6bfd821..a6ae5ac 100644 --- a/.ignore +++ b/.ignore @@ -1,3 +1,4 @@ .git src/VulkanRenderer/vma src/DLibs/external +build diff --git a/dub.json b/dub.json index 27269ab..973a65c 100644 --- a/dub.json +++ b/dub.json @@ -15,7 +15,7 @@ "libs-windows": [], "preGenerateCommands-linux": ["./build.sh"], "preGenerateCommands-windows": [], - "dflags": ["-Xcc=-mno-sse", "-P-I/usr/include/freetype2", "-vgc"], + "dflags": ["-Xcc=-mno-sse", "-P-I/usr/include/freetype2"], "dflags-dmd": ["-P=-DSTBI_NO_SIMD"] }, ] diff --git a/src/editor/buffer.d b/src/editor/buffer.d new file mode 100644 index 0000000..26e47bf --- /dev/null +++ b/src/editor/buffer.d @@ -0,0 +1,211 @@ +import aliases; +import math; +import alloc; +import util; + +struct GapBuffer +{ + u8[] data; + u64 length; + u64 gap_start; + u64 gap_end; + u64 gap_size; +} + +GapBuffer +CreateGapBuffer(u8[] data) +{ + // Match 4k page sizes + u64 capacity = RoundUp(cast(u64)data.length, KB(4)); + if (data.length % KB(4) == 0) + { + capacity += KB(4); + } + + u8[] buf = MAllocArray!(u8)(capacity); + + buf[0 .. data.length] = data[0 .. $]; + + GapBuffer buffer = { + data: buf, + length: data.length, + gap_start: data.length, + gap_end: data.length + (capacity - data.length), + gap_size: capacity - cast(u64)(data.length), + }; + + return buffer; +} + +void +GrowBuffer(GapBuffer* buffer) +{ + u8[] temp_buf = buffer.data[0 .. buffer.length]; + GapBuffer temp_gap = CreateGapBuffer(temp_buf); + MFreeArray(buffer.data); + *buffer = temp_gap; +} + +void +Insert(GapBuffer* buffer, u8[] insert, u64 length, i64 pos) +{ + assert(length <= insert.length, "Insert Error: length is greater than insert buffer length"); + + if (buffer.length + length > buffer.data.length) + { + if (buffer.length < buffer.data.length) + { + Shift(buffer, buffer.data.length); + } + GrowBuffer(buffer); + } + + Shift(buffer, pos); + + buffer.data[buffer.gap_start .. buffer.gap_start+length] = insert[0 .. length]; + buffer.length += length; + buffer.gap_start += length; + buffer.gap_size -= length; +} + +void +Shift(GapBuffer* buffer, i64 pos) +{ + if (pos < 0) + { + pos = 0; + } + else if (pos > buffer.length) + { + pos = buffer.length; + } + + u64 old_pos = buffer.gap_start; + u64 new_pos = cast(u64)pos; + + u64 diff; + if (old_pos < new_pos) + { + diff = new_pos - old_pos; + buffer.data[buffer.gap_start .. buffer.gap_start+diff] = buffer.data[buffer.gap_end .. buffer.gap_end+diff]; + buffer.gap_start += diff; + buffer.gap_end += diff; + } + else if (old_pos > new_pos) + { + diff = old_pos - new_pos; + buffer.data[buffer.gap_end-diff .. buffer.gap_end] = buffer.data[buffer.gap_start-diff .. buffer.gap_start]; + buffer.gap_start -= diff; + buffer.gap_end -= diff; + } +} + +void +GetLines(GapBuffer* buffer, u8[][] lines, u64 start_line) +{ + Shift(buffer, buffer.data.length); + + u64 count; + u64 start; + foreach(i, ch; buffer.data) + { + if (count == lines.length) + { + break; + } + + if (ch == '\n') + { + scope(exit) count += 1; + + if (count < start_line) + { + continue; + } + + lines[count][start .. i-start] = buffer.data[start .. i]; + lines[count][i-start] = '\0'; + start = i + 1; + } + } +} + +void +MoveCursorL(GapBuffer* buffer, u64 length) +{ + assert(length, "MoveCursorL Error: 0 length provided"); + + u64 new_pos = length >= buffer.length ? buffer.length - length : 0; +} + +unittest +{ + // Inserts + { + string test_str = "This is a test string for testing the GapBuffer struct."; + string test_str_2 = "This is an additional string. "; + u8[] data = (cast(u8*)test_str.ptr)[0 .. test_str.length]; + GapBuffer buf = CreateGapBuffer(data); + + u8[] insert = (cast(u8*)test_str_2.ptr)[0 .. test_str_2.length]; + Insert(&buf, insert, insert.length, 0); + + u8[][] line_buf = new u8[][](1, 1024); + GetLines(&buf, line_buf, 0); + + u64 count = 0; + foreach(i, ch; test_str_2) + { + assert(buf.data[count] == ch, "Buffer doesn't match test_str_2"); + count += 1; + } + + foreach(i, ch; test_str) + { + assert(buf.data[count] == ch, "Buffer doesn't match test_str"); + count += 1; + } + + string test_str_3 = "not "; + insert = (cast(u8*)test_str_3.ptr)[0 .. test_str_3.length]; + Insert(&buf, insert, insert.length, 8); + GetLines(&buf, line_buf, 0); + + string result = "This is not an additional string. This is a test string for testing the GapBuffer struct."; + foreach(i, ch; result) + { + assert(buf.data[i] == ch, "Buffer doesn't match result"); + } + } + + // Grow Buffer + { + u8[] data; + GapBuffer buf = CreateGapBuffer(data); + + foreach(i; 0 .. buf.data.length) + { + Insert(&buf, ['a'], 1, 0); + } + + foreach(i; 0 .. 1024) + { + Insert(&buf, ['b'], 1, 0); + } + + u8[][] line_buf = new u8[][](1, 8096); + GetLines(&buf, line_buf, 0); + + foreach(i; 0 .. 5120) + { + if (i < 1024) + { + assert(line_buf[0][i] == 'b'); + } + else + { + assert(line_buf[0][i] == 'a'); + } + } + } +} diff --git a/src/editor/main.d b/src/editor/main.d index 55436b0..29d755c 100644 --- a/src/editor/main.d +++ b/src/editor/main.d @@ -1,6 +1,76 @@ +import aliases; import std.stdio; +import buffer; +import util; void main(string[] argv) { - writeln("Hello, World!"); + // Inserts + { + string test_str = "This is a test string for testing the GapBuffer struct."; + string test_str_2 = "This is an additional string. "; + u8[] data = (cast(u8*)test_str.ptr)[0 .. test_str.length]; + GapBuffer buf = CreateGapBuffer(data); + + u8[] insert = (cast(u8*)test_str_2.ptr)[0 .. test_str_2.length]; + Insert(&buf, insert, insert.length, 0); + + u8[][] line_buf = new u8[][](1, 1024); + GetLines(&buf, line_buf, 0); + + u64 count = 0; + foreach(i, ch; test_str_2) + { + assert(buf.data[count] == ch, "Buffer doesn't match test_str_2"); + count += 1; + } + + foreach(i, ch; test_str) + { + assert(buf.data[count] == ch, "Buffer doesn't match test_str"); + count += 1; + } + + string test_str_3 = "not "; + insert = (cast(u8*)test_str_3.ptr)[0 .. test_str_3.length]; + Insert(&buf, insert, insert.length, 8); + GetLines(&buf, line_buf, 0); + + string result = "This is not an additional string. This is a test string for testing the GapBuffer struct."; + foreach(i, ch; result) + { + assert(buf.data[i] == ch, "Buffer doesn't match result"); + } + } + + // Grow Buffer + { + u8[] data; + GapBuffer buf = CreateGapBuffer(data); + + foreach(i; 0 .. buf.data.length) + { + Insert(&buf, ['a'], 1, 0); + } + + foreach(i; 0 .. 1024) + { + Insert(&buf, ['b'], 1, 0); + } + + u8[][] line_buf = new u8[][](1, 8096); + GetLines(&buf, line_buf, 0); + + foreach(i; 0 .. 5120) + { + if (i < 1024) + { + assert(line_buf[0][i] == 'b'); + } + else + { + assert(line_buf[0][i] == 'a'); + } + } + } }