Add generic list panel primitive
This commit is contained in:
+67
-1
@@ -94,6 +94,12 @@ pub const Client = struct {
|
||||
if (std.mem.startsWith(u8, line, "key ")) return self.handleTraceKey(line[4..]);
|
||||
if (std.mem.startsWith(u8, line, "type ")) return self.handleInput(line[5..]);
|
||||
if (std.mem.startsWith(u8, line, "panel_open ")) return self.applyProtocolCommand(line);
|
||||
if (std.mem.startsWith(u8, line, "list_open ")) return self.applyProtocolCommand(line);
|
||||
if (std.mem.startsWith(u8, line, "list_filter ")) return self.applyProtocolCommand(line);
|
||||
if (std.mem.eql(u8, line, "list_down")) return self.applyProtocol("command list_down");
|
||||
if (std.mem.eql(u8, line, "list_up")) return self.applyProtocol("command list_up");
|
||||
if (std.mem.eql(u8, line, "list_select")) return self.applyProtocol("command list_select");
|
||||
if (std.mem.eql(u8, line, "list_cancel")) return self.applyProtocol("command list_cancel");
|
||||
if (std.mem.eql(u8, line, "panel_close")) return self.applyProtocol("command panel_close");
|
||||
if (std.mem.eql(u8, line, "panel_next")) return self.applyProtocol("command panel_next");
|
||||
if (std.mem.eql(u8, line, "panel_prev")) return self.applyProtocol("command panel_prev");
|
||||
@@ -174,7 +180,20 @@ pub const Client = struct {
|
||||
try out.append(allocator, '\n');
|
||||
body_lines_used += 1;
|
||||
|
||||
if (body_lines_used < max_body_lines) {
|
||||
const remaining_rows = max_body_lines - body_lines_used;
|
||||
const rows = self.session.activeListRowsAlloc(allocator, remaining_rows) catch null;
|
||||
if (rows) |list_rows| {
|
||||
defer {
|
||||
for (list_rows) |row| allocator.free(row);
|
||||
allocator.free(list_rows);
|
||||
}
|
||||
for (list_rows) |row| {
|
||||
if (body_lines_used >= max_body_lines) break;
|
||||
try appendVisibleCells(allocator, &out, row, self.viewport.width);
|
||||
try out.append(allocator, '\n');
|
||||
body_lines_used += 1;
|
||||
}
|
||||
} else if (body_lines_used < max_body_lines) {
|
||||
const title = snap.active_panel_title.?;
|
||||
const detail = try std.fmt.allocPrint(allocator, "{s}: no content yet", .{title});
|
||||
defer allocator.free(detail);
|
||||
@@ -599,3 +618,50 @@ test "adversarial: invalid panel title and empty close recover without changing
|
||||
try std.testing.expect(std.mem.indexOf(u8, frame, "abc") != null);
|
||||
try assertLinesFit(frame, 16);
|
||||
}
|
||||
|
||||
test "regular: list panel filters moves selects and renders rows" {
|
||||
var client = try Client.init(std.testing.allocator, .{ .width = 32, .height = 5 });
|
||||
defer client.deinit();
|
||||
|
||||
try client.handleTraceLine("list_open files src/main.zig|src/panel.zig|README.md");
|
||||
try client.handleTraceLine("list_filter src");
|
||||
try client.handleTraceLine("list_down");
|
||||
const frame = try client.render(std.testing.allocator);
|
||||
defer std.testing.allocator.free(frame);
|
||||
try std.testing.expect(std.mem.indexOf(u8, frame, "panel [files]") != null);
|
||||
try std.testing.expect(std.mem.indexOf(u8, frame, "> src/panel.zig") != null);
|
||||
try assertLinesFit(frame, 32);
|
||||
|
||||
try client.handleTraceLine("list_select");
|
||||
const summary = try client.session.panelSummaryAlloc(std.testing.allocator);
|
||||
defer std.testing.allocator.free(summary);
|
||||
try std.testing.expectEqualStrings("list:filter=src,cursor=1,visible=2,selected=src/panel.zig", summary);
|
||||
}
|
||||
|
||||
test "regular: list cancel returns to previous editor surface" {
|
||||
var client = try Client.init(std.testing.allocator, .{ .width = 24, .height = 4 });
|
||||
defer client.deinit();
|
||||
|
||||
try client.handleTraceLine("open abc");
|
||||
try client.handleTraceLine("list_open files a.zig|b.zig");
|
||||
try client.handleTraceLine("list_cancel");
|
||||
const frame = try client.render(std.testing.allocator);
|
||||
defer std.testing.allocator.free(frame);
|
||||
try std.testing.expect(std.mem.indexOf(u8, frame, "abc") != null);
|
||||
try std.testing.expect(std.mem.indexOf(u8, frame, "panel") == null);
|
||||
}
|
||||
|
||||
test "adversarial: list no-match state and invalid actions recover" {
|
||||
var client = try Client.init(std.testing.allocator, .{ .width = 24, .height = 4 });
|
||||
defer client.deinit();
|
||||
|
||||
try client.handleTraceLine("list_open files a.zig|b.zig");
|
||||
try client.handleTraceLine("list_filter none");
|
||||
const frame = try client.render(std.testing.allocator);
|
||||
defer std.testing.allocator.free(frame);
|
||||
try std.testing.expect(std.mem.indexOf(u8, frame, "no matches") != null);
|
||||
try assertLinesFit(frame, 24);
|
||||
|
||||
try client.handleTraceLine("list_cancel");
|
||||
try std.testing.expectError(Error.ProtocolRejected, client.handleTraceLine("list_down"));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user