src/vs/editor/common/tokens/contiguousMultilineTokens.ts

229 LOC · 79 covered · 150 uncovered · 19 ranges · 1720 concepts · 3 introducers · 863 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.

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 > /*--------------------------------------------------------------------------------------------- textModelTokens.ts ×44
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 * as arrays from '../../../base/common/arrays.js';
7 > import { readUInt32BE, writeUInt32BE } from '../../../base/common/buffer.js';
8 > import { Position } from '../core/position.js';
9 > import { IRange } from '../core/range.js';
10 > import { countEOL } from '../core/misc/eolCounter.js';
11 > import { ContiguousTokensEditing } from './contiguousTokensEditing.js';
12 > import { LineRange } from '../core/ranges/lineRange.js';
13 >
14 > /**
15 > * Represents contiguous tokens over a contiguous range of lines.
16 > */
17 > export class ContiguousMultilineTokens {
18 > public static deserialize(buff: Uint8Array, offset: number, result: ContiguousMultilineTokens[]): number {
19 const view32 = new Uint32Array(buff.buffer);
20 const startLineNumber = readUInt32BE(buff, offset); offset += 4;
21 const count = readUInt32BE(buff, offset); offset += 4;
22 const tokens: Uint32Array[] = [];
23 for (let i = 0; i < count; i++) {
24 const byteCount = readUInt32BE(buff, offset); offset += 4;
25 tokens.push(view32.subarray(offset / 4, offset / 4 + byteCount / 4));
26 offset += byteCount;
27 }
28 result.push(new ContiguousMultilineTokens(startLineNumber, tokens));
29 return offset;
30 }
32 > /**
33 > * The start line number for this block of tokens.
34 > */
35 > private _startLineNumber: number;
36 >
37 > /**
38 > * The tokens are stored in a binary format. There is an element for each line,
39 > * so `tokens[index]` contains all tokens on line `startLineNumber + index`.
40 > *
41 > * On a specific line, each token occupies two array indices. For token i:
42 > * - at offset 2*i => endOffset
43 > * - at offset 2*i + 1 => metadata
44 > *
45 > */
46 > private _tokens: (Uint32Array | ArrayBuffer | null)[];
47 >
48 > /**
49 > * (Inclusive) start line number for these tokens.
50 > */
51 > public get startLineNumber(): number {
52 > return this._startLineNumber; contiguousTokensStore.ts ×11
53 > }
55 > /**
56 > * (Inclusive) end line number for these tokens.
57 > */
58 > public get endLineNumber(): number {
59 > return this._startLineNumber + this._tokens.length - 1; contiguousTokensStore.ts ×11
60 > }
62 > constructor(startLineNumber: number, tokens: Uint32Array[]) {
63 > this._startLineNumber = startLineNumber; contiguousTokensStore.ts ×11
64 > this._tokens = tokens;
65 > }
67 > getLineRange(): LineRange {
68 return new LineRange(this._startLineNumber, this._startLineNumber + this._tokens.length);
69 }
71 > /**
72 > * @see {@link _tokens}
73 > */
74 > public getLineTokens(lineNumber: number): Uint32Array | ArrayBuffer | null {
75 > return this._tokens[lineNumber - this._startLineNumber]; contiguousTokensStore.ts ×11
76 > }
78 > public appendLineTokens(lineTokens: Uint32Array): void {
79 > this._tokens.push(lineTokens); contiguousTokensStore.ts ×2
80 > }
82 > public serializeSize(): number {
83 let result = 0;
84 result += 4; // 4 bytes for the start line number
85 result += 4; // 4 bytes for the line count
86 for (let i = 0; i < this._tokens.length; i++) {
87 const lineTokens = this._tokens[i];
88 if (!(lineTokens instanceof Uint32Array)) {
89 throw new Error(`Not supported!`);
90 }
91 result += 4; // 4 bytes for the byte count
92 result += lineTokens.byteLength;
93 }
94 return result;
95 }
97 > public serialize(destination: Uint8Array, offset: number): number {
98 writeUInt32BE(destination, this._startLineNumber, offset); offset += 4;
99 writeUInt32BE(destination, this._tokens.length, offset); offset += 4;
100 for (let i = 0; i < this._tokens.length; i++) {
101 const lineTokens = this._tokens[i];
102 if (!(lineTokens instanceof Uint32Array)) {
103 throw new Error(`Not supported!`);
104 }
105 writeUInt32BE(destination, lineTokens.byteLength, offset); offset += 4;
106 destination.set(new Uint8Array(lineTokens.buffer), offset); offset += lineTokens.byteLength;
107 }
108 return offset;
109 }
111 > public applyEdit(range: IRange, text: string): void {
112 const [eolCount, firstLineLength] = countEOL(text);
113 this._acceptDeleteRange(range);
114 this._acceptInsertText(new Position(range.startLineNumber, range.startColumn), eolCount, firstLineLength);
115 }
117 > private _acceptDeleteRange(range: IRange): void {
118 if (range.startLineNumber === range.endLineNumber && range.startColumn === range.endColumn) {
119 // Nothing to delete
120 return;
121 }
122
123 const firstLineIndex = range.startLineNumber - this._startLineNumber;
124 const lastLineIndex = range.endLineNumber - this._startLineNumber;
125
126 if (lastLineIndex < 0) {
127 // this deletion occurs entirely before this block, so we only need to adjust line numbers
128 const deletedLinesCount = lastLineIndex - firstLineIndex;
129 this._startLineNumber -= deletedLinesCount;
130 return;
131 }
132
133 if (firstLineIndex >= this._tokens.length) {
134 // this deletion occurs entirely after this block, so there is nothing to do
135 return;
136 }
137
138 if (firstLineIndex < 0 && lastLineIndex >= this._tokens.length) {
139 // this deletion completely encompasses this block
140 this._startLineNumber = 0;
141 this._tokens = [];
142 return;
143 }
144
145 if (firstLineIndex === lastLineIndex) {
146 // a delete on a single line
147 this._tokens[firstLineIndex] = ContiguousTokensEditing.delete(this._tokens[firstLineIndex], range.startColumn - 1, range.endColumn - 1);
148 return;
149 }
150
151 if (firstLineIndex >= 0) {
152 // The first line survives
153 this._tokens[firstLineIndex] = ContiguousTokensEditing.deleteEnding(this._tokens[firstLineIndex], range.startColumn - 1);
154
155 if (lastLineIndex < this._tokens.length) {
156 // The last line survives
157 const lastLineTokens = ContiguousTokensEditing.deleteBeginning(this._tokens[lastLineIndex], range.endColumn - 1);
158
159 // Take remaining text on last line and append it to remaining text on first line
160 this._tokens[firstLineIndex] = ContiguousTokensEditing.append(this._tokens[firstLineIndex], lastLineTokens);
161
162 // Delete middle lines
163 this._tokens.splice(firstLineIndex + 1, lastLineIndex - firstLineIndex);
164 } else {
165 // The last line does not survive
166
167 // Take remaining text on last line and append it to remaining text on first line
168 this._tokens[firstLineIndex] = ContiguousTokensEditing.append(this._tokens[firstLineIndex], null);
169
170 // Delete lines
171 this._tokens = this._tokens.slice(0, firstLineIndex + 1);
172 }
173 } else {
174 // The first line does not survive
175
176 const deletedBefore = -firstLineIndex;
177 this._startLineNumber -= deletedBefore;
178
179 // Remove beginning from last line
180 this._tokens[lastLineIndex] = ContiguousTokensEditing.deleteBeginning(this._tokens[lastLineIndex], range.endColumn - 1);
181
182 // Delete lines
183 this._tokens = this._tokens.slice(lastLineIndex);
184 }
185 }
187 > private _acceptInsertText(position: Position, eolCount: number, firstLineLength: number): void {
188
189 if (eolCount === 0 && firstLineLength === 0) {
190 // Nothing to insert
191 return;
192 }
193
194 const lineIndex = position.lineNumber - this._startLineNumber;
195
196 if (lineIndex < 0) {
197 // this insertion occurs before this block, so we only need to adjust line numbers
198 this._startLineNumber += eolCount;
199 return;
200 }
201
202 if (lineIndex >= this._tokens.length) {
203 // this insertion occurs after this block, so there is nothing to do
204 return;
205 }
206
207 if (eolCount === 0) {
208 // Inserting text on one line
209 this._tokens[lineIndex] = ContiguousTokensEditing.insert(this._tokens[lineIndex], position.column - 1, firstLineLength);
210 return;
211 }
212
213 this._tokens[lineIndex] = ContiguousTokensEditing.deleteEnding(this._tokens[lineIndex], position.column - 1);
214 this._tokens[lineIndex] = ContiguousTokensEditing.insert(this._tokens[lineIndex], position.column - 1, firstLineLength);
215
216 this._insertLines(position.lineNumber, eolCount);
217 }
219 > private _insertLines(insertIndex: number, insertCount: number): void {
220 if (insertCount === 0) {
221 return;
222 }
223 const lineTokens: (Uint32Array | ArrayBuffer | null)[] = [];
224 for (let i = 0; i < insertCount; i++) {
225 lineTokens[i] = null;
226 }
227 this._tokens = arrays.arrayInsert(this._tokens, insertIndex, lineTokens);
228 }