Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/issue/browser/issueReporterModel.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { mainWindow } from '../../../../base/browser/window.js';
7
import { isRemoteDiagnosticError, SystemInfo } from '../../../../platform/diagnostics/common/diagnostics.js';
8
import { ISettingSearchResult, IssueReporterExtensionData, IssueType } from '../common/issue.js';
9
10
export interface IssueReporterData {
11
issueType: IssueType;
12
issueDescription?: string;
13
issueTitle?: string;
14
extensionData?: string;
15
16
versionInfo?: any;
17
systemInfo?: SystemInfo;
18
systemInfoWeb?: string;
19
processInfo?: string;
20
workspaceInfo?: string;
21
22
includeSystemInfo: boolean;
23
includeWorkspaceInfo: boolean;
24
includeProcessInfo: boolean;
25
includeExtensions: boolean;
26
includeExperiments: boolean;
27
includeExtensionData: boolean;
28
29
numberOfThemeExtesions?: number;
30
allExtensions: IssueReporterExtensionData[];
31
enabledNonThemeExtesions?: IssueReporterExtensionData[];
32
extensionsDisabled?: boolean;
33
fileOnExtension?: boolean;
34
fileOnMarketplace?: boolean;
35
fileOnProduct?: boolean;
36
selectedExtension?: IssueReporterExtensionData;
37
actualSearchResults?: ISettingSearchResult[];
38
query?: string;
39
filterResultCount?: number;
40
experimentInfo?: string;
41
restrictedMode?: boolean;
42
isUnsupported?: boolean;
43
}
44
45
export class IssueReporterModel {
46
private readonly _data: IssueReporterData;
47
48
constructor(initialData?: Partial<IssueReporterData>) {
49
const defaultData = {
50
issueType: IssueType.Bug,
51
includeSystemInfo: true,
52
includeWorkspaceInfo: true,
53
includeProcessInfo: true,
54
includeExtensions: true,
55
includeExperiments: true,
56
includeExtensionData: true,
57
allExtensions: []
58
};
59
60
this._data = initialData ? Object.assign(defaultData, initialData) : defaultData;
61
62
mainWindow.addEventListener('message', async (event) => {
63
if (event.data && event.data.sendChannel === 'vscode:triggerIssueData') {
64
mainWindow.postMessage({
65
data: { issueBody: this._data.issueDescription, issueTitle: this._data.issueTitle },
66
replyChannel: 'vscode:triggerIssueDataResponse'
67
}, '*');
68
}
69
});
70
}
71
72
getData(): IssueReporterData {
73
return this._data;
74
}
75
76
update(newData: Partial<IssueReporterData>): void {
77
Object.assign(this._data, newData);
78
}
79
80
serialize(): string {
81
const modes = [];
82
if (this._data.restrictedMode) {
83
modes.push('Restricted');
84
}
85
if (this._data.isUnsupported) {
86
modes.push('Unsupported');
87
}
88
return `
89
Type: <b>${this.getIssueTypeTitle()}</b>
90
91
${this._data.issueDescription}
92
${this.getExtensionVersion()}
93
VS Code version: ${this._data.versionInfo && this._data.versionInfo.vscodeVersion}
94
OS version: ${this._data.versionInfo && this._data.versionInfo.os}
95
Modes:${modes.length ? ' ' + modes.join(', ') : ''}
96
${this.getRemoteOSes()}
97
${this.getInfos()}
98
<!-- generated by issue reporter -->`;
99
}
100
101
private getRemoteOSes(): string {
102
if (this._data.systemInfo && this._data.systemInfo.remoteData.length) {
103
return this._data.systemInfo.remoteData
104
.map(remote => isRemoteDiagnosticError(remote) ? remote.errorMessage : `Remote OS version: ${remote.machineInfo.os}`).join('\n') + '\n';
105
}
106
107
return '';
108
}
109
110
fileOnExtension(): boolean | undefined {
111
const fileOnExtensionSupported = this._data.issueType === IssueType.Bug
112
|| this._data.issueType === IssueType.PerformanceIssue
113
|| this._data.issueType === IssueType.FeatureRequest;
114
115
return fileOnExtensionSupported && this._data.fileOnExtension;
116
}
117
118
private getExtensionVersion(): string {
119
if (this.fileOnExtension() && this._data.selectedExtension) {
120
return `\nExtension version: ${this._data.selectedExtension.version}`;
121
} else {
122
return '';
123
}
124
}
125
126
private getIssueTypeTitle(): string {
127
if (this._data.issueType === IssueType.Bug) {
128
return 'Bug';
129
} else if (this._data.issueType === IssueType.PerformanceIssue) {
130
return 'Performance Issue';
131
} else {
132
return 'Feature Request';
133
}
134
}
135
136
private getInfos(): string {
137
let info = '';
138
139
if (this._data.fileOnMarketplace) {
140
return info;
141
}
142
143
const isBugOrPerformanceIssue = this._data.issueType === IssueType.Bug || this._data.issueType === IssueType.PerformanceIssue;
144
145
if (isBugOrPerformanceIssue) {
146
if (this._data.includeExtensionData && this._data.extensionData) {
147
info += this.getExtensionData();
148
}
149
150
if (this._data.includeSystemInfo && this._data.systemInfo) {
151
info += this.generateSystemInfoMd();
152
}
153
154
if (this._data.includeSystemInfo && this._data.systemInfoWeb) {
155
info += 'System Info: ' + this._data.systemInfoWeb;
156
}
157
}
158
159
if (this._data.issueType === IssueType.PerformanceIssue) {
160
if (this._data.includeProcessInfo) {
161
info += this.generateProcessInfoMd();
162
}
163
164
if (this._data.includeWorkspaceInfo) {
165
info += this.generateWorkspaceInfoMd();
166
}
167
}
168
169
if (isBugOrPerformanceIssue) {
170
if (!this._data.fileOnExtension && this._data.includeExtensions) {
171
info += this.generateExtensionsMd();
172
}
173
174
if (this._data.includeExperiments && this._data.experimentInfo) {
175
info += this.generateExperimentsInfoMd();
176
}
177
}
178
179
return info;
180
}
181
182
private getExtensionData(): string {
183
return this._data.extensionData ?? '';
184
}
185
186
private generateSystemInfoMd(): string {
187
let md = `<details>
188
<summary>System Info</summary>
189
190
|Item|Value|
191
|---|---|
192
`;
193
194
if (this._data.systemInfo) {
195
196
md += `|CPUs|${this._data.systemInfo.cpus}|
197
|GPU Status|${Object.keys(this._data.systemInfo.gpuStatus).map(key => `${key}: ${this._data.systemInfo!.gpuStatus[key]}`).join('<br>')}|
198
|Load (avg)|${this._data.systemInfo.load}|
199
|Memory (System)|${this._data.systemInfo.memory}|
200
|Process Argv|${this._data.systemInfo.processArgs.replace(/\\/g, '\\\\')}|
201
|Screen Reader|${this._data.systemInfo.screenReader}|
202
|VM|${this._data.systemInfo.vmHint}|`;
203
204
if (this._data.systemInfo.linuxEnv) {
205
md += `\n|DESKTOP_SESSION|${this._data.systemInfo.linuxEnv.desktopSession}|
206
|XDG_CURRENT_DESKTOP|${this._data.systemInfo.linuxEnv.xdgCurrentDesktop}|
207
|XDG_SESSION_DESKTOP|${this._data.systemInfo.linuxEnv.xdgSessionDesktop}|
208
|XDG_SESSION_TYPE|${this._data.systemInfo.linuxEnv.xdgSessionType}|`;
209
}
210
211
this._data.systemInfo.remoteData.forEach(remote => {
212
if (isRemoteDiagnosticError(remote)) {
213
md += `\n\n${remote.errorMessage}`;
214
} else {
215
md += `
216
217
|Item|Value|
218
|---|---|
219
|Remote|${remote.latency ? `${remote.hostName} (latency: ${remote.latency.current.toFixed(2)}ms last, ${remote.latency.average.toFixed(2)}ms average)` : remote.hostName}|
220
|OS|${remote.machineInfo.os}|
221
|CPUs|${remote.machineInfo.cpus}|
222
|Memory (System)|${remote.machineInfo.memory}|
223
|VM|${remote.machineInfo.vmHint}|`;
224
}
225
});
226
}
227
228
md += '\n</details>';
229
230
return md;
231
}
232
233
private generateProcessInfoMd(): string {
234
return `<details>
235
<summary>Process Info</summary>
236
237
\`\`\`
238
${this._data.processInfo}
239
\`\`\`
240
241
</details>
242
`;
243
}
244
245
private generateWorkspaceInfoMd(): string {
246
return `<details>
247
<summary>Workspace Info</summary>
248
249
\`\`\`
250
${this._data.workspaceInfo};
251
\`\`\`
252
253
</details>
254
`;
255
}
256
257
private generateExperimentsInfoMd(): string {
258
return `<details>
259
<summary>A/B Experiments</summary>
260
261
\`\`\`
262
${this._data.experimentInfo}
263
\`\`\`
264
265
</details>
266
`;
267
}
268
269
private generateExtensionsMd(): string {
270
if (this._data.extensionsDisabled) {
271
return 'Extensions disabled';
272
}
273
274
const themeExclusionStr = this._data.numberOfThemeExtesions ? `\n(${this._data.numberOfThemeExtesions} theme extensions excluded)` : '';
275
276
if (!this._data.enabledNonThemeExtesions) {
277
return 'Extensions: none' + themeExclusionStr;
278
}
279
280
const tableHeader = `Extension|Author (truncated)|Version
281
---|---|---`;
282
const table = this._data.enabledNonThemeExtesions.map(e => {
283
return `${e.name}|${e.publisher?.substr(0, 3) ?? 'N/A'}|${e.version}`;
284
}).join('\n');
285
286
return `<details><summary>Extensions (${this._data.enabledNonThemeExtesions.length})</summary>
287
288
${tableHeader}
289
${table}
290
${themeExclusionStr}
291
292
</details>`;
293
}
294
}
295
296