Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/editor/contrib/stickyScroll/test/browser/stickyScroll.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
import assert from 'assert';
6
import { withAsyncTestCodeEditor } from '../../../../test/browser/testCodeEditor.js';
7
import { StickyScrollController } from '../../browser/stickyScrollController.js';
8
import { ServiceCollection } from '../../../../../platform/instantiation/common/serviceCollection.js';
9
import { ILanguageFeaturesService } from '../../../../common/services/languageFeatures.js';
10
import { createTextModel } from '../../../../test/common/testTextModel.js';
11
import { LanguageFeaturesService } from '../../../../common/services/languageFeaturesService.js';
12
import { DocumentSymbol, SymbolKind } from '../../../../common/languages.js';
13
import { StickyLineCandidate, StickyLineCandidateProvider } from '../../browser/stickyScrollProvider.js';
14
import { EditorOption } from '../../../../common/config/editorOptions.js';
15
import { ILogService, NullLogService } from '../../../../../platform/log/common/log.js';
16
import { IContextMenuService } from '../../../../../platform/contextview/browser/contextView.js';
17
import { mock } from '../../../../../base/test/common/mock.js';
18
import { ILanguageConfigurationService } from '../../../../common/languages/languageConfigurationRegistry.js';
19
import { ILanguageFeatureDebounceService, LanguageFeatureDebounceService } from '../../../../common/services/languageFeatureDebounce.js';
20
import { TestLanguageConfigurationService } from '../../../../test/common/modes/testLanguageConfigurationService.js';
21
import { SyncDescriptor } from '../../../../../platform/instantiation/common/descriptors.js';
22
import { runWithFakedTimers } from '../../../../../base/test/common/timeTravelScheduler.js';
23
import { IEnvironmentService } from '../../../../../platform/environment/common/environment.js';
24
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js';
25
import { DisposableStore } from '../../../../../base/common/lifecycle.js';
26
27
suite('Sticky Scroll Tests', () => {
28
29
const disposables = new DisposableStore();
30
31
const serviceCollection = new ServiceCollection(
32
[ILanguageFeaturesService, new LanguageFeaturesService()],
33
[ILogService, new NullLogService()],
34
[IContextMenuService, new class extends mock<IContextMenuService>() { }],
35
[ILanguageConfigurationService, new TestLanguageConfigurationService()],
36
[IEnvironmentService, new class extends mock<IEnvironmentService>() {
37
override isBuilt: boolean = true;
38
override isExtensionDevelopment: boolean = false;
39
}],
40
[ILanguageFeatureDebounceService, new SyncDescriptor(LanguageFeatureDebounceService)],
41
);
42
43
const text = [
44
'function foo() {',
45
'',
46
'}',
47
'/* comment related to TestClass',
48
' end of the comment */',
49
'@classDecorator',
50
'class TestClass {',
51
'// comment related to the function functionOfClass',
52
'functionOfClass(){',
53
'function function1(){',
54
'}',
55
'}}',
56
'function bar() { function insideBar() {}',
57
'}'
58
].join('\n');
59
60
setup(() => {
61
disposables.clear();
62
});
63
teardown(() => {
64
disposables.clear();
65
});
66
67
ensureNoDisposablesAreLeakedInTestSuite();
68
69
function documentSymbolProviderForTestModel() {
70
return {
71
provideDocumentSymbols() {
72
return [
73
{
74
name: 'foo',
75
detail: 'foo',
76
kind: SymbolKind.Function,
77
tags: [],
78
range: { startLineNumber: 1, endLineNumber: 3, startColumn: 1, endColumn: 1 },
79
selectionRange: { startLineNumber: 1, endLineNumber: 1, startColumn: 1, endColumn: 1 }
80
} as DocumentSymbol,
81
{
82
name: 'TestClass',
83
detail: 'TestClass',
84
kind: SymbolKind.Class,
85
tags: [],
86
range: { startLineNumber: 4, endLineNumber: 12, startColumn: 1, endColumn: 1 },
87
selectionRange: { startLineNumber: 7, endLineNumber: 7, startColumn: 1, endColumn: 1 },
88
children: [
89
{
90
name: 'functionOfClass',
91
detail: 'functionOfClass',
92
kind: SymbolKind.Function,
93
tags: [],
94
range: { startLineNumber: 8, endLineNumber: 12, startColumn: 1, endColumn: 1 },
95
selectionRange: { startLineNumber: 9, endLineNumber: 9, startColumn: 1, endColumn: 1 },
96
children: [
97
{
98
name: 'function1',
99
detail: 'function1',
100
kind: SymbolKind.Function,
101
tags: [],
102
range: { startLineNumber: 10, endLineNumber: 11, startColumn: 1, endColumn: 1 },
103
selectionRange: { startLineNumber: 10, endLineNumber: 10, startColumn: 1, endColumn: 1 },
104
}
105
]
106
} as DocumentSymbol
107
]
108
} as DocumentSymbol,
109
{
110
name: 'bar',
111
detail: 'bar',
112
kind: SymbolKind.Function,
113
tags: [],
114
range: { startLineNumber: 13, endLineNumber: 14, startColumn: 1, endColumn: 1 },
115
selectionRange: { startLineNumber: 13, endLineNumber: 13, startColumn: 1, endColumn: 1 },
116
children: [
117
{
118
name: 'insideBar',
119
detail: 'insideBar',
120
kind: SymbolKind.Function,
121
tags: [],
122
range: { startLineNumber: 13, endLineNumber: 13, startColumn: 1, endColumn: 1 },
123
selectionRange: { startLineNumber: 13, endLineNumber: 13, startColumn: 1, endColumn: 1 },
124
} as DocumentSymbol
125
]
126
} as DocumentSymbol
127
];
128
}
129
};
130
}
131
132
test('Testing the function getCandidateStickyLinesIntersecting', () => {
133
return runWithFakedTimers({ useFakeTimers: true }, async () => {
134
const model = createTextModel(text);
135
await withAsyncTestCodeEditor(model, {
136
stickyScroll: {
137
enabled: true,
138
maxLineCount: 5,
139
defaultModel: 'outlineModel'
140
},
141
envConfig: {
142
outerHeight: 500
143
},
144
serviceCollection: serviceCollection
145
}, async (editor, _viewModel, instantiationService) => {
146
const languageService = instantiationService.get(ILanguageFeaturesService);
147
const languageConfigurationService = instantiationService.get(ILanguageConfigurationService);
148
disposables.add(languageService.documentSymbolProvider.register('*', documentSymbolProviderForTestModel()));
149
const provider: StickyLineCandidateProvider = new StickyLineCandidateProvider(editor, languageService, languageConfigurationService);
150
await provider.update();
151
assert.deepStrictEqual(provider.getCandidateStickyLinesIntersecting({ startLineNumber: 1, endLineNumber: 4 }), [new StickyLineCandidate(1, 2, 0, 19)]);
152
assert.deepStrictEqual(provider.getCandidateStickyLinesIntersecting({ startLineNumber: 8, endLineNumber: 10 }), [new StickyLineCandidate(7, 11, 0, 19), new StickyLineCandidate(9, 11, 19, 19), new StickyLineCandidate(10, 10, 38, 19)]);
153
assert.deepStrictEqual(provider.getCandidateStickyLinesIntersecting({ startLineNumber: 10, endLineNumber: 13 }), [new StickyLineCandidate(7, 11, 0, 19), new StickyLineCandidate(9, 11, 19, 19), new StickyLineCandidate(10, 10, 38, 19), new StickyLineCandidate(13, 13, 0, 19)]);
154
155
provider.dispose();
156
model.dispose();
157
});
158
});
159
});
160
161
test('issue #157180: Render the correct line corresponding to the scope definition', () => {
162
return runWithFakedTimers({ useFakeTimers: true }, async () => {
163
const model = createTextModel(text);
164
await withAsyncTestCodeEditor(model, {
165
stickyScroll: {
166
enabled: true,
167
maxLineCount: 5,
168
defaultModel: 'outlineModel'
169
},
170
envConfig: {
171
outerHeight: 500
172
},
173
serviceCollection
174
}, async (editor, _viewModel, instantiationService) => {
175
176
const stickyScrollController: StickyScrollController = editor.registerAndInstantiateContribution(StickyScrollController.ID, StickyScrollController);
177
const lineHeight: number = editor.getOption(EditorOption.lineHeight);
178
const languageService: ILanguageFeaturesService = instantiationService.get(ILanguageFeaturesService);
179
disposables.add(languageService.documentSymbolProvider.register('*', documentSymbolProviderForTestModel()));
180
await stickyScrollController.stickyScrollCandidateProvider.update();
181
let state;
182
183
editor.setScrollTop(1);
184
state = stickyScrollController.findScrollWidgetState();
185
assert.deepStrictEqual(state.startLineNumbers, [1]);
186
187
editor.setScrollTop(lineHeight + 1);
188
state = stickyScrollController.findScrollWidgetState();
189
assert.deepStrictEqual(state.startLineNumbers, [1]);
190
191
editor.setScrollTop(4 * lineHeight + 1);
192
state = stickyScrollController.findScrollWidgetState();
193
assert.deepStrictEqual(state.startLineNumbers, []);
194
195
editor.setScrollTop(8 * lineHeight + 1);
196
state = stickyScrollController.findScrollWidgetState();
197
assert.deepStrictEqual(state.startLineNumbers, [7, 9]);
198
199
editor.setScrollTop(9 * lineHeight + 1);
200
state = stickyScrollController.findScrollWidgetState();
201
assert.deepStrictEqual(state.startLineNumbers, [7, 9]);
202
203
editor.setScrollTop(10 * lineHeight + 1);
204
state = stickyScrollController.findScrollWidgetState();
205
assert.deepStrictEqual(state.startLineNumbers, [7]);
206
207
stickyScrollController.dispose();
208
stickyScrollController.stickyScrollCandidateProvider.dispose();
209
model.dispose();
210
});
211
});
212
});
213
214
test('issue #156268 : Do not reveal sticky lines when they are in a folded region ', () => {
215
return runWithFakedTimers({ useFakeTimers: true }, async () => {
216
const model = createTextModel(text);
217
await withAsyncTestCodeEditor(model, {
218
stickyScroll: {
219
enabled: true,
220
maxLineCount: 5,
221
defaultModel: 'outlineModel'
222
},
223
envConfig: {
224
outerHeight: 500
225
},
226
serviceCollection
227
}, async (editor, viewModel, instantiationService) => {
228
229
const stickyScrollController: StickyScrollController = editor.registerAndInstantiateContribution(StickyScrollController.ID, StickyScrollController);
230
const lineHeight = editor.getOption(EditorOption.lineHeight);
231
232
const languageService = instantiationService.get(ILanguageFeaturesService);
233
disposables.add(languageService.documentSymbolProvider.register('*', documentSymbolProviderForTestModel()));
234
await stickyScrollController.stickyScrollCandidateProvider.update();
235
editor.setHiddenAreas([{ startLineNumber: 2, endLineNumber: 2, startColumn: 1, endColumn: 1 }, { startLineNumber: 10, endLineNumber: 11, startColumn: 1, endColumn: 1 }]);
236
let state;
237
238
editor.setScrollTop(1);
239
state = stickyScrollController.findScrollWidgetState();
240
assert.deepStrictEqual(state.startLineNumbers, [1]);
241
242
editor.setScrollTop(lineHeight + 1);
243
state = stickyScrollController.findScrollWidgetState();
244
assert.deepStrictEqual(state.startLineNumbers, []);
245
246
editor.setScrollTop(6 * lineHeight + 1);
247
state = stickyScrollController.findScrollWidgetState();
248
assert.deepStrictEqual(state.startLineNumbers, [7, 9]);
249
250
editor.setScrollTop(7 * lineHeight + 1);
251
state = stickyScrollController.findScrollWidgetState();
252
assert.deepStrictEqual(state.startLineNumbers, [7]);
253
254
editor.setScrollTop(10 * lineHeight + 1);
255
state = stickyScrollController.findScrollWidgetState();
256
assert.deepStrictEqual(state.startLineNumbers, []);
257
258
stickyScrollController.dispose();
259
stickyScrollController.stickyScrollCandidateProvider.dispose();
260
model.dispose();
261
});
262
});
263
});
264
265
const textWithScopesWithSameStartingLines = [
266
'class TestClass { foo() {',
267
'function bar(){',
268
'',
269
'}}',
270
'}',
271
''
272
].join('\n');
273
274
function documentSymbolProviderForSecondTestModel() {
275
return {
276
provideDocumentSymbols() {
277
return [
278
{
279
name: 'TestClass',
280
detail: 'TestClass',
281
kind: SymbolKind.Class,
282
tags: [],
283
range: { startLineNumber: 1, endLineNumber: 5, startColumn: 1, endColumn: 1 },
284
selectionRange: { startLineNumber: 1, endLineNumber: 1, startColumn: 1, endColumn: 1 },
285
children: [
286
{
287
name: 'foo',
288
detail: 'foo',
289
kind: SymbolKind.Function,
290
tags: [],
291
range: { startLineNumber: 1, endLineNumber: 4, startColumn: 1, endColumn: 1 },
292
selectionRange: { startLineNumber: 1, endLineNumber: 1, startColumn: 1, endColumn: 1 },
293
children: [
294
{
295
name: 'bar',
296
detail: 'bar',
297
kind: SymbolKind.Function,
298
tags: [],
299
range: { startLineNumber: 2, endLineNumber: 4, startColumn: 1, endColumn: 1 },
300
selectionRange: { startLineNumber: 2, endLineNumber: 2, startColumn: 1, endColumn: 1 },
301
children: []
302
} as DocumentSymbol
303
]
304
} as DocumentSymbol,
305
]
306
} as DocumentSymbol
307
];
308
}
309
};
310
}
311
312
test('issue #159271 : render the correct widget state when the child scope starts on the same line as the parent scope', () => {
313
return runWithFakedTimers({ useFakeTimers: true }, async () => {
314
const model = createTextModel(textWithScopesWithSameStartingLines);
315
await withAsyncTestCodeEditor(model, {
316
stickyScroll: {
317
enabled: true,
318
maxLineCount: 5,
319
defaultModel: 'outlineModel'
320
},
321
envConfig: {
322
outerHeight: 500
323
},
324
serviceCollection
325
}, async (editor, _viewModel, instantiationService) => {
326
327
const stickyScrollController: StickyScrollController = editor.registerAndInstantiateContribution(StickyScrollController.ID, StickyScrollController);
328
await stickyScrollController.stickyScrollCandidateProvider.update();
329
const lineHeight = editor.getOption(EditorOption.lineHeight);
330
331
const languageService = instantiationService.get(ILanguageFeaturesService);
332
disposables.add(languageService.documentSymbolProvider.register('*', documentSymbolProviderForSecondTestModel()));
333
await stickyScrollController.stickyScrollCandidateProvider.update();
334
let state;
335
336
editor.setScrollTop(1);
337
state = stickyScrollController.findScrollWidgetState();
338
assert.deepStrictEqual(state.startLineNumbers, [1, 2]);
339
340
editor.setScrollTop(lineHeight + 1);
341
state = stickyScrollController.findScrollWidgetState();
342
assert.deepStrictEqual(state.startLineNumbers, [1, 2]);
343
344
editor.setScrollTop(2 * lineHeight + 1);
345
state = stickyScrollController.findScrollWidgetState();
346
assert.deepStrictEqual(state.startLineNumbers, [1]);
347
348
editor.setScrollTop(3 * lineHeight + 1);
349
state = stickyScrollController.findScrollWidgetState();
350
assert.deepStrictEqual(state.startLineNumbers, [1]);
351
352
editor.setScrollTop(4 * lineHeight + 1);
353
state = stickyScrollController.findScrollWidgetState();
354
assert.deepStrictEqual(state.startLineNumbers, []);
355
356
stickyScrollController.dispose();
357
stickyScrollController.stickyScrollCandidateProvider.dispose();
358
model.dispose();
359
});
360
});
361
});
362
});
363
364