diff options
Diffstat (limited to 'src/Selection.zig')
-rw-r--r-- | src/Selection.zig | 367 |
1 files changed, 235 insertions, 132 deletions
diff --git a/src/Selection.zig b/src/Selection.zig index 352af7a..5e01d32 100644 --- a/src/Selection.zig +++ b/src/Selection.zig @@ -2,6 +2,7 @@ // SPDX-FileCopyrightText: 2025 Nguyễn Gia Phong // SPDX-License-Identifier: GPL-3.0-or-later +const assert = std.debug.assert; const eql = std.meta.eql; const order = std.math.order; const std = @import("std"); @@ -13,6 +14,7 @@ const tree_sitter = @import("tree-sitter"); const Grapheme = vaxis.Graphemes.Grapheme; const vaxis = @import("vaxis"); +const Buffer = @import("Buffer.zig"); const graphemes = &@import("root").graphemes; const Selection = @This(); @@ -37,25 +39,79 @@ pub fn inBody(selection: Selection, grapheme: Grapheme) bool { and grapheme.offset + grapheme.len <= selection.endByte()); } -fn graphemeAt(text: []const u8, index: u32) Grapheme { - var iterator = graphemes.iterator(text[index..]); - return .{ .offset = index, .len = iterator.peek().?.len }; -} - -fn graphemeBefore(text: []const u8, index: u32) Grapheme { - var iterator = graphemes.reverseIterator(text[0..index]); - return iterator.peek().?; -} - // TODO: nesting -const Span = struct { - start: u32, - end: u32, +pub const Span = struct { + start: u32 = 0, + end: u32 = 0, tree: *const Tree, - fn parent(unit: Span) Node { - const root_node = unit.tree.rootNode(); - return root_node.descendantForByteRange(unit.start, unit.end).?; + pub fn root(text_len: u32, tree: *const Tree) Span { + return .{ .start = 0, .end = text_len, .tree = tree }; + } + + fn parent(span: Span, text_len: u32) Unit { + const root_span = Span.root(text_len, span.tree); + if (eql(root_span, span)) + return .{ .span = span }; + const root_node = span.tree.rootNode(); + const root_node_start = root_node.startByte(); + const root_node_end = root_node.endByte(); + if (root_node_start == span.start and root_node_end == span.end) + return .{ .span = root_span }; + if (root_node_start > span.start) { + const first_span = Span{ + .start = 0, + .end = root_node_start, + .tree = span.tree, + }; + return .{ + .span = if (eql(first_span, span)) root_span else first_span + }; + } + if (root_node_end < span.end) { + const last_span = Span{ + .start = root_node_start, + .end = text_len, + .tree = span.tree, + }; + return .{ + .span = if (eql(last_span, span)) root_span else last_span + }; + } + const parent_node = root_node.descendantForByteRange(span.start, + span.end).?; + const parent_node_start = parent_node.startByte(); + assert(parent_node_start <= span.start); + const parent_node_end = parent_node.endByte(); + assert(parent_node_end >= span.end); + const parent_span = grow: { + if (parent_node.firstChildForByte(span.end)) |next_node| { + break :grow if (next_node.prevSibling()) |prev_node| Span{ + .start = prev_node.endByte(), + .end = next_node.startByte(), + .tree = span.tree, + } else Span{ + .start = parent_node_start, + .end = next_node.startByte(), + .tree = span.tree, + }; + } else { + const siblings = parent_node.childCount(); + if (siblings == 0) + return .{ .node = parent_node }; + const prev_node = parent_node.child(siblings - 1).?; + break :grow Span{ + .start = prev_node.endByte(), + .end = parent_node_end, + .tree = span.tree, + }; + } + }; + return if (eql(parent_span, span)) .{ + .node = parent_node + } else .{ + .span = parent_span + }; } }; @@ -71,51 +127,26 @@ const GraphemeUnit = struct { 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 parent(unit: GraphemeUnit, text_len: u32) Unit { + return Span.parent(.{ + .start = unit.startByte(), + .end = unit.endByte(), + .tree = unit.tree, + }, text_len); } - fn at(unit: GraphemeUnit, text: []const u8, index: u32) GraphemeUnit { + fn at(unit: GraphemeUnit, buffer: Buffer, index: u32) GraphemeUnit { return .{ - .slice = graphemeAt(text, index), + .slice = buffer.graphemeAt(index), .tree = unit.tree, }; } - fn before(unit: GraphemeUnit, - text: []const u8, index: u32) GraphemeUnit { - return .{ - .slice = graphemeBefore(text, index), + fn before(unit: GraphemeUnit, buffer: Buffer, index: u32) ?GraphemeUnit { + return if (buffer.graphemeBefore(index)) |grapheme| .{ + .slice = grapheme, .tree = unit.tree, - }; + } else null; } }; @@ -141,103 +172,175 @@ pub const Unit = union(enum) { }; } - pub fn up(unit: Unit, text: []const u8) Unit { - switch (unit) { - .node => |node| return if (node.parent()) |parent_node| - if (parent_node.eql(node)) - .up(.{ .node = parent_node }, text) - else - .{ .node = parent_node } - else unit, - .span => |span| { - const parent_node = span.parent(); - return if (parent_node.startByte() == span.start - and parent_node.endByte() == span.end) - .up(.{ .node = parent_node }, text) - 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 }, text) - else - .{ .span = parent_span }; - }, - } + fn tree(unit: Unit) *const Tree { + return switch (unit) { + .node => |node| node.tree, + .span => |span| span.tree, + .grapheme => |grapheme| grapheme.tree, + }; + } + + fn equal(unit: Unit, other: Unit) bool { + return (unit.startByte() == other.startByte() + and unit.endByte() == other.endByte()); + } + + pub fn up(unit: Unit, buffer: Buffer) Unit { + const parent = switch (unit) { + .node => |node| if (node.parent()) |parent| Unit{ + .node = parent + } else Span.parent(.{ + .start = node.startByte(), + .end = node.endByte(), + .tree = node.tree, + }, buffer.length()), + .span => |span| span.parent(buffer.length()), + .grapheme => |grapheme| grapheme.parent(buffer.length()), + }; + return if (!parent.equal(unit) or parent.equal(.{ + .span = .root(buffer.length(), unit.tree()) + })) parent else parent.up(buffer); } - pub fn right(unit: Unit, text: []const u8) Unit { + pub fn right(unit: Unit, buffer: Buffer) Unit { switch (unit) { - .node => |node| if (node.nextSibling()) |sibling| { + .node => |node| { const prev = node.endByte(); - const next = sibling.startByte(); - return switch (order(prev, next)) { - .lt => .{ - .span = .{ - .start = prev, - .end = next, - .tree = node.tree, - } - }, - .eq => .{ .node = sibling }, - .gt => unreachable, - }; + const root_node = node.tree.rootNode(); + if (root_node.eql(node)) { + const span = Span{ + .start = prev, + .end = buffer.length(), + .tree = node.tree, + }; + if (span.start < span.end) + return .{ .span = span }; + } else if (node.nextSibling()) |sibling| { + const next = sibling.startByte(); + return switch (order(prev, next)) { + .lt => .{ + .span = .{ + .start = prev, + .end = next, + .tree = node.tree, + } + }, + .eq => .{ .node = sibling }, + .gt => unreachable, + }; + } }, - .span => |span| { - const parent_node = unit.up(text).node; - if (parent_node.firstChildForByte(span.end)) |next_node| - return .{ .node = next_node }; + .span => |span| switch (unit.up(buffer)) { + .node => |parent_node| { + if (parent_node.firstChildForByte(span.end)) |next_node| + return .{ .node = next_node }; + }, + .span => |parent_span| { + const root_node = span.tree.rootNode(); + if (root_node.startByte() == span.end) + return .{ .node = root_node }; + const next_span = Span{ + .start = span.end, + .end = parent_span.end, + .tree = span.tree, + }; + if (next_span.start < next_span.end) + return .{ .span = next_span }; + }, + .grapheme => unreachable, }, .grapheme => |grapheme| { - const start = grapheme.endByte(); - const next_grapheme = grapheme.at(text, start); - if (eql(next_grapheme.parent(), grapheme.parent())) + const next_grapheme = grapheme.at(buffer, grapheme.endByte()); + const len = buffer.length(); + if (next_grapheme.parent(len).equal(grapheme.parent(len))) return .{ .grapheme = next_grapheme }; + return .right(.{ + .span = .{ + .start = grapheme.startByte(), + .end = grapheme.endByte(), + .tree = grapheme.tree, + } + }, buffer); }, } - return .up(unit, text); + return unit.up(buffer); } - pub fn left(unit: Unit, text: []const u8) Unit { + pub fn left(unit: Unit, buffer: Buffer) Unit { switch (unit) { - .node => |node| if (node.prevSibling()) |sibling| { - const prev = sibling.endByte(); + .node => |node| { const next = node.startByte(); - return switch (order(prev, next)) { - .lt => .{ - .span = .{ - .start = prev, - .end = next, - .tree = node.tree, - } - }, - .eq => .{ .node = sibling }, - .gt => unreachable, - }; - }, - .span => |span| { - const parent_node = unit.up(text).node; - 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).? }; + const root_node = node.tree.rootNode(); + if (root_node.eql(node)) { + const span = Span{ + .start = 0, + .end = next, + .tree = node.tree, + }; + if (span.start < span.end) + return .{ .span = span }; + } else if (node.prevSibling()) |sibling| { + const prev = sibling.endByte(); + return switch (order(prev, next)) { + .lt => .{ + .span = .{ + .start = prev, + .end = next, + .tree = node.tree, + } + }, + .eq => .{ .node = sibling }, + .gt => unreachable, + }; } }, + .span => |span| switch (unit.up(buffer)) { + .node => |parent_node| { + if (parent_node.firstChildForByte(span.end)) |next_node| { + if (next_node.prevSibling()) |prev_node| + return .{ .node = prev_node }; + } else { + const siblings = parent_node.childCount(); + if (siblings > 0) + return .{ + .node = parent_node.child(siblings - 1).? + }; + } + }, + .span => |parent_span| { + const root_node = span.tree.rootNode(); + if (root_node.endByte() == span.start) + return .{ .node = root_node }; + const prev_span = Span{ + .start = span.start, + .end = parent_span.start, + .tree = span.tree, + }; + if (prev_span.start < prev_span.end) + return .{ .span = prev_span }; + }, + .grapheme => unreachable, + }, .grapheme => |grapheme| { - const end = grapheme.slice.offset; - const prev_grapheme = grapheme.before(text, end); - if (eql(prev_grapheme.parent(), grapheme.parent())) - return .{ .grapheme = prev_grapheme }; + const start = grapheme.startByte(); + if (grapheme.before(buffer, start)) |prev_grapheme| { + const len = buffer.length(); + if (prev_grapheme.parent(len).equal(grapheme.parent(len))) + return .{ .grapheme = prev_grapheme }; + } + return .left(.{ + .span = .{ + .start = start, + .end = grapheme.endByte(), + .tree = grapheme.tree, + } + }, buffer); }, } - return .up(unit, text); + return unit.up(buffer); } - pub fn down(unit: Unit, text: []const u8) Unit { + pub fn down(unit: Unit, buffer: Buffer) Unit { switch (unit) { .node => |node| { const start = node.startByte(); @@ -245,17 +348,17 @@ pub const Unit = union(enum) { // TODO: history return if (node.child(0)) |child| if (child.startByte() == start and child.endByte() == end) - .down(.{ .node = child }, text) + .down(.{ .node = child }, buffer) else .{ .node = child } else .down(.{ .span = .{ .start = start, .end = end, .tree = node.tree } - }, text); + }, buffer); }, .span => |span| return .{ .grapheme = .{ // TODO: history - .slice = graphemeAt(text, span.start), + .slice = buffer.graphemeAt(span.start), .tree = span.tree, } }, |