TUI editor and editor backend written in Zig
1const std = @import("std");
2
3/// A `Pos` represents a position in the text editor. `Pos` is a 2-dimensional position based on
4/// `row` and `col`.
5pub const Pos = @This();
6
7row: usize,
8col: usize,
9
10pub const init: Pos = .{ .row = 0, .col = 0 };
11
12/// Returns true if both positions are the same.
13pub fn eql(self: Pos, other: Pos) bool {
14 return self.row == other.row and self.col == other.col;
15}
16
17/// Returns `true` if this `Pos` comes before the `other` `Pos`.
18pub fn comesBefore(self: Pos, other: Pos) bool {
19 if (self.row < other.row) return true;
20 if (self.row > other.row) return false;
21 return self.col < other.col;
22}
23
24/// Returns `true` if this `Pos` comes after the `other` `Pos`.
25pub fn comesAfter(self: Pos, other: Pos) bool {
26 if (self.row > other.row) return true;
27 if (self.row < other.row) return false;
28 return self.col > other.col;
29}
30
31/// Comparison function used for sorting.
32pub fn lessThan(_: void, lhs: Pos, rhs: Pos) bool {
33 return lhs.comesBefore(rhs);
34}
35
36pub fn getVisualColumnForText(self: Pos, text: []const u8) usize {
37 const text_len = len: {
38 if (std.mem.endsWith(u8, text, "\n")) break :len text.len -| 1;
39 break :len text.len;
40 };
41
42 return @min(self.col, text_len);
43}
44
45test eql {
46 const a: Pos = .{ .row = 0, .col = 3 };
47 const b: Pos = .{ .row = 0, .col = 3 };
48
49 try std.testing.expect(a.eql(b));
50 try std.testing.expect(a.eql(a));
51 try std.testing.expect(b.eql(b));
52
53 const c: Pos = .{ .row = 4, .col = 0 };
54
55 try std.testing.expect(!c.eql(a));
56 try std.testing.expect(!c.eql(b));
57 try std.testing.expect(c.eql(c));
58
59 const d: Pos = .{ .row = 1, .col = 5 };
60
61 try std.testing.expect(!d.eql(a));
62 try std.testing.expect(!d.eql(b));
63 try std.testing.expect(!d.eql(c));
64 try std.testing.expect(d.eql(d));
65}
66
67test comesBefore {
68 const a: Pos = .{ .row = 0, .col = 3 };
69 const b: Pos = .{ .row = 0, .col = 3 };
70
71 try std.testing.expect(!a.comesBefore(b));
72 try std.testing.expect(!b.comesBefore(a));
73
74 const c: Pos = .{ .row = 4, .col = 0 };
75
76 try std.testing.expect(!c.comesBefore(a));
77 try std.testing.expect(a.comesBefore(c));
78
79 const d: Pos = .{ .row = 1, .col = 5 };
80
81 try std.testing.expect(d.comesBefore(c));
82 try std.testing.expect(!c.comesBefore(d));
83}
84
85test comesAfter {
86 const a: Pos = .{ .row = 0, .col = 3 };
87 const b: Pos = .{ .row = 0, .col = 3 };
88
89 try std.testing.expect(!a.comesAfter(b));
90 try std.testing.expect(!b.comesAfter(a));
91
92 const c: Pos = .{ .row = 4, .col = 0 };
93
94 try std.testing.expect(c.comesAfter(a));
95 try std.testing.expect(!a.comesAfter(c));
96
97 const d: Pos = .{ .row = 1, .col = 5 };
98
99 try std.testing.expect(!d.comesAfter(c));
100 try std.testing.expect(c.comesAfter(d));
101}
102
103test getVisualColumnForText {
104 const pos: Pos = .{ .row = 0, .col = 3 };
105
106 try std.testing.expect(pos.getVisualColumnForText("0123") == 3);
107 try std.testing.expect(pos.getVisualColumnForText("012") == 3);
108 try std.testing.expect(pos.getVisualColumnForText("012\n") == 3);
109
110 try std.testing.expect(pos.getVisualColumnForText("") == 0);
111 try std.testing.expect(pos.getVisualColumnForText("\n") == 0);
112 try std.testing.expect(pos.getVisualColumnForText("0\n") == 1);
113 try std.testing.expect(pos.getVisualColumnForText("01\n") == 2);
114}
115
116test "refAllDecls" {
117 std.testing.refAllDecls(@This());
118}