viewLine.ts 17.8 KB
Newer Older
E
Erich Gamma 已提交
1 2 3 4 5 6
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
'use strict';

A
Alex Dima 已提交
7
import * as browser from 'vs/base/browser/browser';
A
Alex Dima 已提交
8
import {FastDomNode, createFastDomNode} from 'vs/base/browser/styleMutator';
A
Alex Dima 已提交
9
import {HorizontalRange, IConfigurationChangedEvent, IModelDecoration} from 'vs/editor/common/editorCommon';
E
Erich Gamma 已提交
10
import {ILineParts, createLineParts} from 'vs/editor/common/viewLayout/viewLineParts';
A
Alex Dima 已提交
11
import {renderLine, RenderLineInput} from 'vs/editor/common/viewLayout/viewLineRenderer';
A
Alex Dima 已提交
12 13
import {ClassNames, IViewContext} from 'vs/editor/browser/editorBrowser';
import {IVisibleLineData} from 'vs/editor/browser/view/viewLayer';
E
Erich Gamma 已提交
14

A
Alex Dima 已提交
15
export class ViewLine implements IVisibleLineData {
E
Erich Gamma 已提交
16

A
Alex Dima 已提交
17
	protected _context:IViewContext;
A
Alex Dima 已提交
18 19 20 21 22
	private _renderWhitespace: boolean;
	private _lineHeight: number;
	private _stopRenderingLineAfter: number;
	protected _fontLigatures: boolean;

A
Alex Dima 已提交
23
	private _domNode: FastDomNode;
E
Erich Gamma 已提交
24 25 26 27 28 29

	private _lineParts: ILineParts;

	private _isInvalid: boolean;
	private _isMaybeInvalid: boolean;

A
Alex Dima 已提交
30
	protected _charOffsetInPart:number[];
E
Erich Gamma 已提交
31 32 33
	private _lastRenderedPartIndex:number;
	private _cachedWidth: number;

A
Alex Dima 已提交
34
	constructor(context:IViewContext) {
E
Erich Gamma 已提交
35
		this._context = context;
A
Alex Dima 已提交
36 37 38 39 40
		this._renderWhitespace = this._context.configuration.editor.renderWhitespace;
		this._lineHeight = this._context.configuration.editor.lineHeight;
		this._stopRenderingLineAfter = this._context.configuration.editor.stopRenderingLineAfter;
		this._fontLigatures = this._context.configuration.editor.fontLigatures;

E
Erich Gamma 已提交
41 42 43 44 45 46 47 48
		this._domNode = null;
		this._isInvalid = true;
		this._isMaybeInvalid = false;
		this._lineParts = null;
		this._charOffsetInPart = [];
		this._lastRenderedPartIndex = 0;
	}

A
Alex Dima 已提交
49 50
	// --- begin IVisibleLineData

E
Erich Gamma 已提交
51
	public getDomNode(): HTMLElement {
A
Alex Dima 已提交
52 53 54 55
		if (!this._domNode) {
			return null;
		}
		return this._domNode.domNode;
E
Erich Gamma 已提交
56 57
	}
	public setDomNode(domNode:HTMLElement): void {
A
Alex Dima 已提交
58
		this._domNode = createFastDomNode(domNode);
E
Erich Gamma 已提交
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
	}

