mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-14 15:56:13 +03:00
terminal: search match across page boundary
This commit is contained in:
@ -212,6 +212,12 @@ const SlidingWindow = struct {
|
|||||||
var meta_consumed: usize = 0;
|
var meta_consumed: usize = 0;
|
||||||
const tl: Pin = pin(&meta_it, &meta_consumed, start);
|
const tl: Pin = pin(&meta_it, &meta_consumed, start);
|
||||||
|
|
||||||
|
// Store the information required to prune later. We store this
|
||||||
|
// now because we only want to prune up to our START so we can
|
||||||
|
// find overlapping matches.
|
||||||
|
const tl_meta_idx = meta_it.idx - 1;
|
||||||
|
const tl_meta_consumed = meta_consumed;
|
||||||
|
|
||||||
// We have to seek back so that we reinspect our current
|
// We have to seek back so that we reinspect our current
|
||||||
// iterator value again in case the start and end are in the
|
// iterator value again in case the start and end are in the
|
||||||
// same segment.
|
// same segment.
|
||||||
@ -222,27 +228,27 @@ const SlidingWindow = struct {
|
|||||||
// Our offset into the current meta block is the start index
|
// Our offset into the current meta block is the start index
|
||||||
// minus the amount of data fully consumed. We then add one
|
// minus the amount of data fully consumed. We then add one
|
||||||
// to move one past the match so we don't repeat it.
|
// to move one past the match so we don't repeat it.
|
||||||
self.data_offset = start - meta_consumed + 1;
|
self.data_offset = start - tl_meta_consumed + 1;
|
||||||
|
|
||||||
// meta_it.idx is br's meta index plus one (because the iterator
|
// meta_it.idx is br's meta index plus one (because the iterator
|
||||||
// moves one past the end; we call next() one last time). So
|
// moves one past the end; we call next() one last time). So
|
||||||
// we compare against one to check that the meta that we matched
|
// we compare against one to check that the meta that we matched
|
||||||
// in has prior meta blocks we can prune.
|
// in has prior meta blocks we can prune.
|
||||||
if (meta_it.idx > 1) {
|
if (tl_meta_idx > 0) {
|
||||||
// Deinit all our memory in the meta blocks prior to our
|
// Deinit all our memory in the meta blocks prior to our
|
||||||
// match.
|
// match.
|
||||||
const meta_count = meta_it.idx - 1;
|
const meta_count = tl_meta_idx;
|
||||||
meta_it.reset();
|
meta_it.reset();
|
||||||
for (0..meta_count) |_| meta_it.next().?.deinit();
|
for (0..meta_count) |_| meta_it.next().?.deinit();
|
||||||
if (comptime std.debug.runtime_safety) {
|
if (comptime std.debug.runtime_safety) {
|
||||||
assert(meta_it.idx == meta_count);
|
assert(meta_it.idx == meta_count);
|
||||||
assert(meta_it.next().?.node == br.node);
|
assert(meta_it.next().?.node == tl.node);
|
||||||
}
|
}
|
||||||
self.meta.deleteOldest(meta_count);
|
self.meta.deleteOldest(meta_count);
|
||||||
|
|
||||||
// Delete all the data up to our current index.
|
// Delete all the data up to our current index.
|
||||||
assert(meta_consumed > 0);
|
assert(tl_meta_consumed > 0);
|
||||||
self.data.deleteOldest(meta_consumed);
|
self.data.deleteOldest(tl_meta_consumed);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.assertIntegrity();
|
self.assertIntegrity();
|
||||||
@ -485,6 +491,53 @@ test "SlidingWindow two pages" {
|
|||||||
try testing.expect(w.next(needle) == null);
|
try testing.expect(w.next(needle) == null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "SlidingWindow two pages match across boundary" {
|
||||||
|
const testing = std.testing;
|
||||||
|
const alloc = testing.allocator;
|
||||||
|
|
||||||
|
var w = try SlidingWindow.initEmpty(alloc);
|
||||||
|
defer w.deinit(alloc);
|
||||||
|
|
||||||
|
var s = try Screen.init(alloc, 80, 24, 1000);
|
||||||
|
defer s.deinit();
|
||||||
|
|
||||||
|
// Fill up the first page. The final bytes in the first page
|
||||||
|
// are "boo!"
|
||||||
|
const first_page_rows = s.pages.pages.first.?.data.capacity.rows;
|
||||||
|
for (0..first_page_rows - 1) |_| try s.testWriteString("\n");
|
||||||
|
for (0..s.pages.cols - 4) |_| try s.testWriteString("x");
|
||||||
|
try s.testWriteString("hell");
|
||||||
|
try testing.expect(s.pages.pages.first == s.pages.pages.last);
|
||||||
|
try s.testWriteString("o, world!");
|
||||||
|
try testing.expect(s.pages.pages.first != s.pages.pages.last);
|
||||||
|
|
||||||
|
// Imaginary needle for search
|
||||||
|
const needle = "hello, world";
|
||||||
|
|
||||||
|
// Add both pages
|
||||||
|
const node: *PageList.List.Node = s.pages.pages.first.?;
|
||||||
|
try w.append(alloc, node);
|
||||||
|
try w.append(alloc, node.next.?);
|
||||||
|
|
||||||
|
// Search should find a match
|
||||||
|
{
|
||||||
|
const sel = w.next(needle).?;
|
||||||
|
try testing.expectEqual(point.Point{ .active = .{
|
||||||
|
.x = 76,
|
||||||
|
.y = 22,
|
||||||
|
} }, s.pages.pointFromPin(.active, sel.start()).?);
|
||||||
|
try testing.expectEqual(point.Point{ .active = .{
|
||||||
|
.x = 7,
|
||||||
|
.y = 23,
|
||||||
|
} }, s.pages.pointFromPin(.active, sel.end()).?);
|
||||||
|
}
|
||||||
|
try testing.expect(w.next(needle) == null);
|
||||||
|
try testing.expect(w.next(needle) == null);
|
||||||
|
|
||||||
|
// We shouldn't prune because we don't have enough space
|
||||||
|
try testing.expectEqual(2, w.meta.len());
|
||||||
|
}
|
||||||
|
|
||||||
test "SlidingWindow two pages no match prunes first page" {
|
test "SlidingWindow two pages no match prunes first page" {
|
||||||
const testing = std.testing;
|
const testing = std.testing;
|
||||||
const alloc = testing.allocator;
|
const alloc = testing.allocator;
|
||||||
|
Reference in New Issue
Block a user