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