summary refs log tree commit diff
path: root/src/selection.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/selection.zig')
-rw-r--r--src/selection.zig202
1 files changed, 150 insertions, 52 deletions
diff --git a/src/selection.zig b/src/selection.zig
index 1fb8266..44d537f 100644
--- a/src/selection.zig
+++ b/src/selection.zig
@@ -26,15 +26,63 @@ fn graphemeBefore(graphemes: Graphemes, text: []const u8, index: u32) Grapheme {
 // TODO: undo
 pub const Unit = union(enum) {
     node: Node,
-    grapheme: struct {
-        const GraphemeUnit = @This();
+    span: Span,
+    grapheme: GraphemeUnit,
+
+    // TODO: nesting
+    const Span = struct {
+        start: u32,
+        end: u32,
+        tree: *const Tree,
+
+        fn parent(unit: Span) Node {
+            const root_node = unit.tree.rootNode();
+            return root_node.descendantForByteRange(unit.start, unit.end).?;
+        }
+    };
+
+    const GraphemeUnit = struct {
         slice: Grapheme,
         tree: *const Tree,
 
-        fn parent(unit: GraphemeUnit) Node {
-            const start = unit.slice.offset;
-            const end = start + unit.slice.len;
-            return unit.tree.rootNode().descendantForByteRange(start, end).?;
+        fn startByte(unit: GraphemeUnit) u32 {
+            return unit.slice.offset;
+        }
+
+        fn endByte(unit: GraphemeUnit) u32 {
+            return unit.slice.offset + unit.slice.len;
+        }
+
+        fn parent(unit: GraphemeUnit) Span {
+            const start = unit.startByte();
+            const end = unit.endByte();
+            const root_node = unit.tree.rootNode();
+            const parent_node = root_node.descendantForByteRange(start, end).?;
+            if (parent_node.firstChildForByte(end)) |next_node| {
+                return if (next_node.prevSibling()) |prev_node| .{
+                    .start = prev_node.endByte(),
+                    .end = next_node.startByte(),
+                    .tree = unit.tree,
+                } else .{
+                    .start = parent_node.startByte(),
+                    .end = next_node.startByte(),
+                    .tree = unit.tree,
+                };
+            } else {
+                const siblings = parent_node.childCount();
+                if (siblings == 0)
+                    return .{
+                        .start = parent_node.startByte(),
+                        .end = parent_node.endByte(),
+                        .tree = unit.tree,
+                    };
+                const prev_node = parent_node.child(siblings - 1).?;
+                return .{
+                    .start = prev_node.endByte(),
+                    .end = parent_node.endByte(),
+                    .tree = unit.tree,
+                };
+            }
         }
 
         fn at(unit: GraphemeUnit, graphemes: Graphemes,
@@ -52,18 +100,49 @@ pub const Unit = union(enum) {
                 .tree = unit.tree,
             };
         }
-    },
+    };
 
-    pub fn up(unit: Unit) Unit {
+    fn startByte(unit: Unit) u32 {
+        return switch (unit) {
+            .node => |node| node.startByte(),
+            .span => |span| span.start,
+            .grapheme => |grapheme| grapheme.startByte(),
+        };
+    }
+
+    fn endByte(unit: Unit) u32 {
         return switch (unit) {
-            .node => |node| if (node.parent()) |parent_node|
+            .node => |node| node.endByte(),
+            .span => |span| span.end,
+            .grapheme => |grapheme| grapheme.endByte(),
+        };
+    }
+
+    pub fn up(unit: Unit) Unit {
+        switch (unit) {
+            .node => |node| return if (node.parent()) |parent_node|
                 if (parent_node.eql(node))
                     .up(.{ .node = parent_node })
                 else
                     .{ .node = parent_node }
             else unit,
-            .grapheme => |grapheme| .{ .node = grapheme.parent() },
-        };
+            .span => |span| {
+                const parent_node = span.parent();
+                return if (parent_node.startByte() == span.start
+                               and parent_node.endByte() == span.end)
+                    .up(.{ .node = parent_node })
+                else
+                    .{ .node = parent_node };
+            },
+            .grapheme => |grapheme| {
+                const parent_span = grapheme.parent();
+                return if (parent_span.start == grapheme.startByte()
+                               and parent_span.end == grapheme.endByte())
+                    .up(.{ .span = parent_span })
+                else
+                    .{ .span = parent_span };
+            },
+        }
     }
 
     pub fn right(unit: Unit, graphemes: Graphemes, text: []const u8) Unit {
@@ -73,26 +152,29 @@ pub const Unit = union(enum) {
                 const next = sibling.startByte();
                 return switch (order(prev, next)) {
                     .lt => .{
-                        .grapheme = .{
-                            .slice = graphemeAt(graphemes, text, prev),
+                        .span = .{
+                            .start = prev,
+                            .end = next,
                             .tree = node.tree,
                         }
                     },
                     .eq => .{ .node = sibling },
                     .gt => unreachable,
                 };
-            } else return .up(unit),
+            },
+            .span => |span| {
+                const parent_node = unit.up().node;
+                if (parent_node.firstChildForByte(span.end)) |next_node|
+                    return .{ .node = next_node };
+            },
             .grapheme => |grapheme| {
-                const start = grapheme.slice.offset + grapheme.slice.len;
+                const start = grapheme.endByte();
                 const next_grapheme = grapheme.at(graphemes, text, start);
-                const parent = next_grapheme.parent();
-                return if (parent.eql(grapheme.parent())) .{
-                    .grapheme = next_grapheme
-                } else .{
-                    .node = parent
-                };
+                if (eql(next_grapheme.parent(), grapheme.parent()))
+                    return .{ .grapheme = next_grapheme };
             },
         }
+        return .up(unit);
     }
 
     pub fn left(unit: Unit, graphemes: Graphemes, text: []const u8) Unit {
@@ -102,26 +184,34 @@ pub const Unit = union(enum) {
                 const next = node.startByte();
                 return switch (order(prev, next)) {
                     .lt => .{
-                        .grapheme = .{
-                            .slice = graphemeBefore(graphemes, text, next),
+                        .span = .{
+                            .start = prev,
+                            .end = next,
                             .tree = node.tree,
                         }
                     },
                     .eq => .{ .node = sibling },
                     .gt => unreachable,
                 };
-            } else return .up(unit),
+            },
+            .span => |span| {
+                const parent_node = span.parent();
+                if (parent_node.firstChildForByte(span.end)) |next_node| {
+                    if (next_node.prevSibling()) |prev_node|
+                        return .{ .node = prev_node };
+                } else {
+                    const siblings = parent_node.childCount();
+                    return .{ .node =  parent_node.child(siblings - 1).? };
+                }
+            },
             .grapheme => |grapheme| {
                 const end = grapheme.slice.offset;
                 const prev_grapheme = grapheme.before(graphemes, text, end);
-                const parent = prev_grapheme.parent();
-                return if (parent.eql(grapheme.parent())) .{
-                    .grapheme = prev_grapheme
-                } else .{
-                    .node = parent
-                };
+                if (eql(prev_grapheme.parent(), grapheme.parent()))
+                    return .{ .grapheme = prev_grapheme };
             },
         }
+        return .up(unit);
     }
 
     pub fn down(unit: Unit, graphemes: Graphemes, text: []const u8) Unit {
@@ -135,12 +225,16 @@ pub const Unit = union(enum) {
                         .down(.{ .node = child }, graphemes, text)
                     else
                         .{ .node = child }
-                else .{
-                    .grapheme = .{
-                        .slice = graphemeAt(graphemes, text, start),
-                        .tree = node.tree,
-                    }
-                };
+                else .down(.{
+                    .span = .{ .start = start, .end = end, .tree = node.tree }
+                }, graphemes, text);
+            },
+            // TODO: history
+            .span => |span| return .{
+                .grapheme = .{
+                    .slice = graphemeAt(graphemes, text, span.start),
+                    .tree = span.tree,
+                }
             },
             .grapheme => return unit,
         }
@@ -150,37 +244,41 @@ pub const Unit = union(enum) {
         return switch (unit) {
             .node => |node| (node.startByte() <= other.offset
                              and other.offset + other.len <= node.endByte()),
+            .span => |span| (span.start <= other.offset
+                             and other.offset + other.len <= span.end),
             .grapheme => |grapheme| eql(grapheme.slice, other),
         };
     }
 };
 
