Current section
Files
Jump to
Current section
Files
zig/opentui/tests/text-buffer-selection_viewport_test.zig
const std = @import("std");
const text_buffer = @import("../text-buffer.zig");
const text_buffer_view = @import("../text-buffer-view.zig");
const gp = @import("../grapheme.zig");
const TextBuffer = text_buffer.TextBuffer;
const TextBufferView = text_buffer_view.TextBufferView;
const Viewport = text_buffer_view.Viewport;
// ===== Viewport-Aware Selection Tests =====
test "Selection - vertical viewport selection without wrapping" {
const pool = gp.initGlobalPool(std.testing.allocator);
defer gp.deinitGlobalPool();
var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
defer tb.deinit();
var view = try TextBufferView.init(std.testing.allocator, tb);
defer view.deinit();
try tb.setText("Line 0\nLine 1\nLine 2\nLine 3\nLine 4\nLine 5\nLine 6\nLine 7\nLine 8\nLine 9");
view.setViewport(Viewport{ .x = 0, .y = 5, .width = 10, .height = 5 });
_ = view.setLocalSelection(0, 0, 2, 2, null, null);
var buffer: [100]u8 = undefined;
const len = view.getSelectedTextIntoBuffer(&buffer);
const text = buffer[0..len];
try std.testing.expect(std.mem.indexOf(u8, text, "Line 5") != null);
try std.testing.expect(std.mem.indexOf(u8, text, "Line 6") != null);
try std.testing.expect(std.mem.indexOf(u8, text, "Li") != null);
try std.testing.expect(std.mem.indexOf(u8, text, "Line 7") == null);
}
test "Selection - horizontal viewport selection without wrapping" {
const pool = gp.initGlobalPool(std.testing.allocator);
defer gp.deinitGlobalPool();
var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
defer tb.deinit();
var view = try TextBufferView.init(std.testing.allocator, tb);
defer view.deinit();
try tb.setText("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");
view.setViewport(Viewport{ .x = 10, .y = 0, .width = 10, .height = 1 });
_ = view.setLocalSelection(0, 0, 5, 0, null, null);
var buffer: [100]u8 = undefined;
const len = view.getSelectedTextIntoBuffer(&buffer);
const text = buffer[0..len];
try std.testing.expectEqualStrings("KLMNO", text);
}
test "Selection - wrapping mode ignores horizontal viewport offset" {
const pool = gp.initGlobalPool(std.testing.allocator);
defer gp.deinitGlobalPool();
var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
defer tb.deinit();
var view = try TextBufferView.init(std.testing.allocator, tb);
defer view.deinit();
try tb.setText("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
view.setWrapMode(.char);
view.setWrapWidth(10);
view.setViewport(Viewport{ .x = 10, .y = 0, .width = 10, .height = 3 });
_ = view.setLocalSelection(0, 0, 5, 0, null, null);
var buffer: [100]u8 = undefined;
const len = view.getSelectedTextIntoBuffer(&buffer);
const text = buffer[0..len];
try std.testing.expectEqualStrings("ABCDE", text);
}
test "Selection - vertical viewport with wrapping" {
const pool = gp.initGlobalPool(std.testing.allocator);
defer gp.deinitGlobalPool();
var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
defer tb.deinit();
var view = try TextBufferView.init(std.testing.allocator, tb);
defer view.deinit();
try tb.setText("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");
view.setWrapMode(.char);
view.setWrapWidth(10);
const vline_count = view.getVirtualLineCount();
try std.testing.expectEqual(@as(u32, 4), vline_count);
view.setViewport(Viewport{ .x = 0, .y = 1, .width = 10, .height = 2 });
_ = view.setLocalSelection(0, 0, 5, 1, null, null);
var buffer: [100]u8 = undefined;
const len = view.getSelectedTextIntoBuffer(&buffer);
const text = buffer[0..len];
try std.testing.expectEqualStrings("KLMNOPQRSTUVWXY", text);
}
test "Selection - across empty line with viewport offset" {
const pool = gp.initGlobalPool(std.testing.allocator);
defer gp.deinitGlobalPool();
var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
defer tb.deinit();
var view = try TextBufferView.init(std.testing.allocator, tb);
defer view.deinit();
try tb.setText("Line0\n\nLine2\nLine3\nLine4");
view.setViewport(Viewport{ .x = 0, .y = 1, .width = 10, .height = 3 });
_ = view.setLocalSelection(0, 0, 3, 2, null, null);
var buffer: [100]u8 = undefined;
const len = view.getSelectedTextIntoBuffer(&buffer);
const text = buffer[0..len];
try std.testing.expect(std.mem.indexOf(u8, text, "Line2") != null);
try std.testing.expect(std.mem.indexOf(u8, text, "Lin") != null);
}
test "Selection - viewport offset with multi-line selection" {
const pool = gp.initGlobalPool(std.testing.allocator);
defer gp.deinitGlobalPool();
var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
defer tb.deinit();
var view = try TextBufferView.init(std.testing.allocator, tb);
defer view.deinit();
try tb.setText("AAA\nBBB\nCCC\nDDD\nEEE\nFFF\nGGG\nHHH");
view.setViewport(Viewport{ .x = 0, .y = 2, .width = 10, .height = 4 });
_ = view.setLocalSelection(0, 0, 3, 0, null, null);
var buffer: [100]u8 = undefined;
const len = view.getSelectedTextIntoBuffer(&buffer);
const text = buffer[0..len];
try std.testing.expectEqualStrings("CCC", text);
}
test "Selection - combined horizontal and vertical viewport offsets" {
const pool = gp.initGlobalPool(std.testing.allocator);
defer gp.deinitGlobalPool();
var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
defer tb.deinit();
var view = try TextBufferView.init(std.testing.allocator, tb);
defer view.deinit();
try tb.setText("ABCDEFGHIJKLMNOPQRSTUVWXYZ\n0123456789ABCDEFGHIJKLMNOP\nQRSTUVWXYZ0123456789ABCDEF");
view.setViewport(Viewport{ .x = 5, .y = 1, .width = 10, .height = 2 });
_ = view.setLocalSelection(0, 0, 5, 0, null, null);
var buffer: [100]u8 = undefined;
const len = view.getSelectedTextIntoBuffer(&buffer);
const text = buffer[0..len];
try std.testing.expectEqualStrings("56789", text);
}
test "Selection - viewport without offsets behaves as before" {
const pool = gp.initGlobalPool(std.testing.allocator);
defer gp.deinitGlobalPool();
var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
defer tb.deinit();
var view = try TextBufferView.init(std.testing.allocator, tb);
defer view.deinit();
try tb.setText("Hello World");
view.setViewport(Viewport{ .x = 0, .y = 0, .width = 20, .height = 5 });
_ = view.setLocalSelection(2, 0, 7, 0, null, null);
var buffer: [100]u8 = undefined;
const len = view.getSelectedTextIntoBuffer(&buffer);
const text = buffer[0..len];
try std.testing.expectEqualStrings("llo W", text);
}
test "Selection - no viewport behaves as before" {
const pool = gp.initGlobalPool(std.testing.allocator);
defer gp.deinitGlobalPool();
var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
defer tb.deinit();
var view = try TextBufferView.init(std.testing.allocator, tb);
defer view.deinit();
try tb.setText("Hello World");
_ = view.setLocalSelection(2, 0, 7, 0, null, null);
var buffer: [100]u8 = undefined;
const len = view.getSelectedTextIntoBuffer(&buffer);
const text = buffer[0..len];
try std.testing.expectEqualStrings("llo W", text);
}
test "Selection - VALIDATION: verify selection range matches extracted text with viewport" {
const pool = gp.initGlobalPool(std.testing.allocator);
defer gp.deinitGlobalPool();
var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
defer tb.deinit();
var view = try TextBufferView.init(std.testing.allocator, tb);
defer view.deinit();
try tb.setText("Line0\nLine1\nLine2\nLine3\nLine4\nLine5\nLine6\nLine7\nLine8\nLine9");
view.setViewport(Viewport{ .x = 0, .y = 5, .width = 10, .height = 5 });
_ = view.setLocalSelection(0, 0, 5, 0, null, null);
const selection = view.getSelection();
try std.testing.expect(selection != null);
var selected_buffer: [100]u8 = undefined;
const selected_len = view.getSelectedTextIntoBuffer(&selected_buffer);
const selected_text = selected_buffer[0..selected_len];
try std.testing.expectEqualStrings("Line5", selected_text);
const expected_start: u32 = 30; // Start of line 5
const expected_end: u32 = 35; // First 5 chars of line 5
try std.testing.expectEqual(expected_start, selection.?.start);
try std.testing.expectEqual(expected_end, selection.?.end);
}
test "Selection - VALIDATION: multi-line selection range with viewport" {
const pool = gp.initGlobalPool(std.testing.allocator);
defer gp.deinitGlobalPool();
var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
defer tb.deinit();
var view = try TextBufferView.init(std.testing.allocator, tb);
defer view.deinit();
try tb.setText("AAA\nBBB\nCCC\nDDD\nEEE\nFFF\nGGG\nHHH");
view.setViewport(Viewport{ .x = 0, .y = 3, .width = 10, .height = 5 });
_ = view.setLocalSelection(0, 0, 3, 2, null, null);
var selected_buffer: [100]u8 = undefined;
const selected_len = view.getSelectedTextIntoBuffer(&selected_buffer);
const selected_text = selected_buffer[0..selected_len];
try std.testing.expectEqualStrings("DDD\nEEE\nFFF", selected_text);
const selection = view.getSelection();
try std.testing.expect(selection != null);
const expected_start: u32 = 12; // Start of line 3
const expected_end: u32 = 23; // End of "FFF" on line 5
try std.testing.expectEqual(expected_start, selection.?.start);
try std.testing.expectEqual(expected_end, selection.?.end);
}
test "Selection - RENDER TEST: selection highlights correct cells with viewport scroll" {
const buffer_mod = @import("../buffer.zig");
const RGBA = buffer_mod.RGBA;
const pool = gp.initGlobalPool(std.testing.allocator);
defer gp.deinitGlobalPool();
var tb = try TextBuffer.init(std.testing.allocator, pool, .unicode);
defer tb.deinit();
var view = try TextBufferView.init(std.testing.allocator, tb);
defer view.deinit();
try tb.setText("AAA\nBBB\nCCC\nDDD\nEEE\nFFF\nGGG\nHHH");
view.setViewport(Viewport{ .x = 0, .y = 3, .width = 10, .height = 5 });
const red_bg = RGBA{ 1.0, 0.0, 0.0, 1.0 };
_ = view.setLocalSelection(0, 0, 3, 0, red_bg, null);
var render_buffer = try buffer_mod.OptimizedBuffer.init(std.testing.allocator, pool, 20, 10, .unicode);
defer render_buffer.deinit();
try render_buffer.drawTextBuffer(view, 0, 0);
var x: u32 = 0;
while (x < 3) : (x += 1) {
const cell = render_buffer.get(x, 0);
try std.testing.expect(cell != null);
const bg = cell.?.bg;
try std.testing.expectApproxEqAbs(@as(f32, 1.0), bg[0], 0.01); // Red
try std.testing.expectApproxEqAbs(@as(f32, 0.0), bg[1], 0.01); // Green
try std.testing.expectApproxEqAbs(@as(f32, 0.0), bg[2], 0.01); // Blue
}
const cell_3 = render_buffer.get(3, 0);
try std.testing.expect(cell_3 != null);
const bg_3 = cell_3.?.bg;
try std.testing.expect(bg_3[0] < 0.5); // Not red
}