Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/editor/test/common/model/modelInjectedText.test.ts
5240 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 assert from 'assert';
7
import { mock } from '../../../../base/test/common/mock.js';
8
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js';
9
import { EditOperation } from '../../../common/core/editOperation.js';
10
import { Range } from '../../../common/core/range.js';
11
import { InternalModelContentChangeEvent, LineInjectedText, ModelInjectedTextChangedEvent, ModelRawChange, RawContentChangedType } from '../../../common/textModelEvents.js';
12
import { IViewModel } from '../../../common/viewModel.js';
13
import { createTextModel } from '../testTextModel.js';
14
15
suite('Editor Model - Injected Text Events', () => {
16
const store = ensureNoDisposablesAreLeakedInTestSuite();
17
18
test('Basic', () => {
19
const thisModel = store.add(createTextModel('First Line\nSecond Line'));
20
21
const recordedChanges = new Array<unknown>();
22
23
const spyViewModel = new class extends mock<IViewModel>() {
24
override onDidChangeContentOrInjectedText(e: InternalModelContentChangeEvent | ModelInjectedTextChangedEvent) {
25
const changes = (e instanceof InternalModelContentChangeEvent ? e.rawContentChangedEvent.changes : e.changes);
26
for (const change of changes) {
27
recordedChanges.push(mapChange(change));
28
}
29
}
30
override emitContentChangeEvent(_e: InternalModelContentChangeEvent | ModelInjectedTextChangedEvent): void { }
31
};
32
thisModel.registerViewModel(spyViewModel);
33
34
// Initial decoration
35
let decorations = thisModel.deltaDecorations([], [{
36
options: {
37
after: { content: 'injected1' },
38
description: 'test1',
39
showIfCollapsed: true
40
},
41
range: new Range(1, 1, 1, 1),
42
}]);
43
assert.deepStrictEqual(recordedChanges.splice(0), [
44
{
45
kind: 'lineChanged',
46
line: '[injected1]First Line',
47
lineNumber: 1,
48
}
49
]);
50
51
// Decoration change
52
decorations = thisModel.deltaDecorations(decorations, [{
53
options: {
54
after: { content: 'injected1' },
55
description: 'test1',
56
showIfCollapsed: true
57
},
58
range: new Range(2, 1, 2, 1),
59
}, {
60
options: {
61
after: { content: 'injected2' },
62
description: 'test2',
63
showIfCollapsed: true
64
},
65
range: new Range(2, 2, 2, 2),
66
}]);
67
assert.deepStrictEqual(recordedChanges.splice(0), [
68
{
69
kind: 'lineChanged',
70
line: 'First Line',
71
lineNumber: 1,
72
},
73
{
74
kind: 'lineChanged',
75
line: '[injected1]S[injected2]econd Line',
76
lineNumber: 2,
77
}
78
]);
79
80
// Simple Insert
81
thisModel.applyEdits([EditOperation.replace(new Range(2, 2, 2, 2), 'Hello')]);
82
assert.deepStrictEqual(recordedChanges.splice(0), [
83
{
84
kind: 'lineChanged',
85
line: '[injected1]SHello[injected2]econd Line',
86
lineNumber: 2,
87
}
88
]);
89
90
// Multi-Line Insert
91
thisModel.pushEditOperations(null, [EditOperation.replace(new Range(2, 2, 2, 2), '\n\n\n')], null);
92
assert.deepStrictEqual(thisModel.getAllDecorations(undefined).map(d => ({ description: d.options.description, range: d.range.toString() })), [{
93
'description': 'test1',
94
'range': '[2,1 -> 2,1]'
95
},
96
{
97
'description': 'test2',
98
'range': '[2,2 -> 5,6]'
99
}]);
100
assert.deepStrictEqual(recordedChanges.splice(0), [
101
{
102
kind: 'lineChanged',
103
line: '[injected1]S',
104
lineNumber: 2,
105
},
106
{
107
fromLineNumber: 3,
108
kind: 'linesInserted',
109
lines: [
110
'',
111
'',
112
'Hello[injected2]econd Line',
113
]
114
}
115
]);
116
117
118
// Multi-Line Replace
119
thisModel.pushEditOperations(null, [EditOperation.replace(new Range(3, 1, 5, 1), '\n\n\n\n\n\n\n\n\n\n\n\n\n')], null);
120
assert.deepStrictEqual(recordedChanges.splice(0), [
121
{
122
'kind': 'lineChanged',
123
'line': '',
124
'lineNumber': 5,
125
},
126
{
127
'kind': 'lineChanged',
128
'line': '',
129
'lineNumber': 4,
130
},
131
{
132
'kind': 'lineChanged',
133
'line': '',
134
'lineNumber': 3,
135
},
136
{
137
'fromLineNumber': 6,
138
'kind': 'linesInserted',
139
'lines': [
140
'',
141
'',
142
'',
143
'',
144
'',
145
'',
146
'',
147
'',
148
'',
149
'',
150
'Hello[injected2]econd Line',
151
]
152
}
153
]);
154
155
// Multi-Line Replace undo
156
assert.strictEqual(thisModel.undo(), undefined);
157
assert.deepStrictEqual(recordedChanges.splice(0), [
158
{
159
kind: 'lineChanged',
160
line: '[injected1]SHello[injected2]econd Line',
161
lineNumber: 2,
162
},
163
{
164
kind: 'linesDeleted',
165
}
166
]);
167
168
thisModel.unregisterViewModel(spyViewModel);
169
});
170
});
171
172
function mapChange(change: ModelRawChange): unknown {
173
if (change.changeType === RawContentChangedType.LineChanged) {
174
(change.injectedText || []).every(e => {
175
assert.deepStrictEqual(e.lineNumber, change.lineNumber);
176
});
177
178
return {
179
kind: 'lineChanged',
180
line: getDetail(change.detail, change.injectedText),
181
lineNumber: change.lineNumber,
182
};
183
} else if (change.changeType === RawContentChangedType.LinesInserted) {
184
return {
185
kind: 'linesInserted',
186
lines: change.detail.map((e, idx) => getDetail(e, change.injectedTexts[idx])),
187
fromLineNumber: change.fromLineNumber
188
};
189
} else if (change.changeType === RawContentChangedType.LinesDeleted) {
190
return {
191
kind: 'linesDeleted',
192
};
193
} else if (change.changeType === RawContentChangedType.EOLChanged) {
194
return {
195
kind: 'eolChanged'
196
};
197
} else if (change.changeType === RawContentChangedType.Flush) {
198
return {
199
kind: 'flush'
200
};
201
}
202
return { kind: 'unknown' };
203
}
204
205
function getDetail(line: string, injectedTexts: LineInjectedText[] | null): string {
206
return LineInjectedText.applyInjectedText(line, (injectedTexts || []).map(t => t.withText(`[${t.options.content}]`)));
207
}
208
209