src/vs/editor/common/viewLayout/linesLayout.ts

912 LOC · 796 covered · 116 uncovered · 137 ranges · 35 concepts · 31 introducers · 12 tests

File neighbourhood

The centred file is linked to every concept that introduces one of its ranges, every test that runs code from the file, and the gray connector concepts standing between those tests and the file's own introducer concepts. Undirected links join concepts to every file where they introduce source and concepts to the tests they introduce; arrows show specialization between the displayed concepts and bridge only concepts omitted from this view. Concept colors match the source ranges below; connector concepts have no source color and are shown in gray.

Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the file

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the related-file, concept, and source links on this page.

Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the filesrc/vs/editor/common/viewLayout/lineHeights.ts · 493 LOCviewLayout/lineHeights.t…linesLayout.test|title=Editor ViewLayout - LinesLayout LinesLayout 2|occurrence=1 · 0 introduced LOClinesLayout.test|title=E…linesLayout.ts ×3 · 29 introduced LOClinesLayout.ts ×3linesLayout.test|title=Editor ViewLayout - LinesLayout LinesLayout 1|occurrence=1, linesLayout.test|title=Editor ViewLayout - LinesLayout LinesLayout Padding|occurrence=1 · 0 introduced LOClinesLayout.test|title=E…linesLayout.ts ×2 · 4 introduced LOClinesLayout.ts ×2linesLayout.ts ×1 · 3 introduced LOClinesLayout.ts ×1linesLayout.ts ×6 · 102 introduced LOClinesLayout.ts ×6linesLayout.ts ×1 · 4 introduced LOClinesLayout.ts ×1linesLayout.ts ×2 · 27 introduced LOClinesLayout.ts ×2linesLayout.ts ×1 · 2 introduced LOClinesLayout.ts ×1linesLayout.ts ×2 · 4 introduced LOClinesLayout.ts ×2linesLayout.ts ×3 · 6 introduced LOClinesLayout.ts ×3linesLayout.ts ×4 · 28 introduced LOClinesLayout.ts ×4linesLayout.ts ×8 · 41 introduced LOClinesLayout.ts ×8linesLayout.test|title=Editor ViewLayout - LinesLayout LinesLayout changeWhitespaceAfterLineNumber & getFirstWhitespaceIndexAfterLineNumber|occurrence=1 · 0 introduced LOClinesLayout.test|title=E…linesLayout.ts ×7 · 44 introduced LOClinesLayout.ts ×7linesLayout.ts ×1 · 2 introduced LOClinesLayout.ts ×1linesLayout.ts ×2 · 18 introduced LOClinesLayout.ts ×2linesLayout.ts ×1 · 2 introduced LOClinesLayout.ts ×1linesLayout.ts ×1 · 13 introduced LOClinesLayout.ts ×1linesLayout.ts ×10 · 25 introduced LOClinesLayout.ts ×10linesLayout.test|title=Editor ViewLayout - LinesLayout LinesLayout Bug|occurrence=1 · 0 introduced LOClinesLayout.test|title=E…linesLayout.ts ×1 · 2 introduced LOClinesLayout.ts ×1linesLayout.ts ×1 · 2 introduced LOClinesLayout.ts ×1linesLayout.ts ×3 · 13 introduced LOClinesLayout.ts ×3linesLayout.ts ×3 · 13 introduced LOClinesLayout.ts ×3linesLayout.ts ×2 · 6 introduced LOClinesLayout.ts ×2linesLayout.ts ×1 · 4 introduced LOClinesLayout.ts ×1linesLayout.ts ×2 · 23 introduced LOClinesLayout.ts ×2linesLayout.ts ×2 · 12 introduced LOClinesLayout.ts ×2linesLayout.ts ×1 · 4 introduced LOClinesLayout.ts ×1linesLayout.ts ×13 · 72 introduced LOClinesLayout.ts ×13linesLayout.ts ×1 · 1 introduced LOClinesLayout.ts ×1linesLayout.ts ×3 · 5 introduced LOClinesLayout.ts ×3linesLayout.ts ×3 · 8 introduced LOClinesLayout.ts ×3linesLayout.ts ×46 · 279 introduced LOClinesLayout.ts ×46linesLayout.test|title=Editor ViewLayout - LinesLayout LinesLayout 1|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/editor/test/common/viewLayout/linesLayout.test|title=Editor ViewLayout - LinesLayout LinesLayout 1|occurrence=1linesLayout.test|title=E…linesLayout.test|title=Editor ViewLayout - LinesLayout LinesLayout 2|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/editor/test/common/viewLayout/linesLayout.test|title=Editor ViewLayout - LinesLayout LinesLayout 2|occurrence=1linesLayout.test|title=E…linesLayout.test|title=Editor ViewLayout - LinesLayout LinesLayout Bug|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/editor/test/common/viewLayout/linesLayout.test|title=Editor ViewLayout - LinesLayout LinesLayout Bug|occurrence=1linesLayout.test|title=E…linesLayout.test|title=Editor ViewLayout - LinesLayout LinesLayout Padding|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/editor/test/common/viewLayout/linesLayout.test|title=Editor ViewLayout - LinesLayout LinesLayout Padding|occurrence=1linesLayout.test|title=E…linesLayout.test|title=Editor ViewLayout - LinesLayout LinesLayout changeWhitespaceAfterLineNumber & getFirstWhitespaceIndexAfterLineNumber|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/editor/test/common/viewLayout/linesLayout.test|title=Editor ViewLayout - LinesLayout LinesLayout changeWhitespaceAfterLineNumber & getFirstWhitespaceIndexAfterLineNumber|occurrence=1linesLayout.test|title=E…linesLayout.test|title=Editor ViewLayout - LinesLayout LinesLayout findInsertionIndex|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/editor/test/common/viewLayout/linesLayout.test|title=Editor ViewLayout - LinesLayout LinesLayout findInsertionIndex|occurrence=1linesLayout.test|title=E…linesLayout.test|title=Editor ViewLayout - LinesLayout LinesLayout getCenteredLineInViewport|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/editor/test/common/viewLayout/linesLayout.test|title=Editor ViewLayout - LinesLayout LinesLayout getCenteredLineInViewport|occurrence=1linesLayout.test|title=E…linesLayout.test|title=Editor ViewLayout - LinesLayout LinesLayout getLineNumberAtOrAfterVerticalOffset|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/editor/test/common/viewLayout/linesLayout.test|title=Editor ViewLayout - LinesLayout LinesLayout getLineNumberAtOrAfterVerticalOffset|occurrence=1linesLayout.test|title=E…linesLayout.test|title=Editor ViewLayout - LinesLayout LinesLayout getLinesViewportData 1|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/editor/test/common/viewLayout/linesLayout.test|title=Editor ViewLayout - LinesLayout LinesLayout getLinesViewportData 1|occurrence=1linesLayout.test|title=E…linesLayout.test|title=Editor ViewLayout - LinesLayout LinesLayout getLinesViewportData 2 & getWhitespaceViewportData|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/editor/test/common/viewLayout/linesLayout.test|title=Editor ViewLayout - LinesLayout LinesLayout getLinesViewportData 2 & getWhitespaceViewportData|occurrence=1linesLayout.test|title=E…linesLayout.test|title=Editor ViewLayout - LinesLayout LinesLayout getWhitespaceAtVerticalOffset|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/editor/test/common/viewLayout/linesLayout.test|title=Editor ViewLayout - LinesLayout LinesLayout getWhitespaceAtVerticalOffset|occurrence=1linesLayout.test|title=E…linesLayout.test|title=Editor ViewLayout - LinesLayout LinesLayout|occurrence=1 · introduced test · mocha:v1|namespace=vscode@05c208e9e28d8c1c723fa08f85e2b7a96092e8e5|file=vs/editor/test/common/viewLayout/linesLayout.test|title=Editor ViewLayout - LinesLayout LinesLayout|occurrence=1linesLayout.test|title=E…Focused file · src/vs/editor/common/viewLayout/linesLayout.ts · 912 LOCviewLayout/linesLayout.t…

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the related-file, concept, and source links on this page while the interactive map is unavailable.

