Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/issue/browser/issueService.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 { getZoomLevel } from '../../../../base/browser/browser.js';
7
import { mainWindow } from '../../../../base/browser/window.js';
8
import { userAgent } from '../../../../base/common/platform.js';
9
import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
10
import { IExtensionManagementService } from '../../../../platform/extensionManagement/common/extensionManagement.js';
11
import { ExtensionType, IExtensionDescription } from '../../../../platform/extensions/common/extensions.js';
12
import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js';
13
import { normalizeGitHubUrl } from '../common/issueReporterUtil.js';
14
import { IOpenerService } from '../../../../platform/opener/common/opener.js';
15
import { IProductService } from '../../../../platform/product/common/productService.js';
16
import { buttonBackground, buttonForeground, buttonHoverBackground, foreground, inputActiveOptionBorder, inputBackground, inputBorder, inputForeground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, textLinkActiveForeground, textLinkForeground } from '../../../../platform/theme/common/colorRegistry.js';
17
import { IColorTheme, IThemeService } from '../../../../platform/theme/common/themeService.js';
18
import { IWorkspaceTrustManagementService } from '../../../../platform/workspace/common/workspaceTrust.js';
19
import { SIDE_BAR_BACKGROUND } from '../../../common/theme.js';
20
import { IIssueFormService, IssueReporterData, IssueReporterExtensionData, IssueReporterStyles, IWorkbenchIssueService } from '../common/issue.js';
21
import { IWorkbenchAssignmentService } from '../../../services/assignment/common/assignmentService.js';
22
import { IAuthenticationService } from '../../../services/authentication/common/authentication.js';
23
import { IWorkbenchExtensionEnablementService } from '../../../services/extensionManagement/common/extensionManagement.js';
24
import { IExtensionService } from '../../../services/extensions/common/extensions.js';
25
import { IIntegrityService } from '../../../services/integrity/common/integrity.js';
26
27
28
export class BrowserIssueService implements IWorkbenchIssueService {
29
declare readonly _serviceBrand: undefined;
30
31
constructor(
32
@IExtensionService private readonly extensionService: IExtensionService,
33
@IProductService private readonly productService: IProductService,
34
@IIssueFormService private readonly issueFormService: IIssueFormService,
35
@IThemeService private readonly themeService: IThemeService,
36
@IWorkbenchAssignmentService private readonly experimentService: IWorkbenchAssignmentService,
37
@IWorkspaceTrustManagementService private readonly workspaceTrustManagementService: IWorkspaceTrustManagementService,
38
@IIntegrityService private readonly integrityService: IIntegrityService,
39
@IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService,
40
@IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService,
41
@IAuthenticationService private readonly authenticationService: IAuthenticationService,
42
@IConfigurationService private readonly configurationService: IConfigurationService,
43
@IOpenerService private readonly openerService: IOpenerService
44
) { }
45
46
async openReporter(options: Partial<IssueReporterData>): Promise<void> {
47
// If web reporter setting is false open the old GitHub issue reporter
48
if (!this.configurationService.getValue<boolean>('issueReporter.experimental.webReporter')) {
49
const extensionId = options.extensionId;
50
// If we don't have a extensionId, treat this as a Core issue
51
if (!extensionId) {
52
if (this.productService.reportIssueUrl) {
53
const uri = this.getIssueUriFromStaticContent(this.productService.reportIssueUrl);
54
await this.openerService.open(uri, { openExternal: true });
55
return;
56
}
57
throw new Error(`No issue reporting URL configured for ${this.productService.nameLong}.`);
58
}
59
60
const selectedExtension = this.extensionService.extensions.filter(ext => ext.identifier.value === options.extensionId)[0];
61
const extensionGitHubUrl = this.getExtensionGitHubUrl(selectedExtension);
62
if (!extensionGitHubUrl) {
63
throw new Error(`Unable to find issue reporting url for ${extensionId}`);
64
}
65
const uri = this.getIssueUriFromStaticContent(`${extensionGitHubUrl}/issues/new`, selectedExtension);
66
await this.openerService.open(uri, { openExternal: true });
67
}
68
69
if (this.productService.reportIssueUrl) {
70
const theme = this.themeService.getColorTheme();
71
const experiments = await this.experimentService.getCurrentExperiments();
72
73
let githubAccessToken = '';
74
try {
75
const githubSessions = await this.authenticationService.getSessions('github');
76
const potentialSessions = githubSessions.filter(session => session.scopes.includes('repo'));
77
githubAccessToken = potentialSessions[0]?.accessToken;
78
} catch (e) {
79
// Ignore
80
}
81
82
// air on the side of caution and have false be the default
83
let isUnsupported = false;
84
try {
85
isUnsupported = !(await this.integrityService.isPure()).isPure;
86
} catch (e) {
87
// Ignore
88
}
89
90
const extensionData: IssueReporterExtensionData[] = [];
91
try {
92
const extensions = await this.extensionManagementService.getInstalled();
93
const enabledExtensions = extensions.filter(extension => this.extensionEnablementService.isEnabled(extension) || (options.extensionId && extension.identifier.id === options.extensionId));
94
extensionData.push(...enabledExtensions.map((extension): IssueReporterExtensionData => {
95
const { manifest } = extension;
96
const manifestKeys = manifest.contributes ? Object.keys(manifest.contributes) : [];
97
const isTheme = !manifest.main && !manifest.browser && manifestKeys.length === 1 && manifestKeys[0] === 'themes';
98
const isBuiltin = extension.type === ExtensionType.System;
99
return {
100
name: manifest.name,
101
publisher: manifest.publisher,
102
version: manifest.version,
103
repositoryUrl: manifest.repository && manifest.repository.url,
104
bugsUrl: manifest.bugs && manifest.bugs.url,
105
displayName: manifest.displayName,
106
id: extension.identifier.id,
107
data: options.data,
108
uri: options.uri,
109
isTheme,
110
isBuiltin,
111
extensionData: 'Extensions data loading',
112
};
113
}));
114
} catch (e) {
115
extensionData.push({
116
name: 'Workbench Issue Service',
117
publisher: 'Unknown',
118
version: 'Unknown',
119
repositoryUrl: undefined,
120
bugsUrl: undefined,
121
extensionData: `Extensions not loaded: ${e}`,
122
displayName: `Extensions not loaded: ${e}`,
123
id: 'workbench.issue',
124
isTheme: false,
125
isBuiltin: true
126
});
127
}
128
129
const issueReporterData: IssueReporterData = Object.assign({
130
styles: getIssueReporterStyles(theme),
131
zoomLevel: getZoomLevel(mainWindow),
132
enabledExtensions: extensionData,
133
experiments: experiments?.join('\n'),
134
restrictedMode: !this.workspaceTrustManagementService.isWorkspaceTrusted(),
135
isUnsupported,
136
githubAccessToken
137
}, options);
138
139
return this.issueFormService.openReporter(issueReporterData);
140
}
141
throw new Error(`No issue reporting URL configured for ${this.productService.nameLong}.`);
142
143
}
144
145
private getExtensionGitHubUrl(extension: IExtensionDescription): string {
146
if (extension.isBuiltin && this.productService.reportIssueUrl) {
147
return normalizeGitHubUrl(this.productService.reportIssueUrl);
148
}
149
150
let repositoryUrl = '';
151
152
const bugsUrl = extension?.bugs?.url;
153
const extensionUrl = extension?.repository?.url;
154
155
// If given, try to match the extension's bug url
156
if (bugsUrl && bugsUrl.match(/^https?:\/\/github\.com\/(.*)/)) {
157
repositoryUrl = normalizeGitHubUrl(bugsUrl);
158
} else if (extensionUrl && extensionUrl.match(/^https?:\/\/github\.com\/(.*)/)) {
159
repositoryUrl = normalizeGitHubUrl(extensionUrl);
160
}
161
162
return repositoryUrl;
163
}
164
165
private getIssueUriFromStaticContent(baseUri: string, extension?: IExtensionDescription): string {
166
const issueDescription = `ADD ISSUE DESCRIPTION HERE
167
168
Version: ${this.productService.version}
169
Commit: ${this.productService.commit ?? 'unknown'}
170
User Agent: ${userAgent ?? 'unknown'}
171
Embedder: ${this.productService.embedderIdentifier ?? 'unknown'}
172
${extension?.version ? `\nExtension version: ${extension.version}` : ''}
173
<!-- generated by web issue reporter -->`;
174
175
return `${baseUri}?body=${encodeURIComponent(issueDescription)}&labels=web`;
176
}
177
}
178
179
export function getIssueReporterStyles(theme: IColorTheme): IssueReporterStyles {
180
return {
181
backgroundColor: getColor(theme, SIDE_BAR_BACKGROUND),
182
color: getColor(theme, foreground),
183
textLinkColor: getColor(theme, textLinkForeground),
184
textLinkActiveForeground: getColor(theme, textLinkActiveForeground),
185
inputBackground: getColor(theme, inputBackground),
186
inputForeground: getColor(theme, inputForeground),
187
inputBorder: getColor(theme, inputBorder),
188
inputActiveBorder: getColor(theme, inputActiveOptionBorder),
189
inputErrorBorder: getColor(theme, inputValidationErrorBorder),
190
inputErrorBackground: getColor(theme, inputValidationErrorBackground),
191
inputErrorForeground: getColor(theme, inputValidationErrorForeground),
192
buttonBackground: getColor(theme, buttonBackground),
193
buttonForeground: getColor(theme, buttonForeground),
194
buttonHoverBackground: getColor(theme, buttonHoverBackground),
195
sliderActiveColor: getColor(theme, scrollbarSliderActiveBackground),
196
sliderBackgroundColor: getColor(theme, scrollbarSliderBackground),
197
sliderHoverColor: getColor(theme, scrollbarSliderHoverBackground),
198
};
199
}
200
201
function getColor(theme: IColorTheme, key: string): string | undefined {
202
const color = theme.getColor(key);
203
return color ? color.toString() : undefined;
204
}
205
206
registerSingleton(IWorkbenchIssueService, BrowserIssueService, InstantiationType.Delayed);
207
208