ghostty/src/terminal/StringMap.zig
Qwerasd 2384bd69cc style: use decl literals
This commit changes a LOT of areas of the code to use decl literals
instead of redundantly referring to the type.

These changes were mostly driven by some regex searches and then manual
adjustment on a case-by-case basis.

I almost certainly missed quite a few places where decl literals could
be used, but this is a good first step in converting things, and other
instances can be addressed when they're discovered.

I tested GLFW+Metal and building the framework on macOS and tested a GTK
build on Linux, so I'm 99% sure I didn't introduce any syntax errors or
other problems with this. (fingers crossed)
2025-05-26 21:50:14 -06:00

141 lines
3.8 KiB
Zig

/// A string along with the mapping of each individual byte in the string
/// to the point in the screen.
const StringMap = @This();
const std = @import("std");
const oni = @import("oniguruma");
const point = @import("point.zig");
const Selection = @import("Selection.zig");
const Screen = @import("Screen.zig");
const Pin = @import("PageList.zig").Pin;
const Allocator = std.mem.Allocator;
string: [:0]const u8,
map: []Pin,
pub fn deinit(self: StringMap, alloc: Allocator) void {
alloc.free(self.string);
alloc.free(self.map);
}
/// Returns an iterator that yields the next match of the given regex.
pub fn searchIterator(
self: StringMap,
regex: oni.Regex,
) SearchIterator {
return .{ .map = self, .regex = regex };
}
/// Iterates over the regular expression matches of the string.
pub const SearchIterator = struct {
map: StringMap,
regex: oni.Regex,
offset: usize = 0,
/// Returns the next regular expression match or null if there are
/// no more matches.
pub fn next(self: *SearchIterator) !?Match {
if (self.offset >= self.map.string.len) return null;
var region = self.regex.search(
self.map.string[self.offset..],
.{},
) catch |err| switch (err) {
error.Mismatch => {
self.offset = self.map.string.len;
return null;
},
else => return err,
};
errdefer region.deinit();
// Increment our offset by the number of bytes in the match.
// We defer this so that we can return the match before
// modifying the offset.
const end_idx: usize = @intCast(region.ends()[0]);
defer self.offset += end_idx;
return .{
.map = self.map,
.offset = self.offset,
.region = region,
};
}
};
/// A single regular expression match.
pub const Match = struct {
map: StringMap,
offset: usize,
region: oni.Region,
pub fn deinit(self: *Match) void {
self.region.deinit();
}
/// Returns the selection containing the full match.
pub fn selection(self: Match) Selection {
const start_idx: usize = @intCast(self.region.starts()[0]);
const end_idx: usize = @intCast(self.region.ends()[0] - 1);
const start_pt = self.map.map[self.offset + start_idx];
const end_pt = self.map.map[self.offset + end_idx];
return .init(start_pt, end_pt, false);
}
};
test "StringMap searchIterator" {
const testing = std.testing;
const alloc = testing.allocator;
// Initialize our regex
try oni.testing.ensureInit();
var re = try oni.Regex.init(
"[A-B]{2}",
.{},
oni.Encoding.utf8,
oni.Syntax.default,
null,
);
defer re.deinit();
// Initialize our screen
var s = try Screen.init(alloc, 5, 5, 0);
defer s.deinit();
const str = "1ABCD2EFGH\n3IJKL";
try s.testWriteString(str);
const line = s.selectLine(.{
.pin = s.pages.pin(.{ .active = .{
.x = 2,
.y = 1,
} }).?,
}).?;
var map: StringMap = undefined;
const sel_str = try s.selectionString(alloc, .{
.sel = line,
.trim = false,
.map = &map,
});
alloc.free(sel_str);
defer map.deinit(alloc);
// Get our iterator
var it = map.searchIterator(re);
{
var match = (try it.next()).?;
defer match.deinit();
const sel = match.selection();
try testing.expectEqual(point.Point{ .screen = .{
.x = 1,
.y = 0,
} }, s.pages.pointFromPin(.screen, sel.start()).?);
try testing.expectEqual(point.Point{ .screen = .{
.x = 2,
.y = 0,
} }, s.pages.pointFromPin(.screen, sel.end()).?);
}
try testing.expect(try it.next() == null);
}