Skip to content

Commit 9ff8dda

Browse files
committed
Fix regression where adjacent client rects would not be merged
1 parent 61d1436 commit 9ff8dda

2 files changed

Lines changed: 110 additions & 121 deletions

File tree

src/vs/editor/browser/viewParts/lines/viewLine.ts

Lines changed: 95 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -196,10 +196,10 @@ export class ViewLine implements IVisibleLineData {
196196
* Visible ranges for a model range
197197
*/
198198
public getVisibleRangesForRange(startColumn:number, endColumn:number, clientRectDeltaLeft:number, endNode:HTMLElement): HorizontalRange[] {
199-
startColumn = +startColumn; // @perf
200-
endColumn = +endColumn; // @perf
201-
clientRectDeltaLeft = +clientRectDeltaLeft; // @perf
202-
let stopRenderingLineAfter = +this._stopRenderingLineAfter; // @perf
199+
startColumn = startColumn|0; // @perf
200+
endColumn = endColumn|0; // @perf
201+
clientRectDeltaLeft = clientRectDeltaLeft|0; // @perf
202+
const stopRenderingLineAfter = this._stopRenderingLineAfter|0; // @perf
203203

204204
if (stopRenderingLineAfter !== -1 && startColumn > stopRenderingLineAfter && endColumn > stopRenderingLineAfter) {
205205
// This range is obviously not visible
@@ -218,36 +218,11 @@ export class ViewLine implements IVisibleLineData {
218218
}
219219

220220
protected _readVisibleRangesForRange(startColumn:number, endColumn:number, clientRectDeltaLeft:number, endNode:HTMLElement): HorizontalRange[] {
221-
222-
let result: HorizontalRange[];
223221
if (startColumn === endColumn) {
224-
result = this._readRawVisibleRangesForPosition(startColumn, clientRectDeltaLeft, endNode);
222+
return this._readRawVisibleRangesForPosition(startColumn, clientRectDeltaLeft, endNode);
225223
} else {
226-
result = this._readRawVisibleRangesForRange(startColumn, endColumn, clientRectDeltaLeft, endNode);
227-
}
228-
229-
if (!result || result.length <= 1) {
230-
return result;
224+
return this._readRawVisibleRangesForRange(startColumn, endColumn, clientRectDeltaLeft, endNode);
231225
}
232-
233-
result.sort(compareVisibleRanges);
234-
235-
let output: HorizontalRange[] = [];
236-
let prevRange: HorizontalRange = result[0];
237-
238-
for (let i = 1, len = result.length; i < len; i++) {
239-
let currRange = result[i];
240-
241-
if (prevRange.left + prevRange.width + 0.9 /* account for browser's rounding errors*/ >= currRange.left) {
242-
prevRange.width = Math.max(prevRange.width, currRange.left + currRange.width - prevRange.left);
243-
} else {
244-
output.push(prevRange);
245-
prevRange = currRange;
246-
}
247-
}
248-
output.push(prevRange);
249-
250-
return output;
251226
}
252227

253228
protected _readRawVisibleRangesForPosition(column:number, clientRectDeltaLeft:number, endNode:HTMLElement): HorizontalRange[] {
@@ -260,7 +235,7 @@ export class ViewLine implements IVisibleLineData {
260235
let partIndex = findIndexInArrayWithMax(this._lineParts, column - 1, this._lastRenderedPartIndex);
261236
let charOffsetInPart = this._charOffsetInPart[column - 1];
262237

263-
return this._readRawVisibleRangesFrom(this._getReadingTarget(), partIndex, charOffsetInPart, partIndex, charOffsetInPart, clientRectDeltaLeft, endNode);
238+
return RangeUtil.readHorizontalRanges(this._getReadingTarget(), partIndex, charOffsetInPart, partIndex, charOffsetInPart, clientRectDeltaLeft, this._getScaleRatio(), endNode);
264239
}
265240

266241
private _readRawVisibleRangesForRange(startColumn:number, endColumn:number, clientRectDeltaLeft:number, endNode:HTMLElement): HorizontalRange[] {
@@ -276,70 +251,15 @@ export class ViewLine implements IVisibleLineData {
276251
let endPartIndex = findIndexInArrayWithMax(this._lineParts, endColumn - 1, this._lastRenderedPartIndex);
277252
let endCharOffsetInPart = this._charOffsetInPart[endColumn - 1];
278253

279-
return this._readRawVisibleRangesFrom(this._getReadingTarget(), startPartIndex, startCharOffsetInPart, endPartIndex, endCharOffsetInPart, clientRectDeltaLeft, endNode);
254+
return RangeUtil.readHorizontalRanges(this._getReadingTarget(), startPartIndex, startCharOffsetInPart, endPartIndex, endCharOffsetInPart, clientRectDeltaLeft, this._getScaleRatio(), endNode);
280255
}
281256

282257
private _readRawVisibleRangeForEntireLine(): HorizontalRange {
283258
return new HorizontalRange(0, this._getReadingTarget().offsetWidth);
284259
}
285260

286-
private _readRawVisibleRangesFrom(domNode:HTMLElement, startChildIndex:number, startOffset:number, endChildIndex:number, endOffset:number, clientRectDeltaLeft:number, endNode:HTMLElement): HorizontalRange[] {
287-
let range = RangeUtil.createRange();
288-
289-
try {
290-
// Panic check
291-
let min = 0;
292-
let max = domNode.children.length - 1;
293-
if (min > max) {
294-
return null;
295-
}
296-
startChildIndex = Math.min(max, Math.max(min, startChildIndex));
297-
endChildIndex = Math.min(max, Math.max(min, endChildIndex));
298-
299-
// If crossing over to a span only to select offset 0, then use the previous span's maximum offset
300-
// Chrome is buggy and doesn't handle 0 offsets well sometimes.
301-
if (startChildIndex !== endChildIndex) {
302-
if (endChildIndex > 0 && endOffset === 0) {
303-
endChildIndex--;
304-
endOffset = Number.MAX_VALUE;
305-
}
306-
}
307-
308-
let startElement = domNode.children[startChildIndex].firstChild;
309-
let endElement = domNode.children[endChildIndex].firstChild;
310-
311-
if (!startElement || !endElement) {
312-
return null;
313-
}
314-
315-
startOffset = Math.min(startElement.textContent.length, Math.max(0, startOffset));
316-
endOffset = Math.min(endElement.textContent.length, Math.max(0, endOffset));
317-
318-
range.setStart(startElement, startOffset);
319-
range.setEnd(endElement, endOffset);
320-
321-
let clientRects = range.getClientRects();
322-
if (clientRects.length === 0) {
323-
return null;
324-
}
325-
326-
return this._createRawVisibleRangesFromClientRects(clientRects, clientRectDeltaLeft);
327-
328-
} catch (e) {
329-
// This is life ...
330-
return null;
331-
} finally {
332-
RangeUtil.detachRange(range, endNode);
333-
}
334-
}
335-
336-
protected _createRawVisibleRangesFromClientRects(clientRects:ClientRectList, clientRectDeltaLeft:number): HorizontalRange[] {
337-
let result:HorizontalRange[] = [];
338-
for (let i = 0, len = clientRects.length; i < len; i++) {
339-
let cR = clientRects[i];
340-
result.push(new HorizontalRange(Math.max(0, cR.left - clientRectDeltaLeft), cR.width));
341-
}
342-
return result;
261+
protected _getScaleRatio(): number {
262+
return 1;
343263
}
344264

345265
/**
@@ -437,15 +357,8 @@ class IEViewLine extends ViewLine {
437357
super(context);
438358
}
439359

440-
protected _createRawVisibleRangesFromClientRects(clientRects:ClientRectList, clientRectDeltaLeft:number): HorizontalRange[] {
441-
let ratioX = screen.logicalXDPI / screen.deviceXDPI;
442-
let result:HorizontalRange[] = [];
443-
for (let i = 0, len = clientRects.length; i < len; i++) {
444-
let cR = clientRects[i];
445-
result[i] = new HorizontalRange(Math.max(0, cR.left * ratioX - clientRectDeltaLeft), cR.width * ratioX);
446-
}
447-
448-
return result;
360+
protected _getScaleRatio(): number {
361+
return screen.logicalXDPI / screen.deviceXDPI;
449362
}
450363
}
451364

@@ -508,22 +421,97 @@ class RangeUtil {
508421
*/
509422
private static _handyReadyRange:Range;
510423

511-
public static createRange(): Range {
512-
if (!RangeUtil._handyReadyRange) {
513-
RangeUtil._handyReadyRange = document.createRange();
424+
private static _createRange(): Range {
425+
if (!this._handyReadyRange) {
426+
this._handyReadyRange = document.createRange();
514427
}
515-
return RangeUtil._handyReadyRange;
428+
return this._handyReadyRange;
516429
}
517430

518-
public static detachRange(range:Range, endNode:HTMLElement): void {
431+
private static _detachRange(range:Range, endNode:HTMLElement): void {
519432
// Move range out of the span node, IE doesn't like having many ranges in
520433
// the same spot and will act badly for lines containing dashes ('-')
521434
range.selectNodeContents(endNode);
522435
}
523-
}
524436

525-
function compareVisibleRanges(a: HorizontalRange, b: HorizontalRange): number {
526-
return a.left - b.left;
437+
private static _readClientRects(startElement:Node, startOffset:number, endElement:Node, endOffset:number, endNode:HTMLElement): ClientRectList {
438+
let range = this._createRange();
439+
try {
440+
range.setStart(startElement, startOffset);
441+
range.setEnd(endElement, endOffset);
442+
443+
return range.getClientRects();
444+
} catch (e) {
445+
// This is life ...
446+
return null;
447+
} finally {
448+
this._detachRange(range, endNode);
449+
}
450+
}
451+
452+
private static _createHorizontalRangesFromClientRects(clientRects:ClientRectList, clientRectDeltaLeft:number, scaleRatio:number): HorizontalRange[] {
453+
if (!clientRects || clientRects.length === 0) {
454+
return null;
455+
}
456+
457+
let result:HorizontalRange[] = [];
458+
let prevLeft = Math.max(0, clientRects[0].left * scaleRatio - clientRectDeltaLeft);
459+
let prevWidth = clientRects[0].width * scaleRatio;
460+
461+
for (let i = 1, len = clientRects.length; i < len; i++) {
462+
let myLeft = Math.max(0, clientRects[i].left * scaleRatio - clientRectDeltaLeft);
463+
let myWidth = clientRects[i].width * scaleRatio;
464+
465+
if (myLeft < prevLeft) {
466+
console.error('Unexpected: RangeUtil._createHorizontalRangesFromClientRects: client rects are not sorted');
467+
}
468+
469+
if (prevLeft + prevWidth + 0.9 /* account for browser's rounding errors*/ >= myLeft) {
470+
prevWidth = Math.max(prevWidth, myLeft + myWidth - prevLeft);
471+
} else {
472+
result.push(new HorizontalRange(prevLeft, prevWidth));
473+
prevLeft = myLeft;
474+
prevWidth = myWidth;
475+
}
476+
}
477+
478+
result.push(new HorizontalRange(prevLeft, prevWidth));
479+
480+
return result;
481+
}
482+
483+
public static readHorizontalRanges(domNode:HTMLElement, startChildIndex:number, startOffset:number, endChildIndex:number, endOffset:number, clientRectDeltaLeft:number, scaleRatio:number, endNode:HTMLElement): HorizontalRange[] {
484+
// Panic check
485+
let min = 0;
486+
let max = domNode.children.length - 1;
487+
if (min > max) {
488+
return null;
489+
}
490+
startChildIndex = Math.min(max, Math.max(min, startChildIndex));
491+
endChildIndex = Math.min(max, Math.max(min, endChildIndex));
492+
493+
// If crossing over to a span only to select offset 0, then use the previous span's maximum offset
494+
// Chrome is buggy and doesn't handle 0 offsets well sometimes.
495+
if (startChildIndex !== endChildIndex) {
496+
if (endChildIndex > 0 && endOffset === 0) {
497+
endChildIndex--;
498+
endOffset = Number.MAX_VALUE;
499+
}
500+
}
501+
502+
let startElement = domNode.children[startChildIndex].firstChild;
503+
let endElement = domNode.children[endChildIndex].firstChild;
504+
505+
if (!startElement || !endElement) {
506+
return null;
507+
}
508+
509+
startOffset = Math.min(startElement.textContent.length, Math.max(0, startOffset));
510+
endOffset = Math.min(endElement.textContent.length, Math.max(0, endOffset));
511+
512+
let clientRects = this._readClientRects(startElement, startOffset, endElement, endOffset, endNode);
513+
return this._createHorizontalRangesFromClientRects(clientRects, clientRectDeltaLeft, scaleRatio);
514+
}
527515
}
528516

529517
function findIndexInArrayWithMax(lineParts:ILineParts, desiredIndex: number, maxResult:number): number {

src/vs/editor/common/model/modelLine.ts

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -552,11 +552,17 @@ export class ModelLine {
552552
}
553553

554554
public removeMarker(marker:ILineMarker): void {
555-
var index = this._indexOfMarkerId(marker.id);
555+
if (!this._markers) {
556+
return;
557+
}
558+
let index = this._indexOfMarkerId(marker.id);
556559
if (index >= 0) {
560+
marker.line = null;
557561
this._markers.splice(index, 1);
558562
}
559-
marker.line = null;
563+
if (this._markers.length === 0) {
564+
this._markers = null;
565+
}
560566
}
561567

562568
public removeMarkers(deleteMarkers: {[markerId:string]:boolean;}): void {
@@ -573,6 +579,9 @@ export class ModelLine {
573579
i--;
574580
}
575581
}
582+
if (this._markers.length === 0) {
583+
this._markers = null;
584+
}
576585
}
577586

578587
public getMarkers(): ILineMarker[] {
@@ -624,20 +633,12 @@ export class ModelLine {
624633
}
625634

626635
private _indexOfMarkerId(markerId:string): number {
627-
628-
if (this._markers) {
629-
var markers = this._markers,
630-
i: number,
631-
len: number;
632-
633-
for (i = 0, len = markers.length; i < len; i++) {
634-
if (markers[i].id === markerId) {
635-
return i;
636-
}
636+
let markers = this._markers;
637+
for (let i = 0, len = markers.length; i < len; i++) {
638+
if (markers[i].id === markerId) {
639+
return i;
637640
}
638641
}
639-
640-
return -1;
641642
}
642643
}
643644

0 commit comments

Comments
 (0)