From 01ceb7b2675c92e004bb169687700a56713801fb Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 7 Mar 2024 13:26:42 -0800 Subject: [PATCH] terminal2: selectionString with wide spacer head --- src/terminal/Screen.zig | 2 ++ src/terminal2/Screen.zig | 66 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/src/terminal/Screen.zig b/src/terminal/Screen.zig index 553b97004..107cefb34 100644 --- a/src/terminal/Screen.zig +++ b/src/terminal/Screen.zig @@ -5759,6 +5759,7 @@ test "Screen: selectionString wide char" { } } +// X test "Screen: selectionString wide char with header" { const testing = std.testing; const alloc = testing.allocator; @@ -5779,6 +5780,7 @@ test "Screen: selectionString wide char with header" { } } +// X // https://github.com/mitchellh/ghostty/issues/289 test "Screen: selectionString empty with soft wrap" { const testing = std.testing; diff --git a/src/terminal2/Screen.zig b/src/terminal2/Screen.zig index 2f1e018ec..3e3255e77 100644 --- a/src/terminal2/Screen.zig +++ b/src/terminal2/Screen.zig @@ -852,7 +852,18 @@ pub fn selectionString( const sel_end = end: { var end = sel.end(); const cell = end.rowAndCell().cell; - if (cell.wide == .spacer_tail) end.x -= 1; + switch (cell.wide) { + .narrow, .wide => {}, + + // We can omit the tail + .spacer_tail => end.x -= 1, + + // With the head we want to include the wrapped wide character. + .spacer_head => if (end.down(1)) |p| { + end = p; + end.x = 0; + }, + } break :end end; }; @@ -5277,3 +5288,56 @@ test "Screen: selectionString wide char" { try testing.expectEqualStrings(expected, contents); } } + +test "Screen: selectionString wide char with header" { + const testing = std.testing; + const alloc = testing.allocator; + + var s = try init(alloc, 5, 3, 0); + defer s.deinit(); + const str = "1ABC⚡"; + try s.testWriteString(str); + + { + const sel = Selection.init( + s.pages.pin(.{ .screen = .{ .x = 0, .y = 0 } }).?, + s.pages.pin(.{ .screen = .{ .x = 4, .y = 0 } }).?, + false, + ); + const contents = try s.selectionString(alloc, sel, true); + defer alloc.free(contents); + const expected = str; + try testing.expectEqualStrings(expected, contents); + } +} + +// https://github.com/mitchellh/ghostty/issues/289 +test "Screen: selectionString empty with soft wrap" { + const testing = std.testing; + const alloc = testing.allocator; + + var s = try init(alloc, 5, 2, 0); + defer s.deinit(); + + // Let me describe the situation that caused this because this + // test is not obvious. By writing an emoji below, we introduce + // one cell with the emoji and one cell as a "wide char spacer". + // We then soft wrap the line by writing spaces. + // + // By selecting only the tail, we'd select nothing and we had + // a logic error that would cause a crash. + try s.testWriteString("👨"); + try s.testWriteString(" "); + + { + const sel = Selection.init( + s.pages.pin(.{ .screen = .{ .x = 1, .y = 0 } }).?, + s.pages.pin(.{ .screen = .{ .x = 2, .y = 0 } }).?, + false, + ); + const contents = try s.selectionString(alloc, sel, true); + defer alloc.free(contents); + const expected = "👨"; + try testing.expectEqualStrings(expected, contents); + } +}