splits up process explorer and issue reporter services (#216766)

* split up process explorer and issue reporter services

* more cleanup

* fix comment

* remove comments
This commit is contained in:
Justin Chen 2024-06-20 18:56:01 -07:00 committed by GitHub
parent ee173b0e65
commit 4bbebd8af9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 629 additions and 391 deletions

View File

@ -52,8 +52,9 @@ import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemPro
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { IIssueMainService } from 'vs/platform/issue/common/issue';
import { IIssueMainService, IProcessMainService } from 'vs/platform/issue/common/issue';
import { IssueMainService } from 'vs/platform/issue/electron-main/issueMainService';
import { ProcessMainService } from 'vs/platform/issue/electron-main/processMainService';
import { IKeyboardLayoutMainService, KeyboardLayoutMainService } from 'vs/platform/keyboardLayout/electron-main/keyboardLayoutMainService';
import { ILaunchMainService, LaunchMainService } from 'vs/platform/launch/electron-main/launchMainService';
import { ILifecycleMainService, LifecycleMainPhase, ShutdownReason } from 'vs/platform/lifecycle/electron-main/lifecycleMainService';
@ -121,7 +122,6 @@ import { Lazy } from 'vs/base/common/lazy';
import { IAuxiliaryWindowsMainService } from 'vs/platform/auxiliaryWindow/electron-main/auxiliaryWindows';
import { AuxiliaryWindowsMainService } from 'vs/platform/auxiliaryWindow/electron-main/auxiliaryWindowsMainService';
import { normalizeNFC } from 'vs/base/common/normalization';
/**
* The main VS Code application. There will only ever be one instance,
* even if the user starts many instances (e.g. from the command line).
@ -1051,6 +1051,9 @@ export class CodeApplication extends Disposable {
// Issues
services.set(IIssueMainService, new SyncDescriptor(IssueMainService, [this.userEnv]));
// Process
services.set(IProcessMainService, new SyncDescriptor(ProcessMainService, [this.userEnv]));
// Encryption
services.set(IEncryptionMainService, new SyncDescriptor(EncryptionMainService));
@ -1183,6 +1186,10 @@ export class CodeApplication extends Disposable {
const issueChannel = ProxyChannel.fromService(accessor.get(IIssueMainService), disposables);
mainProcessElectronServer.registerChannel('issue', issueChannel);
// Process
const processChannel = ProxyChannel.fromService(accessor.get(IProcessMainService), disposables);
mainProcessElectronServer.registerChannel('process', processChannel);
// Encryption
const encryptionChannel = ProxyChannel.fromService(accessor.get(IEncryptionMainService), disposables);
mainProcessElectronServer.registerChannel('encryption', encryptionChannel);

View File

@ -127,18 +127,25 @@ export const IIssueMainService = createDecorator<IIssueMainService>('issueServic
export interface IIssueMainService {
readonly _serviceBrand: undefined;
stopTracing(): Promise<void>;
openReporter(data: IssueReporterData): Promise<void>;
openProcessExplorer(data: ProcessExplorerData): Promise<void>;
getSystemStatus(): Promise<string>;
// Used by the issue reporter
$getSystemInfo(): Promise<SystemInfo>;
$getPerformanceInfo(): Promise<PerformanceInfo>;
openReporter(data: IssueReporterData): Promise<void>;
$reloadWithExtensionsDisabled(): Promise<void>;
$showConfirmCloseDialog(): Promise<void>;
$showClipboardDialog(): Promise<boolean>;
$sendReporterMenu(extensionId: string, extensionName: string): Promise<IssueReporterData | undefined>;
$closeReporter(): Promise<void>;
}
export const IProcessMainService = createDecorator<IProcessMainService>('processService');
export interface IProcessMainService {
readonly _serviceBrand: undefined;
getSystemStatus(): Promise<string>;
stopTracing(): Promise<void>;
openProcessExplorer(data: ProcessExplorerData): Promise<void>;
// Used by the process explorer
$getSystemInfo(): Promise<SystemInfo>;
$getPerformanceInfo(): Promise<PerformanceInfo>;
}

View File

@ -3,35 +3,26 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { BrowserWindow, BrowserWindowConstructorOptions, contentTracing, Display, IpcMainEvent, screen } from 'electron';
import { BrowserWindow, BrowserWindowConstructorOptions, Display, screen } from 'electron';
import { arch, release, type } from 'os';
import { raceTimeout } from 'vs/base/common/async';
import { CancellationTokenSource } from 'vs/base/common/cancellation';
import { randomPath } from 'vs/base/common/extpath';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { FileAccess } from 'vs/base/common/network';
import { IProcessEnvironment, isMacintosh } from 'vs/base/common/platform';
import { listProcesses } from 'vs/base/node/ps';
import { validatedIpcMain } from 'vs/base/parts/ipc/electron-main/ipcMain';
import { localize } from 'vs/nls';
import { IDiagnosticsService, isRemoteDiagnosticError, PerformanceInfo, SystemInfo } from 'vs/platform/diagnostics/common/diagnostics';
import { IDiagnosticsMainService } from 'vs/platform/diagnostics/electron-main/diagnosticsMainService';
import { IDialogMainService } from 'vs/platform/dialogs/electron-main/dialogMainService';
import { IEnvironmentMainService } from 'vs/platform/environment/electron-main/environmentMainService';
import { IIssueMainService, IssueReporterData, IssueReporterWindowConfiguration, ProcessExplorerData, ProcessExplorerWindowConfiguration } from 'vs/platform/issue/common/issue';
import { IIssueMainService, IssueReporterData, IssueReporterWindowConfiguration } from 'vs/platform/issue/common/issue';
import { ILogService } from 'vs/platform/log/common/log';
import { INativeHostMainService } from 'vs/platform/native/electron-main/nativeHostMainService';
import product from 'vs/platform/product/common/product';
import { IProductService } from 'vs/platform/product/common/productService';
import { IIPCObjectUrl, IProtocolMainService } from 'vs/platform/protocol/electron-main/protocol';
import { IStateService } from 'vs/platform/state/node/state';
import { UtilityProcess } from 'vs/platform/utilityProcess/electron-main/utilityProcess';
import { zoomLevelToZoomFactor } from 'vs/platform/window/common/window';
import { ICodeWindow, IWindowState } from 'vs/platform/window/electron-main/window';
import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows';
const processExplorerWindowState = 'issue.processExplorerWindowState';
interface IBrowserWindowOptions {
backgroundColor: string | undefined;
title: string;
@ -50,94 +41,15 @@ export class IssueMainService implements IIssueMainService {
private issueReporterWindow: BrowserWindow | null = null;
private issueReporterParentWindow: BrowserWindow | null = null;
private processExplorerWindow: BrowserWindow | null = null;
private processExplorerParentWindow: BrowserWindow | null = null;
constructor(
private userEnv: IProcessEnvironment,
@IEnvironmentMainService private readonly environmentMainService: IEnvironmentMainService,
@ILogService private readonly logService: ILogService,
@IDiagnosticsService private readonly diagnosticsService: IDiagnosticsService,
@IDiagnosticsMainService private readonly diagnosticsMainService: IDiagnosticsMainService,
@IDialogMainService private readonly dialogMainService: IDialogMainService,
@INativeHostMainService private readonly nativeHostMainService: INativeHostMainService,
@IProtocolMainService private readonly protocolMainService: IProtocolMainService,
@IProductService private readonly productService: IProductService,
@IStateService private readonly stateService: IStateService,
@IWindowsMainService private readonly windowsMainService: IWindowsMainService,
) {
this.registerListeners();
}
//#region Register Listeners
private registerListeners(): void {
validatedIpcMain.on('vscode:listProcesses', async event => {
const processes = [];
try {
processes.push({ name: localize('local', "Local"), rootProcess: await listProcesses(process.pid) });
const remoteDiagnostics = await this.diagnosticsMainService.getRemoteDiagnostics({ includeProcesses: true });
remoteDiagnostics.forEach(data => {
if (isRemoteDiagnosticError(data)) {
processes.push({
name: data.hostName,
rootProcess: data
});
} else {
if (data.processes) {
processes.push({
name: data.hostName,
rootProcess: data.processes
});
}
}
});
} catch (e) {
this.logService.error(`Listing processes failed: ${e}`);
}
this.safeSend(event, 'vscode:listProcessesResponse', processes);
});
validatedIpcMain.on('vscode:workbenchCommand', (_: unknown, commandInfo: { id: any; from: any; args: any }) => {
const { id, from, args } = commandInfo;
let parentWindow: BrowserWindow | null;
switch (from) {
case 'processExplorer':
parentWindow = this.processExplorerParentWindow;
break;
default:
// The issue reporter does not use this anymore.
throw new Error(`Unexpected command source: ${from}`);
}
parentWindow?.webContents.send('vscode:runAction', { id, from, args });
});
validatedIpcMain.on('vscode:closeProcessExplorer', event => {
this.processExplorerWindow?.close();
});
validatedIpcMain.on('vscode:pidToNameRequest', async event => {
const mainProcessInfo = await this.diagnosticsMainService.getMainDiagnostics();
const pidToNames: [number, string][] = [];
for (const window of mainProcessInfo.windows) {
pidToNames.push([window.pid, `window [${window.id}] (${window.title})`]);
}
for (const { pid, name } of UtilityProcess.getAll()) {
pidToNames.push([pid, name]);
}
this.safeSend(event, 'vscode:pidToNameResponse', pidToNames);
});
}
//#endregion
) { }
//#region Used by renderer
@ -196,125 +108,9 @@ export class IssueMainService implements IIssueMainService {
}
}
async openProcessExplorer(data: ProcessExplorerData): Promise<void> {
if (!this.processExplorerWindow) {
this.processExplorerParentWindow = BrowserWindow.getFocusedWindow();
if (this.processExplorerParentWindow) {
const processExplorerDisposables = new DisposableStore();
const processExplorerWindowConfigUrl = processExplorerDisposables.add(this.protocolMainService.createIPCObjectUrl<ProcessExplorerWindowConfiguration>());
const savedPosition = this.stateService.getItem<IWindowState>(processExplorerWindowState, undefined);
const position = isStrictWindowState(savedPosition) ? savedPosition : this.getWindowPosition(this.processExplorerParentWindow, 800, 500);
this.processExplorerWindow = this.createBrowserWindow(position, processExplorerWindowConfigUrl, {
backgroundColor: data.styles.backgroundColor,
title: localize('processExplorer', "Process Explorer"),
zoomLevel: data.zoomLevel,
alwaysOnTop: true
}, 'process-explorer');
// Store into config object URL
processExplorerWindowConfigUrl.update({
appRoot: this.environmentMainService.appRoot,
windowId: this.processExplorerWindow.id,
userEnv: this.userEnv,
data,
product
});
this.processExplorerWindow.loadURL(
FileAccess.asBrowserUri(`vs/code/electron-sandbox/processExplorer/processExplorer${this.environmentMainService.isBuilt ? '' : '-dev'}.html`).toString(true)
);
this.processExplorerWindow.on('close', () => {
this.processExplorerWindow = null;
processExplorerDisposables.dispose();
});
this.processExplorerParentWindow.on('close', () => {
if (this.processExplorerWindow) {
this.processExplorerWindow.close();
this.processExplorerWindow = null;
processExplorerDisposables.dispose();
}
});
const storeState = () => {
if (!this.processExplorerWindow) {
return;
}
const size = this.processExplorerWindow.getSize();
const position = this.processExplorerWindow.getPosition();
if (!size || !position) {
return;
}
const state: IWindowState = {
width: size[0],
height: size[1],
x: position[0],
y: position[1]
};
this.stateService.setItem(processExplorerWindowState, state);
};
this.processExplorerWindow.on('moved', storeState);
this.processExplorerWindow.on('resized', storeState);
}
}
if (this.processExplorerWindow) {
this.focusWindow(this.processExplorerWindow);
}
}
async stopTracing(): Promise<void> {
if (!this.environmentMainService.args.trace) {
return; // requires tracing to be on
}
const path = await contentTracing.stopRecording(`${randomPath(this.environmentMainService.userHome.fsPath, this.productService.applicationName)}.trace.txt`);
// Inform user to report an issue
await this.dialogMainService.showMessageBox({
type: 'info',
message: localize('trace.message', "Successfully created the trace file"),
detail: localize('trace.detail', "Please create an issue and manually attach the following file:\n{0}", path),
buttons: [localize({ key: 'trace.ok', comment: ['&& denotes a mnemonic'] }, "&&OK")],
}, BrowserWindow.getFocusedWindow() ?? undefined);
// Show item in explorer
this.nativeHostMainService.showItemInFolder(undefined, path);
}
async getSystemStatus(): Promise<string> {
const [info, remoteData] = await Promise.all([this.diagnosticsMainService.getMainDiagnostics(), this.diagnosticsMainService.getRemoteDiagnostics({ includeProcesses: false, includeWorkspaceMetadata: false })]);
return this.diagnosticsService.getDiagnostics(info, remoteData);
}
//#endregion
//#region used by issue reporter window
async $getSystemInfo(): Promise<SystemInfo> {
const [info, remoteData] = await Promise.all([this.diagnosticsMainService.getMainDiagnostics(), this.diagnosticsMainService.getRemoteDiagnostics({ includeProcesses: false, includeWorkspaceMetadata: false })]);
const msg = await this.diagnosticsService.getSystemInfo(info, remoteData);
return msg;
}
async $getPerformanceInfo(): Promise<PerformanceInfo> {
try {
const [info, remoteData] = await Promise.all([this.diagnosticsMainService.getMainDiagnostics(), this.diagnosticsMainService.getRemoteDiagnostics({ includeProcesses: true, includeWorkspaceMetadata: true })]);
return await this.diagnosticsService.getPerformanceInfo(info, remoteData);
} catch (error) {
this.logService.warn('issueService#getPerformanceInfo ', error.message);
throw error;
}
}
async $reloadWithExtensionsDisabled(): Promise<void> {
if (this.issueReporterParentWindow) {
try {
@ -389,10 +185,6 @@ export class IssueMainService implements IIssueMainService {
this.issueReporterWindow?.close();
}
async closeProcessExplorer(): Promise<void> {
this.processExplorerWindow?.close();
}
//#endregion
private focusWindow(window: BrowserWindow): void {
@ -403,12 +195,6 @@ export class IssueMainService implements IIssueMainService {
window.focus();
}
private safeSend(event: IpcMainEvent, channel: string, ...args: unknown[]): void {
if (!event.sender.isDestroyed()) {
event.sender.send(channel, ...args);
}
}
private createBrowserWindow<T>(position: IWindowState, ipcObjectUrl: IIPCObjectUrl<T>, options: IBrowserWindowOptions, windowKind: string): BrowserWindow {
const window = new BrowserWindow({
fullscreen: false,
@ -509,15 +295,3 @@ export class IssueMainService implements IIssueMainService {
return state;
}
}
function isStrictWindowState(obj: unknown): obj is IStrictWindowState {
if (typeof obj !== 'object' || obj === null) {
return false;
}
return (
'x' in obj &&
'y' in obj &&
'width' in obj &&
'height' in obj
);
}

View File

@ -0,0 +1,375 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { BrowserWindow, BrowserWindowConstructorOptions, contentTracing, Display, IpcMainEvent, screen } from 'electron';
import { randomPath } from 'vs/base/common/extpath';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { FileAccess } from 'vs/base/common/network';
import { IProcessEnvironment, isMacintosh } from 'vs/base/common/platform';
import { listProcesses } from 'vs/base/node/ps';
import { validatedIpcMain } from 'vs/base/parts/ipc/electron-main/ipcMain';
import { localize } from 'vs/nls';
import { IDiagnosticsService, isRemoteDiagnosticError, PerformanceInfo, SystemInfo } from 'vs/platform/diagnostics/common/diagnostics';
import { IDiagnosticsMainService } from 'vs/platform/diagnostics/electron-main/diagnosticsMainService';
import { IDialogMainService } from 'vs/platform/dialogs/electron-main/dialogMainService';
import { IEnvironmentMainService } from 'vs/platform/environment/electron-main/environmentMainService';
import { IProcessMainService, ProcessExplorerData, ProcessExplorerWindowConfiguration } from 'vs/platform/issue/common/issue';
import { ILogService } from 'vs/platform/log/common/log';
import { INativeHostMainService } from 'vs/platform/native/electron-main/nativeHostMainService';
import product from 'vs/platform/product/common/product';
import { IProductService } from 'vs/platform/product/common/productService';
import { IIPCObjectUrl, IProtocolMainService } from 'vs/platform/protocol/electron-main/protocol';
import { IStateService } from 'vs/platform/state/node/state';
import { UtilityProcess } from 'vs/platform/utilityProcess/electron-main/utilityProcess';
import { zoomLevelToZoomFactor } from 'vs/platform/window/common/window';
import { IWindowState } from 'vs/platform/window/electron-main/window';
const processExplorerWindowState = 'issue.processExplorerWindowState';
interface IBrowserWindowOptions {
backgroundColor: string | undefined;
title: string;
zoomLevel: number;
alwaysOnTop: boolean;
}
type IStrictWindowState = Required<Pick<IWindowState, 'x' | 'y' | 'width' | 'height'>>;
export class ProcessMainService implements IProcessMainService {
declare readonly _serviceBrand: undefined;
private static readonly DEFAULT_BACKGROUND_COLOR = '#1E1E1E';
private processExplorerWindow: BrowserWindow | null = null;
private processExplorerParentWindow: BrowserWindow | null = null;
constructor(
private userEnv: IProcessEnvironment,
@IEnvironmentMainService private readonly environmentMainService: IEnvironmentMainService,
@ILogService private readonly logService: ILogService,
@IDiagnosticsService private readonly diagnosticsService: IDiagnosticsService,
@IDiagnosticsMainService private readonly diagnosticsMainService: IDiagnosticsMainService,
@IDialogMainService private readonly dialogMainService: IDialogMainService,
@INativeHostMainService private readonly nativeHostMainService: INativeHostMainService,
@IProtocolMainService private readonly protocolMainService: IProtocolMainService,
@IProductService private readonly productService: IProductService,
@IStateService private readonly stateService: IStateService,
) {
this.registerListeners();
}
//#region Register Listeners
private registerListeners(): void {
validatedIpcMain.on('vscode:listProcesses', async event => {
const processes = [];
try {
processes.push({ name: localize('local', "Local"), rootProcess: await listProcesses(process.pid) });
const remoteDiagnostics = await this.diagnosticsMainService.getRemoteDiagnostics({ includeProcesses: true });
remoteDiagnostics.forEach(data => {
if (isRemoteDiagnosticError(data)) {
processes.push({
name: data.hostName,
rootProcess: data
});
} else {
if (data.processes) {
processes.push({
name: data.hostName,
rootProcess: data.processes
});
}
}
});
} catch (e) {
this.logService.error(`Listing processes failed: ${e}`);
}
this.safeSend(event, 'vscode:listProcessesResponse', processes);
});
validatedIpcMain.on('vscode:workbenchCommand', (_: unknown, commandInfo: { id: any; from: any; args: any }) => {
const { id, from, args } = commandInfo;
let parentWindow: BrowserWindow | null;
switch (from) {
case 'processExplorer':
parentWindow = this.processExplorerParentWindow;
break;
default:
// The issue reporter does not use this anymore.
throw new Error(`Unexpected command source: ${from}`);
}
parentWindow?.webContents.send('vscode:runAction', { id, from, args });
});
validatedIpcMain.on('vscode:closeProcessExplorer', event => {
this.processExplorerWindow?.close();
});
validatedIpcMain.on('vscode:pidToNameRequest', async event => {
const mainProcessInfo = await this.diagnosticsMainService.getMainDiagnostics();
const pidToNames: [number, string][] = [];
for (const window of mainProcessInfo.windows) {
pidToNames.push([window.pid, `window [${window.id}] (${window.title})`]);
}
for (const { pid, name } of UtilityProcess.getAll()) {
pidToNames.push([pid, name]);
}
this.safeSend(event, 'vscode:pidToNameResponse', pidToNames);
});
}
async openProcessExplorer(data: ProcessExplorerData): Promise<void> {
if (!this.processExplorerWindow) {
this.processExplorerParentWindow = BrowserWindow.getFocusedWindow();
if (this.processExplorerParentWindow) {
const processExplorerDisposables = new DisposableStore();
const processExplorerWindowConfigUrl = processExplorerDisposables.add(this.protocolMainService.createIPCObjectUrl<ProcessExplorerWindowConfiguration>());
const savedPosition = this.stateService.getItem<IWindowState>(processExplorerWindowState, undefined);
const position = isStrictWindowState(savedPosition) ? savedPosition : this.getWindowPosition(this.processExplorerParentWindow, 800, 500);
this.processExplorerWindow = this.createBrowserWindow(position, processExplorerWindowConfigUrl, {
backgroundColor: data.styles.backgroundColor,
title: localize('processExplorer', "Process Explorer"),
zoomLevel: data.zoomLevel,
alwaysOnTop: true
}, 'process-explorer');
// Store into config object URL
processExplorerWindowConfigUrl.update({
appRoot: this.environmentMainService.appRoot,
windowId: this.processExplorerWindow.id,
userEnv: this.userEnv,
data,
product
});
this.processExplorerWindow.loadURL(
FileAccess.asBrowserUri(`vs/code/electron-sandbox/processExplorer/processExplorer${this.environmentMainService.isBuilt ? '' : '-dev'}.html`).toString(true)
);
this.processExplorerWindow.on('close', () => {
this.processExplorerWindow = null;
processExplorerDisposables.dispose();
});
this.processExplorerParentWindow.on('close', () => {
if (this.processExplorerWindow) {
this.processExplorerWindow.close();
this.processExplorerWindow = null;
processExplorerDisposables.dispose();
}
});
const storeState = () => {
if (!this.processExplorerWindow) {
return;
}
const size = this.processExplorerWindow.getSize();
const position = this.processExplorerWindow.getPosition();
if (!size || !position) {
return;
}
const state: IWindowState = {
width: size[0],
height: size[1],
x: position[0],
y: position[1]
};
this.stateService.setItem(processExplorerWindowState, state);
};
this.processExplorerWindow.on('moved', storeState);
this.processExplorerWindow.on('resized', storeState);
}
}
if (this.processExplorerWindow) {
this.focusWindow(this.processExplorerWindow);
}
}
private focusWindow(window: BrowserWindow): void {
if (window.isMinimized()) {
window.restore();
}
window.focus();
}
private getWindowPosition(parentWindow: BrowserWindow, defaultWidth: number, defaultHeight: number): IStrictWindowState {
// We want the new window to open on the same display that the parent is in
let displayToUse: Display | undefined;
const displays = screen.getAllDisplays();
// Single Display
if (displays.length === 1) {
displayToUse = displays[0];
}
// Multi Display
else {
// on mac there is 1 menu per window so we need to use the monitor where the cursor currently is
if (isMacintosh) {
const cursorPoint = screen.getCursorScreenPoint();
displayToUse = screen.getDisplayNearestPoint(cursorPoint);
}
// if we have a last active window, use that display for the new window
if (!displayToUse && parentWindow) {
displayToUse = screen.getDisplayMatching(parentWindow.getBounds());
}
// fallback to primary display or first display
if (!displayToUse) {
displayToUse = screen.getPrimaryDisplay() || displays[0];
}
}
const displayBounds = displayToUse.bounds;
const state: IStrictWindowState = {
width: defaultWidth,
height: defaultHeight,
x: displayBounds.x + (displayBounds.width / 2) - (defaultWidth / 2),
y: displayBounds.y + (displayBounds.height / 2) - (defaultHeight / 2)
};
if (displayBounds.width > 0 && displayBounds.height > 0 /* Linux X11 sessions sometimes report wrong display bounds */) {
if (state.x < displayBounds.x) {
state.x = displayBounds.x; // prevent window from falling out of the screen to the left
}
if (state.y < displayBounds.y) {
state.y = displayBounds.y; // prevent window from falling out of the screen to the top
}
if (state.x > (displayBounds.x + displayBounds.width)) {
state.x = displayBounds.x; // prevent window from falling out of the screen to the right
}
if (state.y > (displayBounds.y + displayBounds.height)) {
state.y = displayBounds.y; // prevent window from falling out of the screen to the bottom
}
if (state.width > displayBounds.width) {
state.width = displayBounds.width; // prevent window from exceeding display bounds width
}
if (state.height > displayBounds.height) {
state.height = displayBounds.height; // prevent window from exceeding display bounds height
}
}
return state;
}
async stopTracing(): Promise<void> {
if (!this.environmentMainService.args.trace) {
return; // requires tracing to be on
}
const path = await contentTracing.stopRecording(`${randomPath(this.environmentMainService.userHome.fsPath, this.productService.applicationName)}.trace.txt`);
// Inform user to report an issue
await this.dialogMainService.showMessageBox({
type: 'info',
message: localize('trace.message', "Successfully created the trace file"),
detail: localize('trace.detail', "Please create an issue and manually attach the following file:\n{0}", path),
buttons: [localize({ key: 'trace.ok', comment: ['&& denotes a mnemonic'] }, "&&OK")],
}, BrowserWindow.getFocusedWindow() ?? undefined);
// Show item in explorer
this.nativeHostMainService.showItemInFolder(undefined, path);
}
async getSystemStatus(): Promise<string> {
const [info, remoteData] = await Promise.all([this.diagnosticsMainService.getMainDiagnostics(), this.diagnosticsMainService.getRemoteDiagnostics({ includeProcesses: false, includeWorkspaceMetadata: false })]);
return this.diagnosticsService.getDiagnostics(info, remoteData);
}
async $getSystemInfo(): Promise<SystemInfo> {
const [info, remoteData] = await Promise.all([this.diagnosticsMainService.getMainDiagnostics(), this.diagnosticsMainService.getRemoteDiagnostics({ includeProcesses: false, includeWorkspaceMetadata: false })]);
const msg = await this.diagnosticsService.getSystemInfo(info, remoteData);
return msg;
}
async $getPerformanceInfo(): Promise<PerformanceInfo> {
try {
const [info, remoteData] = await Promise.all([this.diagnosticsMainService.getMainDiagnostics(), this.diagnosticsMainService.getRemoteDiagnostics({ includeProcesses: true, includeWorkspaceMetadata: true })]);
return await this.diagnosticsService.getPerformanceInfo(info, remoteData);
} catch (error) {
this.logService.warn('issueService#getPerformanceInfo ', error.message);
throw error;
}
}
private createBrowserWindow<T>(position: IWindowState, ipcObjectUrl: IIPCObjectUrl<T>, options: IBrowserWindowOptions, windowKind: string): BrowserWindow {
const window = new BrowserWindow({
fullscreen: false,
skipTaskbar: false,
resizable: true,
width: position.width,
height: position.height,
minWidth: 300,
minHeight: 200,
x: position.x,
y: position.y,
title: options.title,
backgroundColor: options.backgroundColor || ProcessMainService.DEFAULT_BACKGROUND_COLOR,
webPreferences: {
preload: FileAccess.asFileUri('vs/base/parts/sandbox/electron-sandbox/preload.js').fsPath,
additionalArguments: [`--vscode-window-config=${ipcObjectUrl.resource.toString()}`],
v8CacheOptions: this.environmentMainService.useCodeCache ? 'bypassHeatCheck' : 'none',
enableWebSQL: false,
spellcheck: false,
zoomFactor: zoomLevelToZoomFactor(options.zoomLevel),
sandbox: true
},
alwaysOnTop: options.alwaysOnTop,
experimentalDarkMode: true
} as BrowserWindowConstructorOptions & { experimentalDarkMode: boolean });
window.setMenuBarVisibility(false);
return window;
}
private safeSend(event: IpcMainEvent, channel: string, ...args: unknown[]): void {
if (!event.sender.isDestroyed()) {
event.sender.send(channel, ...args);
}
}
async closeProcessExplorer(): Promise<void> {
this.processExplorerWindow?.close();
}
}
function isStrictWindowState(obj: unknown): obj is IStrictWindowState {
if (typeof obj !== 'object' || obj === null) {
return false;
}
return (
'x' in obj &&
'y' in obj &&
'width' in obj &&
'height' in obj
);
}

