Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/editor/common/textModelEvents.ts
3292 views
1
/*---------------------------------------------------------------------------------------------
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 { IPosition } from './core/position.js';
7
import { IRange, Range } from './core/range.js';
8
import { Selection } from './core/selection.js';
9
import { IModelDecoration, InjectedTextOptions } from './model.js';
10
import { IModelContentChange } from './model/mirrorTextModel.js';
11
import { TextModelEditSource } from './textModelEditSource.js';
12
13
/**
14
* An event describing that the current language associated with a model has changed.
15
*/
16
export interface IModelLanguageChangedEvent {
17
/**
18
* Previous language
19
*/
20
readonly oldLanguage: string;
21
/**
22
* New language
23
*/
24
readonly newLanguage: string;
25
26
/**
27
* Source of the call that caused the event.
28
*/
29
readonly source: string;
30
}
31
32
/**
33
* An event describing that the language configuration associated with a model has changed.
34
*/
35
export interface IModelLanguageConfigurationChangedEvent {
36
}
37
38
/**
39
* An event describing a change in the text of a model.
40
*/
41
export interface IModelContentChangedEvent {
42
/**
43
* The changes are ordered from the end of the document to the beginning, so they should be safe to apply in sequence.
44
*/
45
readonly changes: IModelContentChange[];
46
/**
47
* The (new) end-of-line character.
48
*/
49
readonly eol: string;
50
/**
51
* The new version id the model has transitioned to.
52
*/
53
readonly versionId: number;
54
/**
55
* Flag that indicates that this event was generated while undoing.
56
*/
57
readonly isUndoing: boolean;
58
/**
59
* Flag that indicates that this event was generated while redoing.
60
*/
61
readonly isRedoing: boolean;
62
/**
63
* Flag that indicates that all decorations were lost with this edit.
64
* The model has been reset to a new value.
65
*/
66
readonly isFlush: boolean;
67
68
/**
69
* Flag that indicates that this event describes an eol change.
70
*/
71
readonly isEolChange: boolean;
72
73
/**
74
* Detailed reason information for the change
75
* @internal
76
*/
77
readonly detailedReasons: TextModelEditSource[];
78
79
/**
80
* The sum of these lengths equals changes.length.
81
* The length of this array must equal the length of detailedReasons.
82
*/
83
readonly detailedReasonsChangeLengths: number[];
84
}
85
86
export interface ISerializedModelContentChangedEvent {
87
/**
88
* The changes are ordered from the end of the document to the beginning, so they should be safe to apply in sequence.
89
*/
90
readonly changes: IModelContentChange[];
91
/**
92
* The (new) end-of-line character.
93
*/
94
readonly eol: string;
95
/**
96
* The new version id the model has transitioned to.
97
*/
98
readonly versionId: number;
99
/**
100
* Flag that indicates that this event was generated while undoing.
101
*/
102
readonly isUndoing: boolean;
103
/**
104
* Flag that indicates that this event was generated while redoing.
105
*/
106
readonly isRedoing: boolean;
107
/**
108
* Flag that indicates that all decorations were lost with this edit.
109
* The model has been reset to a new value.
110
*/
111
readonly isFlush: boolean;
112
113
/**
114
* Flag that indicates that this event describes an eol change.
115
*/
116
readonly isEolChange: boolean;
117
118
/**
119
* Detailed reason information for the change
120
* @internal
121
*/
122
readonly detailedReason: Record<string, unknown> | undefined;
123
}
124
125
/**
126
* An event describing that model decorations have changed.
127
*/
128
export interface IModelDecorationsChangedEvent {
129
readonly affectsMinimap: boolean;
130
readonly affectsOverviewRuler: boolean;
131
readonly affectsGlyphMargin: boolean;
132
readonly affectsLineNumber: boolean;
133
}
134
135
/**
136
* An event describing that some ranges of lines have been tokenized (their tokens have changed).
137
* @internal
138
*/
139
export interface IModelTokensChangedEvent {
140
readonly semanticTokensApplied: boolean;
141
readonly ranges: {
142
/**
143
* The start of the range (inclusive)
144
*/
145
readonly fromLineNumber: number;
146
/**
147
* The end of the range (inclusive)
148
*/
149
readonly toLineNumber: number;
150
}[];
151
}
152
153
export interface IModelOptionsChangedEvent {
154
readonly tabSize: boolean;
155
readonly indentSize: boolean;
156
readonly insertSpaces: boolean;
157
readonly trimAutoWhitespace: boolean;
158
}
159
160
/**
161
* @internal
162
*/
163
export const enum RawContentChangedType {
164
Flush = 1,
165
LineChanged = 2,
166
LinesDeleted = 3,
167
LinesInserted = 4,
168
EOLChanged = 5
169
}
170
171
/**
172
* An event describing that a model has been reset to a new value.
173
* @internal
174
*/
175
export class ModelRawFlush {
176
public readonly changeType = RawContentChangedType.Flush;
177
}
178
179
/**
180
* Represents text injected on a line
181
* @internal
182
*/
183
export class LineInjectedText {
184
public static applyInjectedText(lineText: string, injectedTexts: LineInjectedText[] | null): string {
185
if (!injectedTexts || injectedTexts.length === 0) {
186
return lineText;
187
}
188
let result = '';
189
let lastOriginalOffset = 0;
190
for (const injectedText of injectedTexts) {
191
result += lineText.substring(lastOriginalOffset, injectedText.column - 1);
192
lastOriginalOffset = injectedText.column - 1;
193
result += injectedText.options.content;
194
}
195
result += lineText.substring(lastOriginalOffset);
196
return result;
197
}
198
199
public static fromDecorations(decorations: IModelDecoration[]): LineInjectedText[] {
200
const result: LineInjectedText[] = [];
201
for (const decoration of decorations) {
202
if (decoration.options.before && decoration.options.before.content.length > 0) {
203
result.push(new LineInjectedText(
204
decoration.ownerId,
205
decoration.range.startLineNumber,
206
decoration.range.startColumn,
207
decoration.options.before,
208
0,
209
));
210
}
211
if (decoration.options.after && decoration.options.after.content.length > 0) {
212
result.push(new LineInjectedText(
213
decoration.ownerId,
214
decoration.range.endLineNumber,
215
decoration.range.endColumn,
216
decoration.options.after,
217
1,
218
));
219
}
220
}
221
result.sort((a, b) => {
222
if (a.lineNumber === b.lineNumber) {
223
if (a.column === b.column) {
224
return a.order - b.order;
225
}
226
return a.column - b.column;
227
}
228
return a.lineNumber - b.lineNumber;
229
});
230
return result;
231
}
232
233
constructor(
234
public readonly ownerId: number,
235
public readonly lineNumber: number,
236
public readonly column: number,
237
public readonly options: InjectedTextOptions,
238
public readonly order: number
239
) { }
240
241
public withText(text: string): LineInjectedText {
242
return new LineInjectedText(this.ownerId, this.lineNumber, this.column, { ...this.options, content: text }, this.order);
243
}
244
}
245
246
/**
247
* An event describing that a line has changed in a model.
248
* @internal
249
*/
250
export class ModelRawLineChanged {
251
public readonly changeType = RawContentChangedType.LineChanged;
252
/**
253
* The line that has changed.
254
*/
255
public readonly lineNumber: number;
256
/**
257
* The new value of the line.
258
*/
259
public readonly detail: string;
260
/**
261
* The injected text on the line.
262
*/
263
public readonly injectedText: LineInjectedText[] | null;
264
265
constructor(lineNumber: number, detail: string, injectedText: LineInjectedText[] | null) {
266
this.lineNumber = lineNumber;
267
this.detail = detail;
268
this.injectedText = injectedText;
269
}
270
}
271
272
273
/**
274
* An event describing that a line height has changed in the model.
275
* @internal
276
*/
277
export class ModelLineHeightChanged {
278
/**
279
* Editor owner ID
280
*/
281
public readonly ownerId: number;
282
/**
283
* The decoration ID that has changed.
284
*/
285
public readonly decorationId: string;
286
/**
287
* The line that has changed.
288
*/
289
public readonly lineNumber: number;
290
/**
291
* The line height on the line.
292
*/
293
public readonly lineHeight: number | null;
294
295
constructor(ownerId: number, decorationId: string, lineNumber: number, lineHeight: number | null) {
296
this.ownerId = ownerId;
297
this.decorationId = decorationId;
298
this.lineNumber = lineNumber;
299
this.lineHeight = lineHeight;
300
}
301
}
302
303
/**
304
* An event describing that a line height has changed in the model.
305
* @internal
306
*/
307
export class ModelFontChanged {
308
/**
309
* Editor owner ID
310
*/
311
public readonly ownerId: number;
312
/**
313
* The line that has changed.
314
*/
315
public readonly lineNumber: number;
316
317
constructor(ownerId: number, lineNumber: number) {
318
this.ownerId = ownerId;
319
this.lineNumber = lineNumber;
320
}
321
}
322
323
/**
324
* An event describing that line(s) have been deleted in a model.
325
* @internal
326
*/
327
export class ModelRawLinesDeleted {
328
public readonly changeType = RawContentChangedType.LinesDeleted;
329
/**
330
* At what line the deletion began (inclusive).
331
*/
332
public readonly fromLineNumber: number;
333
/**
334
* At what line the deletion stopped (inclusive).
335
*/
336
public readonly toLineNumber: number;
337
338
constructor(fromLineNumber: number, toLineNumber: number) {
339
this.fromLineNumber = fromLineNumber;
340
this.toLineNumber = toLineNumber;
341
}
342
}
343
344
/**
345
* An event describing that line(s) have been inserted in a model.
346
* @internal
347
*/
348
export class ModelRawLinesInserted {
349
public readonly changeType = RawContentChangedType.LinesInserted;
350
/**
351
* Before what line did the insertion begin
352
*/
353
public readonly fromLineNumber: number;
354
/**
355
* `toLineNumber` - `fromLineNumber` + 1 denotes the number of lines that were inserted
356
*/
357
public readonly toLineNumber: number;
358
/**
359
* The text that was inserted
360
*/
361
public readonly detail: string[];
362
/**
363
* The injected texts for every inserted line.
364
*/
365
public readonly injectedTexts: (LineInjectedText[] | null)[];
366
367
constructor(fromLineNumber: number, toLineNumber: number, detail: string[], injectedTexts: (LineInjectedText[] | null)[]) {
368
this.injectedTexts = injectedTexts;
369
this.fromLineNumber = fromLineNumber;
370
this.toLineNumber = toLineNumber;
371
this.detail = detail;
372
}
373
}
374
375
/**
376
* An event describing that a model has had its EOL changed.
377
* @internal
378
*/
379
export class ModelRawEOLChanged {
380
public readonly changeType = RawContentChangedType.EOLChanged;
381
}
382
383
/**
384
* @internal
385
*/
386
export type ModelRawChange = ModelRawFlush | ModelRawLineChanged | ModelRawLinesDeleted | ModelRawLinesInserted | ModelRawEOLChanged;
387
388
/**
389
* An event describing a change in the text of a model.
390
* @internal
391
*/
392
export class ModelRawContentChangedEvent {
393
394
public readonly changes: ModelRawChange[];
395
/**
396
* The new version id the model has transitioned to.
397
*/
398
public readonly versionId: number;
399
/**
400
* Flag that indicates that this event was generated while undoing.
401
*/
402
public readonly isUndoing: boolean;
403
/**
404
* Flag that indicates that this event was generated while redoing.
405
*/
406
public readonly isRedoing: boolean;
407
408
public resultingSelection: Selection[] | null;
409
410
constructor(changes: ModelRawChange[], versionId: number, isUndoing: boolean, isRedoing: boolean) {
411
this.changes = changes;
412
this.versionId = versionId;
413
this.isUndoing = isUndoing;
414
this.isRedoing = isRedoing;
415
this.resultingSelection = null;
416
}
417
418
public containsEvent(type: RawContentChangedType): boolean {
419
for (let i = 0, len = this.changes.length; i < len; i++) {
420
const change = this.changes[i];
421
if (change.changeType === type) {
422
return true;
423
}
424
}
425
return false;
426
}
427
428
public static merge(a: ModelRawContentChangedEvent, b: ModelRawContentChangedEvent): ModelRawContentChangedEvent {
429
const changes = ([] as ModelRawChange[]).concat(a.changes).concat(b.changes);
430
const versionId = b.versionId;
431
const isUndoing = (a.isUndoing || b.isUndoing);
432
const isRedoing = (a.isRedoing || b.isRedoing);
433
return new ModelRawContentChangedEvent(changes, versionId, isUndoing, isRedoing);
434
}
435
}
436
437
/**
438
* An event describing a change in injected text.
439
* @internal
440
*/
441
export class ModelInjectedTextChangedEvent {
442
443
public readonly changes: ModelRawLineChanged[];
444
445
constructor(changes: ModelRawLineChanged[]) {
446
this.changes = changes;
447
}
448
}
449
450
/**
451
* An event describing a change of a line height.
452
* @internal
453
*/
454
export class ModelLineHeightChangedEvent {
455
456
public readonly changes: ModelLineHeightChanged[];
457
458
constructor(changes: ModelLineHeightChanged[]) {
459
this.changes = changes;
460
}
461
462
public affects(rangeOrPosition: IRange | IPosition) {
463
if (Range.isIRange(rangeOrPosition)) {
464
for (const change of this.changes) {
465
if (change.lineNumber >= rangeOrPosition.startLineNumber && change.lineNumber <= rangeOrPosition.endLineNumber) {
466
return true;
467
}
468
}
469
return false;
470
} else {
471
for (const change of this.changes) {
472
if (change.lineNumber === rangeOrPosition.lineNumber) {
473
return true;
474
}
475
}
476
return false;
477
}
478
}
479
}
480
481
/**
482
* An event describing a change in fonts.
483
* @internal
484
*/
485
export class ModelFontChangedEvent {
486
487
public readonly changes: ModelFontChanged[];
488
489
constructor(changes: ModelFontChanged[]) {
490
this.changes = changes;
491
}
492
}
493
494
/**
495
* @internal
496
*/
497
export class InternalModelContentChangeEvent {
498
constructor(
499
public readonly rawContentChangedEvent: ModelRawContentChangedEvent,
500
public readonly contentChangedEvent: IModelContentChangedEvent,
501
) { }
502
503
public merge(other: InternalModelContentChangeEvent): InternalModelContentChangeEvent {
504
const rawContentChangedEvent = ModelRawContentChangedEvent.merge(this.rawContentChangedEvent, other.rawContentChangedEvent);
505
const contentChangedEvent = InternalModelContentChangeEvent._mergeChangeEvents(this.contentChangedEvent, other.contentChangedEvent);
506
return new InternalModelContentChangeEvent(rawContentChangedEvent, contentChangedEvent);
507
}
508
509
private static _mergeChangeEvents(a: IModelContentChangedEvent, b: IModelContentChangedEvent): IModelContentChangedEvent {
510
const changes = ([] as IModelContentChange[]).concat(a.changes).concat(b.changes);
511
const eol = b.eol;
512
const versionId = b.versionId;
513
const isUndoing = (a.isUndoing || b.isUndoing);
514
const isRedoing = (a.isRedoing || b.isRedoing);
515
const isFlush = (a.isFlush || b.isFlush);
516
const isEolChange = a.isEolChange && b.isEolChange; // both must be true to not confuse listeners who skip such edits
517
return {
518
changes: changes,
519
eol: eol,
520
isEolChange: isEolChange,
521
versionId: versionId,
522
isUndoing: isUndoing,
523
isRedoing: isRedoing,
524
isFlush: isFlush,
525
detailedReasons: a.detailedReasons.concat(b.detailedReasons),
526
detailedReasonsChangeLengths: a.detailedReasonsChangeLengths.concat(b.detailedReasonsChangeLengths),
527
};
528
}
529
}
530
531