1 > /*--------------------------------------------------------------------------------------------- linesLayout.ts ×46
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { IEditorWhitespace, IPartialViewLinesViewportData, ILineHeightChangeAccessor, IViewWhitespaceViewportData, IWhitespaceChangeAccessor } from '../viewModel.js';
7 > import * as strings from '../../../base/common/strings.js';
8 > import { CustomLineHeightData, LineHeightsManager } from './lineHeights.js';
9 >
10 > interface IPendingChange { id: string; newAfterLineNumber: number; newHeight: number }
11 > interface IPendingRemove { id: string }
12 >
13 > class PendingChanges {
14 > private _hasPending: boolean;
15 > private _inserts: EditorWhitespace[];
16 > private _changes: IPendingChange[];
17 > private _removes: IPendingRemove[];
18 >
19 > constructor() {
20 > this._hasPending = false; linesLayout.ts ×13
21 > this._inserts = [];
22 > this._changes = [];
23 > this._removes = [];
24 > }
26 > public insert(x: EditorWhitespace): void {
27 > this._hasPending = true; linesLayout.ts ×13
28 > this._inserts.push(x);
29 > }
31 > public change(x: IPendingChange): void {
32 > this._hasPending = true; linesLayout.ts ×10
33 > this._changes.push(x);
34 > }
36 > public remove(x: IPendingRemove): void {
37 > this._hasPending = true; linesLayout.ts ×8
38 > this._removes.push(x);
39 > }
41 > public commit(linesLayout: LinesLayout): void {
42 > if (!this._hasPending) { linesLayout.ts ×13
43 return;
44 }
46 > const inserts = this._inserts;
47 > const changes = this._changes;
48 > const removes = this._removes;
49 >
50 > this._hasPending = false;
51 > this._inserts = [];
52 > this._changes = [];
53 > this._removes = [];
54 >
55 > linesLayout._commitPendingChanges(inserts, changes, removes);
56 > }
58 >
59 > export class EditorWhitespace implements IEditorWhitespace {
60 > public id: string;
61 > public afterLineNumber: number;
62 > public ordinal: number;
63 > public height: number;
64 > public minWidth: number;
65 > public prefixSum: number;
66 >
67 > constructor(id: string, afterLineNumber: number, ordinal: number, height: number, minWidth: number) {
68 > this.id = id;
69 > this.afterLineNumber = afterLineNumber;
70 > this.ordinal = ordinal;
71 > this.height = height;
72 > this.minWidth = minWidth;
73 > this.prefixSum = 0;
74 > }
75 > }
76 >
77 > /**
78 > * Layouting of objects that take vertical space (by having a height) and push down other objects.
79 > *
80 > * These objects are basically either text (lines) or spaces between those lines (whitespaces).
81 > * This provides commodity operations for working with lines that contain whitespace that pushes lines lower (vertically).
82 > */
83 > export class LinesLayout {
84 >
85 > private static INSTANCE_COUNT = 0;
86 >
87 > private readonly _instanceId: string;
88 > private readonly _pendingChanges: PendingChanges;
89 > private _lastWhitespaceId: number;
90 > private _arr: EditorWhitespace[];
91 > private _prefixSumValidIndex: number;
92 > private _minWidth: number;
93 > private _lineCount: number;
94 > private _paddingTop: number;
95 > private _paddingBottom: number;
96 > private _lineHeightsManager: LineHeightsManager;
97 >
98 > constructor(lineCount: number, defaultLineHeight: number, paddingTop: number, paddingBottom: number, customLineHeightData: CustomLineHeightData[]) {
99 > this._instanceId = strings.singleLetterHash(++LinesLayout.INSTANCE_COUNT); linesLayout.ts ×13
100 > this._pendingChanges = new PendingChanges();
101 > this._lastWhitespaceId = 0;
102 > this._arr = [];
103 > this._prefixSumValidIndex = -1;
104 > this._minWidth = -1; /* marker for not being computed */
105 > this._lineCount = lineCount;
106 > this._paddingTop = paddingTop;
107 > this._paddingBottom = paddingBottom;
108 > this._lineHeightsManager = new LineHeightsManager(defaultLineHeight, customLineHeightData);
109 > }
111 > /**
112 > * Find the insertion index for a new value inside a sorted array of values.
113 > * If the value is already present in the sorted array, the insertion index will be after the already existing value.
114 > */
115 > public static findInsertionIndex(arr: EditorWhitespace[], afterLineNumber: number, ordinal: number): number {
116 > let low = 0;
117 > let high = arr.length;
118 >
119 > while (low < high) {
120 > const mid = ((low + high) >>> 1); linesLayout.ts ×3
121 >
122 > if (afterLineNumber === arr[mid].afterLineNumber) {
123 > if (ordinal < arr[mid].ordinal) { linesLayout.ts ×3
124 > high = mid; linesLayout.ts ×1
125 > } else { linesLayout.ts ×3
126 > low = mid + 1;
127 > }
128 > } else if (afterLineNumber < arr[mid].afterLineNumber) { linesLayout.ts ×3
129 > high = mid; linesLayout.ts ×3
130 > } else { linesLayout.ts ×3
131 > low = mid + 1;
132 > }
133 > }
135 > return low;
136 > }
137 >
138 > /**
139 > * Change the height of a line in pixels.
140 > */
141 > public setDefaultLineHeight(lineHeight: number): void {
142 this._lineHeightsManager.defaultLineHeight = lineHeight;
143 }
145 > /**
146 > * Changes the padding used to calculate vertical offsets.
147 > */
148 > public setPadding(paddingTop: number, paddingBottom: number): void {
149 this._paddingTop = paddingTop;
150 this._paddingBottom = paddingBottom;
151 }
153 > /**
154 > * Set the number of lines.
155 > *
156 > * @param lineCount New number of lines.
157 > */
158 > public onFlushed(lineCount: number, customLineHeightData: CustomLineHeightData[]): void {
159 this._lineCount = lineCount;
160 this._lineHeightsManager = new LineHeightsManager(this._lineHeightsManager.defaultLineHeight, customLineHeightData);
161 }
163 > public changeLineHeights(callback: (accessor: ILineHeightChangeAccessor) => void): boolean {
164 let hadAChange = false;
165 const accessor: ILineHeightChangeAccessor = {
166 insertOrChangeCustomLineHeight: (decorationId: string, startLineNumber: number, endLineNumber: number, lineHeight: number): void => {
167 hadAChange = true;
168 this._lineHeightsManager.insertOrChangeCustomLineHeight(decorationId, startLineNumber, endLineNumber, lineHeight);
169 },
170 removeCustomLineHeight: (decorationId: string): void => {
171 hadAChange = true;
172 this._lineHeightsManager.removeCustomLineHeight(decorationId);
173 }
174 };
175 callback(accessor);
176 return hadAChange;
177 }
179 > public changeWhitespace(callback: (accessor: IWhitespaceChangeAccessor) => void): boolean {
180 > let hadAChange = false; linesLayout.ts ×13
181 > try {
182 > const accessor: IWhitespaceChangeAccessor = {
183 > insertWhitespace: (afterLineNumber: number, ordinal: number, heightInPx: number, minWidth: number): string => {
184 > hadAChange = true;
185 > afterLineNumber = afterLineNumber | 0;
186 > ordinal = ordinal | 0;
187 > heightInPx = heightInPx | 0;
188 > minWidth = minWidth | 0;
189 > const id = this._instanceId + (++this._lastWhitespaceId);
190 > this._pendingChanges.insert(new EditorWhitespace(id, afterLineNumber, ordinal, heightInPx, minWidth));
191 > return id;
192 > },
193 > changeOneWhitespace: (id: string, newAfterLineNumber: number, newHeight: number): void => {
194 > hadAChange = true; linesLayout.ts ×10
195 > newAfterLineNumber = newAfterLineNumber | 0;
196 > newHeight = newHeight | 0;
197 > this._pendingChanges.change({ id, newAfterLineNumber, newHeight });
198 > },
199 > removeWhitespace: (id: string): void => { linesLayout.ts ×13
200 > hadAChange = true; linesLayout.ts ×8
201 > this._pendingChanges.remove({ id });
202 > }
204 > callback(accessor);
205 > } finally {
206 > this._pendingChanges.commit(this);
207 > }
208 > return hadAChange;
209 > }
211 > public _commitPendingChanges(inserts: EditorWhitespace[], changes: IPendingChange[], removes: IPendingRemove[]): void {
212 > if (inserts.length > 0 || removes.length > 0) { linesLayout.ts ×13
213 > this._minWidth = -1; /* marker for not being computed */
214 > }
215 >
216 > if (inserts.length + changes.length + removes.length <= 1) {
217 > // when only one thing happened, handle it "delicately"
218 > for (const insert of inserts) {
219 > this._insertWhitespace(insert);
220 > }
221 > for (const change of changes) {
222 > this._changeOneWhitespace(change.id, change.newAfterLineNumber, change.newHeight); linesLayout.ts ×10
223 > }
224 > for (const remove of removes) { linesLayout.ts ×13
225 > const index = this._findWhitespaceIndex(remove.id); linesLayout.ts ×8
226 > if (index === -1) {
227 continue;
228 }
229 > this._removeWhitespace(index); linesLayout.ts ×8
230 > }
231 > return; linesLayout.ts ×13
232 > }
233
234 // simply rebuild the entire datastructure
235
236 const toRemove = new Set<string>();
237 for (const remove of removes) {
238 toRemove.add(remove.id);
239 }
240
241 const toChange = new Map<string, IPendingChange>();
242 for (const change of changes) {
243 toChange.set(change.id, change);
244 }
245
246 const applyRemoveAndChange = (whitespaces: EditorWhitespace[]): EditorWhitespace[] => {
247 const result: EditorWhitespace[] = [];
248 for (const whitespace of whitespaces) {
249 if (toRemove.has(whitespace.id)) {
250 continue;
251 }
252 if (toChange.has(whitespace.id)) {
253 const change = toChange.get(whitespace.id)!;
254 whitespace.afterLineNumber = change.newAfterLineNumber;
255 whitespace.height = change.newHeight;
256 }
257 result.push(whitespace);
258 }
259 return result;
260 };
261
262 const result = applyRemoveAndChange(this._arr).concat(applyRemoveAndChange(inserts));
263 result.sort((a, b) => {
264 if (a.afterLineNumber === b.afterLineNumber) {
265 return a.ordinal - b.ordinal;
266 }
267 return a.afterLineNumber - b.afterLineNumber;
268 });
269
270 this._arr = result;
271 this._prefixSumValidIndex = -1;
274 > private _insertWhitespace(whitespace: EditorWhitespace): void {
275 > const insertIndex = LinesLayout.findInsertionIndex(this._arr, whitespace.afterLineNumber, whitespace.ordinal); linesLayout.ts ×13
276 > this._arr.splice(insertIndex, 0, whitespace);
277 > this._prefixSumValidIndex = Math.min(this._prefixSumValidIndex, insertIndex - 1);
278 > }
280 > private _findWhitespaceIndex(id: string): number {
281 > const arr = this._arr; linesLayout.ts ×10
282 > for (let i = 0, len = arr.length; i < len; i++) {
283 > if (arr[i].id === id) {
284 > return i;
285 > }
286 > }
287 return -1;
290 > private _changeOneWhitespace(id: string, newAfterLineNumber: number, newHeight: number): void {
291 > const index = this._findWhitespaceIndex(id); linesLayout.ts ×10
292 > if (index === -1) {
293 return;
294 }
295 > if (this._arr[index].height !== newHeight) { linesLayout.ts ×10
296 > this._arr[index].height = newHeight; linesLayout.ts ×8
297 > this._prefixSumValidIndex = Math.min(this._prefixSumValidIndex, index - 1);
298 > }
299 > if (this._arr[index].afterLineNumber !== newAfterLineNumber) { linesLayout.ts ×10
300 > // `afterLineNumber` changed for this whitespace linesLayout.ts ×1
301 >
302 > // Record old whitespace
303 > const whitespace = this._arr[index];
304 >
305 > // Since changing `afterLineNumber` can trigger a reordering, we're gonna remove this whitespace
306 > this._removeWhitespace(index);
307 >
308 > whitespace.afterLineNumber = newAfterLineNumber;
309 >
310 > // And add it again
311 > this._insertWhitespace(whitespace);
312 > }
315 > private _removeWhitespace(removeIndex: number): void {
316 > this._arr.splice(removeIndex, 1); linesLayout.ts ×10
317 > this._prefixSumValidIndex = Math.min(this._prefixSumValidIndex, removeIndex - 1);
318 > }
320 > /**
321 > * Notify the layouter that lines have been deleted (a continuous zone of lines).
322 > *
323 > * @param fromLineNumber The line number at which the deletion started, inclusive
324 > * @param toLineNumber The line number at which the deletion ended, inclusive
325 > */
326 > public onLinesDeleted(fromLineNumber: number, toLineNumber: number): void {
327 > fromLineNumber = fromLineNumber | 0; linesLayout.ts ×8
328 > toLineNumber = toLineNumber | 0;
329 >
330 > this._lineCount -= (toLineNumber - fromLineNumber + 1);
331 > for (let i = 0, len = this._arr.length; i < len; i++) {
332 > const afterLineNumber = this._arr[i].afterLineNumber;
333 >
334 > if (fromLineNumber <= afterLineNumber && afterLineNumber <= toLineNumber) {
335 > // The line this whitespace was after has been deleted
336 > // => move whitespace to before first deleted line
337 > this._arr[i].afterLineNumber = fromLineNumber - 1;
338 > } else if (afterLineNumber > toLineNumber) {
339 > // The line this whitespace was after has been moved up linesLayout.ts ×1
340 > // => move whitespace up
341 > this._arr[i].afterLineNumber -= (toLineNumber - fromLineNumber + 1);
342 > }
344 > this._lineHeightsManager.onLinesDeleted(fromLineNumber, toLineNumber);
345 > }
347 > /**
348 > * Notify the layouter that lines have been inserted (a continuous zone of lines).
349 > *
350 > * @param fromLineNumber The line number at which the insertion started, inclusive
351 > * @param toLineNumber The line number at which the insertion ended, inclusive.
352 > */
353 > public onLinesInserted(fromLineNumber: number, toLineNumber: number): void {
354 > fromLineNumber = fromLineNumber | 0; linesLayout.ts ×8
355 > toLineNumber = toLineNumber | 0;
356 >
357 > this._lineCount += (toLineNumber - fromLineNumber + 1);
358 > for (let i = 0, len = this._arr.length; i < len; i++) {
359 > const afterLineNumber = this._arr[i].afterLineNumber;
360 >
361 > if (fromLineNumber <= afterLineNumber) {
362 > this._arr[i].afterLineNumber += (toLineNumber - fromLineNumber + 1);
363 > }
364 > }
365 > this._lineHeightsManager.onLinesInserted(fromLineNumber, toLineNumber);
366 > }
368 > /**
369 > * Get the sum of all the whitespaces.
370 > */
371 > public getWhitespacesTotalHeight(): number {
372 > if (this._arr.length === 0) { linesLayout.ts ×3
373 > return 0; linesLayout.ts ×1
374 > }
375 > return this.getWhitespacesAccumulatedHeight(this._arr.length - 1); linesLayout.ts ×3
376 > }
378 > /**
379 > * Return the sum of the heights of the whitespaces at [0..index].
380 > * This includes the whitespace at `index`.
381 > *
382 > * @param index The index of the whitespace.
383 > * @return The sum of the heights of all whitespaces before the one at `index`, including the one at `index`.
384 > */
385 > public getWhitespacesAccumulatedHeight(index: number): number {
386 > index = index | 0; linesLayout.ts ×2
387 >
388 > let startIndex = Math.max(0, this._prefixSumValidIndex + 1);
389 > if (startIndex === 0) {
390 > this._arr[0].prefixSum = this._arr[0].height;
391 > startIndex++;
392 > }
393 >
394 > for (let i = startIndex; i <= index; i++) {
395 > this._arr[i].prefixSum = this._arr[i - 1].prefixSum + this._arr[i].height; linesLayout.ts ×1
396 > }
397 > this._prefixSumValidIndex = Math.max(this._prefixSumValidIndex, index); linesLayout.ts ×2
398 > return this._arr[index].prefixSum;
399 > }
401 > /**
402 > * Get the sum of heights for all objects.
403 > *
404 > * @return The sum of heights for all objects.
405 > */
406 > public getLinesTotalHeight(): number {
407 > const linesHeight = this._lineHeightsManager.getAccumulatedLineHeightsIncludingLineNumber(this._lineCount); linesLayout.ts ×2
408 > const whitespacesHeight = this.getWhitespacesTotalHeight();
409 >
410 > return linesHeight + whitespacesHeight + this._paddingTop + this._paddingBottom;
411 > }
413 > /**
414 > * Returns the accumulated height of whitespaces before the given line number.
415 > *
416 > * @param lineNumber The line number
417 > */
418 > public getWhitespaceAccumulatedHeightBeforeLineNumber(lineNumber: number): number {
419 > lineNumber = lineNumber | 0; linesLayout.ts ×3
420 >
421 > const lastWhitespaceBeforeLineNumber = this._findLastWhitespaceBeforeLineNumber(lineNumber);
422 >
423 > if (lastWhitespaceBeforeLineNumber === -1) {
424 > return 0;
425 > }
426 >
427 > return this.getWhitespacesAccumulatedHeight(lastWhitespaceBeforeLineNumber);
428 > }
430 > private _findLastWhitespaceBeforeLineNumber(lineNumber: number): number {
431 > lineNumber = lineNumber | 0; linesLayout.ts ×2
432 >
433 > // Find the whitespace before line number
434 > const arr = this._arr;
435 > let low = 0;
436 > let high = arr.length - 1;
437 >
438 > while (low <= high) {
439 > const delta = (high - low) | 0;
440 > const halfDelta = (delta / 2) | 0;
441 > const mid = (low + halfDelta) | 0;
442 >
443 > if (arr[mid].afterLineNumber < lineNumber) {
444 > if (mid + 1 >= arr.length || arr[mid + 1].afterLineNumber >= lineNumber) {
445 > return mid;
446 > } else {
447 > low = (mid + 1) | 0; linesLayout.ts ×1
448 > }
449 > } else { linesLayout.ts ×2
450 > high = (mid - 1) | 0;
451 > }
452 > }
453 >
454 > return -1;
455 > }
457 > private _findFirstWhitespaceAfterLineNumber(lineNumber: number): number {
458 > lineNumber = lineNumber | 0; linesLayout.ts ×3
459 >
460 > const lastWhitespaceBeforeLineNumber = this._findLastWhitespaceBeforeLineNumber(lineNumber);
461 > const firstWhitespaceAfterLineNumber = lastWhitespaceBeforeLineNumber + 1;
462 >
463 > if (firstWhitespaceAfterLineNumber < this._arr.length) {
464 > return firstWhitespaceAfterLineNumber;
465 > }
467 > return -1;
470 > /**
471 > * Find the index of the first whitespace which has `afterLineNumber` >= `lineNumber`.
472 > * @return The index of the first whitespace with `afterLineNumber` >= `lineNumber` or -1 if no whitespace is found.
473 > */
474 > public getFirstWhitespaceIndexAfterLineNumber(lineNumber: number): number {
475 > lineNumber = lineNumber | 0; linesLayout.ts ×3
476 >
477 > return this._findFirstWhitespaceAfterLineNumber(lineNumber);
478 > }
480 > /**
481 > * Get the vertical offset (the sum of heights for all objects above) a certain line number.
482 > *
483 > * @param lineNumber The line number
484 > * @return The sum of heights for all objects above `lineNumber`.
485 > */
486 > public getVerticalOffsetForLineNumber(lineNumber: number, includeViewZones = false): number {
487 > lineNumber = lineNumber | 0; linesLayout.ts ×2
488 >
489 > let previousLinesHeight: number;
490 > if (lineNumber > 1) {
491 > previousLinesHeight = this._lineHeightsManager.getAccumulatedLineHeightsIncludingLineNumber(lineNumber - 1);
492 > } else {
493 > previousLinesHeight = 0;
494 > }
495 >
496 > const previousWhitespacesHeight = this.getWhitespaceAccumulatedHeightBeforeLineNumber(lineNumber - (includeViewZones ? 1 : 0));
497 >
498 > return previousLinesHeight + previousWhitespacesHeight + this._paddingTop;
499 > }
501 > public getLineHeightForLineNumber(lineNumber: number): number {
502 > return this._lineHeightsManager.heightForLineNumber(lineNumber); linesLayout.ts ×4
503 > }
505 > /**
506 > * Get the vertical offset (the sum of heights for all objects above) a certain line number and also the line height of the line.
507 > *
508 > * @param lineNumber The line number
509 > * @return The sum of heights for all objects above `lineNumber`.
510 > */
511 > public getVerticalOffsetAfterLineNumber(lineNumber: number, includeViewZones = false): number {
512 lineNumber = lineNumber | 0;
513 const previousLinesHeight = this._lineHeightsManager.getAccumulatedLineHeightsIncludingLineNumber(lineNumber);
514 const previousWhitespacesHeight = this.getWhitespaceAccumulatedHeightBeforeLineNumber(lineNumber + (includeViewZones ? 1 : 0));
515 return previousLinesHeight + previousWhitespacesHeight + this._paddingTop;
516 }
518 > /**
519 > * Returns if there is any whitespace in the document.
520 > */
521 > public hasWhitespace(): boolean {
522 return this.getWhitespacesCount() > 0;
523 }
525 > /**
526 > * The maximum min width for all whitespaces.
527 > */
528 > public getWhitespaceMinWidth(): number {
529 if (this._minWidth === -1) {
530 let minWidth = 0;
531 for (let i = 0, len = this._arr.length; i < len; i++) {
532 minWidth = Math.max(minWidth, this._arr[i].minWidth);
533 }
534 this._minWidth = minWidth;
535 }
536 return this._minWidth;
537 }
539 > /**
540 > * Check if `verticalOffset` is below all lines.
541 > */
542 > public isAfterLines(verticalOffset: number): boolean {
543 const totalHeight = this.getLinesTotalHeight();
544 return verticalOffset > totalHeight;
545 }
547 > public isInTopPadding(verticalOffset: number): boolean {
548 if (this._paddingTop === 0) {
549 return false;
550 }
551 return (verticalOffset < this._paddingTop);
552 }
554 > public isInBottomPadding(verticalOffset: number): boolean {
555 if (this._paddingBottom === 0) {
556 return false;
557 }
558 const totalHeight = this.getLinesTotalHeight();
559 return (verticalOffset >= totalHeight - this._paddingBottom);
560 }
562 > /**
563 > * Find the first line number that is at or after vertical offset `verticalOffset`.
564 > * i.e. if getVerticalOffsetForLine(line) is x and getVerticalOffsetForLine(line + 1) is y, then
565 > * getLineNumberAtOrAfterVerticalOffset(i) = line, x <= i < y.
566 > *
567 > * @param verticalOffset The vertical offset to search at.
568 > * @return The line number at or after vertical offset `verticalOffset`.
569 > */
570 > public getLineNumberAtOrAfterVerticalOffset(verticalOffset: number): number {
571 > verticalOffset = verticalOffset | 0; linesLayout.ts ×4
572 >
573 > if (verticalOffset < 0) {
574 > return 1; linesLayout.ts ×1
575 > }
577 > const linesCount = this._lineCount | 0;
578 > let minLineNumber = 1;
579 > let maxLineNumber = linesCount;
580 >
581 > while (minLineNumber < maxLineNumber) {
582 > const midLineNumber = ((minLineNumber + maxLineNumber) / 2) | 0;
583 >
584 > const lineHeight = this.getLineHeightForLineNumber(midLineNumber);
585 > const midLineNumberVerticalOffset = this.getVerticalOffsetForLineNumber(midLineNumber) | 0;
586 >
587 > if (verticalOffset >= midLineNumberVerticalOffset + lineHeight) {
588 > // vertical offset is after mid line number
589 > minLineNumber = midLineNumber + 1;
590 > } else if (verticalOffset >= midLineNumberVerticalOffset) {
591 > // Hit
592 > return midLineNumber;
593 > } else {
594 > // vertical offset is before mid line number, but mid line number could still be what we're searching for
595 > maxLineNumber = midLineNumber;
596 > }
597 > }
599 > if (minLineNumber > linesCount) {
600 return linesCount;
601 }
603 > return minLineNumber;
606 > /**
607 > * Get all the lines and their relative vertical offsets that are positioned between `verticalOffset1` and `verticalOffset2`.
608 > *
609 > * @param verticalOffset1 The beginning of the viewport.
610 > * @param verticalOffset2 The end of the viewport.
611 > * @return A structure describing the lines positioned between `verticalOffset1` and `verticalOffset2`.
612 > */
613 > public getLinesViewportData(verticalOffset1: number, verticalOffset2: number): IPartialViewLinesViewportData {
614 > verticalOffset1 = verticalOffset1 | 0; linesLayout.ts ×6
615 > verticalOffset2 = verticalOffset2 | 0;
616 >
617 > // Find first line number
618 > // We don't live in a perfect world, so the line number might start before or after verticalOffset1
619 > const startLineNumber = this.getLineNumberAtOrAfterVerticalOffset(verticalOffset1) | 0;
620 > const startLineNumberVerticalOffset = this.getVerticalOffsetForLineNumber(startLineNumber) | 0;
621 >
622 > let endLineNumber = this._lineCount | 0;
623 >
624 > // Also keep track of what whitespace we've got
625 > let whitespaceIndex = this.getFirstWhitespaceIndexAfterLineNumber(startLineNumber) | 0;
626 > const whitespaceCount = this.getWhitespacesCount() | 0;
627 > let currentWhitespaceHeight: number;
628 > let currentWhitespaceAfterLineNumber: number;
629 >
630 > if (whitespaceIndex === -1) {
631 > whitespaceIndex = whitespaceCount; linesLayout.ts ×1
632 > currentWhitespaceAfterLineNumber = endLineNumber + 1;
633 > currentWhitespaceHeight = 0;
634 > } else { linesLayout.ts ×6
635 > currentWhitespaceAfterLineNumber = this.getAfterLineNumberForWhitespaceIndex(whitespaceIndex) | 0;
636 > currentWhitespaceHeight = this.getHeightForWhitespaceIndex(whitespaceIndex) | 0;
637 > }
638 >
639 > let currentVerticalOffset = startLineNumberVerticalOffset;
640 > let currentLineRelativeOffset = currentVerticalOffset;
641 >
642 > // IE (all versions) cannot handle units above about 1,533,908 px, so every 500k pixels bring numbers down
643 > const STEP_SIZE = 500000;
644 > let bigNumbersDelta = 0;
645 > if (startLineNumberVerticalOffset >= STEP_SIZE) {
646 // Compute a delta that guarantees that lines are positioned at `lineHeight` increments
647 bigNumbersDelta = Math.floor(startLineNumberVerticalOffset / STEP_SIZE) * STEP_SIZE;
648 bigNumbersDelta = Math.floor(bigNumbersDelta / this._lineHeightsManager.defaultLineHeight) * this._lineHeightsManager.defaultLineHeight;
649
650 currentLineRelativeOffset -= bigNumbersDelta;
651 }
653 > const linesOffsets: number[] = [];
654 >
655 > const verticalCenter = verticalOffset1 + (verticalOffset2 - verticalOffset1) / 2;
656 > let centeredLineNumber = -1;
657 >
658 > // Figure out how far the lines go
659 > for (let lineNumber = startLineNumber; lineNumber <= endLineNumber; lineNumber++) {
660 > const lineHeight = this.getLineHeightForLineNumber(lineNumber);
661 > if (centeredLineNumber === -1) {
662 > const currentLineTop = currentVerticalOffset;
663 > const currentLineBottom = currentVerticalOffset + lineHeight;
664 > if ((currentLineTop <= verticalCenter && verticalCenter < currentLineBottom) || currentLineTop > verticalCenter) {
665 > centeredLineNumber = lineNumber;
666 > }
667 > }
668 >
669 > // Count current line height in the vertical offsets
670 > currentVerticalOffset += lineHeight;
671 > linesOffsets[lineNumber - startLineNumber] = currentLineRelativeOffset;
672 >
673 > // Next line starts immediately after this one
674 > currentLineRelativeOffset += lineHeight;
675 > while (currentWhitespaceAfterLineNumber === lineNumber) {
676 > // Push down next line with the height of the current whitespace
677 > currentLineRelativeOffset += currentWhitespaceHeight;
678 >
679 > // Count current whitespace in the vertical offsets
680 > currentVerticalOffset += currentWhitespaceHeight;
681 > whitespaceIndex++;
682 >
683 > if (whitespaceIndex >= whitespaceCount) {
684 > currentWhitespaceAfterLineNumber = endLineNumber + 1;
685 > } else {
686 > currentWhitespaceAfterLineNumber = this.getAfterLineNumberForWhitespaceIndex(whitespaceIndex) | 0; linesLayout.ts ×3
687 > currentWhitespaceHeight = this.getHeightForWhitespaceIndex(whitespaceIndex) | 0;
688 > }
690 >
691 > if (currentVerticalOffset >= verticalOffset2) {
692 > // We have covered the entire viewport area, time to stop
693 > endLineNumber = lineNumber;
694 > break;
695 > }
696 > }
697 >
698 > if (centeredLineNumber === -1) {
699 > centeredLineNumber = endLineNumber;
700 > }
701 >
702 > const endLineNumberVerticalOffset = this.getVerticalOffsetForLineNumber(endLineNumber) | 0;
703 >
704 > let completelyVisibleStartLineNumber = startLineNumber;
705 > let completelyVisibleEndLineNumber = endLineNumber;
706 >
707 > if (completelyVisibleStartLineNumber < completelyVisibleEndLineNumber) {
708 > if (startLineNumberVerticalOffset < verticalOffset1) {
709 > completelyVisibleStartLineNumber++; linesLayout.ts ×2
710 > }
712 > if (completelyVisibleStartLineNumber < completelyVisibleEndLineNumber) {
713 > const endLineHeight = this.getLineHeightForLineNumber(endLineNumber);
714 > if (endLineNumberVerticalOffset + endLineHeight > verticalOffset2) {
715 > completelyVisibleEndLineNumber--; linesLayout.ts ×2
716 > }
718 >
719 > return {
720 > bigNumbersDelta: bigNumbersDelta,
721 > startLineNumber: startLineNumber,
722 > endLineNumber: endLineNumber,
723 > relativeVerticalOffset: linesOffsets,
724 > centeredLineNumber: centeredLineNumber,
725 > completelyVisibleStartLineNumber: completelyVisibleStartLineNumber,
726 > completelyVisibleEndLineNumber: completelyVisibleEndLineNumber,
727 > lineHeight: this._lineHeightsManager.defaultLineHeight,
728 > };
729 > }
731 > public getVerticalOffsetForWhitespaceIndex(whitespaceIndex: number): number {
732 > whitespaceIndex = whitespaceIndex | 0; linesLayout.ts ×7
733 >
734 > const afterLineNumber = this.getAfterLineNumberForWhitespaceIndex(whitespaceIndex);
735 >
736 > let previousLinesHeight: number;
737 > if (afterLineNumber >= 1) {
738 > previousLinesHeight = this._lineHeightsManager.getAccumulatedLineHeightsIncludingLineNumber(afterLineNumber);
739 > } else {
740 previousLinesHeight = 0;
741 }
743 > let previousWhitespacesHeight: number;
744 > if (whitespaceIndex > 0) {
745 > previousWhitespacesHeight = this.getWhitespacesAccumulatedHeight(whitespaceIndex - 1);
746 > } else {
747 > previousWhitespacesHeight = 0;
748 > }
749 > return previousLinesHeight + previousWhitespacesHeight + this._paddingTop;
750 > }
752 > public getWhitespaceIndexAtOrAfterVerticallOffset(verticalOffset: number): number {
753 > verticalOffset = verticalOffset | 0; linesLayout.ts ×7
754 >
755 > let minWhitespaceIndex = 0;
756 > let maxWhitespaceIndex = this.getWhitespacesCount() - 1;
757 >
758 > if (maxWhitespaceIndex < 0) {
759 return -1;
760 }
762 > // Special case: nothing to be found
763 > const maxWhitespaceVerticalOffset = this.getVerticalOffsetForWhitespaceIndex(maxWhitespaceIndex);
764 > const maxWhitespaceHeight = this.getHeightForWhitespaceIndex(maxWhitespaceIndex);
765 > if (verticalOffset >= maxWhitespaceVerticalOffset + maxWhitespaceHeight) {
766 > return -1; linesLayout.ts ×3
767 > }
769 > while (minWhitespaceIndex < maxWhitespaceIndex) {
770 > const midWhitespaceIndex = Math.floor((minWhitespaceIndex + maxWhitespaceIndex) / 2);
771 >
772 > const midWhitespaceVerticalOffset = this.getVerticalOffsetForWhitespaceIndex(midWhitespaceIndex);
773 > const midWhitespaceHeight = this.getHeightForWhitespaceIndex(midWhitespaceIndex);
774 >
775 > if (verticalOffset >= midWhitespaceVerticalOffset + midWhitespaceHeight) {
776 > // vertical offset is after whitespace linesLayout.ts ×3
777 > minWhitespaceIndex = midWhitespaceIndex + 1;
778 > } else if (verticalOffset >= midWhitespaceVerticalOffset) { linesLayout.ts ×7
779 > // Hit linesLayout.ts ×3
780 > return midWhitespaceIndex;
781 > } else { linesLayout.ts ×7
782 > // vertical offset is before whitespace, but midWhitespaceIndex might still be what we're searching for
783 > maxWhitespaceIndex = midWhitespaceIndex;
784 > }
785 > }
786 > return minWhitespaceIndex;
787 > }
789 > /**
790 > * Get exactly the whitespace that is layouted at `verticalOffset`.
791 > *
792 > * @param verticalOffset The vertical offset.
793 > * @return Precisely the whitespace that is layouted at `verticaloffset` or null.
794 > */
795 > public getWhitespaceAtVerticalOffset(verticalOffset: number): IViewWhitespaceViewportData | null {
796 > verticalOffset = verticalOffset | 0; linesLayout.ts ×2
797 >
798 > const candidateIndex = this.getWhitespaceIndexAtOrAfterVerticallOffset(verticalOffset);
799 >
800 > if (candidateIndex < 0) {
801 > return null;
802 > }
803 >
804 > if (candidateIndex >= this.getWhitespacesCount()) {
805 return null;
806 }
808 > const candidateTop = this.getVerticalOffsetForWhitespaceIndex(candidateIndex);
809 >
810 > if (candidateTop > verticalOffset) {
811 > return null;
812 > }
813 >
814 > const candidateHeight = this.getHeightForWhitespaceIndex(candidateIndex);
815 > const candidateId = this.getIdForWhitespaceIndex(candidateIndex);
816 > const candidateAfterLineNumber = this.getAfterLineNumberForWhitespaceIndex(candidateIndex);
817 >
818 > return {
819 > id: candidateId,
820 > afterLineNumber: candidateAfterLineNumber,
821 > verticalOffset: candidateTop,
822 > height: candidateHeight
823 > };
824 > }
826 > /**
827 > * Get a list of whitespaces that are positioned between `verticalOffset1` and `verticalOffset2`.
828 > *
829 > * @param verticalOffset1 The beginning of the viewport.
830 > * @param verticalOffset2 The end of the viewport.
831 > * @return An array with all the whitespaces in the viewport. If no whitespace is in viewport, the array is empty.
832 > */
833 > public getWhitespaceViewportData(verticalOffset1: number, verticalOffset2: number): IViewWhitespaceViewportData[] {
834 > verticalOffset1 = verticalOffset1 | 0; linesLayout.ts ×3
835 > verticalOffset2 = verticalOffset2 | 0;
836 >
837 > const startIndex = this.getWhitespaceIndexAtOrAfterVerticallOffset(verticalOffset1);
838 > const endIndex = this.getWhitespacesCount() - 1;
839 >
840 > if (startIndex < 0) {
841 return [];
842 }
844 > const result: IViewWhitespaceViewportData[] = [];
845 > for (let i = startIndex; i <= endIndex; i++) {
846 > const top = this.getVerticalOffsetForWhitespaceIndex(i);
847 > const height = this.getHeightForWhitespaceIndex(i);
848 > if (top >= verticalOffset2) {
849 > break;
850 > }
851 >
852 > result.push({
853 > id: this.getIdForWhitespaceIndex(i),
854 > afterLineNumber: this.getAfterLineNumberForWhitespaceIndex(i),
855 > verticalOffset: top,
856 > height: height
857 > });
858 > }
859 >
860 > return result;
861 > }
863 > /**
864 > * Get all whitespaces.
865 > */
866 > public getWhitespaces(): IEditorWhitespace[] {
867 return this._arr.slice(0);
868 }
870 > /**
871 > * The number of whitespaces.
872 > */
873 > public getWhitespacesCount(): number {
874 > return this._arr.length; linesLayout.ts ×2
875 > }
877 > /**
878 > * Get the `id` for whitespace at index `index`.
879 > *
880 > * @param index The index of the whitespace.
881 > * @return `id` of whitespace at `index`.
882 > */
883 > public getIdForWhitespaceIndex(index: number): string {
884 > index = index | 0; linesLayout.ts ×1
885 >
886 > return this._arr[index].id;
887 > }
889 > /**
890 > * Get the `afterLineNumber` for whitespace at index `index`.
891 > *
892 > * @param index The index of the whitespace.
893 > * @return `afterLineNumber` of whitespace at `index`.
894 > */
895 > public getAfterLineNumberForWhitespaceIndex(index: number): number {
896 > index = index | 0; linesLayout.ts ×1
897 >
898 > return this._arr[index].afterLineNumber;
899 > }
901 > /**
902 > * Get the `height` for whitespace at index `index`.
903 > *
904 > * @param index The index of the whitespace.
905 > * @return `height` of whitespace at `index`.
906 > */
907 > public getHeightForWhitespaceIndex(index: number): number {
908 > index = index | 0; linesLayout.ts ×2
909 >
910 > return this._arr[index].height;
911 > }