Vscode merge (#4582)

* Merge from vscode 37cb23d3dd4f9433d56d4ba5ea3203580719a0bd

* fix issues with merges

* bump node version in azpipe

* replace license headers

* remove duplicate launch task

* fix build errors

* fix build errors

* fix tslint issues

* working through package and linux build issues

* more work

* wip

* fix packaged builds

* working through linux build errors

* wip

* wip

* wip

* fix mac and linux file limits

* iterate linux pipeline

* disable editor typing

* revert series to parallel

* remove optimize vscode from linux

* fix linting issues

* revert testing change

* add work round for new node

* readd packaging for extensions

* fix issue with angular not resolving decorator dependencies
This commit is contained in:
Anthony Dresser
2019-03-19 17:44:35 -07:00
committed by GitHub
parent 833d197412
commit 87765e8673
1879 changed files with 54505 additions and 38058 deletions

View File

@@ -7,7 +7,6 @@ import 'vs/css!./media/activityaction';
import * as nls from 'vs/nls';
import * as DOM from 'vs/base/browser/dom';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
import { EventType as TouchEventType, GestureEvent } from 'vs/base/browser/touch';
import { Action } from 'vs/base/common/actions';
import { KeyCode } from 'vs/base/common/keyCodes';
@@ -24,8 +23,8 @@ import { ViewletDescriptor } from 'vs/workbench/browser/viewlet';
import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions';
import { IActivity, IGlobalActivity } from 'vs/workbench/common/activity';
import { ACTIVITY_BAR_FOREGROUND } from 'vs/workbench/common/theme';
import { IActivityService } from 'vs/workbench/services/activity/common/activity';
import { IPartService, Parts } from 'vs/workbench/services/part/common/partService';
import { IActivityBarService } from 'vs/workbench/services/activityBar/browser/activityBarService';
import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
export class ViewletActivityAction extends ActivityAction {
@@ -37,7 +36,7 @@ export class ViewletActivityAction extends ActivityAction {
constructor(
activity: IActivity,
@IViewletService private readonly viewletService: IViewletService,
@IPartService private readonly partService: IPartService,
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,
@ITelemetryService private readonly telemetryService: ITelemetryService
) {
super(activity);
@@ -55,14 +54,14 @@ export class ViewletActivityAction extends ActivityAction {
}
this.lastRun = now;
const sideBarVisible = this.partService.isVisible(Parts.SIDEBAR_PART);
const sideBarVisible = this.layoutService.isVisible(Parts.SIDEBAR_PART);
const activeViewlet = this.viewletService.getActiveViewlet();
// Hide sidebar if selected viewlet already visible
if (sideBarVisible && activeViewlet && activeViewlet.getId() === this.activity.id) {
this.logAction('hide');
this.partService.setSideBarHidden(true);
return Promise.resolve(null);
this.layoutService.setSideBarHidden(true);
return Promise.resolve();
}
this.logAction('show');
@@ -84,20 +83,20 @@ export class ToggleViewletAction extends Action {
constructor(
private _viewlet: ViewletDescriptor,
@IPartService private readonly partService: IPartService,
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,
@IViewletService private readonly viewletService: IViewletService
) {
super(_viewlet.id, _viewlet.name);
}
run(): Promise<any> {
const sideBarVisible = this.partService.isVisible(Parts.SIDEBAR_PART);
const sideBarVisible = this.layoutService.isVisible(Parts.SIDEBAR_PART);
const activeViewlet = this.viewletService.getActiveViewlet();
// Hide sidebar if selected viewlet already visible
if (sideBarVisible && activeViewlet && activeViewlet.getId() === this._viewlet.id) {
this.partService.setSideBarHidden(true);
return Promise.resolve(null);
this.layoutService.setSideBarHidden(true);
return Promise.resolve();
}
return this.viewletService.openViewlet(this._viewlet.id, true);
@@ -130,32 +129,29 @@ export class GlobalActivityActionItem extends ActivityActionItem {
this._register(DOM.addDisposableListener(this.container, DOM.EventType.MOUSE_DOWN, (e: MouseEvent) => {
DOM.EventHelper.stop(e, true);
const event = new StandardMouseEvent(e);
this.showContextMenu({ x: event.posx, y: event.posy });
this.showContextMenu();
}));
this._register(DOM.addDisposableListener(this.container, DOM.EventType.KEY_UP, (e: KeyboardEvent) => {
let event = new StandardKeyboardEvent(e);
if (event.equals(KeyCode.Enter) || event.equals(KeyCode.Space)) {
DOM.EventHelper.stop(e, true);
this.showContextMenu(this.container);
this.showContextMenu();
}
}));
this._register(DOM.addDisposableListener(this.container, TouchEventType.Tap, (e: GestureEvent) => {
DOM.EventHelper.stop(e, true);
const event = new StandardMouseEvent(e);
this.showContextMenu({ x: event.posx, y: event.posy });
this.showContextMenu();
}));
}
private showContextMenu(location: HTMLElement | { x: number, y: number }): void {
private showContextMenu(): void {
const globalAction = this._action as GlobalActivityAction;
const activity = globalAction.activity as IGlobalActivity;
const actions = activity.getActions();
const containerPosition = DOM.getDomNodePagePosition(this.container);
const location = { x: containerPosition.left + containerPosition.width / 2, y: containerPosition.top };
this.contextMenuService.showContextMenu({
getAnchor: () => location,
@@ -170,10 +166,10 @@ export class PlaceHolderViewletActivityAction extends ViewletActivityAction {
constructor(
id: string, iconUrl: URI,
@IViewletService viewletService: IViewletService,
@IPartService partService: IPartService,
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
@ITelemetryService telemetryService: ITelemetryService
) {
super({ id, name: id, cssClass: `extensionViewlet-placeholder-${id.replace(/\./g, '-')}` }, viewletService, partService, telemetryService);
super({ id, name: id, cssClass: `extensionViewlet-placeholder-${id.replace(/\./g, '-')}` }, viewletService, layoutService, telemetryService);
const iconClass = `.monaco-workbench .activitybar .monaco-action-bar .action-label.${this.class}`; // Generate Placeholder CSS to show the icon in the activity bar
DOM.createCSSRule(iconClass, `-webkit-mask: url('${iconUrl || ''}') no-repeat 50% 50%`);
@@ -201,19 +197,19 @@ class SwitchSideBarViewAction extends Action {
id: string,
name: string,
@IViewletService private readonly viewletService: IViewletService,
@IActivityService private readonly activityService: IActivityService
@IActivityBarService private readonly activityBarService: IActivityBarService
) {
super(id, name);
}
run(offset: number): Promise<any> {
const pinnedViewletIds = this.activityService.getPinnedViewletIds();
const pinnedViewletIds = this.activityBarService.getPinnedViewletIds();
const activeViewlet = this.viewletService.getActiveViewlet();
if (!activeViewlet) {
return Promise.resolve(null);
return Promise.resolve();
}
let targetViewletId: string;
let targetViewletId: string | undefined;
for (let i = 0; i < pinnedViewletIds.length; i++) {
if (pinnedViewletIds[i] === activeViewlet.getId()) {
targetViewletId = pinnedViewletIds[(i + pinnedViewletIds.length + offset) % pinnedViewletIds.length];
@@ -233,9 +229,9 @@ export class PreviousSideBarViewAction extends SwitchSideBarViewAction {
id: string,
name: string,
@IViewletService viewletService: IViewletService,
@IActivityService activityService: IActivityService
@IActivityBarService activityBarService: IActivityBarService
) {
super(id, name, viewletService, activityService);
super(id, name, viewletService, activityBarService);
}
run(): Promise<any> {
@@ -252,9 +248,9 @@ export class NextSideBarViewAction extends SwitchSideBarViewAction {
id: string,
name: string,
@IViewletService viewletService: IViewletService,
@IActivityService activityService: IActivityService
@IActivityBarService activityBarService: IActivityBarService
) {
super(id, name, viewletService, activityService);
super(id, name, viewletService, activityBarService);
}
run(): Promise<any> {

View File

@@ -6,7 +6,6 @@
import 'vs/css!./media/activitybarpart';
import * as nls from 'vs/nls';
import { illegalArgument } from 'vs/base/common/errors';
import { Emitter } from 'vs/base/common/event';
import { ActionsOrientation, ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
import { GlobalActivityExtensions, IGlobalActivityRegistry } from 'vs/workbench/common/activity';
import { Registry } from 'vs/platform/registry/common/platform';
@@ -14,8 +13,8 @@ import { Part } from 'vs/workbench/browser/part';
import { GlobalActivityActionItem, GlobalActivityAction, ViewletActivityAction, ToggleViewletAction, PlaceHolderToggleCompositePinnedAction, PlaceHolderViewletActivityAction } from 'vs/workbench/browser/parts/activitybar/activitybarActions';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import { IBadge } from 'vs/workbench/services/activity/common/activity';
import { IPartService, Parts, Position as SideBarPosition } from 'vs/workbench/services/part/common/partService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IWorkbenchLayoutService, Parts, Position as SideBarPosition } from 'vs/workbench/services/layout/browser/layoutService';
import { IInstantiationService, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
import { IDisposable, toDisposable, dispose } from 'vs/base/common/lifecycle';
import { ToggleActivityBarVisibilityAction } from 'vs/workbench/browser/actions/layoutActions';
import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService';
@@ -32,25 +31,34 @@ import { IViewsService, IViewContainersRegistry, Extensions as ViewContainerExte
import { IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { IViewlet } from 'vs/workbench/common/viewlet';
import { isUndefinedOrNull } from 'vs/base/common/types';
import { ISerializableView } from 'vs/base/browser/ui/grid/grid';
const SCM_VIEWLET_ID = 'workbench.view.scm';
import { IActivityBarService } from 'vs/workbench/services/activityBar/browser/activityBarService';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { Schemas } from 'vs/base/common/network';
interface ICachedViewlet {
id: string;
iconUrl: UriComponents;
iconUrl?: UriComponents;
pinned: boolean;
order: number;
order?: number;
visible: boolean;
views?: { when: string }[];
views?: { when?: string }[];
}
export class ActivitybarPart extends Part implements ISerializableView {
export class ActivitybarPart extends Part implements IActivityBarService {
_serviceBrand: ServiceIdentifier<any>;
private static readonly ACTION_HEIGHT = 50;
private static readonly PINNED_VIEWLETS = 'workbench.activity.pinnedViewlets';
private dimension: Dimension;
//#region IView
readonly minimumWidth: number = 50;
readonly maximumWidth: number = 50;
readonly minimumHeight: number = 0;
readonly maximumHeight: number = Number.POSITIVE_INFINITY;
//#endregion
private globalActionBar: ActionBar;
private globalActivityIdToActions: { [globalActivityId: string]: GlobalActivityAction; } = Object.create(null);
@@ -59,27 +67,17 @@ export class ActivitybarPart extends Part implements ISerializableView {
private compositeBar: CompositeBar;
private compositeActions: { [compositeId: string]: { activityAction: ViewletActivityAction, pinnedAction: ToggleCompositePinnedAction } } = Object.create(null);
element: HTMLElement;
minimumWidth: number = 50;
maximumWidth: number = 50;
minimumHeight: number = 0;
maximumHeight: number = Number.POSITIVE_INFINITY;
private _onDidChange = new Emitter<{ width: number; height: number; }>();
readonly onDidChange = this._onDidChange.event;
constructor(
id: string,
@IViewletService private readonly viewletService: IViewletService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IPartService private readonly partService: IPartService,
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,
@IThemeService themeService: IThemeService,
@IStorageService private readonly storageService: IStorageService,
@IExtensionService private readonly extensionService: IExtensionService,
@IViewsService private readonly viewsService: IViewsService,
@IContextKeyService private readonly contextKeyService: IContextKeyService
@IContextKeyService private readonly contextKeyService: IContextKeyService,
) {
super(id, { hasTitle: false }, themeService, storageService);
super(Parts.ACTIVITYBAR_PART, { hasTitle: false }, themeService, storageService, layoutService);
this.cachedViewlets = this.getCachedViewlets();
for (const cachedViewlet of this.cachedViewlets) {
@@ -97,9 +95,9 @@ export class ActivitybarPart extends Part implements ISerializableView {
getOnCompositeClickAction: (compositeId: string) => this.instantiationService.createInstance(ToggleViewletAction, this.viewletService.getViewlet(compositeId)),
getContextMenuActions: () => [this.instantiationService.createInstance(ToggleActivityBarVisibilityAction, ToggleActivityBarVisibilityAction.ID, nls.localize('hideActivitBar', "Hide Activity Bar"))],
getDefaultCompositeId: () => this.viewletService.getDefaultViewletId(),
hidePart: () => this.partService.setSideBarHidden(true),
hidePart: () => this.layoutService.setSideBarHidden(true),
compositeSize: 50,
colors: theme => this.getActivitybarItemColors(theme),
colors: (theme: ITheme) => this.getActivitybarItemColors(theme),
overflowActionSize: ActivitybarPart.ACTION_HEIGHT
}));
@@ -121,6 +119,7 @@ export class ActivitybarPart extends Part implements ISerializableView {
private registerListeners(): void {
// Viewlet registration
this._register(this.viewletService.onDidViewletRegister(viewlet => this.onDidRegisterViewlets([viewlet])));
this._register(this.viewletService.onDidViewletDeregister(({ id }) => this.removeComposite(id, true)));
@@ -130,6 +129,7 @@ export class ActivitybarPart extends Part implements ISerializableView {
// Deactivate viewlet action on close
this._register(this.viewletService.onDidViewletClose(viewlet => this.compositeBar.deactivateComposite(viewlet.getId())));
// Extension registration
let disposables: IDisposable[] = [];
this._register(this.extensionService.onDidRegisterExtensions(() => {
disposables = dispose(disposables);
@@ -137,15 +137,17 @@ export class ActivitybarPart extends Part implements ISerializableView {
this.compositeBar.onDidChange(() => this.saveCachedViewlets(), this, disposables);
this.storageService.onDidChangeStorage(e => this.onDidStorageChange(e), this, disposables);
}));
this._register(toDisposable(() => dispose(disposables)));
}
private onDidRegisterExtensions(): void {
this.removeNotExistingComposites();
for (const viewlet of this.viewletService.getViewlets()) {
this.enableCompositeActions(viewlet);
const viewContainer = this.getViewContainer(viewlet.id);
if (viewContainer) {
if (viewContainer && viewContainer.hideIfEmpty) {
const viewDescriptors = this.viewsService.getViewDescriptors(viewContainer);
if (viewDescriptors) {
this.onDidChangeActiveViews(viewlet, viewDescriptors);
@@ -153,6 +155,7 @@ export class ActivitybarPart extends Part implements ISerializableView {
}
}
}
this.saveCachedViewlets();
}
@@ -165,16 +168,21 @@ export class ActivitybarPart extends Part implements ISerializableView {
}
private onDidViewletOpen(viewlet: IViewlet): void {
// Update the composite bar by adding
this.compositeBar.addComposite(this.viewletService.getViewlet(viewlet.getId()));
const foundViewlet = this.viewletService.getViewlet(viewlet.getId());
if (foundViewlet) {
this.compositeBar.addComposite(foundViewlet);
}
this.compositeBar.activateComposite(viewlet.getId());
const viewletDescriptor = this.viewletService.getViewlet(viewlet.getId());
const viewContainer = this.getViewContainer(viewletDescriptor.id);
if (viewContainer) {
const viewDescriptors = this.viewsService.getViewDescriptors(viewContainer);
if (viewDescriptors && viewDescriptors.activeViewDescriptors.length === 0) {
// Update the composite bar by hiding
this.removeComposite(viewletDescriptor.id, true);
if (viewletDescriptor) {
const viewContainer = this.getViewContainer(viewletDescriptor.id);
if (viewContainer && viewContainer.hideIfEmpty) {
const viewDescriptors = this.viewsService.getViewDescriptors(viewContainer);
if (viewDescriptors && viewDescriptors.activeViewDescriptors.length === 0) {
this.removeComposite(viewletDescriptor.id, true); // Update the composite bar by hiding
}
}
}
}
@@ -230,7 +238,7 @@ export class ActivitybarPart extends Part implements ISerializableView {
container.style.backgroundColor = background;
const borderColor = this.getColor(ACTIVITY_BAR_BORDER) || this.getColor(contrastBorder);
const isPositionLeft = this.partService.getSideBarPosition() === SideBarPosition.LEFT;
const isPositionLeft = this.layoutService.getSideBarPosition() === SideBarPosition.LEFT;
container.style.boxSizing = borderColor && isPositionLeft ? 'border-box' : null;
container.style.borderRightWidth = borderColor && isPositionLeft ? '1px' : null;
container.style.borderRightStyle = borderColor && isPositionLeft ? 'solid' : null;
@@ -247,7 +255,7 @@ export class ActivitybarPart extends Part implements ISerializableView {
badgeBackground: theme.getColor(ACTIVITY_BAR_BADGE_BACKGROUND),
badgeForeground: theme.getColor(ACTIVITY_BAR_BADGE_FOREGROUND),
dragAndDropBackground: theme.getColor(ACTIVITY_BAR_DRAG_AND_DROP_BACKGROUND),
activeBackgroundColor: null, inactiveBackgroundColor: null, activeBorderBottomColor: null,
activeBackgroundColor: undefined, inactiveBackgroundColor: undefined, activeBorderBottomColor: undefined,
};
}
@@ -259,7 +267,7 @@ export class ActivitybarPart extends Part implements ISerializableView {
.map(a => new GlobalActivityAction(a));
this.globalActionBar = this._register(new ActionBar(container, {
actionItemProvider: a => this.instantiationService.createInstance(GlobalActivityActionItem, a, theme => this.getActivitybarItemColors(theme)),
actionItemProvider: a => this.instantiationService.createInstance(GlobalActivityActionItem, a, (theme: ITheme) => this.getActivitybarItemColors(theme)),
orientation: ActionsOrientation.VERTICAL,
ariaLabel: nls.localize('globalActions', "Global Actions"),
animated: false
@@ -283,7 +291,7 @@ export class ActivitybarPart extends Part implements ISerializableView {
} else {
const cachedComposite = this.cachedViewlets.filter(c => c.id === compositeId)[0];
compositeActions = {
activityAction: this.instantiationService.createInstance(PlaceHolderViewletActivityAction, compositeId, cachedComposite ? URI.revive(cachedComposite.iconUrl) : undefined),
activityAction: this.instantiationService.createInstance(PlaceHolderViewletActivityAction, compositeId, cachedComposite && cachedComposite.iconUrl ? URI.revive(cachedComposite.iconUrl) : undefined),
pinnedAction: new PlaceHolderToggleCompositePinnedAction(compositeId, this.compositeBar)
};
}
@@ -316,8 +324,12 @@ export class ActivitybarPart extends Part implements ISerializableView {
}
private shouldBeHidden(viewletId: string, cachedViewlet: ICachedViewlet): boolean {
const viewContainer = this.getViewContainer(viewletId);
if (!viewContainer || !viewContainer.hideIfEmpty) {
return false;
}
return cachedViewlet && cachedViewlet.views && cachedViewlet.views.length
? cachedViewlet.views.every(({ when }) => when && !this.contextKeyService.contextMatchesRules(ContextKeyExpr.deserialize(when)))
? cachedViewlet.views.every(({ when }) => !!when && !this.contextKeyService.contextMatchesRules(ContextKeyExpr.deserialize(when)))
: viewletId === TEST_VIEW_CONTAINER_ID /* Hide Test viewlet for the first time or it had no views registered before */;
}
@@ -336,6 +348,7 @@ export class ActivitybarPart extends Part implements ISerializableView {
} else {
this.compositeBar.removeComposite(compositeId);
}
const compositeActions = this.compositeActions[compositeId];
if (compositeActions) {
compositeActions.activityAction.dispose();
@@ -349,6 +362,7 @@ export class ActivitybarPart extends Part implements ISerializableView {
if (activityAction instanceof PlaceHolderViewletActivityAction) {
activityAction.setActivity(viewlet);
}
if (pinnedAction instanceof PlaceHolderToggleCompositePinnedAction) {
pinnedAction.setActivity(viewlet);
}
@@ -356,38 +370,27 @@ export class ActivitybarPart extends Part implements ISerializableView {
getPinnedViewletIds(): string[] {
const pinnedCompositeIds = this.compositeBar.getPinnedComposites().map(v => v.id);
return this.viewletService.getViewlets()
.filter(v => this.compositeBar.isPinned(v.id))
.sort((v1, v2) => pinnedCompositeIds.indexOf(v1.id) - pinnedCompositeIds.indexOf(v2.id))
.map(v => v.id);
}
layout(dimension: Dimension): Dimension[];
layout(width: number, height: number): void;
layout(dim1: Dimension | number, dim2?: number): Dimension[] | void {
if (!this.partService.isVisible(Parts.ACTIVITYBAR_PART)) {
if (dim1 instanceof Dimension) {
return [dim1];
}
layout(width: number, height: number): void {
if (!this.layoutService.isVisible(Parts.ACTIVITYBAR_PART)) {
return;
}
// Pass to super
const sizes = super.layout(dim1 instanceof Dimension ? dim1 : new Dimension(dim1, dim2));
// Layout contents
const contentAreaSize = super.layoutContents(width, height).contentSize;
this.dimension = sizes[1];
let availableHeight = this.dimension.height;
// Layout composite bar
let availableHeight = contentAreaSize.height;
if (this.globalActionBar) {
// adjust height for global actions showing
availableHeight -= (this.globalActionBar.items.length * ActivitybarPart.ACTION_HEIGHT);
}
this.compositeBar.layout(new Dimension(dim1 instanceof Dimension ? dim1.width : dim1, availableHeight));
if (dim1 instanceof Dimension) {
return sizes;
availableHeight -= (this.globalActionBar.items.length * ActivitybarPart.ACTION_HEIGHT); // adjust height for global actions showing
}
this.compositeBar.layout(new Dimension(width, availableHeight));
}
private onDidStorageChange(e: IWorkspaceStorageChangeEvent): void {
@@ -425,13 +428,14 @@ export class ActivitybarPart extends Part implements ISerializableView {
private saveCachedViewlets(): void {
const state: ICachedViewlet[] = [];
const compositeItems = this.compositeBar.getCompositeBarItems();
const allViewlets = this.viewletService.getViewlets();
const compositeItems = this.compositeBar.getCompositeBarItems();
for (const compositeItem of compositeItems) {
const viewContainer = this.getViewContainer(compositeItem.id);
const viewlet = allViewlets.filter(({ id }) => id === compositeItem.id)[0];
if (viewlet) {
const views: { when: string }[] = [];
const views: { when: string | undefined }[] = [];
if (viewContainer) {
const viewDescriptors = this.viewsService.getViewDescriptors(viewContainer);
if (viewDescriptors) {
@@ -440,9 +444,10 @@ export class ActivitybarPart extends Part implements ISerializableView {
}
}
}
state.push({ id: compositeItem.id, iconUrl: viewlet.iconUrl, views, pinned: compositeItem && compositeItem.pinned, order: compositeItem ? compositeItem.order : undefined, visible: compositeItem && compositeItem.visible });
state.push({ id: compositeItem.id, iconUrl: viewlet.iconUrl && viewlet.iconUrl.scheme === Schemas.file ? viewlet.iconUrl : undefined, views, pinned: compositeItem && compositeItem.pinned, order: compositeItem ? compositeItem.order : undefined, visible: compositeItem && compositeItem.visible });
}
}
this.cachedViewletsValue = JSON.stringify(state);
}
@@ -453,6 +458,7 @@ export class ActivitybarPart extends Part implements ISerializableView {
serialized.visible = isUndefinedOrNull(serialized.visible) ? true : serialized.visible;
return serialized;
});
for (const old of this.loadOldCachedViewlets()) {
const cachedViewlet = cachedViewlets.filter(cached => cached.id === old.id)[0];
if (cachedViewlet) {
@@ -460,6 +466,7 @@ export class ActivitybarPart extends Part implements ISerializableView {
cachedViewlet.views = old.views;
}
}
return cachedViewlets;
}
@@ -467,14 +474,16 @@ export class ActivitybarPart extends Part implements ISerializableView {
const previousState = this.storageService.get('workbench.activity.placeholderViewlets', StorageScope.GLOBAL, '[]');
const result = (<ICachedViewlet[]>JSON.parse(previousState));
this.storageService.remove('workbench.activity.placeholderViewlets', StorageScope.GLOBAL);
return result;
}
private _cachedViewletsValue: string;
private _cachedViewletsValue: string | null;
private get cachedViewletsValue(): string {
if (!this._cachedViewletsValue) {
this._cachedViewletsValue = this.getStoredCachedViewletsValue();
}
return this._cachedViewletsValue;
}
@@ -494,10 +503,6 @@ export class ActivitybarPart extends Part implements ISerializableView {
}
private getViewContainer(viewletId: string): ViewContainer | undefined {
// TODO: @Joao Remove this after moving SCM Viewlet to ViewContainerViewlet - https://github.com/Microsoft/vscode/issues/49054
if (viewletId === SCM_VIEWLET_ID) {
return null;
}
const viewContainerRegistry = Registry.as<IViewContainersRegistry>(ViewContainerExtensions.ViewContainersRegistry);
return viewContainerRegistry.get(viewletId);
}
@@ -508,3 +513,5 @@ export class ActivitybarPart extends Part implements ISerializableView {
};
}
}
registerSingleton(IActivityBarService, ActivitybarPart);

View File

@@ -23,9 +23,9 @@ import { Emitter, Event } from 'vs/base/common/event';
export interface ICompositeBarItem {
id: string;
name: string;
name?: string;
pinned: boolean;
order: number;
order?: number;
visible: boolean;
}
@@ -49,8 +49,8 @@ export class CompositeBar extends Widget implements ICompositeBar {
private dimension: Dimension;
private compositeSwitcherBar: ActionBar;
private compositeOverflowAction: CompositeOverflowActivityAction;
private compositeOverflowActionItem: CompositeOverflowActivityActionItem;
private compositeOverflowAction: CompositeOverflowActivityAction | null;
private compositeOverflowActionItem: CompositeOverflowActivityActionItem | null;
private model: CompositeBarModel;
private visibleComposites: string[];
@@ -112,7 +112,7 @@ export class CompositeBar extends Widget implements ICompositeBar {
if (this.compositeTransfer.hasData(DraggedCompositeIdentifier.prototype)) {
EventHelper.stop(e, true);
const draggedCompositeId = this.compositeTransfer.getData(DraggedCompositeIdentifier.prototype)[0].id;
const draggedCompositeId = this.compositeTransfer.getData(DraggedCompositeIdentifier.prototype)![0].id;
this.compositeTransfer.clearData(DraggedCompositeIdentifier.prototype);
const targetItem = this.model.visibleItems[this.model.visibleItems.length - 1];
@@ -174,7 +174,7 @@ export class CompositeBar extends Widget implements ICompositeBar {
if (this.model.activate(id)) {
// Update if current composite is neither visible nor pinned
// or previous active composite is not pinned
if (this.visibleComposites.indexOf(id) === - 1 || !this.model.activeItem.pinned || (previousActiveItem && !previousActiveItem.pinned)) {
if (this.visibleComposites.indexOf(id) === - 1 || (!!this.model.activeItem && !this.model.activeItem.pinned) || (previousActiveItem && !previousActiveItem.pinned)) {
this.updateCompositeSwitcher();
}
}
@@ -265,7 +265,7 @@ export class CompositeBar extends Widget implements ICompositeBar {
}
}
getAction(compositeId): ActivityAction {
getAction(compositeId: string): ActivityAction {
const item = this.model.findItem(compositeId);
return item && item.activityAction;
}
@@ -304,7 +304,7 @@ export class CompositeBar extends Widget implements ICompositeBar {
let size = 0;
const limit = this.options.orientation === ActionsOrientation.VERTICAL ? this.dimension.height : this.dimension.width;
for (let i = 0; i < compositesToShow.length && size <= limit; i++) {
size += this.compositeSizeInBar.get(compositesToShow[i]);
size += this.compositeSizeInBar.get(compositesToShow[i])!;
if (size > limit) {
maxVisible = i;
}
@@ -312,19 +312,19 @@ export class CompositeBar extends Widget implements ICompositeBar {
overflows = compositesToShow.length > maxVisible;
if (overflows) {
size -= this.compositeSizeInBar.get(compositesToShow[maxVisible]);
size -= this.compositeSizeInBar.get(compositesToShow[maxVisible])!;
compositesToShow = compositesToShow.slice(0, maxVisible);
size += this.options.overflowActionSize;
}
// Check if we need to make extra room for the overflow action
if (size > limit) {
size -= this.compositeSizeInBar.get(compositesToShow.pop());
size -= this.compositeSizeInBar.get(compositesToShow.pop()!)!;
}
// We always try show the active composite
if (this.model.activeItem && compositesToShow.every(compositeId => compositeId !== this.model.activeItem.id)) {
const removedComposite = compositesToShow.pop();
size = size - this.compositeSizeInBar.get(removedComposite) + this.compositeSizeInBar.get(this.model.activeItem.id);
if (this.model.activeItem && compositesToShow.every(compositeId => !!this.model.activeItem && compositeId !== this.model.activeItem.id)) {
const removedComposite = compositesToShow.pop()!;
size = size - this.compositeSizeInBar.get(removedComposite)! + this.compositeSizeInBar.get(this.model.activeItem.id)!;
compositesToShow.push(this.model.activeItem.id);
}
@@ -342,7 +342,9 @@ export class CompositeBar extends Widget implements ICompositeBar {
this.compositeOverflowAction.dispose();
this.compositeOverflowAction = null;
this.compositeOverflowActionItem.dispose();
if (this.compositeOverflowActionItem) {
this.compositeOverflowActionItem.dispose();
}
this.compositeOverflowActionItem = null;
}
@@ -378,7 +380,11 @@ export class CompositeBar extends Widget implements ICompositeBar {
// Add overflow action as needed
if ((visibleCompositesChange && overflows) || this.compositeSwitcherBar.length() === 0) {
this.compositeOverflowAction = this.instantiationService.createInstance(CompositeOverflowActivityAction, () => this.compositeOverflowActionItem.showMenu());
this.compositeOverflowAction = this.instantiationService.createInstance(CompositeOverflowActivityAction, () => {
if (this.compositeOverflowActionItem) {
this.compositeOverflowActionItem.showMenu();
}
});
this.compositeOverflowActionItem = this.instantiationService.createInstance(
CompositeOverflowActivityActionItem,
this.compositeOverflowAction,
@@ -398,7 +404,7 @@ export class CompositeBar extends Widget implements ICompositeBar {
this._onDidChange.fire();
}
private getOverflowingComposites(): { id: string, name: string }[] {
private getOverflowingComposites(): { id: string, name?: string }[] {
let overflowingIds = this.model.visibleItems.filter(item => item.pinned).map(item => item.id);
// Show the active composite even if it is not pinned
@@ -453,7 +459,7 @@ class CompositeBarModel {
private _items: ICompositeBarModelItem[];
private readonly options: ICompositeBarOptions;
activeItem: ICompositeBarModelItem;
activeItem?: ICompositeBarModelItem;
constructor(
items: ICompositeBarItem[],
@@ -507,7 +513,7 @@ class CompositeBarModel {
return this.items.filter(item => item.visible && item.pinned);
}
private createCompositeBarItem(id: string, name: string, order: number, pinned: boolean, visible: boolean): ICompositeBarModelItem {
private createCompositeBarItem(id: string, name: string | undefined, order: number | undefined, pinned: boolean, visible: boolean): ICompositeBarModelItem {
const options = this.options;
return {
id, name, pinned, order, visible,
@@ -521,7 +527,7 @@ class CompositeBarModel {
};
}
add(id: string, name: string, order: number): boolean {
add(id: string, name: string, order: number | undefined): boolean {
const item = this.findItem(id);
if (item) {
let changed = false;
@@ -541,7 +547,7 @@ class CompositeBarModel {
this.items.push(item);
} else {
let index = 0;
while (index < this.items.length && this.items[index].order < order) {
while (index < this.items.length && typeof this.items[index].order === 'number' && this.items[index].order! < order) {
index++;
}
this.items.splice(index, 0, item);

View File

@@ -23,7 +23,7 @@ import { Color } from 'vs/base/common/color';
export interface ICompositeActivity {
badge: IBadge;
clazz: string;
clazz?: string;
priority: number;
}
@@ -57,13 +57,11 @@ export class ActivityAction extends Action {
private _onDidChangeBadge = new Emitter<this>();
get onDidChangeBadge(): Event<this> { return this._onDidChangeBadge.event; }
private badge: IBadge;
private badge?: IBadge;
private clazz: string | undefined;
constructor(private _activity: IActivity) {
super(_activity.id, _activity.name, _activity.cssClass);
this.badge = null;
}
get activity(): IActivity {
@@ -87,7 +85,7 @@ export class ActivityAction extends Action {
}
}
getBadge(): IBadge {
getBadge(): IBadge | undefined {
return this.badge;
}
@@ -95,7 +93,7 @@ export class ActivityAction extends Action {
return this.clazz;
}
setBadge(badge: IBadge, clazz?: string): void {
setBadge(badge: IBadge | undefined, clazz?: string): void {
this.badge = badge;
this.clazz = clazz;
this._onDidChangeBadge.fire(this);
@@ -110,14 +108,14 @@ export class ActivityAction extends Action {
}
export interface ICompositeBarColors {
activeBackgroundColor: Color;
inactiveBackgroundColor: Color;
activeBorderBottomColor: Color;
activeForegroundColor: Color;
inactiveForegroundColor: Color;
badgeBackground: Color;
badgeForeground: Color;
dragAndDropBackground: Color;
activeBackgroundColor?: Color;
inactiveBackgroundColor?: Color;
activeBorderBottomColor?: Color;
activeForegroundColor?: Color;
inactiveForegroundColor?: Color;
badgeBackground?: Color;
badgeForeground?: Color;
dragAndDropBackground?: Color;
}
export interface IActivityActionItemOptions extends IBaseActionItemOptions {
@@ -207,10 +205,10 @@ export class ActivityActionItem extends BaseActionItem {
}));
// Label
this.label = dom.append(this.element, dom.$('a'));
this.label = dom.append(this.element!, dom.$('a'));
// Badge
this.badge = dom.append(this.element, dom.$('.badge'));
this.badge = dom.append(this.element!, dom.$('.badge'));
this.badgeContent = dom.append(this.badge, dom.$('.badge-content'));
dom.hide(this.badge);
@@ -254,9 +252,9 @@ export class ActivityActionItem extends BaseActionItem {
const noOfThousands = badge.number / 1000;
const floor = Math.floor(noOfThousands);
if (noOfThousands > floor) {
number = nls.localize('largeNumberBadge1', '{0}k+', floor);
number = `${floor}K+`;
} else {
number = nls.localize('largeNumberBadge2', '{0}k', noOfThousands);
number = `${noOfThousands}K`;
}
}
this.badgeContent.textContent = number;
@@ -373,7 +371,7 @@ export class CompositeOverflowActivityActionItem extends ActivityActionItem {
this.actions = this.getActions();
this.contextMenuService.showContextMenu({
getAnchor: () => this.element,
getAnchor: () => this.element!,
getActions: () => this.actions,
onHide: () => dispose(this.actions)
});
@@ -385,7 +383,7 @@ export class CompositeOverflowActivityActionItem extends ActivityActionItem {
action.radio = this.getActiveCompositeId() === action.id;
const badge = this.getBadge(composite.id);
let suffix: string | number;
let suffix: string | number | undefined;
if (badge instanceof NumberBadge) {
suffix = badge.number;
} else if (badge instanceof TextBadge) {
@@ -434,7 +432,7 @@ export class CompositeActionItem extends ActivityActionItem {
private static manageExtensionAction: ManageExtensionAction;
private compositeActivity: IActivity;
private compositeActivity: IActivity | null;
private compositeTransfer: LocalSelectionTransfer<DraggedCompositeIdentifier>;
constructor(
@@ -463,7 +461,7 @@ export class CompositeActionItem extends ActivityActionItem {
protected get activity(): IActivity {
if (!this.compositeActivity) {
let activityName: string;
const keybinding = this.getKeybindingLabel(this.compositeActivityAction.activity.keybindingId);
const keybinding = typeof this.compositeActivityAction.activity.keybindingId === 'string' ? this.getKeybindingLabel(this.compositeActivityAction.activity.keybindingId) : null;
if (keybinding) {
activityName = nls.localize('titleKeybinding', "{0} ({1})", this.compositeActivityAction.activity.name, keybinding);
} else {
@@ -480,7 +478,7 @@ export class CompositeActionItem extends ActivityActionItem {
return this.compositeActivity;
}
private getKeybindingLabel(id: string): string {
private getKeybindingLabel(id: string): string | null {
const kb = this.keybindingService.lookupKeybinding(id);
if (kb) {
return kb.getLabel();
@@ -503,7 +501,7 @@ export class CompositeActionItem extends ActivityActionItem {
// Allow to drag
this._register(dom.addDisposableListener(this.container, dom.EventType.DRAG_START, (e: DragEvent) => {
e.dataTransfer.effectAllowed = 'move';
e.dataTransfer!.effectAllowed = 'move';
// Registe as dragged to local transfer
this.compositeTransfer.setData([new DraggedCompositeIdentifier(this.activity.id)], DraggedCompositeIdentifier.prototype);
@@ -516,7 +514,7 @@ export class CompositeActionItem extends ActivityActionItem {
this._register(new DragAndDropObserver(this.container, {
onDragEnter: e => {
if (this.compositeTransfer.hasData(DraggedCompositeIdentifier.prototype) && this.compositeTransfer.getData(DraggedCompositeIdentifier.prototype)[0].id !== this.activity.id) {
if (this.compositeTransfer.hasData(DraggedCompositeIdentifier.prototype) && this.compositeTransfer.getData(DraggedCompositeIdentifier.prototype)![0].id !== this.activity.id) {
this.updateFromDragging(container, true);
}
},
@@ -539,7 +537,7 @@ export class CompositeActionItem extends ActivityActionItem {
dom.EventHelper.stop(e, true);
if (this.compositeTransfer.hasData(DraggedCompositeIdentifier.prototype)) {
const draggedCompositeId = this.compositeTransfer.getData(DraggedCompositeIdentifier.prototype)[0].id;
const draggedCompositeId = this.compositeTransfer.getData(DraggedCompositeIdentifier.prototype)![0].id;
if (draggedCompositeId !== this.activity.id) {
this.updateFromDragging(container, false);
this.compositeTransfer.clearData(DraggedCompositeIdentifier.prototype);
@@ -617,6 +615,10 @@ export class CompositeActionItem extends ActivityActionItem {
}
protected updateEnabled(): void {
if (!this.element) {
return;
}
if (this.getAction().enabled) {
dom.removeClass(this.element, 'disabled');
} else {
@@ -636,12 +638,12 @@ export class CompositeActionItem extends ActivityActionItem {
export class ToggleCompositePinnedAction extends Action {
constructor(
private activity: IActivity,
private activity: IActivity | undefined,
private compositeBar: ICompositeBar
) {
super('show.toggleCompositePinned', activity ? activity.name : nls.localize('toggle', "Toggle View Pinned"));
this.checked = this.activity && this.compositeBar.isPinned(this.activity.id);
this.checked = !!this.activity && this.compositeBar.isPinned(this.activity.id);
}
run(context: string): Promise<any> {

View File

@@ -19,7 +19,7 @@ import { Part, IPartOptions } from 'vs/workbench/browser/part';
import { Composite, CompositeRegistry } from 'vs/workbench/browser/composite';
import { IComposite } from 'vs/workbench/common/composite';
import { ScopedProgressService } from 'vs/workbench/services/progress/browser/progressService';
import { IPartService } from 'vs/workbench/services/part/common/partService';
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
@@ -32,6 +32,7 @@ import { attachProgressBarStyler } from 'vs/platform/theme/common/styler';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { Dimension, append, $, addClass, hide, show, addClasses } from 'vs/base/browser/dom';
import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview';
import { withNullAsUndefined } from 'vs/base/common/types';
export interface ICompositeTitleLabel {
@@ -54,8 +55,8 @@ interface CompositeItem {
export abstract class CompositePart<T extends Composite> extends Part {
protected _onDidCompositeOpen = this._register(new Emitter<{ composite: IComposite, focus: boolean }>());
protected _onDidCompositeClose = this._register(new Emitter<IComposite>());
protected readonly onDidCompositeOpen = this._register(new Emitter<{ composite: IComposite, focus: boolean }>());
protected readonly onDidCompositeClose = this._register(new Emitter<IComposite>());
protected toolBar: ToolBar;
@@ -75,7 +76,7 @@ export abstract class CompositePart<T extends Composite> extends Part {
protected storageService: IStorageService,
private telemetryService: ITelemetryService,
protected contextMenuService: IContextMenuService,
protected partService: IPartService,
protected layoutService: IWorkbenchLayoutService,
protected keybindingService: IKeybindingService,
protected instantiationService: IInstantiationService,
themeService: IThemeService,
@@ -84,11 +85,11 @@ export abstract class CompositePart<T extends Composite> extends Part {
private defaultCompositeId: string,
private nameForTelemetry: string,
private compositeCSSClass: string,
private titleForegroundColor: string,
private titleForegroundColor: string | undefined,
id: string,
options: IPartOptions
) {
super(id, options, themeService, storageService);
super(id, options, themeService, storageService, layoutService);
this.mapCompositeToCompositeContainer = {};
this.mapActionsBindingToComposite = {};
@@ -98,6 +99,7 @@ export abstract class CompositePart<T extends Composite> extends Part {
}
protected openComposite(id: string, focus?: boolean): Composite | undefined {
// Check if composite already visible and just focus in that case
if (this.activeComposite && this.activeComposite.getId() === id) {
if (focus) {
@@ -140,7 +142,7 @@ export abstract class CompositePart<T extends Composite> extends Part {
composite.focus();
}
this._onDidCompositeOpen.fire({ composite, focus });
this.onDidCompositeOpen.fire({ composite, focus });
return composite;
}
@@ -152,7 +154,7 @@ export abstract class CompositePart<T extends Composite> extends Part {
// Return with the composite that is being opened
if (composite) {
this._onDidCompositeOpen.fire({ composite, focus });
this.onDidCompositeOpen.fire({ composite, focus });
}
return composite;
@@ -222,7 +224,7 @@ export abstract class CompositePart<T extends Composite> extends Part {
// Report progress for slow loading composites (but only if we did not create the composites before already)
const compositeItem = this.instantiatedCompositeItems.get(composite.getId());
if (compositeItem && !compositeContainer) {
compositeItem.progressService.showWhile(Promise.resolve(), this.partService.isRestored() ? 800 : 3200 /* less ugly initial startup */);
compositeItem.progressService.showWhile(Promise.resolve(), this.layoutService.isRestored() ? 800 : 3200 /* less ugly initial startup */);
}
// Fill Content and Actions
@@ -244,7 +246,7 @@ export abstract class CompositePart<T extends Composite> extends Part {
// Update title with composite title if it differs from descriptor
const descriptor = this.registry.getComposite(composite.getId());
if (descriptor && descriptor.name !== composite.getTitle()) {
this.updateTitle(composite.getId(), composite.getTitle() || undefined);
this.updateTitle(composite.getId(), withNullAsUndefined(composite.getTitle()));
}
// Handle Composite Actions
@@ -341,6 +343,9 @@ export abstract class CompositePart<T extends Composite> extends Part {
primaryActions.push(...this.getActions());
secondaryActions.push(...this.getSecondaryActions());
// Update context
this.toolBar.context = this.actionsContextProvider();
// Return fn to set into toolbar
return this.toolBar.setActions(prepareActions(primaryActions), prepareActions(secondaryActions));
}
@@ -375,7 +380,7 @@ export abstract class CompositePart<T extends Composite> extends Part {
// Empty Actions
this.toolBar.setActions([])();
this._onDidCompositeClose.fire(composite);
this.onDidCompositeClose.fire(composite);
return composite;
}
@@ -415,12 +420,12 @@ export abstract class CompositePart<T extends Composite> extends Part {
},
updateStyles: () => {
titleLabel.style.color = $this.getColor($this.titleForegroundColor);
titleLabel.style.color = $this.titleForegroundColor ? $this.getColor($this.titleForegroundColor) : null;
}
};
}
protected updateStyles(): void {
updateStyles(): void {
super.updateStyles();
// Forward to title label
@@ -437,6 +442,16 @@ export abstract class CompositePart<T extends Composite> extends Part {
return null;
}
protected actionsContextProvider(): any {
// Check Active Composite
if (this.activeComposite) {
return this.activeComposite.getActionsContext();
}
return null;
}
createContentArea(parent: HTMLElement): HTMLElement {
const contentContainer = append(parent, $('.content'));
@@ -449,6 +464,7 @@ export abstract class CompositePart<T extends Composite> extends Part {
getProgressIndicator(id: string): IProgressService | null {
const compositeItem = this.instantiatedCompositeItems.get(id);
return compositeItem ? compositeItem.progressService : null;
}
@@ -464,27 +480,20 @@ export abstract class CompositePart<T extends Composite> extends Part {
return AnchorAlignment.RIGHT;
}
layout(dimension: Dimension): Dimension[];
layout(width: number, height: number): void;
layout(dim1: Dimension | number, dim2?: number): Dimension[] | void {
// Pass to super
const sizes = super.layout(dim1 instanceof Dimension ? dim1 : new Dimension(dim1, dim2!));
layout(width: number, height: number): void {
// Pass Contentsize to composite
this.contentAreaSize = sizes[1];
// Layout contents
this.contentAreaSize = super.layoutContents(width, height).contentSize;
// Layout composite
if (this.activeComposite) {
this.activeComposite.layout(this.contentAreaSize);
}
if (dim1 instanceof Dimension) {
return sizes;
}
}
protected removeComposite(compositeId: string): boolean {
if (this.activeComposite && this.activeComposite.getId() === compositeId) {
// do not remove active compoiste
return false;
return false; // do not remove active composite
}
delete this.mapCompositeToCompositeContainer[compositeId];
@@ -495,6 +504,7 @@ export abstract class CompositePart<T extends Composite> extends Part {
dispose(compositeItem.disposable);
this.instantiatedCompositeItems.delete(compositeId);
}
return true;
}

View File

@@ -8,7 +8,7 @@ import { EditorInput, EditorOptions, IEditor, GroupIdentifier, IEditorMemento }
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService';
import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { LRUCache } from 'vs/base/common/map';
import { URI } from 'vs/base/common/uri';

View File

@@ -11,7 +11,7 @@ import { localize } from 'vs/nls';
import { IConfigurationOverrides, IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
import { Registry } from 'vs/platform/registry/common/platform';
import { GroupIdentifier } from 'vs/workbench/common/editor';
@@ -19,7 +19,7 @@ export const IBreadcrumbsService = createDecorator<IBreadcrumbsService>('IEditor
export interface IBreadcrumbsService {
_serviceBrand: any;
_serviceBrand: ServiceIdentifier<any>;
register(group: GroupIdentifier, widget: BreadcrumbsWidget): IDisposable;
@@ -29,7 +29,7 @@ export interface IBreadcrumbsService {
export class BreadcrumbsService implements IBreadcrumbsService {
_serviceBrand: any;
_serviceBrand: ServiceIdentifier<any>;
private readonly _map = new Map<number, BreadcrumbsWidget>();

View File

@@ -41,10 +41,12 @@ import { BreadcrumbElement, EditorBreadcrumbsModel, FileElement } from 'vs/workb
import { BreadcrumbsPicker, createBreadcrumbsPicker } from 'vs/workbench/browser/parts/editor/breadcrumbsPicker';
import { SideBySideEditorInput } from 'vs/workbench/common/editor';
import { ACTIVE_GROUP, ACTIVE_GROUP_TYPE, IEditorService, SIDE_GROUP, SIDE_GROUP_TYPE } from 'vs/workbench/services/editor/common/editorService';
import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { IEditorGroupView } from 'vs/workbench/browser/parts/editor/editor';
import { onDidChangeZoomLevel } from 'vs/base/browser/browser';
import { withNullAsUndefined, withUndefinedAsNull } from 'vs/base/common/types';
class Item extends BreadcrumbsItem {
@@ -148,6 +150,7 @@ export class BreadcrumbsControl {
private _disposables = new Array<IDisposable>();
private _breadcrumbsDisposables = new Array<IDisposable>();
private _breadcrumbsPickerShowing = false;
private _breadcrumbsPickerIgnoreOnceItem: BreadcrumbsItem | undefined;
constructor(
container: HTMLElement,
@@ -196,7 +199,7 @@ export class BreadcrumbsControl {
this.domNode.remove();
}
layout(dim: dom.Dimension): void {
layout(dim: dom.Dimension | undefined): void {
this._widget.layout(dim);
}
@@ -219,7 +222,7 @@ export class BreadcrumbsControl {
input = input.master;
}
if (!input || !input.getResource() || (input.getResource().scheme !== Schemas.untitled && !this._fileService.canHandleResource(input.getResource()))) {
if (!input || !input.getResource() || (input.getResource()!.scheme !== Schemas.untitled && !this._fileService.canHandleResource(input.getResource()!))) {
// cleanup and return when there is no input or when
// we cannot handle this input
this._ckBreadcrumbsPossible.set(false);
@@ -236,7 +239,7 @@ export class BreadcrumbsControl {
this._ckBreadcrumbsPossible.set(true);
let editor = this._getActiveCodeEditor();
let model = new EditorBreadcrumbsModel(input.getResource(), editor, this._workspaceService, this._configurationService);
let model = new EditorBreadcrumbsModel(input.getResource()!, editor, this._workspaceService, this._configurationService);
dom.toggleClass(this.domNode, 'relative-path', model.isRelative());
let updateBreadcrumbs = () => {
@@ -260,9 +263,12 @@ export class BreadcrumbsControl {
return true;
}
private _getActiveCodeEditor(): ICodeEditor {
private _getActiveCodeEditor(): ICodeEditor | undefined {
if (!this._editorGroup.activeControl) {
return undefined;
}
let control = this._editorGroup.activeControl.getControl();
let editor: ICodeEditor;
let editor: ICodeEditor | undefined;
if (isCodeEditor(control)) {
editor = control as ICodeEditor;
} else if (isDiffEditor(control)) {
@@ -282,6 +288,13 @@ export class BreadcrumbsControl {
return;
}
if (event.item === this._breadcrumbsPickerIgnoreOnceItem) {
this._breadcrumbsPickerIgnoreOnceItem = undefined;
this._widget.setFocused(undefined);
this._widget.setSelection(undefined);
return;
}
const { element } = event.item as Item;
this._editorGroup.focus();
@@ -313,7 +326,7 @@ export class BreadcrumbsControl {
let picker: BreadcrumbsPicker;
let editor = this._getActiveCodeEditor();
let editorDecorations: string[] = [];
let editorViewState: ICodeEditorViewState;
let editorViewState: ICodeEditorViewState | undefined;
this._contextViewService.showContextView({
render: (parent: HTMLElement) => {
@@ -336,7 +349,7 @@ export class BreadcrumbsControl {
return;
}
if (!editorViewState) {
editorViewState = editor.saveViewState();
editorViewState = withNullAsUndefined(editor.saveViewState());
}
const { symbol } = data.target;
editor.revealRangeInCenter(symbol.range, ScrollType.Smooth);
@@ -347,12 +360,29 @@ export class BreadcrumbsControl {
isWholeLine: true
}
}]);
});
let zoomListener = onDidChangeZoomLevel(() => {
this._contextViewService.hideContextView(this);
});
let focusTracker = dom.trackFocus(parent);
let blurListener = focusTracker.onDidBlur(() => {
this._breadcrumbsPickerIgnoreOnceItem = this._widget.isDOMFocused() ? event.item : undefined;
this._contextViewService.hideContextView(this);
});
this._breadcrumbsPickerShowing = true;
this._updateCkBreadcrumbsActive();
return combinedDisposable([selectListener, focusListener, picker]);
return combinedDisposable([
picker,
selectListener,
focusListener,
zoomListener,
focusTracker,
blurListener
]);
},
getAnchor: () => {
let maxInnerWidth = window.innerWidth - 8 /*a little less the full widget*/;
@@ -381,7 +411,7 @@ export class BreadcrumbsControl {
} else {
pickerArrowOffset = (data.left + (data.width * 0.3)) - x;
}
picker.setInput(element, maxHeight, pickerWidth, pickerArrowSize, Math.max(0, pickerArrowOffset));
picker.show(element, maxHeight, pickerWidth, pickerArrowSize, Math.max(0, pickerArrowOffset));
return { x, y };
},
onHide: (data) => {
@@ -406,7 +436,7 @@ export class BreadcrumbsControl {
this._ckBreadcrumbsActive.set(value);
}
private _revealInEditor(event: IBreadcrumbsItemEvent, element: any, group: SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE, pinned: boolean = false): void {
private _revealInEditor(event: IBreadcrumbsItemEvent, element: any, group: SIDE_GROUP_TYPE | ACTIVE_GROUP_TYPE | undefined, pinned: boolean = false): void {
if (element instanceof FileElement) {
if (element.kind === FileKind.FILE) {
// open file in any editor
@@ -421,14 +451,16 @@ export class BreadcrumbsControl {
} else if (element instanceof OutlineElement) {
// open symbol in code editor
let model = OutlineModel.get(element);
this._codeEditorService.openCodeEditor({
resource: model.textModel.uri,
options: {
selection: Range.collapseToStart(element.symbol.selectionRange),
revealInCenterIfOutsideViewport: true
}
}, this._getActiveCodeEditor(), group === SIDE_GROUP);
const model = OutlineModel.get(element);
if (model) {
this._codeEditorService.openCodeEditor({
resource: model.textModel.uri,
options: {
selection: Range.collapseToStart(element.symbol.selectionRange),
revealInCenterIfOutsideViewport: true
}
}, withUndefinedAsNull(this._getActiveCodeEditor()), group === SIDE_GROUP);
}
}
}
@@ -449,7 +481,7 @@ export class BreadcrumbsControl {
MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
command: {
id: 'breadcrumbs.toggle',
title: { value: localize('cmd.toggle', "Toggle Breadcrumbs"), original: 'Toggle Breadcrumbs' },
title: { value: localize('cmd.toggle', "Toggle Breadcrumbs"), original: 'View: Toggle Breadcrumbs' },
category: localize('cmd.category', "View")
}
});
@@ -540,7 +572,11 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
handler(accessor) {
const groups = accessor.get(IEditorGroupsService);
const breadcrumbs = accessor.get(IBreadcrumbsService);
breadcrumbs.getWidget(groups.activeGroup.id).focusNext();
const widget = breadcrumbs.getWidget(groups.activeGroup.id);
if (!widget) {
return;
}
widget.focusNext();
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
@@ -556,7 +592,11 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
handler(accessor) {
const groups = accessor.get(IEditorGroupsService);
const breadcrumbs = accessor.get(IBreadcrumbsService);
breadcrumbs.getWidget(groups.activeGroup.id).focusPrev();
const widget = breadcrumbs.getWidget(groups.activeGroup.id);
if (!widget) {
return;
}
widget.focusPrev();
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
@@ -569,6 +609,9 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
const groups = accessor.get(IEditorGroupsService);
const breadcrumbs = accessor.get(IBreadcrumbsService);
const widget = breadcrumbs.getWidget(groups.activeGroup.id);
if (!widget) {
return;
}
widget.setSelection(widget.getFocused(), BreadcrumbsControl.Payload_Pick);
}
});
@@ -582,6 +625,9 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
const groups = accessor.get(IEditorGroupsService);
const breadcrumbs = accessor.get(IBreadcrumbsService);
const widget = breadcrumbs.getWidget(groups.activeGroup.id);
if (!widget) {
return;
}
widget.setSelection(widget.getFocused(), BreadcrumbsControl.Payload_Reveal);
}
});
@@ -593,9 +639,15 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
handler(accessor) {
const groups = accessor.get(IEditorGroupsService);
const breadcrumbs = accessor.get(IBreadcrumbsService);
breadcrumbs.getWidget(groups.activeGroup.id).setFocused(undefined);
breadcrumbs.getWidget(groups.activeGroup.id).setSelection(undefined);
groups.activeGroup.activeControl.focus();
const widget = breadcrumbs.getWidget(groups.activeGroup.id);
if (!widget) {
return;
}
widget.setFocused(undefined);
widget.setSelection(undefined);
if (groups.activeGroup.activeControl) {
groups.activeGroup.activeControl.focus();
}
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
@@ -606,15 +658,20 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
handler(accessor) {
const editors = accessor.get(IEditorService);
const lists = accessor.get(IListService);
const element = <OutlineElement | IFileStat>lists.lastFocusedList.getFocus();
const element = lists.lastFocusedList ? <OutlineElement | IFileStat>lists.lastFocusedList.getFocus() : undefined;
if (element instanceof OutlineElement) {
const outlineElement = OutlineModel.get(element);
if (!outlineElement) {
return undefined;
}
// open symbol in editor
return editors.openEditor({
resource: OutlineModel.get(element).textModel.uri,
resource: outlineElement.textModel.uri,
options: { selection: Range.collapseToStart(element.symbol.selectionRange) }
}, SIDE_GROUP);
} else if (URI.isUri(element.resource)) {
} else if (element && URI.isUri(element.resource)) {
// open file in editor
return editors.openEditor({
resource: element.resource,

View File

@@ -21,6 +21,7 @@ import { Schemas } from 'vs/base/common/network';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { BreadcrumbsConfig } from 'vs/workbench/browser/parts/editor/breadcrumbs';
import { FileKind } from 'vs/platform/files/common/files';
import { withNullAsUndefined } from 'vs/base/common/types';
export class FileElement {
constructor(
@@ -105,7 +106,7 @@ export class EditorBreadcrumbsModel {
}
let info: FileInfo = {
folder: workspaceService.getWorkspaceFolder(uri) || undefined,
folder: withNullAsUndefined(workspaceService.getWorkspaceFolder(uri)),
path: []
};
@@ -117,7 +118,7 @@ export class EditorBreadcrumbsModel {
info.path.unshift(new FileElement(uriPrefix, info.path.length === 0 ? FileKind.FILE : FileKind.FOLDER));
let prevPathLength = uriPrefix.path.length;
uriPrefix = dirname(uriPrefix);
if (!uriPrefix || uriPrefix.path.length === prevPathLength) {
if (uriPrefix.path.length === prevPathLength) {
break;
}
}

View File

@@ -3,35 +3,38 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { onDidChangeZoomLevel } from 'vs/base/browser/browser';
import * as dom from 'vs/base/browser/dom';
import { compareFileNames } from 'vs/base/common/comparers';
import { onUnexpectedError } from 'vs/base/common/errors';
import { Emitter, Event } from 'vs/base/common/event';
import { createMatches, FuzzyScore, fuzzyScore } from 'vs/base/common/filters';
import { createMatches, FuzzyScore } from 'vs/base/common/filters';
import * as glob from 'vs/base/common/glob';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { join } from 'vs/base/common/paths';
import { posix } from 'vs/base/common/path';
import { basename, dirname, isEqual } from 'vs/base/common/resources';
import { URI } from 'vs/base/common/uri';
import { IDataSource, IFilter, IRenderer, ISorter, ITree } from 'vs/base/parts/tree/browser/tree';
import 'vs/css!./media/breadcrumbscontrol';
import { OutlineElement, OutlineModel, TreeElement } from 'vs/editor/contrib/documentSymbols/outlineModel';
import { OutlineDataSource, OutlineItemComparator, OutlineRenderer, OutlineItemCompareType } from 'vs/editor/contrib/documentSymbols/outlineTree';
import { localize } from 'vs/nls';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import { FileKind, IFileService, IFileStat } from 'vs/platform/files/common/files';
import { IConstructorSignature1, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { HighlightingWorkbenchTree, IHighlighter, IHighlightingTreeConfiguration, IHighlightingTreeOptions } from 'vs/platform/list/browser/listService';
import { WorkbenchDataTree, WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService';
import { breadcrumbsPickerBackground, widgetShadow } from 'vs/platform/theme/common/colorRegistry';
import { IWorkspace, IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { ResourceLabels, IResourceLabel, DEFAULT_LABELS_CONTAINER } from 'vs/workbench/browser/labels';
import { BreadcrumbsConfig } from 'vs/workbench/browser/parts/editor/breadcrumbs';
import { BreadcrumbElement, FileElement } from 'vs/workbench/browser/parts/editor/breadcrumbsModel';
import { IFileIconTheme, IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { IAsyncDataSource, ITreeRenderer, ITreeNode, ITreeFilter, TreeVisibility, ITreeSorter, IDataSource } from 'vs/base/browser/ui/tree/tree';
import { OutlineVirtualDelegate, OutlineGroupRenderer, OutlineElementRenderer, OutlineItemComparator, OutlineIdentityProvider, OutlineNavigationLabelProvider, OutlineDataSource, OutlineSortOrder, OutlineItem } from 'vs/editor/contrib/documentSymbols/outlineTree';
import { IIdentityProvider, IListVirtualDelegate, IKeyboardNavigationLabelProvider } from 'vs/base/browser/ui/list/list';
import { IDataTreeOptions } from 'vs/base/browser/ui/tree/dataTree';
export function createBreadcrumbsPicker(instantiationService: IInstantiationService, parent: HTMLElement, element: BreadcrumbElement): BreadcrumbsPicker {
let ctor: IConstructorSignature1<HTMLElement, BreadcrumbsPicker> = element instanceof FileElement ? BreadcrumbsFilePicker : BreadcrumbsOutlinePicker;
const ctor: IConstructorSignature1<HTMLElement, BreadcrumbsPicker> = element instanceof FileElement
? BreadcrumbsFilePicker
: BreadcrumbsOutlinePicker;
return instantiationService.createInstance(ctor, parent);
}
@@ -43,16 +46,17 @@ interface ILayoutInfo {
inputHeight: number;
}
type Tree<I, E> = WorkbenchDataTree<I, E, FuzzyScore> | WorkbenchAsyncDataTree<I, E, FuzzyScore>;
export abstract class BreadcrumbsPicker {
protected readonly _disposables = new Array<IDisposable>();
protected readonly _domNode: HTMLDivElement;
protected readonly _arrow: HTMLDivElement;
protected readonly _treeContainer: HTMLDivElement;
protected readonly _tree: HighlightingWorkbenchTree;
protected readonly _focus: dom.IFocusTracker;
protected readonly _symbolSortOrder: BreadcrumbsConfig<'position' | 'name' | 'type'>;
private _layoutInfo: ILayoutInfo;
protected _arrow: HTMLDivElement;
protected _treeContainer: HTMLDivElement;
protected _tree: Tree<any, any>;
protected _fakeEvent = new UIEvent('fakeEvent');
protected _layoutInfo: ILayoutInfo;
private readonly _onDidPickElement = new Emitter<{ target: any, payload: any }>();
readonly onDidPickElement: Event<{ target: any, payload: any }> = this._onDidPickElement.event;
@@ -64,154 +68,111 @@ export abstract class BreadcrumbsPicker {
parent: HTMLElement,
@IInstantiationService protected readonly _instantiationService: IInstantiationService,
@IWorkbenchThemeService protected readonly _themeService: IWorkbenchThemeService,
@IConfigurationService private readonly _configurationService: IConfigurationService,
@IConfigurationService protected readonly _configurationService: IConfigurationService,
) {
this._domNode = document.createElement('div');
this._domNode.className = 'monaco-breadcrumbs-picker show-file-icons';
parent.appendChild(this._domNode);
this._focus = dom.trackFocus(this._domNode);
this._focus.onDidBlur(_ => this._onDidPickElement.fire({ target: undefined, payload: undefined }), undefined, this._disposables);
this._disposables.push(onDidChangeZoomLevel(_ => this._onDidPickElement.fire({ target: undefined, payload: undefined })));
const theme = this._themeService.getTheme();
const color = theme.getColor(breadcrumbsPickerBackground);
this._arrow = document.createElement('div');
this._arrow.className = 'arrow';
this._arrow.style.borderColor = `transparent transparent ${color.toString()}`;
this._domNode.appendChild(this._arrow);
this._treeContainer = document.createElement('div');
this._treeContainer.style.background = color.toString();
this._treeContainer.style.paddingTop = '2px';
this._treeContainer.style.boxShadow = `0px 5px 8px ${this._themeService.getTheme().getColor(widgetShadow)}`;
this._domNode.appendChild(this._treeContainer);
this._symbolSortOrder = BreadcrumbsConfig.SymbolSortOrder.bindTo(this._configurationService);
const filterConfig = BreadcrumbsConfig.FilterOnType.bindTo(this._configurationService);
this._disposables.push(filterConfig);
const treeConfig = this._completeTreeConfiguration({ dataSource: undefined, renderer: undefined, highlighter: undefined });
this._tree = this._instantiationService.createInstance(
HighlightingWorkbenchTree,
this._treeContainer,
treeConfig,
<IHighlightingTreeOptions>{ useShadows: false, filterOnType: filterConfig.getValue(), showTwistie: false, twistiePixels: 12 },
{ placeholder: localize('placeholder', "Find") }
);
this._disposables.push(this._tree.onDidChangeSelection(e => {
if (e.payload !== this._tree) {
const target = this._getTargetFromEvent(e.selection[0], e.payload);
if (target) {
setTimeout(_ => {// need to debounce here because this disposes the tree and the tree doesn't like to be disposed on click
this._onDidPickElement.fire({ target, payload: e.payload });
}, 0);
}
}
}));
this._disposables.push(this._tree.onDidChangeFocus(e => {
const target = this._getTargetFromEvent(e.focus, e.payload);
if (target) {
this._onDidFocusElement.fire({ target, payload: e.payload });
}
}));
this._disposables.push(this._tree.onDidStartFiltering(() => {
this._layoutInfo.inputHeight = 36;
this._layout();
}));
this._disposables.push(this._tree.onDidExpandItem(() => {
this._layout();
}));
this._disposables.push(this._tree.onDidCollapseItem(() => {
this._layout();
}));
// tree icon theme specials
dom.addClass(this._treeContainer, 'file-icon-themable-tree');
dom.addClass(this._treeContainer, 'show-file-icons');
const onFileIconThemeChange = (fileIconTheme: IFileIconTheme) => {
dom.toggleClass(this._treeContainer, 'align-icons-and-twisties', fileIconTheme.hasFileIcons && !fileIconTheme.hasFolderIcons);
dom.toggleClass(this._treeContainer, 'hide-arrows', fileIconTheme.hidesExplorerArrows === true);
};
this._disposables.push(_themeService.onDidFileIconThemeChange(onFileIconThemeChange));
onFileIconThemeChange(_themeService.getFileIconTheme());
this._domNode.focus();
}
dispose(): void {
dispose(this._disposables);
this._onDidPickElement.dispose();
this._tree.dispose();
this._focus.dispose();
this._symbolSortOrder.dispose();
}
setInput(input: any, maxHeight: number, width: number, arrowSize: number, arrowOffset: number): void {
let actualInput = this._getInput(input);
this._tree.setInput(actualInput).then(() => {
show(input: any, maxHeight: number, width: number, arrowSize: number, arrowOffset: number): void {
this._layoutInfo = { maxHeight, width, arrowSize, arrowOffset, inputHeight: 0 };
this._layout();
const theme = this._themeService.getTheme();
const color = theme.getColor(breadcrumbsPickerBackground);
// use proper selection, reveal
let selection = this._getInitialSelection(this._tree, input);
if (selection) {
return this._tree.reveal(selection, 0.5).then(() => {
this._tree.setSelection([selection], this._tree);
this._tree.setFocus(selection);
this._tree.domFocus();
});
} else {
this._tree.focusFirst();
this._tree.setSelection([this._tree.getFocus()], this._tree);
this._tree.domFocus();
return Promise.resolve(null);
this._arrow = document.createElement('div');
this._arrow.className = 'arrow';
this._arrow.style.borderColor = `transparent transparent ${color ? color.toString() : ''}`;
this._domNode.appendChild(this._arrow);
this._treeContainer = document.createElement('div');
this._treeContainer.style.background = color ? color.toString() : '';
this._treeContainer.style.paddingTop = '2px';
this._treeContainer.style.boxShadow = `0px 5px 8px ${this._themeService.getTheme().getColor(widgetShadow)}`;
this._domNode.appendChild(this._treeContainer);
const filterConfig = BreadcrumbsConfig.FilterOnType.bindTo(this._configurationService);
this._disposables.push(filterConfig);
this._layoutInfo = { maxHeight, width, arrowSize, arrowOffset, inputHeight: 0 };
this._tree = this._createTree(this._treeContainer);
this._disposables.push(this._tree.onDidChangeSelection(e => {
if (e.browserEvent !== this._fakeEvent) {
const target = this._getTargetFromEvent(e.elements[0], e.browserEvent);
if (target) {
setTimeout(_ => {// need to debounce here because this disposes the tree and the tree doesn't like to be disposed on click
this._onDidPickElement.fire({ target, payload: undefined });
}, 0);
}
}
}, onUnexpectedError);
}));
this._disposables.push(this._tree.onDidChangeFocus(e => {
const target = this._getTargetFromEvent(e.elements[0], e.browserEvent);
if (target) {
this._onDidFocusElement.fire({ target, payload: undefined });
}
}));
this._disposables.push(this._tree.onDidChangeContentHeight(() => {
this._layout();
}));
// filter on type: state
const cfgFilterOnType = BreadcrumbsConfig.FilterOnType.bindTo(this._configurationService);
this._tree.updateOptions({ filterOnType: cfgFilterOnType.getValue() });
this._disposables.push(this._tree.onDidUpdateOptions(e => {
this._configurationService.updateValue(cfgFilterOnType.name, e.filterOnType, ConfigurationTarget.MEMORY);
}));
this._domNode.focus();
this._setInput(input).then(() => {
this._layout();
}).catch(onUnexpectedError);
}
private _layout(info: ILayoutInfo = this._layoutInfo): void {
protected _layout(): void {
let count = 0;
let nav = this._tree.getNavigator(undefined, false);
while (nav.next() && count < 13) { count += 1; }
let headerHeight = 2 * info.arrowSize;
let treeHeight = Math.min(info.maxHeight - headerHeight, count * 22);
let totalHeight = treeHeight + headerHeight;
const headerHeight = 2 * this._layoutInfo.arrowSize;
const treeHeight = Math.min(this._layoutInfo.maxHeight - headerHeight, this._tree.contentHeight);
const totalHeight = treeHeight + headerHeight;
this._domNode.style.height = `${totalHeight}px`;
this._domNode.style.width = `${info.width}px`;
this._arrow.style.top = `-${2 * info.arrowSize}px`;
this._arrow.style.borderWidth = `${info.arrowSize}px`;
this._arrow.style.marginLeft = `${info.arrowOffset}px`;
this._domNode.style.width = `${this._layoutInfo.width}px`;
this._arrow.style.top = `-${2 * this._layoutInfo.arrowSize}px`;
this._arrow.style.borderWidth = `${this._layoutInfo.arrowSize}px`;
this._arrow.style.marginLeft = `${this._layoutInfo.arrowOffset}px`;
this._treeContainer.style.height = `${treeHeight}px`;
this._treeContainer.style.width = `${info.width}px`;
this._tree.layout();
this._layoutInfo = info;
this._treeContainer.style.width = `${this._layoutInfo.width}px`;
this._tree.layout(treeHeight, this._layoutInfo.width);
}
protected abstract _getInput(input: BreadcrumbElement): any;
protected abstract _getInitialSelection(tree: ITree, input: BreadcrumbElement): any;
protected abstract _completeTreeConfiguration(config: IHighlightingTreeConfiguration): IHighlightingTreeConfiguration;
protected abstract _getTargetFromEvent(element: any, payload: any): any | undefined;
protected abstract _setInput(element: BreadcrumbElement): Promise<void>;
protected abstract _createTree(container: HTMLElement): Tree<any, any>;
protected abstract _getTargetFromEvent(element: any, payload: UIEvent | undefined): any | undefined;
}
//#region - Files
export class FileDataSource implements IDataSource {
class FileVirtualDelegate implements IListVirtualDelegate<IFileStat | IWorkspaceFolder> {
getHeight(_element: IFileStat | IWorkspaceFolder) {
return 22;
}
getTemplateId(_element: IFileStat | IWorkspaceFolder): string {
return 'FileStat';
}
}
private readonly _parents = new WeakMap<object, IWorkspaceFolder | IFileStat>();
constructor(
@IFileService private readonly _fileService: IFileService,
) { }
getId(tree: ITree, element: IWorkspace | IWorkspaceFolder | IFileStat | URI): string {
class FileIdentityProvider implements IIdentityProvider<IWorkspace | IWorkspaceFolder | IFileStat | URI> {
getId(element: IWorkspace | IWorkspaceFolder | IFileStat | URI): { toString(): string; } {
if (URI.isUri(element)) {
return element.toString();
} else if (IWorkspace.isIWorkspace(element)) {
@@ -222,12 +183,26 @@ export class FileDataSource implements IDataSource {
return element.resource.toString();
}
}
}
hasChildren(tree: ITree, element: IWorkspace | IWorkspaceFolder | IFileStat | URI): boolean {
return URI.isUri(element) || IWorkspace.isIWorkspace(element) || IWorkspaceFolder.isIWorkspaceFolder(element) || element.isDirectory;
class FileDataSource implements IAsyncDataSource<IWorkspace | URI, IWorkspaceFolder | IFileStat> {
private readonly _parents = new WeakMap<object, IWorkspaceFolder | IFileStat>();
constructor(
@IFileService private readonly _fileService: IFileService,
) { }
hasChildren(element: IWorkspace | URI | IWorkspaceFolder | IFileStat): boolean {
return URI.isUri(element)
|| IWorkspace.isIWorkspace(element)
|| IWorkspaceFolder.isIWorkspaceFolder(element)
|| element.isDirectory;
}
getChildren(tree: ITree, element: IWorkspace | IWorkspaceFolder | IFileStat | URI): Promise<IWorkspaceFolder[] | IFileStat[]> {
getChildren(element: IWorkspace | URI | IWorkspaceFolder | IFileStat): Promise<(IWorkspaceFolder | IFileStat)[]> {
if (IWorkspace.isIWorkspace(element)) {
return Promise.resolve(element.folders).then(folders => {
for (let child of folders) {
@@ -245,19 +220,62 @@ export class FileDataSource implements IDataSource {
uri = element.resource;
}
return this._fileService.resolveFile(uri).then(stat => {
for (let child of stat.children) {
for (const child of stat.children || []) {
this._parents.set(stat, child);
}
return stat.children;
return stat.children || [];
});
}
getParent(tree: ITree, element: IWorkspace | URI | IWorkspaceFolder | IFileStat): Promise<IWorkspaceFolder | IFileStat> {
return Promise.resolve(this._parents.get(element));
}
}
export class FileFilter implements IFilter {
class FileRenderer implements ITreeRenderer<IFileStat | IWorkspaceFolder, FuzzyScore, IResourceLabel> {
readonly templateId: string = 'FileStat';
constructor(
private readonly _labels: ResourceLabels,
@IConfigurationService private readonly _configService: IConfigurationService,
) { }
renderTemplate(container: HTMLElement): IResourceLabel {
return this._labels.create(container, { supportHighlights: true });
}
renderElement(node: ITreeNode<IWorkspaceFolder | IFileStat, [number, number, number]>, index: number, templateData: IResourceLabel): void {
const fileDecorations = this._configService.getValue<{ colors: boolean, badges: boolean }>('explorer.decorations');
const { element } = node;
let resource: URI;
let fileKind: FileKind;
if (IWorkspaceFolder.isIWorkspaceFolder(element)) {
resource = element.uri;
fileKind = FileKind.ROOT_FOLDER;
} else {
resource = element.resource;
fileKind = element.isDirectory ? FileKind.FOLDER : FileKind.FILE;
}
templateData.setFile(resource, {
fileKind,
hidePath: true,
fileDecorations: fileDecorations,
matches: createMatches(node.filterData),
extraClasses: ['picker-item']
});
}
disposeTemplate(templateData: IResourceLabel): void {
templateData.dispose();
}
}
class FileNavigationLabelProvider implements IKeyboardNavigationLabelProvider<IWorkspaceFolder | IFileStat> {
getKeyboardNavigationLabel(element: IWorkspaceFolder | IFileStat): { toString(): string; } {
return element.name;
}
}
class FileFilter implements ITreeFilter<IWorkspaceFolder | IFileStat> {
private readonly _cachedExpressions = new Map<string, glob.ParsedExpression>();
private readonly _disposables: IDisposable[] = [];
@@ -281,7 +299,7 @@ export class FileFilter implements IFilter {
continue;
}
let patternAbs = pattern.indexOf('**/') !== 0
? join(folder.uri.path, pattern)
? posix.join(folder.uri.path, pattern)
: pattern;
adjustedConfig[patternAbs] = excludesConfig[pattern];
@@ -301,7 +319,7 @@ export class FileFilter implements IFilter {
dispose(this._disposables);
}
isVisible(tree: ITree, element: IWorkspaceFolder | IFileStat): boolean {
filter(element: IWorkspaceFolder | IFileStat, _parentVisibility: TreeVisibility): boolean {
if (IWorkspaceFolder.isIWorkspaceFolder(element)) {
// not a file
return true;
@@ -312,77 +330,24 @@ export class FileFilter implements IFilter {
return true;
}
const expression = this._cachedExpressions.get(folder.uri.toString());
const expression = this._cachedExpressions.get(folder.uri.toString())!;
return !expression(element.resource.path, basename(element.resource));
}
}
export class FileHighlighter implements IHighlighter {
getHighlightsStorageKey(element: IFileStat | IWorkspaceFolder): string {
return IWorkspaceFolder.isIWorkspaceFolder(element) ? element.uri.toString() : element.resource.toString();
}
getHighlights(tree: ITree, element: IFileStat | IWorkspaceFolder, pattern: string): FuzzyScore {
return fuzzyScore(pattern, pattern.toLowerCase(), 0, element.name, element.name.toLowerCase(), 0, true);
}
}
export class FileRenderer implements IRenderer {
constructor(
private readonly _labels: ResourceLabels,
@IConfigurationService private readonly _configService: IConfigurationService,
) { }
getHeight(tree: ITree, element: any): number {
return 22;
}
getTemplateId(tree: ITree, element: any): string {
return 'FileStat';
}
renderTemplate(tree: ITree, templateId: string, container: HTMLElement) {
return this._labels.create(container, { supportHighlights: true });
}
renderElement(tree: ITree, element: IFileStat | IWorkspaceFolder, templateId: string, templateData: IResourceLabel): void {
let fileDecorations = this._configService.getValue<{ colors: boolean, badges: boolean }>('explorer.decorations');
let resource: URI;
let fileKind: FileKind;
if (IWorkspaceFolder.isIWorkspaceFolder(element)) {
resource = element.uri;
fileKind = FileKind.ROOT_FOLDER;
} else {
resource = element.resource;
fileKind = element.isDirectory ? FileKind.FOLDER : FileKind.FILE;
}
templateData.setFile(resource, {
fileKind,
hidePath: true,
fileDecorations: fileDecorations,
matches: createMatches((tree as HighlightingWorkbenchTree).getHighlighterScore(element)),
extraClasses: ['picker-item']
});
}
disposeTemplate(tree: ITree, templateId: string, templateData: IResourceLabel): void {
templateData.dispose();
}
}
export class FileSorter implements ISorter {
compare(tree: ITree, a: IFileStat | IWorkspaceFolder, b: IFileStat | IWorkspaceFolder): number {
export class FileSorter implements ITreeSorter<IFileStat | IWorkspaceFolder> {
compare(a: IFileStat | IWorkspaceFolder, b: IFileStat | IWorkspaceFolder): number {
if (IWorkspaceFolder.isIWorkspaceFolder(a) && IWorkspaceFolder.isIWorkspaceFolder(b)) {
return a.index - b.index;
}
if ((a as IFileStat).isDirectory === (b as IFileStat).isDirectory) {
// same type -> compare on names
return compareFileNames(a.name, b.name);
} else if ((a as IFileStat).isDirectory) {
return -1;
} else {
if ((a as IFileStat).isDirectory === (b as IFileStat).isDirectory) {
// same type -> compare on names
return compareFileNames(a.name, b.name);
} else if ((a as IFileStat).isDirectory) {
return -1;
} else {
return 1;
}
return 1;
}
}
}
@@ -399,44 +364,69 @@ export class BreadcrumbsFilePicker extends BreadcrumbsPicker {
super(parent, instantiationService, themeService, configService);
}
protected _getInput(input: BreadcrumbElement): any {
let { uri, kind } = (input as FileElement);
if (kind === FileKind.ROOT_FOLDER) {
return this._workspaceService.getWorkspace();
} else {
return dirname(uri);
}
}
_createTree(container: HTMLElement) {
protected _getInitialSelection(tree: ITree, input: BreadcrumbElement): any {
let { uri } = (input as FileElement);
let nav = tree.getNavigator();
while (nav.next()) {
let cur = nav.current();
let candidate = IWorkspaceFolder.isIWorkspaceFolder(cur) ? cur.uri : (cur as IFileStat).resource;
if (isEqual(uri, candidate)) {
return cur;
}
}
return undefined;
}
// tree icon theme specials
dom.addClass(this._treeContainer, 'file-icon-themable-tree');
dom.addClass(this._treeContainer, 'show-file-icons');
const onFileIconThemeChange = (fileIconTheme: IFileIconTheme) => {
dom.toggleClass(this._treeContainer, 'align-icons-and-twisties', fileIconTheme.hasFileIcons && !fileIconTheme.hasFolderIcons);
dom.toggleClass(this._treeContainer, 'hide-arrows', fileIconTheme.hidesExplorerArrows === true);
};
this._disposables.push(this._themeService.onDidFileIconThemeChange(onFileIconThemeChange));
onFileIconThemeChange(this._themeService.getFileIconTheme());
protected _completeTreeConfiguration(config: IHighlightingTreeConfiguration): IHighlightingTreeConfiguration {
// todo@joh reuse explorer implementations?
const filter = this._instantiationService.createInstance(FileFilter);
this._disposables.push(filter);
config.dataSource = this._instantiationService.createInstance(FileDataSource);
const labels = this._instantiationService.createInstance(ResourceLabels, DEFAULT_LABELS_CONTAINER /* TODO@Jo visibility propagation */);
this._disposables.push(labels);
config.renderer = this._instantiationService.createInstance(FileRenderer, labels);
config.sorter = new FileSorter();
config.highlighter = new FileHighlighter();
config.filter = filter;
return config;
return this._instantiationService.createInstance(
WorkbenchAsyncDataTree,
container,
new FileVirtualDelegate(),
[this._instantiationService.createInstance(FileRenderer, labels)],
this._instantiationService.createInstance(FileDataSource),
{
filterOnType: true,
multipleSelectionSupport: false,
sorter: new FileSorter(),
filter: this._instantiationService.createInstance(FileFilter),
identityProvider: new FileIdentityProvider(),
keyboardNavigationLabelProvider: new FileNavigationLabelProvider()
}
) as WorkbenchAsyncDataTree<BreadcrumbElement, any, FuzzyScore>;
}
_setInput(element: BreadcrumbElement): Promise<void> {
const { uri, kind } = (element as FileElement);
let input: IWorkspace | URI;
if (kind === FileKind.ROOT_FOLDER) {
input = this._workspaceService.getWorkspace();
} else {
input = dirname(uri);
}
const tree = this._tree as WorkbenchAsyncDataTree<IWorkspace | URI, IWorkspaceFolder | IFileStat, FuzzyScore>;
return tree.setInput(input).then(() => {
let focusElement: IWorkspaceFolder | IFileStat | undefined;
for (const { element } of tree.getNode().children) {
if (IWorkspaceFolder.isIWorkspaceFolder(element) && isEqual(element.uri, uri)) {
focusElement = element;
break;
} else if (isEqual((element as IFileStat).resource, uri)) {
focusElement = element as IFileStat;
break;
}
}
if (focusElement) {
tree.reveal(focusElement, 0.5);
tree.setFocus([focusElement], this._fakeEvent);
}
tree.domFocus();
});
}
protected _getTargetFromEvent(element: any, _payload: any): any | undefined {
// todo@joh
if (element && !IWorkspaceFolder.isIWorkspaceFolder(element) && !(element as IFileStat).isDirectory) {
return new FileElement((element as IFileStat).resource, FileKind.FILE);
}
@@ -446,52 +436,84 @@ export class BreadcrumbsFilePicker extends BreadcrumbsPicker {
//#region - Symbols
class OutlineHighlighter implements IHighlighter {
getHighlights(tree: ITree, element: OutlineElement, pattern: string): FuzzyScore {
OutlineModel.get(element).updateMatches(pattern);
return element.score;
}
}
export class BreadcrumbsOutlinePicker extends BreadcrumbsPicker {
protected _getInput(input: BreadcrumbElement): any {
let element = input as TreeElement;
let model = OutlineModel.get(element);
model.updateMatches('');
return model;
protected readonly _symbolSortOrder: BreadcrumbsConfig<'position' | 'name' | 'type'>;
constructor(
parent: HTMLElement,
@IInstantiationService instantiationService: IInstantiationService,
@IWorkbenchThemeService themeService: IWorkbenchThemeService,
@IConfigurationService configurationService: IConfigurationService,
) {
super(parent, instantiationService, themeService, configurationService);
this._symbolSortOrder = BreadcrumbsConfig.SymbolSortOrder.bindTo(this._configurationService);
}
protected _getInitialSelection(_tree: ITree, input: BreadcrumbElement): any {
return input instanceof OutlineModel ? undefined : input;
protected _createTree(container: HTMLElement) {
return this._instantiationService.createInstance<
HTMLElement,
IListVirtualDelegate<OutlineItem>,
ITreeRenderer<any, FuzzyScore, any>[],
IDataSource<OutlineModel, OutlineItem>,
IDataTreeOptions<OutlineItem, FuzzyScore>,
WorkbenchDataTree<OutlineModel, OutlineItem, FuzzyScore>
>(
WorkbenchDataTree,
container,
new OutlineVirtualDelegate(),
[new OutlineGroupRenderer(), this._instantiationService.createInstance(OutlineElementRenderer)],
new OutlineDataSource(),
{
filterOnType: true,
expandOnlyOnTwistieClick: true,
multipleSelectionSupport: false,
sorter: new OutlineItemComparator(this._getOutlineItemCompareType()),
identityProvider: new OutlineIdentityProvider(),
keyboardNavigationLabelProvider: this._instantiationService.createInstance(OutlineNavigationLabelProvider)
}
) as WorkbenchDataTree<OutlineModel, OutlineItem, FuzzyScore>;
}
protected _completeTreeConfiguration(config: IHighlightingTreeConfiguration): IHighlightingTreeConfiguration {
config.dataSource = this._instantiationService.createInstance(OutlineDataSource);
config.renderer = this._instantiationService.createInstance(OutlineRenderer);
config.sorter = new OutlineItemComparator(this._getOutlineItemComparator());
config.highlighter = new OutlineHighlighter();
return config;
dispose(): void {
this._symbolSortOrder.dispose();
super.dispose();
}
protected _getTargetFromEvent(element: any, payload: any): any | undefined {
if (payload && payload.didClickOnTwistie) {
return;
protected _setInput(input: BreadcrumbElement): Promise<void> {
const element = input as TreeElement;
const model = OutlineModel.get(element)!;
const tree = this._tree as WorkbenchDataTree<OutlineModel, any, FuzzyScore>;
tree.setInput(model);
let focusElement: TreeElement;
if (element === model) {
focusElement = tree.navigate().first();
} else {
focusElement = element;
}
tree.reveal(focusElement, 0.5);
tree.setFocus([focusElement], this._fakeEvent);
tree.domFocus();
return Promise.resolve();
}
protected _getTargetFromEvent(element: any): any | undefined {
if (element instanceof OutlineElement) {
return element;
}
}
private _getOutlineItemComparator(): OutlineItemCompareType {
private _getOutlineItemCompareType(): OutlineSortOrder {
switch (this._symbolSortOrder.getValue()) {
case 'name':
return OutlineItemCompareType.ByName;
return OutlineSortOrder.ByName;
case 'type':
return OutlineItemCompareType.ByKind;
return OutlineSortOrder.ByKind;
case 'position':
default:
return OutlineItemCompareType.ByPosition;
return OutlineSortOrder.ByPosition;
}
}
}

View File

@@ -103,7 +103,7 @@ Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditor(
interface ISerializedUntitledEditorInput {
resource: string;
resourceJSON: object;
modeId: string;
modeId: string | null;
encoding: string;
}
@@ -114,7 +114,7 @@ class UntitledEditorInputFactory implements IEditorInputFactory {
@ITextFileService private readonly textFileService: ITextFileService
) { }
serialize(editorInput: EditorInput): string {
serialize(editorInput: EditorInput): string | null {
if (!this.textFileService.isHotExitEnabled) {
return null; // never restore untitled unless hot exit is enabled
}
@@ -170,7 +170,7 @@ interface ISerializedSideBySideEditorInput {
// Register Side by Side Editor Input Factory
class SideBySideEditorInputFactory implements IEditorInputFactory {
serialize(editorInput: EditorInput): string {
serialize(editorInput: EditorInput): string | null {
const input = <SideBySideEditorInput>editorInput;
if (input.details && input.master) {
@@ -198,7 +198,7 @@ class SideBySideEditorInputFactory implements IEditorInputFactory {
return null;
}
deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): EditorInput {
deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): EditorInput | null {
const deserialized: ISerializedSideBySideEditorInput = JSON.parse(serializedEditorInput);
const registry = Registry.as<IEditorInputFactoryRegistry>(EditorInputExtensions.EditorInputFactories);
@@ -266,7 +266,7 @@ export class QuickOpenActionContributor extends ActionBarContributor {
return actions;
}
private getEntry(context: any): IEditorQuickOpenEntry {
private getEntry(context: any): IEditorQuickOpenEntry | null {
if (!context || !context.element) {
return null;
}

View File

@@ -3,9 +3,9 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { GroupIdentifier, IWorkbenchEditorConfiguration, IWorkbenchEditorPartConfiguration, EditorOptions, TextEditorOptions, IEditorInput, IEditorIdentifier, IEditorCloseEvent, IEditor } from 'vs/workbench/common/editor';
import { GroupIdentifier, IWorkbenchEditorConfiguration, EditorOptions, TextEditorOptions, IEditorInput, IEditorIdentifier, IEditorCloseEvent, IEditor, IEditorPartOptions } from 'vs/workbench/common/editor';
import { EditorGroup } from 'vs/workbench/common/editor/editorGroup';
import { IEditorGroup, GroupDirection, IAddGroupOptions, IMergeGroupOptions, GroupsOrder, IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService';
import { IEditorGroup, GroupDirection, IAddGroupOptions, IMergeGroupOptions, GroupsOrder } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IDisposable } from 'vs/base/common/lifecycle';
import { Dimension } from 'vs/base/browser/dom';
import { Event } from 'vs/base/common/event';
@@ -18,13 +18,13 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic
export const EDITOR_TITLE_HEIGHT = 35;
export interface IEditorPartCreationOptions {
restorePreviousState: boolean;
}
export const DEFAULT_EDITOR_MIN_DIMENSIONS = new Dimension(220, 70);
export const DEFAULT_EDITOR_MAX_DIMENSIONS = new Dimension(Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY);
export interface IEditorPartOptions extends IWorkbenchEditorPartConfiguration {
iconTheme?: string;
}
export const DEFAULT_EDITOR_PART_OPTIONS: IEditorPartOptions = {
showTabs: true,
highlightModifiedTabs: false,
@@ -77,7 +77,7 @@ export interface IEditorOpeningEvent extends IEditorIdentifier {
* to return a promise that resolves to NULL to prevent the opening
* alltogether.
*/
prevent(callback: () => Promise<IEditor>): void;
prevent(callback: () => undefined | Promise<IEditor | undefined>): void;
}
export interface IEditorGroupsAccessor {
@@ -87,7 +87,7 @@ export interface IEditorGroupsAccessor {
readonly partOptions: IEditorPartOptions;
readonly onDidEditorPartOptionsChange: Event<IEditorPartOptionsChangeEvent>;
getGroup(identifier: GroupIdentifier): IEditorGroupView;
getGroup(identifier: GroupIdentifier): IEditorGroupView | undefined;
getGroups(order: GroupsOrder): IEditorGroupView[];
activateGroup(identifier: IEditorGroupView | GroupIdentifier): IEditorGroupView;
@@ -146,15 +146,3 @@ export interface EditorServiceImpl extends IEditorService {
*/
readonly onDidOpenEditorFail: Event<IEditorIdentifier>;
}
/**
* A sub-interface of IEditorGroupsService to hide some workbench-core specific
* methods from clients.
*/
export interface EditorGroupsServiceImpl extends IEditorGroupsService {
/**
* A promise that resolves when groups have been restored.
*/
readonly whenRestored: Promise<void>;
}

View File

@@ -10,7 +10,7 @@ import { IEditorInput, EditorInput, IEditorIdentifier, ConfirmResult, IEditorCom
import { QuickOpenEntryGroup } from 'vs/base/parts/quickopen/browser/quickOpenModel';
import { EditorQuickOpenEntry, EditorQuickOpenEntryGroup, IEditorQuickOpenEntry, QuickOpenAction } from 'vs/workbench/browser/quickopen';
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
import { IPartService } from 'vs/workbench/services/part/common/partService';
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
import { IResourceInput } from 'vs/platform/editor/common/editor';
import { IHistoryService } from 'vs/workbench/services/history/common/history';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
@@ -18,7 +18,7 @@ import { ICommandService } from 'vs/platform/commands/common/commands';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { IWindowsService } from 'vs/platform/windows/common/windows';
import { CLOSE_EDITOR_COMMAND_ID, NAVIGATE_ALL_EDITORS_GROUP_PREFIX, MOVE_ACTIVE_EDITOR_COMMAND_ID, NAVIGATE_IN_ACTIVE_GROUP_PREFIX, ActiveEditorMoveArguments, SPLIT_EDITOR_LEFT, SPLIT_EDITOR_RIGHT, SPLIT_EDITOR_UP, SPLIT_EDITOR_DOWN, splitEditor, LAYOUT_EDITOR_GROUPS_COMMAND_ID, mergeAllGroups } from 'vs/workbench/browser/parts/editor/editorCommands';
import { IEditorGroupsService, IEditorGroup, GroupsArrangement, EditorsOrder, GroupLocation, GroupDirection, preferredSideBySideGroupDirection, IFindGroupScope, GroupOrientation, EditorGroupLayout, GroupsOrder } from 'vs/workbench/services/group/common/editorGroupsService';
import { IEditorGroupsService, IEditorGroup, GroupsArrangement, EditorsOrder, GroupLocation, GroupDirection, preferredSideBySideGroupDirection, IFindGroupScope, GroupOrientation, EditorGroupLayout, GroupsOrder } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
@@ -188,20 +188,22 @@ export class JoinTwoGroupsAction extends Action {
}
run(context?: IEditorIdentifier): Promise<any> {
let sourceGroup: IEditorGroup;
let sourceGroup: IEditorGroup | undefined;
if (context && typeof context.groupId === 'number') {
sourceGroup = this.editorGroupService.getGroup(context.groupId);
} else {
sourceGroup = this.editorGroupService.activeGroup;
}
const targetGroupDirections = [GroupDirection.RIGHT, GroupDirection.DOWN, GroupDirection.LEFT, GroupDirection.UP];
for (const targetGroupDirection of targetGroupDirections) {
const targetGroup = this.editorGroupService.findGroup({ direction: targetGroupDirection }, sourceGroup);
if (targetGroup && sourceGroup !== targetGroup) {
this.editorGroupService.mergeGroup(sourceGroup, targetGroup);
if (sourceGroup) {
const targetGroupDirections = [GroupDirection.RIGHT, GroupDirection.DOWN, GroupDirection.LEFT, GroupDirection.UP];
for (const targetGroupDirection of targetGroupDirections) {
const targetGroup = this.editorGroupService.findGroup({ direction: targetGroupDirection }, sourceGroup);
if (targetGroup && sourceGroup !== targetGroup) {
this.editorGroupService.mergeGroup(sourceGroup, targetGroup);
return Promise.resolve(true);
return Promise.resolve(true);
}
}
}
@@ -428,7 +430,7 @@ export class OpenToSideFromQuickOpenAction extends Action {
if (entry) {
const input = entry.getInput();
if (input instanceof EditorInput) {
return this.editorService.openEditor(input, entry.getOptions(), SIDE_GROUP);
return this.editorService.openEditor(input, entry.getOptions() || undefined, SIDE_GROUP);
}
const resourceInput = input as IResourceInput;
@@ -441,7 +443,7 @@ export class OpenToSideFromQuickOpenAction extends Action {
}
}
export function toEditorQuickOpenEntry(element: any): IEditorQuickOpenEntry {
export function toEditorQuickOpenEntry(element: any): IEditorQuickOpenEntry | null {
// QuickOpenEntryGroup
if (element instanceof QuickOpenEntryGroup) {
@@ -491,13 +493,13 @@ export class CloseOneEditorAction extends Action {
}
run(context?: IEditorCommandsContext): Promise<any> {
let group: IEditorGroup;
let editorIndex: number;
let group: IEditorGroup | undefined;
let editorIndex: number | undefined;
if (context) {
group = this.editorGroupService.getGroup(context.groupId);
if (group) {
editorIndex = context.editorIndex; // only allow editor at index if group is valid
editorIndex = context.editorIndex!; // only allow editor at index if group is valid
}
}
@@ -579,7 +581,7 @@ export class CloseLeftEditorsInGroupAction extends Action {
}
}
function getTarget(editorService: IEditorService, editorGroupService: IEditorGroupsService, context?: IEditorIdentifier): { editor: IEditorInput, group: IEditorGroup } {
function getTarget(editorService: IEditorService, editorGroupService: IEditorGroupsService, context?: IEditorIdentifier): { editor: IEditorInput | null, group: IEditorGroup | undefined } {
if (context) {
return { editor: context.editor, group: editorGroupService.getGroup(context.groupId) };
}
@@ -593,7 +595,7 @@ export abstract class BaseCloseAllAction extends Action {
constructor(
id: string,
label: string,
clazz: string,
clazz: string | undefined,
private textFileService: ITextFileService,
protected editorGroupService: IEditorGroupsService
) {
@@ -629,9 +631,9 @@ export abstract class BaseCloseAllAction extends Action {
let saveOrRevertPromise: Promise<boolean>;
if (confirm === ConfirmResult.DONT_SAVE) {
saveOrRevertPromise = this.textFileService.revertAll(null, { soft: true }).then(() => true);
saveOrRevertPromise = this.textFileService.revertAll(undefined, { soft: true }).then(() => true);
} else {
saveOrRevertPromise = this.textFileService.saveAll(true).then(res => res.results.every(r => r.success));
saveOrRevertPromise = this.textFileService.saveAll(true).then(res => res.results.every(r => !!r.success));
}
return saveOrRevertPromise.then(success => {
@@ -703,8 +705,8 @@ export class CloseEditorsInOtherGroupsAction extends Action {
run(context?: IEditorIdentifier): Promise<any> {
const groupToSkip = context ? this.editorGroupService.getGroup(context.groupId) : this.editorGroupService.activeGroup;
return Promise.all(this.editorGroupService.getGroups(GroupsOrder.MOST_RECENTLY_ACTIVE).map(g => {
if (g.id === groupToSkip.id) {
return Promise.resolve(null);
if (groupToSkip && g.id === groupToSkip.id) {
return Promise.resolve();
}
return g.closeAllEditors();
@@ -732,7 +734,7 @@ export class CloseEditorInAllGroupsAction extends Action {
return Promise.all(this.editorGroupService.getGroups(GroupsOrder.MOST_RECENTLY_ACTIVE).map(g => g.closeEditor(activeEditor)));
}
return Promise.resolve(null);
return Promise.resolve();
}
}
@@ -748,22 +750,24 @@ export class BaseMoveGroupAction extends Action {
}
run(context?: IEditorIdentifier): Promise<any> {
let sourceGroup: IEditorGroup;
let sourceGroup: IEditorGroup | undefined;
if (context && typeof context.groupId === 'number') {
sourceGroup = this.editorGroupService.getGroup(context.groupId);
} else {
sourceGroup = this.editorGroupService.activeGroup;
}
const targetGroup = this.findTargetGroup(sourceGroup);
if (targetGroup) {
this.editorGroupService.moveGroup(sourceGroup, targetGroup, this.direction);
if (sourceGroup) {
const targetGroup = this.findTargetGroup(sourceGroup);
if (targetGroup) {
this.editorGroupService.moveGroup(sourceGroup, targetGroup, this.direction);
}
}
return Promise.resolve(true);
}
private findTargetGroup(sourceGroup: IEditorGroup): IEditorGroup {
private findTargetGroup(sourceGroup: IEditorGroup): IEditorGroup | undefined {
const targetNeighbours: GroupDirection[] = [this.direction];
// Allow the target group to be in alternative locations to support more
@@ -882,14 +886,14 @@ export class ResetGroupSizesAction extends Action {
export class MaximizeGroupAction extends Action {
static readonly ID = 'workbench.action.maximizeEditor';
static readonly LABEL = nls.localize('maximizeEditor', "Maximize Editor Group and Hide Sidebar");
static readonly LABEL = nls.localize('maximizeEditor', "Maximize Editor Group and Hide Side Bar");
constructor(
id: string,
label: string,
@IEditorService private readonly editorService: IEditorService,
@IEditorGroupsService private readonly editorGroupService: IEditorGroupsService,
@IPartService private readonly partService: IPartService
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService
) {
super(id, label);
}
@@ -897,7 +901,7 @@ export class MaximizeGroupAction extends Action {
run(): Promise<any> {
if (this.editorService.activeEditor) {
this.editorGroupService.arrangeGroups(GroupsArrangement.MINIMIZE_OTHERS);
this.partService.setSideBarHidden(true);
this.layoutService.setSideBarHidden(true);
}
return Promise.resolve(false);
@@ -927,10 +931,14 @@ export abstract class BaseNavigateEditorAction extends Action {
}
const group = this.editorGroupService.getGroup(groupId);
return group.openEditor(editor);
if (group) {
return group.openEditor(editor);
}
return Promise.resolve();
}
protected abstract navigate(): IEditorIdentifier;
protected abstract navigate(): IEditorIdentifier | undefined;
}
export class OpenNextEditor extends BaseNavigateEditorAction {
@@ -947,12 +955,12 @@ export class OpenNextEditor extends BaseNavigateEditorAction {
super(id, label, editorGroupService, editorService);
}
protected navigate(): IEditorIdentifier {
protected navigate(): IEditorIdentifier | undefined {
// Navigate in active group if possible
const activeGroup = this.editorGroupService.activeGroup;
const activeGroupEditors = activeGroup.getEditors(EditorsOrder.SEQUENTIAL);
const activeEditorIndex = activeGroupEditors.indexOf(activeGroup.activeEditor);
const activeEditorIndex = activeGroup.activeEditor ? activeGroupEditors.indexOf(activeGroup.activeEditor) : -1;
if (activeEditorIndex + 1 < activeGroupEditors.length) {
return { editor: activeGroupEditors[activeEditorIndex + 1], groupId: activeGroup.id };
}
@@ -982,12 +990,12 @@ export class OpenPreviousEditor extends BaseNavigateEditorAction {
super(id, label, editorGroupService, editorService);
}
protected navigate(): IEditorIdentifier {
protected navigate(): IEditorIdentifier | undefined {
// Navigate in active group if possible
const activeGroup = this.editorGroupService.activeGroup;
const activeGroupEditors = activeGroup.getEditors(EditorsOrder.SEQUENTIAL);
const activeEditorIndex = activeGroupEditors.indexOf(activeGroup.activeEditor);
const activeEditorIndex = activeGroup.activeEditor ? activeGroupEditors.indexOf(activeGroup.activeEditor) : -1;
if (activeEditorIndex > 0) {
return { editor: activeGroupEditors[activeEditorIndex - 1], groupId: activeGroup.id };
}
@@ -1020,7 +1028,7 @@ export class OpenNextEditorInGroup extends BaseNavigateEditorAction {
protected navigate(): IEditorIdentifier {
const group = this.editorGroupService.activeGroup;
const editors = group.getEditors(EditorsOrder.SEQUENTIAL);
const index = editors.indexOf(group.activeEditor);
const index = group.activeEditor ? editors.indexOf(group.activeEditor) : -1;
return { editor: index + 1 < editors.length ? editors[index + 1] : editors[0], groupId: group.id };
}
@@ -1043,7 +1051,7 @@ export class OpenPreviousEditorInGroup extends BaseNavigateEditorAction {
protected navigate(): IEditorIdentifier {
const group = this.editorGroupService.activeGroup;
const editors = group.getEditors(EditorsOrder.SEQUENTIAL);
const index = editors.indexOf(group.activeEditor);
const index = group.activeEditor ? editors.indexOf(group.activeEditor) : -1;
return { editor: index > 0 ? editors[index - 1] : editors[editors.length - 1], groupId: group.id };
}
@@ -1105,7 +1113,7 @@ export class NavigateForwardAction extends Action {
run(): Promise<any> {
this.historyService.forward();
return Promise.resolve(null);
return Promise.resolve();
}
}
@@ -1121,7 +1129,7 @@ export class NavigateBackwardsAction extends Action {
run(): Promise<any> {
this.historyService.back();
return Promise.resolve(null);
return Promise.resolve();
}
}
@@ -1137,7 +1145,7 @@ export class NavigateToLastEditLocationAction extends Action {
run(): Promise<any> {
this.historyService.openLastEditLocation();
return Promise.resolve(null);
return Promise.resolve();
}
}
@@ -1153,7 +1161,7 @@ export class NavigateLastAction extends Action {
run(): Promise<any> {
this.historyService.last();
return Promise.resolve(null);
return Promise.resolve();
}
}
@@ -1296,7 +1304,7 @@ export class OpenPreviousEditorFromHistoryAction extends Action {
run(): Promise<any> {
const keys = this.keybindingService.lookupKeybindings(this.id);
this.quickOpenService.show(null, { quickNavigateConfiguration: { keybindings: keys } });
this.quickOpenService.show(undefined, { quickNavigateConfiguration: { keybindings: keys } });
return Promise.resolve(true);
}
@@ -1314,7 +1322,7 @@ export class OpenNextRecentlyUsedEditorAction extends Action {
run(): Promise<any> {
this.historyService.forward(true);
return Promise.resolve(null);
return Promise.resolve();
}
}
@@ -1330,7 +1338,7 @@ export class OpenPreviousRecentlyUsedEditorAction extends Action {
run(): Promise<any> {
this.historyService.back(true);
return Promise.resolve(null);
return Promise.resolve();
}
}

View File

@@ -8,7 +8,7 @@ import * as types from 'vs/base/common/types';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { TextCompareEditorVisibleContext, EditorInput, IEditorIdentifier, IEditorCommandsContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, CloseDirection, IEditor, IEditorInput } from 'vs/workbench/common/editor';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IEditorService, IVisibleEditor } from 'vs/workbench/services/editor/common/editorService';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { TextDiffEditor } from 'vs/workbench/browser/parts/editor/textDiffEditor';
import { KeyMod, KeyCode, KeyChord } from 'vs/base/common/keyCodes';
@@ -16,8 +16,8 @@ import { URI } from 'vs/base/common/uri';
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
import { IListService } from 'vs/platform/list/browser/listService';
import { List } from 'vs/base/browser/ui/list/listWidget';
import { distinct } from 'vs/base/common/arrays';
import { IEditorGroupsService, IEditorGroup, GroupDirection, GroupLocation, GroupsOrder, preferredSideBySideGroupDirection, EditorGroupLayout } from 'vs/workbench/services/group/common/editorGroupsService';
import { distinct, coalesce } from 'vs/base/common/arrays';
import { IEditorGroupsService, IEditorGroup, GroupDirection, GroupLocation, GroupsOrder, preferredSideBySideGroupDirection, EditorGroupLayout } from 'vs/workbench/services/editor/common/editorGroupsService';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import { CommandsRegistry, ICommandHandler } from 'vs/platform/commands/common/commands';
@@ -52,9 +52,9 @@ export const NAVIGATE_IN_ACTIVE_GROUP_PREFIX = 'edt active ';
export const OPEN_EDITOR_AT_INDEX_COMMAND_ID = 'workbench.action.openEditorAtIndex';
export interface ActiveEditorMoveArguments {
to?: 'first' | 'last' | 'left' | 'right' | 'up' | 'down' | 'center' | 'position' | 'previous' | 'next';
by?: 'tab' | 'group';
value?: number;
to: 'first' | 'last' | 'left' | 'right' | 'up' | 'down' | 'center' | 'position' | 'previous' | 'next';
by: 'tab' | 'group';
value: number;
}
const isActiveEditorMoveArg = function (arg: ActiveEditorMoveArguments): boolean {
@@ -90,7 +90,24 @@ function registerActiveEditorMoveCommand(): void {
{
name: nls.localize('editorCommand.activeEditorMove.arg.name', "Active editor move argument"),
description: nls.localize('editorCommand.activeEditorMove.arg.description', "Argument Properties:\n\t* 'to': String value providing where to move.\n\t* 'by': String value providing the unit for move (by tab or by group).\n\t* 'value': Number value providing how many positions or an absolute position to move."),
constraint: isActiveEditorMoveArg
constraint: isActiveEditorMoveArg,
schema: {
'type': 'object',
'required': ['to'],
'properties': {
'to': {
'type': 'string',
'enum': ['left', 'right']
},
'by': {
'type': 'string',
'enum': ['tab', 'group']
},
'value': {
'type': 'number'
}
},
}
}
]
}
@@ -113,7 +130,7 @@ function moveActiveEditor(args: ActiveEditorMoveArguments = Object.create(null),
}
}
function moveActiveTab(args: ActiveEditorMoveArguments, control: IEditor, accessor: ServicesAccessor): void {
function moveActiveTab(args: ActiveEditorMoveArguments, control: IVisibleEditor, accessor: ServicesAccessor): void {
const group = control.group;
let index = group.getIndexOfEditor(control.input);
switch (args.to) {
@@ -141,12 +158,12 @@ function moveActiveTab(args: ActiveEditorMoveArguments, control: IEditor, access
group.moveEditor(control.input, group, { index });
}
function moveActiveEditorToGroup(args: ActiveEditorMoveArguments, control: IEditor, accessor: ServicesAccessor): void {
function moveActiveEditorToGroup(args: ActiveEditorMoveArguments, control: IVisibleEditor, accessor: ServicesAccessor): void {
const editorGroupService = accessor.get(IEditorGroupsService);
const configurationService = accessor.get(IConfigurationService);
const sourceGroup = control.group;
let targetGroup: IEditorGroup;
let targetGroup: IEditorGroup | undefined;
switch (args.to) {
case 'left':
@@ -340,7 +357,7 @@ function registerOpenEditorAtIndexCommands(): void {
case 9: return KeyCode.KEY_9;
}
return undefined;
throw new Error('invalid index');
}
}
@@ -392,7 +409,7 @@ function registerFocusEditorGroupAtIndexCommands(): void {
case 7: return 'workbench.action.focusEighthEditorGroup';
}
return undefined;
throw new Error('Invalid index');
}
function toKeyCode(index: number): KeyCode {
@@ -406,23 +423,27 @@ function registerFocusEditorGroupAtIndexCommands(): void {
case 7: return KeyCode.KEY_8;
}
return undefined;
throw new Error('Invalid index');
}
}
export function splitEditor(editorGroupService: IEditorGroupsService, direction: GroupDirection, context?: IEditorCommandsContext): void {
let sourceGroup: IEditorGroup;
let sourceGroup: IEditorGroup | undefined;
if (context && typeof context.groupId === 'number') {
sourceGroup = editorGroupService.getGroup(context.groupId);
} else {
sourceGroup = editorGroupService.activeGroup;
}
if (!sourceGroup) {
return;
}
// Add group
const newGroup = editorGroupService.addGroup(sourceGroup, direction);
// Split editor (if it can be split)
let editorToCopy: IEditorInput;
let editorToCopy: IEditorInput | null;
if (context && typeof context.editorIndex === 'number') {
editorToCopy = sourceGroup.getEditor(context.editorIndex);
} else {
@@ -466,9 +487,14 @@ function registerCloseEditorCommands() {
contexts.push({ groupId: activeGroup.id }); // active group as fallback
}
return Promise.all(distinct(contexts.map(c => c.groupId)).map(groupId =>
editorGroupService.getGroup(groupId).closeEditors({ savedOnly: true })
));
return Promise.all(distinct(contexts.map(c => c.groupId)).map(groupId => {
const group = editorGroupService.getGroup(groupId);
if (group) {
return group.closeEditors({ savedOnly: true });
}
return Promise.resolve();
}));
}
});
@@ -486,9 +512,14 @@ function registerCloseEditorCommands() {
distinctGroupIds.push(editorGroupService.activeGroup.id);
}
return Promise.all(distinctGroupIds.map(groupId =>
editorGroupService.getGroup(groupId).closeAllEditors()
));
return Promise.all(distinctGroupIds.map(groupId => {
const group = editorGroupService.getGroup(groupId);
if (group) {
return group.closeAllEditors();
}
return Promise.resolve();
}));
}
});
@@ -511,11 +542,15 @@ function registerCloseEditorCommands() {
return Promise.all(groupIds.map(groupId => {
const group = editorGroupService.getGroup(groupId);
const editors = contexts
.filter(context => context.groupId === groupId)
.map(context => typeof context.editorIndex === 'number' ? group.getEditor(context.editorIndex) : group.activeEditor);
if (group) {
const editors = coalesce(contexts
.filter(context => context.groupId === groupId)
.map(context => typeof context.editorIndex === 'number' ? group.getEditor(context.editorIndex) : group.activeEditor));
return group.closeEditors(editors);
return group.closeEditors(editors);
}
return Promise.resolve();
}));
}
});
@@ -530,14 +565,16 @@ function registerCloseEditorCommands() {
const editorGroupService = accessor.get(IEditorGroupsService);
const commandsContext = getCommandsContext(resourceOrContext, context);
let group: IEditorGroup;
let group: IEditorGroup | undefined;
if (commandsContext && typeof commandsContext.groupId === 'number') {
group = editorGroupService.getGroup(commandsContext.groupId);
} else {
group = editorGroupService.activeGroup;
}
editorGroupService.removeGroup(group);
if (group) {
editorGroupService.removeGroup(group);
}
}
});
@@ -560,12 +597,16 @@ function registerCloseEditorCommands() {
return Promise.all(groupIds.map(groupId => {
const group = editorGroupService.getGroup(groupId);
const editors = contexts
.filter(context => context.groupId === groupId)
.map(context => typeof context.editorIndex === 'number' ? group.getEditor(context.editorIndex) : group.activeEditor);
const editorsToClose = group.editors.filter(e => editors.indexOf(e) === -1);
if (group) {
const editors = contexts
.filter(context => context.groupId === groupId)
.map(context => typeof context.editorIndex === 'number' ? group.getEditor(context.editorIndex) : group.activeEditor);
const editorsToClose = group.editors.filter(e => editors.indexOf(e) === -1);
return group.closeEditors(editorsToClose);
return group.closeEditors(editorsToClose);
}
return Promise.resolve();
}));
}
});
@@ -619,7 +660,10 @@ function registerCloseEditorCommands() {
const commandsContext = getCommandsContext(resourceOrContext, context);
if (commandsContext && typeof commandsContext.groupId === 'number') {
editorGroupService.activateGroup(editorGroupService.getGroup(commandsContext.groupId)); // we need the group to be active
const group = editorGroupService.getGroup(commandsContext.groupId);
if (group) {
editorGroupService.activateGroup(group); // we need the group to be active
}
}
return quickOpenService.show(NAVIGATE_IN_ACTIVE_GROUP_PREFIX);
@@ -642,7 +686,7 @@ function registerCloseEditorCommands() {
});
}
function getCommandsContext(resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext): IEditorCommandsContext {
function getCommandsContext(resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext): IEditorCommandsContext | undefined {
if (URI.isUri(resourceOrContext)) {
return context;
}
@@ -658,11 +702,11 @@ function getCommandsContext(resourceOrContext: URI | IEditorCommandsContext, con
return undefined;
}
function resolveCommandsContext(editorGroupService: IEditorGroupsService, context?: IEditorCommandsContext): { group: IEditorGroup, editor: IEditorInput, control: IEditor } {
function resolveCommandsContext(editorGroupService: IEditorGroupsService, context?: IEditorCommandsContext): { group: IEditorGroup, editor?: IEditorInput, control?: IEditor } {
// Resolve from context
let group = context && typeof context.groupId === 'number' ? editorGroupService.getGroup(context.groupId) : undefined;
let editor = group && typeof context.editorIndex === 'number' ? group.getEditor(context.editorIndex) : undefined;
let editor = group && context && typeof context.editorIndex === 'number' ? types.withNullAsUndefined(group.getEditor(context.editorIndex)) : undefined;
let control = group ? group.activeControl : undefined;
// Fallback to active group as needed
@@ -675,7 +719,7 @@ function resolveCommandsContext(editorGroupService: IEditorGroupsService, contex
return { group, editor, control };
}
export function getMultiSelectedEditorContexts(editorContext: IEditorCommandsContext, listService: IListService, editorGroupService: IEditorGroupsService): IEditorCommandsContext[] {
export function getMultiSelectedEditorContexts(editorContext: IEditorCommandsContext | undefined, listService: IListService, editorGroupService: IEditorGroupsService): IEditorCommandsContext[] {
// First check for a focused list to return the selected items from
const list = listService.lastFocusedList;
@@ -685,7 +729,9 @@ export function getMultiSelectedEditorContexts(editorContext: IEditorCommandsCon
return { groupId: element.id, editorIndex: undefined };
}
return { groupId: element.groupId, editorIndex: editorGroupService.getGroup(element.groupId).getIndexOfEditor(element.editor) };
const group = editorGroupService.getGroup(element.groupId);
return { groupId: element.groupId, editorIndex: group ? group.getIndexOfEditor(element.editor) : -1 };
};
const onlyEditorGroupAndEditor = (e: IEditorIdentifier | IEditorGroup) => isEditorGroup(e) || isEditorIdentifier(e);
@@ -697,7 +743,14 @@ export function getMultiSelectedEditorContexts(editorContext: IEditorCommandsCon
const selection: Array<IEditorIdentifier | IEditorGroup> = list.getSelectedElements().filter(onlyEditorGroupAndEditor);
// Only respect selection if it contains focused element
if (selection && selection.some(s => isEditorGroup(s) ? s.id === focus.groupId : s.groupId === focus.groupId && editorGroupService.getGroup(s.groupId).getIndexOfEditor(s.editor) === focus.editorIndex)) {
if (selection && selection.some(s => {
if (isEditorGroup(s)) {
return s.id === focus.groupId;
}
const group = editorGroupService.getGroup(s.groupId);
return s.groupId === focus.groupId && (group ? group.getIndexOfEditor(s.editor) : -1) === focus.editorIndex;
})) {
return selection.map(elementToContext);
}

View File

@@ -8,12 +8,14 @@ import { EditorInput, EditorOptions } from 'vs/workbench/common/editor';
import { Dimension, show, hide, addClass } from 'vs/base/browser/dom';
import { Registry } from 'vs/platform/registry/common/platform';
import { IEditorRegistry, Extensions as EditorExtensions, IEditorDescriptor } from 'vs/workbench/browser/editor';
import { IPartService } from 'vs/workbench/services/part/common/partService';
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IProgressService, LongRunningOperation } from 'vs/platform/progress/common/progress';
import { IEditorGroupView, DEFAULT_EDITOR_MIN_DIMENSIONS, DEFAULT_EDITOR_MAX_DIMENSIONS } from 'vs/workbench/browser/parts/editor/editor';
import { Event, Emitter } from 'vs/base/common/event';
import { IVisibleEditor } from 'vs/workbench/services/editor/common/editorService';
import { withUndefinedAsNull } from 'vs/base/common/types';
export interface IOpenEditorResult {
readonly control: BaseEditor;
@@ -27,7 +29,7 @@ export class EditorControl extends Disposable {
get maximumWidth() { return this._activeControl ? this._activeControl.maximumWidth : DEFAULT_EDITOR_MAX_DIMENSIONS.width; }
get maximumHeight() { return this._activeControl ? this._activeControl.maximumHeight : DEFAULT_EDITOR_MAX_DIMENSIONS.height; }
private _onDidFocus: Emitter<void> = this._register(new Emitter<void>());
private readonly _onDidFocus: Emitter<void> = this._register(new Emitter<void>());
get onDidFocus(): Event<void> { return this._onDidFocus.event; }
private _onDidSizeConstraintsChange = this._register(new Emitter<{ width: number; height: number; } | undefined>());
@@ -43,7 +45,7 @@ export class EditorControl extends Disposable {
constructor(
private parent: HTMLElement,
private groupView: IEditorGroupView,
@IPartService private readonly partService: IPartService,
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IProgressService progressService: IProgressService
) {
@@ -52,8 +54,8 @@ export class EditorControl extends Disposable {
this.editorOperation = this._register(new LongRunningOperation(progressService));
}
get activeControl() {
return this._activeControl;
get activeControl(): IVisibleEditor | null {
return this._activeControl as IVisibleEditor | null;
}
openEditor(editor: EditorInput, options?: EditorOptions): Promise<IOpenEditorResult> {
@@ -66,7 +68,7 @@ export class EditorControl extends Disposable {
const control = this.doShowEditorControl(descriptor);
// Set input
return this.doSetInput(control, editor, options || null).then((editorChanged => (({ control, editorChanged } as IOpenEditorResult))));
return this.doSetInput(control, editor, withUndefinedAsNull(options)).then((editorChanged => (({ control, editorChanged } as IOpenEditorResult))));
}
private doShowEditorControl(descriptor: IEditorDescriptor): BaseEditor {
@@ -170,7 +172,7 @@ export class EditorControl extends Disposable {
// Show progress while setting input after a certain timeout. If the workbench is opening
// be more relaxed about progress showing by increasing the delay a little bit to reduce flicker.
const operation = this.editorOperation.start(this.partService.isRestored() ? 800 : 3200);
const operation = this.editorOperation.start(this.layoutService.isRestored() ? 800 : 3200);
// Call into editor control
const editorWillChange = !inputMatches;

View File

@@ -12,7 +12,7 @@ import { IThemeService } from 'vs/platform/theme/common/themeService';
import { activeContrastBorder } from 'vs/platform/theme/common/colorRegistry';
import { IEditorIdentifier, EditorInput, EditorOptions } from 'vs/workbench/common/editor';
import { isMacintosh } from 'vs/base/common/platform';
import { GroupDirection, MergeGroupMode } from 'vs/workbench/services/group/common/editorGroupsService';
import { GroupDirection, MergeGroupMode } from 'vs/workbench/services/editor/common/editorGroupsService';
import { toDisposable } from 'vs/base/common/lifecycle';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { RunOnceScheduler } from 'vs/base/common/async';
@@ -28,7 +28,7 @@ class DropOverlay extends Themable {
private container: HTMLElement;
private overlay: HTMLElement;
private currentDropOperation: IDropOperation;
private currentDropOperation?: IDropOperation;
private _disposed: boolean;
private cleanupOverlayScheduler: RunOnceScheduler;
@@ -103,12 +103,12 @@ class DropOverlay extends Themable {
// Update the dropEffect to "copy" if there is no local data to be dragged because
// in that case we can only copy the data into and not move it from its source
if (!isDraggingEditor && !isDraggingGroup) {
if (!isDraggingEditor && !isDraggingGroup && e.dataTransfer) {
e.dataTransfer.dropEffect = 'copy';
}
// Find out if operation is valid
const isCopy = isDraggingGroup ? this.isCopyOperation(e) : isDraggingEditor ? this.isCopyOperation(e, this.editorTransfer.getData(DraggedEditorIdentifier.prototype)[0].identifier) : true;
const isCopy = isDraggingGroup ? this.isCopyOperation(e) : isDraggingEditor ? this.isCopyOperation(e, this.editorTransfer.getData(DraggedEditorIdentifier.prototype)![0].identifier) : true;
if (!isCopy) {
const sourceGroupView = this.findSourceGroupView();
if (sourceGroupView === this.groupView) {
@@ -158,16 +158,16 @@ class DropOverlay extends Themable {
}));
}
private findSourceGroupView(): IEditorGroupView {
private findSourceGroupView(): IEditorGroupView | undefined {
// Check for group transfer
if (this.groupTransfer.hasData(DraggedEditorGroupIdentifier.prototype)) {
return this.accessor.getGroup(this.groupTransfer.getData(DraggedEditorGroupIdentifier.prototype)[0].identifier);
return this.accessor.getGroup(this.groupTransfer.getData(DraggedEditorGroupIdentifier.prototype)![0].identifier);
}
// Check for editor transfer
else if (this.editorTransfer.hasData(DraggedEditorIdentifier.prototype)) {
return this.accessor.getGroup(this.editorTransfer.getData(DraggedEditorIdentifier.prototype)[0].identifier.groupId);
return this.accessor.getGroup(this.editorTransfer.getData(DraggedEditorIdentifier.prototype)![0].identifier.groupId);
}
return undefined;
@@ -189,59 +189,66 @@ class DropOverlay extends Themable {
// Check for group transfer
if (this.groupTransfer.hasData(DraggedEditorGroupIdentifier.prototype)) {
const draggedEditorGroup = this.groupTransfer.getData(DraggedEditorGroupIdentifier.prototype)[0].identifier;
const draggedEditorGroup = this.groupTransfer.getData(DraggedEditorGroupIdentifier.prototype)![0].identifier;
// Return if the drop is a no-op
const sourceGroup = this.accessor.getGroup(draggedEditorGroup);
if (typeof splitDirection !== 'number' && sourceGroup === this.groupView) {
return;
}
if (sourceGroup) {
if (typeof splitDirection !== 'number' && sourceGroup === this.groupView) {
return;
}
// Split to new group
let targetGroup: IEditorGroupView;
if (typeof splitDirection === 'number') {
if (this.isCopyOperation(event)) {
targetGroup = this.accessor.copyGroup(sourceGroup, this.groupView, splitDirection);
} else {
targetGroup = this.accessor.moveGroup(sourceGroup, this.groupView, splitDirection);
// Split to new group
let targetGroup: IEditorGroupView | undefined;
if (typeof splitDirection === 'number') {
if (this.isCopyOperation(event)) {
targetGroup = this.accessor.copyGroup(sourceGroup, this.groupView, splitDirection);
} else {
targetGroup = this.accessor.moveGroup(sourceGroup, this.groupView, splitDirection);
}
}
// Merge into existing group
else {
if (this.isCopyOperation(event)) {
targetGroup = this.accessor.mergeGroup(sourceGroup, this.groupView, { mode: MergeGroupMode.COPY_EDITORS });
} else {
targetGroup = this.accessor.mergeGroup(sourceGroup, this.groupView);
}
}
if (targetGroup) {
this.accessor.activateGroup(targetGroup);
}
}
// Merge into existing group
else {
if (this.isCopyOperation(event)) {
targetGroup = this.accessor.mergeGroup(sourceGroup, this.groupView, { mode: MergeGroupMode.COPY_EDITORS });
} else {
targetGroup = this.accessor.mergeGroup(sourceGroup, this.groupView);
}
}
this.accessor.activateGroup(targetGroup);
this.groupTransfer.clearData(DraggedEditorGroupIdentifier.prototype);
}
// Check for editor transfer
else if (this.editorTransfer.hasData(DraggedEditorIdentifier.prototype)) {
const draggedEditor = this.editorTransfer.getData(DraggedEditorIdentifier.prototype)[0].identifier;
const draggedEditor = this.editorTransfer.getData(DraggedEditorIdentifier.prototype)![0].identifier;
const targetGroup = ensureTargetGroup();
// Return if the drop is a no-op
const sourceGroup = this.accessor.getGroup(draggedEditor.groupId);
if (sourceGroup === targetGroup) {
return;
}
if (sourceGroup) {
if (sourceGroup === targetGroup) {
return;
}
// Open in target group
const options = getActiveTextEditorOptions(sourceGroup, draggedEditor.editor, EditorOptions.create({ pinned: true }));
targetGroup.openEditor(draggedEditor.editor, options);
// Open in target group
const options = getActiveTextEditorOptions(sourceGroup, draggedEditor.editor, EditorOptions.create({ pinned: true }));
targetGroup.openEditor(draggedEditor.editor, options);
// Ensure target has focus
targetGroup.focus();
// Ensure target has focus
targetGroup.focus();
// Close in source group unless we copy
const copyEditor = this.isCopyOperation(event, draggedEditor);
if (!copyEditor) {
sourceGroup.closeEditor(draggedEditor.editor);
// Close in source group unless we copy
const copyEditor = this.isCopyOperation(event, draggedEditor);
if (!copyEditor) {
sourceGroup.closeEditor(draggedEditor.editor);
}
}
this.editorTransfer.clearData(DraggedEditorIdentifier.prototype);
@@ -250,7 +257,7 @@ class DropOverlay extends Themable {
// Check for URI transfer
else {
const dropHandler = this.instantiationService.createInstance(ResourcesDropHandler, { allowWorkspaceOpen: true /* open workspace instead of file if dropped */ });
dropHandler.handleDrop(event, () => ensureTargetGroup(), targetGroup => targetGroup.focus());
dropHandler.handleDrop(event, () => ensureTargetGroup(), targetGroup => targetGroup!.focus());
}
}
@@ -298,7 +305,7 @@ class DropOverlay extends Themable {
// child.style.top = edgeHeightThreshold + 'px';
// No split if mouse is above certain threshold in the center of the view
let splitDirection: GroupDirection;
let splitDirection: GroupDirection | undefined;
if (
mousePosX > edgeWidthThreshold && mousePosX < editorControlWidth - edgeWidthThreshold &&
mousePosY > edgeHeightThreshold && mousePosY < editorControlHeight - edgeHeightThreshold
@@ -429,7 +436,7 @@ class DropOverlay extends Themable {
export class EditorDropTarget extends Themable {
private _overlay: DropOverlay;
private _overlay?: DropOverlay;
private counter = 0;
@@ -447,7 +454,7 @@ export class EditorDropTarget extends Themable {
this.registerListeners();
}
private get overlay(): DropOverlay {
private get overlay(): DropOverlay | undefined {
if (this._overlay && !this._overlay.disposed) {
return this._overlay;
}
@@ -468,7 +475,7 @@ export class EditorDropTarget extends Themable {
if (
!this.editorTransfer.hasData(DraggedEditorIdentifier.prototype) &&
!this.groupTransfer.hasData(DraggedEditorGroupIdentifier.prototype) &&
!event.dataTransfer.types.length // see https://github.com/Microsoft/vscode/issues/25789
event.dataTransfer && !event.dataTransfer.types.length // see https://github.com/Microsoft/vscode/issues/25789
) {
event.dataTransfer.dropEffect = 'none';
return; // unsupported transfer
@@ -510,7 +517,7 @@ export class EditorDropTarget extends Themable {
this.disposeOverlay();
}
private findTargetGroupView(child: HTMLElement): IEditorGroupView {
private findTargetGroupView(child: HTMLElement): IEditorGroupView | undefined {
const groups = this.accessor.groups;
for (const groupView of groups) {
if (isAncestor(child, groupView.element)) {

View File

@@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./media/editorgroupview';
import { EditorGroup, IEditorOpenOptions, EditorCloseEvent, ISerializedEditorGroup, isSerializedEditorGroup } from 'vs/workbench/common/editor/editorGroup';
import { EditorInput, EditorOptions, GroupIdentifier, ConfirmResult, SideBySideEditorInput, CloseDirection, IEditorCloseEvent, EditorGroupActiveEditorDirtyContext, IEditor } from 'vs/workbench/common/editor';
import { Event, Emitter, Relay } from 'vs/base/common/event';
@@ -16,12 +17,11 @@ import { attachProgressBarStyler } from 'vs/platform/theme/common/styler';
import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { editorBackground, contrastBorder } from 'vs/platform/theme/common/colorRegistry';
import { Themable, EDITOR_GROUP_HEADER_TABS_BORDER, EDITOR_GROUP_HEADER_TABS_BACKGROUND, EDITOR_GROUP_HEADER_NO_TABS_BACKGROUND, EDITOR_GROUP_EMPTY_BACKGROUND, EDITOR_GROUP_FOCUSED_EMPTY_BORDER } from 'vs/workbench/common/theme';
import { IMoveEditorOptions, ICopyEditorOptions, ICloseEditorsFilter, IGroupChangeEvent, GroupChangeKind, EditorsOrder, GroupsOrder } from 'vs/workbench/services/group/common/editorGroupsService';
import { IMoveEditorOptions, ICopyEditorOptions, ICloseEditorsFilter, IGroupChangeEvent, GroupChangeKind, EditorsOrder, GroupsOrder, ICloseEditorOptions } from 'vs/workbench/services/editor/common/editorGroupsService';
import { TabsTitleControl } from 'vs/workbench/browser/parts/editor/tabsTitleControl';
import { EditorControl } from 'vs/workbench/browser/parts/editor/editorControl';
import { IProgressService } from 'vs/platform/progress/common/progress';
import { ProgressService } from 'vs/workbench/services/progress/browser/progressService';
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { localize } from 'vs/nls';
import { isPromiseCanceledError } from 'vs/base/common/errors';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
@@ -33,7 +33,6 @@ import { EventType as TouchEventType, GestureEvent } from 'vs/base/browser/touch
import { TitleControl } from 'vs/workbench/browser/parts/editor/titleControl';
import { IEditorGroupsAccessor, IEditorGroupView, IEditorPartOptionsChangeEvent, getActiveTextEditorOptions, IEditorOpeningEvent } from 'vs/workbench/browser/parts/editor/editor';
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
import { join } from 'vs/base/common/paths';
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { ActionRunner, IAction, Action } from 'vs/base/common/actions';
@@ -45,10 +44,14 @@ import { fillInContextMenuActions } from 'vs/platform/actions/browser/menuItemAc
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
// {{SQL CARBON EDIT}}
import { ICommandService } from 'vs/platform/commands/common/commands';
import { GlobalNewUntitledFileAction } from 'vs/workbench/parts/files/electron-browser/fileActions';
import { GlobalNewUntitledFileAction } from 'vs/workbench/contrib/files/browser/fileActions';
// {{SQL CARBON EDIT}} - End
import { isErrorWithActions, IErrorWithActions } from 'vs/base/common/errorsWithActions';
import { URI } from 'vs/base/common/uri';
import { IVisibleEditor } from 'vs/workbench/services/editor/common/editorService';
import { withNullAsUndefined } from 'vs/base/common/types';
import { IHashService } from 'vs/workbench/services/hash/common/hashService';
import { guessMimeTypes } from 'vs/base/common/mime';
import { extname } from 'vs/base/common/path';
export class EditorGroupView extends Themable implements IEditorGroupView {
@@ -70,25 +73,25 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
//#region events
private _onDidFocus: Emitter<void> = this._register(new Emitter<void>());
private readonly _onDidFocus: Emitter<void> = this._register(new Emitter<void>());
get onDidFocus(): Event<void> { return this._onDidFocus.event; }
private _onWillDispose: Emitter<void> = this._register(new Emitter<void>());
private readonly _onWillDispose: Emitter<void> = this._register(new Emitter<void>());
get onWillDispose(): Event<void> { return this._onWillDispose.event; }
private _onDidGroupChange: Emitter<IGroupChangeEvent> = this._register(new Emitter<IGroupChangeEvent>());
private readonly _onDidGroupChange: Emitter<IGroupChangeEvent> = this._register(new Emitter<IGroupChangeEvent>());
get onDidGroupChange(): Event<IGroupChangeEvent> { return this._onDidGroupChange.event; }
private _onWillOpenEditor: Emitter<IEditorOpeningEvent> = this._register(new Emitter<IEditorOpeningEvent>());
private readonly _onWillOpenEditor: Emitter<IEditorOpeningEvent> = this._register(new Emitter<IEditorOpeningEvent>());
get onWillOpenEditor(): Event<IEditorOpeningEvent> { return this._onWillOpenEditor.event; }
private _onDidOpenEditorFail: Emitter<EditorInput> = this._register(new Emitter<EditorInput>());
private readonly _onDidOpenEditorFail: Emitter<EditorInput> = this._register(new Emitter<EditorInput>());
get onDidOpenEditorFail(): Event<EditorInput> { return this._onDidOpenEditorFail.event; }
private _onWillCloseEditor: Emitter<IEditorCloseEvent> = this._register(new Emitter<IEditorCloseEvent>());
private readonly _onWillCloseEditor: Emitter<IEditorCloseEvent> = this._register(new Emitter<IEditorCloseEvent>());
get onWillCloseEditor(): Event<IEditorCloseEvent> { return this._onWillCloseEditor.event; }
private _onDidCloseEditor: Emitter<IEditorCloseEvent> = this._register(new Emitter<IEditorCloseEvent>());
private readonly _onDidCloseEditor: Emitter<IEditorCloseEvent> = this._register(new Emitter<IEditorCloseEvent>());
get onDidCloseEditor(): Event<IEditorCloseEvent> { return this._onDidCloseEditor.event; }
//#endregion
@@ -112,7 +115,6 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
private editorContainer: HTMLElement;
private editorControl: EditorControl;
private ignoreOpenEditorErrors: boolean;
private disposedEditorsWorker: RunOnceWorker<EditorInput>;
private mapEditorToPendingConfirmation: Map<EditorInput, Promise<boolean>> = new Map<EditorInput, Promise<boolean>>();
@@ -130,6 +132,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
@IKeybindingService private readonly keybindingService: IKeybindingService,
@IMenuService private readonly menuService: IMenuService,
@IContextMenuService private readonly contextMenuService: IContextMenuService,
@IHashService private readonly hashService: IHashService,
// {{SQL CARBON EDIT}}
@ICommandService private commandService: ICommandService
) {
@@ -417,6 +420,10 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
}
const activeEditor = this._group.activeEditor;
if (!activeEditor) {
return Promise.resolve();
}
options.pinned = this._group.isPinned(activeEditor); // preserve pinned state
options.preserveFocus = true; // handle focus after editor is opened
@@ -458,14 +465,18 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
}
private onDidEditorOpen(editor: EditorInput): void {
/* __GDPR__
"editorOpened" : {
"${include}": [
"${EditorTelemetryDescriptor}"
]
}
*/
this.telemetryService.publicLog('editorOpened', editor.getTelemetryDescriptor());
// Telemetry
this.toEditorTelemetryDescriptor(editor).then(descriptor => {
/* __GDPR__
"editorOpened" : {
"${include}": [
"${EditorTelemetryDescriptor}"
]
}
*/
this.telemetryService.publicLog('editorOpened', descriptor);
});
// Update container
this.updateContainer();
@@ -496,14 +507,17 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
}
});
/* __GDPR__
"editorClosed" : {
"${include}": [
"${EditorTelemetryDescriptor}"
]
}
*/
this.telemetryService.publicLog('editorClosed', event.editor.getTelemetryDescriptor());
// Telemetry
this.toEditorTelemetryDescriptor(event.editor).then(descriptor => {
/* __GDPR__
"editorClosed" : {
"${include}": [
"${EditorTelemetryDescriptor}"
]
}
*/
this.telemetryService.publicLog('editorClosed', descriptor);
});
// Update container
this.updateContainer();
@@ -513,6 +527,26 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
this._onDidGroupChange.fire({ kind: GroupChangeKind.EDITOR_CLOSE, editor, editorIndex: event.index });
}
private toEditorTelemetryDescriptor(editor: EditorInput): Thenable<object> {
const descriptor = editor.getTelemetryDescriptor();
const resource = editor.getResource();
if (resource && resource.fsPath) {
return this.hashService.createSHA1(resource.fsPath).then(hashedPath => {
descriptor['resource'] = { mimeType: guessMimeTypes(resource.fsPath).join(', '), scheme: resource.scheme, ext: extname(resource.fsPath), path: hashedPath };
/* __GDPR__FRAGMENT__
"EditorTelemetryDescriptor" : {
"resource": { "${inline}": [ "${URIDescriptor}" ] }
}
*/
return descriptor;
});
}
return Promise.resolve(descriptor);
}
private onDidEditorDispose(editor: EditorInput): void {
// To prevent race conditions, we handle disposed editors in our worker with a timeout
@@ -524,7 +558,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
private handleDisposedEditors(editors: EditorInput[]): void {
// Split between visible and hidden editors
let activeEditor: EditorInput;
let activeEditor: EditorInput | undefined;
const inactiveEditors: EditorInput[] = [];
editors.forEach(editor => {
if (this._group.isActive(editor)) {
@@ -567,7 +601,9 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
// Pin preview editor once user disables preview
if (event.oldPartOptions.enablePreview && !event.newPartOptions.enablePreview) {
this.pinEditor(this._group.previewEditor);
if (this._group.previewEditor) {
this.pinEditor(this._group.previewEditor);
}
}
}
@@ -658,15 +694,15 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
return this._group.count;
}
get activeControl(): BaseEditor {
return this.editorControl ? this.editorControl.activeControl : undefined;
get activeControl(): IVisibleEditor | undefined {
return this.editorControl ? withNullAsUndefined(this.editorControl.activeControl) : undefined;
}
get activeEditor(): EditorInput {
get activeEditor(): EditorInput | null {
return this._group.activeEditor;
}
get previewEditor(): EditorInput {
get previewEditor(): EditorInput | null {
return this._group.previewEditor;
}
@@ -686,7 +722,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
return this.editors;
}
getEditor(index: number): EditorInput {
getEditor(index: number): EditorInput | null {
return this._group.getEditor(index);
}
@@ -711,7 +747,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
this._onDidFocus.fire();
}
pinEditor(editor: EditorInput = this.activeEditor): void {
pinEditor(editor: EditorInput | undefined = this.activeEditor || undefined): void {
if (editor && !this._group.isPinned(editor)) {
// Update model
@@ -749,7 +785,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
return this.doOpenEditor(editor, options);
}
private doOpenEditor(editor: EditorInput, options?: EditorOptions): Promise<IEditor> {
private doOpenEditor(editor: EditorInput, options?: EditorOptions): Promise<IEditor | null> {
// Determine options
const openEditorOptions: IEditorOpenOptions = {
@@ -758,7 +794,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
active: this._group.count === 0 || !options || !options.inactive
};
if (!openEditorOptions.active && !openEditorOptions.pinned && this._group.isPreview(this._group.activeEditor)) {
if (!openEditorOptions.active && !openEditorOptions.pinned && this._group.activeEditor && this._group.isPreview(this._group.activeEditor)) {
// Special case: we are to open an editor inactive and not pinned, but the current active
// editor is also not pinned, which means it will get replaced with this one. As such,
// the editor can only be active.
@@ -787,13 +823,13 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
this._group.openEditor(editor, openEditorOptions);
// Show editor
return this.doShowEditor(editor, openEditorOptions.active, options);
return this.doShowEditor(editor, !!openEditorOptions.active, options);
}
private doShowEditor(editor: EditorInput, active: boolean, options?: EditorOptions): Promise<IEditor> {
private doShowEditor(editor: EditorInput, active: boolean, options?: EditorOptions): Promise<IEditor | null> {
// Show in editor control if the active editor changed
let openEditorPromise: Promise<IEditor>;
let openEditorPromise: Promise<IEditor | null>;
if (active) {
openEditorPromise = this.editorControl.openEditor(editor, options).then(result => {
@@ -824,7 +860,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
// Report error only if this was not us restoring previous error state or
// we are told to ignore errors that occur from opening an editor
if (this.isRestored && !isPromiseCanceledError(error) && !this.ignoreOpenEditorErrors) {
if (this.isRestored && !isPromiseCanceledError(error) && (!options || !options.ignoreError)) {
const actions: INotificationActions = { primary: [] };
if (isErrorWithActions(error)) {
actions.primary = (error as IErrorWithActions).actions;
@@ -836,7 +872,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
actions
});
Event.once(handle.onDidClose)(() => dispose(actions.primary));
Event.once(handle.onDidClose)(() => actions.primary && dispose(actions.primary));
}
// Event
@@ -861,10 +897,10 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
// Do not modify original array
editors = editors.slice(0);
let result: IEditor;
let result: IEditor | null;
// Use the first editor as active editor
const { editor, options } = editors.shift();
const { editor, options } = editors.shift()!;
return this.openEditor(editor, options).then(activeEditor => {
result = activeEditor; // this can be NULL if the opening failed
@@ -964,7 +1000,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
//#region closeEditor()
closeEditor(editor: EditorInput = this.activeEditor): Promise<void> {
closeEditor(editor: EditorInput | undefined = this.activeEditor || undefined, options?: ICloseEditorOptions): Promise<void> {
if (!editor) {
return Promise.resolve();
}
@@ -976,11 +1012,11 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
}
// Do close
this.doCloseEditor(editor);
this.doCloseEditor(editor, options && options.preserveFocus ? false : undefined);
});
}
private doCloseEditor(editor: EditorInput, focusNext = this.accessor.activeGroup === this, fromError?: boolean): void {
private doCloseEditor(editor: EditorInput, focusNext = (this.accessor.activeGroup === this), fromError?: boolean): void {
// Closing the active editor of the group is a bit more work
if (this._group.isActive(editor)) {
@@ -996,7 +1032,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
this.titleAreaControl.closeEditor(editor);
}
private doCloseActiveEditor(focusNext = this.accessor.activeGroup === this, fromError?: boolean): void {
private doCloseActiveEditor(focusNext = (this.accessor.activeGroup === this), fromError?: boolean): void {
const editorToClose = this.activeEditor;
const restoreFocus = this.shouldRestoreFocus(this.element);
@@ -1021,32 +1057,34 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
}
// Update model
this._group.closeEditor(editorToClose);
if (editorToClose) {
this._group.closeEditor(editorToClose);
}
// Open next active if there are more to show
const nextActiveEditor = this._group.activeEditor;
if (nextActiveEditor) {
const options = EditorOptions.create({ preserveFocus: !focusNext });
// When closing an editor due to an error we can end up in a loop where we continue closing
// editors that fail to open (e.g. when the file no longer exists). We do not want to show
// repeated errors in this case to the user. As such, if we open the next editor and we are
// in a scope of a previous editor failing, we silence the input errors until the editor is
// opened.
// opened by setting ignoreError: true.
if (fromError) {
this.ignoreOpenEditorErrors = true;
options.ignoreError = true;
}
const options = !focusNext ? EditorOptions.create({ preserveFocus: true }) : undefined;
this.openEditor(nextActiveEditor, options).then(() => {
this.ignoreOpenEditorErrors = false;
});
this.openEditor(nextActiveEditor, options);
}
// Otherwise we are empty, so clear from editor control and send event
else {
// Forward to editor control
this.editorControl.closeEditor(editorToClose);
if (editorToClose) {
this.editorControl.closeEditor(editorToClose);
}
// Restore focus to group container as needed unless group gets closed
if (restoreFocus && !closeEmptyGroup) {
@@ -1085,7 +1123,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
return Promise.resolve(false); // no veto
}
const editor = editors.shift();
const editor = editors.shift()!;
// To prevent multiple confirmation dialogs from showing up one after the other
// we check if a pending confirmation is currently showing and if so, join that
@@ -1157,7 +1195,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
//#region closeEditors()
closeEditors(args: EditorInput[] | ICloseEditorsFilter): Promise<void> {
closeEditors(args: EditorInput[] | ICloseEditorsFilter, options?: ICloseEditorOptions): Promise<void> {
if (this.isEmpty()) {
return Promise.resolve();
}
@@ -1171,7 +1209,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
}
// Do close
this.doCloseEditors(editors);
this.doCloseEditors(editors, options);
});
}
@@ -1205,7 +1243,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
return editorsToClose;
}
private doCloseEditors(editors: EditorInput[]): void {
private doCloseEditors(editors: EditorInput[], options?: ICloseEditorOptions): void {
// Close all inactive editors first
let closeActiveEditor = false;
@@ -1219,7 +1257,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
// Close active editor last if contained in editors list to close
if (closeActiveEditor) {
this.doCloseActiveEditor();
this.doCloseActiveEditor(options && options.preserveFocus ? false : undefined);
}
// Forward to title control
@@ -1278,7 +1316,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
replaceEditors(editors: EditorReplacement[]): Promise<void> {
// Extract active vs. inactive replacements
let activeReplacement: EditorReplacement;
let activeReplacement: EditorReplacement | undefined;
const inactiveReplacements: EditorReplacement[] = [];
editors.forEach(({ editor, replacement, options }) => {
if (editor.isDirty()) {
@@ -1314,11 +1352,11 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
// Open inactive editor
this.doOpenEditor(replacement, options);
// Close replaced inactive edior
this.doCloseInactiveEditor(editor);
// Forward to title control
this.titleAreaControl.closeEditor(editor);
// Close replaced inactive editor unless they match
if (!editor.matches(replacement)) {
this.doCloseInactiveEditor(editor);
this.titleAreaControl.closeEditor(editor);
}
});
// Handle active last
@@ -1327,11 +1365,11 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
// Open replacement as active editor
const openEditorResult = this.doOpenEditor(activeReplacement.replacement, activeReplacement.options);
// Close previous active editor
this.doCloseInactiveEditor(activeReplacement.editor);
// Forward to title control
this.titleAreaControl.closeEditor(activeReplacement.editor);
// Close replaced active editor unless they match
if (!activeReplacement.editor.matches(activeReplacement.replacement)) {
this.doCloseInactiveEditor(activeReplacement.editor);
this.titleAreaControl.closeEditor(activeReplacement.editor);
}
return openEditorResult.then(() => undefined);
}
@@ -1384,8 +1422,8 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
get maximumWidth(): number { return this.editorControl.maximumWidth; }
get maximumHeight(): number { return this.editorControl.maximumHeight; }
private _onDidChange = this._register(new Relay<{ width: number; height: number; }>());
readonly onDidChange: Event<{ width: number; height: number; }> = this._onDidChange.event;
private _onDidChange = this._register(new Relay<{ width: number; height: number; } | undefined>());
readonly onDidChange: Event<{ width: number; height: number; } | undefined> = this._onDidChange.event;
layout(width: number, height: number): void {
this.dimension = new Dimension(width, height);
@@ -1430,7 +1468,7 @@ class EditorOpeningEvent implements IEditorOpeningEvent {
constructor(
private _group: GroupIdentifier,
private _editor: EditorInput,
private _options: EditorOptions
private _options: EditorOptions | undefined
) {
}
@@ -1442,7 +1480,7 @@ class EditorOpeningEvent implements IEditorOpeningEvent {
return this._editor;
}
get options(): EditorOptions {
get options(): EditorOptions | undefined {
return this._options;
}
@@ -1464,10 +1502,10 @@ export interface EditorReplacement {
registerThemingParticipant((theme, collector, environment) => {
// Letterpress
const letterpress = `resources/letterpress${theme.type === 'dark' ? '-dark' : theme.type === 'hc' ? '-hc' : ''}.svg`;
const letterpress = `./media/letterpress${theme.type === 'dark' ? '-dark' : theme.type === 'hc' ? '-hc' : ''}.svg`;
collector.addRule(`
.monaco-workbench .part.editor > .content .editor-group-container.empty .editor-group-letterpress {
background-image: url('${URI.file(join(environment.appRoot, letterpress)).toString()}')
background-image: url('${require.toUrl(letterpress)}')
}
`);

View File

@@ -9,14 +9,14 @@ import { Part } from 'vs/workbench/browser/part';
import { Dimension, isAncestor, toggleClass, addClass, $ } from 'vs/base/browser/dom';
import { Event, Emitter, Relay } from 'vs/base/common/event';
import { contrastBorder, editorBackground } from 'vs/platform/theme/common/colorRegistry';
import { GroupDirection, IAddGroupOptions, GroupsArrangement, GroupOrientation, IMergeGroupOptions, MergeGroupMode, ICopyEditorOptions, GroupsOrder, GroupChangeKind, GroupLocation, IFindGroupScope, EditorGroupLayout, GroupLayoutArgument } from 'vs/workbench/services/group/common/editorGroupsService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { Direction, SerializableGrid, Sizing, ISerializedGrid, Orientation, GridBranchNode, isGridBranchNode, GridNode, createSerializedGrid, Grid, ISerializableView } from 'vs/base/browser/ui/grid/grid';
import { GroupIdentifier, IWorkbenchEditorConfiguration } from 'vs/workbench/common/editor';
import { GroupDirection, IAddGroupOptions, GroupsArrangement, GroupOrientation, IMergeGroupOptions, MergeGroupMode, ICopyEditorOptions, GroupsOrder, GroupChangeKind, GroupLocation, IFindGroupScope, EditorGroupLayout, GroupLayoutArgument, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IInstantiationService, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
import { Direction, SerializableGrid, Sizing, ISerializedGrid, Orientation, GridBranchNode, isGridBranchNode, GridNode, createSerializedGrid, Grid } from 'vs/base/browser/ui/grid/grid';
import { GroupIdentifier, IWorkbenchEditorConfiguration, IEditorPartOptions } from 'vs/workbench/common/editor';
import { values } from 'vs/base/common/map';
import { EDITOR_GROUP_BORDER, EDITOR_PANE_BACKGROUND } from 'vs/workbench/common/theme';
import { distinct } from 'vs/base/common/arrays';
import { IEditorGroupsAccessor, IEditorGroupView, IEditorPartOptions, getEditorPartOptions, impactsEditorPartOptions, IEditorPartOptionsChangeEvent, EditorGroupsServiceImpl } from 'vs/workbench/browser/parts/editor/editor';
import { distinct, coalesce } from 'vs/base/common/arrays';
import { IEditorGroupsAccessor, IEditorGroupView, getEditorPartOptions, impactsEditorPartOptions, IEditorPartOptionsChangeEvent, IEditorPartCreationOptions } from 'vs/workbench/browser/parts/editor/editor';
import { EditorGroupView } from 'vs/workbench/browser/parts/editor/editorGroupView';
import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration';
import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle';
@@ -29,7 +29,8 @@ import { Color } from 'vs/base/common/color';
import { CenteredViewLayout } from 'vs/base/browser/ui/centered/centeredViewLayout';
import { IView, orthogonal, LayoutPriority } from 'vs/base/browser/ui/grid/gridview';
import { onUnexpectedError } from 'vs/base/common/errors';
import { Parts } from 'vs/workbench/services/part/common/partService';
import { Parts, IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
// {{SQL CARBON EDIT}}
import { convertEditorInput } from 'sql/parts/common/customInputConverter';
@@ -50,8 +51,8 @@ class GridWidgetView<T extends IView> implements IView {
get minimumHeight(): number { return this.gridWidget ? this.gridWidget.minimumHeight : 0; }
get maximumHeight(): number { return this.gridWidget ? this.gridWidget.maximumHeight : Number.POSITIVE_INFINITY; }
private _onDidChange = new Relay<{ width: number; height: number; }>();
readonly onDidChange: Event<{ width: number; height: number; }> = this._onDidChange.event;
private _onDidChange = new Relay<{ width: number; height: number; } | undefined>();
readonly onDidChange: Event<{ width: number; height: number; } | undefined> = this._onDidChange.event;
private _gridWidget: Grid<T>;
@@ -83,44 +84,43 @@ class GridWidgetView<T extends IView> implements IView {
}
}
export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditorGroupsAccessor, ISerializableView {
export class EditorPart extends Part implements IEditorGroupsService, IEditorGroupsAccessor {
_serviceBrand: any;
_serviceBrand: ServiceIdentifier<any>;
private static readonly EDITOR_PART_UI_STATE_STORAGE_KEY = 'editorpart.state';
private static readonly EDITOR_PART_CENTERED_VIEW_STORAGE_KEY = 'editorpart.centeredview';
//#region Events
private _onDidLayout: Emitter<Dimension> = this._register(new Emitter<Dimension>());
private readonly _onDidLayout: Emitter<Dimension> = this._register(new Emitter<Dimension>());
get onDidLayout(): Event<Dimension> { return this._onDidLayout.event; }
private _onDidActiveGroupChange: Emitter<IEditorGroupView> = this._register(new Emitter<IEditorGroupView>());
private readonly _onDidActiveGroupChange: Emitter<IEditorGroupView> = this._register(new Emitter<IEditorGroupView>());
get onDidActiveGroupChange(): Event<IEditorGroupView> { return this._onDidActiveGroupChange.event; }
private _onDidAddGroup: Emitter<IEditorGroupView> = this._register(new Emitter<IEditorGroupView>());
private readonly _onDidActivateGroup: Emitter<IEditorGroupView> = this._register(new Emitter<IEditorGroupView>());
get onDidActivateGroup(): Event<IEditorGroupView> { return this._onDidActivateGroup.event; }
private readonly _onDidAddGroup: Emitter<IEditorGroupView> = this._register(new Emitter<IEditorGroupView>());
get onDidAddGroup(): Event<IEditorGroupView> { return this._onDidAddGroup.event; }
private _onDidRemoveGroup: Emitter<IEditorGroupView> = this._register(new Emitter<IEditorGroupView>());
private readonly _onDidRemoveGroup: Emitter<IEditorGroupView> = this._register(new Emitter<IEditorGroupView>());
get onDidRemoveGroup(): Event<IEditorGroupView> { return this._onDidRemoveGroup.event; }
private _onDidMoveGroup: Emitter<IEditorGroupView> = this._register(new Emitter<IEditorGroupView>());
private readonly _onDidMoveGroup: Emitter<IEditorGroupView> = this._register(new Emitter<IEditorGroupView>());
get onDidMoveGroup(): Event<IEditorGroupView> { return this._onDidMoveGroup.event; }
private onDidSetGridWidget = this._register(new Emitter<{ width: number; height: number; }>());
private _onDidSizeConstraintsChange = this._register(new Relay<{ width: number; height: number; }>());
get onDidSizeConstraintsChange(): Event<{ width: number; height: number; }> { return Event.any(this.onDidSetGridWidget.event, this._onDidSizeConstraintsChange.event); }
private onDidSetGridWidget = this._register(new Emitter<{ width: number; height: number; } | undefined>());
private _onDidSizeConstraintsChange = this._register(new Relay<{ width: number; height: number; } | undefined>());
get onDidSizeConstraintsChange(): Event<{ width: number; height: number; } | undefined> { return Event.any(this.onDidSetGridWidget.event, this._onDidSizeConstraintsChange.event); }
private _onDidPreferredSizeChange: Emitter<void> = this._register(new Emitter<void>());
private readonly _onDidPreferredSizeChange: Emitter<void> = this._register(new Emitter<void>());
get onDidPreferredSizeChange(): Event<void> { return this._onDidPreferredSizeChange.event; }
private _onDidActivateGroup: Emitter<IEditorGroupView> = this._register(new Emitter<IEditorGroupView>());
get onDidActivateGroup(): Event<IEditorGroupView> { return this._onDidActivateGroup.event; }
//#endregion
private dimension: Dimension;
private _preferredSize: Dimension;
private _preferredSize: Dimension | undefined;
private workspaceMemento: object;
private globalMemento: object;
@@ -139,22 +139,14 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
private _whenRestored: Promise<void>;
private whenRestoredResolve: () => void;
element: HTMLElement;
private _onDidChange = new Emitter<{ width: number; height: number; }>();
readonly onDidChange = this._onDidChange.event;
priority: LayoutPriority = LayoutPriority.High;
constructor(
id: string,
private restorePreviousState: boolean,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IThemeService themeService: IThemeService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IStorageService storageService: IStorageService
@IStorageService storageService: IStorageService,
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService
) {
super(id, { hasTitle: false }, themeService, storageService);
super(Parts.EDITOR_PART, { hasTitle: false }, themeService, storageService, layoutService);
this.gridWidgetView = new GridWidgetView<IEditorGroupView>();
@@ -172,7 +164,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
private enforcedPartOptions: IEditorPartOptions[] = [];
private _onDidEditorPartOptionsChange: Emitter<IEditorPartOptionsChangeEvent> = this._register(new Emitter<IEditorPartOptionsChangeEvent>());
private readonly _onDidEditorPartOptionsChange: Emitter<IEditorPartOptionsChangeEvent> = this._register(new Emitter<IEditorPartOptionsChangeEvent>());
get onDidEditorPartOptionsChange(): Event<IEditorPartOptionsChangeEvent> { return this._onDidEditorPartOptionsChange.event; }
private registerListeners(): void {
@@ -216,6 +208,9 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
//#region IEditorGroupsService
private _dimension: Dimension;
get dimension(): Dimension { return this._dimension; }
get activeGroup(): IEditorGroupView {
return this._activeGroup;
}
@@ -229,11 +224,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
}
get orientation(): GroupOrientation {
if (!this.gridWidget) {
return undefined; // we have not been created yet
}
return this.gridWidget.orientation === Orientation.VERTICAL ? GroupOrientation.VERTICAL : GroupOrientation.HORIZONTAL;
return (this.gridWidget && this.gridWidget.orientation === Orientation.VERTICAL) ? GroupOrientation.VERTICAL : GroupOrientation.HORIZONTAL;
}
get whenRestored(): Promise<void> {
@@ -246,7 +237,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
return this.groups;
case GroupsOrder.MOST_RECENTLY_ACTIVE:
const mostRecentActive = this.mostRecentActiveGroups.map(groupId => this.getGroup(groupId));
const mostRecentActive = coalesce(this.mostRecentActiveGroups.map(groupId => this.getGroup(groupId)));
// there can be groups that got never active, even though they exist. in this case
// make sure to ust append them at the end so that all groups are returned properly
@@ -270,7 +261,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
}
}
getGroup(identifier: GroupIdentifier): IEditorGroupView {
getGroup(identifier: GroupIdentifier): IEditorGroupView | undefined {
return this.groupViews.get(identifier);
}
@@ -282,7 +273,11 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
}
// by location
return this.doFindGroupByLocation(scope.location, source, wrap);
if (typeof scope.location === 'number') {
return this.doFindGroupByLocation(scope.location, source, wrap);
}
throw new Error('invalid arguments');
}
private doFindGroupByDirection(direction: GroupDirection, source: IEditorGroupView | GroupIdentifier, wrap?: boolean): IEditorGroupView {
@@ -422,7 +417,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
this.doCreateGridControlWithState(gridDescriptor, activeGroup.id, currentGroupViews);
// Layout
this.doLayout(this.dimension);
this.doLayout(this._dimension);
// Update container
this.updateContainer();
@@ -506,7 +501,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
return newGroupView;
}
private doCreateGroupView(from?: IEditorGroupView | ISerializedEditorGroup): IEditorGroupView {
private doCreateGroupView(from?: IEditorGroupView | ISerializedEditorGroup | null): IEditorGroupView {
// Label: just use the number of existing groups as label
const label = this.getGroupLabel(this.count + 1);
@@ -601,7 +596,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
}
}
private toGridViewOrientation(orientation: GroupOrientation, fallback?: Orientation): Orientation {
private toGridViewOrientation(orientation: GroupOrientation, fallback: Orientation): Orientation {
if (typeof orientation === 'number') {
return orientation === GroupOrientation.HORIZONTAL ? Orientation.HORIZONTAL : Orientation.VERTICAL;
}
@@ -742,21 +737,26 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
}
private assertGroupView(group: IEditorGroupView | GroupIdentifier): IEditorGroupView {
let groupView: IEditorGroupView | undefined;
if (typeof group === 'number') {
group = this.getGroup(group);
groupView = this.getGroup(group);
} else {
groupView = group;
}
if (!group) {
if (!groupView) {
throw new Error('Invalid editor group provided!');
}
return group;
return groupView;
}
//#endregion
//#region Part
readonly priority: LayoutPriority = LayoutPriority.High;
get minimumWidth(): number { return this.centeredLayoutWidget.minimumWidth; }
get maximumWidth(): number { return this.centeredLayoutWidget.maximumWidth; }
get minimumHeight(): number { return this.centeredLayoutWidget.minimumHeight; }
@@ -783,7 +783,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
return this.theme.getColor(EDITOR_GROUP_BORDER) || this.theme.getColor(contrastBorder) || Color.transparent;
}
protected updateStyles(): void {
updateStyles(): void {
this.container.style.backgroundColor = this.getColor(editorBackground);
const separatorBorderStyle = { separatorBorder: this.gridSeparatorBorder, background: this.theme.getColor(EDITOR_PANE_BACKGROUND) || Color.transparent };
@@ -791,7 +791,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
this.centeredLayoutWidget.styles(separatorBorderStyle);
}
createContentArea(parent: HTMLElement): HTMLElement {
createContentArea(parent: HTMLElement, options?: IEditorPartCreationOptions): HTMLElement {
// Container
this.element = parent;
@@ -800,7 +800,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
parent.appendChild(this.container);
// Grid control with center layout
this.doCreateGridControl();
this.doCreateGridControl(options);
this.centeredLayoutWidget = this._register(new CenteredViewLayout(this.container, this.gridWidgetView, this.globalMemento[EditorPart.EDITOR_PART_CENTERED_VIEW_STORAGE_KEY]));
@@ -819,15 +819,16 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
return this.centeredLayoutWidget.isActive();
}
private doCreateGridControl(): void {
private doCreateGridControl(options?: IEditorPartCreationOptions): void {
// Grid Widget (with previous UI state)
if (this.restorePreviousState) {
this.doCreateGridControlWithPreviousState();
let restoreError = false;
if (!options || options.restorePreviousState) {
restoreError = !this.doCreateGridControlWithPreviousState();
}
// Grid Widget (no previous UI state or failed to restore)
if (!this.gridWidget) {
if (!this.gridWidget || restoreError) {
const initialGroup = this.doCreateGroupView();
this.doSetGridWidget(new SerializableGrid(initialGroup));
@@ -842,7 +843,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
this.updateContainer();
}
private doCreateGridControlWithPreviousState(): void {
private doCreateGridControlWithPreviousState(): boolean {
const uiState = this.workspaceMemento[EditorPart.EDITOR_PART_UI_STATE_STORAGE_KEY] as IEditorPartUIState;
if (uiState && uiState.serializedGrid) {
try {
@@ -856,25 +857,20 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
// Ensure last active group has focus
this._activeGroup.focus();
} catch (error) {
this.handleGridRestoreError(error, uiState);
// Log error
onUnexpectedError(new Error(`Error restoring editor grid widget: ${error} (with state: ${JSON.stringify(uiState)})`));
// Clear any state we have from the failing restore
this.groupViews.forEach(group => group.dispose());
this.groupViews.clear();
this.mostRecentActiveGroups = [];
return false; // failure
}
}
}
private handleGridRestoreError(error: Error, state: IEditorPartUIState): void {
// Log error
onUnexpectedError(new Error(`Error restoring editor grid widget: ${error} (with state: ${JSON.stringify(state)})`));
// Clear any state we have from the failing restore
if (this.gridWidget) {
this.doSetGridWidget();
}
this.groupViews.forEach(group => group.dispose());
this.groupViews.clear();
this._activeGroup = undefined;
this.mostRecentActiveGroups = [];
return true; // success
}
private doCreateGridControlWithState(serializedGrid: ISerializedGrid, activeGroupId: GroupIdentifier, editorGroupViewsToReuse?: IEditorGroupView[]): void {
@@ -893,7 +889,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
fromJSON: (serializedEditorGroup: ISerializedEditorGroup | null) => {
let groupView: IEditorGroupView;
if (reuseGroupViews.length > 0) {
groupView = reuseGroupViews.shift();
groupView = reuseGroupViews.shift()!;
} else {
groupView = this.doCreateGroupView(serializedEditorGroup);
}
@@ -924,7 +920,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
this.doSetGridWidget(gridWidget);
}
private doSetGridWidget(gridWidget?: SerializableGrid<IEditorGroupView>): void {
private doSetGridWidget(gridWidget: SerializableGrid<IEditorGroupView>): void {
if (this.gridWidget) {
this.gridWidget.dispose();
}
@@ -932,9 +928,7 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
this.gridWidget = gridWidget;
this.gridWidgetView.gridWidget = gridWidget;
if (gridWidget) {
this._onDidSizeConstraintsChange.input = gridWidget.onDidChange;
}
this._onDidSizeConstraintsChange.input = gridWidget.onDidChange;
this.onDidSetGridWidget.fire(undefined);
}
@@ -962,23 +956,20 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
return this.groupViews.size === 1 && this._activeGroup.isEmpty();
}
layout(dimension: Dimension): Dimension[];
layout(width: number, height: number): void;
layout(dim1: Dimension | number, dim2?: number): Dimension[] | void {
const sizes = super.layout(dim1 instanceof Dimension ? dim1 : new Dimension(dim1, dim2));
layout(width: number, height: number): void {
this.doLayout(sizes[1]);
// Layout contents
const contentAreaSize = super.layoutContents(width, height).contentSize;
if (dim1 instanceof Dimension) {
return sizes;
}
// Layout editor container
this.doLayout(contentAreaSize);
}
private doLayout(dimension: Dimension): void {
this.dimension = dimension;
this._dimension = dimension;
// Layout Grid
this.centeredLayoutWidget.layout(this.dimension.width, this.dimension.height);
this.centeredLayoutWidget.layout(this._dimension.width, this._dimension.height);
// Event
this._onDidLayout.fire(dimension);
@@ -1039,3 +1030,5 @@ export class EditorPart extends Part implements EditorGroupsServiceImpl, IEditor
};
}
}
registerSingleton(IEditorGroupsService, EditorPart);

View File

@@ -5,7 +5,6 @@
import 'vs/css!./media/editorpicker';
import * as nls from 'vs/nls';
import { URI } from 'vs/base/common/uri';
import { IIconLabelValueOptions } from 'vs/base/browser/ui/iconLabel/iconLabel';
import { IAutoFocus, Mode, IEntryRunContext, IQuickNavigateConfiguration, IModel } from 'vs/base/parts/quickopen/common/quickOpen';
import { QuickOpenModel, QuickOpenEntry, QuickOpenEntryGroup, QuickOpenItemAccessor } from 'vs/base/parts/quickopen/browser/quickOpenModel';
@@ -14,7 +13,7 @@ import { getIconClasses } from 'vs/editor/common/services/getIconClasses';
import { IModelService } from 'vs/editor/common/services/modelService';
import { QuickOpenHandler } from 'vs/workbench/browser/quickopen';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IEditorGroupsService, IEditorGroup, EditorsOrder, GroupsOrder } from 'vs/workbench/services/group/common/editorGroupsService';
import { IEditorGroupsService, IEditorGroup, EditorsOrder, GroupsOrder } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { EditorInput, toResource } from 'vs/workbench/common/editor';
import { compareItemsByScore, scoreItem, ScorerCache, prepareQuery } from 'vs/base/parts/quickopen/common/quickOpenScorer';
@@ -33,12 +32,12 @@ export class EditorPickerEntry extends QuickOpenEntryGroup {
getLabelOptions(): IIconLabelValueOptions {
return {
extraClasses: getIconClasses(this.modelService, this.modeService, this.getResource()),
extraClasses: getIconClasses(this.modelService, this.modeService, this.getResource() || undefined),
italic: !this._group.isPinned(this.editor)
};
}
getLabel(): string {
getLabel() {
return this.editor.getName();
}
@@ -50,7 +49,7 @@ export class EditorPickerEntry extends QuickOpenEntryGroup {
return this._group;
}
getResource(): URI {
getResource() {
return toResource(this.editor, { supportSideBySide: true });
}
@@ -58,7 +57,7 @@ export class EditorPickerEntry extends QuickOpenEntryGroup {
return nls.localize('entryAriaLabel', "{0}, editor group picker", this.getLabel());
}
getDescription(): string {
getDescription() {
return this.editor.getDescription();
}
@@ -109,7 +108,7 @@ export abstract class BaseEditorPicker extends QuickOpenHandler {
return false;
}
e.setHighlights(itemScore.labelMatch, itemScore.descriptionMatch);
e.setHighlights(itemScore.labelMatch || [], itemScore.descriptionMatch);
return true;
});

View File

@@ -7,16 +7,15 @@ import 'vs/css!./media/editorstatus';
import * as nls from 'vs/nls';
import { $, append, runAtThisOrScheduleAtNextAnimationFrame } from 'vs/base/browser/dom';
import * as strings from 'vs/base/common/strings';
import * as paths from 'vs/base/common/paths';
import { extname, basename } from 'vs/base/common/resources';
import * as types from 'vs/base/common/types';
import { URI as uri } from 'vs/base/common/uri';
import { IStatusbarItem } from 'vs/workbench/browser/parts/statusbar/statusbar';
import { Action } from 'vs/base/common/actions';
import { language, LANGUAGE_DEFAULT, AccessibilitySupport } from 'vs/base/common/platform';
import * as browser from 'vs/base/browser/browser';
import { Language } from 'vs/base/common/platform';
import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput';
import { IFileEditorInput, EncodingMode, IEncodingSupport, toResource, SideBySideEditorInput, IEditor as IBaseEditor, IEditorInput } from 'vs/workbench/common/editor';
import { IDisposable, combinedDisposable, dispose } from 'vs/base/common/lifecycle';
import { IDisposable, combinedDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle';
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
import { IEditorAction } from 'vs/editor/common/editorCommon';
import { EndOfLineSequence, ITextModel } from 'vs/editor/common/model';
@@ -27,7 +26,6 @@ import { BaseBinaryResourceEditor } from 'vs/workbench/browser/parts/editor/bina
import { BinaryResourceDiffEditor } from 'vs/workbench/browser/parts/editor/binaryDiffEditor';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
import { SUPPORTED_ENCODINGS, IFileService, FILES_ASSOCIATIONS_CONFIG } from 'vs/platform/files/common/files';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IModeService, ILanguageSelection } from 'vs/editor/common/services/modeService';
@@ -41,7 +39,7 @@ import { ITextFileService } from 'vs/workbench/services/textfile/common/textfile
import { ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorEvents';
import { IConfigurationChangedEvent, IEditorOptions } from 'vs/editor/common/config/editorOptions';
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { deepClone } from 'vs/base/common/objects';
import { ICodeEditor, isCodeEditor, isDiffEditor, getCodeEditor } from 'vs/editor/browser/editorBrowser';
import { Schemas } from 'vs/base/common/network';
@@ -51,6 +49,10 @@ import { getIconClasses } from 'vs/editor/common/services/getIconClasses';
import { timeout } from 'vs/base/common/async';
import { INotificationHandle, INotificationService, Severity } from 'vs/platform/notification/common/notification';
import { Event } from 'vs/base/common/event';
import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility';
// {{SQL CARBON EDIT}}
import { QueryEditorService } from 'sql/workbench/services/queryEditor/browser/queryEditorService';
class SideBySideEditorEncodingSupport implements IEncodingSupport {
constructor(private master: IEncodingSupport, private details: IEncodingSupport) { }
@@ -64,10 +66,7 @@ class SideBySideEditorEncodingSupport implements IEncodingSupport {
}
}
// {{SQL CARBON EDIT}}
import { QueryEditorService } from 'sql/workbench/services/queryEditor/browser/queryEditorService';
function toEditorWithEncodingSupport(input: IEditorInput): IEncodingSupport {
function toEditorWithEncodingSupport(input: IEditorInput): IEncodingSupport | null {
// Untitled Editor
if (input instanceof UntitledEditorInput) {
@@ -104,25 +103,14 @@ interface IEditorSelectionStatus {
class StateChange {
_stateChangeBrand: void;
indentation: boolean;
selectionStatus: boolean;
mode: boolean;
encoding: boolean;
EOL: boolean;
tabFocusMode: boolean;
screenReaderMode: boolean;
metadata: boolean;
constructor() {
this.indentation = false;
this.selectionStatus = false;
this.mode = false;
this.encoding = false;
this.EOL = false;
this.tabFocusMode = false;
this.screenReaderMode = false;
this.metadata = false;
}
indentation: boolean = false;
selectionStatus: boolean = false;
mode: boolean = false;
encoding: boolean = false;
EOL: boolean = false;
tabFocusMode: boolean = false;
screenReaderMode: boolean = false;
metadata: boolean = false;
combine(other: StateChange) {
this.indentation = this.indentation || other.indentation;
@@ -134,6 +122,17 @@ class StateChange {
this.screenReaderMode = this.screenReaderMode || other.screenReaderMode;
this.metadata = this.metadata || other.metadata;
}
public hasChanges(): boolean {
return this.indentation
|| this.selectionStatus
|| this.mode
|| this.encoding
|| this.EOL
|| this.tabFocusMode
|| this.screenReaderMode
|| this.metadata;
}
}
interface StateDelta {
@@ -144,33 +143,33 @@ interface StateDelta {
indentation?: string;
tabFocusMode?: boolean;
screenReaderMode?: boolean;
metadata?: string;
metadata?: string | null;
}
class State {
private _selectionStatus: string;
get selectionStatus(): string { return this._selectionStatus; }
private _selectionStatus: string | null | undefined;
get selectionStatus(): string | null | undefined { return this._selectionStatus; }
private _mode: string;
get mode(): string { return this._mode; }
private _mode: string | null | undefined;
get mode(): string | null | undefined { return this._mode; }
private _encoding: string;
get encoding(): string { return this._encoding; }
private _encoding: string | null | undefined;
get encoding(): string | null | undefined { return this._encoding; }
private _EOL: string;
get EOL(): string { return this._EOL; }
private _EOL: string | null | undefined;
get EOL(): string | null | undefined { return this._EOL; }
private _indentation: string;
get indentation(): string { return this._indentation; }
private _indentation: string | null | undefined;
get indentation(): string | null | undefined { return this._indentation; }
private _tabFocusMode: boolean;
get tabFocusMode(): boolean { return this._tabFocusMode; }
private _tabFocusMode: boolean | null | undefined;
get tabFocusMode(): boolean | null | undefined { return this._tabFocusMode; }
private _screenReaderMode: boolean;
get screenReaderMode(): boolean { return this._screenReaderMode; }
private _screenReaderMode: boolean | null | undefined;
get screenReaderMode(): boolean | null | undefined { return this._screenReaderMode; }
private _metadata: string;
get metadata(): string { return this._metadata; }
private _metadata: string | null | undefined;
get metadata(): string | null | undefined { return this._metadata; }
constructor() {
this._selectionStatus = null;
@@ -183,70 +182,58 @@ class State {
}
update(update: StateDelta): StateChange {
const e = new StateChange();
let somethingChanged = false;
const change = new StateChange();
if (typeof update.selectionStatus !== 'undefined') {
if ('selectionStatus' in update) {
if (this._selectionStatus !== update.selectionStatus) {
this._selectionStatus = update.selectionStatus;
somethingChanged = true;
e.selectionStatus = true;
change.selectionStatus = true;
}
}
if (typeof update.indentation !== 'undefined') {
if ('indentation' in update) {
if (this._indentation !== update.indentation) {
this._indentation = update.indentation;
somethingChanged = true;
e.indentation = true;
change.indentation = true;
}
}
if (typeof update.mode !== 'undefined') {
if ('mode' in update) {
if (this._mode !== update.mode) {
this._mode = update.mode;
somethingChanged = true;
e.mode = true;
change.mode = true;
}
}
if (typeof update.encoding !== 'undefined') {
if ('encoding' in update) {
if (this._encoding !== update.encoding) {
this._encoding = update.encoding;
somethingChanged = true;
e.encoding = true;
change.encoding = true;
}
}
if (typeof update.EOL !== 'undefined') {
if ('EOL' in update) {
if (this._EOL !== update.EOL) {
this._EOL = update.EOL;
somethingChanged = true;
e.EOL = true;
change.EOL = true;
}
}
if (typeof update.tabFocusMode !== 'undefined') {
if ('tabFocusMode' in update) {
if (this._tabFocusMode !== update.tabFocusMode) {
this._tabFocusMode = update.tabFocusMode;
somethingChanged = true;
e.tabFocusMode = true;
change.tabFocusMode = true;
}
}
if (typeof update.screenReaderMode !== 'undefined') {
if ('screenReaderMode' in update) {
if (this._screenReaderMode !== update.screenReaderMode) {
this._screenReaderMode = update.screenReaderMode;
somethingChanged = true;
e.screenReaderMode = true;
change.screenReaderMode = true;
}
}
if (typeof update.metadata !== 'undefined') {
if ('metadata' in update) {
if (this._metadata !== update.metadata) {
this._metadata = update.metadata;
somethingChanged = true;
e.metadata = true;
change.metadata = true;
}
}
if (somethingChanged) {
return e;
}
return null;
return change;
}
}
@@ -260,34 +247,51 @@ const nlsTabFocusMode = nls.localize('tabFocusModeEnabled', "Tab Moves Focus");
const nlsScreenReaderDetected = nls.localize('screenReaderDetected', "Screen Reader Optimized");
const nlsScreenReaderDetectedTitle = nls.localize('screenReaderDetectedExtra', "If you are not using a Screen Reader, please change the setting `editor.accessibilitySupport` to \"off\".");
function setDisplay(el: HTMLElement, desiredValue: string): void {
if (el.style.display !== desiredValue) {
el.style.display = desiredValue;
class StatusBarItem {
private _showing = true;
constructor(
private readonly element: HTMLElement,
title: string,
) {
this.setVisible(false);
this.element.title = title;
}
public set textContent(value: string) {
this.element.textContent = value;
}
public set onclick(value: () => void) {
this.element.onclick = value;
}
public setVisible(shouldShow: boolean): void {
if (shouldShow !== this._showing) {
this._showing = shouldShow;
this.element.style.display = shouldShow ? '' : 'none';
}
}
}
function show(el: HTMLElement): void {
setDisplay(el, '');
}
function hide(el: HTMLElement): void {
setDisplay(el, 'none');
}
export class EditorStatus implements IStatusbarItem {
private state: State;
private element: HTMLElement;
private tabFocusModeElement: HTMLElement;
private screenRedearModeElement: HTMLElement;
private indentationElement: HTMLElement;
private selectionElement: HTMLElement;
private encodingElement: HTMLElement;
private eolElement: HTMLElement;
private modeElement: HTMLElement;
private metadataElement: HTMLElement;
private tabFocusModeElement: StatusBarItem;
private screenRedearModeElement: StatusBarItem;
private indentationElement: StatusBarItem;
private selectionElement: StatusBarItem;
private encodingElement: StatusBarItem;
private eolElement: StatusBarItem;
private modeElement: StatusBarItem;
private metadataElement: StatusBarItem;
private toDispose: IDisposable[];
private activeEditorListeners: IDisposable[];
private delayedRender: IDisposable;
private toRender: StateChange;
private screenReaderNotification: INotificationHandle;
private delayedRender: IDisposable | null;
private toRender: StateChange | null;
private screenReaderNotification: INotificationHandle | null;
constructor(
@IEditorService private readonly editorService: IEditorService,
@@ -296,8 +300,9 @@ export class EditorStatus implements IStatusbarItem {
@IUntitledEditorService private readonly untitledEditorService: IUntitledEditorService,
@IModeService private readonly modeService: IModeService,
@ITextFileService private readonly textFileService: ITextFileService,
@IWorkspaceConfigurationService private readonly configurationService: IWorkspaceConfigurationService,
@INotificationService private readonly notificationService: INotificationService
@IConfigurationService private readonly configurationService: IConfigurationService,
@INotificationService private readonly notificationService: INotificationService,
@IAccessibilityService private readonly accessibilityService: IAccessibilityService
) {
this.toDispose = [];
this.activeEditorListeners = [];
@@ -307,59 +312,57 @@ export class EditorStatus implements IStatusbarItem {
render(container: HTMLElement): IDisposable {
this.element = append(container, $('.editor-statusbar-item'));
this.tabFocusModeElement = append(this.element, $('a.editor-status-tabfocusmode.status-bar-info'));
this.tabFocusModeElement.title = nls.localize('disableTabMode', "Disable Accessibility Mode");
this.tabFocusModeElement = new StatusBarItem(
append(this.element, $('a.editor-status-tabfocusmode.status-bar-info')),
nls.localize('disableTabMode', "Disable Accessibility Mode"));
this.tabFocusModeElement.onclick = () => this.onTabFocusModeClick();
this.tabFocusModeElement.textContent = nlsTabFocusMode;
hide(this.tabFocusModeElement);
this.screenRedearModeElement = append(this.element, $('a.editor-status-screenreadermode.status-bar-info'));
this.screenRedearModeElement = new StatusBarItem(
append(this.element, $('a.editor-status-screenreadermode.status-bar-info')),
nlsScreenReaderDetectedTitle);
this.screenRedearModeElement.textContent = nlsScreenReaderDetected;
this.screenRedearModeElement.title = nlsScreenReaderDetectedTitle;
this.screenRedearModeElement.onclick = () => this.onScreenReaderModeClick();
hide(this.screenRedearModeElement);
this.selectionElement = append(this.element, $('a.editor-status-selection'));
this.selectionElement.title = nls.localize('gotoLine', "Go to Line");
this.selectionElement = new StatusBarItem(
append(this.element, $('a.editor-status-selection')),
nls.localize('gotoLine', "Go to Line"));
this.selectionElement.onclick = () => this.onSelectionClick();
hide(this.selectionElement);
this.indentationElement = append(this.element, $('a.editor-status-indentation'));
this.indentationElement.title = nls.localize('selectIndentation', "Select Indentation");
this.indentationElement = new StatusBarItem(
append(this.element, $('a.editor-status-indentation')),
nls.localize('selectIndentation', "Select Indentation"));
this.indentationElement.onclick = () => this.onIndentationClick();
hide(this.indentationElement);
this.encodingElement = append(this.element, $('a.editor-status-encoding'));
this.encodingElement.title = nls.localize('selectEncoding', "Select Encoding");
this.encodingElement = new StatusBarItem(
append(this.element, $('a.editor-status-encoding')),
nls.localize('selectEncoding', "Select Encoding"));
this.encodingElement.onclick = () => this.onEncodingClick();
hide(this.encodingElement);
this.eolElement = append(this.element, $('a.editor-status-eol'));
this.eolElement.title = nls.localize('selectEOL', "Select End of Line Sequence");
this.eolElement = new StatusBarItem(
append(this.element, $('a.editor-status-eol')),
nls.localize('selectEOL', "Select End of Line Sequence"));
this.eolElement.onclick = () => this.onEOLClick();
hide(this.eolElement);
this.modeElement = append(this.element, $('a.editor-status-mode'));
this.modeElement.title = nls.localize('selectLanguageMode', "Select Language Mode");
this.modeElement = new StatusBarItem(
append(this.element, $('a.editor-status-mode')),
nls.localize('selectLanguageMode', "Select Language Mode"));
this.modeElement.onclick = () => this.onModeClick();
hide(this.modeElement);
this.metadataElement = append(this.element, $('span.editor-status-metadata'));
this.metadataElement.title = nls.localize('fileInfo', "File Information");
hide(this.metadataElement);
this.metadataElement = new StatusBarItem(
append(this.element, $('span.editor-status-metadata')),
nls.localize('fileInfo', "File Information"));
this.delayedRender = null;
this.toRender = null;
this.toDispose.push(
{
dispose: () => {
if (this.delayedRender) {
this.delayedRender.dispose();
this.delayedRender = null;
}
toDisposable(() => {
if (this.delayedRender) {
this.delayedRender.dispose();
this.delayedRender = null;
}
},
}),
this.editorService.onDidActiveEditorChange(() => this.updateStatusBar()),
this.untitledEditorService.onDidChangeEncoding(r => this.onResourceEncodingChange(r)),
this.textFileService.models.onModelEncodingChanged(e => this.onResourceEncodingChange(e.resource)),
@@ -371,7 +374,7 @@ export class EditorStatus implements IStatusbarItem {
private updateState(update: StateDelta): void {
const changed = this.state.update(update);
if (!changed) {
if (!changed.hasChanges()) {
// Nothing really changed
return;
}
@@ -382,7 +385,9 @@ export class EditorStatus implements IStatusbarItem {
this.delayedRender = null;
const toRender = this.toRender;
this.toRender = null;
this._renderNow(toRender);
if (toRender) {
this._renderNow(toRender);
}
});
} else {
this.toRender.combine(changed);
@@ -391,79 +396,71 @@ export class EditorStatus implements IStatusbarItem {
private _renderNow(changed: StateChange): void {
if (changed.tabFocusMode) {
if (this.state.tabFocusMode && this.state.tabFocusMode === true) {
show(this.tabFocusModeElement);
} else {
hide(this.tabFocusModeElement);
}
this.tabFocusModeElement.setVisible(!!this.state.tabFocusMode);
}
if (changed.screenReaderMode) {
if (this.state.screenReaderMode && this.state.screenReaderMode === true) {
show(this.screenRedearModeElement);
} else {
hide(this.screenRedearModeElement);
}
this.screenRedearModeElement.setVisible(!!this.state.screenReaderMode);
}
if (changed.indentation) {
if (this.state.indentation) {
this.indentationElement.textContent = this.state.indentation;
show(this.indentationElement);
this.indentationElement.setVisible(true);
} else {
hide(this.indentationElement);
this.indentationElement.setVisible(false);
}
}
if (changed.selectionStatus) {
if (this.state.selectionStatus && !this.state.screenReaderMode) {
this.selectionElement.textContent = this.state.selectionStatus;
show(this.selectionElement);
this.selectionElement.setVisible(true);
} else {
hide(this.selectionElement);
this.selectionElement.setVisible(false);
}
}
if (changed.encoding) {
if (this.state.encoding) {
this.encodingElement.textContent = this.state.encoding;
show(this.encodingElement);
this.encodingElement.setVisible(true);
} else {
hide(this.encodingElement);
this.encodingElement.setVisible(false);
}
}
if (changed.EOL) {
if (this.state.EOL) {
this.eolElement.textContent = this.state.EOL === '\r\n' ? nlsEOLCRLF : nlsEOLLF;
show(this.eolElement);
this.eolElement.setVisible(true);
} else {
hide(this.eolElement);
this.eolElement.setVisible(false);
}
}
if (changed.mode) {
if (this.state.mode) {
this.modeElement.textContent = this.state.mode;
show(this.modeElement);
this.modeElement.setVisible(true);
} else {
hide(this.modeElement);
this.modeElement.setVisible(false);
}
}
if (changed.metadata) {
if (this.state.metadata) {
this.metadataElement.textContent = this.state.metadata;
show(this.metadataElement);
this.metadataElement.setVisible(true);
} else {
hide(this.metadataElement);
this.metadataElement.setVisible(false);
}
}
}
private getSelectionLabel(info: IEditorSelectionStatus): string {
private getSelectionLabel(info: IEditorSelectionStatus): string | undefined {
if (!info || !info.selections) {
return null;
return undefined;
}
if (info.selections.length === 1) {
@@ -482,7 +479,7 @@ export class EditorStatus implements IStatusbarItem {
return strings.format(nlsMultiSelection, info.selections.length);
}
return null;
return undefined;
}
private onModeClick(): void {
@@ -502,8 +499,7 @@ export class EditorStatus implements IStatusbarItem {
if (!this.screenReaderNotification) {
this.screenReaderNotification = this.notificationService.prompt(
Severity.Info,
// {{SQL CARBON EDIT}}
nls.localize('screenReaderDetectedExplanation.question', "Are you using a screen reader to operate Azure Data Studio?"),
nls.localize('screenReaderDetectedExplanation.question', "Are you using a screen reader to operate Azure Data Studio? (Certain features like folding, minimap or word wrap are disabled when using a screen reader)"),
[{
label: nls.localize('screenReaderDetectedExplanation.answerYes', "Yes"),
run: () => {
@@ -548,7 +544,7 @@ export class EditorStatus implements IStatusbarItem {
private updateStatusBar(): void {
const activeControl = this.editorService.activeControl;
const activeCodeEditor = activeControl ? getCodeEditor(activeControl.getControl()) : undefined;
const activeCodeEditor = activeControl ? types.withNullAsUndefined(getCodeEditor(activeControl.getControl())) : undefined;
// Update all states
this.onScreenReaderModeChange(activeCodeEditor);
@@ -586,11 +582,13 @@ export class EditorStatus implements IStatusbarItem {
this.activeEditorListeners.push(activeCodeEditor.onDidChangeModelContent((e) => {
this.onEOLChange(activeCodeEditor);
let selections = activeCodeEditor.getSelections();
for (const change of e.changes) {
if (selections.some(selection => Range.areIntersecting(selection, change.range))) {
this.onSelectionChange(activeCodeEditor);
break;
const selections = activeCodeEditor.getSelections();
if (selections) {
for (const change of e.changes) {
if (selections.some(selection => Range.areIntersecting(selection, change.range))) {
this.onSelectionChange(activeCodeEditor);
break;
}
}
}
}));
@@ -630,8 +628,8 @@ export class EditorStatus implements IStatusbarItem {
}
}
private onModeChange(editorWidget: ICodeEditor): void {
let info: StateDelta = { mode: null };
private onModeChange(editorWidget: ICodeEditor | undefined): void {
let info: StateDelta = { mode: undefined };
// We only support text based editors
if (editorWidget) {
@@ -639,15 +637,15 @@ export class EditorStatus implements IStatusbarItem {
if (textModel) {
// Compute mode
const modeId = textModel.getLanguageIdentifier().language;
info = { mode: this.modeService.getLanguageName(modeId) };
info = { mode: this.modeService.getLanguageName(modeId) || undefined };
}
}
this.updateState(info);
}
private onIndentationChange(editorWidget: ICodeEditor): void {
const update: StateDelta = { indentation: null };
private onIndentationChange(editorWidget: ICodeEditor | undefined): void {
const update: StateDelta = { indentation: undefined };
if (editorWidget) {
const model = editorWidget.getModel();
@@ -655,7 +653,7 @@ export class EditorStatus implements IStatusbarItem {
const modelOpts = model.getOptions();
update.indentation = (
modelOpts.insertSpaces
? nls.localize('spacesSize', "Spaces: {0}", modelOpts.tabSize)
? nls.localize('spacesSize', "Spaces: {0}", modelOpts.indentSize)
: nls.localize({ key: 'tabSize', comment: ['Tab corresponds to the tab key'] }, "Tab Size: {0}", modelOpts.tabSize)
);
}
@@ -664,8 +662,8 @@ export class EditorStatus implements IStatusbarItem {
this.updateState(update);
}
private onMetadataChange(editor: IBaseEditor): void {
const update: StateDelta = { metadata: null };
private onMetadataChange(editor: IBaseEditor | undefined): void {
const update: StateDelta = { metadata: undefined };
if (editor instanceof BaseBinaryResourceEditor || editor instanceof BinaryResourceDiffEditor) {
update.metadata = editor.getMetadata();
@@ -676,12 +674,12 @@ export class EditorStatus implements IStatusbarItem {
private _promptedScreenReader: boolean = false;
private onScreenReaderModeChange(editorWidget: ICodeEditor): void {
private onScreenReaderModeChange(editorWidget: ICodeEditor | undefined): void {
let screenReaderMode = false;
// We only support text based editors
if (editorWidget) {
const screenReaderDetected = (browser.getAccessibilitySupport() === AccessibilitySupport.Enabled);
const screenReaderDetected = (this.accessibilityService.getAccessibilitySupport() === AccessibilitySupport.Enabled);
if (screenReaderDetected) {
const screenReaderConfiguration = this.configurationService.getValue<IEditorOptions>('editor').accessibilitySupport;
if (screenReaderConfiguration === 'auto') {
@@ -705,7 +703,7 @@ export class EditorStatus implements IStatusbarItem {
this.updateState({ screenReaderMode: screenReaderMode });
}
private onSelectionChange(editorWidget: ICodeEditor): void {
private onSelectionChange(editorWidget: ICodeEditor | undefined): void {
const info: IEditorSelectionStatus = {};
// We only support text based editors
@@ -719,13 +717,13 @@ export class EditorStatus implements IStatusbarItem {
const textModel = editorWidget.getModel();
if (textModel) {
info.selections.forEach(selection => {
info.charactersSelected += textModel.getValueLengthInRange(selection);
info.charactersSelected! += textModel.getValueLengthInRange(selection);
});
}
// Compute the visible column for one selection. This will properly handle tabs and their configured widths
if (info.selections.length === 1) {
const visibleColumn = editorWidget.getVisibleColumnFromPosition(editorWidget.getPosition());
const visibleColumn = editorWidget.getVisibleColumnFromPosition(editorWidget.getPosition()!);
let selectionClone = info.selections[0].clone(); // do not modify the original position we got from the editor
selectionClone = new Selection(
@@ -742,8 +740,8 @@ export class EditorStatus implements IStatusbarItem {
this.updateState({ selectionStatus: this.getSelectionLabel(info) });
}
private onEOLChange(editorWidget: ICodeEditor): void {
const info: StateDelta = { EOL: null };
private onEOLChange(editorWidget: ICodeEditor | undefined): void {
const info: StateDelta = { EOL: undefined };
if (editorWidget && !editorWidget.getConfiguration().readOnly) {
const codeEditorModel = editorWidget.getModel();
@@ -760,11 +758,11 @@ export class EditorStatus implements IStatusbarItem {
return;
}
const info: StateDelta = { encoding: null };
const info: StateDelta = { encoding: undefined };
// We only support text based editors
if (e && (isCodeEditor(e.getControl()) || isDiffEditor(e.getControl()))) {
const encodingSupport: IEncodingSupport = toEditorWithEncodingSupport(e.input);
const encodingSupport: IEncodingSupport | null = e.input ? toEditorWithEncodingSupport(e.input) : null;
if (encodingSupport) {
const rawEncoding = encodingSupport.getEncoding();
const encodingInfo = SUPPORTED_ENCODINGS[rawEncoding];
@@ -798,11 +796,11 @@ export class EditorStatus implements IStatusbarItem {
private isActiveEditor(control: IBaseEditor): boolean {
const activeControl = this.editorService.activeControl;
return activeControl && activeControl === control;
return !!activeControl && activeControl === control;
}
}
function isWritableCodeEditor(codeEditor: ICodeEditor): boolean {
function isWritableCodeEditor(codeEditor: ICodeEditor | undefined): boolean {
if (!codeEditor) {
return false;
}
@@ -811,7 +809,7 @@ function isWritableCodeEditor(codeEditor: ICodeEditor): boolean {
}
function isWritableBaseEditor(e: IBaseEditor): boolean {
return e && isWritableCodeEditor(getCodeEditor(e.getControl()));
return e && isWritableCodeEditor(getCodeEditor(e.getControl()) || undefined);
}
export class ShowLanguageExtensionsAction extends Action {
@@ -844,7 +842,7 @@ export class ChangeModeAction extends Action {
@IModeService private readonly modeService: IModeService,
@IModelService private readonly modelService: IModelService,
@IEditorService private readonly editorService: IEditorService,
@IWorkspaceConfigurationService private readonly configurationService: IWorkspaceConfigurationService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IQuickInputService private readonly quickInputService: IQuickInputService,
@IPreferencesService private readonly preferencesService: IPreferencesService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@@ -860,19 +858,19 @@ export class ChangeModeAction extends Action {
}
const textModel = activeTextEditorWidget.getModel();
const resource = toResource(this.editorService.activeEditor, { supportSideBySide: true });
const resource = this.editorService.activeEditor ? toResource(this.editorService.activeEditor, { supportSideBySide: true }) : null;
let hasLanguageSupport = !!resource;
if (resource.scheme === Schemas.untitled && !this.untitledEditorService.hasAssociatedFilePath(resource)) {
if (resource && resource.scheme === Schemas.untitled && !this.untitledEditorService.hasAssociatedFilePath(resource)) {
hasLanguageSupport = false; // no configuration for untitled resources (e.g. "Untitled-1")
}
// Compute mode
let currentModeId: string;
let currentModeId: string | undefined;
let modeId: string;
if (textModel) {
modeId = textModel.getLanguageIdentifier().language;
currentModeId = this.modeService.getLanguageName(modeId);
currentModeId = this.modeService.getLanguageName(modeId) || undefined;
}
// All languages are valid picks
@@ -886,7 +884,7 @@ export class ChangeModeAction extends Action {
}
// construct a fake resource to be able to show nice icons if any
let fakeResource: uri;
let fakeResource: uri | undefined;
const extensions = this.modeService.getExtensions(lang);
if (extensions && extensions.length) {
fakeResource = uri.file(extensions[0]);
@@ -912,8 +910,8 @@ export class ChangeModeAction extends Action {
let configureModeAssociations: IQuickPickItem;
let configureModeSettings: IQuickPickItem;
let galleryAction: Action;
if (hasLanguageSupport) {
const ext = paths.extname(resource.fsPath) || paths.basename(resource.fsPath);
if (hasLanguageSupport && resource) {
const ext = extname(resource) || basename(resource);
galleryAction = this.instantiationService.createInstance(ShowLanguageExtensionsAction, ext);
if (galleryAction.enabled) {
@@ -947,7 +945,9 @@ export class ChangeModeAction extends Action {
// User decided to permanently configure associations, return right after
if (pick === configureModeAssociations) {
this.configureFileAssociation(resource);
if (resource) {
this.configureFileAssociation(resource);
}
return;
}
@@ -980,10 +980,15 @@ export class ChangeModeAction extends Action {
}
// Find mode
let languageSelection: ILanguageSelection;
let languageSelection: ILanguageSelection | undefined;
if (pick === autoDetectMode) {
// {{SQL CARBON EDIT}} - use activeEditor.input instead of activeEditor
languageSelection = this.modeService.createByFilepathOrFirstLine(toResource(activeEditor.input, { supportSideBySide: true }).fsPath, textModel.getLineContent(1));
if (textModel) {
// {{SQL CARBON EDIT}} - use activeEditor.input instead of activeEditor
const resource = toResource(activeEditor.input, { supportSideBySide: true });
if (resource) {
languageSelection = this.modeService.createByFilepathOrFirstLine(resource.fsPath, textModel.getLineContent(1));
}
}
} else {
languageSelection = this.modeService.createByLanguageName(pick.label);
}
@@ -991,10 +996,9 @@ export class ChangeModeAction extends Action {
// {{SQL CARBON EDIT}}
// Change mode
models.forEach(textModel => {
let self = this;
QueryEditorService.sqlLanguageModeCheck(textModel, languageSelection, activeEditor).then((newTextModel) => {
if (newTextModel) {
self.modelService.setMode(newTextModel, languageSelection);
this.modelService.setMode(newTextModel, languageSelection);
}
});
});
@@ -1002,9 +1006,9 @@ export class ChangeModeAction extends Action {
}
private configureFileAssociation(resource: uri): void {
const extension = paths.extname(resource.fsPath);
const basename = paths.basename(resource.fsPath);
const currentAssociation = this.modeService.getModeIdByFilepathOrFirstLine(basename);
const extension = extname(resource);
const base = basename(resource);
const currentAssociation = this.modeService.getModeIdByFilepathOrFirstLine(base);
const languages = this.modeService.getRegisteredLanguageNames();
const picks: IQuickPickItem[] = languages.sort().map((lang, index) => {
@@ -1018,15 +1022,15 @@ export class ChangeModeAction extends Action {
});
setTimeout(() => {
this.quickInputService.pick(picks, { placeHolder: nls.localize('pickLanguageToConfigure', "Select Language Mode to Associate with '{0}'", extension || basename) }).then(language => {
this.quickInputService.pick(picks, { placeHolder: nls.localize('pickLanguageToConfigure', "Select Language Mode to Associate with '{0}'", extension || base) }).then(language => {
if (language) {
const fileAssociationsConfig = this.configurationService.inspect(FILES_ASSOCIATIONS_CONFIG);
let associationKey: string;
if (extension && basename[0] !== '.') {
if (extension && base[0] !== '.') {
associationKey = `*${extension}`; // only use "*.ext" if the file path is in the form of <name>.<ext>
} else {
associationKey = basename; // otherwise use the basename (e.g. .gitignore, Dockerfile)
associationKey = base; // otherwise use the basename (e.g. .gitignore, Dockerfile)
}
// If the association is already being made in the workspace, make sure to target workspace settings
@@ -1036,11 +1040,7 @@ export class ChangeModeAction extends Action {
}
// Make sure to write into the value of the target and not the merged value from USER and WORKSPACE config
let currentAssociations = deepClone((target === ConfigurationTarget.WORKSPACE) ? fileAssociationsConfig.workspace : fileAssociationsConfig.user);
if (!currentAssociations) {
currentAssociations = Object.create(null);
}
const currentAssociations = deepClone((target === ConfigurationTarget.WORKSPACE) ? fileAssociationsConfig.workspace : fileAssociationsConfig.user) || Object.create(null);
currentAssociations[associationKey] = language.id;
this.configurationService.updateValue(FILES_ASSOCIATIONS_CONFIG, currentAssociations, target);
@@ -1089,7 +1089,7 @@ class ChangeIndentationAction extends Action {
return {
id: a.id,
label: a.label,
detail: (language === LANGUAGE_DEFAULT) ? null : a.alias,
detail: Language.isDefaultVariant() ? undefined : a.alias,
run: () => {
activeTextEditorWidget.focus();
a.run();
@@ -1140,7 +1140,7 @@ export class ChangeEOLAction extends Action {
return this.quickInputService.pick(EOLOptions, { placeHolder: nls.localize('pickEndOfLine', "Select End of Line Sequence"), activeItem: EOLOptions[selectedIndex] }).then(eol => {
if (eol) {
const activeCodeEditor = getCodeEditor(this.editorService.activeTextEditorWidget);
if (activeCodeEditor && isWritableCodeEditor(activeCodeEditor)) {
if (activeCodeEditor && activeCodeEditor.hasModel() && isWritableCodeEditor(activeCodeEditor)) {
const textModel = activeCodeEditor.getModel();
textModel.pushEOL(eol.eol);
}
@@ -1170,15 +1170,18 @@ export class ChangeEncodingAction extends Action {
return this.quickInputService.pick([{ label: nls.localize('noEditor', "No text editor active at this time") }]);
}
let activeControl = this.editorService.activeControl;
let encodingSupport: IEncodingSupport = toEditorWithEncodingSupport(activeControl.input);
const activeControl = this.editorService.activeControl;
if (!activeControl) {
return this.quickInputService.pick([{ label: nls.localize('noEditor', "No text editor active at this time") }]);
}
const encodingSupport: IEncodingSupport | null = toEditorWithEncodingSupport(activeControl.input);
if (!encodingSupport) {
return this.quickInputService.pick([{ label: nls.localize('noFileEditor', "No file active at this time") }]);
}
let saveWithEncodingPick: IQuickPickItem;
let reopenWithEncodingPick: IQuickPickItem;
if (language === LANGUAGE_DEFAULT) {
if (Language.isDefaultVariant()) {
saveWithEncodingPick = { label: nls.localize('saveWithEncoding', "Save with Encoding") };
reopenWithEncodingPick = { label: nls.localize('reopenWithEncoding', "Reopen with Encoding") };
} else {
@@ -1200,7 +1203,7 @@ export class ChangeEncodingAction extends Action {
return undefined;
}
const resource = toResource(activeControl.input, { supportSideBySide: true });
const resource = toResource(activeControl!.input, { supportSideBySide: true });
return timeout(50 /* quick open is sensitive to being opened so soon after another */)
.then(() => {
@@ -1213,10 +1216,10 @@ export class ChangeEncodingAction extends Action {
.then((guessedEncoding: string) => {
const isReopenWithEncoding = (action === reopenWithEncodingPick);
const configuredEncoding = this.textResourceConfigurationService.getValue(resource, 'files.encoding');
const configuredEncoding = this.textResourceConfigurationService.getValue(types.withNullAsUndefined(resource), 'files.encoding');
let directMatchIndex: number;
let aliasMatchIndex: number;
let directMatchIndex: number | undefined;
let aliasMatchIndex: number | undefined;
// All encodings are valid picks
const picks: QuickPickInput[] = Object.keys(SUPPORTED_ENCODINGS)
@@ -1258,12 +1261,16 @@ export class ChangeEncodingAction extends Action {
placeHolder: isReopenWithEncoding ? nls.localize('pickEncodingForReopen', "Select File Encoding to Reopen File") : nls.localize('pickEncodingForSave', "Select File Encoding to Save with"),
activeItem: items[typeof directMatchIndex === 'number' ? directMatchIndex : typeof aliasMatchIndex === 'number' ? aliasMatchIndex : -1]
}).then(encoding => {
if (encoding) {
activeControl = this.editorService.activeControl;
encodingSupport = toEditorWithEncodingSupport(activeControl.input);
if (encodingSupport && encodingSupport.getEncoding() !== encoding.id) {
encodingSupport.setEncoding(encoding.id, isReopenWithEncoding ? EncodingMode.Decode : EncodingMode.Encode); // Set new encoding
}
if (!encoding) {
return;
}
const activeControl = this.editorService.activeControl;
if (!activeControl) {
return;
}
const encodingSupport = toEditorWithEncodingSupport(activeControl.input);
if (typeof encoding.id !== 'undefined' && encodingSupport && encodingSupport.getEncoding() !== encoding.id) {
encodingSupport.setEncoding(encoding.id, isReopenWithEncoding ? EncodingMode.Decode : EncodingMode.Encode); // Set new encoding
}
});
});

View File

@@ -14,17 +14,16 @@ import { buttonBackground, buttonForeground, editorBackground, editorForeground,
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IWindowService } from 'vs/platform/windows/common/windows';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { Schemas } from 'vs/base/common/network';
import { WORKSPACE_EXTENSION } from 'vs/platform/workspaces/common/workspaces';
import { extname } from 'vs/base/common/paths';
import { hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspaces';
import { Disposable, dispose } from 'vs/base/common/lifecycle';
import { localize } from 'vs/nls';
import { IEditorContribution } from 'vs/editor/common/editorCommon';
import { isEqual } from 'vs/base/common/resources';
import { IFileService } from 'vs/platform/files/common/files';
export class FloatingClickWidget extends Widget implements IOverlayWidget {
private _onClick: Emitter<void> = this._register(new Emitter<void>());
private readonly _onClick: Emitter<void> = this._register(new Emitter<void>());
get onClick(): Event<void> { return this._onClick.event; }
private _domNode: HTMLElement;
@@ -108,7 +107,8 @@ export class OpenWorkspaceButtonContribution extends Disposable implements IEdit
private editor: ICodeEditor,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IWindowService private readonly windowService: IWindowService,
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
@IFileService private readonly fileService: IFileService
) {
super();
@@ -139,8 +139,12 @@ export class OpenWorkspaceButtonContribution extends Disposable implements IEdit
return false; // we need a model
}
if (model.uri.scheme !== Schemas.file || extname(model.uri.fsPath) !== `.${WORKSPACE_EXTENSION}`) {
return false; // we need a local workspace file
if (!hasWorkspaceFileExtension(model.uri.fsPath)) {
return false; // we need a workspace file
}
if (!this.fileService.canHandleResource(model.uri)) {
return false; // needs to be backed by a file service
}
if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) {
@@ -159,7 +163,7 @@ export class OpenWorkspaceButtonContribution extends Disposable implements IEdit
this._register(this.openWorkspaceButton.onClick(() => {
const model = this.editor.getModel();
if (model) {
this.windowService.openWindow([model.uri]);
this.windowService.openWindow([{ uri: model.uri, typeHint: 'file' }]);
}
}));

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
<style type="text/css">
.st0{opacity:0.2;}
.st1{fill:#646464;}
</style>
<g class="st0">
<path class="st1" d="M12.7,19.2L12.1,19H7l-1.4,3.8L3.1,16L2,19H0v1.5C0,22.4,3.1,24,7,24c2.3,0,4.3-0.6,5.6-1.4l-0.1-0.3l-0.4-0.9
l-0.4-0.9l0.9-0.4l0.3-0.1c0-0.1,0-0.2,0-0.2c0-0.1,0-0.2,0-0.2L12.7,19.2z"/>
<path class="st1" d="M12.5,11c-1.3,0.8-3.3,1.3-5.5,1.3c-3.9,0-7-1.6-7-3.5V18h1.3l1.8-5l2.5,6.8L6.3,18h5.8l0-0.1l0.4-0.9l0.4-0.9
l0.9,0.4l0.2,0.1V11L12.5,11L12.5,11z"/>
<g>
<path class="st1" d="M19,4c0.4,0,0.8,0.1,1.2,0.2c0.4,0.2,0.7,0.4,1,0.6c0.3,0.3,0.5,0.6,0.6,0.9C21.9,6.2,22,6.6,22,7
c0,0.4-0.1,0.8-0.2,1.2c-0.2,0.4-0.4,0.7-0.6,1c-0.3,0.3-0.6,0.5-1,0.6C19.8,9.9,19.4,10,19,10h-9c-0.6,0-1.1-0.1-1.6-0.3
C8,9.5,7.5,9.2,7.2,8.8C6.8,8.5,6.5,8,6.3,7.6C6.1,7.1,6,6.6,6,6c0-0.6,0.1-1.1,0.3-1.6C6.5,4,6.8,3.5,7.2,3.2
c0.4-0.4,0.8-0.6,1.3-0.9C8.9,2.1,9.4,2,10,2c0.2,0,0.5,0,0.7,0.1c0.2-0.3,0.4-0.6,0.7-0.9c0.3-0.3,0.6-0.5,0.9-0.7
c0.3-0.2,0.7-0.3,1-0.4C13.7,0.1,14.1,0,14.5,0c0.6,0,1.1,0.1,1.6,0.3c0.5,0.2,1,0.5,1.4,0.8c0.4,0.4,0.7,0.8,1,1.3
C18.7,2.9,18.9,3.4,19,4z"/>
</g>
<path class="st1" d="M21,19.5c0,0.3,0,0.6-0.1,0.9l1.1,0.4l-0.4,0.9l-1.1-0.4c-0.3,0.5-0.7,0.9-1.2,1.2l0.4,1.1L18.8,24l-0.4-1.1
C18.1,23,17.8,23,17.5,23s-0.6,0-0.9-0.1L16.2,24l-0.9-0.4l0.4-1.1c-0.5-0.3-0.9-0.7-1.2-1.2l-1.1,0.4L13,20.8l1.1-0.4
C14,20.1,14,19.8,14,19.5s0-0.6,0.1-0.9L13,18.2l0.4-0.9l1.1,0.4c0.3-0.5,0.7-0.9,1.2-1.2l-0.4-1.1l0.9-0.4l0.4,1.1
c0.3-0.1,0.6-0.1,0.9-0.1s0.6,0,0.9,0.1l0.4-1.1l0.9,0.4l-0.4,1.1c0.5,0.3,0.9,0.7,1.2,1.2l1.1-0.4l0.4,0.9l-1.1,0.4
C21,19,21,19.2,21,19.5z M17.5,22c0.3,0,0.7-0.1,1-0.2c0.3-0.1,0.6-0.3,0.8-0.5s0.4-0.5,0.5-0.8c0.1-0.3,0.2-0.6,0.2-1
s-0.1-0.7-0.2-1c-0.1-0.3-0.3-0.6-0.5-0.8s-0.5-0.4-0.8-0.5c-0.3-0.1-0.6-0.2-1-0.2c-0.3,0-0.7,0.1-1,0.2c-0.3,0.1-0.6,0.3-0.8,0.5
c-0.2,0.2-0.4,0.5-0.5,0.8c-0.1,0.3-0.2,0.6-0.2,1c0,0.3,0.1,0.7,0.2,1c0.1,0.3,0.3,0.6,0.5,0.8s0.5,0.4,0.8,0.5
C16.8,22,17.1,22,17.5,22z"/>
<path class="st1" d="M9.9,11c-0.6,0-1.3-0.1-1.8-0.4C7.4,10.4,6.9,10,6.5,9.5C6,9.1,5.7,8.6,5.4,8C5.2,7.5,5.1,7,5,6.5
C2.5,6.9,1,8,1,8.8c0,1,2.3,2.5,6,2.5C8,11.3,9,11.2,9.9,11z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="24px" height="24px" viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
<g>
<path style="fill:#646464;" d="M12.654,19.244l-0.572-0.236H6.958l-1.374,3.777l-2.48-6.824l-1.108,3.047H0v1.488
c0,1.932,3.131,3.496,6.997,3.496c2.304,0,4.348-0.555,5.621-1.416l-0.139-0.346l-0.377-0.93l-0.371-0.924l0.922-0.375l0.348-0.143
c-0.005-0.08-0.007-0.158-0.007-0.236c0-0.08,0.002-0.16,0.007-0.238L12.654,19.244z"/>
<path style="fill:#646464;" d="M12.491,10.999c-1.28,0.812-3.265,1.334-5.494,1.334C3.132,12.333,0,10.766,0,8.833v9.175h1.296
l1.808-4.973l2.48,6.824l0.675-1.852h5.822l0.024-0.064l0.376-0.93l0.377-0.934l0.931,0.383l0.204,0.086v-5.55H12.491z"/>
<g>
<path style="fill:#646464;" d="M18.968,4.002c0.419,0,0.809,0.078,1.175,0.234c0.368,0.156,0.688,0.369,0.962,0.636
c0.274,0.27,0.49,0.586,0.647,0.949C21.91,6.188,21.99,6.578,21.99,7c0,0.418-0.078,0.806-0.234,1.168
c-0.155,0.362-0.371,0.68-0.645,0.952c-0.273,0.273-0.593,0.488-0.955,0.645c-0.365,0.158-0.755,0.234-1.164,0.234H9.995
c-0.551,0-1.069-0.104-1.558-0.312C7.951,9.48,7.526,9.195,7.164,8.833c-0.36-0.36-0.646-0.786-0.854-1.272
c-0.209-0.487-0.312-1.008-0.312-1.56c0-0.551,0.104-1.069,0.312-1.559c0.206-0.484,0.492-0.91,0.854-1.272
C7.526,2.809,7.95,2.523,8.437,2.314c0.489-0.208,1.008-0.312,1.559-0.312c0.237,0,0.48,0.023,0.727,0.071
c0.209-0.322,0.448-0.613,0.722-0.871c0.276-0.258,0.572-0.474,0.897-0.651c0.321-0.176,0.666-0.312,1.028-0.407
c0.365-0.093,0.738-0.14,1.125-0.14c0.577,0,1.122,0.101,1.636,0.305c0.513,0.203,0.968,0.483,1.362,0.839
c0.396,0.357,0.723,0.781,0.98,1.271C18.729,2.906,18.893,3.437,18.968,4.002z"/>
</g>
<path style="fill:#646464;" d="M20.991,19.537c0,0.287-0.037,0.576-0.109,0.867l1.068,0.436l-0.374,0.93l-1.079-0.445
c-0.308,0.512-0.712,0.918-1.216,1.221l0.445,1.066l-0.93,0.385l-0.439-1.07c-0.291,0.074-0.58,0.109-0.867,0.109
S16.915,23,16.623,22.926l-0.437,1.07l-0.929-0.385l0.444-1.066c-0.511-0.303-0.918-0.709-1.219-1.221l-1.078,0.445l-0.373-0.93
l1.069-0.436c-0.072-0.291-0.108-0.58-0.108-0.867s0.036-0.574,0.111-0.867l-1.071-0.438l0.374-0.93l1.079,0.443
c0.3-0.504,0.706-0.91,1.218-1.217l-0.445-1.078l0.931-0.375l0.437,1.068c0.29-0.072,0.58-0.107,0.866-0.107
s0.577,0.035,0.867,0.107l0.438-1.068l0.93,0.375l-0.444,1.078c0.503,0.307,0.908,0.713,1.217,1.217l1.078-0.443l0.374,0.93
l-1.068,0.438C20.954,18.963,20.991,19.25,20.991,19.537z M17.492,22.035c0.344,0,0.666-0.064,0.969-0.195
c0.302-0.129,0.566-0.309,0.792-0.535s0.407-0.492,0.539-0.797c0.133-0.303,0.199-0.627,0.199-0.971s-0.066-0.666-0.199-0.969
c-0.132-0.303-0.313-0.566-0.539-0.793s-0.49-0.406-0.792-0.539c-0.303-0.133-0.625-0.199-0.969-0.199
c-0.343,0-0.667,0.066-0.973,0.199c-0.304,0.133-0.568,0.312-0.797,0.539c-0.228,0.227-0.404,0.49-0.534,0.793
c-0.131,0.303-0.195,0.625-0.195,0.969c0,0.342,0.064,0.668,0.195,0.971c0.13,0.305,0.309,0.57,0.534,0.797
s0.493,0.406,0.797,0.535C16.825,21.971,17.149,22.035,17.492,22.035z"/>
<path style="fill:#646464;" d="M9.886,10.988c-0.644-0.014-1.265-0.136-1.839-0.383C7.443,10.351,6.91,9.992,6.458,9.54
C6.01,9.092,5.65,8.558,5.391,7.954C5.193,7.492,5.094,6.997,5.045,6.487C2.525,6.88,1,7.981,1,8.833c0,1.044,2.281,2.5,5.997,2.5
C8.014,11.333,9.004,11.209,9.886,10.988z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="24px" height="24px" viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
<g style="opacity:0.2;">
<path style="fill:#646464;" d="M12.654,19.244l-0.572-0.236H6.958l-1.374,3.777l-2.48-6.824l-1.108,3.047H0v1.488
c0,1.932,3.131,3.496,6.997,3.496c2.304,0,4.348-0.555,5.621-1.416l-0.139-0.346l-0.377-0.93l-0.371-0.924l0.922-0.375l0.348-0.143
c-0.005-0.08-0.007-0.158-0.007-0.236c0-0.08,0.002-0.16,0.007-0.238L12.654,19.244z"/>
<path style="fill:#646464;" d="M12.491,10.999c-1.28,0.812-3.265,1.334-5.494,1.334C3.132,12.333,0,10.766,0,8.833v9.174h1.296
l1.808-4.973l2.48,6.824l0.675-1.852h5.822l0.024-0.064l0.376-0.93l0.377-0.934l0.931,0.383l0.204,0.086v-5.55H12.491z"/>
<g>
<path style="fill:#646464;" d="M18.968,4.002c0.419,0,0.809,0.078,1.175,0.234c0.368,0.156,0.688,0.369,0.962,0.636
c0.274,0.27,0.49,0.586,0.647,0.949C21.91,6.188,21.99,6.579,21.99,7c0,0.418-0.078,0.806-0.234,1.168
c-0.155,0.363-0.371,0.68-0.645,0.953s-0.593,0.488-0.955,0.645c-0.365,0.158-0.755,0.234-1.164,0.234H9.995
c-0.551,0-1.069-0.103-1.558-0.312C7.951,9.48,7.526,9.195,7.164,8.833C6.804,8.473,6.518,8.047,6.31,7.561
c-0.209-0.488-0.312-1.008-0.312-1.56c0-0.551,0.104-1.069,0.312-1.559c0.206-0.484,0.492-0.91,0.854-1.272
C7.526,2.809,7.95,2.524,8.437,2.315c0.489-0.208,1.008-0.312,1.559-0.312c0.237,0,0.48,0.023,0.727,0.071
c0.209-0.322,0.448-0.613,0.722-0.871c0.276-0.258,0.572-0.474,0.897-0.652c0.321-0.176,0.666-0.312,1.028-0.407
c0.365-0.093,0.738-0.14,1.125-0.14c0.577,0,1.122,0.1,1.636,0.304c0.513,0.203,0.968,0.483,1.362,0.839
c0.396,0.357,0.723,0.782,0.98,1.271C18.729,2.907,18.893,3.437,18.968,4.002z"/>
</g>
<path style="fill:#646464;" d="M20.991,19.537c0,0.287-0.037,0.576-0.109,0.867l1.068,0.436l-0.374,0.93l-1.079-0.445
c-0.308,0.512-0.712,0.918-1.216,1.221l0.445,1.066l-0.93,0.385l-0.439-1.07c-0.291,0.074-0.58,0.109-0.867,0.109
S16.915,23,16.623,22.926l-0.437,1.07l-0.929-0.385l0.444-1.066c-0.511-0.303-0.918-0.709-1.219-1.221l-1.078,0.445l-0.373-0.93
l1.069-0.436c-0.072-0.291-0.108-0.58-0.108-0.867s0.036-0.574,0.111-0.867l-1.071-0.438l0.374-0.93l1.079,0.443
c0.3-0.504,0.706-0.91,1.218-1.217l-0.445-1.078l0.931-0.375l0.437,1.068c0.29-0.072,0.58-0.107,0.866-0.107
s0.577,0.035,0.867,0.107l0.438-1.068l0.93,0.375l-0.444,1.078c0.503,0.307,0.908,0.713,1.217,1.217l1.078-0.443l0.374,0.93
l-1.068,0.438C20.954,18.963,20.991,19.25,20.991,19.537z M17.492,22.035c0.344,0,0.666-0.064,0.969-0.195
c0.302-0.129,0.566-0.309,0.792-0.535s0.407-0.492,0.539-0.797c0.133-0.303,0.199-0.627,0.199-0.971s-0.066-0.666-0.199-0.969
c-0.132-0.303-0.313-0.566-0.539-0.793s-0.49-0.406-0.792-0.539c-0.303-0.133-0.625-0.199-0.969-0.199
c-0.343,0-0.667,0.066-0.973,0.199c-0.304,0.133-0.568,0.312-0.797,0.539c-0.228,0.227-0.404,0.49-0.534,0.793
c-0.131,0.303-0.195,0.625-0.195,0.969c0,0.342,0.064,0.668,0.195,0.971c0.13,0.305,0.309,0.57,0.534,0.797
s0.493,0.406,0.797,0.535C16.825,21.971,17.149,22.035,17.492,22.035z"/>
<path style="fill:#646464;" d="M9.886,10.988c-0.644-0.014-1.265-0.135-1.839-0.382C7.443,10.351,6.91,9.992,6.458,9.541
C6.01,9.092,5.65,8.558,5.391,7.954C5.193,7.493,5.094,6.997,5.045,6.488C2.525,6.88,1,7.982,1,8.833c0,1.044,2.281,2.5,5.997,2.5
C8.014,11.333,9.004,11.209,9.886,10.988z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@@ -54,15 +54,14 @@
background-image: none;
}
/* {{SQL CARBON EDIT}} */
.windows > .monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item::before {
content: '/';
.monaco-workbench.windows .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item::before {
content: '\\';
}
.monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item.root_folder::before,
.monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item.root_folder + .monaco-breadcrumb-item::before,
.monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control.relative-path .monaco-breadcrumb-item:nth-child(2)::before,
.windows > .monaco-workbench .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item:nth-child(2)::before {
.monaco-workbench.windows .part.editor > .content .editor-group-container > .title.breadcrumbs .breadcrumbs-control .monaco-breadcrumb-item:nth-child(2)::before {
/* workspace folder, item following workspace folder, or relative path -> hide first seperator */
display: none;
}

View File

@@ -4,19 +4,20 @@
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./media/notabstitlecontrol';
import { toResource, Verbosity, IEditorInput } from 'vs/workbench/common/editor';
import { toResource, Verbosity, IEditorInput, IEditorPartOptions } from 'vs/workbench/common/editor';
import { TitleControl, IToolbarActions } from 'vs/workbench/browser/parts/editor/titleControl';
import { ResourceLabel, IResourceLabel } from 'vs/workbench/browser/labels';
import { TAB_ACTIVE_FOREGROUND, TAB_UNFOCUSED_ACTIVE_FOREGROUND } from 'vs/workbench/common/theme';
import { EventType as TouchEventType, GestureEvent, Gesture } from 'vs/base/browser/touch';
import { addDisposableListener, EventType, addClass, EventHelper, removeClass, toggleClass } from 'vs/base/browser/dom';
import { IEditorPartOptions, EDITOR_TITLE_HEIGHT } from 'vs/workbench/browser/parts/editor/editor';
import { EDITOR_TITLE_HEIGHT } from 'vs/workbench/browser/parts/editor/editor';
import { IAction } from 'vs/base/common/actions';
import { CLOSE_EDITOR_COMMAND_ID } from 'vs/workbench/browser/parts/editor/editorCommands';
import { Color } from 'vs/base/common/color';
import { withNullAsUndefined } from 'vs/base/common/types';
interface IRenderedEditorLabel {
editor: IEditorInput;
editor?: IEditorInput;
pinned: boolean;
}
@@ -72,8 +73,16 @@ export class NoTabsTitleControl extends TitleControl {
this._register(addDisposableListener(this.titleContainer, TouchEventType.Tap, (e: GestureEvent) => this.onTitleClick(e)));
// Context Menu
this._register(addDisposableListener(this.titleContainer, EventType.CONTEXT_MENU, (e: Event) => this.onContextMenu(this.group.activeEditor, e, this.titleContainer)));
this._register(addDisposableListener(this.titleContainer, TouchEventType.Contextmenu, (e: Event) => this.onContextMenu(this.group.activeEditor, e, this.titleContainer)));
this._register(addDisposableListener(this.titleContainer, EventType.CONTEXT_MENU, (e: Event) => {
if (this.group.activeEditor) {
this.onContextMenu(this.group.activeEditor, e, this.titleContainer);
}
}));
this._register(addDisposableListener(this.titleContainer, TouchEventType.Contextmenu, (e: Event) => {
if (this.group.activeEditor) {
this.onContextMenu(this.group.activeEditor, e, this.titleContainer);
}
}));
}
private onTitleLabelClick(e: MouseEvent): void {
@@ -95,7 +104,9 @@ export class NoTabsTitleControl extends TitleControl {
if (e instanceof MouseEvent && e.button === 1 /* Middle Button */) {
EventHelper.stop(e, true /* for https://github.com/Microsoft/vscode/issues/56715 */);
this.group.closeEditor(this.group.activeEditor);
if (this.group.activeEditor) {
this.group.closeEditor(this.group.activeEditor);
}
}
}
@@ -167,7 +178,7 @@ export class NoTabsTitleControl extends TitleControl {
if (
!this.activeLabel.editor && this.group.activeEditor || // active editor changed from null => editor
this.activeLabel.editor && !this.group.activeEditor || // active editor changed from editor => null
!this.group.isActive(this.activeLabel.editor) // active editor changed from editorA => editorB
(!this.activeLabel.editor || !this.group.isActive(this.activeLabel.editor)) // active editor changed from editorA => editorB
) {
fn();
@@ -195,9 +206,9 @@ export class NoTabsTitleControl extends TitleControl {
}
private redraw(): void {
const editor = this.group.activeEditor;
const editor = withNullAsUndefined(this.group.activeEditor);
const isEditorPinned = this.group.isPinned(this.group.activeEditor);
const isEditorPinned = this.group.activeEditor ? this.group.isPinned(this.group.activeEditor) : false;
const isGroupActive = this.accessor.activeGroup === this.group;
this.activeLabel = { editor, pinned: isEditorPinned };
@@ -244,7 +255,7 @@ export class NoTabsTitleControl extends TitleControl {
title = ''; // dont repeat what is already shown
}
this.editorLabel.setResource({ name, description, resource }, { title, italic: !isEditorPinned, extraClasses: ['no-tabs', 'title-label'] });
this.editorLabel.setResource({ name, description, resource: resource || undefined }, { title: typeof title === 'string' ? title : undefined, italic: !isEditorPinned, extraClasses: ['no-tabs', 'title-label'] });
if (isGroupActive) {
this.editorLabel.element.style.color = this.getColor(TAB_ACTIVE_FOREGROUND);
} else {
@@ -256,7 +267,7 @@ export class NoTabsTitleControl extends TitleControl {
}
}
private getVerbosity(style: string): Verbosity {
private getVerbosity(style: string | undefined): Verbosity {
switch (style) {
case 'short': return Verbosity.SHORT;
case 'long': return Verbosity.LONG;

View File

@@ -27,7 +27,7 @@ export interface IResourceDescriptor {
readonly resource: URI;
readonly name: string;
readonly size: number;
readonly etag: string;
readonly etag?: string;
readonly mime: string;
}
@@ -236,7 +236,7 @@ export class ZoomStatusbarItem extends Themable implements IStatusbarItem {
static instance: ZoomStatusbarItem;
showTimeout: any;
private showTimeout: any;
private statusBarItem: HTMLElement;
private onSelectScale?: (scale: Scale) => void;

View File

@@ -13,7 +13,7 @@ import { IThemeService } from 'vs/platform/theme/common/themeService';
import { scrollbarShadow } from 'vs/platform/theme/common/colorRegistry';
import { IEditorRegistry, Extensions as EditorExtensions } from 'vs/workbench/browser/editor';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IEditorGroup } from 'vs/workbench/services/group/common/editorGroupsService';
import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService';
import { SplitView, Sizing, Orientation } from 'vs/base/browser/ui/splitview/splitview';
import { Event, Relay, Emitter } from 'vs/base/common/event';
import { IStorageService } from 'vs/platform/storage/common/storage';

View File

@@ -6,7 +6,7 @@
import 'vs/css!./media/tabstitlecontrol';
import { isMacintosh } from 'vs/base/common/platform';
import { shorten } from 'vs/base/common/labels';
import { toResource, GroupIdentifier, IEditorInput, Verbosity, EditorCommandsContextActionRunner } from 'vs/workbench/common/editor';
import { toResource, GroupIdentifier, IEditorInput, Verbosity, EditorCommandsContextActionRunner, IEditorPartOptions } from 'vs/workbench/common/editor';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { EventType as TouchEventType, GestureEvent, Gesture } from 'vs/base/browser/touch';
import { KeyCode } from 'vs/base/common/keyCodes';
@@ -32,25 +32,22 @@ import { ResourcesDropHandler, fillResourceDataTransfers, DraggedEditorIdentifie
import { Color } from 'vs/base/common/color';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { MergeGroupMode, IMergeGroupOptions } from 'vs/workbench/services/group/common/editorGroupsService';
import { MergeGroupMode, IMergeGroupOptions } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
import { addClass, addDisposableListener, hasClass, EventType, EventHelper, removeClass, Dimension, scheduleAtNextAnimationFrame, findParentWithClass, clearNode } from 'vs/base/browser/dom';
import { localize } from 'vs/nls';
import { IEditorGroupsAccessor, IEditorPartOptions, IEditorGroupView } from 'vs/workbench/browser/parts/editor/editor';
import { IEditorGroupsAccessor, IEditorGroupView } from 'vs/workbench/browser/parts/editor/editor';
import { CloseOneEditorAction } from 'vs/workbench/browser/parts/editor/editorActions';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { BreadcrumbsControl } from 'vs/workbench/browser/parts/editor/breadcrumbsControl';
import { IFileService } from 'vs/platform/files/common/files';
import { withNullAsUndefined } from 'vs/base/common/types';
// {{SQL CARBON EDIT}} -- Display the editor's tab color
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
import { IQueryEditorService } from 'sql/workbench/services/queryEditor/common/queryEditorService';
import { IObjectExplorerService } from 'sql/workbench/services/objectExplorer/common/objectExplorerService';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { GlobalNewUntitledFileAction } from 'vs/workbench/parts/files/electron-browser/fileActions';
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
import * as QueryConstants from 'sql/parts/query/common/constants';
import * as WorkbenchUtils from 'sql/workbench/common/sqlWorkbenchUtils';
import { GlobalNewUntitledFileAction } from 'vs/workbench/contrib/files/browser/fileActions';
// {{SQL CARBON EDIT}} -- End
interface IEditorInputLabel {
@@ -74,7 +71,7 @@ export class TabsTitleControl extends TitleControl {
private tabDisposeables: IDisposable[] = [];
private dimension: Dimension;
private layoutScheduled: IDisposable;
private layoutScheduled?: IDisposable;
private blockRevealActiveTab: boolean;
constructor(
@@ -95,11 +92,7 @@ export class TabsTitleControl extends TitleControl {
@IConfigurationService configurationService: IConfigurationService,
@IFileService fileService: IFileService,
// {{SQL CARBON EDIT}} -- Display the editor's tab color
@IWorkspaceConfigurationService private workspaceConfigurationService: IWorkspaceConfigurationService,
@ICommandService private commandService: ICommandService,
@IConnectionManagementService private connectionService: IConnectionManagementService,
@IQueryEditorService private queryEditorService: IQueryEditorService,
@IObjectExplorerService private objectExplorerService: IObjectExplorerService,
// {{SQL CARBON EDIT}} -- End
) {
super(parent, accessor, group, contextMenuService, instantiationService, contextKeyService, keybindingService, telemetryService, notificationService, menuService, quickOpenService, themeService, extensionService, configurationService, fileService);
@@ -222,7 +215,7 @@ export class TabsTitleControl extends TitleControl {
// Return if transfer is unsupported
if (!this.isSupportedDropTransfer(e)) {
e.dataTransfer.dropEffect = 'none';
e.dataTransfer!.dropEffect = 'none';
return;
}
@@ -231,9 +224,9 @@ export class TabsTitleControl extends TitleControl {
if (this.editorTransfer.hasData(DraggedEditorIdentifier.prototype)) {
isLocalDragAndDrop = true;
const localDraggedEditor = this.editorTransfer.getData(DraggedEditorIdentifier.prototype)[0].identifier;
const localDraggedEditor = this.editorTransfer.getData(DraggedEditorIdentifier.prototype)![0].identifier;
if (this.group.id === localDraggedEditor.groupId && this.group.getIndexOfEditor(localDraggedEditor.editor) === this.group.count - 1) {
e.dataTransfer.dropEffect = 'none';
e.dataTransfer!.dropEffect = 'none';
return;
}
}
@@ -241,7 +234,7 @@ export class TabsTitleControl extends TitleControl {
// Update the dropEffect to "copy" if there is no local data to be dragged because
// in that case we can only copy the data into and not move it from its source
if (!isLocalDragAndDrop) {
e.dataTransfer.dropEffect = 'copy';
e.dataTransfer!.dropEffect = 'copy';
}
this.updateDropFeedback(this.tabsContainer, true);
@@ -317,7 +310,7 @@ export class TabsTitleControl extends TitleControl {
(this.tabsContainer.lastChild as HTMLElement).remove();
// Remove associated tab label and widget
this.tabDisposeables.pop().dispose();
this.tabDisposeables.pop()!.dispose();
}
// A removal of a label requires to recompute all labels
@@ -488,7 +481,10 @@ export class TabsTitleControl extends TitleControl {
}
// Open tabs editor
this.group.openEditor(this.group.getEditor(index));
const input = this.group.getEditor(index);
if (input) {
this.group.openEditor(input);
}
return undefined;
};
@@ -496,7 +492,10 @@ export class TabsTitleControl extends TitleControl {
const showContextMenu = (e: Event) => {
EventHelper.stop(e);
this.onContextMenu(this.group.getEditor(index), e, tab);
const input = this.group.getEditor(index);
if (input) {
this.onContextMenu(input, e, tab);
}
};
// Open on Click / Touch
@@ -543,7 +542,10 @@ export class TabsTitleControl extends TitleControl {
// Run action on Enter/Space
if (event.equals(KeyCode.Enter) || event.equals(KeyCode.Space)) {
handled = true;
this.group.openEditor(this.group.getEditor(index));
const input = this.group.getEditor(index);
if (input) {
this.group.openEditor(input);
}
}
// Navigate in editors
@@ -581,22 +583,29 @@ export class TabsTitleControl extends TitleControl {
disposables.push(addDisposableListener(tab, EventType.DBLCLICK, (e: MouseEvent) => {
EventHelper.stop(e);
this.group.pinEditor(this.group.getEditor(index));
this.group.pinEditor(this.group.getEditor(index) || undefined);
}));
// Context menu
disposables.push(addDisposableListener(tab, EventType.CONTEXT_MENU, (e: Event) => {
EventHelper.stop(e, true);
this.onContextMenu(this.group.getEditor(index), e, tab);
const input = this.group.getEditor(index);
if (input) {
this.onContextMenu(input, e, tab);
}
}, true /* use capture to fix https://github.com/Microsoft/vscode/issues/19145 */));
// Drag support
disposables.push(addDisposableListener(tab, EventType.DRAG_START, (e: DragEvent) => {
const editor = this.group.getEditor(index);
if (!editor) {
return;
}
this.editorTransfer.setData([new DraggedEditorIdentifier({ editor, groupId: this.group.id })], DraggedEditorIdentifier.prototype);
e.dataTransfer.effectAllowed = 'copyMove';
e.dataTransfer!.effectAllowed = 'copyMove';
// Apply some datatransfer types to allow for dragging the element outside of the application
const resource = toResource(editor, { supportSideBySide: true });
@@ -618,7 +627,7 @@ export class TabsTitleControl extends TitleControl {
// Return if transfer is unsupported
if (!this.isSupportedDropTransfer(e)) {
e.dataTransfer.dropEffect = 'none';
e.dataTransfer!.dropEffect = 'none';
return;
}
@@ -627,9 +636,9 @@ export class TabsTitleControl extends TitleControl {
if (this.editorTransfer.hasData(DraggedEditorIdentifier.prototype)) {
isLocalDragAndDrop = true;
const localDraggedEditor = this.editorTransfer.getData(DraggedEditorIdentifier.prototype)[0].identifier;
const localDraggedEditor = this.editorTransfer.getData(DraggedEditorIdentifier.prototype)![0].identifier;
if (localDraggedEditor.editor === this.group.getEditor(index) && localDraggedEditor.groupId === this.group.id) {
e.dataTransfer.dropEffect = 'none';
e.dataTransfer!.dropEffect = 'none';
return;
}
}
@@ -637,7 +646,7 @@ export class TabsTitleControl extends TitleControl {
// Update the dropEffect to "copy" if there is no local data to be dragged because
// in that case we can only copy the data into and not move it from its source
if (!isLocalDragAndDrop) {
e.dataTransfer.dropEffect = 'copy';
e.dataTransfer!.dropEffect = 'copy';
}
this.updateDropFeedback(tab, true, index);
@@ -668,7 +677,7 @@ export class TabsTitleControl extends TitleControl {
private isSupportedDropTransfer(e: DragEvent): boolean {
if (this.groupTransfer.hasData(DraggedEditorGroupIdentifier.prototype)) {
const group = this.groupTransfer.getData(DraggedEditorGroupIdentifier.prototype)[0];
const group = this.groupTransfer.getData(DraggedEditorGroupIdentifier.prototype)![0];
if (group.identifier === this.group.id) {
return false; // groups cannot be dropped on title area it originates from
}
@@ -680,7 +689,7 @@ export class TabsTitleControl extends TitleControl {
return true; // (local) editors can always be dropped
}
if (e.dataTransfer.types.length > 0) {
if (e.dataTransfer && e.dataTransfer.types.length > 0) {
return true; // optimistically allow external data (// see https://github.com/Microsoft/vscode/issues/25789)
}
@@ -689,7 +698,8 @@ export class TabsTitleControl extends TitleControl {
private updateDropFeedback(element: HTMLElement, isDND: boolean, index?: number): void {
const isTab = (typeof index === 'number');
const isActiveTab = isTab && this.group.isActive(this.group.getEditor(index));
const editor = typeof index === 'number' ? this.group.getEditor(index) : null;
const isActiveTab = isTab && !!editor && this.group.isActive(editor);
// Background
const noDNDBackgroundColor = isTab ? this.getColor(isActiveTab ? TAB_ACTIVE_BACKGROUND : TAB_INACTIVE_BACKGROUND) : null;
@@ -728,9 +738,9 @@ export class TabsTitleControl extends TitleControl {
// Build labels and descriptions for each editor
const labels = this.group.editors.map(editor => ({
editor,
name: editor.getName(),
description: editor.getDescription(verbosity),
title: editor.getTitle(Verbosity.LONG)
name: editor.getName()!,
description: withNullAsUndefined(editor.getDescription(verbosity)),
title: withNullAsUndefined(editor.getTitle(Verbosity.LONG))
}));
// Shorten labels as needed
@@ -782,7 +792,7 @@ export class TabsTitleControl extends TitleControl {
if (useLongDescriptions) {
mapDescriptionToDuplicates.clear();
duplicateTitles.forEach(label => {
label.description = label.editor.getDescription(Verbosity.LONG);
label.description = withNullAsUndefined(label.editor.getDescription(Verbosity.LONG));
getOrSet(mapDescriptionToDuplicates, label.description, []).push(label);
});
}
@@ -793,7 +803,7 @@ export class TabsTitleControl extends TitleControl {
// Remove description if all descriptions are identical
if (descriptions.length === 1) {
for (const label of mapDescriptionToDuplicates.get(descriptions[0])) {
for (const label of mapDescriptionToDuplicates.get(descriptions[0]) || []) {
label.description = '';
}
@@ -803,14 +813,14 @@ export class TabsTitleControl extends TitleControl {
// Shorten descriptions
const shortenedDescriptions = shorten(descriptions);
descriptions.forEach((description, i) => {
for (const label of mapDescriptionToDuplicates.get(description)) {
for (const label of mapDescriptionToDuplicates.get(description) || []) {
label.description = shortenedDescriptions[i];
}
});
});
}
private getLabelConfigFlags(value: string) {
private getLabelConfigFlags(value: string | undefined) {
switch (value) {
case 'short':
return { verbosity: Verbosity.SHORT, shortenDuplicates: false };
@@ -891,7 +901,7 @@ export class TabsTitleControl extends TitleControl {
tabContainer.title = title;
// Label
tabLabelWidget.setResource({ name, description, resource: toResource(editor, { supportSideBySide: true }) }, { title, extraClasses: ['tab-label'], italic: !this.group.isPinned(editor) });
tabLabelWidget.setResource({ name, description, resource: toResource(editor, { supportSideBySide: true }) || undefined }, { title, extraClasses: ['tab-label'], italic: !this.group.isPinned(editor) });
// {{SQL CARBON EDIT}} -- Display the editor's tab color
const isTabActive = this.group.isActive(editor);
@@ -961,7 +971,7 @@ export class TabsTitleControl extends TitleControl {
// Highlight modified tabs with a border if configured
if (this.accessor.partOptions.highlightModifiedTabs) {
let modifiedBorderColor: string;
let modifiedBorderColor: string | null;
if (isGroupActive && isTabActive) {
modifiedBorderColor = this.getColor(TAB_ACTIVE_MODIFIED_BORDER);
} else if (isGroupActive && !isTabActive) {
@@ -998,7 +1008,7 @@ export class TabsTitleControl extends TitleControl {
layout(dimension: Dimension): void {
this.dimension = dimension;
const activeTab = this.getTab(this.group.activeEditor);
const activeTab = this.group.activeEditor ? this.getTab(this.group.activeEditor) : undefined;
if (!activeTab || !this.dimension) {
return;
}
@@ -1015,7 +1025,7 @@ export class TabsTitleControl extends TitleControl {
}
private doLayout(dimension: Dimension): void {
const activeTab = this.getTab(this.group.activeEditor);
const activeTab = this.group.activeEditor ? this.getTab(this.group.activeEditor) : undefined;
if (!activeTab) {
return;
}
@@ -1050,25 +1060,25 @@ export class TabsTitleControl extends TitleControl {
// Reveal the active one
const containerScrollPosX = this.tabsScrollbar.getScrollPosition().scrollLeft;
const activeTabFits = activeTabWidth <= visibleContainerWidth;
const activeTabFits = activeTabWidth! <= visibleContainerWidth;
// Tab is overflowing to the right: Scroll minimally until the element is fully visible to the right
// Note: only try to do this if we actually have enough width to give to show the tab fully!
if (activeTabFits && containerScrollPosX + visibleContainerWidth < activeTabPosX + activeTabWidth) {
if (activeTabFits && containerScrollPosX + visibleContainerWidth < activeTabPosX! + activeTabWidth!) {
this.tabsScrollbar.setScrollPosition({
scrollLeft: containerScrollPosX + ((activeTabPosX + activeTabWidth) /* right corner of tab */ - (containerScrollPosX + visibleContainerWidth) /* right corner of view port */)
scrollLeft: containerScrollPosX + ((activeTabPosX! + activeTabWidth!) /* right corner of tab */ - (containerScrollPosX + visibleContainerWidth) /* right corner of view port */)
});
}
// Tab is overlflowng to the left or does not fit: Scroll it into view to the left
else if (containerScrollPosX > activeTabPosX || !activeTabFits) {
else if (containerScrollPosX > activeTabPosX! || !activeTabFits) {
this.tabsScrollbar.setScrollPosition({
scrollLeft: activeTabPosX
scrollLeft: activeTabPosX!
});
}
}
private getTab(editor: IEditorInput): HTMLElement {
private getTab(editor: IEditorInput): HTMLElement | undefined {
const editorIndex = this.group.getIndexOfEditor(editor);
if (editorIndex >= 0) {
return this.tabsContainer.children[editorIndex] as HTMLElement;
@@ -1106,17 +1116,20 @@ export class TabsTitleControl extends TitleControl {
// Local Editor DND
if (this.editorTransfer.hasData(DraggedEditorIdentifier.prototype)) {
const draggedEditor = this.editorTransfer.getData(DraggedEditorIdentifier.prototype)[0].identifier;
const draggedEditor = this.editorTransfer.getData(DraggedEditorIdentifier.prototype)![0].identifier;
const sourceGroup = this.accessor.getGroup(draggedEditor.groupId);
// Move editor to target position and index
if (this.isMoveOperation(e, draggedEditor.groupId)) {
sourceGroup.moveEditor(draggedEditor.editor, this.group, { index: targetIndex });
}
if (sourceGroup) {
// Copy editor to target position and index
else {
sourceGroup.copyEditor(draggedEditor.editor, this.group, { index: targetIndex });
// Move editor to target position and index
if (this.isMoveOperation(e, draggedEditor.groupId)) {
sourceGroup.moveEditor(draggedEditor.editor, this.group, { index: targetIndex });
}
// Copy editor to target position and index
else {
sourceGroup.copyEditor(draggedEditor.editor, this.group, { index: targetIndex });
}
}
this.group.focus();
@@ -1125,15 +1138,17 @@ export class TabsTitleControl extends TitleControl {
// Local Editor Group DND
else if (this.groupTransfer.hasData(DraggedEditorGroupIdentifier.prototype)) {
const sourceGroup = this.accessor.getGroup(this.groupTransfer.getData(DraggedEditorGroupIdentifier.prototype)[0].identifier);
const sourceGroup = this.accessor.getGroup(this.groupTransfer.getData(DraggedEditorGroupIdentifier.prototype)![0].identifier);
const mergeGroupOptions: IMergeGroupOptions = { index: targetIndex };
if (!this.isMoveOperation(e, sourceGroup.id)) {
mergeGroupOptions.mode = MergeGroupMode.COPY_EDITORS;
if (sourceGroup) {
const mergeGroupOptions: IMergeGroupOptions = { index: targetIndex };
if (!this.isMoveOperation(e, sourceGroup.id)) {
mergeGroupOptions.mode = MergeGroupMode.COPY_EDITORS;
}
this.accessor.mergeGroup(sourceGroup, this.group, mergeGroupOptions);
}
this.accessor.mergeGroup(sourceGroup, this.group, mergeGroupOptions);
this.group.focus();
this.groupTransfer.clearData(DraggedEditorGroupIdentifier.prototype);
}
@@ -1154,7 +1169,7 @@ export class TabsTitleControl extends TitleControl {
// {{SQL CARBON EDIT}} -- Display the editor's tab color
private setEditorTabColor(editor: IEditorInput, tabContainer: HTMLElement, isTabActive: boolean) {
let sqlEditor = editor as any;
let tabColorMode = WorkbenchUtils.getSqlConfigValue<string>(this.workspaceConfigurationService, 'tabColorMode');
let tabColorMode = WorkbenchUtils.getSqlConfigValue<string>(this.configurationService, 'tabColorMode');
if (tabColorMode === QueryConstants.tabColorModeOff || (tabColorMode !== QueryConstants.tabColorModeBorder && tabColorMode !== QueryConstants.tabColorModeFill)
|| this.themeService.getTheme().type === HIGH_CONTRAST || !sqlEditor.tabColor) {
tabContainer.style.borderTopColor = '';
@@ -1176,7 +1191,8 @@ export class TabsTitleControl extends TitleControl {
dispose(): void {
super.dispose();
this.layoutScheduled = dispose(this.layoutScheduled);
dispose(this.layoutScheduled);
this.layoutScheduled = undefined;
}
}
@@ -1206,6 +1222,16 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
`);
}
// High Contrast Border Color for Editor Actions
const contrastBorderColor = theme.getColor(contrastBorder);
if (contrastBorder) {
collector.addRule(`
.monaco-workbench .part.editor > .content .editor-group-container > .title .editor-actions {
outline: 1px solid ${contrastBorderColor}
}
`);
}
// Hover Background
const tabHoverBackground = theme.getColor(TAB_HOVER_BACKGROUND);
if (tabHoverBackground) {
@@ -1251,12 +1277,12 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
const editorGroupHeaderTabsBackground = theme.getColor(EDITOR_GROUP_HEADER_TABS_BACKGROUND);
const editorDragAndDropBackground = theme.getColor(EDITOR_DRAG_AND_DROP_BACKGROUND);
let adjustedTabBackground: Color;
let adjustedTabBackground: Color | undefined;
if (editorGroupHeaderTabsBackground && editorBackgroundColor) {
adjustedTabBackground = editorGroupHeaderTabsBackground.flatten(editorBackgroundColor, editorBackgroundColor, workbenchBackground);
}
let adjustedTabDragBackground: Color;
let adjustedTabDragBackground: Color | undefined;
if (editorGroupHeaderTabsBackground && editorBackgroundColor && editorDragAndDropBackground && editorBackgroundColor) {
adjustedTabDragBackground = editorGroupHeaderTabsBackground.flatten(editorBackgroundColor, editorDragAndDropBackground, editorBackgroundColor, workbenchBackground);
}

View File

@@ -27,7 +27,7 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { Registry } from 'vs/platform/registry/common/platform';
import { URI } from 'vs/base/common/uri';
import { Event } from 'vs/base/common/event';
import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { CancellationToken } from 'vs/base/common/cancellation';
import { EditorMemento } from 'vs/workbench/browser/parts/editor/baseEditor';
@@ -61,7 +61,7 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor {
return new EditorMemento(this.getId(), key, Object.create(null), limit, editorGroupService); // do not persist in storage as diff editors are never persisted
}
getTitle(): string {
getTitle(): string | null {
if (this.input) {
return this.input.getName();
}
@@ -120,6 +120,7 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor {
// Readonly flag
diffEditor.updateOptions({ readOnly: resolvedDiffEditorModel.isReadonly() });
return undefined;
}, error => {
// In case we tried to open a file and the response indicates that this is not a text file, fallback to binary diff.
@@ -262,7 +263,7 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor {
return super.loadTextEditorViewState(resource) as IDiffEditorViewState; // overridden for text diff editor support
}
private saveTextDiffEditorViewState(input: EditorInput): void {
private saveTextDiffEditorViewState(input: EditorInput | null): void {
if (!(input instanceof DiffEditorInput)) {
return; // only supported for diff editor inputs
}
@@ -288,11 +289,11 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor {
}
}
protected retrieveTextEditorViewState(resource: URI): IDiffEditorViewState {
protected retrieveTextEditorViewState(resource: URI): IDiffEditorViewState | null {
return this.retrieveTextDiffEditorViewState(resource); // overridden for text diff editor support
}
private retrieveTextDiffEditorViewState(resource: URI): IDiffEditorViewState {
private retrieveTextDiffEditorViewState(resource: URI): IDiffEditorViewState | null {
const control = this.getControl();
const model = control.getModel();
if (!model || !model.modified || !model.original) {
@@ -311,9 +312,9 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor {
return control.saveViewState();
}
private toDiffEditorViewStateResource(modelOrInput: IDiffEditorModel | DiffEditorInput): URI {
let original: URI;
let modified: URI;
private toDiffEditorViewStateResource(modelOrInput: IDiffEditorModel | DiffEditorInput): URI | null {
let original: URI | null;
let modified: URI | null;
if (modelOrInput instanceof DiffEditorInput) {
original = modelOrInput.originalInput.getResource();

View File

@@ -20,7 +20,7 @@ import { ITextFileService, SaveReason, AutoSaveMode } from 'vs/workbench/service
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
import { isDiffEditor, isCodeEditor, ICodeEditor, getCodeEditor } from 'vs/editor/browser/editorBrowser';
import { IEditorGroupsService, IEditorGroup } from 'vs/workbench/services/group/common/editorGroupsService';
import { IEditorGroupsService, IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IWindowService } from 'vs/platform/windows/common/windows';

View File

@@ -20,7 +20,7 @@ import { IThemeService } from 'vs/platform/theme/common/themeService';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { Event } from 'vs/base/common/event';
import { ScrollType } from 'vs/editor/common/editorCommon';
import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IWindowService } from 'vs/platform/windows/common/windows';
@@ -46,7 +46,7 @@ export class AbstractTextResourceEditor extends BaseTextEditor {
super(id, telemetryService, instantiationService, storageService, configurationService, themeService, textFileService, editorService, editorGroupService, windowService);
}
getTitle(): string {
getTitle(): string | null {
if (this.input) {
return this.input.getName();
}
@@ -168,7 +168,7 @@ export class AbstractTextResourceEditor extends BaseTextEditor {
super.saveState();
}
private saveTextResourceEditorViewState(input: EditorInput): void {
private saveTextResourceEditorViewState(input: EditorInput | null): void {
if (!(input instanceof UntitledEditorInput) && !(input instanceof ResourceEditorInput)) {
return; // only enabled for untitled and resource inputs
}

View File

@@ -32,13 +32,14 @@ import { DraggedEditorGroupIdentifier, DraggedEditorIdentifier, fillResourceData
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { BreadcrumbsConfig } from 'vs/workbench/browser/parts/editor/breadcrumbs';
import { BreadcrumbsControl, IBreadcrumbsControlOptions } from 'vs/workbench/browser/parts/editor/breadcrumbsControl';
import { EDITOR_TITLE_HEIGHT, IEditorGroupsAccessor, IEditorGroupView, IEditorPartOptions } from 'vs/workbench/browser/parts/editor/editor';
import { EditorCommandsContextActionRunner, IEditorCommandsContext, IEditorInput, toResource } from 'vs/workbench/common/editor';
import { EDITOR_TITLE_HEIGHT, IEditorGroupsAccessor, IEditorGroupView } from 'vs/workbench/browser/parts/editor/editor';
import { EditorCommandsContextActionRunner, IEditorCommandsContext, IEditorInput, toResource, IEditorPartOptions } from 'vs/workbench/common/editor';
import { ResourceContextKey } from 'vs/workbench/common/resources';
import { Themable } from 'vs/workbench/common/theme';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview';
import { IFileService } from 'vs/platform/files/common/files';
import { withUndefinedAsNull } from 'vs/base/common/types';
export interface IToolbarActions {
primary: IAction[];
@@ -50,7 +51,7 @@ export abstract class TitleControl extends Themable {
protected readonly groupTransfer = LocalSelectionTransfer.getInstance<DraggedEditorGroupIdentifier>();
protected readonly editorTransfer = LocalSelectionTransfer.getInstance<DraggedEditorIdentifier>();
protected breadcrumbsControl: BreadcrumbsControl;
protected breadcrumbsControl?: BreadcrumbsControl;
private currentPrimaryEditorActionIds: string[] = [];
private currentSecondaryEditorActionIds: string[] = [];
@@ -155,18 +156,18 @@ export abstract class TitleControl extends Themable {
}));
}
private actionItemProvider(action: Action): IActionItem {
private actionItemProvider(action: Action): IActionItem | null {
const activeControl = this.group.activeControl;
// Check Active Editor
let actionItem: IActionItem;
let actionItem: IActionItem | null = null;
if (activeControl instanceof BaseEditor) {
actionItem = activeControl.getActionItem(action);
}
// Check extensions
if (!actionItem) {
actionItem = createActionItem(action, this.keybindingService, this.notificationService, this.contextMenuService);
actionItem = withUndefinedAsNull(createActionItem(action, this.keybindingService, this.notificationService, this.contextMenuService));
}
return actionItem;
@@ -218,7 +219,7 @@ export abstract class TitleControl extends Themable {
this.editorToolBarMenuDisposables = dispose(this.editorToolBarMenuDisposables);
// Update the resource context
this.resourceContext.set(toResource(this.group.activeEditor, { supportSideBySide: true }));
this.resourceContext.set(this.group.activeEditor ? toResource(this.group.activeEditor, { supportSideBySide: true }) : null);
// Editor actions require the editor control to be there, so we retrieve it via service
const activeControl = this.group.activeControl;
@@ -254,23 +255,25 @@ export abstract class TitleControl extends Themable {
// Set editor group as transfer
this.groupTransfer.setData([new DraggedEditorGroupIdentifier(this.group.id)], DraggedEditorGroupIdentifier.prototype);
e.dataTransfer.effectAllowed = 'copyMove';
e.dataTransfer!.effectAllowed = 'copyMove';
// If tabs are disabled, treat dragging as if an editor tab was dragged
if (!this.accessor.partOptions.showTabs) {
const resource = toResource(this.group.activeEditor, { supportSideBySide: true });
const resource = this.group.activeEditor ? toResource(this.group.activeEditor, { supportSideBySide: true }) : null;
if (resource) {
this.instantiationService.invokeFunction(fillResourceDataTransfers, [resource], e);
}
}
// Drag Image
let label = this.group.activeEditor.getName();
if (this.accessor.partOptions.showTabs && this.group.count > 1) {
label = localize('draggedEditorGroup', "{0} (+{1})", label, this.group.count - 1);
}
if (this.group.activeEditor) {
let label = this.group.activeEditor.getName();
if (this.accessor.partOptions.showTabs && this.group.count > 1) {
label = localize('draggedEditorGroup', "{0} (+{1})", label, this.group.count - 1);
}
applyDragImage(e, label, 'monaco-editor-group-drag-image');
applyDragImage(e, label, 'monaco-editor-group-drag-image');
}
}));
// Drag end
@@ -305,7 +308,7 @@ export abstract class TitleControl extends Themable {
onHide: () => {
// restore previous context
this.resourceContext.set(currentContext);
this.resourceContext.set(currentContext || null);
// restore focus to active group
this.accessor.activeGroup.focus();
@@ -313,14 +316,14 @@ export abstract class TitleControl extends Themable {
});
}
private getKeybinding(action: IAction): ResolvedKeybinding {
private getKeybinding(action: IAction): ResolvedKeybinding | undefined {
return this.keybindingService.lookupKeybinding(action.id);
}
protected getKeybindingLabel(action: IAction): string {
protected getKeybindingLabel(action: IAction): string | undefined {
const keybinding = this.getKeybinding(action);
return keybinding ? keybinding.getLabel() : undefined;
return keybinding ? keybinding.getLabel() || undefined : undefined;
}
abstract openEditor(editor: IEditorInput): void;
@@ -356,7 +359,8 @@ export abstract class TitleControl extends Themable {
}
dispose(): void {
this.breadcrumbsControl = dispose(this.breadcrumbsControl);
dispose(this.breadcrumbsControl);
this.breadcrumbsControl = undefined;
this.editorToolBarMenuDisposables = dispose(this.editorToolBarMenuDisposables);
super.dispose();

View File

@@ -8,7 +8,7 @@ import 'vs/css!./media/notificationsActions';
import { Themable, NOTIFICATIONS_BORDER, NOTIFICATIONS_CENTER_HEADER_FOREGROUND, NOTIFICATIONS_CENTER_HEADER_BACKGROUND, NOTIFICATIONS_CENTER_BORDER } from 'vs/workbench/common/theme';
import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService';
import { INotificationsModel, INotificationChangeEvent, NotificationChangeType } from 'vs/workbench/common/notifications';
import { IPartService, Parts } from 'vs/workbench/services/part/common/partService';
import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService';
import { Event, Emitter } from 'vs/base/common/event';
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { NotificationsCenterVisibleContext } from 'vs/workbench/browser/parts/notifications/notificationsCommands';
@@ -16,7 +16,7 @@ import { NotificationsList } from 'vs/workbench/browser/parts/notifications/noti
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { addClass, removeClass, isAncestor, Dimension } from 'vs/base/browser/dom';
import { widgetShadow } from 'vs/platform/theme/common/colorRegistry';
import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { localize } from 'vs/nls';
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
import { ClearAllNotificationsAction, HideNotificationsCenterAction, NotificationActionRunner } from 'vs/workbench/browser/parts/notifications/notificationsActions';
@@ -43,7 +43,7 @@ export class NotificationsCenter extends Themable {
private model: INotificationsModel,
@IThemeService themeService: IThemeService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IPartService private readonly partService: IPartService,
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,
@IContextKeyService contextKeyService: IContextKeyService,
@IEditorGroupsService private readonly editorGroupService: IEditorGroupsService,
@IKeybindingService private readonly keybindingService: IKeybindingService
@@ -57,6 +57,7 @@ export class NotificationsCenter extends Themable {
private registerListeners(): void {
this._register(this.model.onDidNotificationChange(e => this.onDidNotificationChange(e)));
this._register(this.layoutService.onLayout(dimension => this.layout(dimension)));
}
get isVisible(): boolean {
@@ -251,11 +252,11 @@ export class NotificationsCenter extends Themable {
// Make sure notifications are not exceeding available height
availableHeight = this.workbenchDimensions.height - 35 /* header */;
if (this.partService.isVisible(Parts.STATUSBAR_PART)) {
if (this.layoutService.isVisible(Parts.STATUSBAR_PART)) {
availableHeight -= 22; // adjust for status bar
}
if (this.partService.isVisible(Parts.TITLEBAR_PART)) {
if (this.layoutService.isVisible(Parts.TITLEBAR_PART)) {
availableHeight -= 22; // adjust for title bar
}

View File

@@ -32,14 +32,9 @@ export const TOGGLE_NOTIFICATION = 'notification.toggle';
export const CLEAR_NOTIFICATION = 'notification.clear';
export const CLEAR_ALL_NOTIFICATIONS = 'notifications.clearAll';
const notificationFocusedId = 'notificationFocus';
export const NotificationFocusedContext = new RawContextKey<boolean>(notificationFocusedId, true);
const notificationsCenterVisibleId = 'notificationCenterVisible';
export const NotificationsCenterVisibleContext = new RawContextKey<boolean>(notificationsCenterVisibleId, false);
const notificationsToastsVisibleId = 'notificationToastsVisible';
export const NotificationsToastsVisibleContext = new RawContextKey<boolean>(notificationsToastsVisibleId, false);
export const NotificationFocusedContext = new RawContextKey<boolean>('notificationFocus', true);
export const NotificationsCenterVisibleContext = new RawContextKey<boolean>('notificationCenterVisible', false);
export const NotificationsToastsVisibleContext = new RawContextKey<boolean>('notificationToastsVisible', false);
export interface INotificationsCenterController {
readonly isVisible: boolean;

View File

@@ -10,11 +10,11 @@ import { addClass, removeClass, isAncestor, addDisposableListener, EventType, Di
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { NotificationsList } from 'vs/workbench/browser/parts/notifications/notificationsList';
import { Event } from 'vs/base/common/event';
import { IPartService, Parts } from 'vs/workbench/services/part/common/partService';
import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService';
import { Themable, NOTIFICATIONS_TOAST_BORDER } from 'vs/workbench/common/theme';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { widgetShadow } from 'vs/platform/theme/common/colorRegistry';
import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { NotificationsToastsVisibleContext } from 'vs/workbench/browser/parts/notifications/notificationsCommands';
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { localize } from 'vs/nls';
@@ -62,7 +62,7 @@ export class NotificationsToasts extends Themable {
private container: HTMLElement,
private model: INotificationsModel,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IPartService private readonly partService: IPartService,
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,
@IThemeService themeService: IThemeService,
@IEditorGroupsService private readonly editorGroupService: IEditorGroupsService,
@IContextKeyService contextKeyService: IContextKeyService,
@@ -79,6 +79,9 @@ export class NotificationsToasts extends Themable {
private registerListeners(): void {
// Layout
this._register(this.layoutService.onLayout(dimension => this.layout(dimension)));
// Delay some tasks until after we can show notifications
this.onCanShowNotifications().then(() => {
@@ -466,11 +469,11 @@ export class NotificationsToasts extends Themable {
// Make sure notifications are not exceeding available height
availableHeight = this.workbenchDimensions.height;
if (this.partService.isVisible(Parts.STATUSBAR_PART)) {
if (this.layoutService.isVisible(Parts.STATUSBAR_PART)) {
availableHeight -= 22; // adjust for status bar
}
if (this.partService.isVisible(Parts.TITLEBAR_PART)) {
if (this.layoutService.isVisible(Parts.TITLEBAR_PART)) {
availableHeight -= 22; // adjust for title bar
}

View File

@@ -417,7 +417,7 @@ export class NotificationTemplateRenderer {
actions.forEach(action => this.template.toolbar.push(action, { icon: true, label: false, keybinding: this.getKeybindingLabel(action) }));
}
private renderSource(notification): void {
private renderSource(notification: INotificationViewItem): void {
if (notification.expanded && notification.source) {
this.template.source.textContent = localize('notificationSource', "Source: {0}", notification.source);
this.template.source.title = notification.source;

View File

@@ -12,9 +12,10 @@ import { Registry } from 'vs/platform/registry/common/platform';
import { SyncActionDescriptor, MenuId, MenuRegistry } from 'vs/platform/actions/common/actions';
import { IWorkbenchActionRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/actions';
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
import { IPartService, Parts, Position } from 'vs/workbench/services/part/common/partService';
import { IWorkbenchLayoutService, Parts, Position } from 'vs/workbench/services/layout/browser/layoutService';
import { ActivityAction } from 'vs/workbench/browser/parts/compositeBarActions';
import { IActivity } from 'vs/workbench/common/activity';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
export class ClosePanelAction extends Action {
@@ -24,14 +25,14 @@ export class ClosePanelAction extends Action {
constructor(
id: string,
name: string,
@IPartService private readonly partService: IPartService
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService
) {
super(id, name, 'hide-panel-action');
}
run(): Promise<any> {
this.partService.setPanelHidden(true);
return Promise.resolve(null);
this.layoutService.setPanelHidden(true);
return Promise.resolve();
}
}
@@ -43,14 +44,14 @@ export class TogglePanelAction extends Action {
constructor(
id: string,
name: string,
@IPartService private readonly partService: IPartService
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService
) {
super(id, name, partService.isVisible(Parts.PANEL_PART) ? 'panel expanded' : 'panel');
super(id, name, layoutService.isVisible(Parts.PANEL_PART) ? 'panel expanded' : 'panel');
}
run(): Promise<any> {
this.partService.setPanelHidden(this.partService.isVisible(Parts.PANEL_PART));
return Promise.resolve(null);
this.layoutService.setPanelHidden(this.layoutService.isVisible(Parts.PANEL_PART));
return Promise.resolve();
}
}
@@ -63,7 +64,7 @@ class FocusPanelAction extends Action {
id: string,
label: string,
@IPanelService private readonly panelService: IPanelService,
@IPartService private readonly partService: IPartService
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService
) {
super(id, label);
}
@@ -71,9 +72,9 @@ class FocusPanelAction extends Action {
run(): Promise<any> {
// Show panel
if (!this.partService.isVisible(Parts.PANEL_PART)) {
this.partService.setPanelHidden(false);
return Promise.resolve(null);
if (!this.layoutService.isVisible(Parts.PANEL_PART)) {
this.layoutService.setPanelHidden(false);
return Promise.resolve();
}
// Focus into active panel
@@ -82,7 +83,7 @@ class FocusPanelAction extends Action {
panel.focus();
}
return Promise.resolve(null);
return Promise.resolve();
}
}
@@ -99,28 +100,29 @@ export class TogglePanelPositionAction extends Action {
constructor(
id: string,
label: string,
@IPartService private readonly partService: IPartService,
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,
@IEditorGroupsService editorGroupsService: IEditorGroupsService
) {
super(id, label, partService.getPanelPosition() === Position.RIGHT ? 'move-panel-to-bottom' : 'move-panel-to-right');
super(id, label, layoutService.getPanelPosition() === Position.RIGHT ? 'move-panel-to-bottom' : 'move-panel-to-right');
this.toDispose = [];
const setClassAndLabel = () => {
const positionRight = this.partService.getPanelPosition() === Position.RIGHT;
const positionRight = this.layoutService.getPanelPosition() === Position.RIGHT;
this.class = positionRight ? 'move-panel-to-bottom' : 'move-panel-to-right';
this.label = positionRight ? TogglePanelPositionAction.MOVE_TO_BOTTOM_LABEL : TogglePanelPositionAction.MOVE_TO_RIGHT_LABEL;
};
this.toDispose.push(partService.onEditorLayout(() => setClassAndLabel()));
this.toDispose.push(editorGroupsService.onDidLayout(() => setClassAndLabel()));
setClassAndLabel();
}
run(): Promise<any> {
const position = this.partService.getPanelPosition();
const position = this.layoutService.getPanelPosition();
this.partService.setPanelPosition(position === Position.BOTTOM ? Position.RIGHT : Position.BOTTOM);
return Promise.resolve(null);
this.layoutService.setPanelPosition(position === Position.BOTTOM ? Position.RIGHT : Position.BOTTOM);
return Promise.resolve();
}
dispose(): void {
@@ -143,26 +145,27 @@ export class ToggleMaximizedPanelAction extends Action {
constructor(
id: string,
label: string,
@IPartService private readonly partService: IPartService
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,
@IEditorGroupsService editorGroupsService: IEditorGroupsService
) {
super(id, label, partService.isPanelMaximized() ? 'minimize-panel-action' : 'maximize-panel-action');
super(id, label, layoutService.isPanelMaximized() ? 'minimize-panel-action' : 'maximize-panel-action');
this.toDispose = [];
this.toDispose.push(partService.onEditorLayout(() => {
const maximized = this.partService.isPanelMaximized();
this.toDispose.push(editorGroupsService.onDidLayout(() => {
const maximized = this.layoutService.isPanelMaximized();
this.class = maximized ? 'minimize-panel-action' : 'maximize-panel-action';
this.label = maximized ? ToggleMaximizedPanelAction.RESTORE_LABEL : ToggleMaximizedPanelAction.MAXIMIZE_LABEL;
}));
}
run(): Promise<any> {
if (!this.partService.isVisible(Parts.PANEL_PART)) {
this.partService.setPanelHidden(false);
if (!this.layoutService.isVisible(Parts.PANEL_PART)) {
this.layoutService.setPanelHidden(false);
}
this.partService.toggleMaximizedPanel();
return Promise.resolve(null);
this.layoutService.toggleMaximizedPanel();
return Promise.resolve();
}
dispose(): void {
@@ -184,7 +187,7 @@ export class PanelActivityAction extends ActivityAction {
run(event: any): Promise<any> {
this.panelService.openPanel(this.activity.id, true);
this.activate();
return Promise.resolve(null);
return Promise.resolve();
}
}
@@ -202,17 +205,19 @@ export class SwitchPanelViewAction extends Action {
const pinnedPanels = this.panelService.getPinnedPanels();
const activePanel = this.panelService.getActivePanel();
if (!activePanel) {
return Promise.resolve(null);
return Promise.resolve();
}
let targetPanelId: string;
let targetPanelId: string | undefined;
for (let i = 0; i < pinnedPanels.length; i++) {
if (pinnedPanels[i].id === activePanel.getId()) {
targetPanelId = pinnedPanels[(i + pinnedPanels.length + offset) % pinnedPanels.length].id;
break;
}
}
this.panelService.openPanel(targetPanelId, true);
return Promise.resolve(null);
if (typeof targetPanelId === 'string') {
this.panelService.openPanel(targetPanelId, true);
}
return Promise.resolve();
}
}
@@ -247,7 +252,7 @@ export class NextPanelViewAction extends SwitchPanelViewAction {
super(id, name, panelService);
}
public run(): Promise<any> {
run(): Promise<any> {
return super.run(1);
}
}

View File

@@ -5,19 +5,19 @@
import 'vs/css!./media/panelpart';
import { IAction } from 'vs/base/common/actions';
import { Event, Emitter } from 'vs/base/common/event';
import { Event } from 'vs/base/common/event';
import { Registry } from 'vs/platform/registry/common/platform';
import { ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar';
import { IPanel } from 'vs/workbench/common/panel';
import { IPanel, ActivePanelContext, PanelFocusContext } from 'vs/workbench/common/panel';
import { CompositePart, ICompositeTitleLabel } from 'vs/workbench/browser/parts/compositePart';
import { Panel, PanelRegistry, Extensions as PanelExtensions, PanelDescriptor } from 'vs/workbench/browser/panel';
import { IPanelService, IPanelIdentifier } from 'vs/workbench/services/panel/common/panelService';
import { IPartService, Parts, Position } from 'vs/workbench/services/part/common/partService';
import { IWorkbenchLayoutService, Parts, Position } from 'vs/workbench/services/layout/browser/layoutService';
import { IStorageService, StorageScope, IWorkspaceStorageChangeEvent } from 'vs/platform/storage/common/storage';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IInstantiationService, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
import { ClosePanelAction, TogglePanelPositionAction, PanelActivityAction, ToggleMaximizedPanelAction, TogglePanelAction } from 'vs/workbench/browser/parts/panel/panelActions';
import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService';
import { PANEL_BACKGROUND, PANEL_BORDER, PANEL_ACTIVE_TITLE_FOREGROUND, PANEL_INACTIVE_TITLE_FOREGROUND, PANEL_ACTIVE_TITLE_BORDER, PANEL_DRAG_AND_DROP_BACKGROUND } from 'vs/workbench/common/theme';
@@ -29,56 +29,58 @@ import { INotificationService } from 'vs/platform/notification/common/notificati
import { Dimension, trackFocus } from 'vs/base/browser/dom';
import { localize } from 'vs/nls';
import { IDisposable } from 'vs/base/common/lifecycle';
import { RawContextKey, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { isUndefinedOrNull } from 'vs/base/common/types';
import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
import { ISerializableView } from 'vs/base/browser/ui/grid/grid';
import { LayoutPriority } from 'vs/base/browser/ui/grid/gridview';
export const ActivePanelContext = new RawContextKey<string>('activePanel', '');
export const PanelFocusContext = new RawContextKey<boolean>('panelFocus', false);
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
interface ICachedPanel {
id: string;
pinned: boolean;
order: number;
order?: number;
visible: boolean;
}
export class PanelPart extends CompositePart<Panel> implements IPanelService, ISerializableView {
export class PanelPart extends CompositePart<Panel> implements IPanelService {
static readonly activePanelSettingsKey = 'workbench.panelpart.activepanelid';
private static readonly PINNED_PANELS = 'workbench.panel.pinnedPanels';
private static readonly MIN_COMPOSITE_BAR_WIDTH = 50;
_serviceBrand: any;
_serviceBrand: ServiceIdentifier<any>;
//#region IView
readonly minimumWidth: number = 300;
readonly maximumWidth: number = Number.POSITIVE_INFINITY;
readonly minimumHeight: number = 77;
readonly maximumHeight: number = Number.POSITIVE_INFINITY;
readonly snapSize: number = 50;
readonly priority: LayoutPriority = LayoutPriority.Low;
//#endregion
get onDidPanelOpen(): Event<{ panel: IPanel, focus: boolean }> { return Event.map(this.onDidCompositeOpen.event, compositeOpen => ({ panel: compositeOpen.composite, focus: compositeOpen.focus })); }
get onDidPanelClose(): Event<IPanel> { return this.onDidCompositeClose.event; }
private activePanelContextKey: IContextKey<string>;
private panelFocusContextKey: IContextKey<boolean>;
private blockOpeningPanel: boolean;
private compositeBar: CompositeBar;
private compositeActions: { [compositeId: string]: { activityAction: PanelActivityAction, pinnedAction: ToggleCompositePinnedAction } } = Object.create(null);
private blockOpeningPanel: boolean;
private dimension: Dimension;
element: HTMLElement;
minimumWidth: number = 300;
maximumWidth: number = Number.POSITIVE_INFINITY;
minimumHeight: number = 77;
maximumHeight: number = Number.POSITIVE_INFINITY;
snapSize: number = 50;
priority: LayoutPriority = LayoutPriority.Low;
private _onDidChange = new Emitter<{ width: number; height: number; }>();
readonly onDidChange = this._onDidChange.event;
constructor(
id: string,
@INotificationService notificationService: INotificationService,
@IStorageService storageService: IStorageService,
@ITelemetryService telemetryService: ITelemetryService,
@IContextMenuService contextMenuService: IContextMenuService,
@IPartService partService: IPartService,
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
@IKeybindingService keybindingService: IKeybindingService,
@IInstantiationService instantiationService: IInstantiationService,
@IThemeService themeService: IThemeService,
@@ -90,7 +92,7 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService, IS
storageService,
telemetryService,
contextMenuService,
partService,
layoutService,
keybindingService,
instantiationService,
themeService,
@@ -99,8 +101,8 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService, IS
Registry.as<PanelRegistry>(PanelExtensions.Panels).getDefaultPanelId(),
'panel',
'panel',
null,
id,
undefined,
Parts.PANEL_PART,
{ hasTitle: true }
);
@@ -116,10 +118,10 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService, IS
this.instantiationService.createInstance(TogglePanelAction, TogglePanelAction.ID, localize('hidePanel', "Hide Panel"))
],
getDefaultCompositeId: () => Registry.as<PanelRegistry>(PanelExtensions.Panels).getDefaultPanelId(),
hidePart: () => this.partService.setPanelHidden(true),
hidePart: () => this.layoutService.setPanelHidden(true),
compositeSize: 0,
overflowActionSize: 44,
colors: theme => ({
colors: (theme: ITheme) => ({
activeBackgroundColor: theme.getColor(PANEL_BACKGROUND), // Background color for overflow action
inactiveBackgroundColor: theme.getColor(PANEL_BACKGROUND), // Background color for overflow action
activeBorderBottomColor: theme.getColor(PANEL_ACTIVE_TITLE_BORDER),
@@ -141,25 +143,13 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService, IS
this.registerListeners();
}
create(parent: HTMLElement): void {
this.element = parent;
super.create(parent);
const focusTracker = trackFocus(parent);
focusTracker.onDidFocus(() => {
this.panelFocusContextKey.set(true);
});
focusTracker.onDidBlur(() => {
this.panelFocusContextKey.set(false);
});
}
private registerListeners(): void {
this._register(this.onDidPanelOpen(({ panel }) => this._onDidPanelOpen(panel)));
this._register(this.onDidPanelClose(this._onDidPanelClose, this));
// Panel open/close
this._register(this.onDidPanelOpen(({ panel }) => this.onPanelOpen(panel)));
this._register(this.onDidPanelClose(this.onPanelClose, this));
// Panel register/deregister
this._register(this.registry.onDidRegister(panelDescriptor => this.compositeBar.addComposite(panelDescriptor)));
this._register(this.registry.onDidDeregister(panelDescriptor => {
this.compositeBar.hideComposite(panelDescriptor.id);
@@ -175,17 +165,18 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService, IS
// Deactivate panel action on close
this._register(this.onDidPanelClose(panel => this.compositeBar.deactivateComposite(panel.getId())));
// State
this.lifecycleService.when(LifecyclePhase.Eventually).then(() => {
this._register(this.compositeBar.onDidChange(() => this.saveCachedPanels()));
this._register(this.storageService.onDidChangeStorage(e => this.onDidStorageChange(e)));
});
}
private _onDidPanelOpen(panel: IPanel): void {
private onPanelOpen(panel: IPanel): void {
this.activePanelContextKey.set(panel.getId());
}
private _onDidPanelClose(panel: IPanel): void {
private onPanelClose(panel: IPanel): void {
const id = panel.getId();
if (this.activePanelContextKey.get() === id) {
@@ -193,12 +184,14 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService, IS
}
}
get onDidPanelOpen(): Event<{ panel: IPanel, focus: boolean }> {
return Event.map(this._onDidCompositeOpen.event, compositeOpen => ({ panel: compositeOpen.composite, focus: compositeOpen.focus }));
}
create(parent: HTMLElement): void {
this.element = parent;
get onDidPanelClose(): Event<IPanel> {
return this._onDidCompositeClose.event;
super.create(parent);
const focusTracker = this._register(trackFocus(parent));
this._register(focusTracker.onDidFocus(() => this.panelFocusContextKey.set(true)));
this._register(focusTracker.onDidBlur(() => this.panelFocusContextKey.set(false)));
}
updateStyles(): void {
@@ -209,38 +202,40 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService, IS
container.style.borderLeftColor = this.getColor(PANEL_BORDER) || this.getColor(contrastBorder);
const title = this.getTitleArea();
title.style.borderTopColor = this.getColor(PANEL_BORDER) || this.getColor(contrastBorder);
if (title) {
title.style.borderTopColor = this.getColor(PANEL_BORDER) || this.getColor(contrastBorder);
}
}
openPanel(id: string, focus?: boolean): Panel {
openPanel(id: string, focus?: boolean): Panel | null {
if (this.blockOpeningPanel) {
return null; // Workaround against a potential race condition
}
// First check if panel is hidden and show if so
if (!this.partService.isVisible(Parts.PANEL_PART)) {
if (!this.layoutService.isVisible(Parts.PANEL_PART)) {
try {
this.blockOpeningPanel = true;
this.partService.setPanelHidden(false);
this.layoutService.setPanelHidden(false);
} finally {
this.blockOpeningPanel = false;
}
}
return this.openComposite(id, focus);
return this.openComposite(id, focus) || null;
}
showActivity(panelId: string, badge: IBadge, clazz?: string): IDisposable {
return this.compositeBar.showActivity(panelId, badge, clazz);
}
private getPanel(panelId: string): IPanelIdentifier {
private getPanel(panelId: string): IPanelIdentifier | undefined {
return this.getPanels().filter(p => p.id === panelId).pop();
}
getPanels(): PanelDescriptor[] {
return Registry.as<PanelRegistry>(PanelExtensions.Panels).getPanels()
.sort((v1, v2) => v1.order - v2.order);
.sort((v1, v2) => typeof v1.order === 'number' && typeof v2.order === 'number' ? v1.order - v2.order : NaN);
}
getPinnedPanels(): PanelDescriptor[] {
@@ -257,7 +252,7 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService, IS
];
}
getActivePanel(): IPanel {
getActivePanel(): IPanel | null {
return this.getActiveComposite();
}
@@ -286,41 +281,31 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService, IS
};
}
layout(dimension: Dimension): Dimension[];
layout(width: number, height: number): void;
layout(dim1: Dimension | number, dim2?: number): Dimension[] | void {
if (!this.partService.isVisible(Parts.PANEL_PART)) {
if (dim1 instanceof Dimension) {
return [dim1];
}
layout(width: number, height: number): void {
if (!this.layoutService.isVisible(Parts.PANEL_PART)) {
return;
}
const { width, height } = dim1 instanceof Dimension ? dim1 : { width: dim1, height: dim2 };
if (this.partService.getPanelPosition() === Position.RIGHT) {
// Take into account the 1px border when layouting
this.dimension = new Dimension(width - 1, height);
if (this.layoutService.getPanelPosition() === Position.RIGHT) {
this.dimension = new Dimension(width - 1, height!); // Take into account the 1px border when layouting
} else {
this.dimension = new Dimension(width, height);
this.dimension = new Dimension(width, height!);
}
const sizes = super.layout(this.dimension.width, this.dimension.height);
// Layout contents
super.layout(this.dimension.width, this.dimension.height);
// Layout composite bar
this.layoutCompositeBar();
if (dim1 instanceof Dimension) {
return sizes;
}
}
private layoutCompositeBar(): void {
if (this.dimension) {
let availableWidth = this.dimension.width - 40; // take padding into account
if (this.toolBar) {
// adjust height for global actions showing
availableWidth = Math.max(PanelPart.MIN_COMPOSITE_BAR_WIDTH, availableWidth - this.getToolbarWidth());
availableWidth = Math.max(PanelPart.MIN_COMPOSITE_BAR_WIDTH, availableWidth - this.getToolbarWidth()); // adjust height for global actions showing
}
this.compositeBar.layout(new Dimension(availableWidth, this.dimension.height));
}
}
@@ -334,6 +319,7 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService, IS
};
this.compositeActions[compositeId] = compositeActions;
}
return compositeActions;
}
@@ -345,8 +331,10 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService, IS
compositeActions.pinnedAction.dispose();
delete this.compositeActions[compositeId];
}
return true;
}
return false;
}
@@ -355,6 +343,7 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService, IS
if (!activePanel) {
return 0;
}
return this.toolBar.getItemsWidth();
}
@@ -393,30 +382,35 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService, IS
private saveCachedPanels(): void {
const state: ICachedPanel[] = [];
const compositeItems = this.compositeBar.getCompositeBarItems();
for (const compositeItem of compositeItems) {
state.push({ id: compositeItem.id, pinned: compositeItem.pinned, order: compositeItem.order, visible: compositeItem.visible });
}
this.cachedPanelsValue = JSON.stringify(state);
}
private getCachedPanels(): ICachedPanel[] {
const storedStates = <Array<string | ICachedPanel>>JSON.parse(this.cachedPanelsValue);
const registeredPanels = this.getPanels();
const storedStates = <Array<string | ICachedPanel>>JSON.parse(this.cachedPanelsValue);
const cachedPanels = <ICachedPanel[]>storedStates.map(c => {
const serialized: ICachedPanel = typeof c === 'string' /* migration from pinned states to composites states */ ? <ICachedPanel>{ id: c, pinned: true, order: undefined, visible: true } : c;
const registered = registeredPanels.some(p => p.id === serialized.id);
serialized.visible = registered ? isUndefinedOrNull(serialized.visible) ? true : serialized.visible : false;
return serialized;
});
return cachedPanels;
}
private _cachedPanelsValue: string;
private _cachedPanelsValue: string | null;
private get cachedPanelsValue(): string {
if (!this._cachedPanelsValue) {
this._cachedPanelsValue = this.getStoredCachedPanelsValue();
}
return this._cachedPanelsValue;
}
@@ -509,3 +503,5 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
`);
}
});
registerSingleton(IPanelService, PanelPart);

View File

@@ -6,9 +6,9 @@
import 'vs/css!./quickInput';
import { Component } from 'vs/workbench/common/component';
import { IQuickInputService, IQuickPickItem, IPickOptions, IInputOptions, IQuickNavigateConfiguration, IQuickPick, IQuickInput, IQuickInputButton, IInputBox, IQuickPickItemButtonEvent, QuickPickInput, IQuickPickSeparator, IKeyMods } from 'vs/platform/quickinput/common/quickInput';
import { IPartService } from 'vs/workbench/services/part/common/partService';
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
import * as dom from 'vs/base/browser/dom';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IInstantiationService, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { contrastBorder, widgetShadow } from 'vs/platform/theme/common/colorRegistry';
import { SIDE_BAR_BACKGROUND, SIDE_BAR_FOREGROUND } from 'vs/workbench/common/theme';
@@ -29,10 +29,10 @@ import { Emitter, Event } from 'vs/base/common/event';
import { Button } from 'vs/base/browser/ui/button/button';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import Severity from 'vs/base/common/severity';
import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IContextKeyService, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { ICommandAndKeybindingRule, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { inQuickOpenContext } from 'vs/workbench/browser/parts/quickopen/quickopen';
import { inQuickOpenContext, InQuickOpenContextKey } from 'vs/workbench/browser/parts/quickopen/quickopen';
import { ActionBar, ActionItem } from 'vs/base/browser/ui/actionbar/actionbar';
import { Action } from 'vs/base/common/actions';
import { URI } from 'vs/base/common/uri';
@@ -40,10 +40,10 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { equals } from 'vs/base/common/arrays';
import { TimeoutTimer } from 'vs/base/common/async';
import { getIconClass } from 'vs/workbench/browser/parts/quickinput/quickInputUtils';
import { AccessibilitySupport } from 'vs/base/common/platform';
import * as browser from 'vs/base/browser/browser';
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
const $ = dom.$;
@@ -115,7 +115,7 @@ class QuickInput implements IQuickInput {
this.onDidHideEmitter,
];
private busyDelay: TimeoutTimer;
private busyDelay: TimeoutTimer | null;
constructor(protected ui: QuickInputUI) {
}
@@ -252,21 +252,21 @@ class QuickInput implements IQuickInput {
this.ui.leftActionBar.clear();
const leftButtons = this.buttons.filter(button => button === backButton);
this.ui.leftActionBar.push(leftButtons.map((button, index) => {
const action = new Action(`id-${index}`, '', button.iconClass || getIconClass(button.iconPath), true, () => {
const action = new Action(`id-${index}`, '', button.iconClass || getIconClass(button.iconPath!), true, () => {
this.onDidTriggerButtonEmitter.fire(button);
return Promise.resolve(null);
});
action.tooltip = button.tooltip;
action.tooltip = button.tooltip || '';
return action;
}), { icon: true, label: false });
this.ui.rightActionBar.clear();
const rightButtons = this.buttons.filter(button => button !== backButton);
this.ui.rightActionBar.push(rightButtons.map((button, index) => {
const action = new Action(`id-${index}`, '', button.iconClass || getIconClass(button.iconPath), true, () => {
const action = new Action(`id-${index}`, '', button.iconClass || getIconClass(button.iconPath!), true, () => {
this.onDidTriggerButtonEmitter.fire(button);
return Promise.resolve(null);
});
action.tooltip = button.tooltip;
action.tooltip = button.tooltip || '';
return action;
}), { icon: true, label: false });
}
@@ -311,21 +311,26 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
private _value = '';
private _placeholder;
private onDidChangeValueEmitter = new Emitter<string>();
private onDidAcceptEmitter = new Emitter<string>();
private onDidAcceptEmitter = new Emitter<void>();
private _items: Array<T | IQuickPickSeparator> = [];
private itemsUpdated = false;
private _canSelectMany = false;
private _matchOnDescription = false;
private _matchOnDetail = false;
private _matchOnLabel = true;
private _autoFocusOnList = true;
private _activeItems: T[] = [];
private activeItemsUpdated = false;
private activeItemsToConfirm: T[] = [];
private activeItemsToConfirm: T[] | null = [];
private onDidChangeActiveEmitter = new Emitter<T[]>();
private _selectedItems: T[] = [];
private selectedItemsUpdated = false;
private selectedItemsToConfirm: T[] = [];
private selectedItemsToConfirm: T[] | null = [];
private onDidChangeSelectionEmitter = new Emitter<T[]>();
private onDidTriggerItemButtonEmitter = new Emitter<IQuickPickItemButtonEvent<T>>();
private _valueSelection: Readonly<[number, number]>;
private valueSelectionUpdated = true;
private _validationMessage: string;
quickNavigate: IQuickNavigateConfiguration;
@@ -399,6 +404,24 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
this.update();
}
get matchOnLabel() {
return this._matchOnLabel;
}
set matchOnLabel(matchOnLabel: boolean) {
this._matchOnLabel = matchOnLabel;
this.update();
}
get autoFocusOnList() {
return this._autoFocusOnList;
}
set autoFocusOnList(autoFocusOnList: boolean) {
this._autoFocusOnList = autoFocusOnList;
this.update();
}
get activeItems() {
return this._activeItems;
}
@@ -425,10 +448,33 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
return this.ui.keyMods;
}
set valueSelection(valueSelection: Readonly<[number, number]>) {
this._valueSelection = valueSelection;
this.valueSelectionUpdated = true;
this.update();
}
get validationMessage() {
return this._validationMessage;
}
set validationMessage(validationMessage: string) {
this._validationMessage = validationMessage;
this.update();
}
onDidChangeSelection = this.onDidChangeSelectionEmitter.event;
onDidTriggerItemButton = this.onDidTriggerItemButtonEmitter.event;
private trySelectFirst() {
if (this.autoFocusOnList) {
if (!this.ui.isScreenReaderOptimized() && !this.canSelectMany) {
this.ui.list.focus('First');
}
}
}
show() {
if (!this.visible) {
this.visibleDisposables.push(
@@ -438,11 +484,14 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
}
this._value = value;
this.ui.list.filter(this.ui.inputBox.value);
if (!this.ui.isScreenReaderOptimized() && !this.canSelectMany) {
this.ui.list.focus('First');
}
this.trySelectFirst();
this.onDidChangeValueEmitter.fire(value);
}),
this.ui.inputBox.onMouseDown(event => {
if (!this.autoFocusOnList) {
this.ui.list.clearFocus();
}
}),
this.ui.inputBox.onKeyDown(event => {
switch (event.keyCode) {
case KeyCode.DownArrow:
@@ -529,6 +578,7 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
this.ui.list.onButtonTriggered(event => this.onDidTriggerItemButtonEmitter.fire(event as IQuickPickItemButtonEvent<T>)),
this.registerQuickNavigation()
);
this.valueSelectionUpdated = true;
}
super.show(); // TODO: Why have show() bubble up while update() trickles down? (Could move setComboboxAccessibility() here.)
}
@@ -589,6 +639,10 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
if (this.ui.inputBox.value !== this.value) {
this.ui.inputBox.value = this.value;
}
if (this.valueSelectionUpdated) {
this.valueSelectionUpdated = false;
this.ui.inputBox.select(this._valueSelection && { start: this._valueSelection[0], end: this._valueSelection[1] });
}
if (this.ui.inputBox.placeholder !== (this.placeholder || '')) {
this.ui.inputBox.placeholder = (this.placeholder || '');
}
@@ -599,15 +653,13 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
this.ui.checkAll.checked = this.ui.list.getAllVisibleChecked();
this.ui.visibleCount.setCount(this.ui.list.getVisibleCount());
this.ui.count.setCount(this.ui.list.getCheckedCount());
if (!this.ui.isScreenReaderOptimized() && !this.canSelectMany) {
this.ui.list.focus('First');
}
this.trySelectFirst();
}
if (this.ui.container.classList.contains('show-checkboxes') !== !!this.canSelectMany) {
if (this.canSelectMany) {
this.ui.list.clearFocus();
} else if (!this.ui.isScreenReaderOptimized()) {
this.ui.list.focus('First');
} else {
this.trySelectFirst();
}
}
if (this.activeItemsUpdated) {
@@ -630,11 +682,19 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
this.selectedItemsToConfirm = null;
}
}
if (this.validationMessage) {
this.ui.message.textContent = this.validationMessage;
this.ui.inputBox.showDecoration(Severity.Error);
} else {
this.ui.message.textContent = null;
this.ui.inputBox.showDecoration(Severity.Ignore);
}
this.ui.list.matchOnDescription = this.matchOnDescription;
this.ui.list.matchOnDetail = this.matchOnDetail;
this.ui.list.matchOnLabel = this.matchOnLabel;
this.ui.setComboboxAccessibility(true);
this.ui.inputBox.setAttribute('aria-label', QuickPick.INPUT_BOX_ARIA_LABEL);
this.ui.setVisibilities(this.canSelectMany ? { title: !!this.title || !!this.step, checkAll: true, inputBox: true, visibleCount: true, count: true, ok: true, list: true } : { title: !!this.title || !!this.step, inputBox: true, visibleCount: true, list: true });
this.ui.setVisibilities(this.canSelectMany ? { title: !!this.title || !!this.step, checkAll: true, inputBox: true, visibleCount: true, count: true, ok: true, list: true, message: !!this.validationMessage } : { title: !!this.title || !!this.step, inputBox: true, visibleCount: true, list: true, message: !!this.validationMessage });
}
}
@@ -651,7 +711,7 @@ class InputBox extends QuickInput implements IInputBox {
private noValidationMessage = InputBox.noPromptMessage;
private _validationMessage: string;
private onDidValueChangeEmitter = new Emitter<string>();
private onDidAcceptEmitter = new Emitter<string>();
private onDidAcceptEmitter = new Emitter<void>();
constructor(ui: QuickInputUI) {
super(ui);
@@ -768,13 +828,12 @@ class InputBox extends QuickInput implements IInputBox {
export class QuickInputService extends Component implements IQuickInputService {
public _serviceBrand: any;
public _serviceBrand: ServiceIdentifier<any>;
private static readonly ID = 'workbench.component.quickinput';
private static readonly MAX_WIDTH = 600; // Max total width of quick open widget
private idPrefix = 'quickInput_'; // Constant since there is still only one.
private layoutDimensions: dom.Dimension;
private titleBar: HTMLElement;
private filterContainer: HTMLElement;
private visibleCountContainer: HTMLElement;
@@ -791,24 +850,26 @@ export class QuickInputService extends Component implements IQuickInputService {
private onDidTriggerButtonEmitter = this._register(new Emitter<IQuickInputButton>());
private keyMods: Writeable<IKeyMods> = { ctrlCmd: false, alt: false };
private controller: QuickInput;
private controller: QuickInput | null = null;
constructor(
@IEnvironmentService private readonly environmentService: IEnvironmentService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IPartService private readonly partService: IPartService,
@IQuickOpenService private readonly quickOpenService: IQuickOpenService,
@IEditorGroupsService private readonly editorGroupService: IEditorGroupsService,
@IKeybindingService private readonly keybindingService: IKeybindingService,
@IContextKeyService private readonly contextKeyService: IContextKeyService,
@IThemeService themeService: IThemeService,
@IStorageService storageService: IStorageService
@IStorageService storageService: IStorageService,
@IAccessibilityService private readonly accessibilityService: IAccessibilityService,
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService
) {
super(QuickInputService.ID, themeService, storageService);
this.inQuickOpenContext = new RawContextKey<boolean>('inQuickOpen', false).bindTo(contextKeyService);
this.inQuickOpenContext = InQuickOpenContextKey.bindTo(contextKeyService);
this._register(this.quickOpenService.onShow(() => this.inQuickOpen('quickOpen', true)));
this._register(this.quickOpenService.onHide(() => this.inQuickOpen('quickOpen', false)));
this._register(this.layoutService.onLayout(dimension => this.layout(dimension)));
this.registerKeyModsListeners();
}
@@ -830,7 +891,7 @@ export class QuickInputService extends Component implements IQuickInputService {
}
private setContextKey(id?: string) {
let key: IContextKey<boolean>;
let key: IContextKey<boolean> | undefined;
if (id) {
key = this.contexts[id];
if (!key) {
@@ -860,7 +921,7 @@ export class QuickInputService extends Component implements IQuickInputService {
}
private registerKeyModsListeners() {
const workbench = this.partService.getWorkbenchElement();
const workbench = this.layoutService.getWorkbenchElement();
this._register(dom.addDisposableListener(workbench, dom.EventType.KEY_DOWN, (e: KeyboardEvent) => {
const event = new StandardKeyboardEvent(e);
switch (event.keyCode) {
@@ -892,7 +953,7 @@ export class QuickInputService extends Component implements IQuickInputService {
return;
}
const workbench = this.partService.getWorkbenchElement();
const workbench = this.layoutService.getWorkbenchElement();
const container = dom.append(workbench, $('.quick-input-widget.show-file-icons'));
container.tabIndex = -1;
container.style.display = 'none';
@@ -971,7 +1032,7 @@ export class QuickInputService extends Component implements IQuickInputService {
}));
this._register(list.onDidChangeFocus(() => {
if (this.comboboxAccessibility) {
this.ui.inputBox.setAttribute('aria-activedescendant', this.ui.list.getActiveDescendant());
this.ui.inputBox.setAttribute('aria-activedescendant', this.ui.list.getActiveDescendant() || '');
}
}));
@@ -1060,7 +1121,7 @@ export class QuickInputService extends Component implements IQuickInputService {
return;
}
const input = this.createQuickPick<T>();
let activeItem: T;
let activeItem: T | undefined;
const disposables = [
input,
input.onDidAccept(() => {
@@ -1114,15 +1175,17 @@ export class QuickInputService extends Component implements IQuickInputService {
resolve(undefined);
}),
];
input.canSelectMany = options.canPickMany;
input.canSelectMany = !!options.canPickMany;
input.placeholder = options.placeHolder;
input.ignoreFocusOut = options.ignoreFocusLost;
input.matchOnDescription = options.matchOnDescription;
input.matchOnDetail = options.matchOnDetail;
input.ignoreFocusOut = !!options.ignoreFocusLost;
input.matchOnDescription = !!options.matchOnDescription;
input.matchOnDetail = !!options.matchOnDetail;
input.matchOnLabel = (options.matchOnLabel === undefined) || options.matchOnLabel; // default to true
input.autoFocusOnList = (options.autoFocusOnList === undefined) || options.autoFocusOnList; // default to true
input.quickNavigate = options.quickNavigate;
input.contextKey = options.contextKey;
input.busy = true;
Promise.all([picks, options.activeItem])
Promise.all<QuickPickInput<T>[], T | undefined>([picks, options.activeItem])
.then(([items, _activeItem]) => {
activeItem = _activeItem;
input.busy = false;
@@ -1162,7 +1225,7 @@ export class QuickInputService extends Component implements IQuickInputService {
}
validation.then(result => {
if (value === validationValue) {
input.validationMessage = result;
input.validationMessage = result || undefined;
}
});
}),
@@ -1189,12 +1252,12 @@ export class QuickInputService extends Component implements IQuickInputService {
resolve(undefined);
}),
];
input.value = options.value;
input.value = options.value || '';
input.valueSelection = options.valueSelection;
input.prompt = options.prompt;
input.placeholder = options.placeHolder;
input.password = options.password;
input.ignoreFocusOut = options.ignoreFocusLost;
input.password = !!options.password;
input.ignoreFocusOut = !!options.ignoreFocusLost;
input.show();
});
}
@@ -1236,6 +1299,7 @@ export class QuickInputService extends Component implements IQuickInputService {
this.ui.list.setElements([]);
this.ui.list.matchOnDescription = false;
this.ui.list.matchOnDetail = false;
this.ui.list.matchOnLabel = true;
this.ui.ignoreFocusOut = false;
this.setComboboxAccessibility(false);
this.ui.inputBox.removeAttribute('aria-label');
@@ -1259,7 +1323,7 @@ export class QuickInputService extends Component implements IQuickInputService {
this.countContainer.style.display = visibilities.count ? '' : 'none';
this.okContainer.style.display = visibilities.ok ? '' : 'none';
this.ui.message.style.display = visibilities.message ? '' : 'none';
this.ui.list.display(visibilities.list);
this.ui.list.display(!!visibilities.list);
this.ui.container.classList[visibilities.checkAll ? 'add' : 'remove']('show-checkboxes');
this.updateLayout(); // TODO
}
@@ -1271,7 +1335,7 @@ export class QuickInputService extends Component implements IQuickInputService {
this.ui.inputBox.setAttribute('role', 'combobox');
this.ui.inputBox.setAttribute('aria-haspopup', 'true');
this.ui.inputBox.setAttribute('aria-autocomplete', 'list');
this.ui.inputBox.setAttribute('aria-activedescendant', this.ui.list.getActiveDescendant());
this.ui.inputBox.setAttribute('aria-activedescendant', this.ui.list.getActiveDescendant() || '');
} else {
this.ui.inputBox.removeAttribute('role');
this.ui.inputBox.removeAttribute('aria-haspopup');
@@ -1282,7 +1346,7 @@ export class QuickInputService extends Component implements IQuickInputService {
}
private isScreenReaderOptimized() {
const detected = browser.getAccessibilitySupport() === AccessibilitySupport.Enabled;
const detected = this.accessibilityService.getAccessibilitySupport() === AccessibilitySupport.Enabled;
const config = this.configurationService.getValue<IEditorOptions>('editor').accessibilitySupport;
return config === 'on' || (config === 'auto' && detected);
}
@@ -1354,17 +1418,16 @@ export class QuickInputService extends Component implements IQuickInputService {
}
layout(dimension: dom.Dimension): void {
this.layoutDimensions = dimension;
this.updateLayout();
}
private updateLayout() {
if (this.layoutDimensions && this.ui) {
const titlebarOffset = this.partService.getTitleBarOffset();
if (this.ui) {
const titlebarOffset = this.layoutService.getTitleBarOffset();
this.ui.container.style.top = `${titlebarOffset}px`;
const style = this.ui.container.style;
const width = Math.min(this.layoutDimensions.width * 0.62 /* golden cut */, QuickInputService.MAX_WIDTH);
const width = Math.min(this.layoutService.dimension.width * 0.62 /* golden cut */, QuickInputService.MAX_WIDTH);
style.width = width + 'px';
style.marginLeft = '-' + (width / 2) + 'px';
@@ -1400,7 +1463,7 @@ export const QuickPickManyToggle: ICommandAndKeybindingRule = {
id: 'workbench.action.quickPickManyToggle',
weight: KeybindingWeight.WorkbenchContrib,
when: inQuickOpenContext,
primary: undefined,
primary: 0,
handler: accessor => {
const quickInputService = accessor.get(IQuickInputService);
quickInputService.toggle();
@@ -1418,6 +1481,8 @@ export class BackAction extends Action {
public run(): Promise<any> {
this.quickInputService.back();
return Promise.resolve(null);
return Promise.resolve();
}
}
registerSingleton(IQuickInputService, QuickInputService, true);

View File

@@ -11,6 +11,7 @@ import { ITheme } from 'vs/platform/theme/common/themeService';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import Severity from 'vs/base/common/severity';
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
const $ = dom.$;
@@ -34,6 +35,12 @@ export class QuickInputBox {
});
}
onMouseDown = (handler: (event: StandardMouseEvent) => void): IDisposable => {
return dom.addDisposableListener(this.inputBox.inputElement, dom.EventType.MOUSE_DOWN, (e: MouseEvent) => {
handler(new StandardMouseEvent(e));
});
}
onDidChange = (handler: (event: string) => void): IDisposable => {
return this.inputBox.onDidChange(handler);
}

View File

@@ -220,6 +220,7 @@ export class QuickInputList {
private elementsToIndexes = new Map<IQuickPickItem, number>();
matchOnDescription = false;
matchOnDetail = false;
matchOnLabel = true;
private _onChangedAllVisibleChecked = new Emitter<boolean>();
onChangedAllVisibleChecked: Event<boolean> = this._onChangedAllVisibleChecked.event;
private _onChangedCheckedCount = new Emitter<number>();
@@ -397,6 +398,9 @@ export class QuickInputList {
this.list.setFocus(items
.filter(item => this.elementsToIndexes.has(item))
.map(item => this.elementsToIndexes.get(item)!));
if (items.length > 0) {
this.list.reveal(this.list.getFocus()[0]);
}
}
getActiveDescendant() {
@@ -468,6 +472,9 @@ export class QuickInputList {
}
filter(query: string) {
if (!(this.matchOnLabel || this.matchOnDescription || this.matchOnDetail)) {
return;
}
query = query.trim();
// Reset filtering
@@ -485,7 +492,7 @@ export class QuickInputList {
// Filter by value (since we support octicons, use octicon aware fuzzy matching)
else {
this.elements.forEach(element => {
const labelHighlights = matchesFuzzyOcticonAware(query, parseOcticons(element.saneLabel)) || undefined;
const labelHighlights = this.matchOnLabel ? matchesFuzzyOcticonAware(query, parseOcticons(element.saneLabel)) || undefined : undefined;
const descriptionHighlights = this.matchOnDescription ? matchesFuzzyOcticonAware(query, parseOcticons(element.saneDescription || '')) || undefined : undefined;
const detailHighlights = this.matchOnDetail ? matchesFuzzyOcticonAware(query, parseOcticons(element.saneDetail || '')) || undefined : undefined;

View File

@@ -54,7 +54,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
const registry = Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions);
const globalQuickOpenKeybinding = { primary: KeyMod.CtrlCmd | KeyCode.KEY_P, secondary: [KeyMod.CtrlCmd | KeyCode.KEY_E], mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_P, secondary: null } };
const globalQuickOpenKeybinding = { primary: KeyMod.CtrlCmd | KeyCode.KEY_P, secondary: [KeyMod.CtrlCmd | KeyCode.KEY_E], mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_P, secondary: undefined } };
KeybindingsRegistry.registerKeybindingRule({
id: QUICKOPEN_ACTION_ID,
@@ -96,6 +96,6 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
secondary: [globalQuickOpenKeybinding.secondary[0] | KeyMod.Shift],
mac: {
primary: globalQuickOpenKeybinding.mac.primary | KeyMod.Shift,
secondary: null
secondary: undefined
}
});

View File

@@ -25,12 +25,12 @@ import { IModelService } from 'vs/editor/common/services/modelService';
import { EditorInput, IWorkbenchEditorConfiguration, IEditorInput } from 'vs/workbench/common/editor';
import { Component } from 'vs/workbench/common/component';
import { Event, Emitter } from 'vs/base/common/event';
import { IPartService } from 'vs/workbench/services/part/common/partService';
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
import { QuickOpenHandler, QuickOpenHandlerDescriptor, IQuickOpenRegistry, Extensions, EditorQuickOpenEntry, CLOSE_ON_FOCUS_LOST_CONFIG, SEARCH_EDITOR_HISTORY, PRESERVE_INPUT_CONFIG } from 'vs/workbench/browser/quickopen';
import * as errors from 'vs/base/common/errors';
import { IQuickOpenService, IShowOptions } from 'vs/platform/quickOpen/common/quickOpen';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IInstantiationService, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
import { IContextKeyService, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IHistoryService } from 'vs/workbench/services/history/common/history';
import { IThemeService } from 'vs/platform/theme/common/themeService';
@@ -44,12 +44,13 @@ import { Schemas } from 'vs/base/common/network';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { Dimension, addClass } from 'vs/base/browser/dom';
import { IEditorService, ACTIVE_GROUP, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService';
import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { ILabelService } from 'vs/platform/label/common/label';
import { timeout } from 'vs/base/common/async';
import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cancellation';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
const HELP_PREFIX = '?';
@@ -60,7 +61,7 @@ export class QuickOpenController extends Component implements IQuickOpenService
private static readonly MAX_SHORT_RESPONSE_TIME = 500;
private static readonly ID = 'workbench.component.quickopen';
_serviceBrand: any;
_serviceBrand: ServiceIdentifier<any>;
private readonly _onShow: Emitter<void> = this._register(new Emitter<void>());
get onShow(): Event<void> { return this._onShow.event; }
@@ -73,17 +74,16 @@ export class QuickOpenController extends Component implements IQuickOpenService
private lastInputValue: string;
private lastSubmittedInputValue: string;
private quickOpenWidget: QuickOpenWidget;
private dimension: Dimension;
private mapResolvedHandlersToPrefix: { [prefix: string]: Promise<QuickOpenHandler>; } = Object.create(null);
private mapContextKeyToContext: { [id: string]: IContextKey<boolean>; } = Object.create(null);
private handlerOnOpenCalled: { [prefix: string]: boolean; } = Object.create(null);
private promisesToCompleteOnHide: ValueCallback[] = [];
private previousActiveHandlerDescriptor: QuickOpenHandlerDescriptor;
private previousActiveHandlerDescriptor: QuickOpenHandlerDescriptor | null;
private actionProvider = new ContributableActionProvider();
private closeOnFocusLost: boolean;
private searchInEditorHistory: boolean;
private editorHistoryHandler: EditorHistoryHandler;
private pendingGetResultsInvocation: CancellationTokenSource;
private pendingGetResultsInvocation: CancellationTokenSource | null;
constructor(
@IEditorGroupsService private readonly editorGroupService: IEditorGroupsService,
@@ -91,7 +91,7 @@ export class QuickOpenController extends Component implements IQuickOpenService
@IContextKeyService private readonly contextKeyService: IContextKeyService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IPartService private readonly partService: IPartService,
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,
@IEnvironmentService private readonly environmentService: IEnvironmentService,
@IThemeService themeService: IThemeService,
@IStorageService storageService: IStorageService
@@ -107,8 +107,9 @@ export class QuickOpenController extends Component implements IQuickOpenService
private registerListeners(): void {
this._register(this.configurationService.onDidChangeConfiguration(() => this.updateConfiguration()));
this._register(this.partService.onTitleBarVisibilityChange(() => this.positionQuickOpenWidget()));
this._register(this.layoutService.onTitleBarVisibilityChange(() => this.positionQuickOpenWidget()));
this._register(browser.onDidChangeZoomLevel(() => this.positionQuickOpenWidget()));
this._register(this.layoutService.onLayout(dimension => this.layout(dimension)));
}
private updateConfiguration(): void {
@@ -165,7 +166,7 @@ export class QuickOpenController extends Component implements IQuickOpenService
// Telemetry: log that quick open is shown and log the mode
const registry = Registry.as<IQuickOpenRegistry>(Extensions.Quickopen);
const handlerDescriptor = registry.getQuickOpenHandler(prefix) || registry.getDefaultQuickOpenHandler();
const handlerDescriptor = (prefix ? registry.getQuickOpenHandler(prefix) : undefined) || registry.getDefaultQuickOpenHandler();
// Trigger onOpen
this.resolveHandler(handlerDescriptor);
@@ -173,7 +174,7 @@ export class QuickOpenController extends Component implements IQuickOpenService
// Create upon first open
if (!this.quickOpenWidget) {
this.quickOpenWidget = this._register(new QuickOpenWidget(
this.partService.getWorkbenchElement(),
this.layoutService.getWorkbenchElement(),
{
onOk: () => this.onOk(),
onCancel: () => { /* ignore */ },
@@ -195,9 +196,7 @@ export class QuickOpenController extends Component implements IQuickOpenService
}
// Layout
if (this.dimension) {
this.quickOpenWidget.layout(this.dimension);
}
this.quickOpenWidget.layout(this.layoutService.dimension);
// Show quick open with prefix or editor history
if (!this.quickOpenWidget.isVisible() || quickNavigateConfiguration) {
@@ -206,7 +205,7 @@ export class QuickOpenController extends Component implements IQuickOpenService
} else {
const editorHistory = this.getEditorHistoryWithGroupLabel();
if (editorHistory.getEntries().length < 2) {
quickNavigateConfiguration = null; // If no entries can be shown, default to normal quick open mode
quickNavigateConfiguration = undefined; // If no entries can be shown, default to normal quick open mode
}
// Compute auto focus
@@ -239,7 +238,7 @@ export class QuickOpenController extends Component implements IQuickOpenService
}
private positionQuickOpenWidget(): void {
const titlebarOffset = this.partService.getTitleBarOffset();
const titlebarOffset = this.layoutService.getTitleBarOffset();
if (this.quickOpenWidget) {
this.quickOpenWidget.getElement().style.top = `${titlebarOffset}px`;
@@ -270,7 +269,10 @@ export class QuickOpenController extends Component implements IQuickOpenService
// Complete promises that are waiting
while (this.promisesToCompleteOnHide.length) {
this.promisesToCompleteOnHide.pop()(true);
const callback = this.promisesToCompleteOnHide.pop();
if (callback) {
callback(true);
}
}
if (reason !== HideReason.FOCUS_LOST) {
@@ -297,7 +299,7 @@ export class QuickOpenController extends Component implements IQuickOpenService
}
private setQuickOpenContextKey(id?: string): void {
let key: IContextKey<boolean>;
let key: IContextKey<boolean> | undefined;
if (id) {
key = this.mapContextKeyToContext[id];
if (!key) {
@@ -479,13 +481,13 @@ export class QuickOpenController extends Component implements IQuickOpenService
// merge history and default handler results
const handlerResults = (result && result.entries) || [];
this.mergeResults(quickOpenModel, handlerResults, resolvedHandler.getGroupLabel());
this.mergeResults(quickOpenModel, handlerResults, types.withNullAsUndefined(resolvedHandler.getGroupLabel()));
}
});
});
}
private mergeResults(quickOpenModel: QuickOpenModel, handlerResults: QuickOpenEntry[], groupLabel: string): void {
private mergeResults(quickOpenModel: QuickOpenModel, handlerResults: QuickOpenEntry[], groupLabel: string | undefined): void {
// Remove results already showing by checking for a "resource" property
const mapEntryToResource = this.mapEntriesToResource(quickOpenModel);
@@ -526,7 +528,7 @@ export class QuickOpenController extends Component implements IQuickOpenService
const placeHolderLabel = (typeof canRun === 'string') ? canRun : nls.localize('canNotRunPlaceholder', "This quick open handler can not be used in the current context");
const model = new QuickOpenModel([new PlaceholderQuickOpenEntry(placeHolderLabel)], this.actionProvider);
this.showModel(model, resolvedHandler.getAutoFocus(value, { model, quickNavigateConfiguration: this.quickOpenWidget.getQuickNavigateConfiguration() }), resolvedHandler.getAriaLabel());
this.showModel(model, resolvedHandler.getAutoFocus(value, { model, quickNavigateConfiguration: this.quickOpenWidget.getQuickNavigateConfiguration() }), types.withNullAsUndefined(resolvedHandler.getAriaLabel()));
return Promise.resolve(undefined);
}
@@ -547,9 +549,9 @@ export class QuickOpenController extends Component implements IQuickOpenService
if (!token.isCancellationRequested) {
if (!result || !result.entries.length) {
const model = new QuickOpenModel([new PlaceholderQuickOpenEntry(resolvedHandler.getEmptyLabel(value))]);
this.showModel(model, resolvedHandler.getAutoFocus(value, { model, quickNavigateConfiguration: this.quickOpenWidget.getQuickNavigateConfiguration() }), resolvedHandler.getAriaLabel());
this.showModel(model, resolvedHandler.getAutoFocus(value, { model, quickNavigateConfiguration: this.quickOpenWidget.getQuickNavigateConfiguration() }), types.withNullAsUndefined(resolvedHandler.getAriaLabel()));
} else {
this.showModel(result, resolvedHandler.getAutoFocus(value, { model: result, quickNavigateConfiguration: this.quickOpenWidget.getQuickNavigateConfiguration() }), resolvedHandler.getAriaLabel());
this.showModel(result, resolvedHandler.getAutoFocus(value, { model: result, quickNavigateConfiguration: this.quickOpenWidget.getQuickNavigateConfiguration() }), types.withNullAsUndefined(resolvedHandler.getAriaLabel()));
}
}
});
@@ -570,15 +572,16 @@ export class QuickOpenController extends Component implements IQuickOpenService
}
private clearModel(): void {
this.showModel(new QuickOpenModel(), null);
this.showModel(new QuickOpenModel(), undefined);
}
private mapEntriesToResource(model: QuickOpenModel): { [resource: string]: QuickOpenEntry; } {
const entries = model.getEntries();
const mapEntryToPath: { [path: string]: QuickOpenEntry; } = {};
entries.forEach((entry: QuickOpenEntry) => {
if (entry.getResource()) {
mapEntryToPath[entry.getResource().toString()] = entry;
const resource = entry.getResource();
if (resource) {
mapEntryToPath[resource.toString()] = entry;
}
});
@@ -620,9 +623,8 @@ export class QuickOpenController extends Component implements IQuickOpenService
}
layout(dimension: Dimension): void {
this.dimension = dimension;
if (this.quickOpenWidget) {
this.quickOpenWidget.layout(this.dimension);
this.quickOpenWidget.layout(dimension);
}
}
}
@@ -655,7 +657,7 @@ class EditorHistoryHandler {
getResults(searchValue?: string, token?: CancellationToken): QuickOpenEntry[] {
// Massage search for scoring
const query = prepareQuery(searchValue);
const query = prepareQuery(searchValue || '');
// Just return all if we are not searching
const history = this.historyService.getHistory();
@@ -670,7 +672,7 @@ class EditorHistoryHandler {
// For now, only support to match on inputs that provide resource information
.filter(input => {
let resource: URI;
let resource: URI | undefined;
if (input instanceof EditorInput) {
resource = resourceForEditorHistory(input, this.fileService);
} else {
@@ -690,7 +692,7 @@ class EditorHistoryHandler {
return false;
}
e.setHighlights(itemScore.labelMatch, itemScore.descriptionMatch);
e.setHighlights(itemScore.labelMatch || [], itemScore.descriptionMatch);
return true;
})
@@ -707,8 +709,8 @@ class EditorHistoryItemAccessorClass extends QuickOpenItemAccessorClass {
super();
}
getItemDescription(entry: QuickOpenEntry): string {
return this.allowMatchOnDescription ? entry.getDescription() : undefined;
getItemDescription(entry: QuickOpenEntry): string | null {
return this.allowMatchOnDescription ? entry.getDescription() : null;
}
}
@@ -721,9 +723,9 @@ export class EditorHistoryEntryGroup extends QuickOpenEntryGroup {
export class EditorHistoryEntry extends EditorQuickOpenEntry {
private input: IEditorInput | IResourceInput;
private resource: URI;
private label: string;
private description: string;
private resource: URI | undefined;
private label: string | null;
private description: string | null;
private dirty: boolean;
constructor(
@@ -762,7 +764,7 @@ export class EditorHistoryEntry extends EditorQuickOpenEntry {
return this.dirty ? 'dirty' : '';
}
getLabel(): string {
getLabel(): string | null {
return this.label;
}
@@ -776,12 +778,12 @@ export class EditorHistoryEntry extends EditorQuickOpenEntry {
return nls.localize('entryAriaLabel', "{0}, recently opened", this.getLabel());
}
getDescription(): string {
getDescription(): string | null {
return this.description;
}
getResource(): URI {
return this.resource;
getResource(): URI | null {
return this.resource || null;
}
getInput(): IEditorInput | IResourceInput {
@@ -806,7 +808,7 @@ export class EditorHistoryEntry extends EditorQuickOpenEntry {
}
}
function resourceForEditorHistory(input: EditorInput, fileService: IFileService): URI {
function resourceForEditorHistory(input: EditorInput, fileService: IFileService): URI | undefined {
const resource = input ? input.getResource() : undefined;
// For the editor history we only prefer resources that are either untitled or
@@ -846,7 +848,7 @@ export class RemoveFromEditorHistoryAction extends Action {
return <IHistoryPickEntry>{
input: h,
iconClasses: getIconClasses(this.modelService, this.modeService, entry.getResource()),
iconClasses: getIconClasses(this.modelService, this.modeService, entry.getResource() || undefined),
label: entry.getLabel(),
description: entry.getDescription()
};
@@ -859,3 +861,5 @@ export class RemoveFromEditorHistoryAction extends Action {
});
}
}
registerSingleton(IQuickOpenService, QuickOpenController, true);

View File

@@ -8,23 +8,37 @@ import { Action } from 'vs/base/common/actions';
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { ContextKeyExpr, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
import { ICommandHandler, CommandsRegistry } from 'vs/platform/commands/common/commands';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
export const inQuickOpenContext = ContextKeyExpr.has('inQuickOpen');
const inQuickOpenKey = 'inQuickOpen';
export const InQuickOpenContextKey = new RawContextKey<boolean>(inQuickOpenKey, false);
export const inQuickOpenContext = ContextKeyExpr.has(inQuickOpenKey);
export const defaultQuickOpenContextKey = 'inFilesPicker';
export const defaultQuickOpenContext = ContextKeyExpr.and(inQuickOpenContext, ContextKeyExpr.has(defaultQuickOpenContextKey));
export const QUICKOPEN_ACTION_ID = 'workbench.action.quickOpen';
export const QUICKOPEN_ACION_LABEL = nls.localize('quickOpen', "Go to File...");
CommandsRegistry.registerCommand(QUICKOPEN_ACTION_ID, function (accessor: ServicesAccessor, prefix: string | null = null) {
const quickOpenService = accessor.get(IQuickOpenService);
CommandsRegistry.registerCommand({
id: QUICKOPEN_ACTION_ID,
handler: function (accessor: ServicesAccessor, prefix: string | null = null) {
const quickOpenService = accessor.get(IQuickOpenService);
return quickOpenService.show(typeof prefix === 'string' ? prefix : undefined).then(() => {
return undefined;
});
return quickOpenService.show(typeof prefix === 'string' ? prefix : undefined).then(() => {
return undefined;
});
},
description: {
description: `Quick open`,
args: [{
name: 'prefix',
schema: {
'type': 'string'
}
}]
}
});
export const QUICKOPEN_FOCUS_SECONDARY_ACTION_ID = 'workbench.action.quickOpenPreviousEditor';

View File

@@ -12,59 +12,64 @@ import { Viewlet, ViewletRegistry, Extensions as ViewletExtensions, ViewletDescr
import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions';
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import { IPartService, Parts, Position as SideBarPosition } from 'vs/workbench/services/part/common/partService';
import { IViewlet } from 'vs/workbench/common/viewlet';
import { IWorkbenchLayoutService, Parts, Position as SideBarPosition } from 'vs/workbench/services/layout/browser/layoutService';
import { IViewlet, SidebarFocusContext, ActiveViewletContext } from 'vs/workbench/common/viewlet';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IInstantiationService, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
import { Event, Emitter } from 'vs/base/common/event';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { contrastBorder } from 'vs/platform/theme/common/colorRegistry';
import { SIDE_BAR_TITLE_FOREGROUND, SIDE_BAR_BACKGROUND, SIDE_BAR_FOREGROUND, SIDE_BAR_BORDER } from 'vs/workbench/common/theme';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { EventType, addDisposableListener, trackFocus, Dimension } from 'vs/base/browser/dom';
import { EventType, addDisposableListener, trackFocus } from 'vs/base/browser/dom';
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
import { RawContextKey, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { ISerializableView } from 'vs/base/browser/ui/grid/grid';
import { LayoutPriority } from 'vs/base/browser/ui/grid/gridview';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
export const SidebarFocusContext = new RawContextKey<boolean>('sideBarFocus', false);
export const ActiveViewletContext = new RawContextKey<string>('activeViewlet', '');
export class SidebarPart extends CompositePart<Viewlet> implements IViewletService {
export class SidebarPart extends CompositePart<Viewlet> implements ISerializableView, IViewletService {
_serviceBrand: any;
_serviceBrand: ServiceIdentifier<any>;
static readonly activeViewletSettingsKey = 'workbench.sidebar.activeviewletid';
//#region IView
readonly minimumWidth: number = 170;
readonly maximumWidth: number = Number.POSITIVE_INFINITY;
readonly minimumHeight: number = 0;
readonly maximumHeight: number = Number.POSITIVE_INFINITY;
readonly snapSize: number = 50;
readonly priority: LayoutPriority = LayoutPriority.Low;
//#endregion
get onDidViewletRegister(): Event<ViewletDescriptor> { return <Event<ViewletDescriptor>>this.viewletRegistry.onDidRegister; }
private _onDidViewletDeregister = this._register(new Emitter<ViewletDescriptor>());
get onDidViewletDeregister(): Event<ViewletDescriptor> { return this._onDidViewletDeregister.event; }
get onDidViewletOpen(): Event<IViewlet> { return Event.map(this.onDidCompositeOpen.event, compositeEvent => <IViewlet>compositeEvent.composite); }
get onDidViewletClose(): Event<IViewlet> { return this.onDidCompositeClose.event as Event<IViewlet>; }
private viewletRegistry: ViewletRegistry;
private sideBarFocusContextKey: IContextKey<boolean>;
private activeViewletContextKey: IContextKey<string>;
private blockOpeningViewlet: boolean;
private _onDidViewletDeregister = this._register(new Emitter<ViewletDescriptor>());
element: HTMLElement;
minimumWidth: number = 170;
maximumWidth: number = Number.POSITIVE_INFINITY;
minimumHeight: number = 0;
maximumHeight: number = Number.POSITIVE_INFINITY;
snapSize: number = 50;
priority: LayoutPriority = LayoutPriority.Low;
private _onDidChange = new Emitter<{ width: number; height: number; }>();
readonly onDidChange = this._onDidChange.event;
constructor(
id: string,
@INotificationService notificationService: INotificationService,
@IStorageService storageService: IStorageService,
@ITelemetryService telemetryService: ITelemetryService,
@IContextMenuService contextMenuService: IContextMenuService,
@IPartService partService: IPartService,
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
@IKeybindingService keybindingService: IKeybindingService,
@IInstantiationService instantiationService: IInstantiationService,
@IThemeService themeService: IThemeService,
@@ -76,7 +81,7 @@ export class SidebarPart extends CompositePart<Viewlet> implements ISerializable
storageService,
telemetryService,
contextMenuService,
partService,
layoutService,
keybindingService,
instantiationService,
themeService,
@@ -86,56 +91,51 @@ export class SidebarPart extends CompositePart<Viewlet> implements ISerializable
'sideBar',
'viewlet',
SIDE_BAR_TITLE_FOREGROUND,
id,
Parts.SIDEBAR_PART,
{ hasTitle: true, borderWidth: () => (this.getColor(SIDE_BAR_BORDER) || this.getColor(contrastBorder)) ? 1 : 0 }
);
this.sideBarFocusContextKey = SidebarFocusContext.bindTo(contextKeyService);
this.viewletRegistry = Registry.as<ViewletRegistry>(ViewletExtensions.Viewlets);
this.sideBarFocusContextKey = SidebarFocusContext.bindTo(contextKeyService);
this.activeViewletContextKey = ActiveViewletContext.bindTo(contextKeyService);
this.registerListeners();
}
private registerListeners(): void {
// Viewlet open
this._register(this.onDidViewletOpen(viewlet => {
this.activeViewletContextKey.set(viewlet.getId());
}));
// Viewlet close
this._register(this.onDidViewletClose(viewlet => {
if (this.activeViewletContextKey.get() === viewlet.getId()) {
this.activeViewletContextKey.reset();
}
}));
// Viewlet deregister
this._register(this.registry.onDidDeregister(async (viewletDescriptor: ViewletDescriptor) => {
if (this.getActiveViewlet().getId() === viewletDescriptor.id) {
await this.openViewlet(this.getDefaultViewletId());
}
this.removeComposite(viewletDescriptor.id);
this._onDidViewletDeregister.fire(viewletDescriptor);
}));
}
get onDidViewletRegister(): Event<ViewletDescriptor> { return <Event<ViewletDescriptor>>this.viewletRegistry.onDidRegister; }
get onDidViewletDeregister(): Event<ViewletDescriptor> { return this._onDidViewletDeregister.event; }
get onDidViewletOpen(): Event<IViewlet> {
return Event.map(this._onDidCompositeOpen.event, compositeEvent => <IViewlet>compositeEvent.composite);
}
get onDidViewletClose(): Event<IViewlet> {
return this._onDidCompositeClose.event as Event<IViewlet>;
}
create(parent: HTMLElement): void {
this.element = parent;
super.create(parent);
const focusTracker = trackFocus(parent);
focusTracker.onDidFocus(() => {
this.sideBarFocusContextKey.set(true);
});
focusTracker.onDidBlur(() => {
this.sideBarFocusContextKey.set(false);
});
const focusTracker = this._register(trackFocus(parent));
this._register(focusTracker.onDidFocus(() => this.sideBarFocusContextKey.set(true)));
this._register(focusTracker.onDidBlur(() => this.sideBarFocusContextKey.set(false)));
}
createTitleArea(parent: HTMLElement): HTMLElement {
@@ -158,7 +158,7 @@ export class SidebarPart extends CompositePart<Viewlet> implements ISerializable
container.style.color = this.getColor(SIDE_BAR_FOREGROUND);
const borderColor = this.getColor(SIDE_BAR_BORDER) || this.getColor(contrastBorder);
const isPositionLeft = this.partService.getSideBarPosition() === SideBarPosition.LEFT;
const isPositionLeft = this.layoutService.getSideBarPosition() === SideBarPosition.LEFT;
container.style.borderRightWidth = borderColor && isPositionLeft ? '1px' : null;
container.style.borderRightStyle = borderColor && isPositionLeft ? 'solid' : null;
container.style.borderRightColor = isPositionLeft ? borderColor : null;
@@ -167,22 +167,12 @@ export class SidebarPart extends CompositePart<Viewlet> implements ISerializable
container.style.borderLeftColor = !isPositionLeft ? borderColor : null;
}
layout(dimension: Dimension): Dimension[];
layout(width: number, height: number): void;
layout(dim1: Dimension | number, dim2?: number): Dimension[] | void {
if (!this.partService.isVisible(Parts.SIDEBAR_PART)) {
if (dim1 instanceof Dimension) {
return [dim1];
}
layout(width: number, height: number): void {
if (!this.layoutService.isVisible(Parts.SIDEBAR_PART)) {
return;
}
if (dim1 instanceof Dimension) {
return super.layout(dim1);
}
super.layout(dim1, dim2!);
super.layout(width, height);
}
// Viewlet service
@@ -199,15 +189,17 @@ export class SidebarPart extends CompositePart<Viewlet> implements ISerializable
this.hideActiveComposite();
}
openViewlet(id: string, focus?: boolean): Promise<IViewlet | null> {
if (this.getViewlet(id)) {
openViewlet(id: string | undefined, focus?: boolean): Promise<IViewlet | null> {
if (typeof id === 'string' && this.getViewlet(id)) {
return Promise.resolve(this.doOpenViewlet(id, focus));
}
return this.extensionService.whenInstalledExtensionsRegistered()
.then(() => {
if (this.getViewlet(id)) {
if (typeof id === 'string' && this.getViewlet(id)) {
return this.doOpenViewlet(id, focus);
}
return null;
});
}
@@ -231,10 +223,10 @@ export class SidebarPart extends CompositePart<Viewlet> implements ISerializable
}
// First check if sidebar is hidden and show if so
if (!this.partService.isVisible(Parts.SIDEBAR_PART)) {
if (!this.layoutService.isVisible(Parts.SIDEBAR_PART)) {
try {
this.blockOpeningViewlet = true;
this.partService.setSideBarHidden(false);
this.layoutService.setSideBarHidden(false);
} finally {
this.blockOpeningViewlet = false;
}
@@ -244,7 +236,7 @@ export class SidebarPart extends CompositePart<Viewlet> implements ISerializable
}
protected getTitleAreaDropDownAnchorAlignment(): AnchorAlignment {
return this.partService.getSideBarPosition() === SideBarPosition.LEFT ? AnchorAlignment.LEFT : AnchorAlignment.RIGHT;
return this.layoutService.getSideBarPosition() === SideBarPosition.LEFT ? AnchorAlignment.LEFT : AnchorAlignment.RIGHT;
}
private onTitleAreaContextMenu(event: StandardMouseEvent): void {
@@ -279,7 +271,7 @@ class FocusSideBarAction extends Action {
id: string,
label: string,
@IViewletService private readonly viewletService: IViewletService,
@IPartService private readonly partService: IPartService
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService
) {
super(id, label);
}
@@ -287,8 +279,8 @@ class FocusSideBarAction extends Action {
run(): Promise<any> {
// Show side bar
if (!this.partService.isVisible(Parts.SIDEBAR_PART)) {
return Promise.resolve(this.partService.setSideBarHidden(false));
if (!this.layoutService.isVisible(Parts.SIDEBAR_PART)) {
return Promise.resolve(this.layoutService.setSideBarHidden(false));
}
// Focus into active viewlet
@@ -305,3 +297,5 @@ const registry = Registry.as<IWorkbenchActionRegistry>(ActionExtensions.Workbenc
registry.registerWorkbenchAction(new SyncActionDescriptor(FocusSideBarAction, FocusSideBarAction.ID, FocusSideBarAction.LABEL, {
primary: KeyMod.CtrlCmd | KeyCode.KEY_0
}), 'View: Focus into Side Bar', nls.localize('viewCategory', "View"));
registerSingleton(IViewletService, SidebarPart);

View File

@@ -33,25 +33,30 @@
border-right: 5px solid transparent;
}
.monaco-workbench .part.statusbar > .statusbar-item.left > :first-child {
margin-right: 5px;
.monaco-workbench .part.statusbar > .statusbar-item.left > :first-child,
.monaco-workbench .part.statusbar > .statusbar-item.right > :first-child
{
margin-right: 3px;
margin-left: 3px;
}
.monaco-workbench .part.statusbar > .statusbar-item.right {
float: right;
}
.monaco-workbench .part.statusbar > .statusbar-item.right > :first-child {
margin-left: 5px;
}
/* adding padding to the most left status bar item */
.monaco-workbench .part.statusbar > .statusbar-item.left:first-child, .monaco-workbench .part.statusbar > .statusbar-item.right + .statusbar-item.left {
padding-left: 10px;
padding-left: 7px;
}
.monaco-workbench .part.statusbar > .statusbar-item.has-background-color.left:first-child, .monaco-workbench .part.statusbar > .statusbar-item.right + .statusbar-item.has-background-color.left {
padding-right: 7px; /* expand padding if background color is configured for the status bar entry to make it look centered properly */
}
/* adding padding to the most right status bar item */
.monaco-workbench .part.statusbar > .statusbar-item.right:first-child {
padding-right: 10px;
padding-right: 7px;
}
.monaco-workbench .part.statusbar > .statusbar-item.has-background-color.right:first-child {
padding-left: 7px; /* expand padding if background color is configured for the status bar entry to make it look centered properly */
}
.monaco-workbench .part.statusbar > .statusbar-item a {

View File

@@ -6,60 +6,57 @@
import 'vs/css!./media/statusbarpart';
import * as nls from 'vs/nls';
import { toErrorMessage } from 'vs/base/common/errorMessage';
import { dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { dispose, IDisposable, toDisposable, combinedDisposable } from 'vs/base/common/lifecycle';
import { OcticonLabel } from 'vs/base/browser/ui/octiconLabel/octiconLabel';
import { Registry } from 'vs/platform/registry/common/platform';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { Part } from 'vs/workbench/browser/part';
import { IStatusbarRegistry, Extensions, IStatusbarItem } from 'vs/workbench/browser/parts/statusbar/statusbar';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IInstantiationService, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { StatusbarAlignment, IStatusbarService, IStatusbarEntry } from 'vs/platform/statusbar/common/statusbar';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { Action } from 'vs/base/common/actions';
import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService';
import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector, ThemeColor } from 'vs/platform/theme/common/themeService';
import { STATUS_BAR_BACKGROUND, STATUS_BAR_FOREGROUND, STATUS_BAR_NO_FOLDER_BACKGROUND, STATUS_BAR_ITEM_HOVER_BACKGROUND, STATUS_BAR_ITEM_ACTIVE_BACKGROUND, STATUS_BAR_PROMINENT_ITEM_BACKGROUND, STATUS_BAR_PROMINENT_ITEM_HOVER_BACKGROUND, STATUS_BAR_BORDER, STATUS_BAR_NO_FOLDER_FOREGROUND, STATUS_BAR_NO_FOLDER_BORDER } from 'vs/workbench/common/theme';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { contrastBorder } from 'vs/platform/theme/common/colorRegistry';
import { isThemeColor } from 'vs/editor/common/editorCommon';
import { Color } from 'vs/base/common/color';
import { addClass, EventHelper, createStyleSheet, addDisposableListener, Dimension } from 'vs/base/browser/dom';
import { addClass, EventHelper, createStyleSheet, addDisposableListener } from 'vs/base/browser/dom';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { Emitter } from 'vs/base/common/event';
import { ISerializableView } from 'vs/base/browser/ui/grid/grid';
import { Parts } from 'vs/workbench/services/part/common/partService';
import { Parts, IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
export class StatusbarPart extends Part implements IStatusbarService {
export class StatusbarPart extends Part implements IStatusbarService, ISerializableView {
_serviceBrand: any;
_serviceBrand: ServiceIdentifier<any>;
private static readonly PRIORITY_PROP = 'statusbar-entry-priority';
private static readonly ALIGNMENT_PROP = 'statusbar-entry-alignment';
element: HTMLElement;
//#region IView
readonly minimumWidth: number = 0;
readonly maximumWidth: number = Number.POSITIVE_INFINITY;
readonly minimumHeight: number = 22;
readonly maximumHeight: number = 22;
//#endregion
private statusMsgDispose: IDisposable;
minimumWidth: number = 0;
maximumWidth: number = Number.POSITIVE_INFINITY;
minimumHeight: number = 22;
maximumHeight: number = 22;
private _onDidChange = new Emitter<{ width: number; height: number; }>();
readonly onDidChange = this._onDidChange.event;
private styleElement: HTMLStyleElement;
constructor(
id: string,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IThemeService themeService: IThemeService,
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
@IStorageService storageService: IStorageService
@IStorageService storageService: IStorageService,
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService
) {
super(id, { hasTitle: false }, themeService, storageService);
super(Parts.STATUSBAR_PART, { hasTitle: false }, themeService, storageService, layoutService);
this.registerListeners();
}
@@ -152,7 +149,7 @@ export class StatusbarPart extends Part implements IStatusbarService, ISerializa
return this.element;
}
protected updateStyles(): void {
updateStyles(): void {
super.updateStyles();
const container = this.getContainer();
@@ -233,14 +230,8 @@ export class StatusbarPart extends Part implements IStatusbarService, ISerializa
return dispose;
}
layout(dimension: Dimension): Dimension[];
layout(width: number, height: number): void;
layout(dim1: Dimension | number, dim2?: number): Dimension[] | void {
if (dim1 instanceof Dimension) {
return super.layout(dim1);
} else {
super.layout(new Dimension(dim1, dim2!));
}
layout(width: number, height: number): void {
super.layoutContents(width, height);
}
toJSON(): object {
@@ -292,18 +283,13 @@ class StatusBarEntryItem implements IStatusbarItem {
textContainer.title = this.entry.tooltip;
}
// Color
let color = this.entry.color;
if (color) {
if (isThemeColor(color)) {
let colorId = color.id;
color = (this.themeService.getTheme().getColor(colorId) || Color.transparent).toString();
toDispose.push(this.themeService.onThemeChange(theme => {
let colorValue = (this.themeService.getTheme().getColor(colorId) || Color.transparent).toString();
textContainer.style.color = colorValue;
}));
}
textContainer.style.color = color;
// Color (only applies to text container)
toDispose.push(this.applyColor(textContainer, this.entry.color));
// Background Color (applies to parent element to fully fill container)
if (this.entry.backgroundColor) {
toDispose.push(this.applyColor(el, this.entry.backgroundColor, true));
addClass(el, 'has-background-color');
}
// Context Menu
@@ -328,6 +314,24 @@ class StatusBarEntryItem implements IStatusbarItem {
};
}
private applyColor(container: HTMLElement, color: string | ThemeColor | undefined, isBackground?: boolean): IDisposable {
const disposable: IDisposable[] = [];
if (color) {
if (isThemeColor(color)) {
const colorId = color.id;
color = (this.themeService.getTheme().getColor(colorId) || Color.transparent).toString();
disposable.push(this.themeService.onThemeChange(theme => {
const colorValue = (theme.getColor(colorId) || Color.transparent).toString();
isBackground ? container.style.backgroundColor = colorValue : container.style.color = colorValue;
}));
}
isBackground ? container.style.backgroundColor = color : container.style.color = color;
}
return combinedDisposable(disposable);
}
private executeCommand(id: string, args?: any[]) {
args = args || [];
@@ -382,3 +386,5 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
collector.addRule(`.monaco-workbench .part.statusbar > .statusbar-item a.status-bar-info:hover { background-color: ${statusBarProminentItemHoverBackground}; }`);
}
});
registerSingleton(IStatusbarService, StatusbarPart);

View File

@@ -42,8 +42,8 @@
/* Windows/Linux: Rules for custom title (icon, window controls) */
.windows > .monaco-workbench .part.titlebar,
.linux > .monaco-workbench .part.titlebar {
.monaco-workbench.windows .part.titlebar,
.monaco-workbench.linux .part.titlebar {
padding: 0;
height: 30px;
line-height: 30px;
@@ -51,17 +51,17 @@
overflow: visible;
}
.windows > .monaco-workbench .part.titlebar > .window-title,
.linux > .monaco-workbench .part.titlebar > .window-title {
.monaco-workbench.windows .part.titlebar > .window-title,
.monaco-workbench.linux .part.titlebar > .window-title {
cursor: default;
}
.linux > .monaco-workbench .part.titlebar > .window-title {
.monaco-workbench.linux .part.titlebar > .window-title {
font-size: inherit;
}
.windows > .monaco-workbench .part.titlebar > .resizer,
.linux > .monaco-workbench .part.titlebar > .resizer {
.monaco-workbench.windows .part.titlebar > .resizer,
.monaco-workbench.linux .part.titlebar > .resizer {
-webkit-app-region: no-drag;
position: absolute;
top: 0;
@@ -69,17 +69,16 @@
height: 20%;
}
.windows > .monaco-workbench.fullscreen .part.titlebar > .resizer,
.linux > .monaco-workbench.fullscreen .part.titlebar > .resizer {
.monaco-workbench.windows.fullscreen .part.titlebar > .resizer,
.monaco-workbench.linux.fullscreen .part.titlebar > .resizer {
display: none;
}
.monaco-workbench .part.titlebar > .window-appicon {
width: 35px;
height: 100%;
position: relative;
z-index: 99;
z-index: 3000;
background-image: url('code-icon.svg');
background-repeat: no-repeat;
background-position: center center;
@@ -97,7 +96,7 @@
flex-shrink: 0;
text-align: center;
position: relative;
z-index: 99;
z-index: 3000;
-webkit-app-region: no-drag;
height: 100%;
width: 138px;

View File

@@ -7,7 +7,7 @@ import * as nls from 'vs/nls';
import { IMenubarMenu, IMenubarMenuItemAction, IMenubarMenuItemSubmenu, IMenubarKeybinding, IMenubarService, IMenubarData, MenubarMenuItem } from 'vs/platform/menubar/common/menubar';
import { IMenuService, MenuId, IMenu, SubmenuItemAction } from 'vs/platform/actions/common/actions';
import { registerThemingParticipant, ITheme, ICssStyleCollector, IThemeService } from 'vs/platform/theme/common/themeService';
import { IWindowService, MenuBarVisibility, IWindowsService, getTitleBarStyle } from 'vs/platform/windows/common/windows';
import { IWindowService, MenuBarVisibility, IWindowsService, getTitleBarStyle, URIType } from 'vs/platform/windows/common/windows';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IAction, Action } from 'vs/base/common/actions';
import { Separator } from 'vs/base/browser/ui/actionbar/actionbar';
@@ -17,8 +17,7 @@ import { isMacintosh, isLinux } from 'vs/base/common/platform';
import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration';
import { Event, Emitter } from 'vs/base/common/event';
import { Disposable } from 'vs/base/common/lifecycle';
import { IRecentlyOpened } from 'vs/platform/history/common/history';
import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
import { IRecentlyOpened, isRecentFolder, IRecent, isRecentWorkspace } from 'vs/platform/history/common/history';
import { RunOnceScheduler } from 'vs/base/common/async';
import { MENUBAR_SELECTION_FOREGROUND, MENUBAR_SELECTION_BACKGROUND, MENUBAR_SELECTION_BORDER, TITLE_BAR_ACTIVE_FOREGROUND, TITLE_BAR_INACTIVE_FOREGROUND } from 'vs/workbench/common/theme';
import { URI } from 'vs/base/common/uri';
@@ -32,6 +31,9 @@ import { MenuBar } from 'vs/base/browser/ui/menu/menubar';
import { SubmenuAction } from 'vs/base/browser/ui/menu/menu';
import { attachMenuStyler } from 'vs/platform/theme/common/styler';
import { assign } from 'vs/base/common/objects';
import { mnemonicMenuLabel, unmnemonicLabel } from 'vs/base/common/labels';
import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility';
import { withNullAsUndefined } from 'vs/base/common/types';
export class MenubarControl extends Disposable {
@@ -57,7 +59,7 @@ export class MenubarControl extends Disposable {
// 'Terminal': IMenu;
'Window'?: IMenu;
'Help': IMenu;
// [index: string]: IMenu;
// [index: string]: IMenu | undefined;
};
// {{SQL CARBON EDIT}} - Disable unused menus
@@ -76,9 +78,10 @@ export class MenubarControl extends Disposable {
private menuUpdater: RunOnceScheduler;
private container: HTMLElement;
private recentlyOpened: IRecentlyOpened;
private alwaysOnMnemonics: boolean;
private _onVisibilityChange: Emitter<boolean>;
private _onFocusStateChange: Emitter<boolean>;
private readonly _onVisibilityChange: Emitter<boolean>;
private readonly _onFocusStateChange: Emitter<boolean>;
private static MAX_MENU_RECENT_ENTRIES = 10;
@@ -96,7 +99,8 @@ export class MenubarControl extends Disposable {
@IStorageService private readonly storageService: IStorageService,
@INotificationService private readonly notificationService: INotificationService,
@IPreferencesService private readonly preferencesService: IPreferencesService,
@IEnvironmentService private readonly environmentService: IEnvironmentService
@IEnvironmentService private readonly environmentService: IEnvironmentService,
@IAccessibilityService private readonly accessibilityService: IAccessibilityService
) {
super();
@@ -120,8 +124,11 @@ export class MenubarControl extends Disposable {
this.menuUpdater = this._register(new RunOnceScheduler(() => this.doUpdateMenubar(false), 200));
if (isMacintosh || this.currentTitlebarStyleSetting !== 'custom') {
for (let topLevelMenuName of Object.keys(this.topLevelMenus)) {
this._register(this.topLevelMenus[topLevelMenuName].onDidChange(() => this.updateMenubar()));
for (const topLevelMenuName of Object.keys(this.topLevelMenus)) {
const menu = this.topLevelMenus[topLevelMenuName];
if (menu) {
this._register(menu.onDidChange(() => this.updateMenubar()));
}
}
this.doUpdateMenubar(true);
@@ -134,7 +141,9 @@ export class MenubarControl extends Disposable {
this.recentlyOpened = recentlyOpened;
});
this.detectAndRecommendCustomTitlebar();
this.notifyExistingLinuxUser();
this.notifyUserOfCustomMenubarAccessibility();
this.registerListeners();
}
@@ -194,8 +203,8 @@ export class MenubarControl extends Disposable {
this.updateMenubar();
}
if (event.affectsConfiguration('window.menuBarVisibility')) {
this.detectAndRecommendCustomTitlebar();
if (event.affectsConfiguration('editor.accessibilitySupport')) {
this.notifyUserOfCustomMenubarAccessibility();
}
}
@@ -206,41 +215,63 @@ export class MenubarControl extends Disposable {
});
}
private detectAndRecommendCustomTitlebar(): void {
// {{SQL CARBON EDIT}} - Disable the custom titlebar recommendation
// if (!isLinux) {
// return;
// }
// TODO@sbatten remove after feb19
private notifyExistingLinuxUser(): void {
/*// {{SQL CARBON EDIT}} - Disable the custom titlebar recommendation
if (!isLinux) {
return;
}
// if (!this.storageService.getBoolean('menubar/electronFixRecommended', StorageScope.GLOBAL, false)) {
// if (this.currentMenubarVisibility === 'hidden' || this.currentTitlebarStyleSetting === 'custom') {
// // Issue will not arise for user, abort notification
// return;
// }
const isNewUser = !this.storageService.get('telemetry.lastSessionDate', StorageScope.GLOBAL);
const hasBeenNotified = this.storageService.getBoolean('menubar/linuxTitlebarRevertNotified', StorageScope.GLOBAL, false);
const titleBarConfiguration = this.configurationService.inspect('window.titleBarStyle');
const customShown = getTitleBarStyle(this.configurationService, this.environmentService) === 'custom';
// const message = nls.localize('menubar.electronFixRecommendation', "If you experience hard to read text in the menu bar, we recommend trying out the custom title bar.");
// this.notificationService.prompt(Severity.Info, message, [
// {
// label: nls.localize('goToSetting', "Open Settings"),
// run: () => {
// return this.preferencesService.openGlobalSettings(undefined, { query: 'window.titleBarStyle' });
// }
// },
// {
// label: nls.localize('moreInfo', "More Info"),
// run: () => {
// window.open('https://go.microsoft.com/fwlink/?linkid=2038566');
// }
// },
// {
// label: nls.localize('neverShowAgain', "Don't Show Again"),
// run: () => {
// this.storageService.store('menubar/electronFixRecommended', true, StorageScope.GLOBAL);
// }
// }
// ]);
// }
// {{SQL CARBON EDIT}} - End
if (!hasBeenNotified) {
this.storageService.store('menubar/linuxTitlebarRevertNotified', true, StorageScope.GLOBAL);
}
if (isNewUser || hasBeenNotified || (titleBarConfiguration && titleBarConfiguration.user) || customShown) {
return;
}
const message = nls.localize('menubar.linuxTitlebarRevertNotification', "We have updated the default title bar on Linux to use the native setting. If you prefer, you can go back to the custom setting. More information is available in our [online documentation](https://go.microsoft.com/fwlink/?linkid=2074137).");
this.notificationService.prompt(Severity.Info, message, [
{
label: nls.localize('goToSetting', "Open Settings"),
run: () => {
return this.preferencesService.openGlobalSettings(undefined, { query: 'window.titleBarStyle' });
}
}
]);
*/
}
private notifyUserOfCustomMenubarAccessibility(): void {
if (isMacintosh) {
return;
}
const hasBeenNotified = this.storageService.getBoolean('menubar/accessibleMenubarNotified', StorageScope.GLOBAL, false);
const usingCustomMenubar = getTitleBarStyle(this.configurationService, this.environmentService) === 'custom';
const detected = this.accessibilityService.getAccessibilitySupport() === AccessibilitySupport.Enabled;
const config = this.configurationService.getValue('editor.accessibilitySupport');
if (hasBeenNotified || usingCustomMenubar || !(config === 'on' || (config === 'auto' && detected))) {
return;
}
const message = nls.localize('menubar.customTitlebarAccessibilityNotification', "Accessibility support is enabled for you. For the most accessible experience, we recommend the custom title bar style.");
this.notificationService.prompt(Severity.Info, message, [
{
label: nls.localize('goToSetting', "Open Settings"),
run: () => {
return this.preferencesService.openGlobalSettings(undefined, { query: 'window.titleBarStyle' });
}
}
]);
this.storageService.store('menubar/accessibleMenubarNotified', true, StorageScope.GLOBAL);
}
private registerListeners(): void {
@@ -322,26 +353,34 @@ export class MenubarControl extends Disposable {
return label;
}
private createOpenRecentMenuAction(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | URI, commandId: string, isFile: boolean): IAction & { uri: URI } {
private createOpenRecentMenuAction(recent: IRecent, isFile: boolean): IAction & { uri: URI } {
let label: string;
let uri: URI;
let commandId: string;
let typeHint: URIType | undefined;
if (isSingleFolderWorkspaceIdentifier(workspace) && !isFile) {
label = this.labelService.getWorkspaceLabel(workspace, { verbose: true });
uri = workspace;
} else if (isWorkspaceIdentifier(workspace)) {
label = this.labelService.getWorkspaceLabel(workspace, { verbose: true });
uri = URI.file(workspace.configPath);
if (isRecentFolder(recent)) {
uri = recent.folderUri;
label = recent.label || this.labelService.getWorkspaceLabel(uri, { verbose: true });
commandId = 'openRecentFolder';
typeHint = 'folder';
} else if (isRecentWorkspace(recent)) {
uri = recent.workspace.configPath;
label = recent.label || this.labelService.getWorkspaceLabel(recent.workspace, { verbose: true });
commandId = 'openRecentWorkspace';
typeHint = 'file';
} else {
uri = workspace;
label = this.labelService.getUriLabel(uri);
uri = recent.fileUri;
label = recent.label || this.labelService.getUriLabel(uri);
commandId = 'openRecentFile';
typeHint = 'file';
}
const ret: IAction = new Action(commandId, label, undefined, undefined, (event) => {
const ret: IAction = new Action(commandId, unmnemonicLabel(label), undefined, undefined, (event) => {
const openInNewWindow = event && ((!isMacintosh && (event.ctrlKey || event.shiftKey)) || (isMacintosh && (event.metaKey || event.altKey)));
return this.windowService.openWindow([uri], {
return this.windowService.openWindow([{ uri, typeHint }], {
forceNewWindow: openInNewWindow,
forceOpenWorkspaceAsFile: isFile
});
@@ -362,7 +401,7 @@ export class MenubarControl extends Disposable {
if (workspaces.length > 0) {
for (let i = 0; i < MenubarControl.MAX_MENU_RECENT_ENTRIES && i < workspaces.length; i++) {
result.push(this.createOpenRecentMenuAction(workspaces[i], 'openRecentWorkspace', false));
result.push(this.createOpenRecentMenuAction(workspaces[i], false));
}
result.push(new Separator());
@@ -370,7 +409,7 @@ export class MenubarControl extends Disposable {
if (files.length > 0) {
for (let i = 0; i < MenubarControl.MAX_MENU_RECENT_ENTRIES && i < files.length; i++) {
result.push(this.createOpenRecentMenuAction(files[i], 'openRecentFile', false));
result.push(this.createOpenRecentMenuAction(files[i], true));
}
result.push(new Separator());
@@ -408,7 +447,7 @@ export class MenubarControl extends Disposable {
return new Action('update.checking', nls.localize('checkingForUpdates', "Checking For Updates..."), undefined, false);
case StateType.AvailableForDownload:
return new Action('update.downloadNow', nls.localize({ key: 'download now', comment: ['&& denotes a mnemonic'] }, "D&&ownload Now"), null, true, () =>
return new Action('update.downloadNow', nls.localize({ key: 'download now', comment: ['&& denotes a mnemonic'] }, "D&&ownload Now"), undefined, true, () =>
this.updateService.downloadUpdate());
case StateType.Downloading:
@@ -437,6 +476,7 @@ export class MenubarControl extends Disposable {
if (!isMacintosh) {
const updateAction = this.getUpdateAction();
if (updateAction) {
updateAction.label = mnemonicMenuLabel(updateAction.label);
target.push(updateAction);
target.push(new Separator());
}
@@ -459,12 +499,17 @@ export class MenubarControl extends Disposable {
}
));
this.accessibilityService.alwaysUnderlineAccessKeys().then(val => {
this.alwaysOnMnemonics = val;
this.menubar.update({ enableMnemonics: this.currentEnableMenuBarMnemonics, visibility: this.currentMenubarVisibility, getKeybinding: (action) => this.keybindingService.lookupKeybinding(action.id), alwaysOnMnemonics: this.alwaysOnMnemonics });
});
this._register(this.menubar.onFocusStateChange(e => this._onFocusStateChange.fire(e)));
this._register(this.menubar.onVisibilityChange(e => this._onVisibilityChange.fire(e)));
this._register(attachMenuStyler(this.menubar, this.themeService));
} else {
this.menubar.update({ enableMnemonics: this.currentEnableMenuBarMnemonics, visibility: this.currentMenubarVisibility, getKeybinding: (action) => this.keybindingService.lookupKeybinding(action.id) });
this.menubar.update({ enableMnemonics: this.currentEnableMenuBarMnemonics, visibility: this.currentMenubarVisibility, getKeybinding: (action) => this.keybindingService.lookupKeybinding(action.id), alwaysOnMnemonics: this.alwaysOnMnemonics });
}
// Update the menu actions
@@ -480,10 +525,10 @@ export class MenubarControl extends Disposable {
const submenu = this.menuService.createMenu(action.item.submenu, this.contextKeyService);
const submenuActions: SubmenuAction[] = [];
updateActions(submenu, submenuActions);
target.push(new SubmenuAction(action.label, submenuActions));
target.push(new SubmenuAction(mnemonicMenuLabel(action.label), submenuActions));
submenu.dispose();
} else {
action.label = this.calculateActionLabel(action);
action.label = mnemonicMenuLabel(this.calculateActionLabel(action));
target.push(action);
}
}
@@ -494,28 +539,30 @@ export class MenubarControl extends Disposable {
target.pop();
};
for (let title of Object.keys(this.topLevelMenus)) {
for (const title of Object.keys(this.topLevelMenus)) {
const menu = this.topLevelMenus[title];
if (firstTime) {
if (firstTime && menu) {
this._register(menu.onDidChange(() => {
const actions = [];
const actions: IAction[] = [];
updateActions(menu, actions);
this.menubar.updateMenu({ actions: actions, label: this.topLevelTitles[title] });
this.menubar.updateMenu({ actions: actions, label: mnemonicMenuLabel(this.topLevelTitles[title]) });
}));
}
const actions = [];
updateActions(menu, actions);
const actions: IAction[] = [];
if (menu) {
updateActions(menu, actions);
}
if (!firstTime) {
this.menubar.updateMenu({ actions: actions, label: this.topLevelTitles[title] });
this.menubar.updateMenu({ actions: actions, label: mnemonicMenuLabel(this.topLevelTitles[title]) });
} else {
this.menubar.push({ actions: actions, label: this.topLevelTitles[title] });
this.menubar.push({ actions: actions, label: mnemonicMenuLabel(this.topLevelTitles[title]) });
}
}
}
private getMenubarKeybinding(id: string): IMenubarKeybinding {
private getMenubarKeybinding(id: string): IMenubarKeybinding | undefined {
const binding = this.keybindingService.lookupKeybinding(id);
if (!binding) {
return undefined;
@@ -524,19 +571,19 @@ export class MenubarControl extends Disposable {
// first try to resolve a native accelerator
const electronAccelerator = binding.getElectronAccelerator();
if (electronAccelerator) {
return { label: electronAccelerator, userSettingsLabel: binding.getUserSettingsLabel() };
return { label: electronAccelerator, userSettingsLabel: withNullAsUndefined(binding.getUserSettingsLabel()) };
}
// we need this fallback to support keybindings that cannot show in electron menus (e.g. chords)
const acceleratorLabel = binding.getLabel();
if (acceleratorLabel) {
return { label: acceleratorLabel, isNative: false, userSettingsLabel: binding.getUserSettingsLabel() };
return { label: acceleratorLabel, isNative: false, userSettingsLabel: withNullAsUndefined(binding.getUserSettingsLabel()) };
}
return null;
return undefined;
}
private populateMenuItems(menu: IMenu, menuToPopulate: IMenubarMenu, keybindings: { [id: string]: IMenubarKeybinding }) {
private populateMenuItems(menu: IMenu, menuToPopulate: IMenubarMenu, keybindings: { [id: string]: IMenubarKeybinding | undefined }) {
let groups = menu.getActions();
for (let group of groups) {
const [, actions] = group;
@@ -604,15 +651,17 @@ export class MenubarControl extends Disposable {
}
menubarData.keybindings = this.getAdditionalKeybindings();
for (let topLevelMenuName of Object.keys(this.topLevelMenus)) {
for (const topLevelMenuName of Object.keys(this.topLevelMenus)) {
const menu = this.topLevelMenus[topLevelMenuName];
let menubarMenu: IMenubarMenu = { items: [] };
this.populateMenuItems(menu, menubarMenu, menubarData.keybindings);
if (menubarMenu.items.length === 0) {
// Menus are incomplete
return false;
if (menu) {
const menubarMenu: IMenubarMenu = { items: [] };
this.populateMenuItems(menu, menubarMenu, menubarData.keybindings);
if (menubarMenu.items.length === 0) {
// Menus are incomplete
return false;
}
menubarData.menus[topLevelMenuName] = menubarMenu;
}
menubarData.menus[topLevelMenuName] = menubarMenu;
}
return true;
@@ -632,7 +681,7 @@ export class MenubarControl extends Disposable {
}
if (this.menubar) {
this.menubar.update({ enableMnemonics: this.currentEnableMenuBarMnemonics, visibility: this.currentMenubarVisibility, getKeybinding: (action) => this.keybindingService.lookupKeybinding(action.id) });
this.menubar.update({ enableMnemonics: this.currentEnableMenuBarMnemonics, visibility: this.currentMenubarVisibility, getKeybinding: (action) => this.keybindingService.lookupKeybinding(action.id), alwaysOnMnemonics: this.alwaysOnMnemonics });
}
}

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./media/titlebarpart';
import * as paths from 'vs/base/common/paths';
import { dirname, posix } from 'vs/base/common/path';
import * as resources from 'vs/base/common/resources';
import { Part } from 'vs/workbench/browser/part';
import { ITitleService, ITitleProperties } from 'vs/workbench/services/title/common/titleService';
@@ -28,17 +28,16 @@ import { Color } from 'vs/base/common/color';
import { trim } from 'vs/base/common/strings';
import { EventType, EventHelper, Dimension, isAncestor, hide, show, removeClass, addClass, append, $, addDisposableListener, runAtThisOrScheduleAtNextAnimationFrame } from 'vs/base/browser/dom';
import { MenubarControl } from 'vs/workbench/browser/parts/titlebar/menubarControl';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IInstantiationService, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
import { template, getBaseLabel } from 'vs/base/common/labels';
import { ILabelService } from 'vs/platform/label/common/label';
import { Event, Emitter } from 'vs/base/common/event';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { ISerializableView } from 'vs/base/browser/ui/grid/grid';
import { Parts } from 'vs/workbench/services/part/common/partService';
import { Parts, IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
import { RunOnceScheduler } from 'vs/base/common/async';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
export class TitlebarPart extends Part implements ITitleService, ISerializableView {
_serviceBrand: any;
export class TitlebarPart extends Part implements ITitleService {
private static readonly NLS_UNSUPPORTED = nls.localize('patchedWindowTitle', "[Unsupported]");
private static readonly NLS_USER_IS_ADMIN = isWindows ? nls.localize('userIsAdmin', "[Administrator]") : nls.localize('userIsSudo', "[Superuser]");
@@ -46,7 +45,20 @@ export class TitlebarPart extends Part implements ITitleService, ISerializableVi
private static readonly TITLE_DIRTY = '\u25cf ';
private static readonly TITLE_SEPARATOR = isMacintosh ? ' — ' : ' - '; // macOS uses special - separator
element: HTMLElement;
//#region IView
readonly minimumWidth: number = 0;
readonly maximumWidth: number = Number.POSITIVE_INFINITY;
get minimumHeight(): number { return isMacintosh ? 22 / getZoomFactor() : (30 / (this.configurationService.getValue<MenuBarVisibility>('window.menuBarVisibility') === 'hidden' ? getZoomFactor() : 1)); }
get maximumHeight(): number { return isMacintosh ? 22 / getZoomFactor() : (30 / (this.configurationService.getValue<MenuBarVisibility>('window.menuBarVisibility') === 'hidden' ? getZoomFactor() : 1)); }
//#endregion
private _onMenubarVisibilityChange = this._register(new Emitter<boolean>());
get onMenubarVisibilityChange(): Event<boolean> { return this._onMenubarVisibilityChange.event; }
_serviceBrand: ServiceIdentifier<any>;
private title: HTMLElement;
private dragRegion: HTMLElement;
private windowControls: HTMLElement;
@@ -55,6 +67,7 @@ export class TitlebarPart extends Part implements ITitleService, ISerializableVi
private menubarPart: MenubarControl;
private menubar: HTMLElement;
private resizer: HTMLElement;
private lastLayoutDimensions: Dimension;
private pendingTitle: string;
private representedFileName: string;
@@ -64,16 +77,9 @@ export class TitlebarPart extends Part implements ITitleService, ISerializableVi
private properties: ITitleProperties;
private activeEditorListeners: IDisposable[];
minimumWidth: number = 0;
maximumWidth: number = Number.POSITIVE_INFINITY;
get minimumHeight(): number { return isMacintosh ? 22 / getZoomFactor() : (30 / (this.configurationService.getValue<MenuBarVisibility>('window.menuBarVisibility') === 'hidden' ? getZoomFactor() : 1)); }
get maximumHeight(): number { return isMacintosh ? 22 / getZoomFactor() : (30 / (this.configurationService.getValue<MenuBarVisibility>('window.menuBarVisibility') === 'hidden' ? getZoomFactor() : 1)); }
private _onDidChange = new Emitter<{ width: number; height: number; }>();
readonly onDidChange = this._onDidChange.event;
private titleUpdater: RunOnceScheduler = this._register(new RunOnceScheduler(() => this.doUpdateTitle(), 0));
constructor(
id: string,
@IContextMenuService private readonly contextMenuService: IContextMenuService,
@IWindowService private readonly windowService: IWindowService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@@ -84,9 +90,10 @@ export class TitlebarPart extends Part implements ITitleService, ISerializableVi
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IThemeService themeService: IThemeService,
@ILabelService private readonly labelService: ILabelService,
@IStorageService storageService: IStorageService
@IStorageService storageService: IStorageService,
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService
) {
super(id, { hasTitle: false }, themeService, storageService);
super(Parts.TITLEBAR_PART, { hasTitle: false }, themeService, storageService, layoutService);
this.properties = { isPure: true, isAdmin: false };
this.activeEditorListeners = [];
@@ -98,10 +105,10 @@ export class TitlebarPart extends Part implements ITitleService, ISerializableVi
this._register(this.windowService.onDidChangeFocus(focused => focused ? this.onFocus() : this.onBlur()));
this._register(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationChanged(e)));
this._register(this.editorService.onDidActiveEditorChange(() => this.onActiveEditorChange()));
this._register(this.contextService.onDidChangeWorkspaceFolders(() => this.doUpdateTitle()));
this._register(this.contextService.onDidChangeWorkbenchState(() => this.doUpdateTitle()));
this._register(this.contextService.onDidChangeWorkspaceName(() => this.doUpdateTitle()));
this._register(this.labelService.onDidChangeFormatters(() => this.doUpdateTitle()));
this._register(this.contextService.onDidChangeWorkspaceFolders(() => this.titleUpdater.schedule()));
this._register(this.contextService.onDidChangeWorkbenchState(() => this.titleUpdater.schedule()));
this._register(this.contextService.onDidChangeWorkspaceName(() => this.titleUpdater.schedule()));
this._register(this.labelService.onDidChangeFormatters(() => this.titleUpdater.schedule()));
}
private onBlur(): void {
@@ -116,7 +123,7 @@ export class TitlebarPart extends Part implements ITitleService, ISerializableVi
private onConfigurationChanged(event: IConfigurationChangeEvent): void {
if (event.affectsConfiguration('window.title')) {
this.doUpdateTitle();
this.titleUpdater.schedule();
}
if (event.affectsConfiguration('window.doubleClickIconToClose')) {
@@ -136,6 +143,8 @@ export class TitlebarPart extends Part implements ITitleService, ISerializableVi
}
this.adjustTitleMarginToCenter();
this._onMenubarVisibilityChange.fire(visible);
}
}
@@ -149,10 +158,6 @@ export class TitlebarPart extends Part implements ITitleService, ISerializableVi
}
}
onMenubarVisibilityChange(): Event<boolean> {
return this.menubarPart.onVisibilityChange;
}
private onActiveEditorChange(): void {
// Dispose old listeners
@@ -160,13 +165,13 @@ export class TitlebarPart extends Part implements ITitleService, ISerializableVi
this.activeEditorListeners = [];
// Calculate New Window Title
this.doUpdateTitle();
this.titleUpdater.schedule();
// Apply listener for dirty and label changes
const activeEditor = this.editorService.activeEditor;
if (activeEditor instanceof EditorInput) {
this.activeEditorListeners.push(activeEditor.onDidChangeDirty(() => this.doUpdateTitle()));
this.activeEditorListeners.push(activeEditor.onDidChangeLabel(() => this.doUpdateTitle()));
this.activeEditorListeners.push(activeEditor.onDidChangeDirty(() => this.titleUpdater.schedule()));
this.activeEditorListeners.push(activeEditor.onDidChangeLabel(() => this.titleUpdater.schedule()));
}
// Represented File Name
@@ -174,7 +179,7 @@ export class TitlebarPart extends Part implements ITitleService, ISerializableVi
}
private updateRepresentedFilename(): void {
const file = toResource(this.editorService.activeEditor, { supportSideBySide: true, filter: 'file' });
const file = toResource(this.editorService.activeEditor || null, { supportSideBySide: true, filter: 'file' });
const path = file ? file.fsPath : '';
// Apply to window
@@ -202,7 +207,9 @@ export class TitlebarPart extends Part implements ITitleService, ISerializableVi
}
if ((isWindows || isLinux) && this.title) {
this.adjustTitleMarginToCenter();
if (this.lastLayoutDimensions) {
this.updateLayout(this.lastLayoutDimensions);
}
}
}
@@ -232,7 +239,7 @@ export class TitlebarPart extends Part implements ITitleService, ISerializableVi
this.properties.isAdmin = isAdmin;
this.properties.isPure = isPure;
this.doUpdateTitle();
this.titleUpdater.schedule();
}
}
@@ -258,7 +265,7 @@ export class TitlebarPart extends Part implements ITitleService, ISerializableVi
const workspace = this.contextService.getWorkspace();
// Compute root
let root: URI;
let root: URI | undefined;
if (workspace.configuration) {
root = workspace.configuration;
} else if (workspace.folders.length) {
@@ -275,7 +282,7 @@ export class TitlebarPart extends Part implements ITitleService, ISerializableVi
// Compute folder resource
// Single Root Workspace: always the root single workspace in this case
// Otherwise: root folder of the currently active file if any
const folder = this.contextService.getWorkbenchState() === WorkbenchState.FOLDER ? workspace.folders[0] : this.contextService.getWorkspaceFolder(toResource(editor, { supportSideBySide: true }));
const folder = this.contextService.getWorkbenchState() === WorkbenchState.FOLDER ? workspace.folders[0] : this.contextService.getWorkspaceFolder(toResource(editor || null, { supportSideBySide: true })!);
// Variables
const activeEditorShort = editor ? editor.getTitle(Verbosity.SHORT) : '';
@@ -343,7 +350,7 @@ export class TitlebarPart extends Part implements ITitleService, ISerializableVi
if (this.pendingTitle) {
this.title.innerText = this.pendingTitle;
} else {
this.doUpdateTitle();
this.titleUpdater.schedule();
}
// Maximize/Restore on doubleclick
@@ -452,7 +459,7 @@ export class TitlebarPart extends Part implements ITitleService, ISerializableVi
this.adjustTitleMarginToCenter();
}
protected updateStyles(): void {
updateStyles(): void {
super.updateStyles();
// Part container
@@ -465,7 +472,7 @@ export class TitlebarPart extends Part implements ITitleService, ISerializableVi
const titleBackground = this.getColor(this.isInactive ? TITLE_BAR_INACTIVE_BACKGROUND : TITLE_BAR_ACTIVE_BACKGROUND);
this.element.style.backgroundColor = titleBackground;
if (Color.fromHex(titleBackground).isLighter()) {
if (titleBackground && Color.fromHex(titleBackground).isLighter()) {
addClass(this.element, 'light');
} else {
removeClass(this.element, 'light');
@@ -487,8 +494,7 @@ export class TitlebarPart extends Part implements ITitleService, ISerializableVi
const setting = this.configurationService.getValue('window.doubleClickIconToClose');
if (setting) {
this.appIcon.style['-webkit-app-region'] = 'no-drag';
}
else {
} else {
this.appIcon.style['-webkit-app-region'] = 'drag';
}
}
@@ -514,7 +520,7 @@ export class TitlebarPart extends Part implements ITitleService, ISerializableVi
const actions: IAction[] = [];
if (this.representedFileName) {
const segments = this.representedFileName.split(paths.sep);
const segments = this.representedFileName.split(posix.sep);
for (let i = segments.length; i > 0; i--) {
const isFile = (i === segments.length);
@@ -523,16 +529,16 @@ export class TitlebarPart extends Part implements ITitleService, ISerializableVi
pathOffset++; // for segments which are not the file name we want to open the folder
}
const path = segments.slice(0, pathOffset).join(paths.sep);
const path = segments.slice(0, pathOffset).join(posix.sep);
let label: string;
if (!isFile) {
label = getBaseLabel(paths.dirname(path));
label = getBaseLabel(dirname(path));
} else {
label = getBaseLabel(path);
}
actions.push(new ShowItemInFolderAction(path, label || paths.sep, this.windowsService));
actions.push(new ShowItemInFolderAction(path, label || posix.sep, this.windowsService));
}
}
@@ -554,6 +560,8 @@ export class TitlebarPart extends Part implements ITitleService, ISerializableVi
}
updateLayout(dimension: Dimension): void {
this.lastLayoutDimensions = dimension;
if (getTitleBarStyle(this.configurationService, this.environmentService) === 'custom') {
// Only prevent zooming behavior on macOS or when the menubar is not visible
if (isMacintosh || this.configurationService.getValue<MenuBarVisibility>('window.menuBarVisibility') === 'hidden') {
@@ -573,25 +581,16 @@ export class TitlebarPart extends Part implements ITitleService, ISerializableVi
runAtThisOrScheduleAtNextAnimationFrame(() => this.adjustTitleMarginToCenter());
if (this.menubarPart) {
const menubarDimension = new Dimension(undefined, dimension.height);
const menubarDimension = new Dimension(0, dimension.height);
this.menubarPart.layout(menubarDimension);
}
}
}
layout(dimension: Dimension): Dimension[];
layout(width: number, height: number): void;
layout(dim1: Dimension | number, dim2?: number): Dimension[] | void {
if (dim1 instanceof Dimension) {
this.updateLayout(dim1);
layout(width: number, height: number): void {
this.updateLayout(new Dimension(width, height));
return super.layout(dim1);
}
const dimensions = new Dimension(dim1, dim2);
this.updateLayout(dimensions);
super.layout(dimensions);
super.layoutContents(width, height);
}
toJSON(): object {
@@ -608,7 +607,7 @@ class ShowItemInFolderAction extends Action {
}
run(): Promise<void> {
return this.windowsService.showItemInFolder(this.path);
return this.windowsService.showItemInFolder(URI.file(this.path));
}
}
@@ -631,3 +630,5 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
`);
}
});
registerSingleton(ITitleService, TitlebarPart);

View File

@@ -13,7 +13,7 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView
import { IMenuService, MenuId, MenuItemAction } from 'vs/platform/actions/common/actions';
import { ContextAwareMenuItemActionItem, fillInActionBarActions, fillInContextMenuActions } from 'vs/platform/actions/browser/menuItemActionItem';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IViewsService, ITreeView, ITreeItem, TreeItemCollapsibleState, ITreeViewDataProvider, TreeViewItemHandleArg, ITreeViewDescriptor, ViewsRegistry, ViewContainer, ITreeItemLabel } from 'vs/workbench/common/views';
import { IViewsService, ITreeView, ITreeItem, TreeItemCollapsibleState, ITreeViewDataProvider, TreeViewItemHandleArg, ITreeViewDescriptor, IViewsRegistry, ViewContainer, ITreeItemLabel, Extensions } from 'vs/workbench/common/views';
import { IViewletViewOptions, FileIconThemableWorkbenchTree } from 'vs/workbench/browser/parts/views/viewsViewlet';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { INotificationService } from 'vs/platform/notification/common/notification';
@@ -26,7 +26,7 @@ import { IDataSource, ITree, IRenderer, ContextMenuEvent } from 'vs/base/parts/t
import { ResourceLabels, IResourceLabel } from 'vs/workbench/browser/labels';
import { ActionBar, IActionItemProvider, ActionItem } from 'vs/base/browser/ui/actionbar/actionbar';
import { URI } from 'vs/base/common/uri';
import { basename } from 'vs/base/common/paths';
import { dirname, basename } from 'vs/base/common/resources';
import { LIGHT, FileThemeIcon, FolderThemeIcon, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { FileKind } from 'vs/platform/files/common/files';
import { WorkbenchTreeController } from 'vs/platform/list/browser/listService';
@@ -43,7 +43,7 @@ import { onUnexpectedError } from 'vs/base/common/errors';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { IMarkdownRenderResult } from 'vs/editor/contrib/markdown/markdownRenderer';
import { ILabelService } from 'vs/platform/label/common/label';
import { dirname } from 'vs/base/common/resources';
import { Registry } from 'vs/platform/registry/common/platform';
export class CustomTreeViewPanel extends ViewletPanel {
@@ -58,7 +58,7 @@ export class CustomTreeViewPanel extends ViewletPanel {
@IViewsService viewsService: IViewsService,
) {
super({ ...(options as IViewletPanelOptions), ariaHeaderLabel: options.title }, keybindingService, contextMenuService, configurationService);
const { treeView } = (<ITreeViewDescriptor>ViewsRegistry.getView(options.id));
const { treeView } = (<ITreeViewDescriptor>Registry.as<IViewsRegistry>(Extensions.ViewsRegistry).getView(options.id));
this.treeView = treeView;
this.treeView.onDidChangeActions(() => this.updateActions(), this, this.disposables);
this.disposables.push(toDisposable(() => this.treeView.setVisibility(false)));
@@ -87,8 +87,8 @@ export class CustomTreeViewPanel extends ViewletPanel {
return [...this.treeView.getSecondaryActions()];
}
getActionItem(action: IAction): IActionItem {
return action instanceof MenuItemAction ? new ContextAwareMenuItemActionItem(action, this.keybindingService, this.notificationService, this.contextMenuService) : undefined;
getActionItem(action: IAction): IActionItem | null {
return action instanceof MenuItemAction ? new ContextAwareMenuItemActionItem(action, this.keybindingService, this.notificationService, this.contextMenuService) : null;
}
getOptimalWidth(): number {
@@ -164,9 +164,9 @@ class TitleMenus implements IDisposable {
class Root implements ITreeItem {
label = { label: 'root' };
handle = '0';
parentHandle = null;
parentHandle: string | undefined = undefined;
collapsibleState = TreeItemCollapsibleState.Expanded;
children = undefined;
children: ITreeItem[] | undefined = undefined;
}
const noDataProviderMessage = localize('no-dataprovider', "There is no data provider registered that can provide view data.");
@@ -191,21 +191,21 @@ export class CustomTreeView extends Disposable implements ITreeView {
private menus: TitleMenus;
private markdownRenderer: MarkdownRenderer;
private markdownResult: IMarkdownRenderResult;
private markdownResult: IMarkdownRenderResult | null;
private _onDidExpandItem: Emitter<ITreeItem> = this._register(new Emitter<ITreeItem>());
private readonly _onDidExpandItem: Emitter<ITreeItem> = this._register(new Emitter<ITreeItem>());
readonly onDidExpandItem: Event<ITreeItem> = this._onDidExpandItem.event;
private _onDidCollapseItem: Emitter<ITreeItem> = this._register(new Emitter<ITreeItem>());
private readonly _onDidCollapseItem: Emitter<ITreeItem> = this._register(new Emitter<ITreeItem>());
readonly onDidCollapseItem: Event<ITreeItem> = this._onDidCollapseItem.event;
private _onDidChangeSelection: Emitter<ITreeItem[]> = this._register(new Emitter<ITreeItem[]>());
readonly onDidChangeSelection: Event<ITreeItem[]> = this._onDidChangeSelection.event;
private _onDidChangeVisibility: Emitter<boolean> = this._register(new Emitter<boolean>());
private readonly _onDidChangeVisibility: Emitter<boolean> = this._register(new Emitter<boolean>());
readonly onDidChangeVisibility: Event<boolean> = this._onDidChangeVisibility.event;
private _onDidChangeActions: Emitter<void> = this._register(new Emitter<void>());
private readonly _onDidChangeActions: Emitter<void> = this._register(new Emitter<void>());
readonly onDidChangeActions: Event<void> = this._onDidChangeActions.event;
constructor(
@@ -235,7 +235,7 @@ export class CustomTreeView extends Disposable implements ITreeView {
this.markdownResult.dispose();
}
}));
this._register(ViewsRegistry.onDidChangeContainer(({ views, from, to }) => {
this._register(Registry.as<IViewsRegistry>(Extensions.ViewsRegistry).onDidChangeContainer(({ views, from, to }) => {
if (from === this.viewContainer && views.some(v => v.id === this.id)) {
this.viewContainer = to;
}
@@ -243,15 +243,15 @@ export class CustomTreeView extends Disposable implements ITreeView {
this.create();
}
private _dataProvider: ITreeViewDataProvider;
get dataProvider(): ITreeViewDataProvider {
private _dataProvider: ITreeViewDataProvider | null;
get dataProvider(): ITreeViewDataProvider | null {
return this._dataProvider;
}
set dataProvider(dataProvider: ITreeViewDataProvider) {
set dataProvider(dataProvider: ITreeViewDataProvider | null) {
if (dataProvider) {
this._dataProvider = new class implements ITreeViewDataProvider {
getChildren(node?: ITreeItem): Promise<ITreeItem[]> {
getChildren(node: ITreeItem): Promise<ITreeItem[]> {
if (node && node.children) {
return Promise.resolve(node.children);
}
@@ -305,7 +305,7 @@ export class CustomTreeView extends Disposable implements ITreeView {
getPrimaryActions(): IAction[] {
if (this.showCollapseAllAction) {
const collapseAllAction = new Action('vs.tree.collapse', localize('collapse', "Collapse"), 'monaco-tree-action collapse-all', true, () => this.tree ? new CollapseAllAction(this.tree, true).run() : Promise.resolve());
const collapseAllAction = new Action('vs.tree.collapse', localize('collapseAll', "Collapse All"), 'monaco-tree-action collapse-all', true, () => this.tree ? new CollapseAllAction(this.tree, true).run() : Promise.resolve());
return [...this.menus.getTitleActions(), collapseAllAction];
} else {
return this.menus.getTitleActions();
@@ -378,7 +378,7 @@ export class CustomTreeView extends Disposable implements ITreeView {
}
private createTree() {
const actionItemProvider = (action: IAction) => action instanceof MenuItemAction ? this.instantiationService.createInstance(ContextAwareMenuItemActionItem, action) : undefined;
const actionItemProvider = (action: IAction) => action instanceof MenuItemAction ? this.instantiationService.createInstance(ContextAwareMenuItemActionItem, action) : null;
const menus = this._register(this.instantiationService.createInstance(TreeMenus, this.id));
this.treeLabels = this._register(this.instantiationService.createInstance(ResourceLabels, this));
const dataSource = this.instantiationService.createInstance(TreeDataSource, this, <T>(task: Promise<T>) => this.progressService.withProgress({ location: this.viewContainer.id }, () => task));
@@ -462,7 +462,7 @@ export class CustomTreeView extends Disposable implements ITreeView {
this.elementsToRefresh = [];
}
for (const element of elements) {
element.children = null; // reset children
element.children = undefined; // reset children
}
if (this.isVisible) {
return this.doRefresh(elements);
@@ -508,7 +508,7 @@ export class CustomTreeView extends Disposable implements ITreeView {
if (this.tree) {
return this.tree.reveal(item);
}
return Promise.resolve(null);
return Promise.resolve();
}
private activate() {
@@ -583,7 +583,7 @@ class TreeDataSource implements IDataSource {
}
hasChildren(tree: ITree, node: ITreeItem): boolean {
return this.treeView.dataProvider && node.collapsibleState !== TreeItemCollapsibleState.None;
return !!this.treeView.dataProvider && node.collapsibleState !== TreeItemCollapsibleState.None;
}
getChildren(tree: ITree, node: ITreeItem): Promise<any[]> {
@@ -677,7 +677,7 @@ class TreeRenderer implements IRenderer {
renderElement(tree: ITree, node: ITreeItem, templateId: string, templateData: ITreeExplorerTemplateData): void {
const resource = node.resourceUri ? URI.revive(node.resourceUri) : null;
const treeItemLabel: ITreeItemLabel = node.label ? node.label : resource ? { label: basename(resource.path) } : undefined;
const treeItemLabel: ITreeItemLabel | undefined = node.label ? node.label : resource ? { label: basename(resource) } : undefined;
const description = isString(node.description) ? node.description : resource && node.description === true ? this.labelService.getUriLabel(dirname(resource), { relative: true }) : undefined;
const label = treeItemLabel ? treeItemLabel.label : undefined;
const matches = treeItemLabel && treeItemLabel.highlights ? treeItemLabel.highlights.map(([start, end]) => ({ start, end })) : undefined;
@@ -758,7 +758,7 @@ class Aligner extends Disposable {
if (this.hasIcon(parent)) {
return false;
}
return parent.children && parent.children.every(c => c.collapsibleState === TreeItemCollapsibleState.None || !this.hasIcon(c));
return !!parent.children && parent.children.every(c => c.collapsibleState === TreeItemCollapsibleState.None || !this.hasIcon(c));
}
private hasIcon(node: ITreeItem): boolean {
@@ -873,7 +873,7 @@ class TreeMenus extends Disposable implements IDisposable {
return this.getActions(MenuId.ViewItemContext, { key: 'viewItem', value: element.contextValue }).secondary;
}
private getActions(menuId: MenuId, context: { key: string, value: string }): { primary: IAction[]; secondary: IAction[]; } {
private getActions(menuId: MenuId, context: { key: string, value?: string }): { primary: IAction[]; secondary: IAction[]; } {
const contextKeyService = this.contextKeyService.createScoped();
contextKeyService.createKey('view', this.id);
contextKeyService.createKey(context.key, context.value);

View File

@@ -24,10 +24,11 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { PanelView, IPanelViewOptions, IPanelOptions, Panel } from 'vs/base/browser/ui/splitview/panelview';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IPartService } from 'vs/workbench/services/part/common/partService';
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
import { IView } from 'vs/workbench/common/views';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { withNullAsUndefined } from 'vs/base/common/types';
export interface IPanelColors extends IColorMapping {
dropBackground?: ColorIdentifier;
@@ -127,7 +128,7 @@ export abstract class ViewletPanel extends Panel implements IView {
orientation: ActionsOrientation.HORIZONTAL,
actionItemProvider: action => this.getActionItem(action),
ariaLabel: nls.localize('viewToolbarAriaLabel', "{0} actions", this.title),
getKeyBinding: action => this.keybindingService.lookupKeybinding(action.id) || undefined,
getKeyBinding: action => withNullAsUndefined(this.keybindingService.lookupKeybinding(action.id)),
actionRunner: this.actionRunner
});
@@ -221,13 +222,13 @@ export class PanelViewlet extends Viewlet {
id: string,
private options: IViewsViewletOptions,
@IConfigurationService configurationService: IConfigurationService,
@IPartService partService: IPartService,
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
@IContextMenuService protected contextMenuService: IContextMenuService,
@ITelemetryService telemetryService: ITelemetryService,
@IThemeService themeService: IThemeService,
@IStorageService storageService: IStorageService
) {
super(id, configurationService, partService, telemetryService, themeService, storageService);
super(id, configurationService, layoutService, telemetryService, themeService, storageService);
}
create(parent: HTMLElement): void {

View File

@@ -5,14 +5,13 @@
import 'vs/css!./media/views';
import { Disposable, IDisposable, toDisposable, dispose } from 'vs/base/common/lifecycle';
import { IViewsService, ViewsRegistry, IViewsViewlet, ViewContainer, IViewDescriptor, IViewContainersRegistry, Extensions as ViewContainerExtensions, IView, IViewDescriptorCollection } from 'vs/workbench/common/views';
import { IViewsService, IViewsViewlet, ViewContainer, IViewDescriptor, IViewContainersRegistry, Extensions as ViewExtensions, IView, IViewDescriptorCollection, IViewsRegistry } from 'vs/workbench/common/views';
import { Registry } from 'vs/platform/registry/common/platform';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import { IContextKeyService, IContextKeyChangeEvent, IReadableSet, IContextKey, RawContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { Event, Emitter } from 'vs/base/common/event';
import { sortedDiff, firstIndex, move, isNonEmptyArray } from 'vs/base/common/arrays';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { isUndefinedOrNull } from 'vs/base/common/types';
import { MenuId, MenuRegistry, ICommandAction } from 'vs/platform/actions/common/actions';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
@@ -21,6 +20,8 @@ import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/co
import { values } from 'vs/base/common/map';
import { IFileIconTheme, IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { toggleClass, addClass } from 'vs/base/browser/dom';
import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
function filterViewRegisterEvent(container: ViewContainer, event: Event<{ viewContainer: ViewContainer, views: IViewDescriptor[] }>): Event<IViewDescriptor[]> {
return Event.chain(event)
@@ -96,10 +97,11 @@ class ViewDescriptorCollection extends Disposable implements IViewDescriptorColl
@IContextKeyService private readonly contextKeyService: IContextKeyService
) {
super();
const onRelevantViewsRegistered = filterViewRegisterEvent(container, ViewsRegistry.onViewsRegistered);
const viewsRegistry = Registry.as<IViewsRegistry>(ViewExtensions.ViewsRegistry);
const onRelevantViewsRegistered = filterViewRegisterEvent(container, viewsRegistry.onViewsRegistered);
this._register(onRelevantViewsRegistered(this.onViewsRegistered, this));
const onRelevantViewsMoved = filterViewMoveEvent(container, ViewsRegistry.onDidChangeContainer);
const onRelevantViewsMoved = filterViewMoveEvent(container, viewsRegistry.onDidChangeContainer);
this._register(onRelevantViewsMoved(({ added, removed }) => {
if (isNonEmptyArray(added)) {
this.onViewsRegistered(added);
@@ -109,16 +111,16 @@ class ViewDescriptorCollection extends Disposable implements IViewDescriptorColl
}
}));
const onRelevantViewsDeregistered = filterViewRegisterEvent(container, ViewsRegistry.onViewsDeregistered);
const onRelevantViewsDeregistered = filterViewRegisterEvent(container, viewsRegistry.onViewsDeregistered);
this._register(onRelevantViewsDeregistered(this.onViewsDeregistered, this));
const onRelevantContextChange = Event.filter(contextKeyService.onDidChangeContext, e => e.affectsSome(this.contextKeys));
this._register(onRelevantContextChange(this.onContextChanged, this));
this.onViewsRegistered(ViewsRegistry.getViews(container));
this.onViewsRegistered(viewsRegistry.getViews(container));
}
private onViewsRegistered(viewDescriptors: IViewDescriptor[]): any {
private onViewsRegistered(viewDescriptors: IViewDescriptor[]): void {
const added: IViewDescriptor[] = [];
for (const viewDescriptor of viewDescriptors) {
@@ -145,7 +147,7 @@ class ViewDescriptorCollection extends Disposable implements IViewDescriptorColl
}
}
private onViewsDeregistered(viewDescriptors: IViewDescriptor[]): any {
private onViewsDeregistered(viewDescriptors: IViewDescriptor[]): void {
const removed: IViewDescriptor[] = [];
for (const viewDescriptor of viewDescriptors) {
@@ -174,7 +176,7 @@ class ViewDescriptorCollection extends Disposable implements IViewDescriptorColl
}
}
private onContextChanged(event: IContextKeyChangeEvent): any {
private onContextChanged(event: IContextKeyChangeEvent): void {
const removed: IViewDescriptor[] = [];
const added: IViewDescriptor[] = [];
@@ -203,7 +205,8 @@ class ViewDescriptorCollection extends Disposable implements IViewDescriptorColl
}
export interface IViewState {
visible: boolean;
visibleGlobal: boolean;
visibleWorkspace: boolean;
collapsed: boolean;
order?: number;
size?: number;
@@ -223,7 +226,7 @@ export class ContributableViewsModel extends Disposable {
readonly viewDescriptors: IViewDescriptor[] = [];
get visibleViewDescriptors(): IViewDescriptor[] {
return this.viewDescriptors.filter(v => this.viewStates.get(v.id)!.visible);
return this.viewDescriptors.filter(v => this.isViewDescriptorVisible(v));
}
private _onDidAdd = this._register(new Emitter<IAddedViewDescriptorRef[]>());
@@ -250,27 +253,35 @@ export class ContributableViewsModel extends Disposable {
}
isVisible(id: string): boolean {
const state = this.viewStates.get(id);
const viewDescriptor = this.viewDescriptors.filter(v => v.id === id)[0];
if (!state) {
if (!viewDescriptor) {
throw new Error(`Unknown view ${id}`);
}
return state.visible;
return this.isViewDescriptorVisible(viewDescriptor);
}
setVisible(id: string, visible: boolean): void {
setVisible(id: string, visible: boolean, size?: number): void {
const { visibleIndex, viewDescriptor, state } = this.find(id);
if (!viewDescriptor.canToggleVisibility) {
throw new Error(`Can't toggle this view's visibility`);
}
if (state.visible === visible) {
if (this.isViewDescriptorVisible(viewDescriptor) === visible) {
return;
}
state.visible = visible;
if (viewDescriptor.workspace) {
state.visibleWorkspace = visible;
} else {
state.visibleGlobal = visible;
}
if (typeof size === 'number') {
state.size = size;
}
if (visible) {
this._onDidAdd.fire([{ index: visibleIndex, viewDescriptor, size: state.size, collapsed: state.collapsed }]);
@@ -329,6 +340,14 @@ export class ContributableViewsModel extends Disposable {
});
}
private isViewDescriptorVisible(viewDescriptor: IViewDescriptor): boolean {
const viewState = this.viewStates.get(viewDescriptor.id);
if (!viewState) {
throw new Error(`Unknown view ${viewDescriptor.id}`);
}
return viewDescriptor.workspace ? viewState.visibleWorkspace : viewState.visibleGlobal;
}
private find(id: string): { index: number, visibleIndex: number, viewDescriptor: IViewDescriptor, state: IViewState } {
for (let i = 0, visibleIndex = 0; i < this.viewDescriptors.length; i++) {
const viewDescriptor = this.viewDescriptors[i];
@@ -341,7 +360,7 @@ export class ContributableViewsModel extends Disposable {
return { index: i, visibleIndex, viewDescriptor, state };
}
if (state.visible) {
if (viewDescriptor.workspace ? state.visibleWorkspace : state.visibleGlobal) {
visibleIndex++;
}
}
@@ -376,11 +395,16 @@ export class ContributableViewsModel extends Disposable {
const viewState = this.viewStates.get(viewDescriptor.id);
if (viewState) {
// set defaults if not set
viewState.visible = isUndefinedOrNull(viewState.visible) ? !viewDescriptor.hideByDefault : viewState.visible;
if (viewDescriptor.workspace) {
viewState.visibleWorkspace = isUndefinedOrNull(viewState.visibleWorkspace) ? !viewDescriptor.hideByDefault : viewState.visibleWorkspace;
} else {
viewState.visibleGlobal = isUndefinedOrNull(viewState.visibleGlobal) ? !viewDescriptor.hideByDefault : viewState.visibleGlobal;
}
viewState.collapsed = isUndefinedOrNull(viewState.collapsed) ? !!viewDescriptor.collapsed : viewState.collapsed;
} else {
this.viewStates.set(viewDescriptor.id, {
visible: !viewDescriptor.hideByDefault,
visibleGlobal: !viewDescriptor.hideByDefault,
visibleWorkspace: !viewDescriptor.hideByDefault,
collapsed: !!viewDescriptor.collapsed
});
}
@@ -401,9 +425,8 @@ export class ContributableViewsModel extends Disposable {
for (let i = 0; i < splice.deleteCount; i++) {
const viewDescriptor = this.viewDescriptors[splice.start + i];
const { state } = this.find(viewDescriptor.id);
if (state.visible) {
if (this.isViewDescriptorVisible(viewDescriptor)) {
toRemove.push({ index: startIndex++, viewDescriptor });
}
}
@@ -411,7 +434,7 @@ export class ContributableViewsModel extends Disposable {
for (const viewDescriptor of splice.toInsert) {
const state = this.viewStates.get(viewDescriptor.id)!;
if (state.visible) {
if (this.isViewDescriptorVisible(viewDescriptor)) {
toAdd.push({ index: startIndex++, viewDescriptor, size: state.size, collapsed: state.collapsed });
}
}
@@ -435,24 +458,21 @@ export class PersistentContributableViewsModel extends ContributableViewsModel {
private readonly hiddenViewsStorageId: string;
private storageService: IStorageService;
private contextService: IWorkspaceContextService;
constructor(
container: ViewContainer,
viewletStateStorageId: string,
@IViewsService viewsService: IViewsService,
@IStorageService storageService: IStorageService,
@IWorkspaceContextService contextService: IWorkspaceContextService
) {
const hiddenViewsStorageId = `${viewletStateStorageId}.hidden`;
const viewStates = PersistentContributableViewsModel.loadViewsStates(viewletStateStorageId, hiddenViewsStorageId, storageService, contextService);
const viewStates = PersistentContributableViewsModel.loadViewsStates(viewletStateStorageId, hiddenViewsStorageId, storageService);
super(container, viewsService, viewStates);
this.viewletStateStorageId = viewletStateStorageId;
this.hiddenViewsStorageId = hiddenViewsStorageId;
this.storageService = storageService;
this.contextService = contextService;
this._register(this.onDidAdd(viewDescriptorRefs => this.saveVisibilityStates(viewDescriptorRefs.map(r => r.viewDescriptor))));
this._register(this.onDidRemove(viewDescriptorRefs => this.saveVisibilityStates(viewDescriptorRefs.map(r => r.viewDescriptor))));
@@ -479,27 +499,49 @@ export class PersistentContributableViewsModel extends ContributableViewsModel {
}
private saveVisibilityStates(viewDescriptors: IViewDescriptor[]): void {
const storedViewsVisibilityStates = PersistentContributableViewsModel.loadViewsVisibilityState(this.hiddenViewsStorageId, this.storageService, this.contextService);
const globalViews: IViewDescriptor[] = viewDescriptors.filter(v => !v.workspace);
const workspaceViews: IViewDescriptor[] = viewDescriptors.filter(v => v.workspace);
if (globalViews.length) {
this.saveVisibilityStatesInScope(globalViews, StorageScope.GLOBAL);
}
if (workspaceViews.length) {
this.saveVisibilityStatesInScope(workspaceViews, StorageScope.WORKSPACE);
}
}
private saveVisibilityStatesInScope(viewDescriptors: IViewDescriptor[], scope: StorageScope): void {
const storedViewsVisibilityStates = PersistentContributableViewsModel.loadViewsVisibilityState(this.hiddenViewsStorageId, this.storageService, scope);
for (const viewDescriptor of viewDescriptors) {
if (viewDescriptor.canToggleVisibility) {
const viewState = this.viewStates.get(viewDescriptor.id);
storedViewsVisibilityStates.set(viewDescriptor.id, { id: viewDescriptor.id, isHidden: viewState ? !viewState.visible : false });
storedViewsVisibilityStates.set(viewDescriptor.id, { id: viewDescriptor.id, isHidden: viewState ? (scope === StorageScope.GLOBAL ? !viewState.visibleGlobal : !viewState.visibleWorkspace) : false });
}
}
this.storageService.store(this.hiddenViewsStorageId, JSON.stringify(values(storedViewsVisibilityStates)), StorageScope.GLOBAL);
this.storageService.store(this.hiddenViewsStorageId, JSON.stringify(values(storedViewsVisibilityStates)), scope);
}
private static loadViewsStates(viewletStateStorageId: string, hiddenViewsStorageId: string, storageService: IStorageService, contextService: IWorkspaceContextService): Map<string, IViewState> {
private static loadViewsStates(viewletStateStorageId: string, hiddenViewsStorageId: string, storageService: IStorageService): Map<string, IViewState> {
const viewStates = new Map<string, IViewState>();
const storedViewsStates = JSON.parse(storageService.get(viewletStateStorageId, StorageScope.WORKSPACE, '{}'));
const viewsVisibilityStates = PersistentContributableViewsModel.loadViewsVisibilityState(hiddenViewsStorageId, storageService, contextService);
for (const { id, isHidden } of values(viewsVisibilityStates)) {
const globalVisibilityStates = this.loadViewsVisibilityState(hiddenViewsStorageId, storageService, StorageScope.GLOBAL);
const workspaceVisibilityStates = this.loadViewsVisibilityState(hiddenViewsStorageId, storageService, StorageScope.WORKSPACE);
for (const { id, isHidden } of values(globalVisibilityStates)) {
const viewState = storedViewsStates[id];
if (viewState) {
viewStates.set(id, <IViewState>{ ...viewState, ...{ visible: !isHidden } });
viewStates.set(id, <IViewState>{ ...viewState, ...{ visibleGlobal: !isHidden } });
} else {
// New workspace
viewStates.set(id, <IViewState>{ ...{ visible: !isHidden } });
viewStates.set(id, <IViewState>{ ...{ visibleGlobal: !isHidden } });
}
}
for (const { id, isHidden } of values(workspaceVisibilityStates)) {
const viewState = storedViewsStates[id];
if (viewState) {
viewStates.set(id, <IViewState>{ ...viewState, ...{ visibleWorkspace: !isHidden } });
} else {
// New workspace
viewStates.set(id, <IViewState>{ ...{ visibleWorkspace: !isHidden } });
}
}
for (const id of Object.keys(storedViewsStates)) {
@@ -510,8 +552,8 @@ export class PersistentContributableViewsModel extends ContributableViewsModel {
return viewStates;
}
private static loadViewsVisibilityState(hiddenViewsStorageId: string, storageService: IStorageService, contextService: IWorkspaceContextService): Map<string, { id: string, isHidden: boolean }> {
const storedVisibilityStates = <Array<string | { id: string, isHidden: boolean }>>JSON.parse(storageService.get(hiddenViewsStorageId, StorageScope.GLOBAL, '[]'));
private static loadViewsVisibilityState(hiddenViewsStorageId: string, storageService: IStorageService, scope: StorageScope): Map<string, { id: string, isHidden: boolean }> {
const storedVisibilityStates = <Array<string | { id: string, isHidden: boolean }>>JSON.parse(storageService.get(hiddenViewsStorageId, scope, '[]'));
let hasDuplicates = false;
const storedViewsVisibilityStates = storedVisibilityStates.reduce((result, storedState) => {
if (typeof storedState === 'string' /* migration */) {
@@ -525,7 +567,7 @@ export class PersistentContributableViewsModel extends ContributableViewsModel {
}, new Map<string, { id: string, isHidden: boolean }>());
if (hasDuplicates) {
storageService.store(hiddenViewsStorageId, JSON.stringify(values(storedViewsVisibilityStates)), StorageScope.GLOBAL);
storageService.store(hiddenViewsStorageId, JSON.stringify(values(storedViewsVisibilityStates)), scope);
}
return storedViewsVisibilityStates;
@@ -534,7 +576,7 @@ export class PersistentContributableViewsModel extends ContributableViewsModel {
export class ViewsService extends Disposable implements IViewsService {
_serviceBrand: any;
_serviceBrand: ServiceIdentifier<any>;
private readonly viewDescriptorCollections: Map<ViewContainer, { viewDescriptorCollection: IViewDescriptorCollection, disposable: IDisposable }>;
private readonly viewDisposable: Map<IViewDescriptor, IDisposable>;
@@ -550,14 +592,15 @@ export class ViewsService extends Disposable implements IViewsService {
this.viewDisposable = new Map<IViewDescriptor, IDisposable>();
this.activeViewContextKeys = new Map<string, IContextKey<boolean>>();
const viewContainersRegistry = Registry.as<IViewContainersRegistry>(ViewContainerExtensions.ViewContainersRegistry);
const viewContainersRegistry = Registry.as<IViewContainersRegistry>(ViewExtensions.ViewContainersRegistry);
const viewsRegistry = Registry.as<IViewsRegistry>(ViewExtensions.ViewsRegistry);
viewContainersRegistry.all.forEach(viewContainer => {
this.onDidRegisterViews(viewContainer, ViewsRegistry.getViews(viewContainer));
this.onDidRegisterViews(viewContainer, viewsRegistry.getViews(viewContainer));
this.onDidRegisterViewContainer(viewContainer);
});
this._register(ViewsRegistry.onViewsRegistered(({ views, viewContainer }) => this.onDidRegisterViews(viewContainer, views)));
this._register(ViewsRegistry.onViewsDeregistered(({ views }) => this.onDidDeregisterViews(views)));
this._register(ViewsRegistry.onDidChangeContainer(({ views, to }) => { this.onDidDeregisterViews(views); this.onDidRegisterViews(to, views); }));
this._register(viewsRegistry.onViewsRegistered(({ views, viewContainer }) => this.onDidRegisterViews(viewContainer, views)));
this._register(viewsRegistry.onViewsDeregistered(({ views }) => this.onDidDeregisterViews(views)));
this._register(viewsRegistry.onDidChangeContainer(({ views, to }) => { this.onDidDeregisterViews(views); this.onDidRegisterViews(to, views); }));
this._register(toDisposable(() => {
this.viewDisposable.forEach(disposable => disposable.dispose());
this.viewDisposable.clear();
@@ -576,7 +619,7 @@ export class ViewsService extends Disposable implements IViewsService {
}
openView(id: string, focus: boolean): Promise<IView | null> {
const viewContainer = ViewsRegistry.getViewContainer(id);
const viewContainer = Registry.as<IViewsRegistry>(ViewExtensions.ViewsRegistry).getViewContainer(id);
if (viewContainer) {
const viewletDescriptor = this.viewletService.getViewlet(viewContainer.id);
if (viewletDescriptor) {
@@ -682,4 +725,6 @@ export function createFileIconThemableTreeContainerScope(container: HTMLElement,
onDidChangeFileIconTheme(themeService.getFileIconTheme());
return themeService.onDidFileIconThemeChange(onDidChangeFileIconTheme);
}
}
registerSingleton(IViewsService, ViewsService);

View File

@@ -25,7 +25,7 @@ import { IWorkbenchThemeService, IFileIconTheme } from 'vs/workbench/services/th
import { ITreeConfiguration, ITreeOptions } from 'vs/base/parts/tree/browser/tree';
import { Event } from 'vs/base/common/event';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IPartService } from 'vs/workbench/services/part/common/partService';
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
import { localize } from 'vs/nls';
import { IAddedViewDescriptorRef, IViewDescriptorRef, PersistentContributableViewsModel } from 'vs/workbench/browser/parts/views/views';
import { Registry } from 'vs/platform/registry/common/platform';
@@ -43,7 +43,7 @@ export abstract class ViewContainerViewlet extends PanelViewlet implements IView
private readonly visibleViewsCountFromCache: number;
private readonly visibleViewsStorageId: string;
private readonly viewsModel: PersistentContributableViewsModel;
protected readonly viewsModel: PersistentContributableViewsModel;
private viewDisposables: IDisposable[] = [];
constructor(
@@ -51,7 +51,7 @@ export abstract class ViewContainerViewlet extends PanelViewlet implements IView
viewletStateStorageId: string,
showHeaderInTitleWhenSingleView: boolean,
@IConfigurationService configurationService: IConfigurationService,
@IPartService partService: IPartService,
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
@ITelemetryService telemetryService: ITelemetryService,
@IStorageService protected storageService: IStorageService,
@IInstantiationService protected instantiationService: IInstantiationService,
@@ -60,14 +60,14 @@ export abstract class ViewContainerViewlet extends PanelViewlet implements IView
@IExtensionService protected extensionService: IExtensionService,
@IWorkspaceContextService protected contextService: IWorkspaceContextService
) {
super(id, { showHeaderInTitleWhenSingleView, dnd: new DefaultPanelDndController() }, configurationService, partService, contextMenuService, telemetryService, themeService, storageService);
super(id, { showHeaderInTitleWhenSingleView, dnd: new DefaultPanelDndController() }, configurationService, layoutService, contextMenuService, telemetryService, themeService, storageService);
const container = Registry.as<IViewContainersRegistry>(ViewContainerExtensions.ViewContainersRegistry).get(id);
this.viewsModel = this._register(this.instantiationService.createInstance(PersistentContributableViewsModel, container, viewletStateStorageId));
this.viewletState = this.getMemento(StorageScope.WORKSPACE);
this.visibleViewsStorageId = `${id}.numberOfVisibleViews`;
this.visibleViewsCountFromCache = this.storageService.getInteger(this.visibleViewsStorageId, StorageScope.WORKSPACE, 1);
this.visibleViewsCountFromCache = this.storageService.getNumber(this.visibleViewsStorageId, StorageScope.WORKSPACE, 1);
this._register(toDisposable(() => this.viewDisposables = dispose(this.viewDisposables)));
}
@@ -178,7 +178,7 @@ export abstract class ViewContainerViewlet extends PanelViewlet implements IView
}
protected createView(viewDescriptor: IViewDescriptor, options: IViewletViewOptions): ViewletPanel {
return this.instantiationService.createInstance(viewDescriptor.ctor, options) as ViewletPanel;
return (this.instantiationService as any).createInstance(viewDescriptor.ctorDescriptor.ctor, ...(viewDescriptor.ctorDescriptor.arguments || []), options) as ViewletPanel;
}
protected getView(id: string): ViewletPanel {
@@ -197,7 +197,6 @@ export abstract class ViewContainerViewlet extends PanelViewlet implements IView
viewletState: this.viewletState
});
panel.render();
panel.setVisible(true);
const contextMenuDisposable = DOM.addDisposableListener(panel.draggableElement, 'contextmenu', e => {
e.stopPropagation();
e.preventDefault();