	public onContentChanged(): void {
		this._isInvalid = true;
	}
	public onLinesInsertedAbove(): void {
		this._isMaybeInvalid = true;
	}
	public onLinesDeletedAbove(): void {
		this._isMaybeInvalid = true;
	}
	public onLineChangedAbove(): void {
		this._isMaybeInvalid = true;
	}
	public onTokensChanged(): void {
		this._isMaybeInvalid = true;
	}
	public onModelDecorationsChanged(): void {
		this._isMaybeInvalid = true;
	}
A
Alex Dima 已提交
79
	public onConfigurationChanged(e:IConfigurationChangedEvent): void {
A
Alex Dima 已提交
80 81 82 83 84 85 86 87 88 89 90 91
		if (e.renderWhitespace) {
			this._renderWhitespace = this._context.configuration.editor.renderWhitespace;
		}
		if (e.lineHeight) {
			this._lineHeight = this._context.configuration.editor.lineHeight;
		}
		if (e.stopRenderingLineAfter) {
			this._stopRenderingLineAfter = this._context.configuration.editor.stopRenderingLineAfter;
		}
		if (e.fontLigatures) {
			this._fontLigatures = this._context.configuration.editor.fontLigatures;
		}
E
Erich Gamma 已提交
92 93 94
		this._isInvalid = true;
	}

A
Alex Dima 已提交
95
	public shouldUpdateHTML(startLineNumber:number, lineNumber:number, inlineDecorations:IModelDecoration[]): boolean {
A
Alex Dima 已提交
96
		let newLineParts:ILineParts = null;
E
Erich Gamma 已提交
97 98 99

		if (this._isMaybeInvalid || this._isInvalid) {
			// Compute new line parts only if there is some evidence that something might have changed
A
Alex Dima 已提交
100 101
			newLineParts = createLineParts(
				lineNumber,
102
				this._context.model.getLineMinColumn(lineNumber),
A
Alex Dima 已提交
103 104 105
				this._context.model.getLineContent(lineNumber),
				this._context.model.getLineTokens(lineNumber),
				inlineDecorations,
A
Alex Dima 已提交
106
				this._renderWhitespace
A
Alex Dima 已提交
107
			);
E
Erich Gamma 已提交
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
		}

		// Decide if isMaybeInvalid flips isInvalid to true
		if (this._isMaybeInvalid) {
			if (!this._isInvalid) {
				if (!this._lineParts || !this._lineParts.equals(newLineParts)) {
					this._isInvalid = true;
				}
			}
			this._isMaybeInvalid = false;
		}

		if (this._isInvalid) {
			this._lineParts = newLineParts;
		}

		return this._isInvalid;
	}

	public getLineOuterHTML(out:string[], lineNumber:number, deltaTop:number): void {
		out.push('<div lineNumber="');
		out.push(lineNumber.toString());
		out.push('" style="top:');
		out.push(deltaTop.toString());
		out.push('px;height:');
A
Alex Dima 已提交
133
		out.push(this._lineHeight.toString());
E
Erich Gamma 已提交
134
		out.push('px;" class="');
A
Alex Dima 已提交
135
		out.push(ClassNames.VIEW_LINE);
E
Erich Gamma 已提交
136 137 138 139 140 141 142
		out.push('">');
		out.push(this.getLineInnerHTML(lineNumber));
		out.push('</div>');
	}

	public getLineInnerHTML(lineNumber: number): string {
		this._isInvalid = false;
A
Alex Dima 已提交
143
		return this._render(lineNumber, this._lineParts);
E
Erich Gamma 已提交
144 145 146
	}

	public layoutLine(lineNumber:number, deltaTop:number): void {
A
Alex Dima 已提交
147 148
		this._domNode.setLineNumber(String(lineNumber));
		this._domNode.setTop(deltaTop);
A
Alex Dima 已提交
149
		this._domNode.setHeight(this._lineHeight);
E
Erich Gamma 已提交
150 151
	}

A
Alex Dima 已提交
152 153
	// --- end IVisibleLineData

A
Alex Dima 已提交
154
	private _render(lineNumber:number, lineParts:ILineParts): string {
E
Erich Gamma 已提交
155

A
Alex Dima 已提交
156
		this._cachedWidth = -1;
E
Erich Gamma 已提交
157

A
Alex Dima 已提交
158 159 160
		let r = renderLine(new RenderLineInput(
			this._context.model.getLineContent(lineNumber),
			this._context.model.getTabSize(),
A
Alex Dima 已提交
161 162
			this._stopRenderingLineAfter,
			this._renderWhitespace,
A
Alex Dima 已提交
163 164
			lineParts.getParts()
		));
E
Erich Gamma 已提交
165 166 167 168 169 170 171 172 173

		this._charOffsetInPart = r.charOffsetInPart;
		this._lastRenderedPartIndex = r.lastRenderedPartIndex;

		return r.output;
	}

	// --- Reading from the DOM methods

174
	protected _getReadingTarget(): HTMLElement {
A
Alex Dima 已提交
175
		return <HTMLSpanElement>this._domNode.domNode.firstChild;
E
Erich Gamma 已提交
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
	}

	/**
	 * Width of the line in pixels
	 */
	public getWidth(): number {
		if (this._cachedWidth === -1) {
			this._cachedWidth = this._getReadingTarget().offsetWidth;
		}
		return this._cachedWidth;
	}

