Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/extension/inlineEdits/vscode-node/features/diagnosticsBasedCompletions/anyDiagnosticsCompletionProvider.ts
13406 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 { CodeActionData } from '../../../../../platform/inlineEdits/common/dataTypes/codeActionData';
7
import { LanguageId } from '../../../../../platform/inlineEdits/common/dataTypes/languageId';
8
import { ILogger } from '../../../../../platform/log/common/logService';
9
import { CancellationToken } from '../../../../../util/vs/base/common/cancellation';
10
import { TextReplacement } from '../../../../../util/vs/editor/common/core/edits/textEdit';
11
import { Position } from '../../../../../util/vs/editor/common/core/position';
12
import { INextEditDisplayLocation } from '../../../node/nextEditResult';
13
import { IVSCodeObservableDocument } from '../../parts/vscodeWorkspace';
14
import { Diagnostic, DiagnosticCompletionItem, DiagnosticInlineEditRequestLogContext, IDiagnosticCodeAction, IDiagnosticCompletionProvider, isDiagnosticWithinDistance, log, logList } from './diagnosticsCompletions';
15
16
interface IAnyCodeAction extends IDiagnosticCodeAction {
17
type: string;
18
}
19
20
export class AnyDiagnosticCompletionItem extends DiagnosticCompletionItem {
21
22
public readonly providerName = 'any';
23
24
constructor(
25
codeAction: IAnyCodeAction,
26
diagnostic: Diagnostic,
27
private readonly _nextEditDisplayLabel: string | undefined,
28
workspaceDocument: IVSCodeObservableDocument,
29
) {
30
super(codeAction.type, diagnostic, codeAction.edit, workspaceDocument);
31
}
32
33
protected override _getDisplayLocation(): INextEditDisplayLocation | undefined {
34
if (!this._nextEditDisplayLabel) {
35
return undefined;
36
}
37
38
const transformer = this._workspaceDocument.value.get().getTransformer();
39
return { range: transformer.getRange(this.diagnostic.range), label: this._nextEditDisplayLabel };
40
}
41
}
42
43
export class AnyDiagnosticCompletionProvider implements IDiagnosticCompletionProvider<AnyDiagnosticCompletionItem> {
44
45
public static SupportedLanguages = new Set<string>(['*']);
46
47
public readonly providerName = 'any';
48
49
constructor(private readonly _logger: ILogger) { }
50
51
public providesCompletionsForDiagnostic(workspaceDocument: IVSCodeObservableDocument, diagnostic: Diagnostic, language: LanguageId, pos: Position): boolean {
52
return isDiagnosticWithinDistance(workspaceDocument, diagnostic, pos, 5);
53
}
54
55
async provideDiagnosticCompletionItem(workspaceDocument: IVSCodeObservableDocument, sortedDiagnostics: Diagnostic[], pos: Position, logContext: DiagnosticInlineEditRequestLogContext, token: CancellationToken): Promise<AnyDiagnosticCompletionItem | null> {
56
57
for (const diagnostic of sortedDiagnostics) {
58
const availableCodeActions = await workspaceDocument.getCodeActions(diagnostic.range, 3, token);
59
if (availableCodeActions === undefined) {
60
log(`Fetching code actions likely timed out for \`${diagnostic.message}\``, logContext, this._logger);
61
continue;
62
}
63
64
const codeActionsFixingCodeAction = availableCodeActions.filter(action => doesCodeActionFixDiagnostics(action, diagnostic));
65
if (codeActionsFixingCodeAction.length === 0) {
66
continue;
67
}
68
69
logList(`Found the following code action which fix \`${diagnostic.message}\``, codeActionsFixingCodeAction, logContext, this._logger);
70
71
const filteredCodeActionsWithEdit = filterCodeActions(codeActionsFixingCodeAction);
72
73
if (filteredCodeActionsWithEdit.length === 0) {
74
continue;
75
}
76
77
const codeAction = filteredCodeActionsWithEdit[0];
78
if (!codeAction.edits) { continue; }
79
80
const joinedEdit = TextReplacement.joinReplacements(codeAction.edits, workspaceDocument.value.get());
81
const anyCodeAction: IAnyCodeAction = {
82
edit: joinedEdit,
83
type: getSanitizedCodeActionTitle(codeAction)
84
};
85
86
let displayLocationLabel: string | undefined;
87
const editDistance = Math.abs(joinedEdit.range.startLineNumber - pos.lineNumber);
88
if (editDistance > 12) {
89
displayLocationLabel = codeAction.title;
90
}
91
92
const item = new AnyDiagnosticCompletionItem(anyCodeAction, diagnostic, displayLocationLabel, workspaceDocument);
93
log(`Created Completion Item for diagnostic: ${diagnostic.message}: ${item.toLineEdit().toString()}`);
94
return item;
95
}
96
97
return null;
98
}
99
100
completionItemRejected(item: AnyDiagnosticCompletionItem): void { }
101
}
102
103
function doesCodeActionFixDiagnostics(action: CodeActionData, diagnostic: Diagnostic): boolean {
104
return action.diagnostics.some(d => diagnostic.data.message === d.message && diagnostic.data.range.equals(d.range));
105
}
106
107
function getSanitizedCodeActionTitle(action: CodeActionData): string {
108
return action.title.replace(/(["'])(.*?)\1/g, '$1...$1');
109
}
110
111
function filterCodeActions(codeActionsWithEdit: CodeActionData[]): CodeActionData[] {
112
return codeActionsWithEdit.filter(action => {
113
const edit = action.edits;
114
if (!edit) { return false; }
115
116
if (action.title === 'Infer parameter types from usage') {
117
if (edit.length === 0) { return false; }
118
if (edit.length === 1 && ['any', 'unknown', 'undefined'].some(e => edit[0].text.includes(e))) { return false; }
119
}
120
121
return true;
122
});
123
}
124
125