-pub const Span = struct {
+pub const Segment = struct {
     head: Unit,
     tail: Unit,
 
-    pub fn contains(span: Span, grapheme: Grapheme) bool {
-        // TODO: range
-        return span.head.contains(grapheme);
+    pub fn contains(segment: Segment, grapheme: Grapheme) bool {
+        const start = @min(segment.head.startByte(), segment.tail.startByte());
+        const end = @min(segment.head.endByte(), segment.tail.endByte());
+        return (start <= grapheme.offset
+                and grapheme.offset + grapheme.len <= end);
     }
 
-    pub fn goUp(span: *Span) void {
-        span.head = span.head.up();
-        span.tail = span.tail.up();
+    pub fn goUp(segment: *Segment) void {
+        segment.head = segment.head.up();
+        segment.tail = segment.tail.up();
     }
 
-    pub fn goRight(span: *Span, graphemes: Graphemes, text: []const u8) void {
-        span.head = span.head.right(graphemes, text);
-        span.tail = span.tail.right(graphemes, text);
+    pub fn goRight(segment: *Segment, graphemes: Graphemes, text: []const u8) void {
+        segment.head = segment.head.right(graphemes, text);
+        segment.tail = segment.tail.right(graphemes, text);
     }
 
-    pub fn goLeft(span: *Span, graphemes: Graphemes, text: []const u8) void {
-        span.head = span.head.left(graphemes, text);
-        span.tail = span.tail.left(graphemes, text);
+    pub fn goLeft(segment: *Segment, graphemes: Graphemes, text: []const u8) void {
+        segment.head = segment.head.left(graphemes, text);
+        segment.tail = segment.tail.left(graphemes, text);
     }
 
-    pub fn goDown(span: *Span, graphemes: Graphemes, text: []const u8) void {
-        span.head = span.head.down(graphemes, text);
-        span.tail = span.tail.down(graphemes, text);
+    pub fn goDown(segment: *Segment, graphemes: Graphemes, text: []const u8) void {
+        segment.head = segment.head.down(graphemes, text);
+        segment.tail = segment.tail.down(graphemes, text);
     }
 };