Merge c23773125a into 629838b9bd
commit
8f1ea0dac5
|
|
@ -5565,7 +5565,9 @@ pub fn performBindingAction(self: *Surface, action: input.Binding.Action) !bool
|
|||
.end => .end,
|
||||
.beginning_of_line => .beginning_of_line,
|
||||
.end_of_line => .end_of_line,
|
||||
});
|
||||
.word_left => .word_left,
|
||||
.word_right => .word_right,
|
||||
}, self.config.selection_word_chars);
|
||||
|
||||
// If the selection endpoint is outside of the current viewpoint,
|
||||
// scroll it in to view. Note we always specifically use sel.end
|
||||
|
|
|
|||
|
|
@ -6594,6 +6594,18 @@ pub const Keybinds = struct {
|
|||
.{ .adjust_selection = .end },
|
||||
.{ .performable = true },
|
||||
);
|
||||
try self.set.putFlags(
|
||||
alloc,
|
||||
.{ .key = .{ .physical = .arrow_left }, .mods = .{ .shift = true, .alt = true } },
|
||||
.{ .adjust_selection = .word_left },
|
||||
.{ .performable = true },
|
||||
);
|
||||
try self.set.putFlags(
|
||||
alloc,
|
||||
.{ .key = .{ .physical = .arrow_right }, .mods = .{ .shift = true, .alt = true } },
|
||||
.{ .adjust_selection = .word_right },
|
||||
.{ .performable = true },
|
||||
);
|
||||
|
||||
// Tabs common to all platforms
|
||||
try self.set.put(
|
||||
|
|
|
|||
|
|
@ -505,6 +505,11 @@ pub const Action = union(enum) {
|
|||
/// Adjust the selection to the beginning or the end of the line
|
||||
/// respectively.
|
||||
///
|
||||
/// - `word_left`, `word_right`
|
||||
///
|
||||
/// Adjust the selection one word to the left or right respectively,
|
||||
/// using the configured `selection-word-chars` for word boundaries.
|
||||
///
|
||||
adjust_selection: AdjustSelection,
|
||||
|
||||
/// Jump the viewport forward or back by the given number of prompts.
|
||||
|
|
@ -1028,6 +1033,8 @@ pub const Action = union(enum) {
|
|||
end,
|
||||
beginning_of_line,
|
||||
end_of_line,
|
||||
word_left,
|
||||
word_right,
|
||||
};
|
||||
|
||||
pub const SplitDirection = enum {
|
||||
|
|
|
|||
|
|
@ -414,6 +414,7 @@ pub fn adjust(
|
|||
self: *Selection,
|
||||
s: *const Screen,
|
||||
adjustment: Adjustment,
|
||||
boundary_codepoints: ?[]const u21,
|
||||
) void {
|
||||
// Note that we always adjust "end" because end always represents
|
||||
// the last point of the selection by mouse, not necessarily the
|
||||
|
|
@ -424,7 +425,7 @@ pub fn adjust(
|
|||
.up => if (end_pin.up(1)) |new_end| {
|
||||
end_pin.* = new_end;
|
||||
} else {
|
||||
self.adjust(s, .beginning_of_line);
|
||||
self.adjust(s, .beginning_of_line, null);
|
||||
},
|
||||
|
||||
.down => {
|
||||
|
|
@ -439,7 +440,7 @@ pub fn adjust(
|
|||
}
|
||||
} else {
|
||||
// If we're at the bottom, just go to the end of the line
|
||||
self.adjust(s, .end_of_line);
|
||||
self.adjust(s, .end_of_line, null);
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -472,13 +473,13 @@ pub fn adjust(
|
|||
.page_up => if (end_pin.up(s.pages.rows)) |new_end| {
|
||||
end_pin.* = new_end;
|
||||
} else {
|
||||
self.adjust(s, .home);
|
||||
self.adjust(s, .home, null);
|
||||
},
|
||||
|
||||
.page_down => if (end_pin.down(s.pages.rows)) |new_end| {
|
||||
end_pin.* = new_end;
|
||||
} else {
|
||||
self.adjust(s, .end);
|
||||
self.adjust(s, .end, null);
|
||||
},
|
||||
|
||||
.home => end_pin.* = s.pages.pin(.{ .screen = .{
|
||||
|
|
@ -506,6 +507,118 @@ pub fn adjust(
|
|||
.beginning_of_line => end_pin.x = 0,
|
||||
|
||||
.end_of_line => end_pin.x = end_pin.node.data.size.cols - 1,
|
||||
|
||||
.word_left => {
|
||||
const codepoints = boundary_codepoints orelse return;
|
||||
var it = end_pin.cellIterator(.left_up, null);
|
||||
_ = it.next(); // skip current cell
|
||||
|
||||
// Phase 1: skip boundary characters (whitespace/punctuation) going left.
|
||||
// Empty cells and hard line breaks (non-wrapped rows) are treated
|
||||
// as hard boundaries, consistent with selectWord in Screen.zig.
|
||||
var last_non_boundary: ?Pin = null;
|
||||
while (it.next()) |next| {
|
||||
const rac = next.rowAndCell();
|
||||
const cell = rac.cell;
|
||||
|
||||
// Stop at hard line breaks (landing on last col of a non-wrapped row)
|
||||
if (next.x == next.node.data.size.cols - 1 and !rac.row.wrap) break;
|
||||
|
||||
// Empty cells are hard boundaries
|
||||
if (!cell.hasText()) break;
|
||||
|
||||
const is_boundary = std.mem.indexOfAny(
|
||||
u21,
|
||||
codepoints,
|
||||
&[_]u21{cell.content.codepoint},
|
||||
) != null;
|
||||
if (!is_boundary) {
|
||||
last_non_boundary = next;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 2: skip non-boundary characters (word body) going left
|
||||
// to find the start of the word.
|
||||
if (last_non_boundary != null) {
|
||||
while (it.next()) |next| {
|
||||
const rac = next.rowAndCell();
|
||||
const cell = rac.cell;
|
||||
|
||||
// Stop at hard line breaks
|
||||
if (next.x == next.node.data.size.cols - 1 and !rac.row.wrap) break;
|
||||
|
||||
if (!cell.hasText()) break;
|
||||
const is_boundary = std.mem.indexOfAny(
|
||||
u21,
|
||||
codepoints,
|
||||
&[_]u21{cell.content.codepoint},
|
||||
) != null;
|
||||
if (is_boundary) break;
|
||||
last_non_boundary = next;
|
||||
}
|
||||
}
|
||||
|
||||
if (last_non_boundary) |pos| {
|
||||
end_pin.* = pos;
|
||||
}
|
||||
},
|
||||
|
||||
.word_right => {
|
||||
const codepoints = boundary_codepoints orelse return;
|
||||
var it = end_pin.cellIterator(.right_down, null);
|
||||
_ = it.next(); // skip current cell
|
||||
|
||||
// Phase 1: skip boundary characters (whitespace/punctuation) going right.
|
||||
// Empty cells and hard line breaks (non-wrapped rows) are treated
|
||||
// as hard boundaries, consistent with selectWord in Screen.zig.
|
||||
var last_non_boundary: ?Pin = null;
|
||||
while (it.next()) |next| {
|
||||
const rac = next.rowAndCell();
|
||||
const cell = rac.cell;
|
||||
|
||||
// Stop at hard line breaks
|
||||
if (next.x == next.node.data.size.cols - 1 and !rac.row.wrap) break;
|
||||
|
||||
// Empty cells are hard boundaries
|
||||
if (!cell.hasText()) break;
|
||||
|
||||
const is_boundary = std.mem.indexOfAny(
|
||||
u21,
|
||||
codepoints,
|
||||
&[_]u21{cell.content.codepoint},
|
||||
) != null;
|
||||
if (!is_boundary) {
|
||||
last_non_boundary = next;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 2: skip non-boundary characters (word body) going right
|
||||
// to find the end of the word.
|
||||
if (last_non_boundary != null) {
|
||||
while (it.next()) |next| {
|
||||
const rac = next.rowAndCell();
|
||||
const cell = rac.cell;
|
||||
|
||||
// Stop at hard line breaks
|
||||
if (next.x == next.node.data.size.cols - 1 and !rac.row.wrap) break;
|
||||
|
||||
if (!cell.hasText()) break;
|
||||
const is_boundary = std.mem.indexOfAny(
|
||||
u21,
|
||||
codepoints,
|
||||
&[_]u21{cell.content.codepoint},
|
||||
) != null;
|
||||
if (is_boundary) break;
|
||||
last_non_boundary = next;
|
||||
}
|
||||
}
|
||||
|
||||
if (last_non_boundary) |pos| {
|
||||
end_pin.* = pos;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -523,7 +636,7 @@ test "Selection: adjust right" {
|
|||
false,
|
||||
);
|
||||
defer sel.deinit(&s);
|
||||
sel.adjust(&s, .right);
|
||||
sel.adjust(&s, .right, null);
|
||||
|
||||
try testing.expectEqual(point.Point{ .screen = .{
|
||||
.x = 5,
|
||||
|
|
@ -543,7 +656,7 @@ test "Selection: adjust right" {
|
|||
false,
|
||||
);
|
||||
defer sel.deinit(&s);
|
||||
sel.adjust(&s, .right);
|
||||
sel.adjust(&s, .right, null);
|
||||
|
||||
try testing.expectEqual(point.Point{ .screen = .{
|
||||
.x = 4,
|
||||
|
|
@ -563,7 +676,7 @@ test "Selection: adjust right" {
|
|||
false,
|
||||
);
|
||||
defer sel.deinit(&s);
|
||||
sel.adjust(&s, .right);
|
||||
sel.adjust(&s, .right, null);
|
||||
|
||||
try testing.expectEqual(point.Point{ .screen = .{
|
||||
.x = 5,
|
||||
|
|
@ -590,7 +703,7 @@ test "Selection: adjust left" {
|
|||
false,
|
||||
);
|
||||
defer sel.deinit(&s);
|
||||
sel.adjust(&s, .left);
|
||||
sel.adjust(&s, .left, null);
|
||||
|
||||
// Start line
|
||||
try testing.expectEqual(point.Point{ .screen = .{
|
||||
|
|
@ -611,7 +724,7 @@ test "Selection: adjust left" {
|
|||
false,
|
||||
);
|
||||
defer sel.deinit(&s);
|
||||
sel.adjust(&s, .left);
|
||||
sel.adjust(&s, .left, null);
|
||||
|
||||
// Start line
|
||||
try testing.expectEqual(point.Point{ .screen = .{
|
||||
|
|
@ -639,7 +752,7 @@ test "Selection: adjust left skips blanks" {
|
|||
false,
|
||||
);
|
||||
defer sel.deinit(&s);
|
||||
sel.adjust(&s, .left);
|
||||
sel.adjust(&s, .left, null);
|
||||
|
||||
// Start line
|
||||
try testing.expectEqual(point.Point{ .screen = .{
|
||||
|
|
@ -660,7 +773,7 @@ test "Selection: adjust left skips blanks" {
|
|||
false,
|
||||
);
|
||||
defer sel.deinit(&s);
|
||||
sel.adjust(&s, .left);
|
||||
sel.adjust(&s, .left, null);
|
||||
|
||||
// Start line
|
||||
try testing.expectEqual(point.Point{ .screen = .{
|
||||
|
|
@ -688,7 +801,7 @@ test "Selection: adjust up" {
|
|||
false,
|
||||
);
|
||||
defer sel.deinit(&s);
|
||||
sel.adjust(&s, .up);
|
||||
sel.adjust(&s, .up, null);
|
||||
|
||||
try testing.expectEqual(point.Point{ .screen = .{
|
||||
.x = 5,
|
||||
|
|
@ -708,7 +821,7 @@ test "Selection: adjust up" {
|
|||
false,
|
||||
);
|
||||
defer sel.deinit(&s);
|
||||
sel.adjust(&s, .up);
|
||||
sel.adjust(&s, .up, null);
|
||||
|
||||
try testing.expectEqual(point.Point{ .screen = .{
|
||||
.x = 5,
|
||||
|
|
@ -735,7 +848,7 @@ test "Selection: adjust down" {
|
|||
false,
|
||||
);
|
||||
defer sel.deinit(&s);
|
||||
sel.adjust(&s, .down);
|
||||
sel.adjust(&s, .down, null);
|
||||
|
||||
try testing.expectEqual(point.Point{ .screen = .{
|
||||
.x = 5,
|
||||
|
|
@ -755,7 +868,7 @@ test "Selection: adjust down" {
|
|||
false,
|
||||
);
|
||||
defer sel.deinit(&s);
|
||||
sel.adjust(&s, .down);
|
||||
sel.adjust(&s, .down, null);
|
||||
|
||||
try testing.expectEqual(point.Point{ .screen = .{
|
||||
.x = 4,
|
||||
|
|
@ -782,7 +895,7 @@ test "Selection: adjust down with not full screen" {
|
|||
false,
|
||||
);
|
||||
defer sel.deinit(&s);
|
||||
sel.adjust(&s, .down);
|
||||
sel.adjust(&s, .down, null);
|
||||
|
||||
// Start line
|
||||
try testing.expectEqual(point.Point{ .screen = .{
|
||||
|
|
@ -810,7 +923,7 @@ test "Selection: adjust home" {
|
|||
false,
|
||||
);
|
||||
defer sel.deinit(&s);
|
||||
sel.adjust(&s, .home);
|
||||
sel.adjust(&s, .home, null);
|
||||
|
||||
// Start line
|
||||
try testing.expectEqual(point.Point{ .screen = .{
|
||||
|
|
@ -838,7 +951,7 @@ test "Selection: adjust end with not full screen" {
|
|||
false,
|
||||
);
|
||||
defer sel.deinit(&s);
|
||||
sel.adjust(&s, .end);
|
||||
sel.adjust(&s, .end, null);
|
||||
|
||||
// Start line
|
||||
try testing.expectEqual(point.Point{ .screen = .{
|
||||
|
|
@ -866,7 +979,7 @@ test "Selection: adjust beginning of line" {
|
|||
false,
|
||||
);
|
||||
defer sel.deinit(&s);
|
||||
sel.adjust(&s, .beginning_of_line);
|
||||
sel.adjust(&s, .beginning_of_line, null);
|
||||
|
||||
// Start line
|
||||
try testing.expectEqual(point.Point{ .screen = .{
|
||||
|
|
@ -887,7 +1000,7 @@ test "Selection: adjust beginning of line" {
|
|||
false,
|
||||
);
|
||||
defer sel.deinit(&s);
|
||||
sel.adjust(&s, .beginning_of_line);
|
||||
sel.adjust(&s, .beginning_of_line, null);
|
||||
|
||||
// Start line
|
||||
try testing.expectEqual(point.Point{ .screen = .{
|
||||
|
|
@ -908,7 +1021,7 @@ test "Selection: adjust beginning of line" {
|
|||
false,
|
||||
);
|
||||
defer sel.deinit(&s);
|
||||
sel.adjust(&s, .beginning_of_line);
|
||||
sel.adjust(&s, .beginning_of_line, null);
|
||||
|
||||
// Start line
|
||||
try testing.expectEqual(point.Point{ .screen = .{
|
||||
|
|
@ -936,7 +1049,7 @@ test "Selection: adjust end of line" {
|
|||
false,
|
||||
);
|
||||
defer sel.deinit(&s);
|
||||
sel.adjust(&s, .end_of_line);
|
||||
sel.adjust(&s, .end_of_line, null);
|
||||
|
||||
try testing.expectEqual(point.Point{ .screen = .{
|
||||
.x = 1,
|
||||
|
|
@ -956,7 +1069,7 @@ test "Selection: adjust end of line" {
|
|||
false,
|
||||
);
|
||||
defer sel.deinit(&s);
|
||||
sel.adjust(&s, .end_of_line);
|
||||
sel.adjust(&s, .end_of_line, null);
|
||||
|
||||
try testing.expectEqual(point.Point{ .screen = .{
|
||||
.x = 1,
|
||||
|
|
@ -976,7 +1089,7 @@ test "Selection: adjust end of line" {
|
|||
false,
|
||||
);
|
||||
defer sel.deinit(&s);
|
||||
sel.adjust(&s, .end_of_line);
|
||||
sel.adjust(&s, .end_of_line, null);
|
||||
|
||||
// Start line
|
||||
try testing.expectEqual(point.Point{ .screen = .{
|
||||
|
|
@ -990,6 +1103,187 @@ test "Selection: adjust end of line" {
|
|||
}
|
||||
}
|
||||
|
||||
test "Selection: adjust word_right" {
|
||||
const testing = std.testing;
|
||||
var s = try Screen.init(testing.allocator, .{ .cols = 20, .rows = 10, .max_scrollback = 0 });
|
||||
defer s.deinit();
|
||||
try s.testWriteString("hello world foo bar");
|
||||
|
||||
const boundary = &[_]u21{ ' ', '\t' };
|
||||
|
||||
// word_right from middle of "hello" -> end of "hello"
|
||||
{
|
||||
var sel = Selection.init(
|
||||
s.pages.pin(.{ .screen = .{ .x = 0, .y = 0 } }).?,
|
||||
s.pages.pin(.{ .screen = .{ .x = 2, .y = 0 } }).?,
|
||||
false,
|
||||
);
|
||||
defer sel.deinit(&s);
|
||||
sel.adjust(&s, .word_right, boundary);
|
||||
|
||||
try testing.expectEqual(point.Point{ .screen = .{
|
||||
.x = 4,
|
||||
.y = 0,
|
||||
} }, s.pages.pointFromPin(.screen, sel.end()).?);
|
||||
}
|
||||
|
||||
// word_right from end of "hello" -> end of "world"
|
||||
{
|
||||
var sel = Selection.init(
|
||||
s.pages.pin(.{ .screen = .{ .x = 0, .y = 0 } }).?,
|
||||
s.pages.pin(.{ .screen = .{ .x = 4, .y = 0 } }).?,
|
||||
false,
|
||||
);
|
||||
defer sel.deinit(&s);
|
||||
sel.adjust(&s, .word_right, boundary);
|
||||
|
||||
try testing.expectEqual(point.Point{ .screen = .{
|
||||
.x = 10,
|
||||
.y = 0,
|
||||
} }, s.pages.pointFromPin(.screen, sel.end()).?);
|
||||
}
|
||||
|
||||
// word_right from last word "bar" -> end of "bar"
|
||||
{
|
||||
var sel = Selection.init(
|
||||
s.pages.pin(.{ .screen = .{ .x = 0, .y = 0 } }).?,
|
||||
s.pages.pin(.{ .screen = .{ .x = 16, .y = 0 } }).?,
|
||||
false,
|
||||
);
|
||||
defer sel.deinit(&s);
|
||||
sel.adjust(&s, .word_right, boundary);
|
||||
|
||||
try testing.expectEqual(point.Point{ .screen = .{
|
||||
.x = 18,
|
||||
.y = 0,
|
||||
} }, s.pages.pointFromPin(.screen, sel.end()).?);
|
||||
}
|
||||
}
|
||||
|
||||
test "Selection: adjust word_right with multiple spaces" {
|
||||
const testing = std.testing;
|
||||
var s = try Screen.init(testing.allocator, .{ .cols = 20, .rows = 10, .max_scrollback = 0 });
|
||||
defer s.deinit();
|
||||
try s.testWriteString("hello world");
|
||||
|
||||
const boundary = &[_]u21{ ' ', '\t' };
|
||||
|
||||
// word_right from middle of "hello" -> end of "hello"
|
||||
{
|
||||
var sel = Selection.init(
|
||||
s.pages.pin(.{ .screen = .{ .x = 0, .y = 0 } }).?,
|
||||
s.pages.pin(.{ .screen = .{ .x = 2, .y = 0 } }).?,
|
||||
false,
|
||||
);
|
||||
defer sel.deinit(&s);
|
||||
sel.adjust(&s, .word_right, boundary);
|
||||
|
||||
try testing.expectEqual(point.Point{ .screen = .{
|
||||
.x = 4,
|
||||
.y = 0,
|
||||
} }, s.pages.pointFromPin(.screen, sel.end()).?);
|
||||
}
|
||||
}
|
||||
|
||||
test "Selection: adjust word_left" {
|
||||
const testing = std.testing;
|
||||
var s = try Screen.init(testing.allocator, .{ .cols = 20, .rows = 10, .max_scrollback = 0 });
|
||||
defer s.deinit();
|
||||
try s.testWriteString("hello world foo bar");
|
||||
|
||||
const boundary = &[_]u21{ ' ', '\t' };
|
||||
|
||||
// word_left from middle of "world" -> start of "world"
|
||||
{
|
||||
var sel = Selection.init(
|
||||
s.pages.pin(.{ .screen = .{ .x = 0, .y = 0 } }).?,
|
||||
s.pages.pin(.{ .screen = .{ .x = 8, .y = 0 } }).?,
|
||||
false,
|
||||
);
|
||||
defer sel.deinit(&s);
|
||||
sel.adjust(&s, .word_left, boundary);
|
||||
|
||||
try testing.expectEqual(point.Point{ .screen = .{
|
||||
.x = 6,
|
||||
.y = 0,
|
||||
} }, s.pages.pointFromPin(.screen, sel.end()).?);
|
||||
}
|
||||
|
||||
// word_left from start of "world" -> start of "hello"
|
||||
{
|
||||
var sel = Selection.init(
|
||||
s.pages.pin(.{ .screen = .{ .x = 10, .y = 0 } }).?,
|
||||
s.pages.pin(.{ .screen = .{ .x = 6, .y = 0 } }).?,
|
||||
false,
|
||||
);
|
||||
defer sel.deinit(&s);
|
||||
sel.adjust(&s, .word_left, boundary);
|
||||
|
||||
try testing.expectEqual(point.Point{ .screen = .{
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
} }, s.pages.pointFromPin(.screen, sel.end()).?);
|
||||
}
|
||||
|
||||
// word_left from start of "hello" -> stays put (no previous word)
|
||||
{
|
||||
var sel = Selection.init(
|
||||
s.pages.pin(.{ .screen = .{ .x = 5, .y = 0 } }).?,
|
||||
s.pages.pin(.{ .screen = .{ .x = 0, .y = 0 } }).?,
|
||||
false,
|
||||
);
|
||||
defer sel.deinit(&s);
|
||||
sel.adjust(&s, .word_left, boundary);
|
||||
|
||||
// Should stay at x=0 since there's no previous word
|
||||
try testing.expectEqual(point.Point{ .screen = .{
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
} }, s.pages.pointFromPin(.screen, sel.end()).?);
|
||||
}
|
||||
}
|
||||
|
||||
test "Selection: adjust word_left with multiple spaces" {
|
||||
const testing = std.testing;
|
||||
var s = try Screen.init(testing.allocator, .{ .cols = 20, .rows = 10, .max_scrollback = 0 });
|
||||
defer s.deinit();
|
||||
try s.testWriteString("hello world");
|
||||
|
||||
const boundary = &[_]u21{ ' ', '\t' };
|
||||
|
||||
// word_left from middle of "world" -> start of "world" (skips within word)
|
||||
{
|
||||
var sel = Selection.init(
|
||||
s.pages.pin(.{ .screen = .{ .x = 12, .y = 0 } }).?,
|
||||
s.pages.pin(.{ .screen = .{ .x = 10, .y = 0 } }).?,
|
||||
false,
|
||||
);
|
||||
defer sel.deinit(&s);
|
||||
sel.adjust(&s, .word_left, boundary);
|
||||
|
||||
try testing.expectEqual(point.Point{ .screen = .{
|
||||
.x = 8,
|
||||
.y = 0,
|
||||
} }, s.pages.pointFromPin(.screen, sel.end()).?);
|
||||
}
|
||||
|
||||
// word_left from start of "world" -> start of "hello" (skips multiple spaces)
|
||||
{
|
||||
var sel = Selection.init(
|
||||
s.pages.pin(.{ .screen = .{ .x = 12, .y = 0 } }).?,
|
||||
s.pages.pin(.{ .screen = .{ .x = 8, .y = 0 } }).?,
|
||||
false,
|
||||
);
|
||||
defer sel.deinit(&s);
|
||||
sel.adjust(&s, .word_left, boundary);
|
||||
|
||||
try testing.expectEqual(point.Point{ .screen = .{
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
} }, s.pages.pointFromPin(.screen, sel.end()).?);
|
||||
}
|
||||
}
|
||||
|
||||
test "Selection: order, standard" {
|
||||
const testing = std.testing;
|
||||
const alloc = testing.allocator;
|
||||
|
|
|
|||
Loading…
Reference in New Issue