/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ 'use strict'; import 'vs/css!./media/workbench'; import 'vs/css!./media/octicons/octicons'; import {TPromise, Promise, ValueCallback} from 'vs/base/common/winjs.base'; import types = require('vs/base/common/types'); import {IDisposable, disposeAll} from 'vs/base/common/lifecycle'; import strings = require('vs/base/common/strings'); import DOM = require('vs/base/browser/dom'); import {Box, Builder, withElementById, $} from 'vs/base/browser/builder'; import {Delayer} from 'vs/base/common/async'; import assert = require('vs/base/common/assert'); import timer = require('vs/base/common/timer'); import errors = require('vs/base/common/errors'); import {Registry} from 'vs/platform/platform'; import {Identifiers, Preferences} from 'vs/workbench/common/constants'; import {EventType} from 'vs/workbench/browser/events'; import {IOptions} from 'vs/workbench/common/options'; import {IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions} from 'vs/workbench/common/contributions'; import {IEditorRegistry, Extensions as EditorExtensions} from 'vs/workbench/browser/parts/editor/baseEditor'; import {TextEditorOptions, EditorInput, EditorOptions} from 'vs/workbench/common/editor'; import {Part} from 'vs/workbench/browser/part'; import {HistoryService} from 'vs/workbench/services/history/browser/history'; import {ActivitybarPart} from 'vs/workbench/browser/parts/activitybar/activitybarPart'; import {EditorPart} from 'vs/workbench/browser/parts/editor/editorPart'; import {SidebarPart} from 'vs/workbench/browser/parts/sidebar/sidebarPart'; import {StatusbarPart} from 'vs/workbench/browser/parts/statusbar/statusbarPart'; import {WorkbenchLayout, LayoutOptions} from 'vs/workbench/browser/layout'; import {IActionBarRegistry, Extensions as ActionBarExtensions} from 'vs/workbench/browser/actionBarRegistry'; import {IViewletRegistry, Extensions as ViewletExtensions} from 'vs/workbench/browser/viewlet'; import {QuickOpenController} from 'vs/workbench/browser/parts/quickopen/quickOpenController'; import {getServices} from 'vs/platform/instantiation/common/extensions'; import {AbstractKeybindingService} from 'vs/platform/keybinding/browser/keybindingServiceImpl'; import {UntitledEditorService, IUntitledEditorService} from 'vs/workbench/services/untitled/browser/untitledEditorService'; import {WorkbenchEditorService} from 'vs/workbench/services/editor/browser/editorService'; import {Position, Parts, IPartService} from 'vs/workbench/services/part/common/partService'; import {DEFAULT_THEME_ID} from 'vs/platform/theme/common/themes'; import {IWorkspaceContextService as IWorkbenchWorkspaceContextService} from 'vs/workbench/services/workspace/common/contextService'; import {IStorageService, StorageScope, StorageEvent, StorageEventType} from 'vs/platform/storage/common/storage'; import {IWorkspaceContextService, IWorkspace, IConfiguration} from 'vs/platform/workspace/common/workspace'; import {IKeybindingService} from 'vs/platform/keybinding/common/keybindingService'; import {IContextMenuService} from 'vs/platform/contextview/browser/contextView'; import {IActivityService} from 'vs/workbench/services/activity/common/activityService'; import {IViewletService} from 'vs/workbench/services/viewlet/common/viewletService'; import {WorkbenchMessageService} from 'vs/workbench/services/message/browser/messageService'; import {IWorkbenchEditorService} from 'vs/workbench/services/editor/common/editorService' import {IQuickOpenService} from 'vs/workbench/services/quickopen/browser/quickOpenService'; import {IHistoryService} from 'vs/workbench/services/history/common/history'; import {IEventService} from 'vs/platform/event/common/event'; import {IInstantiationService} from 'vs/platform/instantiation/common/instantiation'; import {ILifecycleService} from 'vs/platform/lifecycle/common/lifecycle'; import {IMessageService} from 'vs/platform/message/common/message'; import {ITelemetryService} from 'vs/platform/telemetry/common/telemetry'; import {IThreadService} from 'vs/platform/thread/common/thread'; import {IPluginService} from 'vs/platform/plugins/common/plugins'; import {AbstractThreadService} from 'vs/platform/thread/common/abstractThreadService'; import {IStatusbarService} from 'vs/workbench/services/statusbar/statusbarService'; interface WorkbenchParams { workspace?: IWorkspace; configuration: IConfiguration; options: IOptions; instantiationService: IInstantiationService; } export interface IWorkbenchCallbacks { onServicesCreated?: () => void; onWorkbenchStarted?: () => void; } /** * The workbench creates and lays out all parts that make up the Monaco Workbench. */ export class Workbench implements IPartService { private static sidebarPositionSettingKey = 'workbench.sidebar.position'; private static sidebarHiddenSettingKey = 'workbench.sidebar.hidden'; public serviceId = IPartService; private container: HTMLElement; private workbenchParams: WorkbenchParams; private workbenchContainer: Builder; private workbench: Builder; private workbenchStarted: boolean; private workbenchCreated: boolean; private workbenchShutdown: boolean; private activitybarPart: ActivitybarPart; private sidebarPart: SidebarPart; private editorPart: EditorPart; private statusbarPart: StatusbarPart; private quickOpen: QuickOpenController; private untitledEditorService: IUntitledEditorService; private instantiationService: IInstantiationService; private eventService: IEventService; private contextService: IWorkbenchWorkspaceContextService; private editorService: WorkbenchEditorService; private storageService: IStorageService; private telemetryService: ITelemetryService; private keybindingService: IKeybindingService; private lifecycleService: ILifecycleService; private workbenchLayout: WorkbenchLayout; private toDispose: IDisposable[]; private toShutdown: { shutdown: () => void; }[]; private currentTheme: string; private callbacks: IWorkbenchCallbacks; private creationPromise: TPromise; private creationPromiseComplete: ValueCallback; private sideBarHidden: boolean; private sideBarPosition: Position; private editorBackgroundDelayer: Delayer; constructor(container: HTMLElement, workspace: IWorkspace, configuration: IConfiguration, options: IOptions, instantiationService: IInstantiationService) { // Validate params this.validateParams(container, configuration, options); // If String passed in as container, try to find it in DOM if (types.isString(container)) { let element = withElementById(container.toString()); this.container = element.getHTMLElement(); } // Otherwise use as HTMLElement else { this.container = container; } this.workbenchParams = { workspace: workspace, configuration: configuration, options: options || {}, instantiationService }; this.currentTheme = null; this.toDispose = []; this.toShutdown = []; this.editorBackgroundDelayer = new Delayer(50); this.creationPromise = new TPromise((c, e, p) => { this.creationPromiseComplete = c; }); } private validateParams(container: HTMLElement, configuration: IConfiguration, options: IOptions): void { // Container assert.ok(container, 'Workbench requires a container to be created with'); if (types.isString(container)) { let element = withElementById(container.toString()); assert.ok(element, strings.format('Can not find HTMLElement with id \'{0}\'.', container)); } } /** * Starts the workbench and creates the HTML elements on the container. A workbench can only be started * once. Use the shutdown function to free up resources created by the workbench on startup. */ public startup(callbacks?: IWorkbenchCallbacks): void { assert.ok(!this.workbenchStarted, 'Can not start a workbench that was already started'); assert.ok(!this.workbenchShutdown, 'Can not start a workbench that was shutdown'); try { this.workbenchStarted = true; this.callbacks = callbacks; // Create Workbench this.createWorkbench(); // Services this.initServices(); if (this.callbacks && this.callbacks.onServicesCreated) { this.callbacks.onServicesCreated(); } // Register Listeners this.registerListeners(); // Settings this.initSettings(); // Create Workbench and Parts this.renderWorkbench(); // Workbench Layout this.createWorkbenchLayout(); // Register Emitters this.registerEmitters(); // Load viewlet and editors in parallel let viewletAndEditorPromises: Promise[] = []; // Show default viewlet unless sidebar is hidden or we dont have a default viewlet let registry = (Registry.as(ViewletExtensions.Viewlets)); let viewletId = registry.getDefaultViewletId(); if (!this.workbenchParams.configuration.env.isBuilt) { viewletId = this.storageService.get(SidebarPart.activeViewletSettingsKey, StorageScope.WORKSPACE, registry.getDefaultViewletId()); // help developers and restore last view } if (!this.sideBarHidden && !!viewletId) { let viewletTimerEvent = timer.start(timer.Topic.STARTUP, strings.format('Opening Viewlet: {0}', viewletId)); viewletAndEditorPromises.push(this.sidebarPart.openViewlet(viewletId, false).then(() => viewletTimerEvent.stop())); } // Check for configured options to open files on startup and resolve if any or open untitled for empty workbench let editorTimerEvent = timer.start(timer.Topic.STARTUP, strings.format('Restoring Editor(s)')); let resolveEditorInputsPromise: TPromise = Promise.as(null); let options: EditorOptions[] = []; // Files to open or create if ((this.workbenchParams.options.filesToCreate && this.workbenchParams.options.filesToCreate.length) || (this.workbenchParams.options.filesToOpen && this.workbenchParams.options.filesToOpen.length)) { let inputs: EditorInput[] = []; let filesToCreate = this.workbenchParams.options.filesToCreate || []; let filesToOpen = this.workbenchParams.options.filesToOpen || []; // Files to create inputs.push(...filesToCreate.map((resourceInput) => this.untitledEditorService.createOrGet(resourceInput.resource))); options.push(...filesToCreate.map(r => null)); // fill empty options for files to create because we dont have options there // Files to open resolveEditorInputsPromise = Promise.join(filesToOpen.map((resourceInput) => this.editorService.inputToType(resourceInput))).then((inputsToOpen) => { inputs.push(...inputsToOpen); options.push(...filesToOpen.map(resourceInput => TextEditorOptions.from(resourceInput))); return inputs; }); } // Empty workbench else if (!this.workbenchParams.workspace) { resolveEditorInputsPromise = Promise.as([this.untitledEditorService.createOrGet()]); } // Restore editor state (either from last session or with given inputs) viewletAndEditorPromises.push(resolveEditorInputsPromise.then((inputs) => { return this.editorPart.restoreEditorState(inputs, options).then(() => { this.onEditorOpenedOrClosed(); // make sure we show the proper background in the editor area editorTimerEvent.stop(); }); })); // Flag workbench as created once done Promise.join(viewletAndEditorPromises).then(() => { this.workbenchCreated = true; this.eventService.emit(EventType.WORKBENCH_CREATED); this.creationPromiseComplete(true); if (this.callbacks && this.callbacks.onWorkbenchStarted) { this.callbacks.onWorkbenchStarted(); } }, errors.onUnexpectedError); } catch (error) { // Print out error console.error(errors.toErrorMessage(error, true)); // Rethrow throw error; } } private initServices(): void { this.instantiationService = this.workbenchParams.instantiationService; // Services we expect this.eventService = this.instantiationService.getInstance(IEventService); this.storageService = this.instantiationService.getInstance(IStorageService); this.keybindingService = this.instantiationService.getInstance(IKeybindingService); this.contextService = this.instantiationService.getInstance(IWorkbenchWorkspaceContextService); this.telemetryService = this.instantiationService.getInstance(ITelemetryService); let messageService = this.instantiationService.getInstance(IMessageService); if (this.keybindingService instanceof AbstractKeybindingService) { (this.keybindingService).setMessageService(messageService); } let threadService = this.instantiationService.getInstance(IThreadService); let pluginService = this.instantiationService.getInstance(IPluginService); this.lifecycleService = this.instantiationService.getInstance(ILifecycleService); this.lifecycleService.onShutdown.add(this.shutdownComponents, this); let contextMenuService = this.instantiationService.getInstance(IContextMenuService); this.untitledEditorService = this.instantiationService.getInstance(IUntitledEditorService); // Services we contribute this.instantiationService.addSingleton(IPartService, this); // Viewlet service (sidebar part) this.sidebarPart = new SidebarPart( messageService, this.storageService, this.eventService, this.telemetryService, contextMenuService, this, this.keybindingService, Identifiers.SIDEBAR_PART ); this.toDispose.push(this.sidebarPart); this.toShutdown.push(this.sidebarPart); this.instantiationService.addSingleton(IViewletService, this.sidebarPart); // Activity service (activitybar part) this.activitybarPart = new ActivitybarPart( this.sidebarPart, messageService, this.telemetryService, this.eventService, contextMenuService, this.keybindingService, Identifiers.ACTIVITYBAR_PART ); this.toDispose.push(this.activitybarPart); this.toShutdown.push(this.activitybarPart); this.instantiationService.addSingleton(IActivityService, this.activitybarPart); // Editor service (editor part) this.editorPart = new EditorPart( messageService, this.eventService, this.telemetryService, this.storageService, this, Identifiers.EDITOR_PART ); this.toDispose.push(this.editorPart); this.toShutdown.push(this.editorPart); this.editorService = new WorkbenchEditorService( this.editorPart, this.untitledEditorService ); this.instantiationService.addSingleton(IWorkbenchEditorService, this.editorService); // Quick open service (quick open controller) this.quickOpen = new QuickOpenController( this.eventService, this.storageService, this.editorService, this.sidebarPart, messageService, this.telemetryService, this.contextService, this.keybindingService ); this.toDispose.push(this.quickOpen); this.toShutdown.push(this.quickOpen); this.instantiationService.addSingleton(IQuickOpenService, this.quickOpen); // Status bar this.statusbarPart = new StatusbarPart(Identifiers.STATUSBAR_PART); this.toDispose.push(this.statusbarPart); this.toShutdown.push(this.statusbarPart); this.instantiationService.addSingleton(IStatusbarService, this.statusbarPart); // History this.instantiationService.addSingleton(IHistoryService, new HistoryService(this.eventService, this.editorService, this.contextService, this.quickOpen)); // a new way to contribute services... let contributedServices = getServices(); for (let contributedService of contributedServices) { this.instantiationService.addSingleton(contributedService.id, contributedService.descriptor); } // Some services need to be set explicitly after all services are created (threadService).setInstantiationService(this.instantiationService); this.telemetryService.setInstantiationService(this.instantiationService); (messageService).setWorkbenchServices(this.quickOpen, this.statusbarPart); this.quickOpen.setInstantiationService(this.instantiationService); this.statusbarPart.setInstantiationService(this.instantiationService); this.activitybarPart.setInstantiationService(this.instantiationService); this.sidebarPart.setInstantiationService(this.instantiationService); this.editorPart.setInstantiationService(this.instantiationService); (this.untitledEditorService).setInstantiationService(this.instantiationService); this.editorService.setInstantiationService(this.instantiationService); // Set the some services to registries that have been created eagerly (this.keybindingService).setInstantiationService(this.instantiationService); Registry.as(ActionBarExtensions.Actionbar).setInstantiationService(this.instantiationService); Registry.as(WorkbenchExtensions.Workbench).setInstantiationService(this.instantiationService); Registry.as(EditorExtensions.Editors).setInstantiationService(this.instantiationService); } private initSettings(): void { // Sidebar visibility this.sideBarHidden = this.storageService.getBoolean(Workbench.sidebarHiddenSettingKey, StorageScope.WORKSPACE, false); if (!!this.workbenchParams.options.singleFileMode) { this.sideBarHidden = true; // we hide sidebar in single-file-mode } let registry = (Registry.as(ViewletExtensions.Viewlets)); if (!registry.getDefaultViewletId()) { this.sideBarHidden = true; // can only hide sidebar if we dont have a default viewlet id } // Sidebar position let rawPosition = this.storageService.get(Workbench.sidebarPositionSettingKey, StorageScope.GLOBAL, 'left'); this.sideBarPosition = (rawPosition === 'left') ? Position.LEFT : Position.RIGHT; } /** * Returns whether the workbench has been started. */ public isStarted(): boolean { return this.workbenchStarted && !this.workbenchShutdown; } /** * Returns whether the workbench has been fully created. */ public isCreated(): boolean { return this.workbenchCreated && this.workbenchStarted; } public joinCreation(): TPromise { return this.creationPromise; } public hasFocus(part: Parts): boolean { let activeElement = document.activeElement; if (!activeElement) { return false; } let container: Builder = null; switch (part) { case Parts.ACTIVITYBAR_PART: container = this.activitybarPart.getContainer(); break; case Parts.SIDEBAR_PART: container = this.sidebarPart.getContainer(); break; case Parts.EDITOR_PART: container = this.editorPart.getContainer(); break; case Parts.STATUSBAR_PART: if (!this.statusbarPart) { return false; // could be disabled by options } container = this.statusbarPart.getContainer(); break; } return DOM.isAncestor(activeElement, container.getHTMLElement()); } public isVisible(part: Parts): boolean { if (part === Parts.SIDEBAR_PART) { return !this.sideBarHidden; } return true; // any other part cannot be hidden } public isSideBarHidden(): boolean { return this.sideBarHidden; } public setSideBarHidden(hidden: boolean, skipLayout?: boolean): void { this.sideBarHidden = hidden; // Adjust CSS if (hidden) { this.workbench.addClass('nosidebar'); } else { this.workbench.removeClass('nosidebar'); } // Layout if (!skipLayout) { this.workbenchLayout.layout(true); } // If sidebar becomes hidden, also hide the current active viewlet if any if (hidden && this.sidebarPart.getActiveViewlet()) { this.sidebarPart.hideActiveViewlet(); // Pass Focus to Editor if Sidebar is now hidden let editor = this.editorPart.getActiveEditor(); if (editor) { editor.focus(); } } // If sidebar becomes visible, show last active viewlet or default viewlet else if (!hidden && !this.sidebarPart.getActiveViewlet()) { let registry = (Registry.as(ViewletExtensions.Viewlets)); let viewletToOpen = this.sidebarPart.getLastActiveViewletId() || registry.getDefaultViewletId(); if (viewletToOpen) { this.sidebarPart.openViewlet(viewletToOpen, true).done(null, errors.onUnexpectedError); } } // Remember in settings this.storageService.store(Workbench.sidebarHiddenSettingKey, hidden ? 'true' : 'false', StorageScope.WORKSPACE); } public getSideBarPosition(): Position { return this.sideBarPosition; } public setSideBarPosition(position: Position): void { if (this.sideBarHidden) { this.setSideBarHidden(false, true /* Skip Layout */); } let newPositionValue = (position === Position.LEFT) ? 'left' : 'right'; let oldPositionValue = (this.sideBarPosition === Position.LEFT) ? 'left' : 'right'; this.sideBarPosition = position; // Adjust CSS this.activitybarPart.getContainer().removeClass(oldPositionValue); this.sidebarPart.getContainer().removeClass(oldPositionValue); this.activitybarPart.getContainer().addClass(newPositionValue); this.sidebarPart.getContainer().addClass(newPositionValue); // Layout this.workbenchLayout.layout(true); // Remember in settings this.storageService.store(Workbench.sidebarPositionSettingKey, position === Position.LEFT ? 'left' : 'right', StorageScope.GLOBAL); } /** * Frees up resources of the workbench. Can only be called once and only on a workbench that was started. With the * optional parameter "force", the workbench can be shutdown ignoring any workbench components that might prevent * shutdown for user interaction (e.g. a dirty editor waiting for save to occur). */ public shutdown(force?: boolean): boolean | string { if (this.isStarted()) { if (!force) { this.shutdownComponents(); } // Event this.eventService.emit(EventType.WORKBENCH_DISPOSING); this.workbenchShutdown = true; // Dispose this.dispose(); } return null; } public dispose(): void { // Dispose all this.toDispose = disposeAll(this.toDispose); // Unhook listener this.lifecycleService.onShutdown.remove(this.shutdownComponents, this); // Event this.eventService.emit(EventType.WORKBENCH_DISPOSED); } /** * Asks the workbench and all its UI components inside to lay out according to * the containers dimension the workbench is living in. */ public layout(): void { if (this.isStarted()) { this.workbenchLayout.layout(); } } private shutdownComponents(): void { // Pass shutdown on to each participant this.toShutdown.forEach(s => s.shutdown()); } private registerEmitters(): void { // Part Emitters this.hookPartListeners(this.activitybarPart); this.hookPartListeners(this.editorPart); this.hookPartListeners(this.sidebarPart); // Storage Emitter this.toDispose.push(this.toDisposable(this.eventService.addEmitter(this.storageService))); } private hookPartListeners(part: Part): void { this.toDispose.push(this.toDisposable(this.eventService.addEmitter(part, part.getId()))); } private registerListeners(): void { // Listen to Preference changes this.toDispose.push(this.toDisposable(this.eventService.addListener(StorageEventType.STORAGE, (e: StorageEvent) => { switch (e.key) { case Preferences.THEME: this.applyTheme(e.newValue); break; } }))); // Listen to editor changes this.toDispose.push(this.toDisposable(this.eventService.addListener(EventType.EDITOR_CLOSED, () => this.onEditorOpenedOrClosed()))); this.toDispose.push(this.toDisposable(this.eventService.addListener(EventType.EDITOR_OPENED, () => this.onEditorOpenedOrClosed()))); } private onEditorOpenedOrClosed(): void { let visibleEditors = this.editorService.getVisibleEditors().length; // We update the editorpart class to indicate if an editor is opened or not // through a delay to accomodate for fast editor switching if (visibleEditors === 0) { this.editorBackgroundDelayer.trigger(() => this.editorPart.getContainer().addClass('empty')); } else { this.editorBackgroundDelayer.trigger(() => this.editorPart.getContainer().removeClass('empty')); } } private toDisposable(fn: () => void): IDisposable { return { dispose: function() { fn(); } }; } private createWorkbenchLayout(): void { let options = new LayoutOptions(); options.setMargin(new Box(0, 0, 0, 0)); this.workbenchLayout = this.instantiationService.createInstance(WorkbenchLayout, $(this.container), // Parent this.workbench, // Workbench Container this.activitybarPart, // Activity Bar this.editorPart, // Editor this.sidebarPart, // Sidebar this.statusbarPart, // Statusbar this.quickOpen, // Quickopen options // Layout Options ); this.toDispose.push(this.workbenchLayout); } private createWorkbench(): void { // Create Workbench DIV Off-DOM this.workbenchContainer = $('.monaco-workbench-container'); this.workbench = $().div({ 'class': 'monaco-workbench', id: Identifiers.WORKBENCH_CONTAINER }).appendTo(this.workbenchContainer); } private renderWorkbench(): void { // Apply sidebar state as CSS class if (this.sideBarHidden) { this.workbench.addClass('nosidebar'); } // Apply readonly state as CSS class if (this.workbenchParams.options.readOnly) { this.workbench.addClass('readonly'); } // Apply no-workspace state as CSS class if (!this.workbenchParams.workspace) { this.workbench.addClass('no-workspace'); } // Create Parts this.createActivityBarPart(); this.createSidebarPart(); this.createEditorPart(); this.createStatusbarPart(); // Create QuickOpen this.createQuickOpen(); // Check theme in preferences let currentTheme = this.storageService.get(Preferences.THEME, StorageScope.GLOBAL, DEFAULT_THEME_ID); // Apply theme this.applyTheme(currentTheme); // Add Workbench to DOM this.workbenchContainer.build(this.container); } private applyTheme(theme: string): void { if (this.currentTheme) { this.workbench.removeClass(this.currentTheme); } this.currentTheme = theme || null; if (this.currentTheme) { this.workbench.addClass(this.currentTheme); } } private createActivityBarPart(): void { let activitybarPartContainer = $(this.workbench) .div({ 'class': ['part', 'activitybar', this.sideBarPosition === Position.LEFT ? 'left' : 'right'], id: Identifiers.ACTIVITYBAR_PART }); this.activitybarPart.create(activitybarPartContainer); } private createSidebarPart(): void { let sidebarPartContainer = $(this.workbench) .div({ 'class': ['part', 'sidebar', this.sideBarPosition === Position.LEFT ? 'left' : 'right'], id: Identifiers.SIDEBAR_PART }); this.sidebarPart.create(sidebarPartContainer); } private createEditorPart(): void { let editorContainer = $(this.workbench) .div({ 'class': ['part', 'editor', 'monaco-editor-background'], id: Identifiers.EDITOR_PART }); this.editorPart.create(editorContainer); } private createStatusbarPart(): void { let statusbarContainer = $(this.workbench).div({ 'class': ['part', 'statusbar'], id: Identifiers.STATUSBAR_PART }); this.statusbarPart.create(statusbarContainer); } private createQuickOpen(): void { this.quickOpen.create(); } public getEditorPart(): EditorPart { assert.ok(this.workbenchStarted, 'Workbench is not started. Call startup() first.'); return this.editorPart; } public getSidebarPart(): SidebarPart { assert.ok(this.workbenchStarted, 'Workbench is not started. Call startup() first.'); return this.sidebarPart; } public getInstantiationService(): IInstantiationService { assert.ok(this.workbenchStarted, 'Workbench is not started. Call startup() first.'); return this.instantiationService; } public addClass(clazz: string): void { if (this.workbench) { this.workbench.addClass(clazz); } } public removeClass(clazz: string): void { if (this.workbench) { this.workbench.removeClass(clazz); } } }