Selection reflow.

This commit is contained in:
Martín Lucas Golini
2026-03-12 03:03:30 -03:00
parent a8384f183b
commit acb76936c1
2 changed files with 93 additions and 6 deletions

View File

@@ -317,7 +317,7 @@ class TerminalEmulator final {
void tdumpsel();
void tdumpline( int );
void tdump();
void tclearregion( int, int, int, int );
void tclearregion( int, int, int, int, bool skip_clear = false );
void tcursor( int );
void tdeletechar( int );
void tdeleteline( int );

View File

@@ -367,7 +367,7 @@ void TerminalEmulator::selinit( void ) {
}
int TerminalEmulator::tlinelen( int y ) const {
if ( y < 0 )
if ( y < mTerm.scr - mTerm.histlen || y >= mTerm.scr + mTerm.row )
return 0;
int i = mTerm.col;
@@ -1012,6 +1012,13 @@ void TerminalEmulator::historyReflow( int old_col, int new_col ) {
int new_len = 0;
int new_histi = -1;
int new_max_width = 0;
bool has_sel = mSel.ob.x != -1;
int ob_abs_y = mSel.ob.y;
int oe_abs_y = mSel.oe.y;
int ob_x = mSel.ob.x;
int oe_x = mSel.oe.x;
int ob_logical_offset = -1;
int oe_logical_offset = -1;
if ( mTerm.histlen == 0 )
return;
@@ -1034,6 +1041,13 @@ void TerminalEmulator::historyReflow( int old_col, int new_col ) {
logical = (Line)eeRealloc( logical, logical_cap * sizeof( TerminalGlyph ) );
}
if ( has_sel ) {
if ( i == ob_abs_y )
ob_logical_offset = logical_len + ob_x;
if ( i == oe_abs_y )
oe_logical_offset = logical_len + oe_x;
}
memcpy( logical + logical_len, line, old_col * sizeof( TerminalGlyph ) );
for ( j = 0; j < old_col; j++ )
logical[logical_len + j].mode &= ~ATTR_WRAP;
@@ -1053,6 +1067,13 @@ void TerminalEmulator::historyReflow( int old_col, int new_col ) {
if ( logical_len == 0 )
logical_len = 1;
if ( has_sel ) {
if ( ob_logical_offset != -1 && ob_logical_offset > logical_len )
ob_logical_offset = logical_len;
if ( oe_logical_offset != -1 && oe_logical_offset > logical_len )
oe_logical_offset = logical_len;
}
int cursor = 0;
while ( cursor < logical_len ) {
Line nl = (Line)eeMalloc( new_col * sizeof( TerminalGlyph ) );
@@ -1071,6 +1092,23 @@ void TerminalEmulator::historyReflow( int old_col, int new_col ) {
copy_width--;
}
if ( has_sel ) {
if ( ob_logical_offset >= cursor &&
ob_logical_offset < cursor + copy_width +
( ( cursor + copy_width == logical_len ) ? 1 : 0 ) ) {
mSel.ob.y = new_len;
mSel.ob.x = ob_logical_offset - cursor;
}
if ( oe_logical_offset >= cursor &&
oe_logical_offset < cursor + copy_width +
( ( cursor + copy_width == logical_len ) ? 1 : 0 ) ) {
mSel.oe.y = new_len;
mSel.oe.x = oe_logical_offset - cursor;
}
mSel.ob.x = eemin( ob_logical_offset - cursor, new_col - 1 );
mSel.oe.x = eemin( oe_logical_offset - cursor, new_col - 1 );
}
memcpy( nl, logical + cursor, copy_width * sizeof( TerminalGlyph ) );
for ( j = 0; j < copy_width; j++ )
nl[j].mode &= ~ATTR_WRAP;
@@ -1095,6 +1133,8 @@ void TerminalEmulator::historyReflow( int old_col, int new_col ) {
cursor += copy_width;
}
logical_len = 0;
ob_logical_offset = -1;
oe_logical_offset = -1;
}
eeSAFE_FREE( logical );
@@ -1250,7 +1290,7 @@ void TerminalEmulator::tsetchar( Rune u, TerminalGlyph* attr, int x, int y ) {
TLINE( y )[x].mode |= ATTR_BOXDRAW;
}
void TerminalEmulator::tclearregion( int x1, int y1, int x2, int y2 ) {
void TerminalEmulator::tclearregion( int x1, int y1, int x2, int y2, bool skip_clear ) {
int x, y, temp;
TerminalGlyph* gp;
@@ -1269,7 +1309,7 @@ void TerminalEmulator::tclearregion( int x1, int y1, int x2, int y2 ) {
mDirty = true;
for ( x = x1; x <= x2; x++ ) {
gp = &TLINE( y )[x];
if ( selected( x, y ) )
if ( !skip_clear && selected( x, y ) )
selclear();
gp->fg = mTerm.c.attr.fg;
gp->bg = mTerm.c.attr.bg;
@@ -2693,8 +2733,25 @@ void TerminalEmulator::tresize( int col, int row ) {
return;
}
if ( mSel.ob.x != -1 )
bool has_sel = mSel.ob.x != -1;
// Rectangular selections and alt-screen selections are not reflowed.
if ( has_sel && ( mSel.type == SEL_RECTANGULAR || mSel.alt || is_alt ) ) {
selclear();
has_sel = false;
}
int old_histlen = mTerm.histlen;
if ( has_sel ) {
if ( mTerm.histsize < mTerm.row ) {
// Back-conversion breaks when histsize < row because
// loaded < save_end; safer to drop the selection.
selclear();
has_sel = false;
} else {
mSel.ob.y += old_histlen - mTerm.scr;
mSel.oe.y += old_histlen - mTerm.scr;
}
}
if ( is_alt )
tswapscreen();
@@ -2702,7 +2759,7 @@ void TerminalEmulator::tresize( int col, int row ) {
save_end = mTerm.row;
if ( mTerm.row != 0 && mTerm.col != 0 ) {
if ( !is_alt ) {
tclearregion( mTerm.c.x, mTerm.c.y, mTerm.col - 1, mTerm.c.y );
tclearregion( mTerm.c.x, mTerm.c.y, mTerm.col - 1, mTerm.c.y, true );
}
if ( !is_alt && mTerm.c.y > 0 && mTerm.c.y < mTerm.row )
@@ -2715,10 +2772,23 @@ void TerminalEmulator::tresize( int col, int row ) {
save_end = i + 1;
if ( !is_alt && save_end < mTerm.c.y + 1 )
save_end = mTerm.c.y + 1;
if ( has_sel )
save_end = mTerm.row;
for ( i = 0; i < save_end; i++ )
historyPush( mTerm.line[i], mTerm.col );
if ( has_sel && old_histlen + save_end > mTerm.histsize ) {
int dropped = ( old_histlen + save_end ) - mTerm.histsize;
if ( eemax( mSel.ob.y, mSel.oe.y ) < dropped ) {
selclear();
has_sel = false;
} else {
mSel.ob.y = eemax( 0, mSel.ob.y - dropped );
mSel.oe.y = eemax( 0, mSel.oe.y - dropped );
}
}
bool needs_reflow = false;
if ( col > mTerm.col ) {
needs_reflow = mTerm.max_width >= mTerm.col;
@@ -2742,6 +2812,12 @@ void TerminalEmulator::tresize( int col, int row ) {
}
}
}
if ( has_sel ) {
if ( mSel.ob.x >= col )
mSel.ob.x = col - 1;
if ( mSel.oe.x >= col )
mSel.oe.x = col - 1;
}
}
}
@@ -2795,6 +2871,17 @@ void TerminalEmulator::tresize( int col, int row ) {
LIMIT( mTerm.c.y, 0, mTerm.row - 1 );
LIMIT( mTerm.c.x, 0, mTerm.col - 1 );
if ( has_sel ) {
mSel.ob.y = mSel.ob.y - mTerm.histlen + mTerm.scr;
mSel.oe.y = mSel.oe.y - mTerm.histlen + mTerm.scr;
// Clamp to the full available range (history + screen)
mSel.ob.y =
eemax( mTerm.scr - mTerm.histlen, eemin( mTerm.scr + mTerm.row - 1, mSel.ob.y ) );
mSel.oe.y =
eemax( mTerm.scr - mTerm.histlen, eemin( mTerm.scr + mTerm.row - 1, mSel.oe.y ) );
selnormalize();
}
mDirty = true;
onScrollPositionChange();
}