mirror of
https://github.com/microsoft/vscode.git
synced 2026-06-01 04:26:50 -05:00
Remove test generation commands and related configurations from Copilot Chat (#3937)
* Remove test generation commands and related configurations from Copilot Chat * Remove unused TestGenLensContribution import from contributions.ts
This commit is contained in:
1
extensions/copilot/.vscode/settings.json
vendored
1
extensions/copilot/.vscode/settings.json
vendored
@@ -138,7 +138,6 @@
|
||||
"scoredEdits.w.json": "https://microsoft.github.io/vscode-workbench-recorder-viewer/?editRating",
|
||||
"workspaceRecording.jsonl": "https://microsoft.github.io/vscode-workbench-recorder-viewer/?jsonl=true",
|
||||
},
|
||||
"github.copilot.chat.generateTests.codeLens": true,
|
||||
"explorer.fileNesting.patterns": {
|
||||
"vscode.d.ts": "vscode.proposed.*.ts",
|
||||
},
|
||||
|
||||
@@ -2349,18 +2349,6 @@
|
||||
"enablement": "!github.copilot.interactiveSession.disabled && !editorReadonly",
|
||||
"category": "Chat"
|
||||
},
|
||||
{
|
||||
"command": "github.copilot.chat.generateDocs",
|
||||
"title": "%github.copilot.command.generateDocs%",
|
||||
"enablement": "!github.copilot.interactiveSession.disabled && !editorReadonly",
|
||||
"category": "Chat"
|
||||
},
|
||||
{
|
||||
"command": "github.copilot.chat.generateTests",
|
||||
"title": "%github.copilot.command.generateTests%",
|
||||
"enablement": "!github.copilot.interactiveSession.disabled && !editorReadonly",
|
||||
"category": "Chat"
|
||||
},
|
||||
{
|
||||
"command": "github.copilot.chat.fix",
|
||||
"title": "%github.copilot.command.fixThis%",
|
||||
@@ -3507,14 +3495,6 @@
|
||||
"experimental"
|
||||
]
|
||||
},
|
||||
"github.copilot.chat.generateTests.codeLens": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "%github.copilot.config.generateTests.codeLens%",
|
||||
"tags": [
|
||||
"experimental"
|
||||
]
|
||||
},
|
||||
"github.copilot.chat.setupTests.enabled": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
@@ -4527,10 +4507,20 @@
|
||||
}
|
||||
],
|
||||
"editor/context": [
|
||||
{
|
||||
"command": "github.copilot.chat.fix",
|
||||
"when": "!github.copilot.interactiveSession.disabled && !editorReadonly && editorSelectionHasDiagnostics",
|
||||
"group": "1_chat@4"
|
||||
},
|
||||
{
|
||||
"command": "github.copilot.chat.explain",
|
||||
"when": "!github.copilot.interactiveSession.disabled",
|
||||
"group": "1_chat@4"
|
||||
"group": "1_chat@5"
|
||||
},
|
||||
{
|
||||
"command": "github.copilot.chat.review",
|
||||
"when": "config.github.copilot.chat.reviewSelection.enabled && !github.copilot.interactiveSession.disabled && resourceScheme != 'vscode-chat-code-block'",
|
||||
"group": "1_chat@6"
|
||||
},
|
||||
{
|
||||
"command": "github.copilot.chat.copilotCLI.addFileReference",
|
||||
@@ -4543,28 +4533,6 @@
|
||||
"when": "github.copilot.chat.copilotCLI.hasSession && editorHasSelection && !inOutput && resourceScheme != 'vscode-webview' && resourceScheme != 'webview-panel'"
|
||||
}
|
||||
],
|
||||
"editor/context/chat": [
|
||||
{
|
||||
"command": "github.copilot.chat.fix",
|
||||
"when": "!github.copilot.interactiveSession.disabled && !editorReadonly",
|
||||
"group": "copilotAction@1"
|
||||
},
|
||||
{
|
||||
"command": "github.copilot.chat.review",
|
||||
"when": "config.github.copilot.chat.reviewSelection.enabled && !github.copilot.interactiveSession.disabled && resourceScheme != 'vscode-chat-code-block'",
|
||||
"group": "copilotAction@2"
|
||||
},
|
||||
{
|
||||
"command": "github.copilot.chat.generateDocs",
|
||||
"when": "!github.copilot.interactiveSession.disabled && !editorReadonly",
|
||||
"group": "copilotGenerate@1"
|
||||
},
|
||||
{
|
||||
"command": "github.copilot.chat.generateTests",
|
||||
"when": "!github.copilot.interactiveSession.disabled && !editorReadonly",
|
||||
"group": "copilotGenerate@2"
|
||||
}
|
||||
],
|
||||
"chat/editor/inlineGutter": [
|
||||
{
|
||||
"command": "github.copilot.chat.explain",
|
||||
|
||||
@@ -26,8 +26,6 @@
|
||||
"github.copilot.command.unhelpfulReviewSuggestion": "Unhelpful",
|
||||
"github.copilot.command.fixThis": "Fix",
|
||||
"github.copilot.command.generateThis": "Generate This",
|
||||
"github.copilot.command.generateDocs": "Generate Docs",
|
||||
"github.copilot.command.generateTests": "Generate Tests",
|
||||
"github.copilot.command.openUserPreferences": "Open User Preferences",
|
||||
"github.copilot.command.openMemoryFolder": "Open Memory Folder",
|
||||
"github.copilot.command.sendChatFeedback": "Send Chat Feedback",
|
||||
@@ -158,7 +156,6 @@
|
||||
"github.copilot.config.pullRequestDescriptionGeneration.instructions": "A set of instructions that will be added to Copilot requests that generate pull request titles and descriptions.\nInstructions can come from: \n- a file in the workspace: `{ \"file\": \"fileName\" }`\n- text in natural language: `{ \"text\": \"Always include a list of key changes.\" }`\n\nNote: Keep your instructions short and precise. Poor instructions can degrade Copilot's quality and performance.",
|
||||
"github.copilot.config.pullRequestDescriptionGeneration.instruction.text": "Text instructions that will be added to Copilot requests that generate pull request titles and descriptions.",
|
||||
"github.copilot.config.pullRequestDescriptionGeneration.instruction.file": "A path to a file with instructions that will be added to Copilot requests that generate pull request titles and descriptions.",
|
||||
"github.copilot.config.generateTests.codeLens": "Show 'Generate tests' code lens for symbols that are not covered by current test coverage information.",
|
||||
"github.copilot.config.notebook.followCellExecution": "Controls whether the currently executing cell is revealed into the viewport upon execution from Copilot.",
|
||||
"github.copilot.config.notebook.enhancedNextEditSuggestions": "Controls whether to use an enhanced approach for generating next edit suggestions in notebook cells.",
|
||||
"github.copilot.config.imageUpload.enabled": "Enables the use of image upload URLs in chat requests instead of raw base64 strings.",
|
||||
|
||||
@@ -152,16 +152,6 @@ ConfigurationMigrationRegistry.registerConfigurationMigrations([{
|
||||
}
|
||||
}]);
|
||||
|
||||
ConfigurationMigrationRegistry.registerConfigurationMigrations([{
|
||||
key: 'github.copilot.chat.experimental.generateTests.codeLens',
|
||||
migrateFn: async (value: any) => {
|
||||
return [
|
||||
['github.copilot.chat.generateTests.codeLens', { value }],
|
||||
['github.copilot.chat.experimental.generateTests.codeLens', { value: undefined }]
|
||||
];
|
||||
}
|
||||
}]);
|
||||
|
||||
ConfigurationMigrationRegistry.registerConfigurationMigrations([{
|
||||
key: 'github.copilot.chat.planAgent.model',
|
||||
migrateFn: async (value: any) => {
|
||||
|
||||
@@ -29,7 +29,6 @@ import { GitHubMcpContrib } from '../../githubMcp/vscode-node/githubMcp.contribu
|
||||
import { IgnoredFileProviderContribution } from '../../ignore/vscode-node/ignoreProvider';
|
||||
import { JointCompletionsProviderContribution } from '../../inlineEdits/vscode-node/jointInlineCompletionProvider';
|
||||
import { FixTestFailureContribution } from '../../intents/vscode-node/fixTestFailureContributions';
|
||||
import { TestGenLensContribution } from '../../intents/vscode-node/testGenLens';
|
||||
import { ExtensionStateCommandContribution } from '../../log/vscode-node/extensionStateCommand';
|
||||
import { FetcherTelemetryContribution, LoggingActionsContrib } from '../../log/vscode-node/loggingActions';
|
||||
import { RequestLogTree } from '../../log/vscode-node/requestLogTree';
|
||||
@@ -110,7 +109,6 @@ export const vscodeNodeContributions: IExtensionContributionFactory[] = [
|
||||
*/
|
||||
export const vscodeNodeChatContributions: IExtensionContributionFactory[] = [
|
||||
asContributionFactory(ConfigurationMigrationContribution),
|
||||
asContributionFactory(TestGenLensContribution),
|
||||
asContributionFactory(RequestLogTree),
|
||||
asContributionFactory(OnboardTerminalTestsContribution),
|
||||
asContributionFactory(ToolsContribution),
|
||||
|
||||
@@ -84,6 +84,10 @@ export class QuickFixesProvider implements vscode.CodeActionProvider {
|
||||
codeActions.push(altTextQuickFixes);
|
||||
}
|
||||
|
||||
if (vscode.workspace.getConfiguration('inlineChat').get('affordance') !== 'off') {
|
||||
return codeActions;
|
||||
}
|
||||
|
||||
if (this.reviewService.isCodeFeedbackEnabled() && !activeTextEditor.selection.isEmpty) {
|
||||
const reviewAction = new AICodeAction(vscode.l10n.t('Review'), QuickFixesProvider.reviewKind);
|
||||
reviewAction.command = {
|
||||
|
||||
@@ -27,9 +27,7 @@ import * as path from '../../../util/vs/base/common/path';
|
||||
import { URI } from '../../../util/vs/base/common/uri';
|
||||
import { IInstantiationService, ServicesAccessor } from '../../../util/vs/platform/instantiation/common/instantiation';
|
||||
import { Intent } from '../../common/constants';
|
||||
import { InlineDocIntent } from '../../intents/node/docIntent';
|
||||
import { explainIntentPromptSnippet } from '../../intents/node/explainIntent';
|
||||
import { GenerateTests } from '../../intents/vscode-node/testGenAction';
|
||||
import { ChatParticipantRequestHandler } from '../../prompt/node/chatParticipantRequestHandler';
|
||||
import { sendReviewActionTelemetry } from '../../prompt/node/feedbackGenerator';
|
||||
import { CurrentSelection } from '../../prompts/node/panel/currentSelection';
|
||||
@@ -240,21 +238,6 @@ ${message}`,
|
||||
const doGenerate = () => {
|
||||
return vscode.commands.executeCommand('vscode.editorChat.start', { message: '/generate ' });
|
||||
};
|
||||
const doGenerateDocs = () => {
|
||||
return vscode.commands.executeCommand('vscode.editorChat.start', { message: `/${InlineDocIntent.ID} `, autoSend: true, initialRange: vscode.window.activeTextEditor?.selection });
|
||||
};
|
||||
const doGenerateTests = (arg?: unknown) => {
|
||||
// @ulugbekna: `github.copilot.chat.generateTests` is invoked from editor context menu, which means
|
||||
// the first arguments can be a vscode.Uri
|
||||
const context =
|
||||
(arg && typeof arg === 'object' &&
|
||||
'document' in arg && arg.document && typeof arg.document === 'object' && 'getText' in arg.document &&
|
||||
'selection' in arg && arg.selection instanceof vscode.Range
|
||||
)
|
||||
? arg as { document: vscode.TextDocument; selection: vscode.Range }
|
||||
: undefined;
|
||||
return instaService.createInstance(GenerateTests).runCommand(context);
|
||||
};
|
||||
const doFix = () => {
|
||||
const activeDocument = vscode.window.activeTextEditor;
|
||||
if (!activeDocument) {
|
||||
@@ -305,8 +288,6 @@ ${message}`,
|
||||
disposables.add(vscode.commands.registerCommand('github.copilot.chat.review.next', thread => goToNextReview(thread, +1)));
|
||||
disposables.add(vscode.commands.registerCommand('github.copilot.chat.review.current', thread => goToNextReview(thread, 0)));
|
||||
disposables.add(vscode.commands.registerCommand('github.copilot.chat.generate', doGenerate));
|
||||
disposables.add(vscode.commands.registerCommand('github.copilot.chat.generateDocs', doGenerateDocs));
|
||||
disposables.add(vscode.commands.registerCommand('github.copilot.chat.generateTests', doGenerateTests));
|
||||
disposables.add(vscode.commands.registerCommand('github.copilot.chat.fix', doFix));
|
||||
disposables.add(vscode.commands.registerCommand('github.copilot.chat.generateAltText', doGenerateAltText));
|
||||
// register code actions
|
||||
|
||||
@@ -1,178 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { TextDocumentSnapshot } from '../../../platform/editing/common/textDocumentSnapshot';
|
||||
import { IIgnoreService } from '../../../platform/ignore/common/ignoreService';
|
||||
import { IParserService } from '../../../platform/parser/node/parserService';
|
||||
import { ITabsAndEditorsService } from '../../../platform/tabs/common/tabsAndEditorsService';
|
||||
import { CancellationToken } from '../../../util/vs/base/common/cancellation';
|
||||
import { IInstantiationService } from '../../../util/vs/platform/instantiation/common/instantiation';
|
||||
import { Intent } from '../../common/constants';
|
||||
import { isTestFile, suggestUntitledTestFileLocation, TestFileFinder } from '../../prompt/node/testFiles';
|
||||
import { ITestGenInfoStorage } from '../node/testIntent/testInfoStorage';
|
||||
|
||||
|
||||
export class GenerateTests {
|
||||
|
||||
constructor(
|
||||
@IInstantiationService private readonly instaService: IInstantiationService,
|
||||
@IParserService private readonly parserService: IParserService,
|
||||
@ITestGenInfoStorage private readonly testGenInfoStorage: ITestGenInfoStorage,
|
||||
@ITabsAndEditorsService private readonly tabsAndEditorsService: ITabsAndEditorsService,
|
||||
@IIgnoreService private readonly ignoreService: IIgnoreService,
|
||||
) {
|
||||
}
|
||||
|
||||
public async runCommand(context?: { document: vscode.TextDocument; selection: vscode.Range }) {
|
||||
|
||||
let srcFile: TextDocumentSnapshot;
|
||||
let selection: vscode.Range;
|
||||
|
||||
if (context) {
|
||||
srcFile = TextDocumentSnapshot.create(context.document);
|
||||
selection = context.selection;
|
||||
} else {
|
||||
const initialActiveEditor = vscode.window.activeTextEditor;
|
||||
|
||||
if (initialActiveEditor === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
srcFile = TextDocumentSnapshot.create(initialActiveEditor.document);
|
||||
|
||||
selection = initialActiveEditor.selection;
|
||||
}
|
||||
|
||||
if (isTestFile(srcFile.uri)) {
|
||||
return vscode.commands.executeCommand(
|
||||
'vscode.editorChat.start',
|
||||
{
|
||||
message: `/${Intent.Tests} `,
|
||||
autoSend: true,
|
||||
initialRange: selection
|
||||
});
|
||||
} else {
|
||||
|
||||
// identify the range for the symbol to test
|
||||
|
||||
const testableNode = await this.identifyTestableNode(srcFile, selection);
|
||||
|
||||
this.updateTestGenInfo(srcFile, testableNode, selection);
|
||||
|
||||
// identify the file to write tests at -- either existing one or a new untitled one
|
||||
|
||||
const testFile = await this.findOrCreateTestFile(srcFile);
|
||||
|
||||
const testDoc = await vscode.workspace.openTextDocument(testFile);
|
||||
|
||||
// identify where in the test file to insert the tests at
|
||||
|
||||
const insertTestsAt: vscode.Range = await this.determineTestInsertPosition(testDoc);
|
||||
|
||||
const testEditor = await vscode.window.showTextDocument(testDoc, this.getTabGroupByUri(testFile));
|
||||
|
||||
testEditor.selection = new vscode.Selection(insertTestsAt.start, insertTestsAt.end);
|
||||
testEditor.revealRange(insertTestsAt, vscode.TextEditorRevealType.InCenter);
|
||||
|
||||
const isDocEmpty = insertTestsAt.end.line === 0 && insertTestsAt.end.character === 0;
|
||||
|
||||
if (!isDocEmpty) {
|
||||
await testEditor.edit(editBuilder => {
|
||||
editBuilder.insert(insertTestsAt.start, '\n\n');
|
||||
});
|
||||
}
|
||||
|
||||
return vscode.commands.executeCommand(
|
||||
'vscode.editorChat.start',
|
||||
{
|
||||
message: `/${Intent.Tests}`,
|
||||
autoSend: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async determineTestInsertPosition(testDoc: vscode.TextDocument) {
|
||||
const testFileAST = this.parserService.getTreeSitterAST(testDoc);
|
||||
|
||||
const lastTest = testFileAST ? await testFileAST.findLastTest() : null;
|
||||
|
||||
let insertTestsAt: vscode.Range;
|
||||
if (lastTest === null) {
|
||||
const lastLine = testDoc.lineAt(testDoc.lineCount - 1);
|
||||
insertTestsAt = new vscode.Range(lastLine.range.end, lastLine.range.end);
|
||||
} else {
|
||||
const lastTestEndPos = testDoc.positionAt(lastTest.endIndex);
|
||||
const endOfLastLine = testDoc.lineAt(lastTestEndPos).range.end;
|
||||
insertTestsAt = new vscode.Range(endOfLastLine, endOfLastLine);
|
||||
}
|
||||
return insertTestsAt;
|
||||
}
|
||||
|
||||
private updateTestGenInfo(srcFile: TextDocumentSnapshot, testableNode: { identifier: string; range: vscode.Range } | null, selection: vscode.Range) {
|
||||
this.testGenInfoStorage.sourceFileToTest = {
|
||||
uri: srcFile.uri,
|
||||
target: testableNode?.range ?? selection,
|
||||
identifier: testableNode?.identifier,
|
||||
};
|
||||
}
|
||||
|
||||
private async identifyTestableNode(srcFile: TextDocumentSnapshot, selection: vscode.Range): Promise<{ identifier: string; range: vscode.Range } | null> {
|
||||
|
||||
const srcFileAST = this.parserService.getTreeSitterAST(srcFile);
|
||||
|
||||
if (!srcFileAST) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const testableNode = await srcFileAST.getTestableNode({
|
||||
startIndex: srcFile.offsetAt(selection.start),
|
||||
endIndex: srcFile.offsetAt(selection.end),
|
||||
});
|
||||
|
||||
if (!testableNode) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { startIndex, endIndex } = testableNode.node;
|
||||
const testedSymbolRange = new vscode.Range(srcFile.positionAt(startIndex), srcFile.positionAt(endIndex));
|
||||
|
||||
return {
|
||||
identifier: testableNode.identifier.name,
|
||||
range: testedSymbolRange
|
||||
};
|
||||
}
|
||||
|
||||
private async findOrCreateTestFile(srcFile: TextDocumentSnapshot) {
|
||||
const finder = this.instaService.createInstance(TestFileFinder);
|
||||
|
||||
let testFile = await finder.findTestFileForSourceFile(srcFile, CancellationToken.None);
|
||||
|
||||
if (testFile !== undefined && await this.ignoreService.isCopilotIgnored(testFile)) {
|
||||
testFile = undefined;
|
||||
}
|
||||
|
||||
if (testFile === undefined) {
|
||||
testFile = suggestUntitledTestFileLocation(srcFile);
|
||||
}
|
||||
return testFile;
|
||||
}
|
||||
|
||||
private getTabGroupByUri(uri: vscode.Uri) {
|
||||
for (const tab of this.tabsAndEditorsService.tabs) {
|
||||
if (tab.uri?.toString() === uri.toString()) {
|
||||
return tab.tab.group.viewColumn;
|
||||
}
|
||||
}
|
||||
|
||||
const currentTab = this.tabsAndEditorsService.activeTextEditor?.viewColumn;
|
||||
|
||||
if (currentTab === undefined) {
|
||||
return vscode.ViewColumn.Two;
|
||||
} else {
|
||||
return currentTab > vscode.ViewColumn.One ? currentTab - 1 : vscode.ViewColumn.Beside;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,161 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
import * as vscode from 'vscode';
|
||||
import { ConfigKey, IConfigurationService } from '../../../platform/configuration/common/configurationService';
|
||||
import { ILogService } from '../../../platform/log/common/logService';
|
||||
import { IParserService } from '../../../platform/parser/node/parserService';
|
||||
import { range } from '../../../util/vs/base/common/arrays';
|
||||
import { Disposable, DisposableStore, IDisposable } from '../../../util/vs/base/common/lifecycle';
|
||||
import { IInstantiationService } from '../../../util/vs/platform/instantiation/common/instantiation';
|
||||
import { IExtensionContribution } from '../../common/contributions';
|
||||
|
||||
|
||||
class TestGenLensProvider implements vscode.CodeLensProvider<vscode.CodeLens>, IDisposable {
|
||||
|
||||
public static codeLensTitle = vscode.l10n.t('Generate tests using Copilot');
|
||||
|
||||
public static isEnabled(configService: IConfigurationService) {
|
||||
return configService.getConfig(ConfigKey.GenerateTestsCodeLens);
|
||||
}
|
||||
|
||||
public readonly onDidChangeCodeLenses: vscode.Event<void>;
|
||||
|
||||
private readonly store;
|
||||
|
||||
constructor(
|
||||
@ILogService private readonly logService: ILogService,
|
||||
@IParserService private readonly parserService: IParserService,
|
||||
) {
|
||||
this.store = new DisposableStore();
|
||||
this.onDidChangeCodeLenses = vscode.tests.onDidChangeTestResults;
|
||||
}
|
||||
|
||||
public dispose() {
|
||||
this.store.dispose();
|
||||
}
|
||||
|
||||
public provideCodeLenses(document: vscode.TextDocument, token: vscode.CancellationToken): Promise<vscode.CodeLens[]> {
|
||||
return this.computeCodeLens(document, token);
|
||||
}
|
||||
|
||||
private async computeCodeLens(document: vscode.TextDocument, token: vscode.CancellationToken): Promise<vscode.CodeLens[]> {
|
||||
|
||||
// don't show code lenses for output channels
|
||||
if (document.uri.scheme === 'output') {
|
||||
return [];
|
||||
}
|
||||
|
||||
const testResults = vscode.tests.testResults;
|
||||
if (testResults.length === 0) {
|
||||
this.logService.trace('No test results');
|
||||
return [];
|
||||
}
|
||||
|
||||
const lastTest = testResults[0];
|
||||
|
||||
let detailedCoverage: vscode.FileCoverageDetail[] | undefined;
|
||||
try {
|
||||
detailedCoverage = await lastTest.getDetailedCoverage?.(document.uri, token);
|
||||
} catch (e) {
|
||||
this.logService.error(e);
|
||||
return [];
|
||||
}
|
||||
|
||||
if (!detailedCoverage || detailedCoverage.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const codeLens: vscode.CodeLens[] = [];
|
||||
|
||||
for (const detail of detailedCoverage) {
|
||||
|
||||
if (detail instanceof vscode.DeclarationCoverage) {
|
||||
this.logService.trace(`Received statement coverage for ${detail.name}. (detail.executed: ${detail.executed})`);
|
||||
const wasExecuted = !!detail.executed;
|
||||
if (wasExecuted) {
|
||||
continue;
|
||||
}
|
||||
const locationAsRange = detail.location instanceof vscode.Range ? detail.location : new vscode.Range(detail.location, detail.location);
|
||||
codeLens.push(this.createCodeLens(document, locationAsRange));
|
||||
} else if (detail instanceof vscode.StatementCoverage) {
|
||||
this.logService.trace('Received statement coverage; did nothing');
|
||||
} else {
|
||||
this.logService.error('Unexpected coverage type');
|
||||
}
|
||||
}
|
||||
|
||||
if (codeLens.length === 0) {
|
||||
// try identifying untested declarations using tree sitter based approach
|
||||
|
||||
const ast = this.parserService.getTreeSitterAST(document);
|
||||
|
||||
if (ast === undefined) {
|
||||
return codeLens;
|
||||
}
|
||||
|
||||
const testableNodes = await ast.getTestableNodes();
|
||||
|
||||
if (testableNodes === null) {
|
||||
return codeLens;
|
||||
}
|
||||
|
||||
const uncoveredLines = detailedCoverage.flatMap(cov =>
|
||||
!!cov.executed ? [] : (cov.location instanceof vscode.Position ? [cov.location.line] : this.toLineNumbers(cov.location))
|
||||
);
|
||||
|
||||
const uncoveredLinesSet = new Set(uncoveredLines);
|
||||
|
||||
for (const node of testableNodes) {
|
||||
const start = document.positionAt(node.node.startIndex);
|
||||
const end = document.positionAt(node.node.endIndex);
|
||||
const codeLensRange = new vscode.Range(start, end);
|
||||
if (range(start.line, end.line).every(lineN => uncoveredLinesSet.has(lineN))) {
|
||||
codeLens.push(this.createCodeLens(document, codeLensRange));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
return codeLens;
|
||||
}
|
||||
|
||||
private createCodeLens(document: vscode.TextDocument, range: vscode.Range) {
|
||||
return new vscode.CodeLens(
|
||||
range,
|
||||
{
|
||||
title: TestGenLensProvider.codeLensTitle,
|
||||
command: 'github.copilot.chat.generateTests',
|
||||
arguments: [{ document, selection: range }],
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private toLineNumbers(range: vscode.Range): number[] {
|
||||
const lineNumbers: number[] = [];
|
||||
for (let i = range.start.line; i <= range.end.line; i++) {
|
||||
lineNumbers.push(i);
|
||||
}
|
||||
return lineNumbers;
|
||||
}
|
||||
}
|
||||
|
||||
export class TestGenLensContribution extends Disposable implements IExtensionContribution {
|
||||
constructor(
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
) {
|
||||
super();
|
||||
|
||||
if (TestGenLensProvider.isEnabled(configurationService)) {
|
||||
const testGenCodeLensProvider = this._register(instantiationService.createInstance(TestGenLensProvider));
|
||||
this._register(
|
||||
vscode.languages.registerCodeLensProvider(
|
||||
'*',
|
||||
testGenCodeLensProvider
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -919,8 +919,6 @@ export namespace ConfigKey {
|
||||
export const TestGenerationInstructions = defineSetting('chat.testGeneration.instructions', ConfigType.Simple, [] as CodeGenerationInstruction[]);
|
||||
export const CommitMessageGenerationInstructions = defineSetting('chat.commitMessageGeneration.instructions', ConfigType.Simple, [] as CommitMessageGenerationInstruction[]);
|
||||
export const PullRequestDescriptionGenerationInstructions = defineSetting('chat.pullRequestDescriptionGeneration.instructions', ConfigType.Simple, [] as CommitMessageGenerationInstruction[]);
|
||||
/** Show code lens "Generate tests" when we have test coverage info about this symbol and it's not covered */
|
||||
export const GenerateTestsCodeLens = defineSetting('chat.generateTests.codeLens', ConfigType.Simple, false);
|
||||
/** Whether new flows around setting up tests are enabled */
|
||||
export const SetupTests = defineSetting<boolean>('chat.setupTests.enabled', ConfigType.Simple, true);
|
||||
/** Whether the Copilot TypeScript context provider is enabled and if how */
|
||||
|
||||
Reference in New Issue
Block a user