summary refs log tree commit diff
path: root/src/Buffer.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/Buffer.zig')
-rw-r--r--src/Buffer.zig79
1 files changed, 57 insertions, 22 deletions
diff --git a/src/Buffer.zig b/src/Buffer.zig
index faee5c4..b1aacaf 100644
--- a/src/Buffer.zig
+++ b/src/Buffer.zig
@@ -27,7 +27,8 @@ const initial_gap_size = std.atomic.cache_line;
 
 const Buffer = @This();
 /// File content with gap, owned by Janet runtime.
-data: []u8,
+data: []const u8,
+file: []const u8,
 gap_position: u32 = 0,
 gap_size: u32 = initial_gap_size,
 parser: *Parser,
@@ -53,10 +54,11 @@ fn read(payload: ?*anyopaque, byte_index: u32, _: Point,
     }
 }
 
-pub fn open(text: []u8) !Buffer {
+pub fn open(text: []const u8, file: []const u8) !Buffer {
     const data = gap: {
+        const unsafe_ptr: *anyopaque = @constCast(@ptrCast(text.ptr));
         const n: i32 = @intCast(text.len);
-        const buffer = janet.pointerBufferUnsafe(@ptrCast(text.ptr), n, n);
+        const buffer = janet.pointerBufferUnsafe(unsafe_ptr, n, n);
         const capacity: usize = @intCast(n + initial_gap_size);
         if (janet.realloc(buffer.*.data, capacity)) |data| {
             janet.gcpressure(initial_gap_size);
@@ -78,6 +80,7 @@ pub fn open(text: []u8) !Buffer {
     try parser.setLanguage(language);
     var result = Buffer{
         .data = data,
+        .file = file,
         .parser = parser,
         .tree = undefined,
         .selection = undefined,
@@ -86,10 +89,8 @@ pub fn open(text: []u8) !Buffer {
         .payload = &result,
         .read = Buffer.read,
     }, null).?;
-    result.selection = .{
-        .head = .{ .node = result.tree.rootNode() },
-        .tail = .{ .node = result.tree.rootNode() },
-    };
+    const span0 = Selection.Unit{ .span = .{ .tree = result.tree } };
+    result.selection = .{ .head = span0, .tail = span0 };
     return result;
 }
 
@@ -99,11 +100,37 @@ pub fn close(buffer: Buffer) void {
     buffer.parser.destroy();
 }
 
+pub fn length(buffer: Buffer) u32 {
+    return @intCast(buffer.data.len - buffer.gap_size);
+}
+
+pub fn graphemeAt(buffer: Buffer, n: u32) Grapheme {
+    const text = if (n < buffer.gap_position)
+        buffer.data[n..buffer.gap_position]
+    else
+        buffer.data[buffer.gap_size..][n..];
+    var iterator = graphemes.iterator(text);
+    return .{ .offset = n, .len = iterator.peek().?.len };
+}
+
+pub fn graphemeBefore(buffer: Buffer, n: u32) ?Grapheme {
+    if (n == 0)
+        return null;
+    const text = if (n <= buffer.gap_position)
+        buffer.data[0..n]
+    else
+        buffer.data[buffer.gap_size..][buffer.gap_position..n];
+    var iterator = graphemes.reverseIterator(text);
+    const len = iterator.peek().?.len;
+    return .{ .offset = n - len, .len = len };
+}
+
 fn skipLines(text: []const u8, from: u32, n: u32) union(enum) {
     done: u32,
     left: u32,
 } {
-    assert(n > 0);
+    if (n == 0)
+        return .{ .done = from };
     var lines: u32 = 0;
     for (text[from..], 1..) |c, i|
         if (c == '\n') {
@@ -138,20 +165,28 @@ pub fn iterate(buffer: Buffer) struct {
         } else return null;
     }
 } {
-    const root_node = buffer.tree.rootNode();
     const point = Point{ .row = buffer.scroll_row, .column = 0 };
-    const node = root_node.descendantForPointRange(point, point).?;
-    const start_point = node.startPoint();
-    assert(start_point.row < point.row or eql(start_point, point));
-
-    const start_byte = node.startByte();
-    const start = if (eql(start_point, point))
-        start_byte
-    else if (start_byte >= buffer.gap_position)
-        skipLines(buffer.data[buffer.gap_size..], start_byte,
-                  point.row - start_point.row).done
-    else switch (skipLines(buffer.data[0..buffer.gap_position], start_byte,
-                           point.row - start_point.row)) {
+    const root_node = buffer.tree.rootNode();
+    const root_start = root_node.startPoint();
+    const start = if (root_start.row < point.row
+                      or eql(root_start, point)) skip: {
+        const node = root_node.descendantForPointRange(point, point).?;
+        const start_point = node.startPoint();
+        const start_byte = node.startByte();
+        if (eql(start_point, point))
+            break :skip start_byte;
+        assert(start_point.row < point.row);
+        break :skip if (start_byte >= buffer.gap_position)
+            skipLines(buffer.data[buffer.gap_size..],
+                      start_byte, point.row - start_point.row).done
+        else switch (skipLines(buffer.data[0..buffer.gap_position],
+                               start_byte, point.row - start_point.row)) {
+            .done => |offset| offset,
+            .left => |left| skipLines(buffer.data[buffer.gap_size..],
+                                      buffer.gap_position, left).done,
+        };
+    } else switch (skipLines(buffer.data[0..buffer.gap_position], 0,
+                             point.row)) {
         .done => |offset| offset,
         .left => |left| skipLines(buffer.data[buffer.gap_size..],
                                   buffer.gap_position, left).done,
@@ -172,7 +207,7 @@ pub fn iterate(buffer: Buffer) struct {
         .graphemes_iterator = graphemes.iterator(buffer.data[offset..]),
         .data = undefined,
         .offset = start,
-        .before_gap = true,
+        .before_gap = false,
         .gap_start = undefined,
         .gap_end = undefined,
     };