	/**
	 * Visible ranges for a model range
	 */
191
	public getVisibleRangesForRange(startColumn:number, endColumn:number, clientRectDeltaLeft:number, endNode:HTMLElement): HorizontalRange[] {
A
Alex Dima 已提交
192 193 194
		startColumn = +startColumn; // @perf
		endColumn = +endColumn; // @perf
		clientRectDeltaLeft = +clientRectDeltaLeft; // @perf
A
Alex Dima 已提交
195
		let stopRenderingLineAfter = +this._stopRenderingLineAfter; // @perf
E
Erich Gamma 已提交
196 197 198 199 200 201 202 203 204 205 206 207 208 209

		if (stopRenderingLineAfter !== -1 && startColumn > stopRenderingLineAfter && endColumn > stopRenderingLineAfter) {
			// This range is obviously not visible
			return null;
		}

		if (stopRenderingLineAfter !== -1 && startColumn > stopRenderingLineAfter) {
			startColumn = stopRenderingLineAfter;
		}

		if (stopRenderingLineAfter !== -1 && endColumn > stopRenderingLineAfter) {
			endColumn = stopRenderingLineAfter;
		}

210
		return this._readVisibleRangesForRange(startColumn, endColumn, clientRectDeltaLeft, endNode);
E
Erich Gamma 已提交
211 212
	}

213
	protected _readVisibleRangesForRange(startColumn:number, endColumn:number, clientRectDeltaLeft:number, endNode:HTMLElement): HorizontalRange[] {
E
Erich Gamma 已提交
214

A
Alex Dima 已提交
215
		let result: HorizontalRange[];
E
Erich Gamma 已提交
216
		if (startColumn === endColumn) {
217
			result = this._readRawVisibleRangesForPosition(startColumn, clientRectDeltaLeft, endNode);
E
Erich Gamma 已提交
218
		} else {
219
			result = this._readRawVisibleRangesForRange(startColumn, endColumn, clientRectDeltaLeft, endNode);
E
Erich Gamma 已提交
220 221 222 223 224 225 226 227
		}

		if (!result || result.length <= 1) {
			return result;
		}

		result.sort(compareVisibleRanges);

A
Alex Dima 已提交
228 229
		let output: HorizontalRange[] = [];
		let prevRange: HorizontalRange = result[0];
E
Erich Gamma 已提交
230

A
Alex Dima 已提交
231 232
		for (let i = 1, len = result.length; i < len; i++) {
			let currRange = result[i];
E
Erich Gamma 已提交
233

234
			if (prevRange.left + prevRange.width + 0.9 /* account for browser's rounding errors*/ >= currRange.left) {
E
Erich Gamma 已提交
235 236 237 238 239 240 241 242 243 244 245
				prevRange.width = Math.max(prevRange.width, currRange.left + currRange.width - prevRange.left);
			} else {
				output.push(prevRange);
				prevRange = currRange;
			}
		}
		output.push(prevRange);

		return output;
	}

246
	protected _readRawVisibleRangesForPosition(column:number, clientRectDeltaLeft:number, endNode:HTMLElement): HorizontalRange[] {
E
Erich Gamma 已提交
247 248 249

		if (this._charOffsetInPart.length === 0) {
			// This line is empty
A
Alex Dima 已提交
250
			return [new HorizontalRange(0, 0)];
E
Erich Gamma 已提交
251 252
		}

A
Alex Dima 已提交
253 254
		let partIndex = findIndexInArrayWithMax(this._lineParts, column - 1, this._lastRenderedPartIndex);
		let charOffsetInPart = this._charOffsetInPart[column - 1];
E
Erich Gamma 已提交
255

256
		return this._readRawVisibleRangesFrom(this._getReadingTarget(), partIndex, charOffsetInPart, partIndex, charOffsetInPart, clientRectDeltaLeft, endNode);
E
Erich Gamma 已提交
257 258
	}

259
	private _readRawVisibleRangesForRange(startColumn:number, endColumn:number, clientRectDeltaLeft:number, endNode:HTMLElement): HorizontalRange[] {
E
Erich Gamma 已提交
260 261 262 263

		if (startColumn === 1 && endColumn === this._charOffsetInPart.length) {
			// This branch helps IE with bidi text & gives a performance boost to other browsers when reading visible ranges for an entire line

A
Alex Dima 已提交
264
			return [this._readRawVisibleRangeForEntireLine()];
E
Erich Gamma 已提交
265 266
		}

A
Alex Dima 已提交
267 268 269 270
		let startPartIndex = findIndexInArrayWithMax(this._lineParts, startColumn - 1, this._lastRenderedPartIndex);
		let startCharOffsetInPart = this._charOffsetInPart[startColumn - 1];
		let endPartIndex = findIndexInArrayWithMax(this._lineParts, endColumn - 1, this._lastRenderedPartIndex);
		let endCharOffsetInPart = this._charOffsetInPart[endColumn - 1];
E
Erich Gamma 已提交
271

272
		return this._readRawVisibleRangesFrom(this._getReadingTarget(), startPartIndex, startCharOffsetInPart, endPartIndex, endCharOffsetInPart, clientRectDeltaLeft, endNode);
E
Erich Gamma 已提交
273 274
	}

A
Alex Dima 已提交
275 276
	private _readRawVisibleRangeForEntireLine(): HorizontalRange {
		return new HorizontalRange(0, this._getReadingTarget().offsetWidth);
E
Erich Gamma 已提交
277 278
	}

279
	private _readRawVisibleRangesFrom(domNode:HTMLElement, startChildIndex:number, startOffset:number, endChildIndex:number, endOffset:number, clientRectDeltaLeft:number, endNode:HTMLElement): HorizontalRange[] {
A
Alex Dima 已提交
280
		let range = RangeUtil.createRange();
E
Erich Gamma 已提交
281 282 283

		try {
			// Panic check
A
Alex Dima 已提交
284 285
			let min = 0;
			let max = domNode.children.length - 1;
E
Erich Gamma 已提交
286 287 288 289 290 291 292
			if (min > max) {
				return null;
			}
			startChildIndex = Math.min(max, Math.max(min, startChildIndex));
			endChildIndex = Math.min(max, Math.max(min, endChildIndex));

			// If crossing over to a span only to select offset 0, then use the previous span's maximum offset
293
			// Chrome is buggy and doesn't handle 0 offsets well sometimes.
E
Erich Gamma 已提交
294 295 296 297 298 299 300
			if (startChildIndex !== endChildIndex) {
				if (endChildIndex > 0 && endOffset === 0) {
					endChildIndex--;
					endOffset = Number.MAX_VALUE;
				}
			}

A
Alex Dima 已提交
301 302
			let startElement = domNode.children[startChildIndex].firstChild;
			let endElement = domNode.children[endChildIndex].firstChild;
E
Erich Gamma 已提交
303 304 305 306 307 308 309 310 311 312 313

			if (!startElement || !endElement) {
				return null;
			}

			startOffset = Math.min(startElement.textContent.length, Math.max(0, startOffset));
			endOffset = Math.min(endElement.textContent.length, Math.max(0, endOffset));

			range.setStart(startElement, startOffset);
			range.setEnd(endElement, endOffset);

A
Alex Dima 已提交
314 315 316
			let clientRects = range.getClientRects();
			if (clientRects.length === 0) {
				return null;
E
Erich Gamma 已提交
317 318
			}

319
			return this._createRawVisibleRangesFromClientRects(clientRects, clientRectDeltaLeft);
E
Erich Gamma 已提交
320 321 322 323 324 325 326 327 328

		} catch (e) {
			// This is life ...
			return null;
		} finally {
			RangeUtil.detachRange(range, endNode);
		}
	}

329
	protected _createRawVisibleRangesFromClientRects(clientRects:ClientRectList, clientRectDeltaLeft:number): HorizontalRange[] {
A
Alex Dima 已提交
330 331 332
		let result:HorizontalRange[] = [];
		for (let i = 0, len = clientRects.length; i < len; i++) {
			let cR = clientRects[i];
333
			result.push(new HorizontalRange(Math.max(0, cR.left - clientRectDeltaLeft), cR.width));
E
Erich Gamma 已提交
334 335 336 337
		}
		return result;
	}

A
Alex Dima 已提交
338 339 340
	/**
	 * Returns the column for the text found at a specific offset inside a rendered dom node
	 */
E
Erich Gamma 已提交
341
	public getColumnOfNodeOffset(lineNumber:number, spanNode:HTMLElement, offset:number): number {
A
Alex Dima 已提交
342
		let spanIndex = -1;
E
Erich Gamma 已提交
343 344 345 346
		while (spanNode) {
			spanNode = <HTMLElement>spanNode.previousSibling;
			spanIndex++;
		}
A
Alex Dima 已提交
347
		let lineParts = this._lineParts.getParts();
E
Erich Gamma 已提交
348 349

		if (spanIndex >= lineParts.length) {
A
Alex Dima 已提交
350
			return this._stopRenderingLineAfter;
E
Erich Gamma 已提交
351 352 353 354 355 356
		}

		if (offset === 0) {
			return lineParts[spanIndex].startIndex + 1;
		}

A
Alex Dima 已提交
357 358 359
		let originalMin = lineParts[spanIndex].startIndex;
		let originalMax:number;
		let originalMaxStartOffset:number;
E
Erich Gamma 已提交
360 361 362 363 364 365 366 367 368 369

		if (spanIndex + 1 < lineParts.length) {
			// Stop searching characters at the beginning of the next part
			originalMax = lineParts[spanIndex + 1].startIndex;
			originalMaxStartOffset = this._charOffsetInPart[originalMax - 1] + this._charOffsetInPart[originalMax];
		} else {
			originalMax = this._context.model.getLineMaxColumn(lineNumber) - 1;
			originalMaxStartOffset = this._charOffsetInPart[originalMax];
		}

A
Alex Dima 已提交
370 371
		let min = originalMin;
		let max = originalMax;
E
Erich Gamma 已提交
372

A
Alex Dima 已提交
373 374
		if (this._stopRenderingLineAfter !== -1) {
			max = Math.min(this._stopRenderingLineAfter - 1, originalMax);
E
Erich Gamma 已提交
375 376
		}

A
Alex Dima 已提交
377 378
		let nextStartOffset:number;
		let prevStartOffset:number;
E
Erich Gamma 已提交
379 380 381 382 383 384 385 386 387

		// Here are the variables and their relation plotted on an axis

		// prevStartOffset    a    midStartOffset    b    nextStartOffset
		// ------|------------|----------|-----------|-----------|--------->

		// Everything in (a;b] will match mid

		while (min < max) {
A
Alex Dima 已提交
388 389
			let mid = Math.floor( (min + max) / 2 );
			let midStartOffset = this._charOffsetInPart[mid];
E
Erich Gamma 已提交
390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407

			if (mid === originalMax) {
				// Using Number.MAX_VALUE to ensure that any offset after midStartOffset will match mid
				nextStartOffset = Number.MAX_VALUE;
			} else if (mid + 1 === originalMax) {
				// mid + 1 is already in next part and might have the _charOffsetInPart = 0
				nextStartOffset = originalMaxStartOffset;
			} else {
				nextStartOffset = this._charOffsetInPart[mid + 1];
			}

			if (mid === originalMin) {
				// Using Number.MIN_VALUE to ensure that any offset before midStartOffset will match mid
				prevStartOffset = Number.MIN_VALUE;
			} else {
				prevStartOffset = this._charOffsetInPart[mid - 1];
			}

A
Alex Dima 已提交
408 409
			let a = (prevStartOffset + midStartOffset) / 2;
			let b = (midStartOffset + nextStartOffset) / 2;
E
Erich Gamma 已提交
410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428

			if (a < offset && offset <= b) {
				// Hit!
				return mid + 1;
			}

			if (offset <= a) {
				max = mid - 1;
			} else {
				min = mid + 1;
			}
		}

		return min + 1;
	}
}

