mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-16 16:56:09 +03:00
terminal/new: erase according to bg sgr
This commit is contained in:
@ -4373,6 +4373,7 @@ test "Terminal: index bottom of primary screen" {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// X
|
||||||
test "Terminal: index bottom of primary screen background sgr" {
|
test "Terminal: index bottom of primary screen background sgr" {
|
||||||
const alloc = testing.allocator;
|
const alloc = testing.allocator;
|
||||||
var t = try init(alloc, 5, 5);
|
var t = try init(alloc, 5, 5);
|
||||||
@ -5241,6 +5242,7 @@ test "Terminal: eraseChars beyond screen edge" {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// X
|
||||||
test "Terminal: eraseChars preserves background sgr" {
|
test "Terminal: eraseChars preserves background sgr" {
|
||||||
const alloc = testing.allocator;
|
const alloc = testing.allocator;
|
||||||
var t = try init(alloc, 10, 10);
|
var t = try init(alloc, 10, 10);
|
||||||
|
@ -8,6 +8,7 @@ const Allocator = std.mem.Allocator;
|
|||||||
const assert = std.debug.assert;
|
const assert = std.debug.assert;
|
||||||
const point = @import("point.zig");
|
const point = @import("point.zig");
|
||||||
const pagepkg = @import("page.zig");
|
const pagepkg = @import("page.zig");
|
||||||
|
const stylepkg = @import("style.zig");
|
||||||
const size = @import("size.zig");
|
const size = @import("size.zig");
|
||||||
const OffsetBuf = size.OffsetBuf;
|
const OffsetBuf = size.OffsetBuf;
|
||||||
const Page = pagepkg.Page;
|
const Page = pagepkg.Page;
|
||||||
@ -488,13 +489,24 @@ const Cell = struct {
|
|||||||
row_idx: usize,
|
row_idx: usize,
|
||||||
col_idx: usize,
|
col_idx: usize,
|
||||||
|
|
||||||
|
/// Get the cell style.
|
||||||
|
///
|
||||||
|
/// Not meant for non-test usage since this is inefficient.
|
||||||
|
pub fn style(self: Cell) stylepkg.Style {
|
||||||
|
if (self.cell.style_id == stylepkg.default_id) return .{};
|
||||||
|
return self.page.data.styles.lookupId(
|
||||||
|
self.page.data.memory,
|
||||||
|
self.cell.style_id,
|
||||||
|
).?.*;
|
||||||
|
}
|
||||||
|
|
||||||
/// Gets the screen point for the given cell.
|
/// Gets the screen point for the given cell.
|
||||||
///
|
///
|
||||||
/// This is REALLY expensive/slow so it isn't pub. This was built
|
/// This is REALLY expensive/slow so it isn't pub. This was built
|
||||||
/// for debugging and tests. If you have a need for this outside of
|
/// for debugging and tests. If you have a need for this outside of
|
||||||
/// this file then consider a different approach and ask yourself very
|
/// this file then consider a different approach and ask yourself very
|
||||||
/// carefully if you really need this.
|
/// carefully if you really need this.
|
||||||
fn screenPoint(self: Cell) point.Point {
|
pub fn screenPoint(self: Cell) point.Point {
|
||||||
var x: usize = self.col_idx;
|
var x: usize = self.col_idx;
|
||||||
var y: usize = self.row_idx;
|
var y: usize = self.row_idx;
|
||||||
var page = self.page;
|
var page = self.page;
|
||||||
|
@ -199,10 +199,10 @@ pub fn cursorAbsolute(self: *Screen, x: size.CellCountInt, y: size.CellCountInt)
|
|||||||
pub fn cursorDownScroll(self: *Screen) !void {
|
pub fn cursorDownScroll(self: *Screen) !void {
|
||||||
assert(self.cursor.y == self.pages.rows - 1);
|
assert(self.cursor.y == self.pages.rows - 1);
|
||||||
|
|
||||||
// If we have cap space in our current cursor page then we can take
|
|
||||||
// a fast path: update the size, recalculate the row/cell cursor pointers.
|
|
||||||
const cursor_page = self.cursor.page_offset.page;
|
const cursor_page = self.cursor.page_offset.page;
|
||||||
if (cursor_page.data.capacity.rows > cursor_page.data.size.rows) {
|
if (cursor_page.data.capacity.rows > cursor_page.data.size.rows) {
|
||||||
|
// If we have cap space in our current cursor page then we can take
|
||||||
|
// a fast path: update the size, recalculate the row/cell cursor pointers.
|
||||||
cursor_page.data.size.rows += 1;
|
cursor_page.data.size.rows += 1;
|
||||||
|
|
||||||
const page_offset = self.cursor.page_offset.forward(1).?;
|
const page_offset = self.cursor.page_offset.forward(1).?;
|
||||||
@ -210,23 +210,30 @@ pub fn cursorDownScroll(self: *Screen) !void {
|
|||||||
self.cursor.page_offset = page_offset;
|
self.cursor.page_offset = page_offset;
|
||||||
self.cursor.page_row = page_rac.row;
|
self.cursor.page_row = page_rac.row;
|
||||||
self.cursor.page_cell = page_rac.cell;
|
self.cursor.page_cell = page_rac.cell;
|
||||||
return;
|
} else {
|
||||||
|
// No space, we need to allocate a new page and move the cursor to it.
|
||||||
|
const new_page = try self.pages.grow();
|
||||||
|
assert(new_page.data.size.rows == 0);
|
||||||
|
new_page.data.size.rows = 1;
|
||||||
|
const page_offset: PageList.RowOffset = .{
|
||||||
|
.page = new_page,
|
||||||
|
.row_offset = 0,
|
||||||
|
};
|
||||||
|
const page_rac = page_offset.rowAndCell(self.cursor.x);
|
||||||
|
self.cursor.page_offset = page_offset;
|
||||||
|
self.cursor.page_row = page_rac.row;
|
||||||
|
self.cursor.page_cell = page_rac.cell;
|
||||||
}
|
}
|
||||||
|
|
||||||
// No space, we need to allocate a new page and move the cursor to it.
|
// The newly created line needs to be styled according to the bg color
|
||||||
// TODO: copy style over
|
// if it is set.
|
||||||
|
if (self.cursor.style_id != style.default_id) {
|
||||||
const new_page = try self.pages.grow();
|
if (self.cursor.style.bgCell()) |blank_cell| {
|
||||||
assert(new_page.data.size.rows == 0);
|
const cell_current: [*]pagepkg.Cell = @ptrCast(self.cursor.page_cell);
|
||||||
new_page.data.size.rows = 1;
|
const cells = cell_current - self.cursor.x;
|
||||||
const page_offset: PageList.RowOffset = .{
|
@memset(cells[0..self.pages.cols], blank_cell);
|
||||||
.page = new_page,
|
}
|
||||||
.row_offset = 0,
|
}
|
||||||
};
|
|
||||||
const page_rac = page_offset.rowAndCell(self.cursor.x);
|
|
||||||
self.cursor.page_offset = page_offset;
|
|
||||||
self.cursor.page_row = page_rac.row;
|
|
||||||
self.cursor.page_cell = page_rac.cell;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Options for scrolling the viewport of the terminal grid. The reason
|
/// Options for scrolling the viewport of the terminal grid. The reason
|
||||||
|
@ -1084,6 +1084,8 @@ pub fn insertLines(self: *Terminal, count: usize) void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Inserted lines should keep our bg color
|
||||||
|
const blank_cell = self.blankCell();
|
||||||
for (0..adjusted_count) |i| {
|
for (0..adjusted_count) |i| {
|
||||||
const row: *Row = @ptrCast(top + i);
|
const row: *Row = @ptrCast(top + i);
|
||||||
|
|
||||||
@ -1100,8 +1102,7 @@ pub fn insertLines(self: *Terminal, count: usize) void {
|
|||||||
assert(!row.grapheme);
|
assert(!row.grapheme);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: cells should keep bg style of pen
|
@memset(cells, blank_cell);
|
||||||
@memset(cells, .{});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move the cursor to the left margin. But importantly this also
|
// Move the cursor to the left margin. But importantly this also
|
||||||
@ -1177,6 +1178,7 @@ pub fn deleteLines(self: *Terminal, count_req: usize) void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const bottom: [*]Row = top + (rem - 1);
|
const bottom: [*]Row = top + (rem - 1);
|
||||||
|
const blank_cell = self.blankCell();
|
||||||
while (@intFromPtr(y) <= @intFromPtr(bottom)) : (y += 1) {
|
while (@intFromPtr(y) <= @intFromPtr(bottom)) : (y += 1) {
|
||||||
const row: *Row = @ptrCast(y);
|
const row: *Row = @ptrCast(y);
|
||||||
|
|
||||||
@ -1193,8 +1195,7 @@ pub fn deleteLines(self: *Terminal, count_req: usize) void {
|
|||||||
assert(!row.grapheme);
|
assert(!row.grapheme);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: cells should keep bg style of pen
|
@memset(cells, blank_cell);
|
||||||
@memset(cells, .{});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Move the cursor to the left margin. But importantly this also
|
// Move the cursor to the left margin. But importantly this also
|
||||||
@ -1230,9 +1231,8 @@ pub fn eraseChars(self: *Terminal, count_req: usize) void {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Clear the cells
|
// Clear the cells
|
||||||
// TODO: clear with current bg color
|
|
||||||
const cells: [*]Cell = @ptrCast(self.screen.cursor.page_cell);
|
const cells: [*]Cell = @ptrCast(self.screen.cursor.page_cell);
|
||||||
@memset(cells[0..end], .{});
|
@memset(cells[0..end], self.blankCell());
|
||||||
|
|
||||||
// This resets the soft-wrap of this line
|
// This resets the soft-wrap of this line
|
||||||
self.screen.cursor.page_row.wrap = false;
|
self.screen.cursor.page_row.wrap = false;
|
||||||
@ -1275,6 +1275,13 @@ pub fn plainString(self: *Terminal, alloc: Allocator) ![]const u8 {
|
|||||||
return try self.screen.dumpStringAlloc(alloc, .{ .viewport = .{} });
|
return try self.screen.dumpStringAlloc(alloc, .{ .viewport = .{} });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the blank cell to use when doing terminal operations that
|
||||||
|
/// require preserving the bg color.
|
||||||
|
fn blankCell(self: *const Terminal) Cell {
|
||||||
|
if (self.screen.cursor.style_id == style.default_id) return .{};
|
||||||
|
return self.screen.cursor.style.bgCell() orelse .{};
|
||||||
|
}
|
||||||
|
|
||||||
test "Terminal: input with no control characters" {
|
test "Terminal: input with no control characters" {
|
||||||
const alloc = testing.allocator;
|
const alloc = testing.allocator;
|
||||||
var t = try init(alloc, 40, 40);
|
var t = try init(alloc, 40, 40);
|
||||||
@ -2475,6 +2482,44 @@ test "Terminal: insertLines simple" {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "Terminal: insertLines colors with bg color" {
|
||||||
|
const alloc = testing.allocator;
|
||||||
|
var t = try init(alloc, 5, 5);
|
||||||
|
defer t.deinit(alloc);
|
||||||
|
|
||||||
|
try t.printString("ABC");
|
||||||
|
t.carriageReturn();
|
||||||
|
try t.linefeed();
|
||||||
|
try t.printString("DEF");
|
||||||
|
t.carriageReturn();
|
||||||
|
try t.linefeed();
|
||||||
|
try t.printString("GHI");
|
||||||
|
t.setCursorPos(2, 2);
|
||||||
|
|
||||||
|
try t.setAttribute(.{ .direct_color_bg = .{
|
||||||
|
.r = 0xFF,
|
||||||
|
.g = 0,
|
||||||
|
.b = 0,
|
||||||
|
} });
|
||||||
|
t.insertLines(1);
|
||||||
|
|
||||||
|
{
|
||||||
|
const str = try t.plainString(testing.allocator);
|
||||||
|
defer testing.allocator.free(str);
|
||||||
|
try testing.expectEqualStrings("ABC\n\nDEF\nGHI", str);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (0..t.cols) |x| {
|
||||||
|
const list_cell = t.screen.pages.getCell(.{ .active = .{ .x = x, .y = 1 } }).?;
|
||||||
|
try testing.expect(list_cell.cell.content_tag == .bg_color_rgb);
|
||||||
|
try testing.expectEqual(Cell.RGB{
|
||||||
|
.r = 0xFF,
|
||||||
|
.g = 0,
|
||||||
|
.b = 0,
|
||||||
|
}, list_cell.cell.content.color_rgb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
test "Terminal: insertLines outside of scroll region" {
|
test "Terminal: insertLines outside of scroll region" {
|
||||||
const alloc = testing.allocator;
|
const alloc = testing.allocator;
|
||||||
var t = try init(alloc, 5, 5);
|
var t = try init(alloc, 5, 5);
|
||||||
@ -2958,6 +3003,45 @@ test "Terminal: eraseChars resets wrap" {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "Terminal: eraseChars preserves background sgr" {
|
||||||
|
const alloc = testing.allocator;
|
||||||
|
var t = try init(alloc, 10, 10);
|
||||||
|
defer t.deinit(alloc);
|
||||||
|
|
||||||
|
for ("ABC") |c| try t.print(c);
|
||||||
|
t.setCursorPos(1, 1);
|
||||||
|
try t.setAttribute(.{ .direct_color_bg = .{
|
||||||
|
.r = 0xFF,
|
||||||
|
.g = 0,
|
||||||
|
.b = 0,
|
||||||
|
} });
|
||||||
|
t.eraseChars(2);
|
||||||
|
|
||||||
|
{
|
||||||
|
const str = try t.plainString(testing.allocator);
|
||||||
|
defer testing.allocator.free(str);
|
||||||
|
try testing.expectEqualStrings(" C", str);
|
||||||
|
{
|
||||||
|
const list_cell = t.screen.pages.getCell(.{ .active = .{ .x = 0, .y = 0 } }).?;
|
||||||
|
try testing.expect(list_cell.cell.content_tag == .bg_color_rgb);
|
||||||
|
try testing.expectEqual(Cell.RGB{
|
||||||
|
.r = 0xFF,
|
||||||
|
.g = 0,
|
||||||
|
.b = 0,
|
||||||
|
}, list_cell.cell.content.color_rgb);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const list_cell = t.screen.pages.getCell(.{ .active = .{ .x = 1, .y = 0 } }).?;
|
||||||
|
try testing.expect(list_cell.cell.content_tag == .bg_color_rgb);
|
||||||
|
try testing.expectEqual(Cell.RGB{
|
||||||
|
.r = 0xFF,
|
||||||
|
.g = 0,
|
||||||
|
.b = 0,
|
||||||
|
}, list_cell.cell.content.color_rgb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
test "Terminal: reverseIndex" {
|
test "Terminal: reverseIndex" {
|
||||||
const alloc = testing.allocator;
|
const alloc = testing.allocator;
|
||||||
var t = try init(alloc, 2, 5);
|
var t = try init(alloc, 2, 5);
|
||||||
@ -3231,6 +3315,36 @@ test "Terminal: index bottom of primary screen" {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "Terminal: index bottom of primary screen background sgr" {
|
||||||
|
const alloc = testing.allocator;
|
||||||
|
var t = try init(alloc, 5, 5);
|
||||||
|
defer t.deinit(alloc);
|
||||||
|
|
||||||
|
t.setCursorPos(5, 1);
|
||||||
|
try t.print('A');
|
||||||
|
try t.setAttribute(.{ .direct_color_bg = .{
|
||||||
|
.r = 0xFF,
|
||||||
|
.g = 0,
|
||||||
|
.b = 0,
|
||||||
|
} });
|
||||||
|
try t.index();
|
||||||
|
|
||||||
|
{
|
||||||
|
const str = try t.plainString(testing.allocator);
|
||||||
|
defer testing.allocator.free(str);
|
||||||
|
try testing.expectEqualStrings("\n\n\nA", str);
|
||||||
|
for (0..5) |x| {
|
||||||
|
const list_cell = t.screen.pages.getCell(.{ .active = .{ .x = x, .y = 4 } }).?;
|
||||||
|
try testing.expect(list_cell.cell.content_tag == .bg_color_rgb);
|
||||||
|
try testing.expectEqual(Cell.RGB{
|
||||||
|
.r = 0xFF,
|
||||||
|
.g = 0,
|
||||||
|
.b = 0,
|
||||||
|
}, list_cell.cell.content.color_rgb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
test "Terminal: index inside scroll region" {
|
test "Terminal: index inside scroll region" {
|
||||||
const alloc = testing.allocator;
|
const alloc = testing.allocator;
|
||||||
var t = try init(alloc, 5, 5);
|
var t = try init(alloc, 5, 5);
|
||||||
@ -3794,6 +3908,44 @@ test "Terminal: deleteLines simple" {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "Terminal: deleteLines colors with bg color" {
|
||||||
|
const alloc = testing.allocator;
|
||||||
|
var t = try init(alloc, 5, 5);
|
||||||
|
defer t.deinit(alloc);
|
||||||
|
|
||||||
|
try t.printString("ABC");
|
||||||
|
t.carriageReturn();
|
||||||
|
try t.linefeed();
|
||||||
|
try t.printString("DEF");
|
||||||
|
t.carriageReturn();
|
||||||
|
try t.linefeed();
|
||||||
|
try t.printString("GHI");
|
||||||
|
t.setCursorPos(2, 2);
|
||||||
|
|
||||||
|
try t.setAttribute(.{ .direct_color_bg = .{
|
||||||
|
.r = 0xFF,
|
||||||
|
.g = 0,
|
||||||
|
.b = 0,
|
||||||
|
} });
|
||||||
|
t.deleteLines(1);
|
||||||
|
|
||||||
|
{
|
||||||
|
const str = try t.plainString(testing.allocator);
|
||||||
|
defer testing.allocator.free(str);
|
||||||
|
try testing.expectEqualStrings("ABC\nGHI", str);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (0..t.cols) |x| {
|
||||||
|
const list_cell = t.screen.pages.getCell(.{ .active = .{ .x = x, .y = 4 } }).?;
|
||||||
|
try testing.expect(list_cell.cell.content_tag == .bg_color_rgb);
|
||||||
|
try testing.expectEqual(Cell.RGB{
|
||||||
|
.r = 0xFF,
|
||||||
|
.g = 0,
|
||||||
|
.b = 0,
|
||||||
|
}, list_cell.cell.content.color_rgb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
test "Terminal: deleteLines (legacy)" {
|
test "Terminal: deleteLines (legacy)" {
|
||||||
const alloc = testing.allocator;
|
const alloc = testing.allocator;
|
||||||
var t = try init(alloc, 80, 80);
|
var t = try init(alloc, 80, 80);
|
||||||
|
@ -51,6 +51,25 @@ pub const Style = struct {
|
|||||||
return std.mem.eql(u8, std.mem.asBytes(&self), def);
|
return std.mem.eql(u8, std.mem.asBytes(&self), def);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a bg-color only cell from this style, if it exists.
|
||||||
|
pub fn bgCell(self: Style) ?page.Cell {
|
||||||
|
return switch (self.bg_color) {
|
||||||
|
.none => null,
|
||||||
|
.palette => |idx| .{
|
||||||
|
.content_tag = .bg_color_palette,
|
||||||
|
.content = .{ .color_palette = idx },
|
||||||
|
},
|
||||||
|
.rgb => |rgb| .{
|
||||||
|
.content_tag = .bg_color_rgb,
|
||||||
|
.content = .{ .color_rgb = .{
|
||||||
|
.r = rgb.r,
|
||||||
|
.g = rgb.g,
|
||||||
|
.b = rgb.b,
|
||||||
|
} },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
test {
|
test {
|
||||||
// The size of the struct so we can be aware of changes.
|
// The size of the struct so we can be aware of changes.
|
||||||
const testing = std.testing;
|
const testing = std.testing;
|
||||||
|
Reference in New Issue
Block a user