Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/extension/inlineEdits/node/rebaseResult.ts
13399 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 type { MarkdownLoggable } from '../../../platform/inlineEdits/common/inlineEditLogContext';
7
import { StringEdit, StringReplacement } from '../../../util/vs/editor/common/core/edits/stringEdit';
8
import { OffsetRange } from '../../../util/vs/editor/common/core/ranges/offsetRange';
9
import { NesRebaseConfigs } from '../common/editRebase';
10
import { CachedOrRebasedEdit } from './nextEditCache';
11
12
export interface RebaseResult {
13
readonly edit: CachedOrRebasedEdit | undefined;
14
readonly failureInfo?: RebaseFailureInfo;
15
}
16
17
export class RebaseFailureInfo implements MarkdownLoggable {
18
constructor(
19
readonly originalDocument: string,
20
readonly editWindow: OffsetRange | undefined,
21
readonly originalEdits: readonly StringReplacement[],
22
readonly userEditSince: StringEdit,
23
readonly currentDocument: string,
24
readonly currentSelection: readonly OffsetRange[],
25
readonly nesRebaseConfigs: NesRebaseConfigs,
26
) { }
27
28
toMarkdown(): string {
29
const lines: string[] = [];
30
31
lines.push('### Original Document');
32
lines.push('```');
33
lines.push(this.originalDocument);
34
lines.push('```');
35
36
lines.push('');
37
lines.push('### Suggested Edits');
38
for (let i = 0; i < this.originalEdits.length; i++) {
39
const edit = this.originalEdits[i];
40
lines.push(`- **Edit ${i}**: \`${edit.toString()}\``);
41
lines.push(` - replaces: \`${JSON.stringify(edit.replaceRange.substring(this.originalDocument))}\``);
42
lines.push(` - with: \`${JSON.stringify(edit.newText)}\``);
43
}
44
45
if (this.editWindow) {
46
lines.push('');
47
lines.push(`### Edit Window: ${this.editWindow.toString()}`);
48
lines.push(`Content: \`${JSON.stringify(this.editWindow.substring(this.originalDocument))}\``);
49
}
50
51
lines.push('');
52
lines.push('### User Edit Since');
53
for (const replacement of this.userEditSince.replacements) {
54
lines.push(`- \`${replacement.toString()}\``);
55
lines.push(` - replaces: \`${JSON.stringify(replacement.replaceRange.substring(this.originalDocument))}\``);
56
lines.push(` - with: \`${JSON.stringify(replacement.newText)}\``);
57
}
58
59
lines.push('');
60
lines.push('### Current Document (after user edits)');
61
lines.push('```');
62
lines.push(this.currentDocument);
63
lines.push('```');
64
65
if (this.currentSelection.length > 0) {
66
lines.push('');
67
lines.push(`### Cursor: ${this.currentSelection.map(s => s.toString()).join(', ')}`);
68
}
69
70
lines.push('');
71
lines.push('### Document Intended After Suggested Edits');
72
lines.push('```');
73
try {
74
const intended = new StringEdit(this.originalEdits.slice()).apply(this.originalDocument);
75
lines.push(intended);
76
} catch {
77
lines.push('<could not compute>');
78
}
79
lines.push('```');
80
81
lines.push('');
82
lines.push('### Copy-Pasteable Test');
83
lines.push('```typescript');
84
lines.push(this._generateTest());
85
lines.push('```');
86
87
return lines.join('\n');
88
}
89
90
private _generateTest(): string {
91
const lines: string[] = [];
92
lines.push(`test('rebase failure (auto-generated)', () => {`);
93
lines.push(`\tconst originalDocument = ${toBacktickLiteral(this.originalDocument)};`);
94
95
lines.push('\tconst originalEdits = [');
96
for (const edit of this.originalEdits) {
97
lines.push(`\t\tStringReplacement.replace(new OffsetRange(${edit.replaceRange.start}, ${edit.replaceRange.endExclusive}), ${toBacktickLiteral(edit.newText)}),`);
98
}
99
lines.push('\t];');
100
101
lines.push('\tconst userEditSince = StringEdit.create([');
102
for (const replacement of this.userEditSince.replacements) {
103
lines.push(`\t\tStringReplacement.replace(new OffsetRange(${replacement.replaceRange.start}, ${replacement.replaceRange.endExclusive}), ${toBacktickLiteral(replacement.newText)}),`);
104
}
105
lines.push('\t]);');
106
107
lines.push(`\tconst currentDocumentContent = ${toBacktickLiteral(this.currentDocument)};`);
108
109
if (this.editWindow) {
110
lines.push(`\tconst editWindow = new OffsetRange(${this.editWindow.start}, ${this.editWindow.endExclusive});`);
111
} else {
112
lines.push('\tconst editWindow = undefined;');
113
}
114
115
lines.push(`\tconst currentSelection = [${this.currentSelection.map(s => `new OffsetRange(${s.start}, ${s.endExclusive})`).join(', ')}];`);
116
117
const configEntries: string[] = [];
118
if (this.nesRebaseConfigs.absorbSubsequenceTyping) {
119
configEntries.push(`absorbSubsequenceTyping: ${this.nesRebaseConfigs.absorbSubsequenceTyping}`);
120
}
121
if (this.nesRebaseConfigs.reverseAgreement) {
122
configEntries.push(`reverseAgreement: ${this.nesRebaseConfigs.reverseAgreement}`);
123
}
124
configEntries.push(`maxImperfectAgreementLength: ${this.nesRebaseConfigs.maxImperfectAgreementLength}`);
125
lines.push(`\tconst nesConfigs = { ${configEntries.join(', ')} };`);
126
127
lines.push('');
128
lines.push('\tconst logger = new TestLogService();');
129
lines.push('\texpect(userEditSince.apply(originalDocument)).toBe(currentDocumentContent);');
130
131
const configsArg = ', nesConfigs';
132
lines.push(`\texpect(tryRebase(originalDocument, editWindow, originalEdits, [], userEditSince, currentDocumentContent, currentSelection, 'strict', logger${configsArg})).toMatchInlineSnapshot();`);
133
134
lines.push('});');
135
136
return lines.join('\n');
137
}
138
}
139
140
function toBacktickLiteral(value: string): string {
141
const escaped = value.replace(/\\/g, '\\\\').replace(/`/g, '\\`').replace(/\$\{/g, '\\${');
142
return '`' + escaped + '`';
143
}
144
145