@@ -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,44 @@ 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+ if ( ! isValidJoinRange ) {
131+ skipJoinedCheckUntilX = range [ 1 ] ;
132+ } else {
133+ isJoined = true ;
134+
135+ // We already know the exact start and end column of the joined range,
136+ // so we get the string and width representing it directly
137+ cell = new JoinedCellData (
138+ this . _workCell ,
139+ lineData . translateToString ( true , range [ 0 ] , range [ 1 ] ) ,
140+ range [ 1 ] - range [ 0 ]
141+ ) ;
118142
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 ;
143+ // Skip over the cells occupied by this range in the loop
144+ lastCharX = range [ 1 ] - 1 ;
129145
130- // Recalculate width
131- width = cell . getWidth ( ) ;
146+ // Recalculate width
147+ width = cell . getWidth ( ) ;
148+ }
132149 }
133150
134151 const isInSelection = this . _isCellInSelection ( x , row ) ;
@@ -178,6 +195,7 @@ export class DomRendererRowFactory {
178195 && ! isCursorCell
179196 && ! isJoined
180197 && ! isDecorated
198+ && isValidJoinRange
181199 ) {
182200 // no span alterations, thus only account chars skipping all code below
183201 if ( cell . isInvisible ( ) ) {
@@ -435,7 +453,7 @@ export class DomRendererRowFactory {
435453 }
436454
437455 // exclude conditions for cell merging - never merge these
438- if ( ! isCursorCell && ! isJoined && ! isDecorated ) {
456+ if ( ! isCursorCell && ! isJoined && ! isDecorated && isValidJoinRange ) {
439457 cellAmount ++ ;
440458 } else {
441459 charElement . textContent = text ;
0 commit comments