summaryrefslogtreecommitdiff
path: root/src/tag_line.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/tag_line.zig')
-rw-r--r--src/tag_line.zig41
1 files changed, 41 insertions, 0 deletions
diff --git a/src/tag_line.zig b/src/tag_line.zig
new file mode 100644
index 00000000..d1a9d166
--- /dev/null
+++ b/src/tag_line.zig
@@ -0,0 +1,41 @@
+//! Bounded, single-line editable chrome. Offsets are UTF-8 byte offsets.
+const std = @import("std");
+const modal = @import("modal.zig");
+pub const Line = struct {
+ text: [@import("memory.zig").limits.max_tag_tail]u8 = undefined,
+ len: usize = 0,
+ custom: bool = false,
+
+ pub fn slice(self: *const Line, fallback: []const u8) []const u8 {
+ return if (self.custom) self.text[0..self.len] else fallback;
+ }
+ pub fn set(self: *Line, bytes: []const u8) bool {
+ if (bytes.len > self.text.len or std.mem.indexOfAny(u8, bytes, "\r\n\x00") != null or !std.unicode.utf8ValidateSlice(bytes)) return false;
+ @memcpy(self.text[0..bytes.len], bytes);
+ self.len = bytes.len;
+ self.custom = true;
+ return true;
+ }
+ pub fn replace(self: *Line, fallback: []const u8, lo: usize, hi: usize, bytes: []const u8) bool {
+ const current = self.slice(fallback);
+ if (lo > hi or hi > current.len) return false;
+ var next: [@import("memory.zig").limits.max_tag_tail]u8 = undefined;
+ const size = lo + bytes.len + current.len - hi;
+ if (size > next.len) return false;
+ @memcpy(next[0..lo], current[0..lo]);
+ @memcpy(next[lo..][0..bytes.len], bytes);
+ @memcpy(next[lo + bytes.len ..][0 .. current.len - hi], current[hi..]);
+ return self.set(next[0..size]);
+ }
+};
+
+test "tag replacement is atomic and keeps UTF-8 intact" {
+ var line: Line = .{};
+ try std.testing.expect(line.replace("New Find", 4, 8, "Grep"));
+ try std.testing.expectEqualStrings("New Grep", line.slice(""));
+ try std.testing.expect(!line.replace("", 0, 0, "bad\nline"));
+ try std.testing.expectEqualStrings("New Grep", line.slice(""));
+ try std.testing.expect(line.set("é界"));
+ try std.testing.expect(line.replace("", 0, modal.nextGrapheme(line.slice(""), 0), ""));
+ try std.testing.expectEqualStrings("界", line.slice(""));
+}