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
3296 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