lineHeights.ts ×24

Frontier kind: Code frontier

unlabeled · c_14dfb4953f87

50 tests · 12681 LOC · 38 files · introduces 0 tests · 127 LOC · 1 file

Introduces — evidence that enters the hierarchy at this concept

Code
24 ranges127 lines · 1 files
Tests
0 tests

Contains — complete concept membership

All code (extent)
1205 ranges12681 lines · 38 files · Browse complete extent
All tests (intent)
50 testsBrowse complete intent

Neighbourhood graph

The orange circle is the focus. Violet and green circles are every ancestor and descendant, broader and narrower, at any distance; blue squares and pink diamonds are the introduced files and exact introduced tests of every visible concept, not only the focus's. Arrows point from broader to narrower concepts and bridge only concepts omitted from this view. Undirected links show source or test introduction. Concept and file size follows LOC; exact test nodes use test-count units.

Introduced files, introduced tests, and structurally relevant concept specialization

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 native relationship evidence on this page.

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the native relationship evidence on this page while the interactive map is unavailable.

Native relationship evidence

Every exact file and test below is linked only from the concept that introduces it.

Introduced tests

Every collected test enters the hierarchy at exactly one concept.

No tests are introduced at this concept. Its intent tests are introduced by other concepts.

Introduced code

Every collected source range enters the hierarchy at exactly one concept.

1 file ranked by introduced lines: 127 introduced LOC across 24 ranges. Expand a file to inspect source; the > gutter marks introduced lines.

