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