Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/editor/test/browser/widget/observableCodeEditor.test.ts
5237 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 * as assert from 'assert';
7
import { DisposableStore } from '../../../../base/common/lifecycle.js';
8
import { IObservable, derivedHandleChanges } from '../../../../base/common/observable.js';
9
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js';
10
import { ICodeEditor } from '../../../browser/editorBrowser.js';
11
import { ObservableCodeEditor, observableCodeEditor } from '../../../browser/observableCodeEditor.js';
12
import { Position } from '../../../common/core/position.js';
13
import { Range } from '../../../common/core/range.js';
14
import { ViewModel } from '../../../common/viewModel/viewModelImpl.js';
15
import { withTestCodeEditor } from '../testCodeEditor.js';
16
17
suite('CodeEditorWidget', () => {
18
ensureNoDisposablesAreLeakedInTestSuite();
19
20
function withTestFixture(
21
cb: (args: { editor: ICodeEditor; viewModel: ViewModel; log: Log; derived: IObservable<string> }) => void
22
) {
23
withEditorSetupTestFixture(undefined, cb);
24
}
25
26
function withEditorSetupTestFixture(
27
preSetupCallback:
28
| ((editor: ICodeEditor, disposables: DisposableStore) => void)
29
| undefined,
30
cb: (args: { editor: ICodeEditor; viewModel: ViewModel; log: Log; derived: IObservable<string> }) => void
31
) {
32
withTestCodeEditor('hello world', {}, (editor, viewModel) => {
33
const disposables = new DisposableStore();
34
preSetupCallback?.(editor, disposables);
35
const obsEditor = observableCodeEditor(editor);
36
const log = new Log();
37
38
const derived = derivedHandleChanges(
39
{
40
changeTracker: {
41
createChangeSummary: () => undefined,
42
handleChange: (context) => {
43
const obsName = observableName(context.changedObservable, obsEditor);
44
45
log.log(`handle change: ${obsName} ${formatChange(context.change)}`);
46
return true;
47
},
48
},
49
},
50
(reader) => {
51
const versionId = obsEditor.versionId.read(reader);
52
const selection = obsEditor.selections.read(reader)?.map((s) => s.toString()).join(', ');
53
obsEditor.onDidType.read(reader);
54
55
const str = `running derived: selection: ${selection}, value: ${versionId}`;
56
log.log(str);
57
return str;
58
}
59
);
60
61
derived.recomputeInitiallyAndOnChange(disposables);
62
assert.deepStrictEqual(log.getAndClearEntries(), [
63
'running derived: selection: [1,1 -> 1,1], value: 1',
64
]);
65
66
cb({ editor, viewModel, log, derived });
67
68
disposables.dispose();
69
});
70
}
71
72
test('setPosition', () =>
73
withTestFixture(({ editor, log }) => {
74
editor.setPosition(new Position(1, 2));
75
76
assert.deepStrictEqual(log.getAndClearEntries(), ([
77
'handle change: editor.selections {"selection":"[1,2 -> 1,2]","modelVersionId":1,"oldSelections":["[1,1 -> 1,1]"],"oldModelVersionId":1,"source":"api","reason":0}',
78
'running derived: selection: [1,2 -> 1,2], value: 1'
79
]));
80
}));
81
82
test('keyboard.type', () =>
83
withTestFixture(({ editor, log }) => {
84
editor.trigger('keyboard', 'type', { text: 'abc' });
85
86
assert.deepStrictEqual(log.getAndClearEntries(), ([
87
'handle change: editor.onDidType "abc"',
88
'handle change: editor.versionId {"changes":[{"range":"[1,1 -> 1,1]","rangeLength":0,"text":"a","rangeOffset":0}],"eol":"\\n","versionId":2,"detailedReasons":[{"metadata":{"source":"cursor","kind":"type","detailedSource":"keyboard"}}],"detailedReasonsChangeLengths":[1]}',
89
'handle change: editor.versionId {"changes":[{"range":"[1,2 -> 1,2]","rangeLength":0,"text":"b","rangeOffset":1}],"eol":"\\n","versionId":3,"detailedReasons":[{"metadata":{"source":"cursor","kind":"type","detailedSource":"keyboard"}}],"detailedReasonsChangeLengths":[1]}',
90
'handle change: editor.versionId {"changes":[{"range":"[1,3 -> 1,3]","rangeLength":0,"text":"c","rangeOffset":2}],"eol":"\\n","versionId":4,"detailedReasons":[{"metadata":{"source":"cursor","kind":"type","detailedSource":"keyboard"}}],"detailedReasonsChangeLengths":[1]}',
91
'handle change: editor.selections {"selection":"[1,4 -> 1,4]","modelVersionId":4,"oldSelections":["[1,1 -> 1,1]"],"oldModelVersionId":1,"source":"keyboard","reason":0}',
92
'running derived: selection: [1,4 -> 1,4], value: 4'
93
]));
94
}));
95
96
test('keyboard.type and set position', () =>
97
withTestFixture(({ editor, log }) => {
98
editor.trigger('keyboard', 'type', { text: 'abc' });
99
100
assert.deepStrictEqual(log.getAndClearEntries(), ([
101
'handle change: editor.onDidType "abc"',
102
'handle change: editor.versionId {"changes":[{"range":"[1,1 -> 1,1]","rangeLength":0,"text":"a","rangeOffset":0}],"eol":"\\n","versionId":2,"detailedReasons":[{"metadata":{"source":"cursor","kind":"type","detailedSource":"keyboard"}}],"detailedReasonsChangeLengths":[1]}',
103
'handle change: editor.versionId {"changes":[{"range":"[1,2 -> 1,2]","rangeLength":0,"text":"b","rangeOffset":1}],"eol":"\\n","versionId":3,"detailedReasons":[{"metadata":{"source":"cursor","kind":"type","detailedSource":"keyboard"}}],"detailedReasonsChangeLengths":[1]}',
104
'handle change: editor.versionId {"changes":[{"range":"[1,3 -> 1,3]","rangeLength":0,"text":"c","rangeOffset":2}],"eol":"\\n","versionId":4,"detailedReasons":[{"metadata":{"source":"cursor","kind":"type","detailedSource":"keyboard"}}],"detailedReasonsChangeLengths":[1]}',
105
'handle change: editor.selections {"selection":"[1,4 -> 1,4]","modelVersionId":4,"oldSelections":["[1,1 -> 1,1]"],"oldModelVersionId":1,"source":"keyboard","reason":0}',
106
'running derived: selection: [1,4 -> 1,4], value: 4'
107
]));
108
109
editor.setPosition(new Position(1, 5), 'test');
110
111
assert.deepStrictEqual(log.getAndClearEntries(), ([
112
'handle change: editor.selections {"selection":"[1,5 -> 1,5]","modelVersionId":4,"oldSelections":["[1,4 -> 1,4]"],"oldModelVersionId":4,"source":"test","reason":0}',
113
'running derived: selection: [1,5 -> 1,5], value: 4'
114
]));
115
}));
116
117
test('listener interaction (unforced)', () => {
118
let derived: IObservable<string>;
119
let log: Log;
120
withEditorSetupTestFixture(
121
(editor, disposables) => {
122
disposables.add(
123
editor.onDidChangeModelContent(() => {
124
log.log('>>> before get');
125
derived.get();
126
log.log('<<< after get');
127
})
128
);
129
},
130
(args) => {
131
const editor = args.editor;
132
derived = args.derived;
133
log = args.log;
134
135
editor.trigger('keyboard', 'type', { text: 'a' });
136
assert.deepStrictEqual(log.getAndClearEntries(), ([
137
'>>> before get',
138
'<<< after get',
139
'handle change: editor.onDidType "a"',
140
'handle change: editor.versionId {"changes":[{"range":"[1,1 -> 1,1]","rangeLength":0,"text":"a","rangeOffset":0}],"eol":"\\n","versionId":2,"detailedReasons":[{"metadata":{"source":"cursor","kind":"type","detailedSource":"keyboard"}}],"detailedReasonsChangeLengths":[1]}',
141
'handle change: editor.selections {"selection":"[1,2 -> 1,2]","modelVersionId":2,"oldSelections":["[1,1 -> 1,1]"],"oldModelVersionId":1,"source":"keyboard","reason":0}',
142
'running derived: selection: [1,2 -> 1,2], value: 2'
143
]));
144
}
145
);
146
});
147
148
test('listener interaction ()', () => {
149
let derived: IObservable<string>;
150
let log: Log;
151
withEditorSetupTestFixture(
152
(editor, disposables) => {
153
disposables.add(
154
editor.onDidChangeModelContent(() => {
155
log.log('>>> before forceUpdate');
156
observableCodeEditor(editor).forceUpdate();
157
158
log.log('>>> before get');
159
derived.get();
160
log.log('<<< after get');
161
})
162
);
163
},
164
(args) => {
165
const editor = args.editor;
166
derived = args.derived;
167
log = args.log;
168
169
editor.trigger('keyboard', 'type', { text: 'a' });
170
171
assert.deepStrictEqual(log.getAndClearEntries(), ([
172
'>>> before forceUpdate',
173
'>>> before get',
174
'handle change: editor.versionId undefined',
175
'running derived: selection: [1,2 -> 1,2], value: 2',
176
'<<< after get',
177
'handle change: editor.onDidType "a"',
178
'handle change: editor.versionId {"changes":[{"range":"[1,1 -> 1,1]","rangeLength":0,"text":"a","rangeOffset":0}],"eol":"\\n","versionId":2,"detailedReasons":[{"metadata":{"source":"cursor","kind":"type","detailedSource":"keyboard"}}],"detailedReasonsChangeLengths":[1]}',
179
'handle change: editor.selections {"selection":"[1,2 -> 1,2]","modelVersionId":2,"oldSelections":["[1,1 -> 1,1]"],"oldModelVersionId":1,"source":"keyboard","reason":0}',
180
'running derived: selection: [1,2 -> 1,2], value: 2'
181
]));
182
}
183
);
184
});
185
});
186
187
class Log {
188
private readonly entries: string[] = [];
189
public log(message: string): void {
190
this.entries.push(message);
191
}
192
193
public getAndClearEntries(): string[] {
194
const entries = [...this.entries];
195
this.entries.length = 0;
196
return entries;
197
}
198
}
199
200
function formatChange(change: unknown) {
201
return JSON.stringify(
202
change,
203
(key, value) => {
204
if (value instanceof Range) {
205
return value.toString();
206
}
207
if (
208
value === false ||
209
(Array.isArray(value) && value.length === 0)
210
) {
211
return undefined;
212
}
213
return value;
214
}
215
);
216
}
217
218
function observableName(obs: IObservable<any>, obsEditor: ObservableCodeEditor): string {
219
switch (obs) {
220
case obsEditor.selections:
221
return 'editor.selections';
222
case obsEditor.versionId:
223
return 'editor.versionId';
224
case obsEditor.onDidType:
225
return 'editor.onDidType';
226
default:
227
return 'unknown';
228
}
229
}
230
231