Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/extension/inlineEdits/test/node/nextEditCacheCursorDistance.spec.ts
13405 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, beforeEach, describe, it } from 'vitest';
6
import { ConfigKey } from '../../../../platform/configuration/common/configurationService';
7
import { DefaultsOnlyConfigurationService } from '../../../../platform/configuration/common/defaultsOnlyConfigurationService';
8
import { InMemoryConfigurationService } from '../../../../platform/configuration/test/common/inMemoryConfigurationService';
9
import { DocumentId } from '../../../../platform/inlineEdits/common/dataTypes/documentId';
10
import { InlineEditRequestLogContext } from '../../../../platform/inlineEdits/common/inlineEditLogContext';
11
import { MutableObservableWorkspace } from '../../../../platform/inlineEdits/common/observableWorkspace';
12
import { LogServiceImpl } from '../../../../platform/log/common/logService';
13
import { NullExperimentationService } from '../../../../platform/telemetry/common/nullExperimentationService';
14
import { URI } from '../../../../util/vs/base/common/uri';
15
import { generateUuid } from '../../../../util/vs/base/common/uuid';
16
import { StringReplacement } from '../../../../util/vs/editor/common/core/edits/stringEdit';
17
import { Position } from '../../../../util/vs/editor/common/core/position';
18
import { OffsetRange } from '../../../../util/vs/editor/common/core/ranges/offsetRange';
19
import { StringText } from '../../../../util/vs/editor/common/core/text/abstractText';
20
import { NextEditCache } from '../../node/nextEditCache';
21
import { NextEditFetchRequest } from '../../node/nextEditProvider';
22
23
describe('NextEditCache cursor distance check', () => {
24
25
let configService: InMemoryConfigurationService;
26
let obsWorkspace: MutableObservableWorkspace;
27
let logService: LogServiceImpl;
28
let expService: NullExperimentationService;
29
let cache: NextEditCache;
30
let docId: DocumentId;
31
32
// A multi-line document:
33
// Line 1: "line1"
34
// Line 2: "line2"
35
// ...
36
// Line 10: "line10"
37
const docContent = Array.from({ length: 10 }, (_, i) => `line${i + 1}`).join('\n');
38
const docText = new StringText(docContent);
39
40
function makeSource(): NextEditFetchRequest {
41
const logContext = new InlineEditRequestLogContext('test', 0, undefined);
42
return new NextEditFetchRequest(generateUuid(), logContext, undefined, false);
43
}
44
45
/** Get the offset of the start of a 1-indexed line in docContent. */
46
function lineStartOffset(lineNumber: number): number {
47
return docText.getTransformer().getOffset(new Position(lineNumber, 1));
48
}
49
50
function cursorAtLine(lineNumber: number): OffsetRange[] {
51
const offset = lineStartOffset(lineNumber);
52
return [new OffsetRange(offset, offset)];
53
}
54
55
beforeEach(() => {
56
configService = new InMemoryConfigurationService(new DefaultsOnlyConfigurationService());
57
obsWorkspace = new MutableObservableWorkspace();
58
logService = new LogServiceImpl([]);
59
expService = new NullExperimentationService();
60
61
docId = DocumentId.create(URI.file('/test/cursor-distance.ts').toString());
62
obsWorkspace.addDocument({ id: docId, initialValue: docContent });
63
64
cache = new NextEditCache(obsWorkspace, logService, configService, expService);
65
});
66
67
// Edit targets line 6 (replaces "line6" with "REPLACED")
68
const editStartOffset = docContent.indexOf('line6');
69
const editEndOffset = editStartOffset + 'line6'.length;
70
const edit = new StringReplacement(new OffsetRange(editStartOffset, editEndOffset), 'REPLACED');
71
72
function cacheEditWithCursorAtLine(cursorLine: number) {
73
cache.setKthNextEdit(
74
docId,
75
docText,
76
undefined, // editWindow
77
edit,
78
0, // subsequentN
79
undefined, // nextEdits
80
undefined, // userEditSince
81
makeSource(),
82
{ isFromCursorJump: false, cursorOffset: lineStartOffset(cursorLine) },
83
);
84
}
85
86
describe('when flag is disabled (default)', () => {
87
it('serves cached edit regardless of cursor distance', () => {
88
// Cache edit with cursor on line 5 (1 line away from edit on line 6)
89
cacheEditWithCursorAtLine(5);
90
91
// Move cursor to line 1 (5 lines away — farther)
92
const result = cache.lookupNextEdit(docId, docText, cursorAtLine(1));
93
assert(result?.edit, 'should serve cached edit when flag is off');
94
});
95
});
96
97
describe('when flag is enabled', () => {
98
beforeEach(async () => {
99
await configService.setConfig(ConfigKey.TeamInternal.InlineEditsCacheCursorDistanceCheck, true);
100
});
101
102
it('serves cached edit when cursor moves closer to the edit', () => {
103
// Cache edit with cursor on line 4 (2 lines away from edit on line 6)
104
cacheEditWithCursorAtLine(4);
105
106
// Move cursor to line 5 (1 line away — closer)
107
const result = cache.lookupNextEdit(docId, docText, cursorAtLine(5));
108
assert(result?.edit, 'should serve cached edit when cursor is closer');
109
});
110
111
it('serves cached edit when cursor stays at the same distance', () => {
112
// Cache edit with cursor on line 4 (2 lines away from edit on line 6)
113
cacheEditWithCursorAtLine(4);
114
115
// Move cursor to line 8 (also 2 lines away — same distance, other side)
116
const result = cache.lookupNextEdit(docId, docText, cursorAtLine(8));
117
assert(result?.edit, 'should serve cached edit at equal distance');
118
});
119
120
it('rejects cached edit when cursor moves farther from the edit', () => {
121
// Cache edit with cursor on line 5 (1 line away from edit on line 6)
122
cacheEditWithCursorAtLine(5);
123
124
// Move cursor to line 1 (5 lines away — farther)
125
const result = cache.lookupNextEdit(docId, docText, cursorAtLine(1));
126
assert(result?.rejected === true, 'should return cached edit marked as rejected');
127
});
128
129
it('marks the cached edit as rejected when cursor moves farther', () => {
130
// Cache edit with cursor on line 5 (1 line away from edit on line 6)
131
cacheEditWithCursorAtLine(5);
132
133
// Move cursor to line 1 (5 lines away — farther) — triggers rejection
134
cache.lookupNextEdit(docId, docText, cursorAtLine(1));
135
136
// Now even looking up from the original close position should show rejected
137
const result = cache.lookupNextEdit(docId, docText, cursorAtLine(5));
138
assert(result?.rejected === true, 'cached edit should be marked as rejected');
139
});
140
141
it('does not apply to subsequent edits (subsequentN > 0)', () => {
142
// Cache a subsequent edit (subsequentN = 1) with cursor on line 5
143
cache.setKthNextEdit(
144
docId,
145
docText,
146
undefined,
147
edit,
148
1, // subsequentN > 0
149
undefined,
150
undefined,
151
makeSource(),
152
{ isFromCursorJump: false, cursorOffset: lineStartOffset(5) },
153
);
154
155
// Move cursor to line 1 (farther) — should still serve because it's subsequent
156
const result = cache.lookupNextEdit(docId, docText, cursorAtLine(1));
157
assert(result?.edit, 'subsequent edits should not be filtered by cursor distance');
158
});
159
160
it('does not apply when cursorOffsetAtCacheTime is not set', () => {
161
// Cache edit without cursor offset
162
cache.setKthNextEdit(
163
docId,
164
docText,
165
undefined,
166
edit,
167
0,
168
undefined,
169
undefined,
170
makeSource(),
171
{ isFromCursorJump: false }, // no cursorOffset
172
);
173
174
// Move cursor to line 1 (farther) — should still serve because no cursor offset recorded
175
const result = cache.lookupNextEdit(docId, docText, cursorAtLine(1));
176
assert(result?.edit, 'should serve when no cursor offset was recorded at cache time');
177
});
178
179
it('serves cached edit when cursor is on the same line as the edit', () => {
180
// Cache edit with cursor on line 4 (2 lines away from edit on line 6)
181
cacheEditWithCursorAtLine(4);
182
183
// Move cursor to line 6 (0 lines away — on the edit itself)
184
const result = cache.lookupNextEdit(docId, docText, cursorAtLine(6));
185
assert(result?.edit, 'should serve cached edit when cursor is on the edit line');
186
});
187
});
188
});
189
190