mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-03-24 13:50:29 -04:00
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:
@@ -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> {
|
||||
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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>();
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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>;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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)}')
|
||||
}
|
||||
`);
|
||||
|
||||
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
});
|
||||
|
||||
@@ -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
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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' }]);
|
||||
}
|
||||
}));
|
||||
|
||||
|
||||
@@ -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 |
@@ -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 |
39
src/vs/workbench/browser/parts/editor/media/letterpress.svg
Normal file
39
src/vs/workbench/browser/parts/editor/media/letterpress.svg
Normal 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 |
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
});
|
||||
@@ -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);
|
||||
@@ -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';
|
||||
|
||||
@@ -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);
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
@@ -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;
|
||||
|
||||
@@ -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 });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user