Path: blob/main/extensions/copilot/src/extension/inlineEdits/vscode-node/features/diagnosticsBasedCompletions/anyDiagnosticsCompletionProvider.ts
13406 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { CodeActionData } from '../../../../../platform/inlineEdits/common/dataTypes/codeActionData';6import { LanguageId } from '../../../../../platform/inlineEdits/common/dataTypes/languageId';7import { ILogger } from '../../../../../platform/log/common/logService';8import { CancellationToken } from '../../../../../util/vs/base/common/cancellation';9import { TextReplacement } from '../../../../../util/vs/editor/common/core/edits/textEdit';10import { Position } from '../../../../../util/vs/editor/common/core/position';11import { INextEditDisplayLocation } from '../../../node/nextEditResult';12import { IVSCodeObservableDocument } from '../../parts/vscodeWorkspace';13import { Diagnostic, DiagnosticCompletionItem, DiagnosticInlineEditRequestLogContext, IDiagnosticCodeAction, IDiagnosticCompletionProvider, isDiagnosticWithinDistance, log, logList } from './diagnosticsCompletions';1415interface IAnyCodeAction extends IDiagnosticCodeAction {16type: string;17}1819export class AnyDiagnosticCompletionItem extends DiagnosticCompletionItem {2021public readonly providerName = 'any';2223constructor(24codeAction: IAnyCodeAction,25diagnostic: Diagnostic,26private readonly _nextEditDisplayLabel: string | undefined,27workspaceDocument: IVSCodeObservableDocument,28) {29super(codeAction.type, diagnostic, codeAction.edit, workspaceDocument);30}3132protected override _getDisplayLocation(): INextEditDisplayLocation | undefined {33if (!this._nextEditDisplayLabel) {34return undefined;35}3637const transformer = this._workspaceDocument.value.get().getTransformer();38return { range: transformer.getRange(this.diagnostic.range), label: this._nextEditDisplayLabel };39}40}4142export class AnyDiagnosticCompletionProvider implements IDiagnosticCompletionProvider<AnyDiagnosticCompletionItem> {4344public static SupportedLanguages = new Set<string>(['*']);4546public readonly providerName = 'any';4748constructor(private readonly _logger: ILogger) { }4950public providesCompletionsForDiagnostic(workspaceDocument: IVSCodeObservableDocument, diagnostic: Diagnostic, language: LanguageId, pos: Position): boolean {51return isDiagnosticWithinDistance(workspaceDocument, diagnostic, pos, 5);52}5354async provideDiagnosticCompletionItem(workspaceDocument: IVSCodeObservableDocument, sortedDiagnostics: Diagnostic[], pos: Position, logContext: DiagnosticInlineEditRequestLogContext, token: CancellationToken): Promise<AnyDiagnosticCompletionItem | null> {5556for (const diagnostic of sortedDiagnostics) {57const availableCodeActions = await workspaceDocument.getCodeActions(diagnostic.range, 3, token);58if (availableCodeActions === undefined) {59log(`Fetching code actions likely timed out for \`${diagnostic.message}\``, logContext, this._logger);60continue;61}6263const codeActionsFixingCodeAction = availableCodeActions.filter(action => doesCodeActionFixDiagnostics(action, diagnostic));64if (codeActionsFixingCodeAction.length === 0) {65continue;66}6768logList(`Found the following code action which fix \`${diagnostic.message}\``, codeActionsFixingCodeAction, logContext, this._logger);6970const filteredCodeActionsWithEdit = filterCodeActions(codeActionsFixingCodeAction);7172if (filteredCodeActionsWithEdit.length === 0) {73continue;74}7576const codeAction = filteredCodeActionsWithEdit[0];77if (!codeAction.edits) { continue; }7879const joinedEdit = TextReplacement.joinReplacements(codeAction.edits, workspaceDocument.value.get());80const anyCodeAction: IAnyCodeAction = {81edit: joinedEdit,82type: getSanitizedCodeActionTitle(codeAction)83};8485let displayLocationLabel: string | undefined;86const editDistance = Math.abs(joinedEdit.range.startLineNumber - pos.lineNumber);87if (editDistance > 12) {88displayLocationLabel = codeAction.title;89}9091const item = new AnyDiagnosticCompletionItem(anyCodeAction, diagnostic, displayLocationLabel, workspaceDocument);92log(`Created Completion Item for diagnostic: ${diagnostic.message}: ${item.toLineEdit().toString()}`);93return item;94}9596return null;97}9899completionItemRejected(item: AnyDiagnosticCompletionItem): void { }100}101102function doesCodeActionFixDiagnostics(action: CodeActionData, diagnostic: Diagnostic): boolean {103return action.diagnostics.some(d => diagnostic.data.message === d.message && diagnostic.data.range.equals(d.range));104}105106function getSanitizedCodeActionTitle(action: CodeActionData): string {107return action.title.replace(/(["'])(.*?)\1/g, '$1...$1');108}109110function filterCodeActions(codeActionsWithEdit: CodeActionData[]): CodeActionData[] {111return codeActionsWithEdit.filter(action => {112const edit = action.edits;113if (!edit) { return false; }114115if (action.title === 'Infer parameter types from usage') {116if (edit.length === 0) { return false; }117if (edit.length === 1 && ['any', 'unknown', 'undefined'].some(e => edit[0].text.includes(e))) { return false; }118}119120return true;121});122}123124125