class IEViewLine extends ViewLine {

A
Alex Dima 已提交
429
	constructor(context:IViewContext) {
E
Erich Gamma 已提交
430 431 432
		super(context);
	}

433
	protected _createRawVisibleRangesFromClientRects(clientRects:ClientRectList, clientRectDeltaLeft:number): HorizontalRange[] {
A
Alex Dima 已提交
434 435 436 437
		let ratioX = screen.logicalXDPI / screen.deviceXDPI;
		let result:HorizontalRange[] = [];
		for (let i = 0, len = clientRects.length; i < len; i++) {
			let cR = clientRects[i];
438
			result[i] = new HorizontalRange(Math.max(0, cR.left * ratioX - clientRectDeltaLeft), cR.width * ratioX);
E
Erich Gamma 已提交
439 440 441 442 443 444 445 446
		}

		return result;
	}
}

class WebKitViewLine extends ViewLine {

A
Alex Dima 已提交
447
	constructor(context:IViewContext) {
E
Erich Gamma 已提交
448 449 450
		super(context);
	}

451 452
	protected _readVisibleRangesForRange(startColumn:number, endColumn:number, clientRectDeltaLeft:number, endNode:HTMLElement): HorizontalRange[] {
		let output = super._readVisibleRangesForRange(startColumn, endColumn, clientRectDeltaLeft, endNode);
E
Erich Gamma 已提交
453

A
Alex Dima 已提交
454
		if (this._fontLigatures && output.length === 1 && endColumn > 1 && endColumn === this._charOffsetInPart.length) {
455 456 457 458 459 460 461
			let lastSpanBoundingClientRect = (<HTMLElement>this._getReadingTarget().lastChild).getBoundingClientRect();
			let lastSpanBoundingClientRectRight = lastSpanBoundingClientRect.right - clientRectDeltaLeft;
			if (startColumn === endColumn) {
				output[0].left = lastSpanBoundingClientRectRight;
				output[0].width = 0;
			} else {
				output[0].width = lastSpanBoundingClientRectRight - output[0].left;
462
			}
463
			return output;
464 465
		}

E
Erich Gamma 已提交
466 467 468 469
		if (!output || output.length === 0 || startColumn === endColumn || (startColumn === 1 && endColumn === this._charOffsetInPart.length)) {
			return output;
		}

470
		// WebKit is buggy and returns an expanded range (to contain words in some cases)
E
Erich Gamma 已提交
471 472 473 474
		// The last client rect is enlarged (I think)

		// This is an attempt to patch things up
		// Find position of previous column
475
		let beforeEndVisibleRanges = this._readRawVisibleRangesForPosition(endColumn - 1, clientRectDeltaLeft, endNode);
E
Erich Gamma 已提交
476
		// Find position of last column
477
		let endVisibleRanges = this._readRawVisibleRangesForPosition(endColumn, clientRectDeltaLeft, endNode);
E
Erich Gamma 已提交
478 479

		if (beforeEndVisibleRanges && beforeEndVisibleRanges.length > 0 && endVisibleRanges && endVisibleRanges.length > 0) {
A
Alex Dima 已提交
480 481 482 483
			let beforeEndVisibleRange = beforeEndVisibleRanges[0];
			let endVisibleRange = endVisibleRanges[0];
			let isLTR = (beforeEndVisibleRange.left <= endVisibleRange.left);
			let lastRange = output[output.length - 1];
E
Erich Gamma 已提交
484

A
Andre Weinand 已提交
485
			if (isLTR && lastRange.left < endVisibleRange.left) {
E
Erich Gamma 已提交
486 487 488 489 490 491 492 493 494 495 496 497 498
				// Trim down the width of the last visible range to not go after the last column's position
				lastRange.width = endVisibleRange.left - lastRange.left;
			}
		}

		return output;
	}
}

