mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-16 16:56:09 +03:00
perf(terminal): specialize splitCellBoundary
to cursor row
+ do some abstraction leakage in `cursorResetWrap`, since they're both used in hot functions for TUI stuff so performance is important.
This commit is contained in:
@ -3172,34 +3172,6 @@ pub const Pin = struct {
|
|||||||
set.set(self.y);
|
set.set(self.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resets the soft-wrap state of the row this pin is on, appropriately
|
|
||||||
/// handling the wrap and wrap_continuation flags for the previous and
|
|
||||||
/// next row as well.
|
|
||||||
///
|
|
||||||
/// DOES NOT handle clearing spacer heads.
|
|
||||||
/// Use `Screen.splitCellBoundary` for that.
|
|
||||||
///
|
|
||||||
/// TODO: test
|
|
||||||
pub fn resetWrap(self: Pin) void {
|
|
||||||
const rac = self.rowAndCell();
|
|
||||||
|
|
||||||
// This row does not wrap
|
|
||||||
rac.row.wrap = false;
|
|
||||||
|
|
||||||
// This row is not wrapped to
|
|
||||||
rac.row.wrap_continuation = false;
|
|
||||||
|
|
||||||
// The previous row does not wrap to this row
|
|
||||||
if (self.up(1)) |prev_row| {
|
|
||||||
prev_row.rowAndCell().row.wrap = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The next row is not wrapped to
|
|
||||||
if (self.down(1)) |next_row| {
|
|
||||||
next_row.rowAndCell().row.wrap_continuation = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns true if the row of this pin should never have its background
|
/// Returns true if the row of this pin should never have its background
|
||||||
/// color extended for filling padding space in the renderer. This is
|
/// color extended for filling padding space in the renderer. This is
|
||||||
/// a set of heuristics that help making our padding look better.
|
/// a set of heuristics that help making our padding look better.
|
||||||
|
@ -1044,12 +1044,57 @@ pub fn cursorMarkDirty(self: *Screen) void {
|
|||||||
/// Reset the cursor row's soft-wrap state and the cursor's pending wrap.
|
/// Reset the cursor row's soft-wrap state and the cursor's pending wrap.
|
||||||
/// Also clears spacer heads from the cursor row and prior row as necessary.
|
/// Also clears spacer heads from the cursor row and prior row as necessary.
|
||||||
pub fn cursorResetWrap(self: *Screen) void {
|
pub fn cursorResetWrap(self: *Screen) void {
|
||||||
// Handle boundary conditions on left and right of the row.
|
// Reset the cursor's pending wrap state
|
||||||
self.splitCellBoundary(self.cursor.page_pin.*, 0);
|
|
||||||
self.splitCellBoundary(self.cursor.page_pin.*, self.cursor.page_pin.page.data.size.cols);
|
|
||||||
|
|
||||||
self.cursor.page_pin.resetWrap();
|
|
||||||
self.cursor.pending_wrap = false;
|
self.cursor.pending_wrap = false;
|
||||||
|
|
||||||
|
const page_row = self.cursor.page_row;
|
||||||
|
|
||||||
|
if (!(page_row.wrap or page_row.wrap_continuation)) return;
|
||||||
|
|
||||||
|
const cells = self.cursor.page_pin.cells(.all);
|
||||||
|
|
||||||
|
// The previous row does not wrap and this row is not wrapped to
|
||||||
|
if (page_row.wrap_continuation) {
|
||||||
|
page_row.wrap_continuation = false;
|
||||||
|
|
||||||
|
if (self.cursor.page_pin.up(1)) |prev_row| {
|
||||||
|
const p_rac = prev_row.rowAndCell();
|
||||||
|
p_rac.row.wrap = false;
|
||||||
|
|
||||||
|
// If the first cell in a row is wide the previous row
|
||||||
|
// may have a spacer head which needs to be cleared.
|
||||||
|
if (cells[0].wide == .wide) {
|
||||||
|
const p_cells = prev_row.cells(.all);
|
||||||
|
const p_cell = p_cells[prev_row.page.data.size.cols - 1];
|
||||||
|
if (p_cell.wide == .spacer_head) {
|
||||||
|
self.clearCells(
|
||||||
|
&prev_row.page.data,
|
||||||
|
p_rac.row,
|
||||||
|
p_cells[prev_row.page.data.size.cols - 1 ..][0..1],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This row does not wrap and the next row is not wrapped to
|
||||||
|
if (page_row.wrap) {
|
||||||
|
page_row.wrap = false;
|
||||||
|
|
||||||
|
if (self.cursor.page_pin.down(1)) |next_row| {
|
||||||
|
next_row.rowAndCell().row.wrap_continuation = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the last cell in the row is a spacer head we need to clear it.
|
||||||
|
const cell = cells[self.cursor.page_pin.page.data.size.cols - 1];
|
||||||
|
if (cell.wide == .spacer_head) {
|
||||||
|
self.clearCells(
|
||||||
|
&self.cursor.page_pin.page.data,
|
||||||
|
page_row,
|
||||||
|
cells[self.cursor.page_pin.page.data.size.cols - 1 ..][0..1],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Options for scrolling the viewport of the terminal grid. The reason
|
/// Options for scrolling the viewport of the terminal grid. The reason
|
||||||
@ -1296,6 +1341,8 @@ pub fn clearPrompt(self: *Screen) void {
|
|||||||
/// Clean up boundary conditions where a cell will become discontiguous with
|
/// Clean up boundary conditions where a cell will become discontiguous with
|
||||||
/// a neighboring cell because either one of them will be moved and/or cleared.
|
/// a neighboring cell because either one of them will be moved and/or cleared.
|
||||||
///
|
///
|
||||||
|
/// For performance reasons this is specialized to operate on the cursor row.
|
||||||
|
///
|
||||||
/// Handles the boundary between the cell at `x` and the cell at `x - 1`.
|
/// Handles the boundary between the cell at `x` and the cell at `x - 1`.
|
||||||
///
|
///
|
||||||
/// So, for example, when moving a region of cells [a, b] (inclusive), call this
|
/// So, for example, when moving a region of cells [a, b] (inclusive), call this
|
||||||
@ -1320,15 +1367,14 @@ pub fn clearPrompt(self: *Screen) void {
|
|||||||
/// o `x - 1` will be cleared.
|
/// o `x - 1` will be cleared.
|
||||||
pub fn splitCellBoundary(
|
pub fn splitCellBoundary(
|
||||||
self: *Screen,
|
self: *Screen,
|
||||||
row: Pin,
|
|
||||||
x: size.CellCountInt,
|
x: size.CellCountInt,
|
||||||
) void {
|
) void {
|
||||||
row.page.data.pauseIntegrityChecks(true);
|
const page = &self.cursor.page_pin.page.data;
|
||||||
defer row.page.data.pauseIntegrityChecks(false);
|
|
||||||
|
|
||||||
const rac = row.rowAndCell();
|
page.pauseIntegrityChecks(true);
|
||||||
const cells = row.cells(.all);
|
defer page.pauseIntegrityChecks(false);
|
||||||
const cols = row.page.data.size.cols;
|
|
||||||
|
const cols = self.cursor.page_pin.page.data.size.cols;
|
||||||
|
|
||||||
// `x` may be up to an INCLUDING `cols`, since that signifies splitting
|
// `x` may be up to an INCLUDING `cols`, since that signifies splitting
|
||||||
// the boundary to the right of the final cell in the row.
|
// the boundary to the right of the final cell in the row.
|
||||||
@ -1337,11 +1383,15 @@ pub fn splitCellBoundary(
|
|||||||
// [ A B C D E F|]
|
// [ A B C D E F|]
|
||||||
// ^ Boundary between final cell and row end.
|
// ^ Boundary between final cell and row end.
|
||||||
if (x == cols) {
|
if (x == cols) {
|
||||||
|
if (!self.cursor.page_row.wrap) return;
|
||||||
|
|
||||||
|
const cells = self.cursor.page_pin.cells(.all);
|
||||||
|
|
||||||
// Spacer head at end of wrapped row.
|
// Spacer head at end of wrapped row.
|
||||||
if (cells[cols - 1].wide == .spacer_head) {
|
if (cells[cols - 1].wide == .spacer_head) {
|
||||||
self.clearCells(
|
self.clearCells(
|
||||||
&row.page.data,
|
page,
|
||||||
rac.row,
|
self.cursor.page_row,
|
||||||
cells[cols - 1 ..][0..1],
|
cells[cols - 1 ..][0..1],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -1359,11 +1409,13 @@ pub fn splitCellBoundary(
|
|||||||
//
|
//
|
||||||
// First cell may be a wrapped wide cell with a spacer
|
// First cell may be a wrapped wide cell with a spacer
|
||||||
// head on the previous row that needs to be cleared.
|
// head on the previous row that needs to be cleared.
|
||||||
if (x == 0 or x == 1) {
|
if ((x == 0 or x == 1) and self.cursor.page_row.wrap_continuation) {
|
||||||
|
const cells = self.cursor.page_pin.cells(.all);
|
||||||
|
|
||||||
// If the first cell in a row is wide the previous row
|
// If the first cell in a row is wide the previous row
|
||||||
// may have a spacer head which needs to be cleared.
|
// may have a spacer head which needs to be cleared.
|
||||||
if (cells[0].wide == .wide and rac.row.wrap_continuation) {
|
if (cells[0].wide == .wide) {
|
||||||
if (row.up(1)) |p_row| {
|
if (self.cursor.page_pin.up(1)) |p_row| {
|
||||||
const p_rac = p_row.rowAndCell();
|
const p_rac = p_row.rowAndCell();
|
||||||
const p_cells = p_row.cells(.all);
|
const p_cells = p_row.cells(.all);
|
||||||
const p_cell = p_cells[p_row.page.data.size.cols - 1];
|
const p_cell = p_cells[p_row.page.data.size.cols - 1];
|
||||||
@ -1376,32 +1428,36 @@ pub fn splitCellBoundary(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If x is 0 then we're done.
|
|
||||||
if (x == 0) return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If x is 0 then we're done.
|
||||||
|
if (x == 0) return;
|
||||||
|
|
||||||
// [ ... X|Y ... ]
|
// [ ... X|Y ... ]
|
||||||
// ^ Boundary between two cells in the middle of the row.
|
// ^ Boundary between two cells in the middle of the row.
|
||||||
assert(x > 0);
|
{
|
||||||
assert(x < cols);
|
assert(x > 0);
|
||||||
|
assert(x < cols);
|
||||||
|
|
||||||
const left = cells[x - 1];
|
const cells = self.cursor.page_pin.cells(.all);
|
||||||
switch (left.wide) {
|
|
||||||
// There should not be spacer heads in the middle of the row.
|
|
||||||
.spacer_head => unreachable,
|
|
||||||
|
|
||||||
// We don't need to do anything for narrow cells or spacer tails.
|
const left = cells[x - 1];
|
||||||
.narrow, .spacer_tail => {},
|
switch (left.wide) {
|
||||||
|
// There should not be spacer heads in the middle of the row.
|
||||||
|
.spacer_head => unreachable,
|
||||||
|
|
||||||
// A wide char would be split, so must be cleared.
|
// We don't need to do anything for narrow cells or spacer tails.
|
||||||
.wide => {
|
.narrow, .spacer_tail => {},
|
||||||
self.clearCells(
|
|
||||||
&row.page.data,
|
// A wide char would be split, so must be cleared.
|
||||||
rac.row,
|
.wide => {
|
||||||
cells[x - 1 ..][0..2],
|
self.clearCells(
|
||||||
);
|
page,
|
||||||
},
|
self.cursor.page_row,
|
||||||
|
cells[x - 1 ..][0..2],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1917,18 +1917,9 @@ pub fn deleteChars(self: *Terminal, count_req: usize) void {
|
|||||||
// We can only insert blanks up to our remaining cols
|
// We can only insert blanks up to our remaining cols
|
||||||
const count = @min(count_req, rem);
|
const count = @min(count_req, rem);
|
||||||
|
|
||||||
self.screen.splitCellBoundary(
|
self.screen.splitCellBoundary(self.screen.cursor.x);
|
||||||
self.screen.cursor.page_pin.*,
|
self.screen.splitCellBoundary(self.screen.cursor.x + count);
|
||||||
self.screen.cursor.x,
|
self.screen.splitCellBoundary(self.scrolling_region.right + 1);
|
||||||
);
|
|
||||||
self.screen.splitCellBoundary(
|
|
||||||
self.screen.cursor.page_pin.*,
|
|
||||||
self.screen.cursor.x + count,
|
|
||||||
);
|
|
||||||
self.screen.splitCellBoundary(
|
|
||||||
self.screen.cursor.page_pin.*,
|
|
||||||
self.scrolling_region.right + 1,
|
|
||||||
);
|
|
||||||
|
|
||||||
// This is the amount of space at the right of the scroll region
|
// This is the amount of space at the right of the scroll region
|
||||||
// that will NOT be blank, so we need to shift the correct cols right.
|
// that will NOT be blank, so we need to shift the correct cols right.
|
||||||
@ -1982,14 +1973,8 @@ pub fn eraseChars(self: *Terminal, count_req: usize) void {
|
|||||||
// TODO(qwerasd): This isn't actually correct if you take in to account
|
// TODO(qwerasd): This isn't actually correct if you take in to account
|
||||||
// protected modes. We need to figure out how to make `clearCells` or at
|
// protected modes. We need to figure out how to make `clearCells` or at
|
||||||
// least `clearUnprotectedCells` handle boundary conditions...
|
// least `clearUnprotectedCells` handle boundary conditions...
|
||||||
self.screen.splitCellBoundary(
|
self.screen.splitCellBoundary(self.screen.cursor.x);
|
||||||
self.screen.cursor.page_pin.*,
|
self.screen.splitCellBoundary(end);
|
||||||
self.screen.cursor.x,
|
|
||||||
);
|
|
||||||
self.screen.splitCellBoundary(
|
|
||||||
self.screen.cursor.page_pin.*,
|
|
||||||
end,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Reset our row's soft-wrap.
|
// Reset our row's soft-wrap.
|
||||||
self.screen.cursorResetWrap();
|
self.screen.cursorResetWrap();
|
||||||
|
Reference in New Issue
Block a user