Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/notebook/test/browser/notebookStickyScroll.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 assert from 'assert';
7
import { Event } from '../../../../../base/common/event.js';
8
import { DisposableStore } from '../../../../../base/common/lifecycle.js';
9
import { mock } from '../../../../../base/test/common/mock.js';
10
import { assertSnapshot } from '../../../../../base/test/common/snapshot.js';
11
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js';
12
import { ILanguageFeaturesService } from '../../../../../editor/common/services/languageFeatures.js';
13
import { LanguageFeaturesService } from '../../../../../editor/common/services/languageFeaturesService.js';
14
import { TestInstantiationService } from '../../../../../platform/instantiation/test/common/instantiationServiceMock.js';
15
import { IEditorPaneSelectionChangeEvent } from '../../../../common/editor.js';
16
import { NotebookCellOutline } from '../../browser/contrib/outline/notebookOutline.js';
17
import { INotebookEditor, INotebookEditorPane } from '../../browser/notebookBrowser.js';
18
import { INotebookCellList } from '../../browser/view/notebookRenderingCommon.js';
19
import { OutlineEntry } from '../../browser/viewModel/OutlineEntry.js';
20
import { NotebookStickyLine, computeContent } from '../../browser/viewParts/notebookEditorStickyScroll.js';
21
import { CellKind } from '../../common/notebookCommon.js';
22
import { createNotebookCellList, setupInstantiationService, withTestNotebook } from './testNotebookEditor.js';
23
import { OutlineTarget } from '../../../../services/outline/browser/outline.js';
24
25
suite('NotebookEditorStickyScroll', () => {
26
let disposables: DisposableStore;
27
let instantiationService: TestInstantiationService;
28
29
const domNode: HTMLElement = document.createElement('div');
30
31
teardown(() => {
32
disposables.dispose();
33
});
34
35
const store = ensureNoDisposablesAreLeakedInTestSuite();
36
37
setup(() => {
38
disposables = new DisposableStore();
39
instantiationService = setupInstantiationService(disposables);
40
instantiationService.set(ILanguageFeaturesService, new LanguageFeaturesService());
41
});
42
43
function getOutline(editor: any) {
44
if (!editor.hasModel()) {
45
assert.ok(false, 'MUST have active text editor');
46
}
47
const outline = store.add(instantiationService.createInstance(NotebookCellOutline, new class extends mock<INotebookEditorPane>() {
48
override getControl() {
49
return editor;
50
}
51
override onDidChangeModel: Event<void> = Event.None;
52
override onDidChangeSelection: Event<IEditorPaneSelectionChangeEvent> = Event.None;
53
}, OutlineTarget.QuickPick));
54
return outline;
55
}
56
57
function nbStickyTestHelper(domNode: HTMLElement, notebookEditor: INotebookEditor, notebookCellList: INotebookCellList, notebookOutlineEntries: OutlineEntry[], disposables: Pick<DisposableStore, 'add'>) {
58
const output = computeContent(notebookEditor, notebookCellList, notebookOutlineEntries, 0);
59
for (const stickyLine of output.values()) {
60
disposables.add(stickyLine.line);
61
}
62
return createStickyTestElement(output.values());
63
}
64
65
function createStickyTestElement(stickyLines: IterableIterator<{ line: NotebookStickyLine; rendered: boolean }>) {
66
const outputElements = [];
67
for (const stickyLine of stickyLines) {
68
if (stickyLine.rendered) {
69
outputElements.unshift(stickyLine.line.element.innerText);
70
}
71
}
72
return outputElements;
73
}
74
75
test('test0: should render empty, scrollTop at 0', async function () {
76
await withTestNotebook(
77
[
78
['# header a', 'markdown', CellKind.Markup, [], {}],
79
['## header aa', 'markdown', CellKind.Markup, [], {}],
80
['var b = 1;', 'javascript', CellKind.Code, [], {}],
81
['var b = 1;', 'javascript', CellKind.Code, [], {}],
82
['var b = 1;', 'javascript', CellKind.Code, [], {}],
83
['var b = 1;', 'javascript', CellKind.Code, [], {}],
84
['# header b', 'markdown', CellKind.Markup, [], {}],
85
['var c = 2;', 'javascript', CellKind.Code, [], {}]
86
],
87
async (editor, viewModel) => {
88
viewModel.restoreEditorViewState({
89
editingCells: Array.from({ length: 8 }, () => false),
90
editorViewStates: Array.from({ length: 8 }, () => null),
91
cellTotalHeights: Array.from({ length: 8 }, () => 50),
92
cellLineNumberStates: {},
93
collapsedInputCells: {},
94
collapsedOutputCells: {},
95
});
96
97
const cellList = disposables.add(createNotebookCellList(instantiationService, disposables));
98
cellList.attachViewModel(viewModel);
99
cellList.layout(400, 100);
100
101
editor.setScrollTop(0);
102
editor.visibleRanges = [{ start: 0, end: 8 }];
103
104
const outline = getOutline(editor);
105
const notebookOutlineEntries = outline.entries;
106
const resultingMap = nbStickyTestHelper(domNode, editor, cellList, notebookOutlineEntries, disposables);
107
await assertSnapshot(resultingMap);
108
outline.dispose();
109
});
110
});
111
112
test('test1: should render 0->1, visible range 3->8', async function () {
113
await withTestNotebook(
114
[
115
['# header a', 'markdown', CellKind.Markup, [], {}], // 0
116
['## header aa', 'markdown', CellKind.Markup, [], {}], // 50
117
['var b = 1;', 'javascript', CellKind.Code, [], {}], // 100
118
['var b = 1;', 'javascript', CellKind.Code, [], {}], // 150
119
['var b = 1;', 'javascript', CellKind.Code, [], {}], // 200
120
['var b = 1;', 'javascript', CellKind.Code, [], {}], // 250
121
['# header b', 'markdown', CellKind.Markup, [], {}], // 300
122
['var c = 2;', 'javascript', CellKind.Code, [], {}] // 350
123
],
124
async (editor, viewModel, ds) => {
125
viewModel.restoreEditorViewState({
126
editingCells: Array.from({ length: 8 }, () => false),
127
editorViewStates: Array.from({ length: 8 }, () => null),
128
cellTotalHeights: Array.from({ length: 8 }, () => 50),
129
cellLineNumberStates: {},
130
collapsedInputCells: {},
131
collapsedOutputCells: {},
132
});
133
134
const cellList = ds.add(createNotebookCellList(instantiationService, ds));
135
cellList.attachViewModel(viewModel);
136
cellList.layout(400, 100);
137
138
editor.setScrollTop(175);
139
editor.visibleRanges = [{ start: 3, end: 8 }];
140
141
const outline = getOutline(editor);
142
const notebookOutlineEntries = outline.entries;
143
const resultingMap = nbStickyTestHelper(domNode, editor, cellList, notebookOutlineEntries, ds);
144
145
await assertSnapshot(resultingMap);
146
outline.dispose();
147
});
148
});
149
150
test('test2: should render 0, visible range 6->9 so collapsing next 2 against following section', async function () {
151
await withTestNotebook(
152
[
153
['# header a', 'markdown', CellKind.Markup, [], {}], // 0
154
['## header aa', 'markdown', CellKind.Markup, [], {}], // 50
155
['### header aaa', 'markdown', CellKind.Markup, [], {}],// 100
156
['var b = 1;', 'javascript', CellKind.Code, [], {}], // 150
157
['var b = 1;', 'javascript', CellKind.Code, [], {}], // 200
158
['var b = 1;', 'javascript', CellKind.Code, [], {}], // 250
159
['var b = 1;', 'javascript', CellKind.Code, [], {}], // 300
160
['# header b', 'markdown', CellKind.Markup, [], {}], // 350
161
['var c = 2;', 'javascript', CellKind.Code, [], {}] // 400
162
],
163
async (editor, viewModel, ds) => {
164
viewModel.restoreEditorViewState({
165
editingCells: Array.from({ length: 9 }, () => false),
166
editorViewStates: Array.from({ length: 9 }, () => null),
167
cellTotalHeights: Array.from({ length: 9 }, () => 50),
168
cellLineNumberStates: {},
169
collapsedInputCells: {},
170
collapsedOutputCells: {},
171
});
172
173
const cellList = ds.add(createNotebookCellList(instantiationService, ds));
174
cellList.attachViewModel(viewModel);
175
cellList.layout(400, 100);
176
177
editor.setScrollTop(325); // room for a single header
178
editor.visibleRanges = [{ start: 6, end: 9 }];
179
180
const outline = getOutline(editor);
181
const notebookOutlineEntries = outline.entries;
182
const resultingMap = nbStickyTestHelper(domNode, editor, cellList, notebookOutlineEntries, ds);
183
184
await assertSnapshot(resultingMap);
185
outline.dispose();
186
});
187
});
188
189
test('test3: should render 0->2, collapsing against equivalent level header', async function () {
190
await withTestNotebook(
191
[
192
['# header a', 'markdown', CellKind.Markup, [], {}], // 0
193
['## header aa', 'markdown', CellKind.Markup, [], {}], // 50
194
['### header aaa', 'markdown', CellKind.Markup, [], {}],// 100
195
['var b = 1;', 'javascript', CellKind.Code, [], {}], // 150
196
['### header aab', 'markdown', CellKind.Markup, [], {}],// 200
197
['var b = 1;', 'javascript', CellKind.Code, [], {}], // 250
198
['var b = 1;', 'javascript', CellKind.Code, [], {}], // 300
199
['var b = 1;', 'javascript', CellKind.Code, [], {}], // 350
200
['# header b', 'markdown', CellKind.Markup, [], {}], // 400
201
['var c = 2;', 'javascript', CellKind.Code, [], {}] // 450
202
],
203
async (editor, viewModel, ds) => {
204
viewModel.restoreEditorViewState({
205
editingCells: Array.from({ length: 10 }, () => false),
206
editorViewStates: Array.from({ length: 10 }, () => null),
207
cellTotalHeights: Array.from({ length: 10 }, () => 50),
208
cellLineNumberStates: {},
209
collapsedInputCells: {},
210
collapsedOutputCells: {},
211
});
212
213
const cellList = ds.add(createNotebookCellList(instantiationService, ds));
214
cellList.attachViewModel(viewModel);
215
cellList.layout(400, 100);
216
217
editor.setScrollTop(175); // room for a single header
218
editor.visibleRanges = [{ start: 3, end: 10 }];
219
220
const outline = getOutline(editor);
221
const notebookOutlineEntries = outline.entries;
222
const resultingMap = nbStickyTestHelper(domNode, editor, cellList, notebookOutlineEntries, ds);
223
224
await assertSnapshot(resultingMap);
225
outline.dispose();
226
});
227
});
228
229
// outdated/improper behavior
230
test('test4: should render 0, scrolltop halfway through cell 0', async function () {
231
await withTestNotebook(
232
[
233
['# header a', 'markdown', CellKind.Markup, [], {}],
234
['## header aa', 'markdown', CellKind.Markup, [], {}],
235
['var b = 1;', 'javascript', CellKind.Code, [], {}],
236
['var b = 1;', 'javascript', CellKind.Code, [], {}],
237
['var b = 1;', 'javascript', CellKind.Code, [], {}],
238
['var b = 1;', 'javascript', CellKind.Code, [], {}],
239
['# header b', 'markdown', CellKind.Markup, [], {}],
240
['var c = 2;', 'javascript', CellKind.Code, [], {}]
241
],
242
async (editor, viewModel, ds) => {
243
viewModel.restoreEditorViewState({
244
editingCells: Array.from({ length: 8 }, () => false),
245
editorViewStates: Array.from({ length: 8 }, () => null),
246
cellTotalHeights: Array.from({ length: 8 }, () => 50),
247
cellLineNumberStates: {},
248
collapsedInputCells: {},
249
collapsedOutputCells: {},
250
});
251
252
const cellList = ds.add(createNotebookCellList(instantiationService, ds));
253
cellList.attachViewModel(viewModel);
254
cellList.layout(400, 100);
255
256
editor.setScrollTop(50);
257
editor.visibleRanges = [{ start: 0, end: 8 }];
258
259
const outline = getOutline(editor);
260
const notebookOutlineEntries = outline.entries;
261
const resultingMap = nbStickyTestHelper(domNode, editor, cellList, notebookOutlineEntries, ds);
262
263
await assertSnapshot(resultingMap);
264
outline.dispose();
265
});
266
});
267
268
test('test5: should render 0->2, scrolltop halfway through cell 2', async function () {
269
await withTestNotebook(
270
[
271
['# header a', 'markdown', CellKind.Markup, [], {}],
272
['## header aa', 'markdown', CellKind.Markup, [], {}],
273
['### header aaa', 'markdown', CellKind.Markup, [], {}],
274
['#### header aaaa', 'markdown', CellKind.Markup, [], {}],
275
['var b = 1;', 'javascript', CellKind.Code, [], {}],
276
['var b = 1;', 'javascript', CellKind.Code, [], {}],
277
['var b = 1;', 'javascript', CellKind.Code, [], {}],
278
['var b = 1;', 'javascript', CellKind.Code, [], {}],
279
['# header b', 'markdown', CellKind.Markup, [], {}],
280
['var c = 2;', 'javascript', CellKind.Code, [], {}]
281
],
282
async (editor, viewModel, ds) => {
283
viewModel.restoreEditorViewState({
284
editingCells: Array.from({ length: 10 }, () => false),
285
editorViewStates: Array.from({ length: 10 }, () => null),
286
cellTotalHeights: Array.from({ length: 10 }, () => 50),
287
cellLineNumberStates: {},
288
collapsedInputCells: {},
289
collapsedOutputCells: {},
290
});
291
292
const cellList = ds.add(createNotebookCellList(instantiationService, ds));
293
cellList.attachViewModel(viewModel);
294
cellList.layout(400, 100);
295
296
editor.setScrollTop(125);
297
editor.visibleRanges = [{ start: 2, end: 10 }];
298
299
const outline = getOutline(editor);
300
const notebookOutlineEntries = outline.entries;
301
const resultingMap = nbStickyTestHelper(domNode, editor, cellList, notebookOutlineEntries, ds);
302
303
await assertSnapshot(resultingMap);
304
outline.dispose();
305
});
306
});
307
308
test('test6: should render 6->7, scrolltop halfway through cell 7', async function () {
309
await withTestNotebook(
310
[
311
['# header a', 'markdown', CellKind.Markup, [], {}],
312
['## header aa', 'markdown', CellKind.Markup, [], {}],
313
['var b = 1;', 'javascript', CellKind.Code, [], {}],
314
['var b = 1;', 'javascript', CellKind.Code, [], {}],
315
['var b = 1;', 'javascript', CellKind.Code, [], {}],
316
['var b = 1;', 'javascript', CellKind.Code, [], {}],
317
['# header b', 'markdown', CellKind.Markup, [], {}],
318
['## header bb', 'markdown', CellKind.Markup, [], {}],
319
['### header bbb', 'markdown', CellKind.Markup, [], {}],
320
['var c = 2;', 'javascript', CellKind.Code, [], {}]
321
],
322
async (editor, viewModel, ds) => {
323
viewModel.restoreEditorViewState({
324
editingCells: Array.from({ length: 10 }, () => false),
325
editorViewStates: Array.from({ length: 10 }, () => null),
326
cellTotalHeights: Array.from({ length: 10 }, () => 50),
327
cellLineNumberStates: {},
328
collapsedInputCells: {},
329
collapsedOutputCells: {},
330
});
331
332
const cellList = ds.add(createNotebookCellList(instantiationService, ds));
333
cellList.attachViewModel(viewModel);
334
cellList.layout(400, 100);
335
336
editor.setScrollTop(375);
337
editor.visibleRanges = [{ start: 7, end: 10 }];
338
339
const outline = getOutline(editor);
340
const notebookOutlineEntries = outline.entries;
341
const resultingMap = nbStickyTestHelper(domNode, editor, cellList, notebookOutlineEntries, ds);
342
343
await assertSnapshot(resultingMap);
344
outline.dispose();
345
});
346
});
347
348
test('test7: should render 0->1, collapsing against next section', async function () {
349
await withTestNotebook(
350
[
351
['# header a', 'markdown', CellKind.Markup, [], {}], //0
352
['## header aa', 'markdown', CellKind.Markup, [], {}], //50
353
['### header aaa', 'markdown', CellKind.Markup, [], {}], //100
354
['#### header aaaa', 'markdown', CellKind.Markup, [], {}], //150
355
['var b = 1;', 'javascript', CellKind.Code, [], {}], //200
356
['var b = 1;', 'javascript', CellKind.Code, [], {}], //250
357
['var b = 1;', 'javascript', CellKind.Code, [], {}], //300
358
['var b = 1;', 'javascript', CellKind.Code, [], {}], //350
359
['# header b', 'markdown', CellKind.Markup, [], {}], //400
360
['## header bb', 'markdown', CellKind.Markup, [], {}], //450
361
['### header bbb', 'markdown', CellKind.Markup, [], {}],
362
['var c = 2;', 'javascript', CellKind.Code, [], {}]
363
],
364
async (editor, viewModel, ds) => {
365
viewModel.restoreEditorViewState({
366
editingCells: Array.from({ length: 12 }, () => false),
367
editorViewStates: Array.from({ length: 12 }, () => null),
368
cellTotalHeights: Array.from({ length: 12 }, () => 50),
369
cellLineNumberStates: {},
370
collapsedInputCells: {},
371
collapsedOutputCells: {},
372
});
373
374
const cellList = ds.add(createNotebookCellList(instantiationService, ds));
375
cellList.attachViewModel(viewModel);
376
cellList.layout(400, 100);
377
378
editor.setScrollTop(350);
379
editor.visibleRanges = [{ start: 7, end: 12 }];
380
381
const outline = getOutline(editor);
382
const notebookOutlineEntries = outline.entries;
383
const resultingMap = nbStickyTestHelper(domNode, editor, cellList, notebookOutlineEntries, ds);
384
385
await assertSnapshot(resultingMap);
386
outline.dispose();
387
});
388
});
389
});
390
391