Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/editor/contrib/inlineCompletions/test/browser/longDistanceWidgetPlacement.test.ts
4798 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 { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js';
8
import { Size2D } from '../../../../common/core/2d/size.js';
9
import { LineRange } from '../../../../common/core/ranges/lineRange.js';
10
import { WidgetLayoutConstants, WidgetPlacementContext, ContinuousLineSizes } from '../../browser/view/inlineEdits/inlineEditsViews/longDistanceHint/longDistnaceWidgetPlacement.js';
11
12
suite('WidgetPlacementContext', () => {
13
ensureNoDisposablesAreLeakedInTestSuite();
14
15
function createLineRangeInfo(startLine: number, sizes: Size2D[], top: number = 0): ContinuousLineSizes {
16
return {
17
lineRange: LineRange.ofLength(startLine, sizes.length),
18
top,
19
sizes,
20
};
21
}
22
23
const defaultLayoutConstants: WidgetLayoutConstants = {
24
previewEditorMargin: 5,
25
widgetPadding: 2,
26
widgetBorder: 1,
27
lowerBarHeight: 10,
28
minWidgetWidth: 50,
29
};
30
31
suite('constructor - availableSpaceSizes computation', () => {
32
test('computes available space sizes correctly with no padding', () => {
33
const sizes = [new Size2D(100, 20), new Size2D(150, 20), new Size2D(80, 20)];
34
const lineRangeInfo = createLineRangeInfo(1, sizes);
35
const editorTrueContentWidth = 500;
36
const endOfLinePadding = () => 0;
37
38
const context = new WidgetPlacementContext(lineRangeInfo, editorTrueContentWidth, endOfLinePadding);
39
40
assert.strictEqual(context.availableSpaceSizes.length, 3);
41
assert.strictEqual(context.availableSpaceSizes[0].width, 400); // 500 - 100
42
assert.strictEqual(context.availableSpaceSizes[1].width, 350); // 500 - 150
43
assert.strictEqual(context.availableSpaceSizes[2].width, 420); // 500 - 80
44
});
45
46
test('computes available space sizes with end of line padding', () => {
47
const sizes = [new Size2D(100, 20), new Size2D(150, 20)];
48
const lineRangeInfo = createLineRangeInfo(1, sizes);
49
const editorTrueContentWidth = 500;
50
const endOfLinePadding = (lineNumber: number) => lineNumber * 10;
51
52
const context = new WidgetPlacementContext(lineRangeInfo, editorTrueContentWidth, endOfLinePadding);
53
54
assert.strictEqual(context.availableSpaceSizes[0].width, 390); // 500 - 100 - 10
55
assert.strictEqual(context.availableSpaceSizes[1].width, 330); // 500 - 150 - 20
56
});
57
58
test('available space width is never negative', () => {
59
const sizes = [new Size2D(600, 20)];
60
const lineRangeInfo = createLineRangeInfo(1, sizes);
61
const editorTrueContentWidth = 500;
62
const endOfLinePadding = () => 0;
63
64
const context = new WidgetPlacementContext(lineRangeInfo, editorTrueContentWidth, endOfLinePadding);
65
66
assert.strictEqual(context.availableSpaceSizes[0].width, 0);
67
});
68
69
test('preserves heights in available space sizes', () => {
70
const sizes = [new Size2D(100, 25), new Size2D(100, 30), new Size2D(100, 20)];
71
const lineRangeInfo = createLineRangeInfo(1, sizes);
72
const editorTrueContentWidth = 500;
73
const endOfLinePadding = () => 0;
74
75
const context = new WidgetPlacementContext(lineRangeInfo, editorTrueContentWidth, endOfLinePadding);
76
77
assert.strictEqual(context.availableSpaceSizes[0].height, 25);
78
assert.strictEqual(context.availableSpaceSizes[1].height, 30);
79
assert.strictEqual(context.availableSpaceSizes[2].height, 20);
80
});
81
});
82
83
suite('constructor - prefix sums computation', () => {
84
test('computes height prefix sums correctly', () => {
85
const sizes = [new Size2D(100, 20), new Size2D(100, 30), new Size2D(100, 25)];
86
const lineRangeInfo = createLineRangeInfo(1, sizes);
87
88
const context = new WidgetPlacementContext(lineRangeInfo, 500, () => 0);
89
90
assert.deepStrictEqual(context.availableSpaceHeightPrefixSums, [0, 20, 50, 75]);
91
});
92
93
test('prefix sums start with 0 and have length = sizes.length + 1', () => {
94
const sizes = [new Size2D(100, 10), new Size2D(100, 20)];
95
const lineRangeInfo = createLineRangeInfo(1, sizes);
96
97
const context = new WidgetPlacementContext(lineRangeInfo, 500, () => 0);
98
99
assert.strictEqual(context.availableSpaceHeightPrefixSums[0], 0);
100
assert.strictEqual(context.availableSpaceHeightPrefixSums.length, 3);
101
});
102
});
103
104
suite('constructor - transposed sizes', () => {
105
test('transposes width and height correctly', () => {
106
const sizes = [new Size2D(100, 20), new Size2D(150, 30)];
107
const lineRangeInfo = createLineRangeInfo(1, sizes);
108
109
const context = new WidgetPlacementContext(lineRangeInfo, 500, () => 0);
110
111
// Transposed: width becomes height and vice versa
112
// Available widths are 400 and 350, heights are 20 and 30
113
assert.strictEqual(context.availableSpaceSizesTransposed[0].width, 20);
114
assert.strictEqual(context.availableSpaceSizesTransposed[0].height, 400);
115
assert.strictEqual(context.availableSpaceSizesTransposed[1].width, 30);
116
assert.strictEqual(context.availableSpaceSizesTransposed[1].height, 350);
117
});
118
});
119
120
suite('getWidgetVerticalOutline', () => {
121
test('computes vertical outline for first line', () => {
122
const sizes = [new Size2D(100, 20), new Size2D(100, 20)];
123
const lineRangeInfo = createLineRangeInfo(1, sizes, 100);
124
125
const context = new WidgetPlacementContext(lineRangeInfo, 500, () => 0);
126
const outline = context.getWidgetVerticalOutline(1, 50, defaultLayoutConstants);
127
128
// previewEditorMargin + widgetPadding + widgetBorder = 5 + 2 + 1 = 8
129
// editorRange = [100, 150)
130
// verticalWidgetRange = [100 - 8, 150 + 8 + 10) = [92, 168)
131
assert.strictEqual(outline.start, 92);
132
assert.strictEqual(outline.endExclusive, 168);
133
});
134
135
test('computes vertical outline for second line', () => {
136
const sizes = [new Size2D(100, 20), new Size2D(100, 25)];
137
const lineRangeInfo = createLineRangeInfo(1, sizes, 100);
138
139
const context = new WidgetPlacementContext(lineRangeInfo, 500, () => 0);
140
const outline = context.getWidgetVerticalOutline(2, 50, defaultLayoutConstants);
141
142
// Line 2 is at index 1, prefixSum[1] = 20
143
// top = 100 + 20 = 120
144
// editorRange = [120, 170)
145
// margin = 8, lowerBarHeight = 10
146
// verticalWidgetRange = [120 - 8, 170 + 8 + 10) = [112, 188)
147
assert.strictEqual(outline.start, 112);
148
assert.strictEqual(outline.endExclusive, 188);
149
});
150
151
test('works with zero margins', () => {
152
const sizes = [new Size2D(100, 20)];
153
const lineRangeInfo = createLineRangeInfo(1, sizes, 0);
154
const zeroConstants: WidgetLayoutConstants = {
155
previewEditorMargin: 0,
156
widgetPadding: 0,
157
widgetBorder: 0,
158
lowerBarHeight: 0,
159
minWidgetWidth: 50,
160
};
161
162
const context = new WidgetPlacementContext(lineRangeInfo, 500, () => 0);
163
const outline = context.getWidgetVerticalOutline(1, 50, zeroConstants);
164
165
assert.strictEqual(outline.start, 0);
166
assert.strictEqual(outline.endExclusive, 50);
167
});
168
});
169
170
suite('tryFindWidgetOutline', () => {
171
test('returns undefined when no line has enough width', () => {
172
// All lines have content that leaves less than minWidgetWidth
173
const sizes = [new Size2D(460, 20), new Size2D(470, 20), new Size2D(480, 20)];
174
const lineRangeInfo = createLineRangeInfo(1, sizes);
175
176
const context = new WidgetPlacementContext(lineRangeInfo, 500, () => 0);
177
const result = context.tryFindWidgetOutline(2, 15, 500, defaultLayoutConstants);
178
179
assert.strictEqual(result, undefined);
180
});
181
182
test('finds widget outline on target line when it has enough space', () => {
183
const sizes = [new Size2D(100, 20), new Size2D(100, 20), new Size2D(100, 20)];
184
const lineRangeInfo = createLineRangeInfo(1, sizes, 0);
185
186
const context = new WidgetPlacementContext(lineRangeInfo, 500, () => 0);
187
const result = context.tryFindWidgetOutline(2, 15, 500, defaultLayoutConstants);
188
189
assert.ok(result !== undefined);
190
assert.ok(result.horizontalWidgetRange.length >= defaultLayoutConstants.minWidgetWidth);
191
});
192
193
test('searches outward from target line', () => {
194
// First and last lines are excluded from placement
195
// Lines 2, 3 have no space, line 4 has space
196
const sizes = [
197
new Size2D(100, 20), // line 1 - excluded (first)
198
new Size2D(460, 20), // line 2 - no space
199
new Size2D(460, 20), // line 3 - no space (target)
200
new Size2D(100, 20), // line 4 - has space
201
new Size2D(100, 20), // line 5 - has space
202
new Size2D(100, 20), // line 6 - has space
203
new Size2D(100, 20), // line 7 - excluded (last)
204
];
205
const lineRangeInfo = createLineRangeInfo(1, sizes, 0);
206
207
const context = new WidgetPlacementContext(lineRangeInfo, 500, () => 0);
208
// Target is line 3, but it should find line 4 (searching outward)
209
const result = context.tryFindWidgetOutline(3, 15, 500, defaultLayoutConstants);
210
211
assert.ok(result !== undefined);
212
});
213
214
test('prefers closer lines to target', () => {
215
const sizes = [
216
new Size2D(100, 20), // line 0 - excluded (first)
217
new Size2D(100, 20), // line 1 - has space
218
new Size2D(100, 20), // line 2 - has space
219
new Size2D(100, 20), // line 3 - has space
220
new Size2D(500, 9999),// line 4 - no space (target)
221
new Size2D(100, 20), // line 5 - has space
222
new Size2D(100, 20), // line 6 - has space
223
new Size2D(100, 20), // line 7 - has space
224
new Size2D(100, 20), // line 8 - excluded (last)
225
];
226
const lineRangeInfo = createLineRangeInfo(1, sizes, 0);
227
228
for (let targetLine = 0; targetLine <= 4; targetLine++) {
229
const context = new WidgetPlacementContext(lineRangeInfo, 500, () => 0);
230
const result = context.tryFindWidgetOutline(targetLine, 15, 500, defaultLayoutConstants);
231
assert.ok(result !== undefined);
232
assert.ok(result.verticalWidgetRange.endExclusive < 9999);
233
}
234
235
for (let targetLine = 5; targetLine <= 10 /* test outside line range */; targetLine++) {
236
const context = new WidgetPlacementContext(lineRangeInfo, 500, () => 0);
237
const result = context.tryFindWidgetOutline(targetLine, 15, 500, defaultLayoutConstants);
238
assert.ok(result !== undefined);
239
assert.ok(result.verticalWidgetRange.start > 9999);
240
}
241
});
242
243
test('horizontal widget range ends at editor content right', () => {
244
const sizes = [new Size2D(100, 20), new Size2D(100, 20), new Size2D(100, 20)];
245
const lineRangeInfo = createLineRangeInfo(1, sizes, 0);
246
const editorTrueContentRight = 500;
247
248
const context = new WidgetPlacementContext(lineRangeInfo, 500, () => 0);
249
const result = context.tryFindWidgetOutline(2, 15, editorTrueContentRight, defaultLayoutConstants);
250
251
assert.ok(result !== undefined);
252
assert.strictEqual(result.horizontalWidgetRange.endExclusive, editorTrueContentRight);
253
});
254
});
255
256
suite('edge cases', () => {
257
test('handles single line range', () => {
258
const sizes = [new Size2D(100, 20)];
259
const lineRangeInfo = createLineRangeInfo(5, sizes, 50);
260
261
const context = new WidgetPlacementContext(lineRangeInfo, 500, () => 0);
262
263
assert.strictEqual(context.availableSpaceSizes.length, 1);
264
assert.deepStrictEqual(context.availableSpaceHeightPrefixSums, [0, 20]);
265
});
266
267
test('handles empty content lines (width 0)', () => {
268
const sizes = [new Size2D(0, 20), new Size2D(0, 20)];
269
const lineRangeInfo = createLineRangeInfo(1, sizes);
270
271
const context = new WidgetPlacementContext(lineRangeInfo, 500, () => 0);
272
273
assert.strictEqual(context.availableSpaceSizes[0].width, 500);
274
assert.strictEqual(context.availableSpaceSizes[1].width, 500);
275
});
276
277
test('handles varying line heights', () => {
278
const sizes = [new Size2D(100, 10), new Size2D(100, 30), new Size2D(100, 20)];
279
const lineRangeInfo = createLineRangeInfo(1, sizes, 100);
280
281
const context = new WidgetPlacementContext(lineRangeInfo, 500, () => 0);
282
283
// Verify prefix sums account for varying heights
284
assert.deepStrictEqual(context.availableSpaceHeightPrefixSums, [0, 10, 40, 60]);
285
});
286
287
test('handles very large line numbers', () => {
288
const sizes = [new Size2D(100, 20)];
289
const lineRangeInfo = createLineRangeInfo(10000, sizes, 0);
290
291
const context = new WidgetPlacementContext(lineRangeInfo, 500, () => 0);
292
293
const outline = context.getWidgetVerticalOutline(10000, 50, defaultLayoutConstants);
294
assert.ok(outline !== undefined);
295
});
296
});
297
});
298
299