diff --git a/extensions/typescript-language-features/src/typescriptServiceClient.ts b/extensions/typescript-language-features/src/typescriptServiceClient.ts index b3f4877ee37c16b8ebb526b93e8cc727ba99e11e..afd6e382cc5f84e8a5757eebe834164d15c5ccef 100644 --- a/extensions/typescript-language-features/src/typescriptServiceClient.ts +++ b/extensions/typescript-language-features/src/typescriptServiceClient.ts @@ -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; diff --git a/src/vs/code/electron-browser/issue/issueReporterMain.ts b/src/vs/code/electron-browser/issue/issueReporterMain.ts index f47161fb8bfa8c1637440261840f6a30b9b42dc2..169dbf0c326736b4b340ed23c6cde424cbe48db4 100644 --- a/src/vs/code/electron-browser/issue/issueReporterMain.ts +++ b/src/vs/code/electron-browser/issue/issueReporterMain.ts @@ -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('issue-title'); + if (issueTitleElement) { + issueTitleElement.value = issueTitle; + } + } + + const issueBody = configuration.data.issueBody; + if (issueBody) { + const description = this.getElementById('description'); + if (description) { + description.value = issueBody; + this.issueReporterModel.update({ issueDescription: issueBody }); + } + } + ipcRenderer.on('vscode:issuePerformanceInfoResponse', (_: unknown, info: Partial) => { 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(elementId: string): T | undefined { + const element = document.getElementById(elementId) as T | undefined; if (element) { return element; } else { diff --git a/src/vs/platform/issue/node/issue.ts b/src/vs/platform/issue/node/issue.ts index aeb55398adf7d21e4e8b2515d0dd958555b557bd..75fb71f9817990b12045e4d2984b1412e5f42d7a 100644 --- a/src/vs/platform/issue/node/issue.ts +++ b/src/vs/platform/issue/node/issue.ts @@ -59,6 +59,8 @@ export interface IssueReporterData extends WindowData { enabledExtensions: IssueReporterExtensionData[]; issueType?: IssueType; extensionId?: string; + readonly issueTitle?: string; + readonly issueBody?: string; } export interface ISettingSearchResult { diff --git a/src/vs/workbench/api/common/apiCommands.ts b/src/vs/workbench/api/common/apiCommands.ts index 6e7bb05ba2332f7886c55b7bd96c89d11b6487f9..ea4e3428172468381753db2b762b862b72bee151 100644 --- a/src/vs/workbench/api/common/apiCommands.ts +++ b/src/vs/workbench/api/common/apiCommands.ts @@ -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 { - return executor.executeCommand('workbench.action.openIssueReporter', [extensionId]); + + public static execute(executor: ICommandsExecutor, args: string | OpenIssueReporterArgs): Promise { + const commandArgs = typeof args === 'string' + ? { extensionId: args } + : args; + return executor.executeCommand('workbench.action.openIssueReporter', commandArgs); } } diff --git a/src/vs/workbench/api/common/extHostApiCommands.ts b/src/vs/workbench/api/common/extHostApiCommands.ts index ab21443e9c4010bd3439e5ff9fe0c160828645d4..4a35b91d591a70817995f272fb50e58debdaae24 100644 --- a/src/vs/workbench/api/common/extHostApiCommands.ts +++ b/src/vs/workbench/api/common/extHostApiCommands.ts @@ -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') } ] }); } diff --git a/src/vs/workbench/contrib/issue/browser/issue.contribution.ts b/src/vs/workbench/contrib/issue/browser/issue.contribution.ts index 4be89357d7b94b0f8f8a1c52a84e659ec69b8b60..65f8ffb824533cf34b3fdb3d5a94849e2b2caf0f 100644 --- a/src/vs/workbench/contrib/issue/browser/issue.contribution.ts +++ b/src/vs/workbench/contrib/issue/browser/issue.contribution.ts @@ -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 }); diff --git a/src/vs/workbench/contrib/issue/common/commands.ts b/src/vs/workbench/contrib/issue/common/commands.ts new file mode 100644 index 0000000000000000000000000000000000000000..bcd6e252c88c17fa701e971f211d2220352ef968 --- /dev/null +++ b/src/vs/workbench/contrib/issue/common/commands.ts @@ -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; +} diff --git a/src/vs/workbench/contrib/issue/electron-browser/issue.contribution.ts b/src/vs/workbench/contrib/issue/electron-browser/issue.contribution.ts index b9316bb1271030898b69e98699fbba3a61e93a3c..8f50836d67a844cc0b5e53c907eb3d8c37a37ea4 100644 --- a/src/vs/workbench/contrib/issue/electron-browser/issue.contribution.ts +++ b/src/vs/workbench/contrib/issue/electron-browser/issue.contribution.ts @@ -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(Extensions.WorkbenchActions); @@ -21,16 +22,14 @@ const workbenchActionsRegistry = Registry.as(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 = Array.isArray(args) + ? { extensionId: args[0] } + : args || {}; - return accessor.get(IWorkbenchIssueService).openReporter({ extensionId }); + return accessor.get(IWorkbenchIssueService).openReporter(data); }); const command: ICommandAction = {