未验证 提交 0b3aa0a6 编写于 作者: M Matt Bierner 提交者: GitHub

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
上级 9061a91f
......@@ -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)) {
if (args) {
if (Array.isArray(args)) {
[extensionId] = args;
} else {
extensionId = args.extensionId;
}
}
return accessor.get(IWebIssueService).openReporter({ extensionId });
......
/*---------------------------------------------------------------------------------------------
* 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 = {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册