@@ -84,13 +84,15 @@ export class DomRendererRowFactory {
8484 let charElement : HTMLSpanElement | undefined ;
8585 let cellAmount = 0 ;
8686 let text = '' ;
87+ let i = 0 ;
8788 let oldBg = 0 ;
8889 let oldFg = 0 ;
8990 let oldExt = 0 ;
9091 let oldLinkHover : number | boolean = false ;
9192 let oldSpacing = 0 ;
9293 let oldIsInSelection : boolean = false ;
9394 let spacing = 0 ;
95+ let skipJoinedCheckUntilX = 0 ;
9496 const classes : string [ ] = [ ] ;
9597
9698 const hasHover = linkStart !== - 1 && linkEnd !== - 1 ;
@@ -106,29 +108,46 @@ export class DomRendererRowFactory {
106108
107109 // If true, indicates that the current character(s) to draw were joined.
108110 let isJoined = false ;
111+
112+ // Indicates whether this cell is part of a joined range that should be ignored as it cannot
113+ // be rendered entirely, like the selection state differs across the range.
114+ let isValidJoinRange = ( x >= skipJoinedCheckUntilX ) ;
115+
109116 let lastCharX = x ;
110117
111118 // Process any joined character ranges as needed. Because of how the
112119 // ranges are produced, we know that they are valid for the characters
113120 // and attributes of our input.
114121 let cell = this . _workCell ;
115- if ( joinedRanges . length > 0 && x === joinedRanges [ 0 ] [ 0 ] ) {
116- isJoined = true ;
122+ if ( joinedRanges . length > 0 && x === joinedRanges [ 0 ] [ 0 ] && isValidJoinRange ) {
117123 const range = joinedRanges . shift ( ) ! ;
124+ // If the ligature's selection state is not consistent, don't join it. This helps the
125+ // selection render correctly regardless whether they should be joined.
126+ const firstSelectionState = this . _isCellInSelection ( range [ 0 ] , row ) ;
127+ for ( i = range [ 0 ] + 1 ; i < range [ 1 ] ; i ++ ) {
128+ isValidJoinRange &&= ( firstSelectionState === this . _isCellInSelection ( i , row ) ) ;
129+ }
130+ // Similarly, if the cursor is in the ligature, don't join it.
131+ isValidJoinRange &&= ! isCursorRow || cursorX < range [ 0 ] || cursorX >= range [ 1 ] ;
132+ if ( ! isValidJoinRange ) {
133+ skipJoinedCheckUntilX = range [ 1 ] ;
134+ } else {
135+ isJoined = true ;
136+
137+ // We already know the exact start and end column of the joined range,
138+ // so we get the string and width representing it directly
139+ cell = new JoinedCellData (
140+ this . _workCell ,
141+ lineData . translateToString ( true , range [ 0 ] , range [ 1 ] ) ,
142+ range [ 1 ] - range [ 0 ]
143+ ) ;
118144
119- // We already know the exact start and end column of the joined range,
120- // so we get the string and width representing it directly
121- cell = new JoinedCellData (
122- this . _workCell ,
123- lineData . translateToString ( true , range [ 0 ] , range [ 1 ] ) ,
124- range [ 1 ] - range [ 0 ]
125- ) ;
126-
127- // Skip over the cells occupied by this range in the loop
128- lastCharX = range [ 1 ] - 1 ;
145+ // Skip over the cells occupied by this range in the loop
146+ lastCharX = range [ 1 ] - 1 ;
129147
130- // Recalculate width
131- width = cell . getWidth ( ) ;
148+ // Recalculate width
149+ width = cell . getWidth ( ) ;
150+ }
132151 }
133152
134153 const isInSelection = this . _isCellInSelection ( x , row ) ;
@@ -178,6 +197,7 @@ export class DomRendererRowFactory {
178197 && ! isCursorCell
179198 && ! isJoined
180199 && ! isDecorated
200+ && isValidJoinRange
181201 ) {
182202 // no span alterations, thus only account chars skipping all code below
183203 if ( cell . isInvisible ( ) ) {
@@ -435,7 +455,7 @@ export class DomRendererRowFactory {
435455 }
436456
437457 // exclude conditions for cell merging - never merge these
438- if ( ! isCursorCell && ! isJoined && ! isDecorated ) {
458+ if ( ! isCursorCell && ! isJoined && ! isDecorated && isValidJoinRange ) {
439459 cellAmount ++ ;
440460 } else {
441461 charElement . textContent = text ;
0 commit comments