Path: blob/main/extensions/copilot/src/extension/conversation/vscode-node/feedbackCollection.ts
13399 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 * as vscode from 'vscode';6import { ConfigKey, IConfigurationService } from '../../../platform/configuration/common/configurationService';7import { TextDocumentSnapshot } from '../../../platform/editing/common/textDocumentSnapshot';8import { ILogService } from '../../../platform/log/common/logService';9import { IReviewService } from '../../../platform/review/common/reviewService';10import { CancellationToken } from '../../../util/vs/base/common/cancellation';11import { DisposableStore } from '../../../util/vs/base/common/lifecycle';12import * as path from '../../../util/vs/base/common/path';13import { IInstantiationService, ServicesAccessor } from '../../../util/vs/platform/instantiation/common/instantiation';14import { FeedbackGenerator } from '../../prompt/node/feedbackGenerator';15import { CurrentChange } from '../../prompts/node/feedback/currentChange';1617const maxRequests = 10;18const maxRequestsInterval = 5 * 60 * 1000;19const requestDelay = 10 * 1000;20let requestTimes: number[] = [];2122export function startFeedbackCollection(accessor: ServicesAccessor) {23const configurationService = accessor.get(IConfigurationService);24const reviewService = accessor.get(IReviewService);25const instantiationService = accessor.get(IInstantiationService);26const logService = accessor.get(ILogService);27const disposables = new DisposableStore();28const enabled = configurationService.getConfig(ConfigKey.Advanced.FeedbackOnChange);29if (!enabled) {30return disposables;31}32const collection = reviewService.getDiagnosticCollection();33const feedbackGenerator = instantiationService.createInstance(FeedbackGenerator);34disposables.add(vscode.workspace.onDidChangeTextDocument(async event => {35if (event.document.uri.scheme === 'file' && event.contentChanges.length && event.document === vscode.window.activeTextEditor?.document) {36try {37logService.warn('Document changed, delaying diagnostics request');38const version = event.document.version;39await new Promise(resolve => setTimeout(resolve, requestDelay));40if (version !== event.document.version) {41logService.warn('Skipping diagnostics request because the document has changed');42return;43}44const now = Date.now();45const before = now - maxRequestsInterval;46requestTimes = requestTimes.filter(t => t > before);47if (requestTimes.length >= maxRequests) {48logService.warn('Max requests reached, skipping diagnostics request');49return;50}51requestTimes.push(now);52logService.trace('Requesting diagnostics');5354const selection = vscode.window.activeTextEditor?.selection;5556// TODO: Use all changes in the current document.57const change = await instantiationService.invokeFunction(CurrentChange.getCurrentChange, event.document, selection.start);58if (!change) {59logService.trace('No change found in the current document at the current position.');60return [];61}6263const result = await feedbackGenerator.generateComments([64{65document: TextDocumentSnapshot.create(event.document),66relativeDocumentPath: path.basename(event.document.uri.fsPath),67change,68selection69}70], CancellationToken.None);71if (result.type === 'success') {72const diagnostics = result.comments.map(comment => new vscode.Diagnostic(comment.range, typeof comment.body === 'string' ? comment.body : comment.body.value, vscode.DiagnosticSeverity.Information));73collection.set(event.document.uri, diagnostics);74}75} catch (err) {76logService.error(err, 'Error generating diagnostics');77}78}79}));80disposables.add(vscode.workspace.onDidCloseTextDocument(doc => {81collection.set(doc.uri, undefined);82}));83return disposables;84}858687