class RangeUtil {

	/**
	 * Reusing the same range here
499
	 * because IE is buggy and constantly freezes when using a large number
E
Erich Gamma 已提交
500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517
	 * of ranges and calling .detach on them
	 */
	private static _handyReadyRange:Range;

	public static createRange(): Range {
		if (!RangeUtil._handyReadyRange) {
			RangeUtil._handyReadyRange = document.createRange();
		}
		return RangeUtil._handyReadyRange;
	}

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

A
Alex Dima 已提交
518
function compareVisibleRanges(a: HorizontalRange, b: HorizontalRange): number {
A
Andre Weinand 已提交
519
	return a.left - b.left;
E
Erich Gamma 已提交
520 521 522
}

function findIndexInArrayWithMax(lineParts:ILineParts, desiredIndex: number, maxResult:number): number {
A
Alex Dima 已提交
523
	let r = lineParts.findIndexOfOffset(desiredIndex);
E
Erich Gamma 已提交
524 525 526
	return r <= maxResult ? r : maxResult;
}

A
Alex Dima 已提交
527
export let createLine: (context: IViewContext) => ViewLine = (function() {
E
Erich Gamma 已提交
528 529 530 531
	if (window.screen && window.screen.deviceXDPI && (navigator.userAgent.indexOf('Trident/6.0') >= 0 || navigator.userAgent.indexOf('Trident/5.0') >= 0)) {
		// IE11 doesn't need the screen.logicalXDPI / screen.deviceXDPI ratio multiplication
		// for TextRange.getClientRects() anymore
		return createIELine;
A
Alex Dima 已提交
532
	} else if (browser.isWebKit) {
E
Erich Gamma 已提交
533 534 535 536 537
		return createWebKitLine;
	}
	return createNormalLine;
})();

A
Alex Dima 已提交
538
function createIELine(context: IViewContext): ViewLine {
E
Erich Gamma 已提交
539 540 541
	return new IEViewLine(context);
}

A
Alex Dima 已提交
542
function createWebKitLine(context: IViewContext): ViewLine {
E
Erich Gamma 已提交
543 544 545
	return new WebKitViewLine(context);
}

A
Alex Dima 已提交
546
function createNormalLine(context: IViewContext): ViewLine {
E
Erich Gamma 已提交
547 548 549
	return new ViewLine(context);
}