@@ -45,6 +45,9 @@ export class DomRenderer extends Disposable implements IRenderer {
4545 private _selectionContainer : HTMLElement ;
4646 private _widthCache : WidthCache ;
4747 private _selectionRenderModel : ISelectionRenderModel = createSelectionRenderModel ( ) ;
48+ private _lastSelectionStart : [ number , number ] | undefined ;
49+ private _lastSelectionEnd : [ number , number ] | undefined ;
50+ private _lastSelectionColumnMode : boolean = false ;
4851 private _cursorBlinkStateManager : CursorBlinkStateManager ;
4952 private _textBlinkStateManager : TextBlinkStateManager ;
5053 private _rowHasBlinkingCells : boolean [ ] = [ ] ;
@@ -374,51 +377,89 @@ export class DomRenderer extends Disposable implements IRenderer {
374377 }
375378
376379 public handleSelectionChanged ( start : [ number , number ] | undefined , end : [ number , number ] | undefined , columnSelectMode : boolean ) : void {
380+ const rows = this . _bufferService . rows ;
381+
377382 // Remove all selections
378383 this . _selectionContainer . replaceChildren ( ) ;
379384 this . _rowFactory . handleSelectionChanged ( start , end , columnSelectMode ) ;
380- this . renderRows ( 0 , this . _bufferService . rows - 1 ) ;
381385
382- // Selection does not exist
386+ // Determine old selection viewport band
387+ let oldViewportStart = 0 ;
388+ let oldViewportEnd = - 1 ;
389+ if ( this . _lastSelectionStart && this . _lastSelectionEnd ) {
390+ this . _selectionRenderModel . update ( this . _terminal , this . _lastSelectionStart , this . _lastSelectionEnd , this . _lastSelectionColumnMode ) ;
391+ if ( this . _selectionRenderModel . hasSelection ) {
392+ oldViewportStart = this . _selectionRenderModel . viewportCappedStartRow ;
393+ oldViewportEnd = this . _selectionRenderModel . viewportCappedEndRow ;
394+ }
395+ }
396+
397+ // Determine new selection viewport band and create overlays
398+ let newViewportStart = 0 ;
399+ let newViewportEnd = - 1 ;
383400 if ( ! start || ! end ) {
384401 return ;
385402 }
386-
387403 this . _selectionRenderModel . update ( this . _terminal , start , end , columnSelectMode ) ;
388- if ( ! this . _selectionRenderModel . hasSelection ) {
389- return ;
404+ if ( this . _selectionRenderModel . hasSelection ) {
405+ const viewportStartRow = this . _selectionRenderModel . viewportStartRow ;
406+ const viewportEndRow = this . _selectionRenderModel . viewportEndRow ;
407+ const viewportCappedStartRow = this . _selectionRenderModel . viewportCappedStartRow ;
408+ const viewportCappedEndRow = this . _selectionRenderModel . viewportCappedEndRow ;
409+
410+ newViewportStart = viewportCappedStartRow ;
411+ newViewportEnd = viewportCappedEndRow ;
412+
413+ // Create the selections
414+ const documentFragment = this . _document . createDocumentFragment ( ) ;
415+
416+ if ( columnSelectMode ) {
417+ const isXFlipped = start [ 0 ] > end [ 0 ] ;
418+ documentFragment . appendChild (
419+ this . _createSelectionElement ( viewportCappedStartRow , isXFlipped ? end [ 0 ] : start [ 0 ] , isXFlipped ? start [ 0 ] : end [ 0 ] , viewportCappedEndRow - viewportCappedStartRow + 1 )
420+ ) ;
421+ } else {
422+ // Draw first row
423+ const startCol = viewportStartRow === viewportCappedStartRow ? start [ 0 ] : 0 ;
424+ const endCol = viewportCappedStartRow === viewportEndRow ? end [ 0 ] : this . _bufferService . cols ;
425+ documentFragment . appendChild ( this . _createSelectionElement ( viewportCappedStartRow , startCol , endCol ) ) ;
426+ // Draw middle rows
427+ const middleRowsCount = viewportCappedEndRow - viewportCappedStartRow - 1 ;
428+ documentFragment . appendChild ( this . _createSelectionElement ( viewportCappedStartRow + 1 , 0 , this . _bufferService . cols , middleRowsCount ) ) ;
429+ // Draw final row
430+ if ( viewportCappedStartRow !== viewportCappedEndRow ) {
431+ // Only draw viewportEndRow if it's not the same as viewporttartRow
432+ const finalEndCol = viewportEndRow === viewportCappedEndRow ? end [ 0 ] : this . _bufferService . cols ;
433+ documentFragment . appendChild ( this . _createSelectionElement ( viewportCappedEndRow , 0 , finalEndCol ) ) ;
434+ }
435+ }
436+ this . _selectionContainer . appendChild ( documentFragment ) ;
390437 }
391438
392- // Translate from buffer position to viewport position
393- const viewportStartRow = this . _selectionRenderModel . viewportStartRow ;
394- const viewportEndRow = this . _selectionRenderModel . viewportEndRow ;
395- const viewportCappedStartRow = this . _selectionRenderModel . viewportCappedStartRow ;
396- const viewportCappedEndRow = this . _selectionRenderModel . viewportCappedEndRow ;
397-
398- // Create the selections
399- const documentFragment = this . _document . createDocumentFragment ( ) ;
400-
401- if ( columnSelectMode ) {
402- const isXFlipped = start [ 0 ] > end [ 0 ] ;
403- documentFragment . appendChild (
404- this . _createSelectionElement ( viewportCappedStartRow , isXFlipped ? end [ 0 ] : start [ 0 ] , isXFlipped ? start [ 0 ] : end [ 0 ] , viewportCappedEndRow - viewportCappedStartRow + 1 )
405- ) ;
406- } else {
407- // Draw first row
408- const startCol = viewportStartRow === viewportCappedStartRow ? start [ 0 ] : 0 ;
409- const endCol = viewportCappedStartRow === viewportEndRow ? end [ 0 ] : this . _bufferService . cols ;
410- documentFragment . appendChild ( this . _createSelectionElement ( viewportCappedStartRow , startCol , endCol ) ) ;
411- // Draw middle rows
412- const middleRowsCount = viewportCappedEndRow - viewportCappedStartRow - 1 ;
413- documentFragment . appendChild ( this . _createSelectionElement ( viewportCappedStartRow + 1 , 0 , this . _bufferService . cols , middleRowsCount ) ) ;
414- // Draw final row
415- if ( viewportCappedStartRow !== viewportCappedEndRow ) {
416- // Only draw viewportEndRow if it's not the same as viewporttartRow
417- const endCol = viewportEndRow === viewportCappedEndRow ? end [ 0 ] : this . _bufferService . cols ;
418- documentFragment . appendChild ( this . _createSelectionElement ( viewportCappedEndRow , 0 , endCol ) ) ;
439+ // Compute minimal row range to redraw
440+ let renderStartRow = Math . min ( oldViewportStart , newViewportStart ) ;
441+ let renderEndRow = Math . max ( oldViewportEnd , newViewportEnd ) ;
442+
443+ if ( renderEndRow >= 0 ) {
444+ // Clamp to viewport
445+ renderStartRow = Math . max ( renderStartRow , 0 ) ;
446+ renderEndRow = Math . min ( renderEndRow , rows - 1 ) ;
447+
448+ // Ensure cursor row is included when a selection is present
449+ const buffer = this . _bufferService . buffer ;
450+ const cursorViewportRow = buffer . y ;
451+ if ( this . _selectionRenderModel . hasSelection && cursorViewportRow >= 0 && cursorViewportRow < rows ) {
452+ renderStartRow = Math . min ( renderStartRow , cursorViewportRow ) ;
453+ renderEndRow = Math . max ( renderEndRow , cursorViewportRow ) ;
419454 }
455+
456+ this . renderRows ( renderStartRow , renderEndRow ) ;
420457 }
421- this . _selectionContainer . appendChild ( documentFragment ) ;
458+
459+ // Update last selection state
460+ this . _lastSelectionStart = start ;
461+ this . _lastSelectionEnd = end ;
462+ this . _lastSelectionColumnMode = columnSelectMode ;
422463 }
423464
424465 /**
0 commit comments