提交 25069814 编写于 作者: I isidor

introduce ExtensionHostDebugService to no longer piggy back on the broadcastService

fixes #70835
上级 ae836215
......@@ -259,6 +259,13 @@ export class CodeApplication extends Disposable {
}
});
ipc.on('vscode:extensionHostDebug', (_: Event, windowId: number, broadcast: any) => {
if (this.windowsMainService) {
// Send to all windows (except sender window)
this.windowsMainService.sendToAll('vscode:extensionHostDebug', broadcast, [windowId]);
}
});
ipc.on('vscode:toggleDevTools', (event: Event) => event.sender.toggleDevTools());
ipc.on('vscode:openDevTools', (event: Event) => event.sender.openDevTools());
......@@ -790,4 +797,3 @@ function getURIToOpenFromPathSync(path: string): IURIToOpen {
}
return { fileUri: URI.file(path) };
}
......@@ -9,8 +9,7 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment'
import { IRemoteConsoleLog, log, parse } from 'vs/base/common/console';
import { parseExtensionDevOptions } from 'vs/workbench/services/extensions/common/extensionDevOptions';
import { IWindowsService } from 'vs/platform/windows/common/windows';
import { IBroadcastService } from 'vs/workbench/services/broadcast/common/broadcast';
import { EXTENSION_LOG_BROADCAST_CHANNEL } from 'vs/platform/extensions/common/extensionHost';
import { IExtensionHostDebugService } from 'vs/workbench/services/extensionHostDebug/common/extensionHostDebug';
@extHostNamedCustomer(MainContext.MainThreadConsole)
export class MainThreadConsole implements MainThreadConsoleShape {
......@@ -22,7 +21,7 @@ export class MainThreadConsole implements MainThreadConsoleShape {
extHostContext: IExtHostContext,
@IEnvironmentService private readonly _environmentService: IEnvironmentService,
@IWindowsService private readonly _windowsService: IWindowsService,
@IBroadcastService private readonly _broadcastService: IBroadcastService,
@IExtensionHostDebugService private readonly _extensionHostDebugService: IExtensionHostDebugService,
) {
const devOpts = parseExtensionDevOptions(this._environmentService);
this._isExtensionDevHost = devOpts.isExtensionDevHost;
......@@ -45,14 +44,8 @@ export class MainThreadConsole implements MainThreadConsoleShape {
}
// Broadcast to other windows if we are in development mode
else if (!this._environmentService.isBuilt || this._isExtensionDevHost) {
this._broadcastService.broadcast({
channel: EXTENSION_LOG_BROADCAST_CHANNEL,
payload: {
logEntry: entry,
debugId: this._environmentService.debugExtensionHost.debugId
}
});
else if (this._environmentService.debugExtensionHost.debugId && (!this._environmentService.isBuilt || this._isExtensionDevHost)) {
this._extensionHostDebugService.logToSession(this._environmentService.debugExtensionHost.debugId, entry);
}
}
}
......@@ -33,9 +33,7 @@ import { ITextFileService } from 'vs/workbench/services/textfile/common/textfile
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { EXTENSION_LOG_BROADCAST_CHANNEL, EXTENSION_ATTACH_BROADCAST_CHANNEL, EXTENSION_TERMINATE_BROADCAST_CHANNEL, EXTENSION_RELOAD_BROADCAST_CHANNEL, EXTENSION_CLOSE_EXTHOST_BROADCAST_CHANNEL } from 'vs/platform/extensions/common/extensionHost';
import { IBroadcastService } from 'vs/workbench/services/broadcast/common/broadcast';
import { IRemoteConsoleLog, parse, getFirstFrame } from 'vs/base/common/console';
import { parse, getFirstFrame } from 'vs/base/common/console';
import { TaskEvent, TaskEventKind, TaskIdentifier } from 'vs/workbench/contrib/tasks/common/tasks';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { INotificationService } from 'vs/platform/notification/common/notification';
......@@ -47,6 +45,7 @@ import { IDebugService, State, IDebugSession, CONTEXT_DEBUG_TYPE, CONTEXT_DEBUG_
import { isExtensionHostDebugging } from 'vs/workbench/contrib/debug/common/debugUtils';
import { isErrorWithActions, createErrorWithActions } from 'vs/base/common/errorsWithActions';
import { RunOnceScheduler } from 'vs/base/common/async';
import { IExtensionHostDebugService } from 'vs/workbench/services/extensionHostDebug/common/extensionHostDebug';
const DEBUG_BREAKPOINTS_KEY = 'debug.breakpoint';
const DEBUG_BREAKPOINTS_ACTIVATED_KEY = 'debug.breakpointactivated';
......@@ -98,7 +97,6 @@ export class DebugService implements IDebugService {
@INotificationService private readonly notificationService: INotificationService,
@IDialogService private readonly dialogService: IDialogService,
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,
@IBroadcastService private readonly broadcastService: IBroadcastService,
@ITelemetryService private readonly telemetryService: ITelemetryService,
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
@IContextKeyService contextKeyService: IContextKeyService,
......@@ -109,6 +107,7 @@ export class DebugService implements IDebugService {
@ITaskService private readonly taskService: ITaskService,
@IFileService private readonly fileService: IFileService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IExtensionHostDebugService private readonly extensionHostDebugService: IExtensionHostDebugService
) {
this.toDispose = [];
......@@ -136,34 +135,31 @@ export class DebugService implements IDebugService {
this.toDispose.push(this.storageService.onWillSaveState(this.saveState, this));
this.lifecycleService.onShutdown(this.dispose, this);
this.toDispose.push(this.broadcastService.onBroadcast(broadcast => {
const session = this.model.getSession(broadcast.payload.debugId, true);
this.toDispose.push(this.extensionHostDebugService.onAttachSession(data => {
const session = this.model.getSession(data.id, true);
if (session) {
switch (broadcast.channel) {
case EXTENSION_ATTACH_BROADCAST_CHANNEL:
// EH was started in debug mode -> attach to it
session.configuration.request = 'attach';
session.configuration.port = broadcast.payload.port;
this.launchOrAttachToSession(session);
break;
case EXTENSION_TERMINATE_BROADCAST_CHANNEL:
// EH was terminated
session.disconnect();
break;
case EXTENSION_LOG_BROADCAST_CHANNEL:
// extension logged output -> show it in REPL
const extensionOutput = <IRemoteConsoleLog>broadcast.payload.logEntry;
const sev = extensionOutput.severity === 'warn' ? severity.Warning : extensionOutput.severity === 'error' ? severity.Error : severity.Info;
const { args, stack } = parse(extensionOutput);
const frame = !!stack ? getFirstFrame(stack) : undefined;
session.logToRepl(sev, args, frame);
break;
}
// EH was started in debug mode -> attach to it
session.configuration.request = 'attach';
session.configuration.port = data.port;
this.launchOrAttachToSession(session).then(undefined, errors.onUnexpectedError);
}
}, this));
}));
this.toDispose.push(this.extensionHostDebugService.onTerminateSession(sessionId => {
const session = this.model.getSession(sessionId);
if (session) {
session.disconnect().then(undefined, errors.onUnexpectedError);
}
}));
this.toDispose.push(this.extensionHostDebugService.onLogToSession(data => {
const session = this.model.getSession(data.id, true);
if (session) {
// extension logged output -> show it in REPL
const sev = data.log.severity === 'warn' ? severity.Warning : data.log.severity === 'error' ? severity.Error : severity.Info;
const { args, stack } = parse(data.log);
const frame = !!stack ? getFirstFrame(stack) : undefined;
session.logToRepl(sev, args, frame);
}
}));
this.toDispose.push(this.viewModel.onDidFocusStackFrame(() => {
this.onStateChange();
......@@ -512,10 +508,7 @@ export class DebugService implements IDebugService {
// 'Run without debugging' mode VSCode must terminate the extension host. More details: #3905
if (isExtensionHostDebugging(session.configuration) && session.state === State.Running && session.configuration.noDebug) {
this.broadcastService.broadcast({
channel: EXTENSION_CLOSE_EXTHOST_BROADCAST_CHANNEL,
payload: [session.root.uri.toString()]
});
this.extensionHostDebugService.close(session.root.uri);
}
this.telemetryDebugSessionStop(session, adapterExitEvent);
......@@ -563,10 +556,7 @@ export class DebugService implements IDebugService {
}
if (isExtensionHostDebugging(session.configuration) && session.root) {
return runTasks().then(taskResult => taskResult === TaskRunResult.Success ? this.broadcastService.broadcast({
channel: EXTENSION_RELOAD_BROADCAST_CHANNEL,
payload: [session.root.uri.toString()]
}) : undefined);
return runTasks().then(taskResult => taskResult === TaskRunResult.Success ? this.extensionHostDebugService.reload(session.root.uri) : undefined);
}
const shouldFocus = this.viewModel.focusedSession && session.getId() === this.viewModel.focusedSession.getId();
......
......@@ -11,7 +11,7 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IWindowService } from 'vs/platform/windows/common/windows';
import { IBroadcastService, IBroadcast } from 'vs/workbench/services/broadcast/common/broadcast';
export class BroadcastService extends Disposable implements IBroadcastService {
class BroadcastService extends Disposable implements IBroadcastService {
_serviceBrand: any;
private readonly _onBroadcast: Emitter<IBroadcast> = this._register(new Emitter<IBroadcast>());
......
......@@ -3,10 +3,28 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
// Broadcast communication constants
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { URI } from 'vs/base/common/uri';
import { Event } from 'vs/base/common/event';
import { IRemoteConsoleLog } from 'vs/base/common/console';
export const EXTENSION_LOG_BROADCAST_CHANNEL = 'vscode:extensionLog';
export const EXTENSION_ATTACH_BROADCAST_CHANNEL = 'vscode:extensionAttach';
export const EXTENSION_TERMINATE_BROADCAST_CHANNEL = 'vscode:extensionTerminate';
export const EXTENSION_RELOAD_BROADCAST_CHANNEL = 'vscode:extensionReload';
export const EXTENSION_CLOSE_EXTHOST_BROADCAST_CHANNEL = 'vscode:extensionCloseExtensionHost';
\ No newline at end of file
export const IExtensionHostDebugService = createDecorator<IExtensionHostDebugService>('extensionHostDebugService');
export interface IExtensionHostDebugService {
_serviceBrand: any;
reload(resource: URI): void;
onReload: Event<URI>;
close(resource: URI): void;
onClose: Event<URI>;
attachSession(id: string, port: number): void;
onAttachSession: Event<{ id: string, port: number }>;
logToSession(id: string, log: IRemoteConsoleLog): void;
onLogToSession: Event<{ id: string, log: IRemoteConsoleLog }>;
terminateSession(id: string): void;
onTerminateSession: Event<string>;
}
\ No newline at end of file
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Event, Emitter } from 'vs/base/common/event';
import { IWindowService } from 'vs/platform/windows/common/windows';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IExtensionHostDebugService } from 'vs/workbench/services/extensionHostDebug/common/extensionHostDebug';
import { URI } from 'vs/base/common/uri';
import { IRemoteConsoleLog } from 'vs/base/common/console';
import { ipcRenderer as ipc } from 'electron';
interface IReloadBroadcast {
type: 'vscode:extensionReload';
resource: string;
}
interface IAttachSessionBroadcast {
type: 'vscode:extensionAttach';
id: string;
port: number;
}
interface ICloseBroadcast {
type: 'vscode:extensionCloseExtensionHost';
resource: string;
}
interface ILogToSessionBroadcast {
type: 'vscode:extensionLog';
id: string;
log: IRemoteConsoleLog;
}
interface ITerminateSessionBroadcast {
type: 'vscode:extensionTerminate';
id: string;
}
const CHANNEL = 'vscode:extensionHostDebug';
class ExtensionHostDebugService implements IExtensionHostDebugService {
_serviceBrand: any;
private windowId: number;
private readonly _onReload = new Emitter<URI>();
private readonly _onClose = new Emitter<URI>();
private readonly _onAttachSession = new Emitter<{ id: string, port: number }>();
private readonly _onLogToSession = new Emitter<{ id: string, log: IRemoteConsoleLog }>();
private readonly _onTerminateSession = new Emitter<string>();
constructor(
@IWindowService readonly windowService: IWindowService,
) {
this.windowId = windowService.getCurrentWindowId();
ipc.on(CHANNEL, (_: unknown, broadcast: IReloadBroadcast | ICloseBroadcast | IAttachSessionBroadcast | ILogToSessionBroadcast | ITerminateSessionBroadcast) => {
if (broadcast.type === 'vscode:extensionReload') {
this._onReload.fire(URI.parse(broadcast.resource));
}
if (broadcast.type === 'vscode:extensionCloseExtensionHost') {
this._onClose.fire(URI.parse(broadcast.resource));
}
if (broadcast.type === 'vscode:extensionAttach') {
this._onAttachSession.fire({ id: broadcast.id, port: broadcast.port });
}
if (broadcast.type === 'vscode:extensionLog') {
this._onLogToSession.fire({ id: broadcast.id, log: broadcast.log });
}
if (broadcast.type === 'vscode:extensionTerminate') {
this._onTerminateSession.fire(broadcast.id);
}
});
}
reload(resource: URI): void {
ipc.send(CHANNEL, this.windowId, <IReloadBroadcast>{
type: 'vscode:extensionReload',
resource: resource.toString()
});
}
get onReload(): Event<URI> {
return this._onReload.event;
}
close(resource: URI): void {
ipc.send(CHANNEL, this.windowId, <ICloseBroadcast>{
type: 'vscode:extensionCloseExtensionHost',
resource: resource.toString()
});
}
get onClose(): Event<URI> {
return this._onClose.event;
}
attachSession(id: string, port: number): void {
ipc.send(CHANNEL, this.windowId, <IAttachSessionBroadcast>{
type: 'vscode:extensionAttach',
id,
port
});
}
get onAttachSession(): Event<{ id: string, port: number }> {
return this._onAttachSession.event;
}
logToSession(id: string, log: IRemoteConsoleLog): void {
ipc.send(CHANNEL, this.windowId, <ILogToSessionBroadcast>{
type: 'vscode:extensionLog',
id,
log
});
}
get onLogToSession(): Event<{ id: string, log: IRemoteConsoleLog }> {
return this._onLogToSession.event;
}
terminateSession(id: string): void {
ipc.send(CHANNEL, this.windowId, <ITerminateSessionBroadcast>{
type: 'vscode:extensionTerminate',
id
});
}
get onTerminateSession(): Event<string> {
return this._onTerminateSession.event;
}
}
registerSingleton(IExtensionHostDebugService, ExtensionHostDebugService, true);
......@@ -21,9 +21,7 @@ import { findFreePort, randomPort } from 'vs/base/node/ports';
import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc';
import { PersistentProtocol } from 'vs/base/parts/ipc/common/ipc.net';
import { generateRandomPipeName, NodeSocket } from 'vs/base/parts/ipc/node/ipc.net';
import { IBroadcast, IBroadcastService } from 'vs/workbench/services/broadcast/common/broadcast';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { EXTENSION_ATTACH_BROADCAST_CHANNEL, EXTENSION_CLOSE_EXTHOST_BROADCAST_CHANNEL, EXTENSION_LOG_BROADCAST_CHANNEL, EXTENSION_RELOAD_BROADCAST_CHANNEL, EXTENSION_TERMINATE_BROADCAST_CHANNEL } from 'vs/platform/extensions/common/extensionHost';
import { ILabelService } from 'vs/platform/label/common/label';
import { ILifecycleService, WillShutdownEvent } from 'vs/platform/lifecycle/common/lifecycle';
import { ILogService } from 'vs/platform/log/common/log';
......@@ -38,6 +36,7 @@ import { withNullAsUndefined } from 'vs/base/common/types';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { parseExtensionDevOptions } from '../common/extensionDevOptions';
import { VSBuffer } from 'vs/base/common/buffer';
import { IExtensionHostDebugService } from 'vs/workbench/services/extensionHostDebug/common/extensionHostDebug';
export interface IExtensionHostStarter {
readonly onCrashed: Event<[number, string | null]>;
......@@ -77,12 +76,12 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter {
@INotificationService private readonly _notificationService: INotificationService,
@IWindowsService private readonly _windowsService: IWindowsService,
@IWindowService private readonly _windowService: IWindowService,
@IBroadcastService private readonly _broadcastService: IBroadcastService,
@ILifecycleService private readonly _lifecycleService: ILifecycleService,
@IEnvironmentService private readonly _environmentService: IEnvironmentService,
@ITelemetryService private readonly _telemetryService: ITelemetryService,
@ILogService private readonly _logService: ILogService,
@ILabelService private readonly _labelService: ILabelService
@ILabelService private readonly _labelService: ILabelService,
@IExtensionHostDebugService private readonly _extensionHostDebugService: IExtensionHostDebugService
) {
const devOpts = parseExtensionDevOptions(this._environmentService);
this._isExtensionDevHost = devOpts.isExtensionDevHost;
......@@ -102,7 +101,16 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter {
this._toDispose.push(this._onCrashed);
this._toDispose.push(this._lifecycleService.onWillShutdown(e => this._onWillShutdown(e)));
this._toDispose.push(this._lifecycleService.onShutdown(reason => this.terminate()));
this._toDispose.push(this._broadcastService.onBroadcast(b => this._onBroadcast(b)));
this._toDispose.push(this._extensionHostDebugService.onClose(resource => {
if (this._isExtensionDevHost && isEqual(resource, this._environmentService.extensionDevelopmentLocationURI)) {
this._windowService.closeWindow();
}
}));
this._toDispose.push(this._extensionHostDebugService.onReload(resource => {
if (this._isExtensionDevHost && isEqual(resource, this._environmentService.extensionDevelopmentLocationURI)) {
this._windowService.reloadWindow();
}
}));
const globalExitListener = () => this.terminate();
process.once('exit', globalExitListener);
......@@ -115,24 +123,6 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter {
this.terminate();
}
private _onBroadcast(broadcast: IBroadcast): void {
// Close Ext Host Window Request
if (broadcast.channel === EXTENSION_CLOSE_EXTHOST_BROADCAST_CHANNEL && this._isExtensionDevHost) {
const extensionLocations = broadcast.payload as string[];
if (Array.isArray(extensionLocations) && extensionLocations.some(uriString => isEqual(this._environmentService.extensionDevelopmentLocationURI, URI.parse(uriString)))) {
this._windowService.closeWindow();
}
}
if (broadcast.channel === EXTENSION_RELOAD_BROADCAST_CHANNEL && this._isExtensionDevHost) {
const extensionPaths = broadcast.payload as string[];
if (Array.isArray(extensionPaths) && extensionPaths.some(uriString => isEqual(this._environmentService.extensionDevelopmentLocationURI, URI.parse(uriString)))) {
this._windowService.reloadWindow();
}
}
}
public start(): Promise<IMessagePassingProtocol> | null {
if (this._terminating) {
// .terminate() was called
......@@ -230,14 +220,8 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter {
this._extensionHostProcess.on('exit', (code: number, signal: string) => this._onExtHostProcessExit(code, signal));
// Notify debugger that we are ready to attach to the process if we run a development extension
if (this._isExtensionDevHost && portData.actual && this._isExtensionDevDebug) {
this._broadcastService.broadcast({
channel: EXTENSION_ATTACH_BROADCAST_CHANNEL,
payload: {
debugId: this._environmentService.debugExtensionHost.debugId,
port: portData.actual
}
});
if (this._isExtensionDevHost && portData.actual && this._isExtensionDevDebug && this._environmentService.debugExtensionHost.debugId) {
this._extensionHostDebugService.attachSession(this._environmentService.debugExtensionHost.debugId, portData.actual);
}
this._inspectPort = portData.actual;
......@@ -445,14 +429,8 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter {
}
// Broadcast to other windows if we are in development mode
else if (!this._environmentService.isBuilt || this._isExtensionDevHost) {
this._broadcastService.broadcast({
channel: EXTENSION_LOG_BROADCAST_CHANNEL,
payload: {
logEntry: entry,
debugId: this._environmentService.debugExtensionHost.debugId
}
});
else if (this._environmentService.debugExtensionHost.debugId && (!this._environmentService.isBuilt || this._isExtensionDevHost)) {
this._extensionHostDebugService.logToSession(this._environmentService.debugExtensionHost.debugId, entry);
}
}
......@@ -557,14 +535,8 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter {
// If the extension development host was started without debugger attached we need
// to communicate this back to the main side to terminate the debug session
if (this._isExtensionDevHost && !this._isExtensionDevTestFromCli && !this._isExtensionDevDebug) {
this._broadcastService.broadcast({
channel: EXTENSION_TERMINATE_BROADCAST_CHANNEL,
payload: {
debugId: this._environmentService.debugExtensionHost.debugId
}
});
if (this._isExtensionDevHost && !this._isExtensionDevTestFromCli && !this._isExtensionDevDebug && this._environmentService.debugExtensionHost.debugId) {
this._extensionHostDebugService.terminateSession(this._environmentService.debugExtensionHost.debugId);
event.join(timeout(100 /* wait a bit for IPC to get delivered */));
}
}
......
......@@ -103,6 +103,7 @@ import 'vs/workbench/services/search/node/searchService';
import 'vs/workbench/services/progress/browser/progressService2';
import 'vs/workbench/services/editor/browser/codeEditorService';
import 'vs/workbench/services/broadcast/electron-browser/broadcastService';
import 'vs/workbench/services/extensionHostDebug/electron-browser/extensionHostDebugService';
import 'vs/workbench/services/preferences/browser/preferencesService';
import 'vs/workbench/services/output/node/outputChannelModelService';
import 'vs/workbench/services/configuration/common/jsonEditingService';
......
......@@ -239,6 +239,7 @@ import 'vs/workbench/contrib/scm/browser/scmViewlet';
// import 'vs/workbench/contrib/debug/browser/debugEditorContribution';
// import 'vs/workbench/contrib/debug/browser/repl';
// import 'vs/workbench/contrib/debug/browser/debugViewlet';
// import 'vs/workbench/services/extensionHostDebug/electron-browser/extensionHostDebugService';
// Markers
import 'vs/workbench/contrib/markers/browser/markers.contribution';
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册