mirror of
https://github.com/microsoft/vscode.git
synced 2025-12-24 20:26:08 +00:00
Let extensions prepopulate the issue reporter title and description (#91039)
* Let extensions prepopulate the issue reporter title and description Fixes #91028 Adds two new optional arguments to the `vscode.openIssueReporter` command: `issueTitle` and `issueBody`. These are taken using an options object and are used to pre-populate the native issue reporter fields Hooks up these fields for TypeScript's report issue prompt. We use this to post the most recent TS Server error stack * Extract duplicate command id to constant * Log version directly instead of prompting users for it
This commit is contained in:
@@ -529,7 +529,10 @@ export default class TypeScriptServiceClient extends Disposable implements IType
|
||||
id: MessageAction;
|
||||
}
|
||||
|
||||
const previousVersion = this.apiVersion;
|
||||
const previousState = this.serverState;
|
||||
this.serverState = ServerState.None;
|
||||
|
||||
if (restart) {
|
||||
const diff = Date.now() - this.lastStart;
|
||||
this.numberRestarts++;
|
||||
@@ -565,8 +568,11 @@ export default class TypeScriptServiceClient extends Disposable implements IType
|
||||
}
|
||||
if (prompt) {
|
||||
prompt.then(item => {
|
||||
if (item && item.id === MessageAction.reportIssue) {
|
||||
return vscode.commands.executeCommand('workbench.action.openIssueReporter');
|
||||
if (item?.id === MessageAction.reportIssue) {
|
||||
const args = previousState.type === ServerState.Type.Errored && previousState.error instanceof TypeScriptServerError
|
||||
? getReportIssueArgsForError(previousState.error, previousVersion)
|
||||
: undefined;
|
||||
return vscode.commands.executeCommand('workbench.action.openIssueReporter', args);
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
@@ -719,9 +725,6 @@ export default class TypeScriptServiceClient extends Disposable implements IType
|
||||
}
|
||||
|
||||
private fatalError(command: string, error: unknown): void {
|
||||
if (!(error instanceof TypeScriptServerError)) {
|
||||
console.log('fdasfasdf');
|
||||
}
|
||||
/* __GDPR__
|
||||
"fatalError" : {
|
||||
"${include}": [
|
||||
@@ -740,6 +743,9 @@ export default class TypeScriptServiceClient extends Disposable implements IType
|
||||
if (this.serverState.type === ServerState.Type.Running) {
|
||||
this.info('Killing TS Server');
|
||||
this.serverState.server.kill();
|
||||
if (error instanceof TypeScriptServerError) {
|
||||
this.serverState = new ServerState.Errored(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -869,6 +875,32 @@ export default class TypeScriptServiceClient extends Disposable implements IType
|
||||
}
|
||||
}
|
||||
|
||||
function getReportIssueArgsForError(error: TypeScriptServerError, apiVersion: API): { issueTitle: string, issueBody: string } | undefined {
|
||||
if (!error.serverStack || !error.serverMessage) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Note these strings are intentionally not localized
|
||||
// as we want users to file issues in english
|
||||
return {
|
||||
issueTitle: `TS Server fatal error: ${error.serverMessage}`,
|
||||
|
||||
issueBody: `**TypeScript Version:** ${apiVersion.fullVersionString}
|
||||
|
||||
**Steps to reproduce crash**
|
||||
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
** TS Server Error Stack **
|
||||
|
||||
\`\`\`
|
||||
${error.serverStack}
|
||||
\`\`\``,
|
||||
};
|
||||
}
|
||||
|
||||
function getDignosticsKind(event: Proto.Event) {
|
||||
switch (event.event) {
|
||||
case 'syntaxDiag': return DiagnosticKind.Syntax;
|
||||
|
||||
@@ -97,6 +97,23 @@ export class IssueReporter extends Disposable {
|
||||
this.previewButton = new Button(issueReporterElement);
|
||||
}
|
||||
|
||||
const issueTitle = configuration.data.issueTitle;
|
||||
if (issueTitle) {
|
||||
const issueTitleElement = this.getElementById<HTMLInputElement>('issue-title');
|
||||
if (issueTitleElement) {
|
||||
issueTitleElement.value = issueTitle;
|
||||
}
|
||||
}
|
||||
|
||||
const issueBody = configuration.data.issueBody;
|
||||
if (issueBody) {
|
||||
const description = this.getElementById<HTMLTextAreaElement>('description');
|
||||
if (description) {
|
||||
description.value = issueBody;
|
||||
this.issueReporterModel.update({ issueDescription: issueBody });
|
||||
}
|
||||
}
|
||||
|
||||
ipcRenderer.on('vscode:issuePerformanceInfoResponse', (_: unknown, info: Partial<IssueReporterData>) => {
|
||||
this.logService.trace('issueReporter: Received performance data');
|
||||
this.issueReporterModel.update(info);
|
||||
@@ -1175,8 +1192,8 @@ export class IssueReporter extends Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
private getElementById(elementId: string): HTMLElement | undefined {
|
||||
const element = document.getElementById(elementId);
|
||||
private getElementById<T extends HTMLElement = HTMLElement>(elementId: string): T | undefined {
|
||||
const element = document.getElementById(elementId) as T | undefined;
|
||||
if (element) {
|
||||
return element;
|
||||
} else {
|
||||
|
||||
@@ -59,6 +59,8 @@ export interface IssueReporterData extends WindowData {
|
||||
enabledExtensions: IssueReporterExtensionData[];
|
||||
issueType?: IssueType;
|
||||
extensionId?: string;
|
||||
readonly issueTitle?: string;
|
||||
readonly issueBody?: string;
|
||||
}
|
||||
|
||||
export interface ISettingSearchResult {
|
||||
|
||||
@@ -174,10 +174,20 @@ export class RemoveFromRecentlyOpenedAPICommand {
|
||||
}
|
||||
CommandsRegistry.registerCommand(RemoveFromRecentlyOpenedAPICommand.ID, adjustHandler(RemoveFromRecentlyOpenedAPICommand.execute));
|
||||
|
||||
export interface OpenIssueReporterArgs {
|
||||
readonly extensionId: string;
|
||||
readonly issueTitle?: string;
|
||||
readonly issueBody?: string;
|
||||
}
|
||||
|
||||
export class OpenIssueReporter {
|
||||
public static readonly ID = 'vscode.openIssueReporter';
|
||||
public static execute(executor: ICommandsExecutor, extensionId: string): Promise<void> {
|
||||
return executor.executeCommand('workbench.action.openIssueReporter', [extensionId]);
|
||||
|
||||
public static execute(executor: ICommandsExecutor, args: string | OpenIssueReporterArgs): Promise<void> {
|
||||
const commandArgs = typeof args === 'string'
|
||||
? { extensionId: args }
|
||||
: args;
|
||||
return executor.executeCommand('workbench.action.openIssueReporter', commandArgs);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import * as search from 'vs/workbench/contrib/search/common/search';
|
||||
import { ICommandHandlerDescription } from 'vs/platform/commands/common/commands';
|
||||
import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
|
||||
import { CustomCodeAction } from 'vs/workbench/api/common/extHostLanguageFeatures';
|
||||
import { ICommandsExecutor, OpenFolderAPICommand, DiffAPICommand, OpenAPICommand, RemoveFromRecentlyOpenedAPICommand, SetEditorLayoutAPICommand, OpenIssueReporter } from './apiCommands';
|
||||
import { ICommandsExecutor, OpenFolderAPICommand, DiffAPICommand, OpenAPICommand, RemoveFromRecentlyOpenedAPICommand, SetEditorLayoutAPICommand, OpenIssueReporter, OpenIssueReporterArgs } from './apiCommands';
|
||||
import { EditorGroupLayout } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { isFalsyOrEmpty } from 'vs/base/common/arrays';
|
||||
import { IRange } from 'vs/editor/common/core/range';
|
||||
@@ -364,7 +364,7 @@ export class ExtHostApiCommands {
|
||||
this._register(OpenIssueReporter.ID, adjustHandler(OpenIssueReporter.execute), {
|
||||
description: 'Opens the issue reporter with the provided extension id as the selected source',
|
||||
args: [
|
||||
{ name: 'extensionId', description: 'extensionId to report an issue on', constraint: (value: any) => typeof value === 'string' }
|
||||
{ name: 'extensionId', description: 'extensionId to report an issue on', constraint: (value: unknown) => typeof value === 'string' || (typeof value === 'object' && typeof (value as OpenIssueReporterArgs).extensionId === 'string') }
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
@@ -12,19 +12,23 @@ import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { Extensions as WorkbenchExtensions, IWorkbenchContribution, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions';
|
||||
import { IWebIssueService, WebIssueService } from 'vs/workbench/contrib/issue/browser/issueService';
|
||||
import { OpenIssueReporterArgs, OpenIssueReporterActionId } from 'vs/workbench/contrib/issue/common/commands';
|
||||
|
||||
class RegisterIssueContribution implements IWorkbenchContribution {
|
||||
|
||||
constructor(@IProductService readonly productService: IProductService) {
|
||||
if (productService.reportIssueUrl) {
|
||||
const helpCategory = { value: nls.localize('help', "Help"), original: 'Help' };
|
||||
const OpenIssueReporterActionId = 'workbench.action.openIssueReporter';
|
||||
const OpenIssueReporterActionLabel = nls.localize({ key: 'reportIssueInEnglish', comment: ['Translate this to "Report Issue in English" in all languages please!'] }, "Report Issue");
|
||||
|
||||
CommandsRegistry.registerCommand(OpenIssueReporterActionId, function (accessor, args?: [string]) {
|
||||
CommandsRegistry.registerCommand(OpenIssueReporterActionId, function (accessor, args?: [string] | OpenIssueReporterArgs) {
|
||||
let extensionId: string | undefined;
|
||||
if (args && Array.isArray(args)) {
|
||||
[extensionId] = args;
|
||||
if (args) {
|
||||
if (Array.isArray(args)) {
|
||||
[extensionId] = args;
|
||||
} else {
|
||||
extensionId = args.extensionId;
|
||||
}
|
||||
}
|
||||
|
||||
return accessor.get(IWebIssueService).openReporter({ extensionId });
|
||||
|
||||
12
src/vs/workbench/contrib/issue/common/commands.ts
Normal file
12
src/vs/workbench/contrib/issue/common/commands.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
export const OpenIssueReporterActionId = 'workbench.action.openIssueReporter';
|
||||
|
||||
export interface OpenIssueReporterArgs {
|
||||
readonly extensionId?: string;
|
||||
readonly issueTitle?: string;
|
||||
readonly issueBody?: string;
|
||||
}
|
||||
@@ -13,7 +13,8 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { IWorkbenchIssueService } from 'vs/workbench/contrib/issue/electron-browser/issue';
|
||||
import { WorkbenchIssueService } from 'vs/workbench/contrib/issue/electron-browser/issueService';
|
||||
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { IIssueService } from 'vs/platform/issue/node/issue';
|
||||
import { IIssueService, IssueReporterData } from 'vs/platform/issue/node/issue';
|
||||
import { OpenIssueReporterArgs, OpenIssueReporterActionId } from 'vs/workbench/contrib/issue/common/commands';
|
||||
|
||||
const helpCategory = { value: nls.localize('help', "Help"), original: 'Help' };
|
||||
const workbenchActionsRegistry = Registry.as<IWorkbenchActionRegistry>(Extensions.WorkbenchActions);
|
||||
@@ -21,16 +22,14 @@ const workbenchActionsRegistry = Registry.as<IWorkbenchActionRegistry>(Extension
|
||||
if (!!product.reportIssueUrl) {
|
||||
workbenchActionsRegistry.registerWorkbenchAction(SyncActionDescriptor.create(ReportPerformanceIssueUsingReporterAction, ReportPerformanceIssueUsingReporterAction.ID, ReportPerformanceIssueUsingReporterAction.LABEL), 'Help: Report Performance Issue', helpCategory.value);
|
||||
|
||||
const OpenIssueReporterActionId = 'workbench.action.openIssueReporter';
|
||||
const OpenIssueReporterActionLabel = nls.localize({ key: 'reportIssueInEnglish', comment: ['Translate this to "Report Issue in English" in all languages please!'] }, "Report Issue");
|
||||
|
||||
CommandsRegistry.registerCommand(OpenIssueReporterActionId, function (accessor, args?: [string]) {
|
||||
let extensionId: string | undefined;
|
||||
if (args && Array.isArray(args)) {
|
||||
[extensionId] = args;
|
||||
}
|
||||
CommandsRegistry.registerCommand(OpenIssueReporterActionId, function (accessor, args?: [string] | OpenIssueReporterArgs) {
|
||||
const data: Partial<IssueReporterData> = Array.isArray(args)
|
||||
? { extensionId: args[0] }
|
||||
: args || {};
|
||||
|
||||
return accessor.get(IWorkbenchIssueService).openReporter({ extensionId });
|
||||
return accessor.get(IWorkbenchIssueService).openReporter(data);
|
||||
});
|
||||
|
||||
const command: ICommandAction = {
|
||||
|
||||
Reference in New Issue
Block a user