Render active panels on narrow terminal
This commit is contained in:
+99
@@ -93,6 +93,10 @@ 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.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");
|
||||
if (std.mem.startsWith(u8, line, "open ")) return self.applyProtocol(line);
|
||||
if (std.mem.startsWith(u8, line, "insert ")) return self.applyProtocolCommand(line);
|
||||
if (std.mem.eql(u8, line, "left")) return self.applyProtocol("command move_left");
|
||||
@@ -108,6 +112,11 @@ pub const Client = struct {
|
||||
pub fn render(self: *const Client, allocator: std.mem.Allocator) ![]u8 {
|
||||
try self.viewport.validate();
|
||||
const snap = try self.session.snapshot();
|
||||
if (snap.active_panel_title != null) return self.renderPanel(allocator, snap);
|
||||
return self.renderEditor(allocator, snap);
|
||||
}
|
||||
|
||||
fn renderEditor(self: *const Client, allocator: std.mem.Allocator, snap: session_mod.Snapshot) ![]u8 {
|
||||
var out = std.ArrayList(u8).empty;
|
||||
errdefer out.deinit(allocator);
|
||||
|
||||
@@ -151,6 +160,43 @@ pub const Client = struct {
|
||||
return out.toOwnedSlice(allocator);
|
||||
}
|
||||
|
||||
fn renderPanel(self: *const Client, allocator: std.mem.Allocator, snap: session_mod.Snapshot) ![]u8 {
|
||||
var out = std.ArrayList(u8).empty;
|
||||
errdefer out.deinit(allocator);
|
||||
const max_body_lines = self.viewport.height - 1;
|
||||
var body_lines_used: usize = 0;
|
||||
|
||||
const path = try self.session.panelPathAlloc(allocator);
|
||||
defer allocator.free(path);
|
||||
const header = try std.fmt.allocPrint(allocator, "panel {s}", .{path});
|
||||
defer allocator.free(header);
|
||||
try appendVisibleCells(allocator, &out, header, self.viewport.width);
|
||||
try out.append(allocator, '\n');
|
||||
body_lines_used += 1;
|
||||
|
||||
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);
|
||||
try appendVisibleCells(allocator, &out, detail, self.viewport.width);
|
||||
try out.append(allocator, '\n');
|
||||
body_lines_used += 1;
|
||||
}
|
||||
while (body_lines_used < max_body_lines) : (body_lines_used += 1) {
|
||||
try out.append(allocator, '~');
|
||||
try out.append(allocator, '\n');
|
||||
}
|
||||
|
||||
const status = try std.fmt.allocPrint(
|
||||
allocator,
|
||||
"panel {d}/{d} x close",
|
||||
.{ snap.active_panel_index.? + 1, snap.panel_depth },
|
||||
);
|
||||
defer allocator.free(status);
|
||||
try appendVisibleCells(allocator, &out, status, self.viewport.width);
|
||||
return out.toOwnedSlice(allocator);
|
||||
}
|
||||
|
||||
pub fn handleInput(self: *Client, raw: []const u8) !void {
|
||||
if (self.quit) return Error.ClientQuit;
|
||||
const event = input.normalize(raw);
|
||||
@@ -500,3 +546,56 @@ test "adversarial: unknown symbol key does not mutate the buffer" {
|
||||
defer std.testing.allocator.free(frame);
|
||||
try std.testing.expect(std.mem.indexOf(u8, frame, "unknown symbol key") != null);
|
||||
}
|
||||
|
||||
test "regular: narrow terminal renders active panel instead of editor and returns cleanly" {
|
||||
var client = try Client.init(std.testing.allocator, .{ .width = 20, .height = 4 });
|
||||
defer client.deinit();
|
||||
|
||||
try client.handleTraceLine("open editor-text");
|
||||
try client.handleTraceLine("panel_open files");
|
||||
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, "editor-text") == null);
|
||||
try assertLinesFit(frame, 20);
|
||||
|
||||
try client.handleTraceLine("panel_close");
|
||||
const editor_frame = try client.render(std.testing.allocator);
|
||||
defer std.testing.allocator.free(editor_frame);
|
||||
try std.testing.expect(std.mem.indexOf(u8, editor_frame, "editor-text") != null);
|
||||
try std.testing.expect(std.mem.indexOf(u8, editor_frame, "panel [files]") == null);
|
||||
try assertLinesFit(editor_frame, 20);
|
||||
}
|
||||
|
||||
test "regular: nested panel breadcrumbs render on narrow terminal without overflow" {
|
||||
var client = try Client.init(std.testing.allocator, .{ .width = 18, .height = 4 });
|
||||
defer client.deinit();
|
||||
|
||||
try client.handleTraceLine("open abc");
|
||||
try client.handleTraceLine("panel_open files");
|
||||
try client.handleTraceLine("panel_open diagnostics");
|
||||
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, "[diagnostics]") == null);
|
||||
try assertLinesFit(frame, 18);
|
||||
|
||||
try client.handleTraceLine("panel_prev");
|
||||
const previous_frame = try client.render(std.testing.allocator);
|
||||
defer std.testing.allocator.free(previous_frame);
|
||||
try std.testing.expect(std.mem.indexOf(u8, previous_frame, "panel [files]") != null);
|
||||
try assertLinesFit(previous_frame, 18);
|
||||
}
|
||||
|
||||
test "adversarial: invalid panel title and empty close recover without changing editor render" {
|
||||
var client = try Client.init(std.testing.allocator, .{ .width = 16, .height = 4 });
|
||||
defer client.deinit();
|
||||
|
||||
try client.handleTraceLine("open abc");
|
||||
try std.testing.expectError(Error.ProtocolRejected, client.handleTraceLine("panel_open bad title"));
|
||||
try std.testing.expectError(Error.ProtocolRejected, client.handleTraceLine("panel_close"));
|
||||
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 assertLinesFit(frame, 16);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user