View File

@ -240,26 +240,6 @@ export class BaseIssueReporterService extends Disposable {
}
public setEventHandlers(): void {
this.addEventListener('issue-type', 'change', (event: Event) => {
const issueType = parseInt((<HTMLInputElement>event.target).value);
this.issueReporterModel.update({ issueType: issueType });
if (issueType === IssueType.PerformanceIssue && !this.receivedPerformanceInfo) {
this.issueMainService.$getPerformanceInfo().then(info => {
this.updatePerformanceInfo(info as Partial<IssueReporterData>);
});
}
// Resets placeholder
const descriptionTextArea = <HTMLInputElement>this.getElementById('issue-title');
if (descriptionTextArea) {
descriptionTextArea.placeholder = localize('undefinedPlaceholder', "Please enter a title");
}
this.updatePreviewButtonState();
this.setSourceOptions();
this.render();
});
(['includeSystemInfo', 'includeProcessInfo', 'includeWorkspaceInfo', 'includeExtensions', 'includeExperiments', 'includeExtensionData'] as const).forEach(elementId => {
this.addEventListener(elementId, 'click', (event: Event) => {
event.stopPropagation();

View File

@ -72,6 +72,20 @@ export class IssueWebReporter extends BaseIssueReporterService {
public override setEventHandlers(): void {
super.setEventHandlers();
this.addEventListener('issue-type', 'change', (event: Event) => {
const issueType = parseInt((<HTMLInputElement>event.target).value);
this.issueReporterModel.update({ issueType: issueType });
// Resets placeholder
const descriptionTextArea = <HTMLInputElement>this.getElementById('issue-title');
if (descriptionTextArea) {
descriptionTextArea.placeholder = localize('undefinedPlaceholder', "Please enter a title");
}
this.updatePreviewButtonState();
this.setSourceOptions();
this.render();
});
this.previewButton.onDidClick(async () => {
this.delayedSubmit.trigger(async () => {
this.createIssue();

View File

@ -10,5 +10,12 @@ export const IWorkbenchIssueService = createDecorator<IWorkbenchIssueService>('w
export interface IWorkbenchIssueService {
readonly _serviceBrand: undefined;
openReporter(dataOverrides?: Partial<IssueReporterData>): Promise<void>;
}
export const IWorkbenchProcessService = createDecorator<IWorkbenchProcessService>('workbenchProcessService');
export interface IWorkbenchProcessService {
readonly _serviceBrand: undefined;
openProcessExplorer(): Promise<void>;
}

View File

@ -4,9 +4,8 @@
*--------------------------------------------------------------------------------------------*/
import { localize, localize2 } from 'vs/nls';
import { MenuRegistry, MenuId, registerAction2, Action2 } from 'vs/platform/actions/common/actions';
import { registerAction2, Action2 } from 'vs/platform/actions/common/actions';
import { IWorkbenchIssueService } from 'vs/workbench/contrib/issue/common/issue';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { BaseIssueContribution } from 'vs/workbench/contrib/issue/common/issue.contribution';
import { IProductService } from 'vs/platform/product/common/productService';
import { Registry } from 'vs/platform/registry/common/platform';
@ -14,11 +13,7 @@ import { Extensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common
import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
import { Categories } from 'vs/platform/action/common/actionCommonCategories';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { INativeEnvironmentService } from 'vs/platform/environment/common/environment';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { INativeHostService } from 'vs/platform/native/common/native';
import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress';
import { IIssueMainService, IssueType } from 'vs/platform/issue/common/issue';
import { IssueType } from 'vs/platform/issue/common/issue';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IQuickAccessRegistry, Extensions as QuickAccessExtensions } from 'vs/platform/quickinput/common/quickAccess';
@ -27,7 +22,6 @@ import 'vs/workbench/contrib/issue/electron-sandbox/issueMainService';
import 'vs/workbench/contrib/issue/electron-sandbox/issueService';
import 'vs/workbench/contrib/issue/browser/issueTroubleshoot';
//#region Issue Contribution
class NativeIssueContribution extends BaseIssueContribution {
@ -87,87 +81,10 @@ class ReportPerformanceIssueUsingReporterAction extends Action2 {
}
override async run(accessor: ServicesAccessor): Promise<void> {
const issueService = accessor.get(IWorkbenchIssueService);
const issueService = accessor.get(IWorkbenchIssueService); // later can just get IIssueFormService
return issueService.openReporter({ issueType: IssueType.PerformanceIssue });
}
}
//#endregion
//#region Commands
class OpenProcessExplorer extends Action2 {
static readonly ID = 'workbench.action.openProcessExplorer';
constructor() {
super({
id: OpenProcessExplorer.ID,
title: localize2('openProcessExplorer', 'Open Process Explorer'),
category: Categories.Developer,
f1: true
});
}
override async run(accessor: ServicesAccessor): Promise<void> {
const issueService = accessor.get(IWorkbenchIssueService);
return issueService.openProcessExplorer();
}
}
registerAction2(OpenProcessExplorer);
MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, {
group: '5_tools',
command: {
id: OpenProcessExplorer.ID,
title: localize({ key: 'miOpenProcessExplorerer', comment: ['&& denotes a mnemonic'] }, "Open &&Process Explorer")
},
order: 2
});
class StopTracing extends Action2 {
static readonly ID = 'workbench.action.stopTracing';
constructor() {
super({
id: StopTracing.ID,
title: localize2('stopTracing', 'Stop Tracing'),
category: Categories.Developer,
f1: true
});
}
override async run(accessor: ServicesAccessor): Promise<void> {
const issueService = accessor.get(IIssueMainService);
const environmentService = accessor.get(INativeEnvironmentService);
const dialogService = accessor.get(IDialogService);
const nativeHostService = accessor.get(INativeHostService);
const progressService = accessor.get(IProgressService);
if (!environmentService.args.trace) {
const { confirmed } = await dialogService.confirm({
message: localize('stopTracing.message', "Tracing requires to launch with a '--trace' argument"),
primaryButton: localize({ key: 'stopTracing.button', comment: ['&& denotes a mnemonic'] }, "&&Relaunch and Enable Tracing"),
});
if (confirmed) {
return nativeHostService.relaunch({ addArgs: ['--trace'] });
}
}
await progressService.withProgress({
location: ProgressLocation.Dialog,
title: localize('stopTracing.title', "Creating trace file..."),
cancellable: false,
detail: localize('stopTracing.detail', "This can take up to one minute to complete.")
}, () => issueService.stopTracing());
}
}
registerAction2(StopTracing);
CommandsRegistry.registerCommand('_issues.getSystemStatus', (accessor) => {
return accessor.get(IIssueMainService).getSystemStatus();
});
//#endregion
// #endregion

View File

@ -4,6 +4,8 @@
*--------------------------------------------------------------------------------------------*/
import { registerMainProcessRemoteService } from 'vs/platform/ipc/electron-sandbox/services';
import { IIssueMainService } from 'vs/platform/issue/common/issue';
import { IIssueMainService, IProcessMainService } from 'vs/platform/issue/common/issue';
registerMainProcessRemoteService(IIssueMainService, 'issue');
registerMainProcessRemoteService(IProcessMainService, 'process');

View File

@ -15,12 +15,13 @@ import { ServiceCollection } from 'vs/platform/instantiation/common/serviceColle
import { IMainProcessService } from 'vs/platform/ipc/common/mainProcessService';
import { ElectronIPCMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService';
import { registerMainProcessRemoteService } from 'vs/platform/ipc/electron-sandbox/services';
import { IIssueMainService, IssueReporterWindowConfiguration } from 'vs/platform/issue/common/issue';
import { IIssueMainService, IProcessMainService, IssueReporterWindowConfiguration } from 'vs/platform/issue/common/issue';
import { INativeHostService } from 'vs/platform/native/common/native';
import { NativeHostService } from 'vs/platform/native/common/nativeHostService';
import { IssueReporter2 } from 'vs/workbench/contrib/issue/electron-sandbox/issueReporterService2';
import { mainWindow } from 'vs/base/browser/window';
export function startup(configuration: IssueReporterWindowConfiguration) {
const platformClass = isWindows ? 'windows' : isLinux ? 'linux' : 'mac';
mainWindow.document.body.classList.add(platformClass); // used by our fonts
@ -50,3 +51,4 @@ function initServices(windowId: number) {
}
registerMainProcessRemoteService(IIssueMainService, 'issue');
registerMainProcessRemoteService(IProcessMainService, 'process');

View File

@ -19,7 +19,7 @@ import { URI } from 'vs/base/common/uri';
import { IssueReporterModel, IssueReporterData as IssueReporterModelData } from 'vs/workbench/contrib/issue/browser/issueReporterModel';
import { localize } from 'vs/nls';
import { isRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnostics';
import { IIssueMainService, IssueReporterData, IssueReporterExtensionData, IssueReporterStyles, IssueReporterWindowConfiguration, IssueType } from 'vs/platform/issue/common/issue';
import { IIssueMainService, IProcessMainService, IssueReporterData, IssueReporterExtensionData, IssueReporterStyles, IssueReporterWindowConfiguration, IssueType } from 'vs/platform/issue/common/issue';
import { normalizeGitHubUrl } from 'vs/platform/issue/common/issueReporterUtil';
import { INativeHostService } from 'vs/platform/native/common/native';
import { getIconsStyleSheet } from 'vs/platform/theme/browser/iconsStyleSheet';
@ -59,7 +59,8 @@ export class IssueReporter extends Disposable {
constructor(
private readonly configuration: IssueReporterWindowConfiguration,
@INativeHostService private readonly nativeHostService: INativeHostService,
@IIssueMainService private readonly issueMainService: IIssueMainService
@IIssueMainService private readonly issueMainService: IIssueMainService,
@IProcessMainService private readonly processMainService: IProcessMainService
) {
super();
const targetExtension = configuration.data.extensionId ? configuration.data.enabledExtensions.find(extension => extension.id.toLocaleLowerCase() === configuration.data.extensionId?.toLocaleLowerCase()) : undefined;
@ -107,7 +108,7 @@ export class IssueReporter extends Disposable {
}
}
this.issueMainService.$getSystemInfo().then(info => {
this.processMainService.$getSystemInfo().then(info => {
this.issueReporterModel.update({ systemInfo: info });
this.receivedSystemInfo = true;
@ -115,7 +116,7 @@ export class IssueReporter extends Disposable {
this.updatePreviewButtonState();
});
if (configuration.data.issueType === IssueType.PerformanceIssue) {
this.issueMainService.$getPerformanceInfo().then(info => {
this.processMainService.$getPerformanceInfo().then(info => {
this.updatePerformanceInfo(info as Partial<IssueReporterData>);
});
}
@ -286,7 +287,7 @@ export class IssueReporter extends Disposable {
const issueType = parseInt((<HTMLInputElement>event.target).value);
this.issueReporterModel.update({ issueType: issueType });
if (issueType === IssueType.PerformanceIssue && !this.receivedPerformanceInfo) {
this.issueMainService.$getPerformanceInfo().then(info => {
this.processMainService.$getPerformanceInfo().then(info => {
this.updatePerformanceInfo(info as Partial<IssueReporterData>);
});
}

View File

@ -12,7 +12,7 @@ import { ThemeIcon } from 'vs/base/common/themables';
import { URI } from 'vs/base/common/uri';
import { localize } from 'vs/nls';
import { isRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnostics';
import { IIssueMainService, IssueReporterData, IssueReporterExtensionData, IssueReporterWindowConfiguration, IssueType } from 'vs/platform/issue/common/issue';
import { IIssueMainService, IProcessMainService, IssueReporterData, IssueReporterExtensionData, IssueReporterWindowConfiguration, IssueType } from 'vs/platform/issue/common/issue';
import { INativeHostService } from 'vs/platform/native/common/native';
import { applyZoom, zoomIn, zoomOut } from 'vs/platform/window/electron-sandbox/window';
import { BaseIssueReporterService, hide, show } from 'vs/workbench/contrib/issue/browser/issue';
@ -24,14 +24,17 @@ const MAX_URL_LENGTH = 7500;
export class IssueReporter2 extends BaseIssueReporterService {
private readonly processMainService: IProcessMainService;
constructor(
private readonly configuration: IssueReporterWindowConfiguration,
@INativeHostService private readonly nativeHostService: INativeHostService,
@IIssueMainService issueMainService: IIssueMainService
@IIssueMainService issueMainService: IIssueMainService,
@IProcessMainService processMainService: IProcessMainService
) {
super(configuration.disableExtensions, configuration.data, configuration.os, configuration.product, mainWindow, false, issueMainService);
this.issueMainService.$getSystemInfo().then(info => {
this.processMainService = processMainService;
this.processMainService.$getSystemInfo().then(info => {
this.issueReporterModel.update({ systemInfo: info });
this.receivedSystemInfo = true;
@ -39,7 +42,7 @@ export class IssueReporter2 extends BaseIssueReporterService {
this.updatePreviewButtonState();
});
if (configuration.data.issueType === IssueType.PerformanceIssue) {
this.issueMainService.$getPerformanceInfo().then(info => {
this.processMainService.$getPerformanceInfo().then(info => {
this.updatePerformanceInfo(info as Partial<IssueReporterData>);
});
}
@ -81,6 +84,26 @@ export class IssueReporter2 extends BaseIssueReporterService {
public override setEventHandlers(): void {
super.setEventHandlers();
this.addEventListener('issue-type', 'change', (event: Event) => {
const issueType = parseInt((<HTMLInputElement>event.target).value);
this.issueReporterModel.update({ issueType: issueType });
if (issueType === IssueType.PerformanceIssue && !this.receivedPerformanceInfo) {
this.processMainService.$getPerformanceInfo().then(info => {
this.updatePerformanceInfo(info as Partial<IssueReporterData>);
});
}
// Resets placeholder
const descriptionTextArea = <HTMLInputElement>this.getElementById('issue-title');
if (descriptionTextArea) {
descriptionTextArea.placeholder = localize('undefinedPlaceholder', "Please enter a title");
}
this.updatePreviewButtonState();
this.setSourceOptions();
this.render();
});
// Keep all event listerns involving window and issue creation
this.previewButton.onDidClick(async () => {
this.delayedSubmit.trigger(async () => {

View File

@ -4,20 +4,17 @@
*--------------------------------------------------------------------------------------------*/
import { getZoomLevel } from 'vs/base/browser/browser';
import { platform } from 'vs/base/common/process';
import { ipcRenderer } from 'vs/base/parts/sandbox/electron-sandbox/globals';
import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { ExtensionIdentifier, ExtensionType, ExtensionIdentifierSet } from 'vs/platform/extensions/common/extensions';
import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IIssueMainService, IssueReporterData, IssueReporterExtensionData, IssueReporterStyles, ProcessExplorerData } from 'vs/platform/issue/common/issue';
import { IProductService } from 'vs/platform/product/common/productService';
import { activeContrastBorder, buttonBackground, buttonForeground, buttonHoverBackground, editorBackground, editorForeground, foreground, inputActiveOptionBorder, inputBackground, inputBorder, inputForeground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, listActiveSelectionBackground, listActiveSelectionForeground, listFocusBackground, listFocusForeground, listFocusOutline, listHoverBackground, listHoverForeground, scrollbarShadow, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, textLinkActiveForeground, textLinkForeground } from 'vs/platform/theme/common/colorRegistry';
import { IIssueMainService, IssueReporterData, IssueReporterExtensionData, IssueReporterStyles } from 'vs/platform/issue/common/issue';
import { buttonBackground, buttonForeground, buttonHoverBackground, foreground, inputActiveOptionBorder, inputBackground, inputBorder, inputForeground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, textLinkActiveForeground, textLinkForeground } from 'vs/platform/theme/common/colorRegistry';
import { IColorTheme, IThemeService } from 'vs/platform/theme/common/themeService';
import { IWorkspaceTrustManagementService } from 'vs/platform/workspace/common/workspaceTrust';
import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
import { IWorkbenchAssignmentService } from 'vs/workbench/services/assignment/common/assignmentService';
import { IAuthenticationService } from 'vs/workbench/services/authentication/common/authentication';
import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService';
import { IWorkbenchExtensionEnablementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { IIntegrityService } from 'vs/workbench/services/integrity/common/integrity';
import { IWorkbenchIssueService } from 'vs/workbench/contrib/issue/common/issue';
@ -34,9 +31,7 @@ export class NativeIssueService implements IWorkbenchIssueService {
@IThemeService private readonly themeService: IThemeService,
@IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService,
@IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService,
@INativeWorkbenchEnvironmentService private readonly environmentService: INativeWorkbenchEnvironmentService,
@IWorkspaceTrustManagementService private readonly workspaceTrustManagementService: IWorkspaceTrustManagementService,
@IProductService private readonly productService: IProductService,
@IWorkbenchAssignmentService private readonly experimentService: IWorkbenchAssignmentService,
@IAuthenticationService private readonly authenticationService: IAuthenticationService,
@IIntegrityService private readonly integrityService: IIntegrityService,
@ -153,34 +148,6 @@ export class NativeIssueService implements IWorkbenchIssueService {
return this.issueMainService.openReporter(issueReporterData);
}
openProcessExplorer(): Promise<void> {
const theme = this.themeService.getColorTheme();
const data: ProcessExplorerData = {
pid: this.environmentService.mainPid,
zoomLevel: getZoomLevel(mainWindow),
styles: {
backgroundColor: getColor(theme, editorBackground),
color: getColor(theme, editorForeground),
listHoverBackground: getColor(theme, listHoverBackground),
listHoverForeground: getColor(theme, listHoverForeground),
listFocusBackground: getColor(theme, listFocusBackground),
listFocusForeground: getColor(theme, listFocusForeground),
listFocusOutline: getColor(theme, listFocusOutline),
listActiveSelectionBackground: getColor(theme, listActiveSelectionBackground),
listActiveSelectionForeground: getColor(theme, listActiveSelectionForeground),
listHoverOutline: getColor(theme, activeContrastBorder),
scrollbarShadowColor: getColor(theme, scrollbarShadow),
scrollbarSliderActiveBackgroundColor: getColor(theme, scrollbarSliderActiveBackground),
scrollbarSliderBackgroundColor: getColor(theme, scrollbarSliderBackground),
scrollbarSliderHoverBackgroundColor: getColor(theme, scrollbarSliderHoverBackground),
},
platform: platform,
applicationName: this.productService.applicationName
};
return this.issueMainService.openProcessExplorer(data);
}
}
export function getIssueReporterStyles(theme: IColorTheme): IssueReporterStyles {

View File

@ -0,0 +1,96 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { localize, localize2 } from 'vs/nls';
import { MenuRegistry, MenuId, registerAction2, Action2 } from 'vs/platform/actions/common/actions';
import { IWorkbenchProcessService } from 'vs/workbench/contrib/issue/common/issue';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { Categories } from 'vs/platform/action/common/actionCommonCategories';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { INativeEnvironmentService } from 'vs/platform/environment/common/environment';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { INativeHostService } from 'vs/platform/native/common/native';
import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress';
import { IProcessMainService } from 'vs/platform/issue/common/issue';
import 'vs/workbench/contrib/issue/electron-sandbox/processService';
import 'vs/workbench/contrib/issue/electron-sandbox/issueMainService';
//#region Commands
class OpenProcessExplorer extends Action2 {
static readonly ID = 'workbench.action.openProcessExplorer';
constructor() {
super({
id: OpenProcessExplorer.ID,
title: localize2('openProcessExplorer', 'Open Process Explorer'),
category: Categories.Developer,
f1: true
});
}
override async run(accessor: ServicesAccessor): Promise<void> {
const processService = accessor.get(IWorkbenchProcessService);
return processService.openProcessExplorer();
}
}
registerAction2(OpenProcessExplorer);
MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, {
group: '5_tools',
command: {
id: OpenProcessExplorer.ID,
title: localize({ key: 'miOpenProcessExplorerer', comment: ['&& denotes a mnemonic'] }, "Open &&Process Explorer")
},
order: 2
});
class StopTracing extends Action2 {
static readonly ID = 'workbench.action.stopTracing';
constructor() {
super({
id: StopTracing.ID,
title: localize2('stopTracing', 'Stop Tracing'),
category: Categories.Developer,
f1: true
});
}
override async run(accessor: ServicesAccessor): Promise<void> {
const processService = accessor.get(IProcessMainService);
const environmentService = accessor.get(INativeEnvironmentService);
const dialogService = accessor.get(IDialogService);
const nativeHostService = accessor.get(INativeHostService);
const progressService = accessor.get(IProgressService);
if (!environmentService.args.trace) {
const { confirmed } = await dialogService.confirm({
message: localize('stopTracing.message', "Tracing requires to launch with a '--trace' argument"),
primaryButton: localize({ key: 'stopTracing.button', comment: ['&& denotes a mnemonic'] }, "&&Relaunch and Enable Tracing"),
});
if (confirmed) {
return nativeHostService.relaunch({ addArgs: ['--trace'] });
}
}
await progressService.withProgress({
location: ProgressLocation.Dialog,
title: localize('stopTracing.title', "Creating trace file..."),
cancellable: false,
detail: localize('stopTracing.detail', "This can take up to one minute to complete.")
}, () => processService.stopTracing());
}
}
registerAction2(StopTracing);
CommandsRegistry.registerCommand('_issues.getSystemStatus', (accessor) => {
return accessor.get(IProcessMainService).getSystemStatus();
});
//#endregion

View File

@ -0,0 +1,62 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { getZoomLevel } from 'vs/base/browser/browser';
import { platform } from 'vs/base/common/process';
import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IProcessMainService, ProcessExplorerData } from 'vs/platform/issue/common/issue';
import { IProductService } from 'vs/platform/product/common/productService';
import { activeContrastBorder, editorBackground, editorForeground, listActiveSelectionBackground, listActiveSelectionForeground, listFocusBackground, listFocusForeground, listFocusOutline, listHoverBackground, listHoverForeground, scrollbarShadow, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground } from 'vs/platform/theme/common/colorRegistry';
import { IColorTheme, IThemeService } from 'vs/platform/theme/common/themeService';
import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService';
import { IWorkbenchProcessService } from 'vs/workbench/contrib/issue/common/issue';
import { mainWindow } from 'vs/base/browser/window';
export class ProcessService implements IWorkbenchProcessService {
declare readonly _serviceBrand: undefined;
constructor(
@IProcessMainService private readonly processMainService: IProcessMainService,
@IThemeService private readonly themeService: IThemeService,
@INativeWorkbenchEnvironmentService private readonly environmentService: INativeWorkbenchEnvironmentService,
@IProductService private readonly productService: IProductService,
) { }
openProcessExplorer(): Promise<void> {
const theme = this.themeService.getColorTheme();
const data: ProcessExplorerData = {
pid: this.environmentService.mainPid,
zoomLevel: getZoomLevel(mainWindow),
styles: {
backgroundColor: getColor(theme, editorBackground),
color: getColor(theme, editorForeground),
listHoverBackground: getColor(theme, listHoverBackground),
listHoverForeground: getColor(theme, listHoverForeground),
listFocusBackground: getColor(theme, listFocusBackground),
listFocusForeground: getColor(theme, listFocusForeground),
listFocusOutline: getColor(theme, listFocusOutline),
listActiveSelectionBackground: getColor(theme, listActiveSelectionBackground),
listActiveSelectionForeground: getColor(theme, listActiveSelectionForeground),
listHoverOutline: getColor(theme, activeContrastBorder),
scrollbarShadowColor: getColor(theme, scrollbarShadow),
scrollbarSliderActiveBackgroundColor: getColor(theme, scrollbarSliderActiveBackground),
scrollbarSliderBackgroundColor: getColor(theme, scrollbarSliderBackground),
scrollbarSliderHoverBackgroundColor: getColor(theme, scrollbarSliderHoverBackground),
},
platform: platform,
applicationName: this.productService.applicationName
};
return this.processMainService.openProcessExplorer(data);
}
}
function getColor(theme: IColorTheme, key: string): string | undefined {
const color = theme.getColor(key);
return color ? color.toString() : undefined;
}
registerSingleton(IWorkbenchProcessService, ProcessService, InstantiationType.Delayed);

View File

@ -118,6 +118,9 @@ import 'vs/workbench/contrib/extensions/electron-sandbox/extensions.contribution
// Issues
import 'vs/workbench/contrib/issue/electron-sandbox/issue.contribution';
// Process
import 'vs/workbench/contrib/issue/electron-sandbox/process.contribution';
// Remote
import 'vs/workbench/contrib/remote/electron-sandbox/remote.contribution';

View File

@ -156,6 +156,7 @@ import 'vs/workbench/contrib/tags/browser/workspaceTagsService';
// Issues
import 'vs/workbench/contrib/issue/browser/issue.contribution';
// Splash
import 'vs/workbench/contrib/splash/browser/splash.contribution';