src/vs/editor/common/viewLayout/lineHeights.ts 127 introduced LOC · 24 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- lineHeights.ts
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 { binarySearch2 } from '../../../base/common/arrays.js';
7 > import { intersection } from '../../../base/common/collections.js';
8 > import { IEditorConfiguration } from '../config/editorConfiguration.js';
9 > import { EditorOption } from '../config/editorOptions.js';
10 > import { ICoordinatesConverter } from '../coordinatesConverter.js';
11 > import { IModelDecoration } from '../model.js';
12 >
13 > const enum PendingChangeKind {
14 > InsertOrChange,
15 > Remove,
16 > LinesDeleted,
17 > LinesInserted,
18 > }
19 >
20 > type PendingChange =
21 > | { readonly kind: PendingChangeKind.InsertOrChange; readonly decorationId: string; readonly startLineNumber: number; readonly endLineNumber: number; readonly lineHeight: number }
22 > | { readonly kind: PendingChangeKind.Remove; readonly decorationId: string }
23 > | { readonly kind: PendingChangeKind.LinesDeleted; readonly fromLineNumber: number; readonly toLineNumber: number }
24 > | { readonly kind: PendingChangeKind.LinesInserted; readonly fromLineNumber: number; readonly toLineNumber: number };
25 >
26 > export class CustomLine {
27 >
28 > public index: number;
29 > public lineNumber: number;
30 > public specialHeight: number;
31 > public prefixSum: number;
32 > public maximumSpecialHeight: number;
33 > public decorationId: string;
34 > public deleted: boolean;
35 >
36 > constructor(decorationId: string, index: number, lineNumber: number, specialHeight: number, prefixSum: number) {
37 this.decorationId = decorationId;
38 this.index = index;
43 this.deleted = false;
44 }
46 >
47 > /**
48 > * Manages line heights in the editor with support for custom line heights from decorations.
49 > *
50 > * This class maintains an ordered collection of line heights, where each line can have either
51 > * the default height or a custom height specified by decorations. It supports efficient querying
52 > * of individual line heights as well as accumulated heights up to a specific line.
53 > *
54 > * Line heights are stored in a sorted array for efficient binary search operations. Each line
55 > * with custom height is represented by a {@link CustomLine} object which tracks its special height,
56 > * accumulated height prefix sum, and associated decoration ID.
57 > *
58 > * The class optimizes performance by:
59 > * - Using binary search to locate lines in the ordered array
60 > * - Batching updates through a pending changes mechanism
61 > * - Computing prefix sums for O(1) accumulated height lookup
62 > * - Tracking maximum height for lines with multiple decorations
63 > * - Efficiently handling document changes (line insertions and deletions)
64 > *
65 > * When lines are inserted or deleted, the manager updates line numbers and prefix sums
66 > * for all affected lines. It also handles special cases like decorations that span
67 > * the insertion/deletion points by re-applying those decorations appropriately.
68 > *
69 > * All query operations automatically commit pending changes to ensure consistent results.
70 > * Clients can modify line heights by adding or removing custom line height decorations,
71 > * which are tracked by their unique decoration IDs.
72 > */
73 > export class LineHeightsManager {
74 >
75 > private _decorationIDToCustomLine: ArrayMap<string, CustomLine> = new ArrayMap<string, CustomLine>();
76 > private _orderedCustomLines: CustomLine[] = [];
77 > private _pendingChanges: PendingChange[] = [];
78 > private _invalidIndex: number = Infinity;
79 > private _defaultLineHeight: number;
80 > private _hasPending: boolean = false;
81 >
82 > constructor(defaultLineHeight: number, customLineHeightData: CustomLineHeightData[]) {
83 this._defaultLineHeight = defaultLineHeight;
84 for (const data of customLineHeightData) {
86 }
87 }
89 > set defaultLineHeight(defaultLineHeight: number) {
90 this._defaultLineHeight = defaultLineHeight;
91 }
93 > get defaultLineHeight() {
94 return this._defaultLineHeight;
95 }
97 > public removeCustomLineHeight(decorationID: string): void {
98 this._pendingChanges.push({ kind: PendingChangeKind.Remove, decorationId: decorationID });
99 this._hasPending = true;
100 }
102 > public insertOrChangeCustomLineHeight(decorationId: string, startLineNumber: number, endLineNumber: number, lineHeight: number): void {
103 this._pendingChanges.push({ kind: PendingChangeKind.InsertOrChange, decorationId, startLineNumber, endLineNumber, lineHeight });
104 this._hasPending = true;
105 }
107 > public heightForLineNumber(lineNumber: number): number {
108 this._commit();
109 const searchIndex = this._binarySearchOverOrderedCustomLinesArray(lineNumber);
113 return this._defaultLineHeight;
114 }
116 > public getAccumulatedLineHeightsIncludingLineNumber(lineNumber: number): number {
117 this._commit();
118 const searchIndex = this._binarySearchOverOrderedCustomLinesArray(lineNumber);
127 return previousSpecialLine.prefixSum + previousSpecialLine.maximumSpecialHeight + this._defaultLineHeight * (lineNumber - previousSpecialLine.lineNumber);
128 }
130 > public onLinesDeleted(fromLineNumber: number, toLineNumber: number): void {
131 this._pendingChanges.push({ kind: PendingChangeKind.LinesDeleted, fromLineNumber, toLineNumber });
132 this._hasPending = true;
133 }
135 > public onLinesInserted(fromLineNumber: number, toLineNumber: number): void {
136 this._pendingChanges.push({ kind: PendingChangeKind.LinesInserted, fromLineNumber, toLineNumber });
137 this._hasPending = true;
138 }
140 > private _commit(): void {
141 if (!this._hasPending) {
142 return;
168 this._flushStagedDecorationChanges(stagedInserts, stagedIdMap);
169 }
171 > private _doRemoveCustomLineHeight(decorationID: string, stagedIdMap: ArrayMap<string, CustomLine>): void {
172 const customLines = this._decorationIDToCustomLine.get(decorationID);
173 if (customLines) {
186 }
187 }
189 > private _doInsertOrChangeCustomLineHeight(decorationId: string, startLineNumber: number, endLineNumber: number, lineHeight: number, stagedInserts: CustomLine[], stagedIdMap: ArrayMap<string, CustomLine>): void {
190 this._doRemoveCustomLineHeight(decorationId, stagedIdMap);
191 for (let lineNumber = startLineNumber; lineNumber <= endLineNumber; lineNumber++) {
195 }
196 }
198 > private _flushStagedDecorationChanges(stagedInserts: CustomLine[], stagedIdMap: ArrayMap<string, CustomLine>): void {
199 if (stagedInserts.length === 0 && this._invalidIndex === Infinity) {
200 return;
265 this._invalidIndex = Infinity;
266 }
268 > private _doLinesDeleted(fromLineNumber: number, toLineNumber: number): void {
269 const deleteCount = toLineNumber - fromLineNumber + 1;
270 const numberOfCustomLines = this._orderedCustomLines.length;
369 }
370 }
372 > private _doLinesInserted(fromLineNumber: number, toLineNumber: number, stagedInserts: CustomLine[], stagedIdMap: ArrayMap<string, CustomLine>): void {
373 const insertCount = toLineNumber - fromLineNumber + 1;
374 const candidateStartIndexOfInsertion = this._binarySearchOverOrderedCustomLinesArray(fromLineNumber);
427 }
428 }
430 > private _binarySearchOverOrderedCustomLinesArray(lineNumber: number): number {
431 return binarySearch2(this._orderedCustomLines.length, (index) => {
432 const line = this._orderedCustomLines[index];
440 });
441 }
442 > } lineHeights.ts
443 >
444 > export class CustomLineHeightData {
445 >
446 > constructor(
447 readonly decorationId: string,
448 readonly startLineNumber: number,
450 readonly lineHeight: number
451 ) { }
453 > public static fromDecorations(decorations: IModelDecoration[], coordinatesConverter: ICoordinatesConverter, configuration: IEditorConfiguration): CustomLineHeightData[] {
454 const defaultLineHeight = configuration.options.get(EditorOption.lineHeight);
455 return decorations.map((d) => {
463 });
464 }
465 > } lineHeights.ts
466 >
467 > class ArrayMap<K, T> {
468 >
469 > private _map: Map<K, T[]> = new Map<K, T[]>();
470 >
471 > constructor() { }
472 >
473 > add(key: K, value: T) {
474 const array = this._map.get(key);
475 if (!array) {
479 }
480 }
482 > get(key: K): T[] | undefined {
483 return this._map.get(key);
484 }
486 > delete(key: K): void {
487 this._map.delete(key);
488 }
490 > clear(): void {
491 this._map.clear();
492 }
493 > } lineHeights.ts