Update to VS Code 1.52.1

This commit is contained in:
Asher
2021-02-09 16:08:37 +00:00
1351 changed files with 56560 additions and 38990 deletions

View File

@@ -10,15 +10,14 @@ import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { EventType as TouchEventType, GestureEvent } from 'vs/base/browser/touch';
import { Action, IAction, Separator, SubmenuAction } from 'vs/base/common/actions';
import { KeyCode } from 'vs/base/common/keyCodes';
import { dispose } from 'vs/base/common/lifecycle';
import { SyncActionDescriptor, IMenuService, MenuId, IMenu } from 'vs/platform/actions/common/actions';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { IMenuService, MenuId, IMenu, registerAction2, Action2, IAction2Options } from 'vs/platform/actions/common/actions';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { Registry } from 'vs/platform/registry/common/platform';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { activeContrastBorder, focusBorder } from 'vs/platform/theme/common/colorRegistry';
import { ICssStyleCollector, IColorTheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { ActivityAction, ActivityActionViewItem, ICompositeBar, ICompositeBarColors, ToggleCompositePinnedAction } from 'vs/workbench/browser/parts/compositeBarActions';
import { CATEGORIES, Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions';
import { CATEGORIES } from 'vs/workbench/common/actions';
import { IActivity } from 'vs/workbench/common/activity';
import { ACTIVITY_BAR_FOREGROUND, ACTIVITY_BAR_ACTIVE_BORDER, ACTIVITY_BAR_ACTIVE_FOCUS_BORDER, ACTIVITY_BAR_ACTIVE_BACKGROUND, ACTIVITY_BAR_BACKGROUND } from 'vs/workbench/common/theme';
import { IActivityBarService } from 'vs/workbench/services/activityBar/browser/activityBarService';
@@ -26,44 +25,31 @@ import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/bro
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem';
import { Codicon } from 'vs/base/common/codicons';
import { isMacintosh } from 'vs/base/common/platform';
import { isMacintosh, isWeb } from 'vs/base/common/platform';
import { getCurrentAuthenticationSessionInfo, IAuthenticationService } from 'vs/workbench/services/authentication/browser/authenticationService';
import { AuthenticationSession } from 'vs/editor/common/modes';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IProductService } from 'vs/platform/product/common/productService';
import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview';
import { AnchorAlignment, AnchorAxisAlignment } from 'vs/base/browser/ui/contextview/contextview';
import { getTitleBarStyle } from 'vs/platform/windows/common/windows';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
export class ViewContainerActivityAction extends ActivityAction {
private static readonly preventDoubleClickDelay = 300;
private readonly viewletService: IViewletService;
private readonly layoutService: IWorkbenchLayoutService;
private readonly telemetryService: ITelemetryService;
private readonly configurationService: IConfigurationService;
private lastRun: number;
private lastRun = 0;
constructor(
activity: IActivity,
@IViewletService viewletService: IViewletService,
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
@ITelemetryService telemetryService: ITelemetryService,
@IConfigurationService configurationService: IConfigurationService
@IViewletService private readonly viewletService: IViewletService,
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,
@ITelemetryService private readonly telemetryService: ITelemetryService,
@IConfigurationService private readonly configurationService: IConfigurationService
) {
super(activity);
this.lastRun = 0;
this.viewletService = viewletService;
this.layoutService = layoutService;
this.telemetryService = telemetryService;
this.configurationService = configurationService;
}
updateActivity(activity: IActivity): void {
@@ -105,6 +91,7 @@ export class ViewContainerActivityAction extends ActivityAction {
this.logAction('show');
await this.viewletService.openViewlet(this.activity.id, true);
return this.activate();
}
@@ -117,21 +104,18 @@ export class ViewContainerActivityAction extends ActivityAction {
}
}
export const ACCOUNTS_VISIBILITY_PREFERENCE_KEY = 'workbench.activity.showAccounts';
class MenuActivityActionViewItem extends ActivityActionViewItem {
export class AccountsActionViewItem extends ActivityActionViewItem {
constructor(
private readonly menuId: MenuId,
action: ActivityAction,
colors: (theme: IColorTheme) => ICompositeBarColors,
@IThemeService themeService: IThemeService,
@IContextMenuService protected contextMenuService: IContextMenuService,
@IMenuService protected menuService: IMenuService,
@IContextKeyService private readonly contextKeyService: IContextKeyService,
@IAuthenticationService private readonly authenticationService: IAuthenticationService,
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
@IStorageService private readonly storageService: IStorageService,
@IProductService private readonly productService: IProductService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IMenuService protected readonly menuService: IMenuService,
@IContextMenuService protected readonly contextMenuService: IContextMenuService,
@IContextKeyService protected readonly contextKeyService: IContextKeyService,
@IConfigurationService protected readonly configurationService: IConfigurationService,
@IWorkbenchEnvironmentService protected readonly environmentService: IWorkbenchEnvironmentService
) {
super(action, { draggable: false, colors, icon: true }, themeService);
}
@@ -161,12 +145,103 @@ export class AccountsActionViewItem extends ActivityActionViewItem {
}));
}
private async getActions(accountsMenu: IMenu) {
protected async showContextMenu(e?: MouseEvent): Promise<void> {
const disposables = new DisposableStore();
const menu = disposables.add(this.menuService.createMenu(this.menuId, this.contextKeyService));
const actions = await this.resolveActions(menu, disposables);
const isUsingCustomMenu = isWeb || (getTitleBarStyle(this.configurationService) !== 'native' && !isMacintosh); // see #40262
const position = this.configurationService.getValue('workbench.sideBar.location');
this.contextMenuService.showContextMenu({
getAnchor: () => isUsingCustomMenu ? this.container : e || this.container,
anchorAlignment: isUsingCustomMenu ? (position === 'left' ? AnchorAlignment.RIGHT : AnchorAlignment.LEFT) : undefined,
anchorAxisAlignment: isUsingCustomMenu ? AnchorAxisAlignment.HORIZONTAL : AnchorAxisAlignment.VERTICAL,
getActions: () => actions,
onHide: () => disposables.dispose()
});
}
protected async resolveActions(menu: IMenu, disposables: DisposableStore): Promise<IAction[]> {
const actions: IAction[] = [];
disposables.add(createAndFillInActionBarActions(menu, undefined, { primary: [], secondary: actions }));
return actions;
}
}
export class HomeActivityActionViewItem extends MenuActivityActionViewItem {
static readonly HOME_BAR_VISIBILITY_PREFERENCE = 'workbench.activity.showHomeIndicator';
constructor(
private readonly goHomeHref: string,
action: ActivityAction,
colors: (theme: IColorTheme) => ICompositeBarColors,
@IThemeService themeService: IThemeService,
@IMenuService menuService: IMenuService,
@IContextMenuService contextMenuService: IContextMenuService,
@IContextKeyService contextKeyService: IContextKeyService,
@IConfigurationService configurationService: IConfigurationService,
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService,
@IStorageService private readonly storageService: IStorageService
) {
super(MenuId.MenubarHomeMenu, action, colors, themeService, menuService, contextMenuService, contextKeyService, configurationService, environmentService);
}
protected async resolveActions(homeMenu: IMenu, disposables: DisposableStore): Promise<IAction[]> {
const actions = [];
// Go Home
actions.push(disposables.add(new Action('goHome', nls.localize('goHome', "Go Home"), undefined, true, async () => window.location.href = this.goHomeHref)));
actions.push(disposables.add(new Separator()));
// Contributed
const contributedActions = await super.resolveActions(homeMenu, disposables);
actions.push(...contributedActions);
// Hide
if (contributedActions.length > 0) {
actions.push(disposables.add(new Separator()));
}
actions.push(disposables.add(new Action('hide', nls.localize('hide', "Hide"), undefined, true, async () => {
this.storageService.store(HomeActivityActionViewItem.HOME_BAR_VISIBILITY_PREFERENCE, false, StorageScope.GLOBAL, StorageTarget.USER);
})));
return actions;
}
}
export class AccountsActivityActionViewItem extends MenuActivityActionViewItem {
static readonly ACCOUNTS_VISIBILITY_PREFERENCE_KEY = 'workbench.activity.showAccounts';
constructor(
action: ActivityAction,
colors: (theme: IColorTheme) => ICompositeBarColors,
@IThemeService themeService: IThemeService,
@IContextMenuService contextMenuService: IContextMenuService,
@IMenuService menuService: IMenuService,
@IContextKeyService contextKeyService: IContextKeyService,
@IAuthenticationService private readonly authenticationService: IAuthenticationService,
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService,
@IStorageService private readonly storageService: IStorageService,
@IProductService private readonly productService: IProductService,
@IConfigurationService configurationService: IConfigurationService,
) {
super(MenuId.AccountsContext, action, colors, themeService, menuService, contextMenuService, contextKeyService, configurationService, environmentService);
}
protected async resolveActions(accountsMenu: IMenu, disposables: DisposableStore): Promise<IAction[]> {
await super.resolveActions(accountsMenu, disposables);
const otherCommands = accountsMenu.getActions();
const providers = this.authenticationService.getProviderIds();
const allSessions = providers.map(async id => {
const allSessions = providers.map(async providerId => {
try {
const sessions = await this.authenticationService.getSessions(id);
const sessions = await this.authenticationService.getSessions(providerId);
const groupedSessions: { [label: string]: AuthenticationSession[] } = {};
sessions.forEach(session => {
@@ -177,14 +252,9 @@ export class AccountsActionViewItem extends ActivityActionViewItem {
}
});
return {
providerId: id,
sessions: groupedSessions
};
return { providerId, sessions: groupedSessions };
} catch {
return {
providerId: id
};
return { providerId };
}
});
@@ -196,132 +266,67 @@ export class AccountsActionViewItem extends ActivityActionViewItem {
if (sessionInfo.sessions) {
Object.keys(sessionInfo.sessions).forEach(accountName => {
const hasEmbedderAccountSession = sessionInfo.sessions[accountName].some(session => session.id === (authenticationSession?.id));
const manageExtensionsAction = new Action(`configureSessions${accountName}`, nls.localize('manageTrustedExtensions', "Manage Trusted Extensions"), '', true, _ => {
const manageExtensionsAction = disposables.add(new Action(`configureSessions${accountName}`, nls.localize('manageTrustedExtensions', "Manage Trusted Extensions"), '', true, () => {
return this.authenticationService.manageTrustedExtensionsForAccount(sessionInfo.providerId, accountName);
});
const signOutAction = new Action('signOut', nls.localize('signOut', "Sign Out"), '', true, _ => {
return this.authenticationService.signOutOfAccount(sessionInfo.providerId, accountName);
});
}));
const actions = [manageExtensionsAction];
const signOutAction = disposables.add(new Action('signOut', nls.localize('signOut', "Sign Out"), '', true, () => {
return this.authenticationService.signOutOfAccount(sessionInfo.providerId, accountName);
}));
const providerSubMenuActions = [manageExtensionsAction];
const hasEmbedderAccountSession = sessionInfo.sessions[accountName].some(session => session.id === (authenticationSession?.id));
if (!hasEmbedderAccountSession || authenticationSession?.canSignOut) {
actions.push(signOutAction);
providerSubMenuActions.push(signOutAction);
}
const menu = new SubmenuAction('activitybar.submenu', `${accountName} (${providerDisplayName})`, actions);
menus.push(menu);
const providerSubMenu = disposables.add(new SubmenuAction('activitybar.submenu', `${accountName} (${providerDisplayName})`, providerSubMenuActions));
menus.push(providerSubMenu);
});
} else {
const menu = new Action('providerUnavailable', nls.localize('authProviderUnavailable', '{0} is currently unavailable', providerDisplayName));
menus.push(menu);
const providerUnavailableAction = disposables.add(new Action('providerUnavailable', nls.localize('authProviderUnavailable', '{0} is currently unavailable', providerDisplayName)));
menus.push(providerUnavailableAction);
}
});
if (menus.length && otherCommands.length) {
menus.push(new Separator());
menus.push(disposables.add(new Separator()));
}
otherCommands.forEach((group, i) => {
const actions = group[1];
menus = menus.concat(actions);
if (i !== otherCommands.length - 1) {
menus.push(new Separator());
menus.push(disposables.add(new Separator()));
}
});
if (menus.length) {
menus.push(new Separator());
menus.push(disposables.add(new Separator()));
}
menus.push(new Action('hide', nls.localize('hide', "Hide"), undefined, true, _ => {
this.storageService.store(ACCOUNTS_VISIBILITY_PREFERENCE_KEY, false, StorageScope.GLOBAL);
return Promise.resolve();
}));
menus.push(disposables.add(new Action('hide', nls.localize('hide', "Hide"), undefined, true, async () => {
this.storageService.store(AccountsActivityActionViewItem.ACCOUNTS_VISIBILITY_PREFERENCE_KEY, false, StorageScope.GLOBAL, StorageTarget.USER);
})));
return menus;
}
private async showContextMenu(e?: MouseEvent): Promise<void> {
const accountsActions: IAction[] = [];
const accountsMenu = this.menuService.createMenu(MenuId.AccountsContext, this.contextKeyService);
const actionsDisposable = createAndFillInActionBarActions(accountsMenu, undefined, { primary: [], secondary: accountsActions });
const native = getTitleBarStyle(this.configurationService, this.environmentService) === 'native';
const position = this.configurationService.getValue('workbench.sideBar.location');
const containerPosition = DOM.getDomNodePagePosition(this.container);
const location = { x: containerPosition.left + (position === 'left' ? containerPosition.width : 0), y: containerPosition.top };
const actions = await this.getActions(accountsMenu);
this.contextMenuService.showContextMenu({
getAnchor: () => !native ? location : e || this.container,
anchorAlignment: !native ? (position === 'left' ? AnchorAlignment.RIGHT : AnchorAlignment.LEFT) : undefined,
getActions: () => actions,
onHide: () => {
accountsMenu.dispose();
dispose(actionsDisposable);
}
});
}
}
export class GlobalActivityActionViewItem extends ActivityActionViewItem {
export class GlobalActivityActionViewItem extends MenuActivityActionViewItem {
constructor(
action: ActivityAction,
colors: (theme: IColorTheme) => ICompositeBarColors,
@IThemeService themeService: IThemeService,
@IMenuService private readonly menuService: IMenuService,
@IContextMenuService protected readonly contextMenuService: IContextMenuService,
@IContextKeyService private readonly contextKeyService: IContextKeyService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IEnvironmentService private readonly environmentService: IEnvironmentService
@IMenuService menuService: IMenuService,
@IContextMenuService contextMenuService: IContextMenuService,
@IContextKeyService contextKeyService: IContextKeyService,
@IConfigurationService configurationService: IConfigurationService,
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService
) {
super(action, { draggable: false, colors, icon: true }, themeService);
}
render(container: HTMLElement): void {
super.render(container);
// Context menus are triggered on mouse down so that an item can be picked
// and executed with releasing the mouse over it
this._register(DOM.addDisposableListener(this.container, DOM.EventType.MOUSE_DOWN, (e: MouseEvent) => {
DOM.EventHelper.stop(e, true);
this.showContextMenu(e);
}));
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._register(DOM.addDisposableListener(this.container, TouchEventType.Tap, (e: GestureEvent) => {
DOM.EventHelper.stop(e, true);
this.showContextMenu();
}));
}
private showContextMenu(e?: MouseEvent): void {
const globalActivityActions: IAction[] = [];
const globalActivityMenu = this.menuService.createMenu(MenuId.GlobalActivity, this.contextKeyService);
const actionsDisposable = createAndFillInActionBarActions(globalActivityMenu, undefined, { primary: [], secondary: globalActivityActions });
const native = getTitleBarStyle(this.configurationService, this.environmentService) === 'native';
const position = this.configurationService.getValue('workbench.sideBar.location');
const containerPosition = DOM.getDomNodePagePosition(this.container);
const location = { x: containerPosition.left + (position === 'left' ? containerPosition.width : 0), y: containerPosition.top + containerPosition.height };
this.contextMenuService.showContextMenu({
getAnchor: () => !native ? location : e || this.container,
anchorAlignment: !native ? (position === 'left' ? AnchorAlignment.RIGHT : AnchorAlignment.LEFT) : undefined,
getActions: () => globalActivityActions,
onHide: () => {
globalActivityMenu.dispose();
dispose(actionsDisposable);
}
});
super(MenuId.GlobalActivity, action, colors, themeService, menuService, contextMenuService, contextKeyService, configurationService, environmentService);
}
}
@@ -338,106 +343,62 @@ export class PlaceHolderToggleCompositePinnedAction extends ToggleCompositePinne
}
}
class SwitchSideBarViewAction extends Action {
class SwitchSideBarViewAction extends Action2 {
constructor(
id: string,
name: string,
@IViewletService private readonly viewletService: IViewletService,
@IActivityBarService private readonly activityBarService: IActivityBarService
desc: Readonly<IAction2Options>,
private readonly offset: number
) {
super(id, name);
super(desc);
}
async run(offset: number): Promise<void> {
const visibleViewletIds = this.activityBarService.getVisibleViewContainerIds();
async run(accessor: ServicesAccessor): Promise<void> {
const activityBarService = accessor.get(IActivityBarService);
const viewletService = accessor.get(IViewletService);
const activeViewlet = this.viewletService.getActiveViewlet();
const visibleViewletIds = activityBarService.getVisibleViewContainerIds();
const activeViewlet = viewletService.getActiveViewlet();
if (!activeViewlet) {
return;
}
let targetViewletId: string | undefined;
for (let i = 0; i < visibleViewletIds.length; i++) {
if (visibleViewletIds[i] === activeViewlet.getId()) {
targetViewletId = visibleViewletIds[(i + visibleViewletIds.length + offset) % visibleViewletIds.length];
targetViewletId = visibleViewletIds[(i + visibleViewletIds.length + this.offset) % visibleViewletIds.length];
break;
}
}
await this.viewletService.openViewlet(targetViewletId, true);
await viewletService.openViewlet(targetViewletId, true);
}
}
export class PreviousSideBarViewAction extends SwitchSideBarViewAction {
static readonly ID = 'workbench.action.previousSideBarView';
static readonly LABEL = nls.localize('previousSideBarView', 'Previous Side Bar View');
constructor(
id: string,
name: string,
@IViewletService viewletService: IViewletService,
@IActivityBarService activityBarService: IActivityBarService
) {
super(id, name, viewletService, activityBarService);
}
run(): Promise<void> {
return super.run(-1);
}
}
export class NextSideBarViewAction extends SwitchSideBarViewAction {
static readonly ID = 'workbench.action.nextSideBarView';
static readonly LABEL = nls.localize('nextSideBarView', 'Next Side Bar View');
constructor(
id: string,
name: string,
@IViewletService viewletService: IViewletService,
@IActivityBarService activityBarService: IActivityBarService
) {
super(id, name, viewletService, activityBarService);
}
run(): Promise<void> {
return super.run(1);
}
}
export class HomeAction extends Action {
constructor(
private readonly href: string,
name: string,
icon: Codicon
) {
super('workbench.action.home', name, icon.classNames);
}
async run(event: MouseEvent): Promise<void> {
let openInNewWindow = false;
if (isMacintosh) {
openInNewWindow = event.metaKey;
} else {
openInNewWindow = event.ctrlKey;
}
if (openInNewWindow) {
DOM.windowOpenNoOpener(this.href);
} else {
window.location.href = this.href;
registerAction2(
class PreviousSideBarViewAction extends SwitchSideBarViewAction {
constructor() {
super({
id: 'workbench.action.previousSideBarView',
title: { value: nls.localize('previousSideBarView', "Previous Side Bar View"), original: 'Previous Side Bar View' },
category: CATEGORIES.View,
f1: true
}, -1);
}
}
}
);
export class HomeActionViewItem extends ActionViewItem {
constructor(action: IAction) {
super(undefined, action, { icon: true, label: false, useEventAsContext: true });
registerAction2(
class NextSideBarViewAction extends SwitchSideBarViewAction {
constructor() {
super({
id: 'workbench.action.nextSideBarView',
title: { value: nls.localize('nextSideBarView', "Next Side Bar View"), original: 'Next Side Bar View' },
category: CATEGORIES.View,
f1: true
}, 1);
}
}
}
);
registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) => {
const activityBarBackgroundColor = theme.getColor(ACTIVITY_BAR_BACKGROUND);
@@ -510,6 +471,7 @@ registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) =
left: 9px;
height: 32px;
width: 32px;
z-index: 1;
}
.monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item.active:before,
@@ -549,7 +511,3 @@ registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) =
}
}
});
const registry = Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions);
registry.registerWorkbenchAction(SyncActionDescriptor.from(PreviousSideBarViewAction), 'View: Previous Side Bar View', CATEGORIES.View.value);
registry.registerWorkbenchAction(SyncActionDescriptor.from(NextSideBarViewAction), 'View: Next Side Bar View', CATEGORIES.View.value);

View File

@@ -8,24 +8,24 @@ import * as nls from 'vs/nls';
import { ActionsOrientation, ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
import { GLOBAL_ACTIVITY_ID, IActivity, ACCOUNTS_ACTIVITY_ID } from 'vs/workbench/common/activity';
import { Part } from 'vs/workbench/browser/part';
import { GlobalActivityActionViewItem, ViewContainerActivityAction, PlaceHolderToggleCompositePinnedAction, PlaceHolderViewContainerActivityAction, AccountsActionViewItem, HomeAction, HomeActionViewItem, ACCOUNTS_VISIBILITY_PREFERENCE_KEY } from 'vs/workbench/browser/parts/activitybar/activitybarActions';
import { GlobalActivityActionViewItem, ViewContainerActivityAction, PlaceHolderToggleCompositePinnedAction, PlaceHolderViewContainerActivityAction, AccountsActivityActionViewItem, HomeActivityActionViewItem } from 'vs/workbench/browser/parts/activitybar/activitybarActions';
import { IBadge, NumberBadge } from 'vs/workbench/services/activity/common/activity';
import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService';
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { IDisposable, toDisposable, DisposableStore, Disposable } from 'vs/base/common/lifecycle';
import { ToggleActivityBarVisibilityAction, ToggleMenuBarAction, ToggleSidebarPositionAction } from 'vs/workbench/browser/actions/layoutActions';
import { IThemeService, IColorTheme } from 'vs/platform/theme/common/themeService';
import { IThemeService, IColorTheme, ThemeIcon } from 'vs/platform/theme/common/themeService';
import { ACTIVITY_BAR_BACKGROUND, ACTIVITY_BAR_BORDER, ACTIVITY_BAR_FOREGROUND, ACTIVITY_BAR_ACTIVE_BORDER, ACTIVITY_BAR_BADGE_BACKGROUND, ACTIVITY_BAR_BADGE_FOREGROUND, ACTIVITY_BAR_INACTIVE_FOREGROUND, ACTIVITY_BAR_ACTIVE_BACKGROUND, ACTIVITY_BAR_DRAG_AND_DROP_BORDER } from 'vs/workbench/common/theme';
import { contrastBorder } from 'vs/platform/theme/common/colorRegistry';
import { CompositeBar, ICompositeBarItem, CompositeDragAndDrop } from 'vs/workbench/browser/parts/compositeBar';
import { Dimension, createCSSRule, asCSSUrl, addDisposableListener, EventType } from 'vs/base/browser/dom';
import { IStorageService, StorageScope, IWorkspaceStorageChangeEvent } from 'vs/platform/storage/common/storage';
import { IStorageService, StorageScope, IStorageValueChangeEvent, StorageTarget } from 'vs/platform/storage/common/storage';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { URI, UriComponents } from 'vs/base/common/uri';
import { ToggleCompositePinnedAction, ICompositeBarColors, ActivityAction, ICompositeActivity } from 'vs/workbench/browser/parts/compositeBarActions';
import { IViewDescriptorService, ViewContainer, TEST_VIEW_CONTAINER_ID, IViewContainerModel, ViewContainerLocation, IViewsService } from 'vs/workbench/common/views';
import { IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { isUndefinedOrNull, assertIsDefined, isString } from 'vs/base/common/types';
import { assertIsDefined } from 'vs/base/common/types';
import { IActivityBarService } from 'vs/workbench/services/activityBar/browser/activityBarService';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { Schemas } from 'vs/base/common/network';
@@ -34,7 +34,6 @@ import { CustomMenubarControl } from 'vs/workbench/browser/parts/titlebar/menuba
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { getMenuBarVisibility } from 'vs/platform/windows/common/windows';
import { isWeb } from 'vs/base/common/platform';
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
import { Before2D } from 'vs/workbench/browser/dnd';
import { Codicon, iconRegistry } from 'vs/base/common/codicons';
import { Action, Separator } from 'vs/base/common/actions';
@@ -43,41 +42,45 @@ import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyCode } from 'vs/base/common/keyCodes';
import { Action2, registerAction2 } from 'vs/platform/actions/common/actions';
import { CATEGORIES } from 'vs/workbench/common/actions';
import { registerIcon } from 'vs/platform/theme/common/iconRegistry';
interface IPlaceholderViewContainer {
id: string;
name?: string;
iconUrl?: UriComponents;
iconCSS?: string;
views?: { when?: string }[];
readonly id: string;
readonly name?: string;
readonly iconUrl?: UriComponents;
readonly themeIcon?: ThemeIcon;
readonly views?: { when?: string }[];
}
interface IPinnedViewContainer {
id: string;
pinned: boolean;
order?: number;
visible: boolean;
readonly id: string;
readonly pinned: boolean;
readonly order?: number;
readonly visible: boolean;
}
interface ICachedViewContainer {
id: string;
readonly id: string;
name?: string;
icon?: URI | string;
pinned: boolean;
order?: number;
icon?: URI | ThemeIcon;
readonly pinned: boolean;
readonly order?: number;
visible: boolean;
views?: { when?: string }[];
}
const settingsViewBarIcon = registerIcon('settings-view-bar-icon', Codicon.settingsGear, nls.localize('settingsViewBarIcon', 'Settings icon in the view bar.'));
const accountsViewBarIcon = registerIcon('accounts-view-bar-icon', Codicon.account, nls.localize('accountsViewBarIcon', 'Accounts icon in the view bar.'));
export class ActivitybarPart extends Part implements IActivityBarService {
declare readonly _serviceBrand: undefined;
private static readonly ACTION_HEIGHT = 48;
static readonly PINNED_VIEW_CONTAINERS = 'workbench.activity.pinnedViewlets2';
private static readonly PINNED_VIEW_CONTAINERS = 'workbench.activity.pinnedViewlets2';
private static readonly PLACEHOLDER_VIEW_CONTAINERS = 'workbench.activity.placeholderViewlets';
private static readonly HOME_BAR_VISIBILITY_PREFERENCE = 'workbench.activity.showHomeIndicator';
private static readonly ACTION_HEIGHT = 48;
private static readonly ACCOUNTS_ACTION_INDEX = 0;
//#region IView
readonly minimumWidth: number = 48;
@@ -100,17 +103,17 @@ export class ActivitybarPart extends Part implements IActivityBarService {
private globalActivityAction: ActivityAction | undefined;
private globalActivityActionBar: ActionBar | undefined;
private readonly globalActivity: ICompositeActivity[] = [];
private globalActivitiesContainer: HTMLElement | undefined;
private readonly globalActivity: ICompositeActivity[] = [];
private accountsActivityAction: ActivityAction | undefined;
private accountsActivity: ICompositeActivity[] = [];
private readonly accountsActivity: ICompositeActivity[] = [];
private readonly compositeActions = new Map<string, { activityAction: ViewContainerActivityAction, pinnedAction: ToggleCompositePinnedAction }>();
private readonly viewContainerDisposables = new Map<string, IDisposable>();
private readonly keyboardNavigationDisposables = new DisposableStore();
private readonly keyboardNavigationDisposables = this._register(new DisposableStore());
private readonly location = ViewContainerLocation.Sidebar;
@@ -125,27 +128,36 @@ export class ActivitybarPart extends Part implements IActivityBarService {
@IContextKeyService private readonly contextKeyService: IContextKeyService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
@IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService
) {
super(Parts.ACTIVITYBAR_PART, { hasTitle: false }, themeService, storageService, layoutService);
storageKeysSyncRegistryService.registerStorageKey({ key: ActivitybarPart.PINNED_VIEW_CONTAINERS, version: 1 });
storageKeysSyncRegistryService.registerStorageKey({ key: ActivitybarPart.HOME_BAR_VISIBILITY_PREFERENCE, version: 1 });
storageKeysSyncRegistryService.registerStorageKey({ key: ACCOUNTS_VISIBILITY_PREFERENCE_KEY, version: 1 });
this.migrateFromOldCachedViewContainersValue();
for (const cachedViewContainer of this.cachedViewContainers) {
if (environmentService.remoteAuthority // In remote window, hide activity bar entries until registered.
|| this.shouldBeHidden(cachedViewContainer.id, cachedViewContainer)
if (
environmentService.remoteAuthority || // In remote window, hide activity bar entries until registered
this.shouldBeHidden(cachedViewContainer.id, cachedViewContainer)
) {
cachedViewContainer.visible = false;
}
}
this.compositeBar = this.createCompositeBar();
this.onDidRegisterViewContainers(this.getViewContainers());
this.registerListeners();
}
private createCompositeBar() {
const cachedItems = this.cachedViewContainers
.map(v => ({ id: v.id, name: v.name, visible: v.visible, order: v.order, pinned: v.pinned }));
this.compositeBar = this._register(this.instantiationService.createInstance(CompositeBar, cachedItems, {
.map(container => ({
id: container.id,
name: container.name,
visible: container.visible,
order: container.order,
pinned: container.pinned
}));
return this._register(this.instantiationService.createInstance(CompositeBar, cachedItems, {
icon: true,
orientation: ActionsOrientation.VERTICAL,
preventLoopNavigation: true,
@@ -154,8 +166,9 @@ export class ActivitybarPart extends Part implements IActivityBarService {
getCompositePinnedAction: (compositeId: string) => this.getCompositeActions(compositeId).pinnedAction,
getOnCompositeClickAction: (compositeId: string) => new Action(compositeId, '', '', true, () => this.viewsService.isViewContainerVisible(compositeId) ? Promise.resolve(this.viewsService.closeViewContainer(compositeId)) : this.viewsService.openViewContainer(compositeId)),
getContextMenuActions: () => {
const menuBarVisibility = getMenuBarVisibility(this.configurationService, this.environmentService);
const actions = [];
// Home
if (this.homeBarContainer) {
actions.push(new Action(
'toggleHomeBarAction',
@@ -166,22 +179,26 @@ export class ActivitybarPart extends Part implements IActivityBarService {
));
}
// Menu
const menuBarVisibility = getMenuBarVisibility(this.configurationService);
if (menuBarVisibility === 'compact' || (menuBarVisibility === 'hidden' && isWeb)) {
actions.push(this.instantiationService.createInstance(ToggleMenuBarAction, ToggleMenuBarAction.ID, menuBarVisibility === 'compact' ? nls.localize('hideMenu', "Hide Menu") : nls.localize('showMenu', "Show Menu")));
}
const toggleAccountsVisibilityAction = new Action(
// Accounts
actions.push(new Action(
'toggleAccountsVisibility',
this.accountsVisibilityPreference ? nls.localize('hideAccounts', "Hide Accounts") : nls.localize('showAccounts', "Show Accounts"),
undefined,
true,
async () => { this.accountsVisibilityPreference = !this.accountsVisibilityPreference; }
);
actions.push(toggleAccountsVisibilityAction);
));
actions.push(new Separator());
// Toggle Sidebar
actions.push(this.instantiationService.createInstance(ToggleSidebarPositionAction, ToggleSidebarPositionAction.ID, ToggleSidebarPositionAction.getLabel(this.layoutService)));
// Toggle Activity Bar
actions.push(new Action(
ToggleActivityBarVisibilityAction.ID,
nls.localize('hideActivitBar', "Hide Activity Bar"),
@@ -204,19 +221,12 @@ export class ActivitybarPart extends Part implements IActivityBarService {
colors: (theme: IColorTheme) => this.getActivitybarItemColors(theme),
overflowActionSize: ActivitybarPart.ACTION_HEIGHT
}));
this.onDidRegisterViewContainers(this.getViewContainers());
this.registerListeners();
}
focusActivityBar(): void {
this.compositeBar.focus();
}
private getContextMenuActionsForComposite(compositeId: string): Action[] {
const viewContainer = this.viewDescriptorService.getViewContainerById(compositeId)!;
const actions = [];
const viewContainer = this.viewDescriptorService.getViewContainerById(compositeId)!;
const defaultLocation = this.viewDescriptorService.getDefaultViewContainerLocation(viewContainer)!;
if (defaultLocation !== this.viewDescriptorService.getViewContainerLocation(viewContainer)) {
actions.push(new Action('resetLocationAction', nls.localize('resetLocation', "Reset Location"), undefined, true, async () => {
@@ -253,13 +263,13 @@ export class ActivitybarPart extends Part implements IActivityBarService {
disposables.clear();
this.onDidRegisterExtensions();
this.compositeBar.onDidChange(() => this.saveCachedViewContainers(), this, disposables);
this.storageService.onDidChangeStorage(e => this.onDidStorageChange(e), this, disposables);
this.storageService.onDidChangeValue(e => this.onDidStorageValueChange(e), this, disposables);
}));
// Register for configuration changes
this._register(this.configurationService.onDidChangeConfiguration(e => {
if (e.affectsConfiguration('window.menuBarVisibility')) {
if (getMenuBarVisibility(this.configurationService, this.environmentService) === 'compact') {
if (getMenuBarVisibility(this.configurationService) === 'compact') {
this.installMenubar();
} else {
this.uninstallMenubar();
@@ -277,6 +287,7 @@ export class ActivitybarPart extends Part implements IActivityBarService {
if (from === this.location) {
this.onDidDeregisterViewContainer(container);
}
if (to === this.location) {
this.onDidRegisterViewContainers([container]);
}
@@ -306,6 +317,7 @@ export class ActivitybarPart extends Part implements IActivityBarService {
private onDidViewContainerVisible(id: string): void {
const viewContainer = this.getViewContainer(id);
if (viewContainer) {
// Update the composite bar by adding
this.compositeBar.addComposite(viewContainer);
this.compositeBar.activateComposite(viewContainer.id);
@@ -313,7 +325,8 @@ export class ActivitybarPart extends Part implements IActivityBarService {
if (viewContainer.hideIfEmpty) {
const viewContainerModel = this.viewDescriptorService.getViewContainerModel(viewContainer);
if (viewContainerModel.activeViewDescriptors.length === 0) {
this.hideComposite(viewContainer.id); // Update the composite bar by hiding
// Update the composite bar by hiding
this.hideComposite(viewContainer.id);
}
}
}
@@ -339,6 +352,7 @@ export class ActivitybarPart extends Part implements IActivityBarService {
if (typeof priority !== 'number') {
priority = 0;
}
const activity: ICompositeActivity = { badge, clazz, priority };
const activityCache = activityId === GLOBAL_ACTIVITY_ID ? this.globalActivity : this.accountsActivity;
@@ -387,16 +401,18 @@ export class ActivitybarPart extends Part implements IActivityBarService {
private getCumulativeNumberBadge(activityCache: ICompositeActivity[], priority: number): NumberBadge {
const numberActivities = activityCache.filter(activity => activity.badge instanceof NumberBadge && activity.priority === priority);
let number = numberActivities.reduce((result, activity) => { return result + (<NumberBadge>activity.badge).number; }, 0);
let descriptorFn = (): string => {
const number = numberActivities.reduce((result, activity) => { return result + (<NumberBadge>activity.badge).number; }, 0);
const descriptorFn = (): string => {
return numberActivities.reduce((result, activity, index) => {
result = result + (<NumberBadge>activity.badge).getDescription();
if (index < numberActivities.length - 1) {
result = result + '\n';
result = `${result}\n`;
}
return result;
}, '');
};
return new NumberBadge(number, descriptorFn);
}
@@ -414,11 +430,19 @@ export class ActivitybarPart extends Part implements IActivityBarService {
}
private installMenubar() {
if (this.menuBar) {
return; // prevent menu bar from installing twice #110720
}
this.menuBarContainer = document.createElement('div');
this.menuBarContainer.classList.add('menubar');
const content = assertIsDefined(this.content);
content.prepend(this.menuBarContainer);
if (this.homeBarContainer) {
content.insertBefore(this.menuBarContainer, this.homeBarContainer.nextSibling);
} else {
content.prepend(this.menuBarContainer);
}
// Menubar: install a custom menu bar depending on configuration
this.menuBar = this._register(this.instantiationService.createInstance(CustomMenubarControl));
@@ -442,12 +466,12 @@ export class ActivitybarPart extends Part implements IActivityBarService {
codicon = Codicon.code;
}
this.createHomeBar(homeIndicator.href, homeIndicator.title, codicon);
this.createHomeBar(homeIndicator.href, codicon);
this.onDidChangeHomeBarVisibility();
}
// Install menubar if compact
if (getMenuBarVisibility(this.configurationService, this.environmentService) === 'compact') {
if (getMenuBarVisibility(this.configurationService) === 'compact') {
this.installMenubar();
}
@@ -456,11 +480,11 @@ export class ActivitybarPart extends Part implements IActivityBarService {
// Global action bar
this.globalActivitiesContainer = document.createElement('div');
this.globalActivitiesContainer.classList.add('global-activity');
this.content.appendChild(this.globalActivitiesContainer);
this.createGlobalActivityActionBar(this.globalActivitiesContainer);
// Keyboard Navigation
this.registerKeyboardNavigationListeners();
return this.content;
@@ -528,23 +552,19 @@ export class ActivitybarPart extends Part implements IActivityBarService {
}
}));
}
}
private createHomeBar(href: string, title: string, icon: Codicon): void {
private createHomeBar(href: string, icon: Codicon): void {
this.homeBarContainer = document.createElement('div');
this.homeBarContainer.setAttribute('aria-label', nls.localize('homeIndicator', "Home"));
this.homeBarContainer.setAttribute('role', 'toolbar');
this.homeBarContainer.classList.add('home-bar');
this.homeBar = this._register(new ActionBar(this.homeBarContainer, {
actionViewItemProvider: action => this.instantiationService.createInstance(HomeActivityActionViewItem, href, action as ActivityAction, (theme: IColorTheme) => this.getActivitybarItemColors(theme)),
orientation: ActionsOrientation.VERTICAL,
animated: false,
ariaLabel: nls.localize('home', "Home"),
actionViewItemProvider: action => new HomeActionViewItem(action),
allowContextMenu: true,
animated: false,
preventLoopNavigation: true,
ignoreOrientationForPreviousAndNextKey: true
}));
@@ -552,35 +572,15 @@ export class ActivitybarPart extends Part implements IActivityBarService {
const homeBarIconBadge = document.createElement('div');
homeBarIconBadge.classList.add('home-bar-icon-badge');
this.homeBarContainer.appendChild(homeBarIconBadge);
this.homeBar.push(this._register(this.instantiationService.createInstance(HomeAction, href, title, icon)));
this.homeBar.push(this._register(new ActivityAction({
id: 'workbench.actions.home',
name: nls.localize('home', "Home"),
cssClass: icon.classNames
})));
const content = assertIsDefined(this.content);
content.prepend(this.homeBarContainer);
}
updateStyles(): void {
super.updateStyles();
const container = assertIsDefined(this.getContainer());
const background = this.getColor(ACTIVITY_BAR_BACKGROUND) || '';
container.style.backgroundColor = background;
const borderColor = this.getColor(ACTIVITY_BAR_BORDER) || this.getColor(contrastBorder) || '';
container.classList.toggle('bordered', !!borderColor);
container.style.borderColor = borderColor ? borderColor : '';
}
private getActivitybarItemColors(theme: IColorTheme): ICompositeBarColors {
return {
activeForegroundColor: theme.getColor(ACTIVITY_BAR_FOREGROUND),
inactiveForegroundColor: theme.getColor(ACTIVITY_BAR_INACTIVE_FOREGROUND),
activeBorderColor: theme.getColor(ACTIVITY_BAR_ACTIVE_BORDER),
activeBackground: theme.getColor(ACTIVITY_BAR_ACTIVE_BACKGROUND),
badgeBackground: theme.getColor(ACTIVITY_BAR_BADGE_BACKGROUND),
badgeForeground: theme.getColor(ACTIVITY_BAR_BADGE_FOREGROUND),
dragAndDropBorder: theme.getColor(ACTIVITY_BAR_DRAG_AND_DROP_BORDER),
activeBackgroundColor: undefined, inactiveBackgroundColor: undefined, activeBorderBottomColor: undefined,
};
content.appendChild(this.homeBarContainer);
}
private createGlobalActivityActionBar(container: HTMLElement): void {
@@ -591,7 +591,7 @@ export class ActivitybarPart extends Part implements IActivityBarService {
}
if (action.id === 'workbench.actions.accounts') {
return this.instantiationService.createInstance(AccountsActionViewItem, action as ActivityAction, (theme: IColorTheme) => this.getActivitybarItemColors(theme));
return this.instantiationService.createInstance(AccountsActivityActionViewItem, action as ActivityAction, (theme: IColorTheme) => this.getActivitybarItemColors(theme));
}
throw new Error(`No view item for action '${action.id}'`);
@@ -603,18 +603,18 @@ export class ActivitybarPart extends Part implements IActivityBarService {
ignoreOrientationForPreviousAndNextKey: true
}));
this.globalActivityAction = new ActivityAction({
this.globalActivityAction = this._register(new ActivityAction({
id: 'workbench.actions.manage',
name: nls.localize('manage', "Manage"),
cssClass: Codicon.settingsGear.classNames
});
cssClass: ThemeIcon.asClassName(settingsViewBarIcon)
}));
if (this.accountsVisibilityPreference) {
this.accountsActivityAction = new ActivityAction({
this.accountsActivityAction = this._register(new ActivityAction({
id: 'workbench.actions.accounts',
name: nls.localize('accounts', "Accounts"),
cssClass: Codicon.account.classNames
});
cssClass: ThemeIcon.asClassName(accountsViewBarIcon)
}));
this.globalActivityActionBar.push(this.accountsActivityAction, { index: ActivitybarPart.ACCOUNTS_ACTION_INDEX });
}
@@ -628,11 +628,11 @@ export class ActivitybarPart extends Part implements IActivityBarService {
this.globalActivityActionBar.pull(ActivitybarPart.ACCOUNTS_ACTION_INDEX);
this.accountsActivityAction = undefined;
} else {
this.accountsActivityAction = new ActivityAction({
this.accountsActivityAction = this._register(new ActivityAction({
id: 'workbench.actions.accounts',
name: nls.localize('accounts', "Accounts"),
cssClass: Codicon.account.classNames
});
}));
this.globalActivityActionBar.push(this.accountsActivityAction, { index: ActivitybarPart.ACCOUNTS_ACTION_INDEX });
}
}
@@ -723,7 +723,7 @@ export class ActivitybarPart extends Part implements IActivityBarService {
return ActivitybarPart.toActivity(id, name, icon, focusCommand?.id || id);
}
private static toActivity(id: string, name: string, icon: URI | string | undefined, keybindingId: string | undefined): IActivity {
private static toActivity(id: string, name: string, icon: URI | ThemeIcon | undefined, keybindingId: string | undefined): IActivity {
let cssClass: string | undefined = undefined;
let iconUrl: URI | undefined = undefined;
if (URI.isUri(icon)) {
@@ -736,9 +736,10 @@ export class ActivitybarPart extends Part implements IActivityBarService {
-webkit-mask: ${asCSSUrl(icon)} no-repeat 50% 50%;
-webkit-mask-size: 24px;
`);
} else if (isString(icon)) {
cssClass = icon;
} else if (ThemeIcon.isThemeIcon(icon)) {
cssClass = ThemeIcon.asClassName(icon);
}
return { id, name, cssClass, iconUrl, keybindingId };
}
@@ -810,6 +811,35 @@ export class ActivitybarPart extends Part implements IActivityBarService {
.map(v => v.id);
}
focusActivityBar(): void {
this.compositeBar.focus();
}
updateStyles(): void {
super.updateStyles();
const container = assertIsDefined(this.getContainer());
const background = this.getColor(ACTIVITY_BAR_BACKGROUND) || '';
container.style.backgroundColor = background;
const borderColor = this.getColor(ACTIVITY_BAR_BORDER) || this.getColor(contrastBorder) || '';
container.classList.toggle('bordered', !!borderColor);
container.style.borderColor = borderColor ? borderColor : '';
}
private getActivitybarItemColors(theme: IColorTheme): ICompositeBarColors {
return {
activeForegroundColor: theme.getColor(ACTIVITY_BAR_FOREGROUND),
inactiveForegroundColor: theme.getColor(ACTIVITY_BAR_INACTIVE_FOREGROUND),
activeBorderColor: theme.getColor(ACTIVITY_BAR_ACTIVE_BORDER),
activeBackground: theme.getColor(ACTIVITY_BAR_ACTIVE_BACKGROUND),
badgeBackground: theme.getColor(ACTIVITY_BAR_BADGE_BACKGROUND),
badgeForeground: theme.getColor(ACTIVITY_BAR_BADGE_FOREGROUND),
dragAndDropBorder: theme.getColor(ACTIVITY_BAR_DRAG_AND_DROP_BORDER),
activeBackgroundColor: undefined, inactiveBackgroundColor: undefined, activeBorderBottomColor: undefined,
};
}
layout(width: number, height: number): void {
if (!this.layoutService.isVisible(Parts.ACTIVITYBAR_PART)) {
return;
@@ -834,6 +864,7 @@ export class ActivitybarPart extends Part implements IActivityBarService {
private getViewContainer(id: string): ViewContainer | undefined {
const viewContainer = this.viewDescriptorService.getViewContainerById(id);
return viewContainer && this.viewDescriptorService.getViewContainerLocation(viewContainer) === this.location ? viewContainer : undefined;
}
@@ -841,7 +872,7 @@ export class ActivitybarPart extends Part implements IActivityBarService {
return this.viewDescriptorService.getViewContainersByLocation(this.location);
}
private onDidStorageChange(e: IWorkspaceStorageChangeEvent): void {
private onDidStorageValueChange(e: IStorageValueChangeEvent): void {
if (e.key === ActivitybarPart.PINNED_VIEW_CONTAINERS && e.scope === StorageScope.GLOBAL
&& this.pinnedViewContainersValue !== this.getStoredPinnedViewContainersValue() /* This checks if current window changed the value or not */) {
this._pinnedViewContainersValue = undefined;
@@ -870,11 +901,11 @@ export class ActivitybarPart extends Part implements IActivityBarService {
this.compositeBar.setCompositeBarItems(newCompositeItems);
}
if (e.key === ActivitybarPart.HOME_BAR_VISIBILITY_PREFERENCE && e.scope === StorageScope.GLOBAL) {
if (e.key === HomeActivityActionViewItem.HOME_BAR_VISIBILITY_PREFERENCE && e.scope === StorageScope.GLOBAL) {
this.onDidChangeHomeBarVisibility();
}
if (e.key === ACCOUNTS_VISIBILITY_PREFERENCE_KEY && e.scope === StorageScope.GLOBAL) {
if (e.key === AccountsActivityActionViewItem.ACCOUNTS_VISIBILITY_PREFERENCE_KEY && e.scope === StorageScope.GLOBAL) {
this.toggleAccountsActivity();
}
}
@@ -917,12 +948,13 @@ export class ActivitybarPart extends Part implements IActivityBarService {
const cachedViewContainer = this._cachedViewContainers.filter(cached => cached.id === placeholderViewContainer.id)[0];
if (cachedViewContainer) {
cachedViewContainer.name = placeholderViewContainer.name;
cachedViewContainer.icon = placeholderViewContainer.iconCSS ? placeholderViewContainer.iconCSS :
cachedViewContainer.icon = placeholderViewContainer.themeIcon ? ThemeIcon.revive(placeholderViewContainer.themeIcon) :
placeholderViewContainer.iconUrl ? URI.revive(placeholderViewContainer.iconUrl) : undefined;
cachedViewContainer.views = placeholderViewContainer.views;
}
}
}
return this._cachedViewContainers;
}
@@ -933,10 +965,11 @@ export class ActivitybarPart extends Part implements IActivityBarService {
visible,
order
})));
this.setPlaceholderViewContainers(cachedViewContainers.map(({ id, icon, name, views }) => (<IPlaceholderViewContainer>{
id,
iconUrl: URI.isUri(icon) ? icon : undefined,
iconCSS: isString(icon) ? icon : undefined,
themeIcon: ThemeIcon.isThemeIcon(icon) ? icon : undefined,
name,
views
})));
@@ -971,7 +1004,7 @@ export class ActivitybarPart extends Part implements IActivityBarService {
}
private setStoredPinnedViewContainersValue(value: string): void {
this.storageService.store(ActivitybarPart.PINNED_VIEW_CONTAINERS, value, StorageScope.GLOBAL);
this.storageService.store(ActivitybarPart.PINNED_VIEW_CONTAINERS, value, StorageScope.GLOBAL, StorageTarget.USER);
}
private getPlaceholderViewContainers(): IPlaceholderViewContainer[] {
@@ -1003,37 +1036,23 @@ export class ActivitybarPart extends Part implements IActivityBarService {
}
private setStoredPlaceholderViewContainersValue(value: string): void {
this.storageService.store(ActivitybarPart.PLACEHOLDER_VIEW_CONTAINERS, value, StorageScope.GLOBAL);
this.storageService.store(ActivitybarPart.PLACEHOLDER_VIEW_CONTAINERS, value, StorageScope.GLOBAL, StorageTarget.MACHINE);
}
private get homeBarVisibilityPreference(): boolean {
return this.storageService.getBoolean(ActivitybarPart.HOME_BAR_VISIBILITY_PREFERENCE, StorageScope.GLOBAL, true);
return this.storageService.getBoolean(HomeActivityActionViewItem.HOME_BAR_VISIBILITY_PREFERENCE, StorageScope.GLOBAL, true);
}
private set homeBarVisibilityPreference(value: boolean) {
this.storageService.store(ActivitybarPart.HOME_BAR_VISIBILITY_PREFERENCE, value, StorageScope.GLOBAL);
this.storageService.store(HomeActivityActionViewItem.HOME_BAR_VISIBILITY_PREFERENCE, value, StorageScope.GLOBAL, StorageTarget.USER);
}
private get accountsVisibilityPreference(): boolean {
return this.storageService.getBoolean(ACCOUNTS_VISIBILITY_PREFERENCE_KEY, StorageScope.GLOBAL, true);
return this.storageService.getBoolean(AccountsActivityActionViewItem.ACCOUNTS_VISIBILITY_PREFERENCE_KEY, StorageScope.GLOBAL, true);
}
private set accountsVisibilityPreference(value: boolean) {
this.storageService.store(ACCOUNTS_VISIBILITY_PREFERENCE_KEY, value, StorageScope.GLOBAL);
}
private migrateFromOldCachedViewContainersValue(): void {
const value = this.storageService.get('workbench.activity.pinnedViewlets', StorageScope.GLOBAL);
if (value !== undefined) {
const storedStates: Array<string | ICachedViewContainer> = JSON.parse(value);
const cachedViewContainers = storedStates.map(c => {
const serialized: ICachedViewContainer = typeof c === 'string' /* migration from pinned states to composites states */ ? { id: c, pinned: true, order: undefined, visible: true, name: undefined, icon: undefined, views: undefined } : c;
serialized.visible = isUndefinedOrNull(serialized.visible) ? true : serialized.visible;
return serialized;
});
this.storeCachedViewContainersState(cachedViewContainers);
this.storageService.remove('workbench.activity.pinnedViewlets', StorageScope.GLOBAL);
}
this.storageService.store(AccountsActivityActionViewItem.ACCOUNTS_VISIBILITY_PREFERENCE_KEY, value, StorageScope.GLOBAL, StorageTarget.USER);
}
toJSON(): object {

View File

@@ -9,10 +9,6 @@
margin-bottom: 4px;
}
.monaco-workbench .activitybar > .content > .home-bar > .monaco-action-bar .action-item {
margin-bottom: 0;
}
.monaco-workbench .activitybar > .content .composite-bar > .monaco-action-bar .action-item::before,
.monaco-workbench .activitybar > .content .composite-bar > .monaco-action-bar .action-item::after {
position: absolute;

View File

@@ -51,6 +51,7 @@
position: relative;
width: 100%;
height: 48px;
<<<<<<< HEAD
display: flex;
align-items: center;
justify-content: center;
@@ -59,6 +60,8 @@
/* NOTE@coder: Hide since it doesn't seem to do anything when used with
code-server except open the VS Code repository. */
display: none !important;
=======
>>>>>>> e4a830e9b7ca039c7c70697786d29f5b6679d775
}
.monaco-workbench .activitybar > .content > .home-bar > .home-bar-icon-badge {
@@ -92,10 +95,6 @@
margin-bottom: auto;
}
.monaco-workbench .activitybar > .content > .composite-bar-excess {
height: 100%;
}
/** Menu Bar */
.monaco-workbench .activitybar .menubar {

View File

@@ -18,7 +18,7 @@ import { Composite, CompositeRegistry } from 'vs/workbench/browser/composite';
import { IComposite } from 'vs/workbench/common/composite';
import { CompositeProgressIndicator } from 'vs/workbench/services/progress/browser/progressIndicator';
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
@@ -199,7 +199,7 @@ export abstract class CompositePart<T extends Composite> extends Part {
// Store in preferences
const id = this.activeComposite.getId();
if (id !== this.defaultCompositeId) {
this.storageService.store(this.activeCompositeSettingsKey, id, StorageScope.WORKSPACE);
this.storageService.store(this.activeCompositeSettingsKey, id, StorageScope.WORKSPACE, StorageTarget.USER);
} else {
this.storageService.remove(this.activeCompositeSettingsKey, StorageScope.WORKSPACE);
}

View File

@@ -0,0 +1,76 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { IDialogHandler, IDialogResult, IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { ILayoutService } from 'vs/platform/layout/browser/layoutService';
import { ILogService } from 'vs/platform/log/common/log';
import { IProductService } from 'vs/platform/product/common/productService';
import { Registry } from 'vs/platform/registry/common/platform';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
import { IDialogsModel, IDialogViewItem } from 'vs/workbench/common/dialogs';
import { BrowserDialogHandler } from 'vs/workbench/browser/parts/dialogs/dialogHandler';
import { DialogService } from 'vs/workbench/services/dialogs/common/dialogService';
import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
import { Disposable } from 'vs/base/common/lifecycle';
export class DialogHandlerContribution extends Disposable implements IWorkbenchContribution {
private impl: IDialogHandler;
private model: IDialogsModel;
private currentDialog: IDialogViewItem | undefined;
constructor(
@IDialogService private dialogService: IDialogService,
@ILogService logService: ILogService,
@ILayoutService layoutService: ILayoutService,
@IThemeService themeService: IThemeService,
@IKeybindingService keybindingService: IKeybindingService,
@IProductService productService: IProductService,
@IClipboardService clipboardService: IClipboardService
) {
super();
this.impl = new BrowserDialogHandler(logService, layoutService, themeService, keybindingService, productService, clipboardService);
this.model = (this.dialogService as DialogService).model;
this._register(this.model.onDidShowDialog(() => {
if (!this.currentDialog) {
this.processDialogs();
}
}));
this.processDialogs();
}
private async processDialogs(): Promise<void> {
while (this.model.dialogs.length) {
this.currentDialog = this.model.dialogs[0];
let result: IDialogResult | undefined = undefined;
if (this.currentDialog.args.confirmArgs) {
const args = this.currentDialog.args.confirmArgs;
result = await this.impl.confirm(args.confirmation);
} else if (this.currentDialog.args.inputArgs) {
const args = this.currentDialog.args.inputArgs;
result = await this.impl.input(args.severity, args.message, args.buttons, args.inputs, args.options);
} else if (this.currentDialog.args.showArgs) {
const args = this.currentDialog.args.showArgs;
result = await this.impl.show(args.severity, args.message, args.buttons, args.options);
} else {
await this.impl.about();
}
this.currentDialog.close(result);
this.currentDialog = undefined;
}
}
}
const workbenchRegistry = Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench);
workbenchRegistry.registerWorkbenchContribution(DialogHandlerContribution, LifecyclePhase.Starting);

View File

@@ -0,0 +1,144 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import { IDialogOptions, IConfirmation, IConfirmationResult, DialogType, IShowResult, IInputResult, ICheckbox, IInput, IDialogHandler } from 'vs/platform/dialogs/common/dialogs';
import { ILayoutService } from 'vs/platform/layout/browser/layoutService';
import { ILogService } from 'vs/platform/log/common/log';
import Severity from 'vs/base/common/severity';
import { Dialog, IDialogResult } from 'vs/base/browser/ui/dialog/dialog';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { attachDialogStyler } from 'vs/platform/theme/common/styler';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { EventHelper } from 'vs/base/browser/dom';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IProductService } from 'vs/platform/product/common/productService';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { fromNow } from 'vs/base/common/date';
export class BrowserDialogHandler implements IDialogHandler {
private static readonly ALLOWABLE_COMMANDS = [
'copy',
'cut',
'editor.action.selectAll',
'editor.action.clipboardCopyAction',
'editor.action.clipboardCutAction',
'editor.action.clipboardPasteAction'
];
constructor(
@ILogService private readonly logService: ILogService,
@ILayoutService private readonly layoutService: ILayoutService,
@IThemeService private readonly themeService: IThemeService,
@IKeybindingService private readonly keybindingService: IKeybindingService,
@IProductService private readonly productService: IProductService,
@IClipboardService private readonly clipboardService: IClipboardService
) { }
async confirm(confirmation: IConfirmation): Promise<IConfirmationResult> {
this.logService.trace('DialogService#confirm', confirmation.message);
const buttons: string[] = [];
if (confirmation.primaryButton) {
buttons.push(confirmation.primaryButton);
} else {
buttons.push(nls.localize({ key: 'yesButton', comment: ['&& denotes a mnemonic'] }, "&&Yes"));
}
if (confirmation.secondaryButton) {
buttons.push(confirmation.secondaryButton);
} else if (typeof confirmation.secondaryButton === 'undefined') {
buttons.push(nls.localize('cancelButton', "Cancel"));
}
const result = await this.doShow(confirmation.type, confirmation.message, buttons, confirmation.detail, 1, confirmation.checkbox);
return { confirmed: result.button === 0, checkboxChecked: result.checkboxChecked };
}
private getDialogType(severity: Severity): DialogType {
return (severity === Severity.Info) ? 'question' : (severity === Severity.Error) ? 'error' : (severity === Severity.Warning) ? 'warning' : 'none';
}
async show(severity: Severity, message: string, buttons: string[], options?: IDialogOptions): Promise<IShowResult> {
this.logService.trace('DialogService#show', message);
const result = await this.doShow(this.getDialogType(severity), message, buttons, options?.detail, options?.cancelId, options?.checkbox);
return {
choice: result.button,
checkboxChecked: result.checkboxChecked
};
}
private async doShow(type: 'none' | 'info' | 'error' | 'question' | 'warning' | 'pending' | undefined, message: string, buttons: string[], detail?: string, cancelId?: number, checkbox?: ICheckbox, inputs?: IInput[]): Promise<IDialogResult> {
const dialogDisposables = new DisposableStore();
const dialog = new Dialog(
this.layoutService.container,
message,
buttons,
{
detail,
cancelId,
type,
keyEventProcessor: (event: StandardKeyboardEvent) => {
const resolved = this.keybindingService.softDispatch(event, this.layoutService.container);
if (resolved && resolved.commandId) {
if (BrowserDialogHandler.ALLOWABLE_COMMANDS.indexOf(resolved.commandId) === -1) {
EventHelper.stop(event, true);
}
}
},
checkboxLabel: checkbox?.label,
checkboxChecked: checkbox?.checked,
inputs
});
dialogDisposables.add(dialog);
dialogDisposables.add(attachDialogStyler(dialog, this.themeService));
const result = await dialog.show();
dialogDisposables.dispose();
return result;
}
async input(severity: Severity, message: string, buttons: string[], inputs: IInput[], options?: IDialogOptions): Promise<IInputResult> {
this.logService.trace('DialogService#input', message);
const result = await this.doShow(this.getDialogType(severity), message, buttons, options?.detail, options?.cancelId, options?.checkbox, inputs);
return {
choice: result.button,
checkboxChecked: result.checkboxChecked,
values: result.values
};
}
async about(): Promise<void> {
const detailString = (useAgo: boolean): string => {
return nls.localize('aboutDetail',
"code-server: v{4}\n VS Code: v{0}\nCommit: {1}\nDate: {2}\nBrowser: {3}",
this.productService.version || 'Unknown',
this.productService.commit || 'Unknown',
this.productService.date ? `${this.productService.date}${useAgo ? ' (' + fromNow(new Date(this.productService.date), true) + ')' : ''}` : 'Unknown',
navigator.userAgent,
this.productService.codeServerVersion || 'Unknown',
);
};
const detail = detailString(true);
const detailToCopy = detailString(false);
const { choice } = await this.show(Severity.Info, this.productService.nameLong, [nls.localize('copy', "Copy"), nls.localize('ok', "OK")], { detail, cancelId: 1 });
if (choice === 0) {
this.clipboardService.writeText(detailToCopy);
}
}
}

View File

@@ -20,7 +20,7 @@ import { dispose, IDisposable, Disposable, DisposableStore } from 'vs/base/commo
import { IStorageService } from 'vs/platform/storage/common/storage';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { assertIsDefined, assertAllDefined } from 'vs/base/common/types';
import { BinarySize } from 'vs/platform/files/common/files';
import { ByteSize } from 'vs/platform/files/common/files';
export interface IOpenCallbacks {
openInternal: (input: EditorInput, options: EditorOptions | undefined) => Promise<void>;
@@ -182,7 +182,7 @@ interface ResourceViewerDelegate {
class ResourceViewer {
private static readonly MAX_OPEN_INTERNAL_SIZE = BinarySize.MB * 200; // max size until we offer an action to open internally
private static readonly MAX_OPEN_INTERNAL_SIZE = ByteSize.MB * 200; // max size until we offer an action to open internally
static show(
descriptor: IResourceDescriptor,
@@ -211,7 +211,7 @@ class FileTooLargeFileView {
scrollbar: DomScrollableElement,
delegate: ResourceViewerDelegate
) {
const size = BinarySize.formatSize(descriptorSize);
const size = ByteSize.formatSize(descriptorSize);
delegate.metadataClb(size);
clearNode(container);
@@ -233,7 +233,7 @@ class FileSeemsBinaryFileView {
scrollbar: DomScrollableElement,
delegate: ResourceViewerDelegate
) {
delegate.metadataClb(typeof descriptor.size === 'number' ? BinarySize.formatSize(descriptor.size) : '');
delegate.metadataClb(typeof descriptor.size === 'number' ? ByteSize.formatSize(descriptor.size) : '');
clearNode(container);

View File

@@ -491,7 +491,7 @@ export class BreadcrumbsControl {
if (element instanceof FileElement) {
if (element.kind === FileKind.FILE) {
// open file in any editor
this._editorService.openEditor({ resource: element.uri, options: { pinned: pinned } }, group);
this._editorService.openEditor({ resource: element.uri, options: { pinned } }, group);
} else {
// show next picker
let items = this._widget.getItems();
@@ -508,7 +508,8 @@ export class BreadcrumbsControl {
resource: model.uri,
options: {
selection: Range.collapseToStart(element.symbol.selectionRange),
selectionRevealType: TextEditorSelectionRevealType.CenterIfOutsideViewport
selectionRevealType: TextEditorSelectionRevealType.CenterIfOutsideViewport,
pinned
}
}, withUndefinedAsNull(this._getActiveCodeEditor()), group === SIDE_GROUP);
}
@@ -753,13 +754,14 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({
// open symbol in editor
return editors.openEditor({
resource: outlineElement.uri,
options: { selection: Range.collapseToStart(element.symbol.selectionRange) }
options: { selection: Range.collapseToStart(element.symbol.selectionRange), pinned: true }
}, SIDE_GROUP);
} else if (element && URI.isUri(element.resource)) {
// open file in editor
return editors.openEditor({
resource: element.resource,
options: { pinned: true }
}, SIDE_GROUP);
} else {

View File

@@ -99,7 +99,7 @@ export abstract class BreadcrumbsPicker {
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.getColorTheme().getColor(widgetShadow)}`;
this._treeContainer.style.boxShadow = `0 0 8px 2px ${this._themeService.getColorTheme().getColor(widgetShadow)}`;
this._domNode.appendChild(this._treeContainer);
this._layoutInfo = { maxHeight, width, arrowSize, arrowOffset, inputHeight: 0 };
@@ -436,7 +436,6 @@ export class BreadcrumbsFilePicker extends BreadcrumbsPicker {
}
protected _getTargetFromEvent(element: any): any | undefined {
// todo@joh
if (element && !IWorkspaceFolder.isIWorkspaceFolder(element) && !(element as IFileStat).isDirectory) {
return new FileElement((element as IFileStat).resource, FileKind.FILE);
}

View File

@@ -55,6 +55,8 @@ import { IQuickAccessRegistry, Extensions as QuickAccessExtensions } from 'vs/pl
import { ActiveGroupEditorsByMostRecentlyUsedQuickAccess, AllEditorsByAppearanceQuickAccess, AllEditorsByMostRecentlyUsedQuickAccess } from 'vs/workbench/browser/parts/editor/editorQuickAccess';
import { IPathService } from 'vs/workbench/services/path/common/pathService';
import { FileAccess } from 'vs/base/common/network';
import { Codicon } from 'vs/base/common/codicons';
import { registerIcon } from 'vs/platform/theme/common/iconRegistry';
// Register String Editor
Registry.as<IEditorRegistry>(EditorExtensions.Editors).registerEditor(
@@ -238,27 +240,27 @@ export abstract class AbstractSideBySideEditorInputFactory implements IEditorInp
const secondaryInput = secondaryInputFactory.deserialize(instantiationService, deserialized.secondarySerialized);
if (primaryInput && secondaryInput) {
return this.createEditorInput(deserialized.name, deserialized.description, secondaryInput, primaryInput);
return this.createEditorInput(instantiationService, deserialized.name, deserialized.description, secondaryInput, primaryInput);
}
}
return undefined;
}
protected abstract createEditorInput(name: string, description: string | undefined, secondaryInput: EditorInput, primaryInput: EditorInput): EditorInput;
protected abstract createEditorInput(instantiationService: IInstantiationService, name: string, description: string | undefined, secondaryInput: EditorInput, primaryInput: EditorInput): EditorInput;
}
class SideBySideEditorInputFactory extends AbstractSideBySideEditorInputFactory {
protected createEditorInput(name: string, description: string | undefined, secondaryInput: EditorInput, primaryInput: EditorInput): EditorInput {
protected createEditorInput(instantiationService: IInstantiationService, name: string, description: string | undefined, secondaryInput: EditorInput, primaryInput: EditorInput): EditorInput {
return new SideBySideEditorInput(name, description, secondaryInput, primaryInput);
}
}
class DiffEditorInputFactory extends AbstractSideBySideEditorInputFactory {
protected createEditorInput(name: string, description: string | undefined, secondaryInput: EditorInput, primaryInput: EditorInput): EditorInput {
return new DiffEditorInput(name, description, secondaryInput, primaryInput);
protected createEditorInput(instantiationService: IInstantiationService, name: string, description: string | undefined, secondaryInput: EditorInput, primaryInput: EditorInput): EditorInput {
return instantiationService.createInstance(DiffEditorInput, name, description, secondaryInput, primaryInput, undefined);
}
}
@@ -462,6 +464,7 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.
MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.SHOW_EDITORS_IN_GROUP, title: nls.localize('showOpenedEditors', "Show Opened Editors") }, group: '3_open', order: 10 });
MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.CLOSE_EDITORS_IN_GROUP_COMMAND_ID, title: nls.localize('closeAll', "Close All") }, group: '5_close', order: 10 });
MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.CLOSE_SAVED_EDITORS_COMMAND_ID, title: nls.localize('closeAllSaved', "Close Saved") }, group: '5_close', order: 20 });
MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: editorCommands.TOGGLE_KEEP_EDITORS_COMMAND_ID, title: nls.localize('toggleKeepEditors', "Keep Editors Open"), toggled: ContextKeyExpr.not('config.workbench.editor.enablePreview') }, group: '7_settings', order: 10 });
interface IEditorToolItem { id: string; title: string; icon?: { dark?: URI; light?: URI; } | ThemeIcon; }
@@ -494,14 +497,14 @@ appendEditorToolItem(
{
id: SplitEditorAction.ID,
title: nls.localize('splitEditorRight', "Split Editor Right"),
icon: { id: 'codicon/split-horizontal' }
icon: Codicon.splitHorizontal
},
ContextKeyExpr.not('splitEditorsVertically'),
100000, // towards the end
{
id: editorCommands.SPLIT_EDITOR_DOWN,
title: nls.localize('splitEditorDown', "Split Editor Down"),
icon: { id: 'codicon/split-vertical' }
icon: Codicon.splitVertical
}
);
@@ -509,14 +512,14 @@ appendEditorToolItem(
{
id: SplitEditorAction.ID,
title: nls.localize('splitEditorDown', "Split Editor Down"),
icon: { id: 'codicon/split-vertical' }
icon: Codicon.splitVertical
},
ContextKeyExpr.has('splitEditorsVertically'),
100000, // towards the end
{
id: editorCommands.SPLIT_EDITOR_RIGHT,
title: nls.localize('splitEditorRight', "Split Editor Right"),
icon: { id: 'codicon/split-horizontal' }
icon: Codicon.splitHorizontal
}
);
@@ -525,14 +528,14 @@ appendEditorToolItem(
{
id: editorCommands.CLOSE_EDITOR_COMMAND_ID,
title: nls.localize('close', "Close"),
icon: { id: 'codicon/close' }
icon: Codicon.close
},
ContextKeyExpr.and(ContextKeyExpr.not('config.workbench.editor.showTabs'), ActiveEditorDirtyContext.toNegated(), ActiveEditorStickyContext.toNegated()),
1000000, // towards the far end
{
id: editorCommands.CLOSE_EDITORS_IN_GROUP_COMMAND_ID,
title: nls.localize('closeAll', "Close All"),
icon: { id: 'codicon/close-all' }
icon: Codicon.closeAll
}
);
@@ -541,14 +544,14 @@ appendEditorToolItem(
{
id: editorCommands.CLOSE_EDITOR_COMMAND_ID,
title: nls.localize('close', "Close"),
icon: { id: 'codicon/close-dirty' }
icon: Codicon.closeDirty
},
ContextKeyExpr.and(ContextKeyExpr.not('config.workbench.editor.showTabs'), ActiveEditorDirtyContext, ActiveEditorStickyContext.toNegated()),
1000000, // towards the far end
{
id: editorCommands.CLOSE_EDITORS_IN_GROUP_COMMAND_ID,
title: nls.localize('closeAll', "Close All"),
icon: { id: 'codicon/close-all' }
icon: Codicon.closeAll
}
);
@@ -557,14 +560,14 @@ appendEditorToolItem(
{
id: editorCommands.UNPIN_EDITOR_COMMAND_ID,
title: nls.localize('unpin', "Unpin"),
icon: { id: 'codicon/pinned' }
icon: Codicon.pinned
},
ContextKeyExpr.and(ContextKeyExpr.not('config.workbench.editor.showTabs'), ActiveEditorDirtyContext.toNegated(), ActiveEditorStickyContext),
1000000, // towards the far end
{
id: editorCommands.CLOSE_EDITOR_COMMAND_ID,
title: nls.localize('close', "Close"),
icon: { id: 'codicon/close' }
icon: Codicon.close
}
);
@@ -573,23 +576,28 @@ appendEditorToolItem(
{
id: editorCommands.UNPIN_EDITOR_COMMAND_ID,
title: nls.localize('unpin', "Unpin"),
icon: { id: 'codicon/pinned-dirty' }
icon: Codicon.pinnedDirty
},
ContextKeyExpr.and(ContextKeyExpr.not('config.workbench.editor.showTabs'), ActiveEditorDirtyContext, ActiveEditorStickyContext),
1000000, // towards the far end
{
id: editorCommands.CLOSE_EDITOR_COMMAND_ID,
title: nls.localize('close', "Close"),
icon: { id: 'codicon/close' }
icon: Codicon.close
}
);
const previousChangeIcon = registerIcon('diff-editor-previous-change', Codicon.arrowUp, nls.localize('previousChangeIcon', 'Icon for the previous change action in the diff editor.'));
const nextChangeIcon = registerIcon('diff-editor-next-change', Codicon.arrowDown, nls.localize('nextChangeIcon', 'Icon for the next change action in the diff editor.'));
const toggleWhitespace = registerIcon('diff-editor-toggle-whitespace', Codicon.whitespace, nls.localize('toggleWhitespace', 'Icon for the toggle whitespace action in the diff editor.'));
// Diff Editor Title Menu: Previous Change
appendEditorToolItem(
{
id: editorCommands.GOTO_PREVIOUS_CHANGE,
title: nls.localize('navigate.prev.label', "Previous Change"),
icon: { id: 'codicon/arrow-up' }
icon: previousChangeIcon
},
TextCompareEditorActiveContext,
10
@@ -600,7 +608,7 @@ appendEditorToolItem(
{
id: editorCommands.GOTO_NEXT_CHANGE,
title: nls.localize('navigate.next.label', "Next Change"),
icon: { id: 'codicon/arrow-down' }
icon: nextChangeIcon
},
TextCompareEditorActiveContext,
11
@@ -611,7 +619,7 @@ appendEditorToolItem(
{
id: editorCommands.TOGGLE_DIFF_IGNORE_TRIM_WHITESPACE,
title: nls.localize('ignoreTrimWhitespace.label', "Ignore Leading/Trailing Whitespace Differences"),
icon: { id: 'codicon/whitespace' }
icon: toggleWhitespace
},
ContextKeyExpr.and(TextCompareEditorActiveContext, ContextKeyExpr.notEquals('config.diffEditor.ignoreTrimWhitespace', true)),
20
@@ -622,7 +630,7 @@ appendEditorToolItem(
{
id: editorCommands.TOGGLE_DIFF_IGNORE_TRIM_WHITESPACE,
title: nls.localize('showTrimWhitespace.label', "Show Leading/Trailing Whitespace Differences"),
icon: { id: 'codicon/whitespace~disabled' }
icon: ThemeIcon.modify(toggleWhitespace, 'disabled')
},
ContextKeyExpr.and(TextCompareEditorActiveContext, ContextKeyExpr.notEquals('config.diffEditor.ignoreTrimWhitespace', false)),
20

View File

@@ -38,7 +38,8 @@ export const DEFAULT_EDITOR_PART_OPTIONS: IEditorPartOptions = {
openSideBySideDirection: 'right',
closeEmptyGroups: true,
labelFormat: 'default',
splitSizing: 'distribute'
splitSizing: 'distribute',
splitOnDragAndDrop: true
};
export function impactsEditorPartOptions(event: IConfigurationChangeEvent): boolean {
@@ -64,12 +65,12 @@ export interface IEditorOpeningEvent extends IEditorIdentifier {
/**
* The options used when opening the editor.
*/
options?: IEditorOptions;
readonly options?: IEditorOptions;
/**
* Context indicates how the editor open event is initialized.
*/
context?: OpenEditorContext;
readonly context?: OpenEditorContext;
/**
* Allows to prevent the opening of an editor by providing a callback

View File

@@ -7,22 +7,26 @@ import * as nls from 'vs/nls';
import { isObject, isString, isUndefined, isNumber, withNullAsUndefined } 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, IEditorInput, IVisibleEditorPane, ActiveEditorStickyContext, EditorsOrder } from 'vs/workbench/common/editor';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { TextCompareEditorVisibleContext, EditorInput, IEditorIdentifier, IEditorCommandsContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, CloseDirection, IEditorInput, IVisibleEditorPane, ActiveEditorStickyContext, EditorsOrder, viewColumnToEditorGroup, EditorGroupColumn } from 'vs/workbench/common/editor';
import { IEditorService, SIDE_GROUP } 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';
import { URI } from 'vs/base/common/uri';
import { URI, UriComponents } from 'vs/base/common/uri';
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
import { IListService } from 'vs/platform/list/browser/listService';
import { IListService, IOpenEvent } from 'vs/platform/list/browser/listService';
import { List } from 'vs/base/browser/ui/list/listWidget';
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 { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { CommandsRegistry, ICommandHandler } from 'vs/platform/commands/common/commands';
import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
import { ActiveGroupEditorsByMostRecentlyUsedQuickAccess } from 'vs/workbench/browser/parts/editor/editorQuickAccess';
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { ITextEditorOptions } from 'vs/platform/editor/common/editor';
import { openEditorWith } from 'vs/workbench/services/editor/common/editorOpenWith';
export const CLOSE_SAVED_EDITORS_COMMAND_ID = 'workbench.action.closeUnmodifiedEditors';
export const CLOSE_EDITORS_IN_GROUP_COMMAND_ID = 'workbench.action.closeEditorsInGroup';
@@ -36,6 +40,7 @@ export const CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID = 'workbench.action.closeOt
export const MOVE_ACTIVE_EDITOR_COMMAND_ID = 'moveActiveEditor';
export const LAYOUT_EDITOR_GROUPS_COMMAND_ID = 'layoutEditorGroups';
export const KEEP_EDITOR_COMMAND_ID = 'workbench.action.keepEditor';
export const TOGGLE_KEEP_EDITORS_COMMAND_ID = 'workbench.action.toggleKeepEditors';
export const SHOW_EDITORS_IN_GROUP = 'workbench.action.showEditorsInGroup';
export const PIN_EDITOR_COMMAND_ID = 'workbench.action.pinEditor';
@@ -44,6 +49,9 @@ export const UNPIN_EDITOR_COMMAND_ID = 'workbench.action.unpinEditor';
export const TOGGLE_DIFF_SIDE_BY_SIDE = 'toggle.diff.renderSideBySide';
export const GOTO_NEXT_CHANGE = 'workbench.action.compareEditor.nextChange';
export const GOTO_PREVIOUS_CHANGE = 'workbench.action.compareEditor.previousChange';
export const DIFF_FOCUS_PRIMARY_SIDE = 'workbench.action.compareEditor.focusPrimarySide';
export const DIFF_FOCUS_SECONDARY_SIDE = 'workbench.action.compareEditor.focusSecondarySide';
export const DIFF_FOCUS_OTHER_SIDE = 'workbench.action.compareEditor.focusOtherSide';
export const TOGGLE_DIFF_IGNORE_TRIM_WHITESPACE = 'toggle.diff.ignoreTrimWhitespace';
export const SPLIT_EDITOR_UP = 'workbench.action.splitEditorUp';
@@ -58,6 +66,10 @@ export const FOCUS_BELOW_GROUP_WITHOUT_WRAP_COMMAND_ID = 'workbench.action.focus
export const OPEN_EDITOR_AT_INDEX_COMMAND_ID = 'workbench.action.openEditorAtIndex';
export const API_OPEN_EDITOR_COMMAND_ID = '_workbench.open';
export const API_OPEN_DIFF_EDITOR_COMMAND_ID = '_workbench.diff';
export const API_OPEN_WITH_EDITOR_COMMAND_ID = '_workbench.openWith';
export interface ActiveEditorMoveArguments {
to: 'first' | 'last' | 'left' | 'right' | 'up' | 'down' | 'center' | 'position' | 'previous' | 'next';
by: 'tab' | 'group';
@@ -227,13 +239,45 @@ function moveActiveEditorToGroup(args: ActiveEditorMoveArguments, control: IVisi
}
function registerEditorGroupsLayoutCommand(): void {
CommandsRegistry.registerCommand(LAYOUT_EDITOR_GROUPS_COMMAND_ID, (accessor: ServicesAccessor, args: EditorGroupLayout) => {
if (!args || typeof args !== 'object') {
function applyEditorLayout(accessor: ServicesAccessor, layout: EditorGroupLayout): void {
if (!layout || typeof layout !== 'object') {
return;
}
const editorGroupService = accessor.get(IEditorGroupsService);
editorGroupService.applyLayout(args);
editorGroupService.applyLayout(layout);
}
CommandsRegistry.registerCommand(LAYOUT_EDITOR_GROUPS_COMMAND_ID, (accessor: ServicesAccessor, args: EditorGroupLayout) => {
applyEditorLayout(accessor, args);
});
// API Command
CommandsRegistry.registerCommand({
id: 'vscode.setEditorLayout',
handler: (accessor: ServicesAccessor, args: EditorGroupLayout) => applyEditorLayout(accessor, args),
description: {
description: 'Set Editor Layout',
args: [{
name: 'args',
schema: {
'type': 'object',
'required': ['groups'],
'properties': {
'orientation': {
'type': 'number',
'default': 0,
'enum': [0, 1]
},
'groups': {
'$ref': '#/definitions/editorGroupsSchema',
'default': [{}, {}]
}
}
}
}]
}
});
}
@@ -265,30 +309,68 @@ function registerDiffEditorCommands(): void {
handler: accessor => navigateInDiffEditor(accessor, false)
});
function navigateInDiffEditor(accessor: ServicesAccessor, next: boolean): void {
function getActiveTextDiffEditor(accessor: ServicesAccessor): TextDiffEditor | undefined {
const editorService = accessor.get(IEditorService);
const candidates = [editorService.activeEditorPane, ...editorService.visibleEditorPanes].filter(editor => editor instanceof TextDiffEditor);
if (candidates.length > 0) {
const navigator = (<TextDiffEditor>candidates[0]).getDiffNavigator();
for (const editor of [editorService.activeEditorPane, ...editorService.visibleEditorPanes]) {
if (editor instanceof TextDiffEditor) {
return editor;
}
}
return undefined;
}
function navigateInDiffEditor(accessor: ServicesAccessor, next: boolean): void {
const activeTextDiffEditor = getActiveTextDiffEditor(accessor);
if (activeTextDiffEditor) {
const navigator = activeTextDiffEditor.getDiffNavigator();
if (navigator) {
next ? navigator.next() : navigator.previous();
}
}
}
enum FocusTextDiffEditorMode {
Original,
Modified,
Toggle
}
function focusInDiffEditor(accessor: ServicesAccessor, mode: FocusTextDiffEditorMode): void {
const activeTextDiffEditor = getActiveTextDiffEditor(accessor);
if (activeTextDiffEditor) {
switch (mode) {
case FocusTextDiffEditorMode.Original:
activeTextDiffEditor.getControl()?.getOriginalEditor().focus();
break;
case FocusTextDiffEditorMode.Modified:
activeTextDiffEditor.getControl()?.getModifiedEditor().focus();
break;
case FocusTextDiffEditorMode.Toggle:
if (activeTextDiffEditor.getControl()?.getModifiedEditor().hasWidgetFocus()) {
return focusInDiffEditor(accessor, FocusTextDiffEditorMode.Original);
} else {
return focusInDiffEditor(accessor, FocusTextDiffEditorMode.Modified);
}
}
}
}
function toggleDiffSideBySide(accessor: ServicesAccessor): void {
const configurationService = accessor.get(IConfigurationService);
const newValue = !configurationService.getValue<boolean>('diffEditor.renderSideBySide');
configurationService.updateValue('diffEditor.renderSideBySide', newValue, ConfigurationTarget.USER);
configurationService.updateValue('diffEditor.renderSideBySide', newValue);
}
function toggleDiffIgnoreTrimWhitespace(accessor: ServicesAccessor): void {
const configurationService = accessor.get(IConfigurationService);
const newValue = !configurationService.getValue<boolean>('diffEditor.ignoreTrimWhitespace');
configurationService.updateValue('diffEditor.ignoreTrimWhitespace', newValue, ConfigurationTarget.USER);
configurationService.updateValue('diffEditor.ignoreTrimWhitespace', newValue);
}
KeybindingsRegistry.registerCommandAndKeybindingRule({
@@ -299,6 +381,30 @@ function registerDiffEditorCommands(): void {
handler: accessor => toggleDiffSideBySide(accessor)
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: DIFF_FOCUS_PRIMARY_SIDE,
weight: KeybindingWeight.WorkbenchContrib,
when: undefined,
primary: undefined,
handler: accessor => focusInDiffEditor(accessor, FocusTextDiffEditorMode.Modified)
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: DIFF_FOCUS_SECONDARY_SIDE,
weight: KeybindingWeight.WorkbenchContrib,
when: undefined,
primary: undefined,
handler: accessor => focusInDiffEditor(accessor, FocusTextDiffEditorMode.Original)
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: DIFF_FOCUS_OTHER_SIDE,
weight: KeybindingWeight.WorkbenchContrib,
when: undefined,
primary: undefined,
handler: accessor => focusInDiffEditor(accessor, FocusTextDiffEditorMode.Toggle)
});
MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
command: {
id: TOGGLE_DIFF_SIDE_BY_SIDE,
@@ -320,6 +426,88 @@ function registerDiffEditorCommands(): void {
});
}
function registerOpenEditorAPICommands(): void {
function mixinContext(context: IOpenEvent<unknown> | undefined, options: ITextEditorOptions | undefined, column: EditorGroupColumn | undefined): [ITextEditorOptions | undefined, EditorGroupColumn | undefined] {
if (!context) {
return [options, column];
}
return [
{ ...context.editorOptions, ...(options ?? Object.create(null)) },
context.sideBySide ? SIDE_GROUP : column
];
}
CommandsRegistry.registerCommand(API_OPEN_EDITOR_COMMAND_ID, async function (accessor: ServicesAccessor, resourceArg: UriComponents, columnAndOptions?: [EditorGroupColumn?, ITextEditorOptions?], label?: string, context?: IOpenEvent<unknown>) {
const editorService = accessor.get(IEditorService);
const editorGroupService = accessor.get(IEditorGroupsService);
const openerService = accessor.get(IOpenerService);
const resource = URI.revive(resourceArg);
const [columnArg, optionsArg] = columnAndOptions ?? [];
// use editor options or editor view column as a hint to use the editor service for opening
if (optionsArg || typeof columnArg === 'number') {
const [options, column] = mixinContext(context, optionsArg, columnArg);
await editorService.openEditor({ resource, options, label }, viewColumnToEditorGroup(editorGroupService, column));
}
// do not allow to execute commands from here
else if (resource.scheme === 'command') {
return;
}
// finally, delegate to opener service
else {
await openerService.open(resource, { openToSide: context?.sideBySide, editorOptions: context?.editorOptions });
}
});
CommandsRegistry.registerCommand(API_OPEN_DIFF_EDITOR_COMMAND_ID, async function (accessor: ServicesAccessor, leftResource: UriComponents, rightResource: UriComponents, label?: string, columnAndOptions?: [EditorGroupColumn?, ITextEditorOptions?], context?: IOpenEvent<unknown>) {
const editorService = accessor.get(IEditorService);
const editorGroupService = accessor.get(IEditorGroupsService);
const [columnArg, optionsArg] = columnAndOptions ?? [];
const [options, column] = mixinContext(context, optionsArg, columnArg);
await editorService.openEditor({
leftResource: URI.revive(leftResource),
rightResource: URI.revive(rightResource),
label,
options
}, viewColumnToEditorGroup(editorGroupService, column));
});
CommandsRegistry.registerCommand(API_OPEN_WITH_EDITOR_COMMAND_ID, (accessor: ServicesAccessor, resource: UriComponents, id: string, columnAndOptions?: [EditorGroupColumn?, ITextEditorOptions?]) => {
const editorService = accessor.get(IEditorService);
const editorGroupsService = accessor.get(IEditorGroupsService);
const configurationService = accessor.get(IConfigurationService);
const quickInputService = accessor.get(IQuickInputService);
const [columnArg, optionsArg] = columnAndOptions ?? [];
let group: IEditorGroup | undefined = undefined;
if (columnArg === SIDE_GROUP) {
const direction = preferredSideBySideGroupDirection(configurationService);
let neighbourGroup = editorGroupsService.findGroup({ direction });
if (!neighbourGroup) {
neighbourGroup = editorGroupsService.addGroup(editorGroupsService.activeGroup, direction);
}
group = neighbourGroup;
} else {
group = editorGroupsService.getGroup(viewColumnToEditorGroup(editorGroupsService, columnArg)) ?? editorGroupsService.activeGroup;
}
const textOptions: ITextEditorOptions = optionsArg ? { ...optionsArg, override: false } : { override: false };
const input = editorService.createEditorInput({ resource: URI.revive(resource) });
return openEditorWith(input, id, textOptions, group, editorService, configurationService, quickInputService);
});
}
function registerOpenEditorAtIndexCommands(): void {
const openEditorAtIndex: ICommandHandler = (accessor: ServicesAccessor, editorIndex: number): void => {
const editorService = accessor.get(IEditorService);
@@ -710,6 +898,31 @@ function registerOtherEditorCommands(): void {
}
});
CommandsRegistry.registerCommand({
id: TOGGLE_KEEP_EDITORS_COMMAND_ID,
handler: accessor => {
const configurationService = accessor.get(IConfigurationService);
const notificationService = accessor.get(INotificationService);
const openerService = accessor.get(IOpenerService);
// Update setting
const currentSetting = configurationService.getValue<boolean>('workbench.editor.enablePreview');
const newSetting = currentSetting === true ? false : true;
configurationService.updateValue('workbench.editor.enablePreview', newSetting);
// Inform user
notificationService.prompt(
Severity.Info,
newSetting ?
nls.localize('enablePreview', "Preview editors have been enabled in settings.") :
nls.localize('disablePreview', "Preview editors have been disabled in settings."),
[{
label: nls.localize('learnMode', "Learn More"), run: () => openerService.open('https://go.microsoft.com/fwlink/?linkid=2147473')
}]
);
}
});
KeybindingsRegistry.registerCommandAndKeybindingRule({
id: PIN_EDITOR_COMMAND_ID,
weight: KeybindingWeight.WorkbenchContrib,
@@ -877,6 +1090,7 @@ export function setup(): void {
registerActiveEditorMoveCommand();
registerEditorGroupsLayoutCommand();
registerDiffEditorCommands();
registerOpenEditorAPICommands();
registerOpenEditorAtIndexCommands();
registerCloseEditorCommands();
registerOtherEditorCommands();

View File

@@ -12,7 +12,7 @@ import { IThemeService, Themable } 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, isWeb } from 'vs/base/common/platform';
import { GroupDirection, MergeGroupMode, OpenEditorContext } from 'vs/workbench/services/editor/common/editorGroupsService';
import { GroupDirection, IEditorGroupsService, MergeGroupMode, OpenEditorContext } 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';
@@ -25,6 +25,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic
import { assertIsDefined, assertAllDefined } from 'vs/base/common/types';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { localize } from 'vs/nls';
import { ByteSize } from 'vs/platform/files/common/files';
interface IDropOperation {
splitDirection?: GroupDirection;
@@ -34,7 +35,7 @@ class DropOverlay extends Themable {
private static readonly OVERLAY_ID = 'monaco-workbench-editor-drop-overlay';
private static readonly MAX_FILE_UPLOAD_SIZE = 100 * 1024 * 1024; // 100mb
private static readonly MAX_FILE_UPLOAD_SIZE = 100 * ByteSize.MB;
private container: HTMLElement | undefined;
private overlay: HTMLElement | undefined;
@@ -54,7 +55,8 @@ class DropOverlay extends Themable {
@IInstantiationService private instantiationService: IInstantiationService,
@IFileDialogService private readonly fileDialogService: IFileDialogService,
@IEditorService private readonly editorService: IEditorService,
@INotificationService private readonly notificationService: INotificationService
@INotificationService private readonly notificationService: INotificationService,
@IEditorGroupsService private readonly editorGroupService: IEditorGroupsService
) {
super(themeService);
@@ -143,8 +145,14 @@ class DropOverlay extends Themable {
}
}
// Position overlay
this.positionOverlay(e.offsetX, e.offsetY, isDraggingGroup);
// Position overlay and conditionally enable or disable
// editor group splitting support based on setting and
// keymodifiers used.
let splitOnDragAndDrop = !!this.editorGroupService.partOptions.splitOnDragAndDrop;
if (this.isToggleSplitOperation(e)) {
splitOnDragAndDrop = !splitOnDragAndDrop;
}
this.positionOverlay(e.offsetX, e.offsetY, isDraggingGroup, splitOnDragAndDrop);
// Make sure to stop any running cleanup scheduler to remove the overlay
if (this.cleanupOverlayScheduler.isScheduled()) {
@@ -319,7 +327,7 @@ class DropOverlay extends Themable {
// Try to come up with a good file path for the untitled
// editor by asking the file dialog service for the default
let proposedFilePath: URI | undefined = undefined;
const defaultFilePath = this.fileDialogService.defaultFilePath();
const defaultFilePath = await this.fileDialogService.defaultFilePath();
if (defaultFilePath) {
proposedFilePath = joinPath(defaultFilePath, name);
}
@@ -362,24 +370,33 @@ class DropOverlay extends Themable {
return (e.ctrlKey && !isMacintosh) || (e.altKey && isMacintosh);
}
private positionOverlay(mousePosX: number, mousePosY: number, isDraggingGroup: boolean): void {
private isToggleSplitOperation(e: DragEvent): boolean {
return (e.altKey && !isMacintosh) || (e.shiftKey && isMacintosh);
}
private positionOverlay(mousePosX: number, mousePosY: number, isDraggingGroup: boolean, enableSplitting: boolean): void {
const preferSplitVertically = this.accessor.partOptions.openSideBySideDirection === 'right';
const editorControlWidth = this.groupView.element.clientWidth;
const editorControlHeight = this.groupView.element.clientHeight - this.getOverlayOffsetHeight();
let edgeWidthThresholdFactor: number;
if (isDraggingGroup) {
edgeWidthThresholdFactor = preferSplitVertically ? 0.3 : 0.1; // give larger threshold when dragging group depending on preferred split direction
} else {
edgeWidthThresholdFactor = 0.1; // 10% threshold to split if dragging editors
}
let edgeHeightThresholdFactor: number;
if (isDraggingGroup) {
edgeHeightThresholdFactor = preferSplitVertically ? 0.1 : 0.3; // give larger threshold when dragging group depending on preferred split direction
if (enableSplitting) {
if (isDraggingGroup) {
edgeWidthThresholdFactor = preferSplitVertically ? 0.3 : 0.1; // give larger threshold when dragging group depending on preferred split direction
} else {
edgeWidthThresholdFactor = 0.1; // 10% threshold to split if dragging editors
}
if (isDraggingGroup) {
edgeHeightThresholdFactor = preferSplitVertically ? 0.1 : 0.3; // give larger threshold when dragging group depending on preferred split direction
} else {
edgeHeightThresholdFactor = 0.1; // 10% threshold to split if dragging editors
}
} else {
edgeHeightThresholdFactor = 0.1; // 10% threshold to split if dragging editors
edgeWidthThresholdFactor = 0;
edgeHeightThresholdFactor = 0;
}
const edgeWidthThreshold = editorControlWidth * edgeWidthThresholdFactor;

View File

@@ -1742,28 +1742,17 @@ class EditorOpeningEvent implements IEditorOpeningEvent {
private override: (() => Promise<IEditorPane | undefined>) | undefined = undefined;
constructor(
private _group: GroupIdentifier,
private _editor: EditorInput,
public readonly groupId: GroupIdentifier,
public readonly editor: EditorInput,
private _options: EditorOptions | undefined,
private _context: OpenEditorContext | undefined
public readonly context: OpenEditorContext | undefined
) {
}
get groupId(): GroupIdentifier {
return this._group;
}
get editor(): EditorInput {
return this._editor;
}
get options(): EditorOptions | undefined {
return this._options;
}
get context(): OpenEditorContext | undefined {
return this._context;
}
prevent(callback: () => Promise<IEditorPane | undefined>): void {
this.override = callback;
@@ -1775,9 +1764,9 @@ class EditorOpeningEvent implements IEditorOpeningEvent {
}
export interface EditorReplacement {
editor: EditorInput;
replacement: EditorInput;
options?: EditorOptions;
readonly editor: EditorInput;
readonly replacement: EditorInput;
readonly options?: EditorOptions;
}
registerThemingParticipant((theme, collector, environment) => {

View File

@@ -9,7 +9,7 @@ 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, GroupsOrder } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { LRUCache, Touch } from 'vs/base/common/map';
import { URI } from 'vs/base/common/uri';
import { Event } from 'vs/base/common/event';
@@ -156,7 +156,7 @@ export abstract class EditorPane extends Composite implements IEditorPane {
let editorMemento = EditorPane.EDITOR_MEMENTOS.get(mementoKey);
if (!editorMemento) {
editorMemento = new EditorMemento(this.getId(), key, this.getMemento(StorageScope.WORKSPACE), limit, editorGroupService);
editorMemento = new EditorMemento(this.getId(), key, this.getMemento(StorageScope.WORKSPACE, StorageTarget.MACHINE), limit, editorGroupService);
EditorPane.EDITOR_MEMENTOS.set(mementoKey, editorMemento);
}
@@ -220,18 +220,7 @@ export class EditorMemento<T> implements IEditorMemento<T> {
// Automatically clear when editor input gets disposed if any
if (resourceOrEditor instanceof EditorInput) {
const editor = resourceOrEditor;
if (!this.editorDisposables) {
this.editorDisposables = new Map<EditorInput, IDisposable>();
}
if (!this.editorDisposables.has(editor)) {
this.editorDisposables.set(editor, Event.once(resourceOrEditor.onDispose)(() => {
this.clearEditorState(resource);
this.editorDisposables?.delete(editor);
}));
}
this.clearEditorStateOnDispose(resource, resourceOrEditor);
}
}
@@ -240,7 +229,7 @@ export class EditorMemento<T> implements IEditorMemento<T> {
loadEditorState(group: IEditorGroup, resourceOrEditor: URI | EditorInput, fallbackToOtherGroupState?: boolean): T | undefined {
const resource = this.doGetResource(resourceOrEditor);
if (!resource || !group) {
return undefined; // we are not in a good state to load any state for a resource
return; // we are not in a good state to load any state for a resource
}
const cache = this.doLoad();
@@ -261,12 +250,16 @@ export class EditorMemento<T> implements IEditorMemento<T> {
}
}
return undefined;
return;
}
clearEditorState(resource: URI, group?: IEditorGroup): void;
clearEditorState(editor: EditorInput, group?: IEditorGroup): void;
clearEditorState(resourceOrEditor: URI | EditorInput, group?: IEditorGroup): void {
if (resourceOrEditor instanceof EditorInput) {
this.editorDisposables?.delete(resourceOrEditor);
}
const resource = this.doGetResource(resourceOrEditor);
if (resource) {
const cache = this.doLoad();
@@ -286,6 +279,19 @@ export class EditorMemento<T> implements IEditorMemento<T> {
}
}
clearEditorStateOnDispose(resource: URI, editor: EditorInput): void {
if (!this.editorDisposables) {
this.editorDisposables = new Map<EditorInput, IDisposable>();
}
if (!this.editorDisposables.has(editor)) {
this.editorDisposables.set(editor, Event.once(editor.onDispose)(() => {
this.clearEditorState(resource);
this.editorDisposables?.delete(editor);
}));
}
}
moveEditorState(source: URI, target: URI, comparer: IExtUri): void {
const cache = this.doLoad();
@@ -342,7 +348,7 @@ export class EditorMemento<T> implements IEditorMemento<T> {
saveState(): void {
const cache = this.doLoad();
// Cleanup once during shutdown
// Cleanup once during session
if (!this.cleanedUp) {
this.cleanUp();
this.cleanedUp = true;

View File

@@ -19,7 +19,7 @@ import { IEditorGroupsAccessor, IEditorGroupView, getEditorPartOptions, impactsE
import { EditorGroupView } from 'vs/workbench/browser/parts/editor/editorGroupView';
import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration';
import { IDisposable, dispose, toDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { ISerializedEditorGroup, isSerializedEditorGroup } from 'vs/workbench/common/editor/editorGroup';
import { EditorDropTarget, IEditorDropTargetDelegate } from 'vs/workbench/browser/parts/editor/editorDropTarget';
import { IEditorDropService } from 'vs/workbench/services/editor/browser/editorDropService';
@@ -148,8 +148,8 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro
this.gridWidgetView = new GridWidgetView<IEditorGroupView>();
this.workspaceMemento = this.getMemento(StorageScope.WORKSPACE);
this.globalMemento = this.getMemento(StorageScope.GLOBAL);
this.workspaceMemento = this.getMemento(StorageScope.WORKSPACE, StorageTarget.MACHINE);
this.globalMemento = this.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE);
this._whenRestored = new Promise(resolve => (this.whenRestoredResolve = resolve));

View File

@@ -6,7 +6,7 @@
import 'vs/css!./media/editorstatus';
import * as nls from 'vs/nls';
import { runAtThisOrScheduleAtNextAnimationFrame } from 'vs/base/browser/dom';
import { format, compare } from 'vs/base/common/strings';
import { format, compare, splitLines } from 'vs/base/common/strings';
import { extname, basename, isEqual } from 'vs/base/common/resources';
import { areFunctions, withNullAsUndefined, withUndefinedAsNull } from 'vs/base/common/types';
import { URI } from 'vs/base/common/uri';
@@ -346,12 +346,12 @@ export class EditorStatus extends Disposable implements IWorkbenchContribution {
[{
label: nls.localize('screenReaderDetectedExplanation.answerYes', "Yes"),
run: () => {
this.configurationService.updateValue('editor.accessibilitySupport', 'on', ConfigurationTarget.USER);
this.configurationService.updateValue('editor.accessibilitySupport', 'on');
}
}, {
label: nls.localize('screenReaderDetectedExplanation.answerNo', "No"),
run: () => {
this.configurationService.updateValue('editor.accessibilitySupport', 'off', ConfigurationTarget.USER);
this.configurationService.updateValue('editor.accessibilitySupport', 'off');
}
}],
{ sticky: true }
@@ -918,7 +918,7 @@ class ShowCurrentMarkerInStatusbarContribution extends Disposable {
this.currentMarker = this.getMarker();
if (this.hasToUpdateStatus(previousMarker, this.currentMarker)) {
if (this.currentMarker) {
const line = this.currentMarker.message.split(/\r\n|\r|\n/g)[0];
const line = splitLines(this.currentMarker.message)[0];
const text = `${this.getType(this.currentMarker)} ${line}`;
if (!this.statusBarEntryAccessor.value) {
this.statusBarEntryAccessor.value = this.statusbarService.addEntry({ text: '', ariaLabel: '' }, 'statusbar.currentProblem', nls.localize('currentProblem', "Current Problem"), StatusbarAlignment.LEFT);
@@ -1046,12 +1046,19 @@ export class ChangeModeAction extends Action {
@IQuickInputService private readonly quickInputService: IQuickInputService,
@IPreferencesService private readonly preferencesService: IPreferencesService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@ITextFileService private readonly textFileService: ITextFileService
@ITextFileService private readonly textFileService: ITextFileService,
@ICommandService private readonly commandService: ICommandService
) {
super(actionId, actionLabel);
}
async run(): Promise<void> {
const activeEditorPane = this.editorService.activeEditorPane as unknown as { isNotebookEditor?: boolean } | undefined;
if (activeEditorPane?.isNotebookEditor) {
// it's inside notebook editor
return this.commandService.executeCommand('notebook.cell.changeLanguage');
}
const activeTextEditorControl = getCodeEditor(this.editorService.activeTextEditorControl);
if (!activeTextEditorControl) {
await this.quickInputService.pick([{ label: nls.localize('noEditor', "No text editor active at this time") }]);
@@ -1143,7 +1150,7 @@ export class ChangeModeAction extends Action {
// User decided to configure settings for current language
if (pick === configureModeSettings) {
this.preferencesService.openGlobalSettings(true, { editSetting: `[${withUndefinedAsNull(currentModeId)}]` });
this.preferencesService.openGlobalSettings(true, { revealSetting: { key: `[${withUndefinedAsNull(currentModeId)}]`, edit: true } });
return;
}

View File

@@ -38,6 +38,8 @@ export class FloatingClickWidget extends Widget implements IOverlayWidget {
super();
this._domNode = $('.floating-click-widget');
this._domNode.style.padding = '10px';
this._domNode.style.cursor = 'pointer';
if (keyBindingAction) {
const keybinding = keybindingService.lookupKeybinding(keyBindingAction);

View File

@@ -5,7 +5,7 @@
import { IEditorInput, IEditorInputFactoryRegistry, IEditorIdentifier, GroupIdentifier, Extensions, IEditorPartOptionsChangeEvent, EditorsOrder, EditorResourceAccessor, SideBySideEditor } from 'vs/workbench/common/editor';
import { dispose, Disposable, DisposableStore } from 'vs/base/common/lifecycle';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { Registry } from 'vs/platform/registry/common/platform';
import { Event, Emitter } from 'vs/base/common/event';
import { IEditorGroupsService, IEditorGroup, GroupChangeKind, GroupsOrder } from 'vs/workbench/services/editor/common/editorGroupsService';
@@ -339,7 +339,7 @@ export class EditorsObserver extends Disposable {
if (this.mostRecentEditorsMap.isEmpty()) {
this.storageService.remove(EditorsObserver.STORAGE_KEY, StorageScope.WORKSPACE);
} else {
this.storageService.store(EditorsObserver.STORAGE_KEY, JSON.stringify(this.serialize()), StorageScope.WORKSPACE);
this.storageService.store(EditorsObserver.STORAGE_KEY, JSON.stringify(this.serialize()), StorageScope.WORKSPACE, StorageTarget.MACHINE);
}
}

View File

@@ -21,7 +21,7 @@
padding-right: 6px;
}
/* todo@joh move somewhere else */
/* breadcrumbs-picker-style */
.monaco-workbench .monaco-breadcrumbs-picker .arrow {
position: absolute;

View File

@@ -589,8 +589,8 @@ export class TabsTitleControl extends TitleControl {
const tabActionRunner = new EditorCommandsContextActionRunner({ groupId: this.group.id, editorIndex: index });
const tabActionBar = new ActionBar(tabActionsContainer, { ariaLabel: localize('ariaLabelTabActions', "Tab actions"), actionRunner: tabActionRunner, });
tabActionBar.onDidBeforeRun(e => {
const tabActionBar = new ActionBar(tabActionsContainer, { ariaLabel: localize('ariaLabelTabActions', "Tab actions"), actionRunner: tabActionRunner });
tabActionBar.onBeforeRun(e => {
if (e.action.id === this.closeEditorAction.id) {
this.blockRevealActiveTabOnce();
}

View File

@@ -24,7 +24,6 @@ import { ScrollType, IDiffEditorViewState, IDiffEditorModel } from 'vs/editor/co
import { DisposableStore } 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/editor/common/editorGroupsService';
import { IEditorService, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService';
import { CancellationToken } from 'vs/base/common/cancellation';
@@ -32,6 +31,7 @@ import { EditorActivation, IEditorOptions } from 'vs/platform/editor/common/edit
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { isEqual } from 'vs/base/common/resources';
import { multibyteAwareBtoa } from 'vs/base/browser/dom';
import { IFileService } from 'vs/platform/files/common/files';
/**
* The text editor that leverages the diff text editor for the editing experience.
@@ -62,9 +62,28 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditorPan
@ITextResourceConfigurationService configurationService: ITextResourceConfigurationService,
@IEditorService editorService: IEditorService,
@IThemeService themeService: IThemeService,
@IEditorGroupsService editorGroupService: IEditorGroupsService
@IEditorGroupsService editorGroupService: IEditorGroupsService,
@IFileService private readonly fileService: IFileService
) {
super(TextDiffEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, editorService, editorGroupService);
// Listen to file system provider changes
this._register(this.fileService.onDidChangeFileSystemProviderCapabilities(e => this.onDidFileSystemProviderChange(e.scheme)));
this._register(this.fileService.onDidChangeFileSystemProviderRegistrations(e => this.onDidFileSystemProviderChange(e.scheme)));
}
private onDidFileSystemProviderChange(scheme: string): void {
const control = this.getControl();
const input = this.input;
if (control && input instanceof DiffEditorInput) {
if (input.originalInput.resource?.scheme === scheme || input.modifiedInput.resource?.scheme === scheme) {
control.updateOptions({
readOnly: input.modifiedInput.isReadonly(),
originalEditable: !input.originalInput.isReadonly()
});
}
}
}
protected onWillCloseEditorInGroup(editor: IEditorInput): void {
@@ -175,7 +194,7 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditorPan
const originalInput = input.originalInput;
const modifiedInput = input.modifiedInput;
const binaryDiffInput = new DiffEditorInput(input.getName(), input.getDescription(), originalInput, modifiedInput, true);
const binaryDiffInput = this.instantiationService.createInstance(DiffEditorInput, input.getName(), input.getDescription(), originalInput, modifiedInput, true);
// Forward binary flag to input if supported
const fileEditorInputFactory = Registry.as<IEditorInputFactoryRegistry>(EditorInputExtensions.EditorInputFactories).getFileEditorInputFactory();
@@ -217,16 +236,17 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditorPan
// Handle diff editor specially by merging in diffEditor configuration
if (isObject(configuration.diffEditor)) {
// User settings defines `diffEditor.codeLens`, but there is also `editor.codeLens`.
// Due to the mixin, the two settings cannot be distinguished anymore.
//
// So we map `diffEditor.codeLens` to `diffEditor.originalCodeLens` and `diffEditor.modifiedCodeLens`.
const diffEditorConfiguration = <IDiffEditorOptions>objects.deepClone(configuration.diffEditor);
diffEditorConfiguration.originalCodeLens = diffEditorConfiguration.codeLens;
diffEditorConfiguration.modifiedCodeLens = diffEditorConfiguration.codeLens;
// User settings defines `diffEditor.codeLens`, but here we rename that to `diffEditor.diffCodeLens` to avoid collisions with `editor.codeLens`.
diffEditorConfiguration.diffCodeLens = diffEditorConfiguration.codeLens;
delete diffEditorConfiguration.codeLens;
objects.mixin(editorConfiguration, diffEditorConfiguration);
// User settings defines `diffEditor.wordWrap`, but here we rename that to `diffEditor.diffWordWrap` to avoid collisions with `editor.wordWrap`.
diffEditorConfiguration.diffWordWrap = <'off' | 'on' | 'inherit' | undefined>diffEditorConfiguration.wordWrap;
delete diffEditorConfiguration.wordWrap;
Object.assign(editorConfiguration, diffEditorConfiguration);
}
return editorConfiguration;
@@ -309,12 +329,7 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditorPan
// Otherwise save it
else {
super.saveTextEditorViewState(resource);
// Make sure to clean up when the input gets disposed
Event.once(input.onDispose)(() => {
super.clearTextEditorViewState([resource], this.group);
});
super.saveTextEditorViewState(resource, input);
}
}

View File

@@ -67,6 +67,7 @@ export abstract class BaseTextEditor extends EditorPane implements ITextEditorPa
@IEditorGroupsService protected editorGroupService: IEditorGroupsService
) {
super(id, telemetryService, themeService, storageService);
this._instantiationService = instantiationService;
this.editorMemento = this.getEditorMemento<IEditorViewState>(editorGroupService, BaseTextEditor.TEXT_EDITOR_VIEW_STATE_PREFERENCE_KEY, 100);
@@ -223,13 +224,27 @@ export abstract class BaseTextEditor extends EditorPane implements ITextEditorPa
return this.editorControl;
}
protected saveTextEditorViewState(resource: URI): void {
getViewState(): IEditorViewState | undefined {
const resource = this.input?.resource;
if (resource) {
return withNullAsUndefined(this.retrieveTextEditorViewState(resource));
}
return undefined;
}
protected saveTextEditorViewState(resource: URI, cleanUpOnDispose?: IEditorInput): void {
const editorViewState = this.retrieveTextEditorViewState(resource);
if (!editorViewState || !this.group) {
return;
}
this.editorMemento.saveEditorState(this.group, resource, editorViewState);
if (cleanUpOnDispose) {
this.editorMemento.clearEditorStateOnDispose(resource, cleanUpOnDispose);
}
}
protected shouldRestoreTextEditorViewState(editor: IEditorInput, context?: IEditorOpenContext): boolean {
@@ -243,15 +258,6 @@ export abstract class BaseTextEditor extends EditorPane implements ITextEditorPa
return true;
}
getViewState(): IEditorViewState | undefined {
const resource = this.input?.resource;
if (resource) {
return withNullAsUndefined(this.retrieveTextEditorViewState(resource));
}
return undefined;
}
protected retrieveTextEditorViewState(resource: URI): IEditorViewState | null {
const control = this.getControl();
if (!isCodeEditor(control)) {
@@ -284,9 +290,9 @@ export abstract class BaseTextEditor extends EditorPane implements ITextEditorPa
}
protected clearTextEditorViewState(resources: URI[], group?: IEditorGroup): void {
resources.forEach(resource => {
for (const resource of resources) {
this.editorMemento.clearEditorState(resource, group);
});
}
}
private updateEditorConfiguration(configuration?: IEditorConfiguration): void {

View File

@@ -16,7 +16,6 @@ import { IStorageService } from 'vs/platform/storage/common/storage';
import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfigurationService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { Event } from 'vs/base/common/event';
import { ScrollType, IEditor } from 'vs/editor/common/editorCommon';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { CancellationToken } from 'vs/base/common/cancellation';
@@ -158,12 +157,7 @@ export class AbstractTextResourceEditor extends BaseTextEditor {
// Otherwise save it
else {
super.saveTextEditorViewState(resource);
// Make sure to clean up when the input gets disposed
Event.once(input.onDispose)(() => {
super.clearTextEditorViewState([resource]);
});
super.saveTextEditorViewState(resource, input);
}
}
}

View File

@@ -105,18 +105,27 @@
overflow: hidden;
}
.monaco-workbench .notifications-list-container .notification-list-item .notification-list-item-buttons-container > .monaco-button-dropdown,
.monaco-workbench .notifications-list-container .notification-list-item .notification-list-item-buttons-container > .monaco-button {
margin: 4px 5px; /* allows button focus outline to be visible */
}
.monaco-workbench .notifications-list-container .notification-list-item .notification-list-item-buttons-container .monaco-button {
outline-offset: 2px !important;
}
.monaco-workbench .notifications-list-container .notification-list-item .notification-list-item-buttons-container .monaco-text-button {
width: fit-content;
width: -moz-fit-content;
padding: 5px 10px;
margin: 4px 5px; /* allows button focus outline to be visible */
display: inline-block; /* to enable ellipsis in text overflow */
font-size: 12px;
overflow: hidden;
text-overflow: ellipsis;
}
.monaco-workbench .notifications-list-container .notification-list-item .notification-list-item-buttons-container .monaco-text-button {
display: inline-block; /* to enable ellipsis in text overflow */
.monaco-workbench .notifications-list-container .notification-list-item .notification-list-item-buttons-container .monaco-dropdown-button {
padding: 5px
}
/** Notification: Progress */

View File

@@ -26,7 +26,7 @@
}
.monaco-workbench > .notifications-toasts .notification-toast-container > .notification-toast {
margin: 5px; /* enables separation and drop shadows around toasts */
margin: 8px; /* enables separation and drop shadows around toasts */
transform: translate3d(0px, 100%, 0px); /* move the notification 50px to the bottom (to prevent bleed through) */
opacity: 0; /* fade the toast in */
transition: transform 300ms ease-out, opacity 300ms ease-out;

View File

@@ -12,14 +12,16 @@ import { INotificationService } from 'vs/platform/notification/common/notificati
import { CLEAR_NOTIFICATION, EXPAND_NOTIFICATION, COLLAPSE_NOTIFICATION, CLEAR_ALL_NOTIFICATIONS, HIDE_NOTIFICATIONS_CENTER } from 'vs/workbench/browser/parts/notifications/notificationsCommands';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { Codicon, registerIcon } from 'vs/base/common/codicons';
import { Codicon } from 'vs/base/common/codicons';
import { registerIcon } from 'vs/platform/theme/common/iconRegistry';
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
const clearIcon = registerIcon('notifications-clear', Codicon.close);
const clearAllIcon = registerIcon('notifications-clear-all', Codicon.clearAll);
const hideIcon = registerIcon('notifications-hide', Codicon.chevronDown);
const expandIcon = registerIcon('notifications-expand', Codicon.chevronUp);
const collapseIcon = registerIcon('notifications-collapse', Codicon.chevronDown);
const configureIcon = registerIcon('notifications-configure', Codicon.gear);
const clearIcon = registerIcon('notifications-clear', Codicon.close, localize('clearIcon', 'Icon for the clear action in notifications.'));
const clearAllIcon = registerIcon('notifications-clear-all', Codicon.clearAll, localize('clearAllIcon', 'Icon for the clear all action in notifications.'));
const hideIcon = registerIcon('notifications-hide', Codicon.chevronDown, localize('hideIcon', 'Icon for the hide action in notifications.'));
const expandIcon = registerIcon('notifications-expand', Codicon.chevronUp, localize('expandIcon', 'Icon for the expand action in notifications.'));
const collapseIcon = registerIcon('notifications-collapse', Codicon.chevronDown, localize('collapseIcon', 'Icon for the collapse action in notifications.'));
const configureIcon = registerIcon('notifications-configure', Codicon.gear, localize('configureIcon', 'Icon for the configure action in notifications.'));
export class ClearNotificationAction extends Action {
@@ -31,7 +33,7 @@ export class ClearNotificationAction extends Action {
label: string,
@ICommandService private readonly commandService: ICommandService
) {
super(id, label, clearIcon.classNames);
super(id, label, ThemeIcon.asClassName(clearIcon));
}
async run(notification: INotificationViewItem): Promise<void> {
@@ -49,7 +51,7 @@ export class ClearAllNotificationsAction extends Action {
label: string,
@ICommandService private readonly commandService: ICommandService
) {
super(id, label, clearAllIcon.classNames);
super(id, label, ThemeIcon.asClassName(clearAllIcon));
}
async run(): Promise<void> {
@@ -67,7 +69,7 @@ export class HideNotificationsCenterAction extends Action {
label: string,
@ICommandService private readonly commandService: ICommandService
) {
super(id, label, hideIcon.classNames);
super(id, label, ThemeIcon.asClassName(hideIcon));
}
async run(): Promise<void> {
@@ -85,7 +87,7 @@ export class ExpandNotificationAction extends Action {
label: string,
@ICommandService private readonly commandService: ICommandService
) {
super(id, label, expandIcon.classNames);
super(id, label, ThemeIcon.asClassName(expandIcon));
}
async run(notification: INotificationViewItem): Promise<void> {
@@ -103,7 +105,7 @@ export class CollapseNotificationAction extends Action {
label: string,
@ICommandService private readonly commandService: ICommandService
) {
super(id, label, collapseIcon.classNames);
super(id, label, ThemeIcon.asClassName(collapseIcon));
}
async run(notification: INotificationViewItem): Promise<void> {
@@ -121,7 +123,7 @@ export class ConfigureNotificationAction extends Action {
label: string,
public readonly configurationActions: ReadonlyArray<IAction>
) {
super(id, label, configureIcon.classNames);
super(id, label, ThemeIcon.asClassName(configureIcon));
}
}

View File

@@ -251,7 +251,7 @@ export class NotificationsCenter extends Themable implements INotificationsCente
protected updateStyles(): void {
if (this.notificationsCenterContainer && this.notificationsCenterHeader) {
const widgetShadowColor = this.getColor(widgetShadow);
this.notificationsCenterContainer.style.boxShadow = widgetShadowColor ? `0 0px 8px ${widgetShadowColor}` : '';
this.notificationsCenterContainer.style.boxShadow = widgetShadowColor ? `0 0 8px 2px ${widgetShadowColor}` : '';
const borderColor = this.getColor(NOTIFICATIONS_CENTER_BORDER);
this.notificationsCenterContainer.style.border = borderColor ? `1px solid ${borderColor}` : '';

View File

@@ -15,20 +15,20 @@ import { IListService, WorkbenchList } from 'vs/platform/list/browser/listServic
// Center
export const SHOW_NOTIFICATIONS_CENTER = 'notifications.showList';
export const HIDE_NOTIFICATIONS_CENTER = 'notifications.hideList';
export const TOGGLE_NOTIFICATIONS_CENTER = 'notifications.toggleList';
const TOGGLE_NOTIFICATIONS_CENTER = 'notifications.toggleList';
// Toasts
export const HIDE_NOTIFICATION_TOAST = 'notifications.hideToasts';
export const FOCUS_NOTIFICATION_TOAST = 'notifications.focusToasts';
export const FOCUS_NEXT_NOTIFICATION_TOAST = 'notifications.focusNextToast';
export const FOCUS_PREVIOUS_NOTIFICATION_TOAST = 'notifications.focusPreviousToast';
export const FOCUS_FIRST_NOTIFICATION_TOAST = 'notifications.focusFirstToast';
export const FOCUS_LAST_NOTIFICATION_TOAST = 'notifications.focusLastToast';
const HIDE_NOTIFICATION_TOAST = 'notifications.hideToasts';
const FOCUS_NOTIFICATION_TOAST = 'notifications.focusToasts';
const FOCUS_NEXT_NOTIFICATION_TOAST = 'notifications.focusNextToast';
const FOCUS_PREVIOUS_NOTIFICATION_TOAST = 'notifications.focusPreviousToast';
const FOCUS_FIRST_NOTIFICATION_TOAST = 'notifications.focusFirstToast';
const FOCUS_LAST_NOTIFICATION_TOAST = 'notifications.focusLastToast';
// Notification
export const COLLAPSE_NOTIFICATION = 'notification.collapse';
export const EXPAND_NOTIFICATION = 'notification.expand';
export const TOGGLE_NOTIFICATION = 'notification.toggle';
const TOGGLE_NOTIFICATION = 'notification.toggle';
export const CLEAR_NOTIFICATION = 'notification.clear';
export const CLEAR_ALL_NOTIFICATIONS = 'notifications.clearAll';
@@ -159,12 +159,20 @@ export function registerNotificationCommands(center: INotificationsCenterControl
});
// Hide Toasts
KeybindingsRegistry.registerCommandAndKeybindingRule({
CommandsRegistry.registerCommand(HIDE_NOTIFICATION_TOAST, accessor => toasts.hide());
KeybindingsRegistry.registerKeybindingRule({
id: HIDE_NOTIFICATION_TOAST,
weight: KeybindingWeight.WorkbenchContrib + 50,
weight: KeybindingWeight.WorkbenchContrib - 50, // lower when not focused (e.g. let editor suggest win over this command)
when: NotificationsToastsVisibleContext,
primary: KeyCode.Escape,
handler: accessor => toasts.hide()
primary: KeyCode.Escape
});
KeybindingsRegistry.registerKeybindingRule({
id: HIDE_NOTIFICATION_TOAST,
weight: KeybindingWeight.WorkbenchContrib + 100, // higher when focused
when: ContextKeyExpr.and(NotificationsToastsVisibleContext, NotificationFocusedContext),
primary: KeyCode.Escape
});
// Focus Toasts

View File

@@ -482,7 +482,7 @@ export class NotificationsToasts extends Themable implements INotificationsToast
toast.style.background = backgroundColor ? backgroundColor : '';
const widgetShadowColor = this.getColor(widgetShadow);
toast.style.boxShadow = widgetShadowColor ? `0 0px 8px ${widgetShadowColor}` : '';
toast.style.boxShadow = widgetShadowColor ? `0 0 8px 2px ${widgetShadowColor}` : '';
const borderColor = this.getColor(NOTIFICATIONS_TOAST_BORDER);
toast.style.border = borderColor ? `1px solid ${borderColor}` : '';

View File

@@ -8,11 +8,11 @@ import { clearNode, addDisposableListener, EventType, EventHelper, $ } from 'vs/
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { URI } from 'vs/base/common/uri';
import { localize } from 'vs/nls';
import { ButtonGroup } from 'vs/base/browser/ui/button/button';
import { ButtonBar } from 'vs/base/browser/ui/button/button';
import { attachButtonStyler, attachProgressBarStyler } from 'vs/platform/theme/common/styler';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
import { IAction, IActionRunner } from 'vs/base/common/actions';
import { ActionRunner, ActionWithMenuAction, IAction, IActionRunner } from 'vs/base/common/actions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { dispose, DisposableStore, Disposable } from 'vs/base/common/lifecycle';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
@@ -285,7 +285,8 @@ export class NotificationTemplateRenderer extends Disposable {
@IOpenerService private readonly openerService: IOpenerService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IThemeService private readonly themeService: IThemeService,
@IKeybindingService private readonly keybindingService: IKeybindingService
@IKeybindingService private readonly keybindingService: IKeybindingService,
@IContextMenuService private readonly contextMenuService: IContextMenuService,
) {
super();
@@ -441,27 +442,40 @@ export class NotificationTemplateRenderer extends Disposable {
const primaryActions = notification.actions ? notification.actions.primary : undefined;
if (notification.expanded && isNonEmptyArray(primaryActions)) {
const buttonGroup = new ButtonGroup(this.template.buttonsContainer, primaryActions.length, { title: true /* assign titles to buttons in case they overflow */ });
buttonGroup.buttons.forEach((button, index) => {
const action = primaryActions[index];
button.label = action.label;
this.inputDisposables.add(button.onDidClick(e => {
EventHelper.stop(e, true);
const that = this;
const actionRunner: IActionRunner = new class extends ActionRunner {
protected async runAction(action: IAction): Promise<void> {
// Run action
this.actionRunner.run(action, notification);
that.actionRunner.run(action, notification);
// Hide notification (unless explicitly prevented)
if (!(action instanceof ChoiceAction) || !action.keepOpen) {
notification.close();
}
}
}();
const buttonToolbar = this.inputDisposables.add(new ButtonBar(this.template.buttonsContainer));
for (const action of primaryActions) {
const buttonOptions = { title: true, /* assign titles to buttons in case they overflow */ };
const dropdownActions = action instanceof ChoiceAction ? action.menu
: action instanceof ActionWithMenuAction ? action.actions : undefined;
const button = this.inputDisposables.add(
dropdownActions
? buttonToolbar.addButtonWithDropdown({
...buttonOptions,
contextMenuProvider: this.contextMenuService,
actions: dropdownActions,
actionRunner
})
: buttonToolbar.addButton(buttonOptions));
button.label = action.label;
this.inputDisposables.add(button.onDidClick(e => {
EventHelper.stop(e, true);
actionRunner.run(action);
}));
this.inputDisposables.add(attachButtonStyler(button, this.themeService));
});
this.inputDisposables.add(buttonGroup);
}
}
}

View File

@@ -84,10 +84,6 @@
justify-content: center;
}
.monaco-workbench .part.panel > .composite.title > .composite-bar-excess {
width: 100px;
}
.monaco-workbench .part.panel > .composite.title> .panel-switcher-container > .monaco-action-bar {
line-height: 27px; /* matches panel titles in settings */
height: 35px;

View File

@@ -18,11 +18,13 @@ import { IActivity } from 'vs/workbench/common/activity';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { ActivePanelContext, PanelPositionContext } from 'vs/workbench/common/panel';
import { ContextKeyExpression } from 'vs/platform/contextkey/common/contextkey';
import { Codicon, registerIcon } from 'vs/base/common/codicons';
import { Codicon } from 'vs/base/common/codicons';
import { registerIcon } from 'vs/platform/theme/common/iconRegistry';
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
const maximizeIcon = registerIcon('panel-maximize', Codicon.chevronUp);
const restoreIcon = registerIcon('panel-restore', Codicon.chevronDown);
const closeIcon = registerIcon('panel-close', Codicon.close);
const maximizeIcon = registerIcon('panel-maximize', Codicon.chevronUp, nls.localize('maximizeIcon', 'Icon to maximize a panel.'));
const restoreIcon = registerIcon('panel-restore', Codicon.chevronDown, nls.localize('restoreIcon', 'Icon to restore a panel.'));
const closeIcon = registerIcon('panel-close', Codicon.close, nls.localize('closeIcon', 'Icon to close a panel.'));
export class ClosePanelAction extends Action {
@@ -34,7 +36,7 @@ export class ClosePanelAction extends Action {
name: string,
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService
) {
super(id, name, closeIcon.classNames);
super(id, name, ThemeIcon.asClassName(closeIcon));
}
async run(): Promise<void> {
@@ -106,11 +108,11 @@ export class ToggleMaximizedPanelAction extends Action {
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,
@IEditorGroupsService editorGroupsService: IEditorGroupsService
) {
super(id, label, layoutService.isPanelMaximized() ? restoreIcon.classNames : maximizeIcon.classNames);
super(id, label, layoutService.isPanelMaximized() ? ThemeIcon.asClassName(restoreIcon) : ThemeIcon.asClassName(maximizeIcon));
this.toDispose.add(editorGroupsService.onDidLayout(() => {
const maximized = this.layoutService.isPanelMaximized();
this.class = maximized ? restoreIcon.classNames : maximizeIcon.classNames;
this.class = maximized ? ThemeIcon.asClassName(restoreIcon) : ThemeIcon.asClassName(maximizeIcon);
this.label = maximized ? ToggleMaximizedPanelAction.RESTORE_LABEL : ToggleMaximizedPanelAction.MAXIMIZE_LABEL;
}));
}

View File

@@ -13,7 +13,7 @@ import { CompositePart, ICompositeTitleLabel } from 'vs/workbench/browser/parts/
import { Panel, PanelRegistry, Extensions as PanelExtensions, PanelDescriptor } from 'vs/workbench/browser/panel';
import { IPanelService, IPanelIdentifier } from 'vs/workbench/services/panel/common/panelService';
import { IWorkbenchLayoutService, Parts, Position } from 'vs/workbench/services/layout/browser/layoutService';
import { IStorageService, StorageScope, IWorkspaceStorageChangeEvent } from 'vs/platform/storage/common/storage';
import { IStorageService, StorageScope, IStorageValueChangeEvent, StorageTarget } 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';
@@ -37,7 +37,6 @@ import { ViewContainer, IViewDescriptorService, IViewContainerModel, ViewContain
import { MenuId } from 'vs/platform/actions/common/actions';
import { ViewMenuActions, ViewContainerMenuActions } from 'vs/workbench/browser/parts/views/viewMenuActions';
import { IPaneComposite } from 'vs/workbench/common/panecomposite';
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
import { Before2D, CompositeDragAndDropObserver, ICompositeDragAndDrop, toggleDropEffect } from 'vs/workbench/browser/dnd';
import { IActivity } from 'vs/workbench/common/activity';
@@ -118,7 +117,6 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
@IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService,
@IContextKeyService private readonly contextKeyService: IContextKeyService,
@IExtensionService private readonly extensionService: IExtensionService,
@IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService
) {
super(
notificationService,
@@ -140,7 +138,6 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
);
this.panelRegistry = Registry.as<PanelRegistry>(PanelExtensions.Panels);
storageKeysSyncRegistryService.registerStorageKey({ key: PanelPart.PINNED_PANELS, version: 1 });
this.dndHandler = new CompositeDragAndDrop(this.viewDescriptorService, ViewContainerLocation.Panel,
(id: string, focus?: boolean) => (this.openPanel(id, focus) as Promise<IPaneComposite | undefined>).then(panel => panel || null),
@@ -338,7 +335,7 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
disposables.clear();
this.onDidRegisterExtensions();
this.compositeBar.onDidChange(() => this.saveCachedPanels(), this, disposables);
this.storageService.onDidChangeStorage(e => this.onDidStorageChange(e), this, disposables);
this.storageService.onDidChangeValue(e => this.onDidStorageValueChange(e), this, disposables);
}));
}
@@ -670,7 +667,7 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
return this.toolBar.getItemsWidth();
}
private onDidStorageChange(e: IWorkspaceStorageChangeEvent): void {
private onDidStorageValueChange(e: IStorageValueChangeEvent): void {
if (e.key === PanelPart.PINNED_PANELS && e.scope === StorageScope.GLOBAL
&& this.cachedPanelsValue !== this.getStoredCachedPanelsValue() /* This checks if current window changed the value or not */) {
this._cachedPanelsValue = undefined;
@@ -760,7 +757,7 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
}
private setStoredCachedViewletsValue(value: string): void {
this.storageService.store(PanelPart.PINNED_PANELS, value, StorageScope.GLOBAL);
this.storageService.store(PanelPart.PINNED_PANELS, value, StorageScope.GLOBAL, StorageTarget.USER);
}
private getPlaceholderViewContainers(): IPlaceholderViewContainer[] {
@@ -792,7 +789,7 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
}
private setStoredPlaceholderViewContainersValue(value: string): void {
this.storageService.store(PanelPart.PLACEHOLDER_VIEW_CONTAINERS, value, StorageScope.WORKSPACE);
this.storageService.store(PanelPart.PLACEHOLDER_VIEW_CONTAINERS, value, StorageScope.WORKSPACE, StorageTarget.MACHINE);
}
private getViewContainer(panelId: string): ViewContainer | undefined {

View File

@@ -23,7 +23,7 @@ import { isThemeColor } from 'vs/editor/common/editorCommon';
import { Color } from 'vs/base/common/color';
import { EventHelper, createStyleSheet, addDisposableListener, EventType, hide, show, isAncestor, appendChildren } from 'vs/base/browser/dom';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IStorageService, StorageScope, IWorkspaceStorageChangeEvent } from 'vs/platform/storage/common/storage';
import { IStorageService, StorageScope, IStorageValueChangeEvent, StorageTarget } from 'vs/platform/storage/common/storage';
import { Parts, IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { coalesce } from 'vs/base/common/arrays';
@@ -32,7 +32,6 @@ import { ToggleStatusbarVisibilityAction } from 'vs/workbench/browser/actions/la
import { assertIsDefined } from 'vs/base/common/types';
import { Emitter } from 'vs/base/common/event';
import { Command } from 'vs/editor/common/modes';
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyCode } from 'vs/base/common/keyCodes';
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
@@ -102,10 +101,10 @@ class StatusbarViewModel extends Disposable {
}
private registerListeners(): void {
this._register(this.storageService.onDidChangeStorage(e => this.onDidStorageChange(e)));
this._register(this.storageService.onDidChangeValue(e => this.onDidStorageValueChange(e)));
}
private onDidStorageChange(event: IWorkspaceStorageChangeEvent): void {
private onDidStorageValueChange(event: IStorageValueChangeEvent): void {
if (event.key === StatusbarViewModel.HIDDEN_ENTRIES_KEY && event.scope === StorageScope.GLOBAL) {
// Keep current hidden entries
@@ -275,7 +274,7 @@ class StatusbarViewModel extends Disposable {
private saveState(): void {
if (this.hidden.size > 0) {
this.storageService.store(StatusbarViewModel.HIDDEN_ENTRIES_KEY, JSON.stringify(Array.from(this.hidden.values())), StorageScope.GLOBAL);
this.storageService.store(StatusbarViewModel.HIDDEN_ENTRIES_KEY, JSON.stringify(Array.from(this.hidden.values())), StorageScope.GLOBAL, StorageTarget.USER);
} else {
this.storageService.remove(StatusbarViewModel.HIDDEN_ENTRIES_KEY, StorageScope.GLOBAL);
}
@@ -405,12 +404,9 @@ export class StatusbarPart extends Part implements IStatusbarService {
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
@IContextMenuService private contextMenuService: IContextMenuService,
@IContextKeyService private readonly contextKeyService: IContextKeyService,
@IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService,
) {
super(Parts.STATUSBAR_PART, { hasTitle: false }, themeService, storageService, layoutService);
storageKeysSyncRegistryService.registerStorageKey({ key: StatusbarViewModel.HIDDEN_ENTRIES_KEY, version: 1 });
this.registerListeners();
}

View File

@@ -11,7 +11,7 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IAction, Action, SubmenuAction, Separator } from 'vs/base/common/actions';
import * as DOM from 'vs/base/browser/dom';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { isMacintosh, isWeb, isIOS } from 'vs/base/common/platform';
import { isMacintosh, isWeb, isIOS, isNative } 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';
@@ -21,7 +21,7 @@ import { MENUBAR_SELECTION_FOREGROUND, MENUBAR_SELECTION_BACKGROUND, MENUBAR_SEL
import { URI } from 'vs/base/common/uri';
import { ILabelService } from 'vs/platform/label/common/label';
import { IUpdateService, StateType } from 'vs/platform/update/common/update';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
@@ -122,7 +122,7 @@ export abstract class MenubarControl extends Disposable {
this._register(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationUpdated(e)));
// Listen to update service
this.updateService.onStateChange(() => this.updateMenubar());
this.updateService.onStateChange(() => this.onUpdateStateChange());
// Listen for changes in recently opened menu
this._register(this.workspacesService.onRecentlyOpenedChange(() => { this.onRecentlyOpenedChange(); }));
@@ -148,6 +148,14 @@ export abstract class MenubarControl extends Disposable {
return label;
}
protected onUpdateStateChange(): void {
this.updateMenubar();
}
protected onUpdateKeybindings(): void {
this.updateMenubar();
}
protected getOpenRecentActions(): (Separator | IAction & { uri: URI })[] {
if (!this.recentlyOpened) {
return [];
@@ -191,13 +199,27 @@ export abstract class MenubarControl extends Disposable {
if (event.affectsConfiguration('editor.accessibilitySupport')) {
this.notifyUserOfCustomMenubarAccessibility();
}
// Since we try not update when hidden, we should
// try to update the recently opened list on visibility changes
if (event.affectsConfiguration('window.menuBarVisibility')) {
this.onRecentlyOpenedChange();
}
}
private onRecentlyOpenedChange(): void {
this.workspacesService.getRecentlyOpened().then(recentlyOpened => {
this.recentlyOpened = recentlyOpened;
this.updateMenubar();
});
private get menubarHidden(): boolean {
return isMacintosh && isNative ? false : getMenuBarVisibility(this.configurationService) === 'hidden';
}
protected onRecentlyOpenedChange(): void {
// Do not update recently opened when the menubar is hidden #108712
if (!this.menubarHidden) {
this.workspacesService.getRecentlyOpened().then(recentlyOpened => {
this.recentlyOpened = recentlyOpened;
this.updateMenubar();
});
}
}
private createOpenRecentMenuAction(recent: IRecent): IAction & { uri: URI } {
@@ -241,7 +263,7 @@ export abstract class MenubarControl extends Disposable {
}
const hasBeenNotified = this.storageService.getBoolean('menubar/accessibleMenubarNotified', StorageScope.GLOBAL, false);
const usingCustomMenubar = getTitleBarStyle(this.configurationService, this.environmentService) === 'custom';
const usingCustomMenubar = getTitleBarStyle(this.configurationService) === 'custom';
if (hasBeenNotified || usingCustomMenubar || !this.accessibilityService.isScreenReaderOptimized()) {
return;
@@ -257,7 +279,7 @@ export abstract class MenubarControl extends Disposable {
}
]);
this.storageService.store('menubar/accessibleMenubarNotified', true, StorageScope.GLOBAL);
this.storageService.store('menubar/accessibleMenubarNotified', true, StorageScope.GLOBAL, StorageTarget.USER);
}
}
@@ -266,6 +288,7 @@ export class CustomMenubarControl extends MenubarControl {
private container: HTMLElement | undefined;
private alwaysOnMnemonics: boolean = false;
private focusInsideMenubar: boolean = false;
private visible: boolean = true;
private readonly _onVisibilityChange: Emitter<boolean>;
private readonly _onFocusStateChange: Emitter<boolean>;
@@ -475,7 +498,7 @@ export class CustomMenubarControl extends MenubarControl {
}
private get currentMenubarVisibility(): MenuBarVisibility {
return getMenuBarVisibility(this.configurationService, this.environmentService);
return getMenuBarVisibility(this.configurationService);
}
private get currentDisableMenuBarAltFocus(): boolean {
@@ -530,6 +553,12 @@ export class CustomMenubarControl extends MenubarControl {
return currentSidebarLocation === 'right' ? Direction.Left : Direction.Right;
}
private onDidVisibilityChange(visible: boolean): void {
this.visible = visible;
this.onRecentlyOpenedChange();
this._onVisibilityChange.fire(visible);
}
private setupCustomMenubar(firstTime: boolean): void {
// If there is no container, we cannot setup the menubar
if (!this.container) {
@@ -554,7 +583,7 @@ export class CustomMenubarControl extends MenubarControl {
}
}));
this._register(this.menubar.onVisibilityChange(e => this._onVisibilityChange.fire(e)));
this._register(this.menubar.onVisibilityChange(e => this.onDidVisibilityChange(e)));
// Before we focus the menubar, stop updates to it so that focus-related context keys will work
this._register(DOM.addDisposableListener(this.container, DOM.EventType.FOCUS_IN, () => {
@@ -645,29 +674,15 @@ export class CustomMenubarControl extends MenubarControl {
visibility: this.currentMenubarVisibility,
getKeybinding: (action) => this.keybindingService.lookupKeybinding(action.id),
alwaysOnMnemonics: this.alwaysOnMnemonics,
compactMode: this.currentCompactMenuMode,
getCompactMenuActions: () => {
if (!isWeb) {
return []; // only for web
}
const webNavigationActions: IAction[] = [];
const webNavigationMenu = this.menuService.createMenu(MenuId.MenubarWebNavigationMenu, this.contextKeyService);
for (const groups of webNavigationMenu.getActions()) {
const [, actions] = groups;
for (const action of actions) {
action.label = mnemonicMenuLabel(this.calculateActionLabel(action));
webNavigationActions.push(action);
}
}
webNavigationMenu.dispose();
return webNavigationActions;
}
compactMode: this.currentCompactMenuMode
};
}
protected onDidChangeWindowFocus(hasFocus: boolean): void {
if (!this.visible) {
return;
}
super.onDidChangeWindowFocus(hasFocus);
if (this.container) {
@@ -682,6 +697,30 @@ export class CustomMenubarControl extends MenubarControl {
}
}
protected onUpdateStateChange(): void {
if (!this.visible) {
return;
}
super.onUpdateStateChange();
}
protected onRecentlyOpenedChange(): void {
if (!this.visible) {
return;
}
super.onRecentlyOpenedChange();
}
protected onUpdateKeybindings(): void {
if (!this.visible) {
return;
}
super.onUpdateKeybindings();
}
protected registerListeners(): void {
super.registerListeners();

View File

@@ -53,8 +53,8 @@ export class TitlebarPart extends Part implements ITitleService {
readonly minimumWidth: number = 0;
readonly maximumWidth: number = Number.POSITIVE_INFINITY;
get minimumHeight(): number { return isMacintosh && !isWeb ? 22 / getZoomFactor() : (30 / (this.currentMenubarVisibility === 'hidden' ? getZoomFactor() : 1)); }
get maximumHeight(): number { return isMacintosh && !isWeb ? 22 / getZoomFactor() : (30 / (this.currentMenubarVisibility === 'hidden' ? getZoomFactor() : 1)); }
get minimumHeight(): number { return 30 / (this.currentMenubarVisibility === 'hidden' ? getZoomFactor() : 1); }
get maximumHeight(): number { return this.minimumHeight; }
//#endregion
@@ -100,7 +100,7 @@ export class TitlebarPart extends Part implements ITitleService {
this.contextMenu = this._register(menuService.createMenu(MenuId.TitleBarContext, contextKeyService));
this.titleBarStyle = getTitleBarStyle(this.configurationService, this.environmentService);
this.titleBarStyle = getTitleBarStyle(this.configurationService);
this.registerListeners();
}
@@ -461,13 +461,13 @@ export class TitlebarPart extends Part implements ITitleService {
}
protected get currentMenubarVisibility(): MenuBarVisibility {
return getMenuBarVisibility(this.configurationService, this.environmentService);
return getMenuBarVisibility(this.configurationService);
}
updateLayout(dimension: Dimension): void {
this.lastLayoutDimensions = dimension;
if (getTitleBarStyle(this.configurationService, this.environmentService) === 'custom') {
if (getTitleBarStyle(this.configurationService) === 'custom') {
// Only prevent zooming behavior on macOS or when the menubar is not visible
if ((!isWeb && isMacintosh) || this.currentMenubarVisibility === 'hidden') {
this.title.style.zoom = `${1 / getZoomFactor()}`;

View File

@@ -0,0 +1,174 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
/* File icons in trees */
.file-icon-themable-tree.align-icons-and-twisties .monaco-tl-twistie:not(.force-twistie):not(.collapsible),
.file-icon-themable-tree .align-icon-with-twisty .monaco-tl-twistie:not(.force-twistie):not(.collapsible),
.file-icon-themable-tree.hide-arrows .monaco-tl-twistie:not(.force-twistie),
.file-icon-themable-tree .monaco-tl-twistie.force-no-twistie {
background-image: none !important;
width: 0 !important;
padding-right: 0 !important;
visibility: hidden;
}
/* Misc */
.monaco-workbench .tree-explorer-viewlet-tree-view {
height: 100%;
}
.monaco-workbench .tree-explorer-viewlet-tree-view .message {
display: flex;
padding: 4px 12px 4px 18px;
user-select: text;
-webkit-user-select: text;
}
.monaco-workbench .tree-explorer-viewlet-tree-view .message p {
margin-top: 0px;
margin-bottom: 0px;
padding-bottom: 4px;
}
.monaco-workbench .tree-explorer-viewlet-tree-view .message ul {
padding-left: 24px;
}
.monaco-workbench .tree-explorer-viewlet-tree-view .message.hide {
display: none;
}
.monaco-workbench .tree-explorer-viewlet-tree-view .customview-tree {
height: 100%;
}
.monaco-workbench .tree-explorer-viewlet-tree-view .customview-tree.hide {
display: none;
}
.monaco-workbench .pane > .pane-body > .welcome-view {
width: 100%;
height: 100%;
box-sizing: border-box;
display: flex;
flex-direction: column;
}
.monaco-workbench .pane > .pane-body:not(.welcome) > .welcome-view,
.monaco-workbench .pane > .pane-body.welcome > :not(.welcome-view) {
display: none;
}
.monaco-workbench .pane > .pane-body > .welcome-view .monaco-button {
margin-left: auto;
margin-right: auto;
}
.monaco-workbench .pane > .pane-body.wide > .welcome-view .monaco-button {
margin-left: inherit;
max-width: 260px;
}
.monaco-workbench .pane > .pane-body .welcome-view-content {
padding: 0 20px 0 20px;
box-sizing: border-box;
}
.monaco-workbench .pane > .pane-body .welcome-view-content > * {
margin-block-start: 1em;
margin-block-end: 1em;
margin-inline-start: 0px;
margin-inline-end: 0px;
}
.customview-tree .monaco-list-row .monaco-tl-contents.align-icon-with-twisty::before {
display: none;
}
.customview-tree .monaco-list-row .monaco-tl-contents:not(.align-icon-with-twisty)::before {
display: inline-block;
}
.customview-tree .monaco-list .monaco-list-row {
padding-right: 12px;
padding-left: 0px;
}
.customview-tree .monaco-list .monaco-list-row .custom-view-tree-node-item {
display: flex;
height: 22px;
line-height: 22px;
flex: 1;
text-overflow: ellipsis;
overflow: hidden;
flex-wrap: nowrap;
padding-left: 3px;
}
.customview-tree .monaco-list .monaco-list-row .custom-view-tree-node-item .monaco-inputbox {
line-height: normal;
flex: 1;
}
.customview-tree .monaco-list .monaco-list-row .custom-view-tree-node-item .custom-view-tree-node-item-resourceLabel {
flex: 1;
text-overflow: ellipsis;
overflow: hidden;
}
.customview-tree .monaco-list .monaco-list-row .custom-view-tree-node-item > .custom-view-tree-node-item-icon {
background-size: 16px;
background-position: left center;
background-repeat: no-repeat;
padding-right: 6px;
width: 16px;
height: 22px;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.customview-tree .monaco-list .monaco-list-row .custom-view-tree-node-item > .custom-view-tree-node-item-icon.codicon {
margin-top: 3px;
}
.customview-tree .monaco-list .monaco-list-row.selected .custom-view-tree-node-item > .custom-view-tree-node-item-icon.codicon {
color: currentColor !important;
}
.customview-tree .monaco-list .monaco-list-row .custom-view-tree-node-item .custom-view-tree-node-item-resourceLabel .monaco-icon-label-container > .monaco-icon-name-container {
flex: 1;
}
.customview-tree .monaco-list .monaco-list-row .custom-view-tree-node-item .custom-view-tree-node-item-resourceLabel::after {
padding-right: 0px;
}
.customview-tree .monaco-list .monaco-list-row .custom-view-tree-node-item .actions {
display: none;
}
.customview-tree .monaco-list .monaco-list-row:hover .custom-view-tree-node-item .actions,
.customview-tree .monaco-list .monaco-list-row.selected .custom-view-tree-node-item .actions,
.customview-tree .monaco-list .monaco-list-row.focused .custom-view-tree-node-item .actions {
display: block;
}
.customview-tree .monaco-list .custom-view-tree-node-item .actions .action-label {
width: 16px;
height: 100%;
background-size: 16px;
background-position: 50% 50%;
background-repeat: no-repeat;
}
.customview-tree .monaco-list .custom-view-tree-node-item .actions .action-label.codicon {
line-height: 22px;
}
.customview-tree .monaco-list .custom-view-tree-node-item .actions .action-label.codicon::before {
vertical-align: middle;
}

File diff suppressed because it is too large Load Diff

View File

@@ -18,15 +18,15 @@ import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IThemeService, Themable } from 'vs/platform/theme/common/themeService';
import { IThemeService, Themable, ThemeIcon } from 'vs/platform/theme/common/themeService';
import { PaneView, IPaneViewOptions, IPaneOptions, Pane, IPaneStyles } from 'vs/base/browser/ui/splitview/paneview';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IWorkbenchLayoutService, Position } from 'vs/workbench/services/layout/browser/layoutService';
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
import { Extensions as ViewContainerExtensions, IView, FocusedViewContext, IViewDescriptor, ViewContainer, IViewDescriptorService, ViewContainerLocation, IViewPaneContainer, IViewsRegistry, IViewContentDescriptor, IAddedViewDescriptorRef, IViewDescriptorRef, IViewContainerModel } from 'vs/workbench/common/views';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { Extensions as ViewContainerExtensions, IView, FocusedViewContext, IViewDescriptor, ViewContainer, IViewDescriptorService, ViewContainerLocation, IViewPaneContainer, IViewsRegistry, IViewContentDescriptor, IAddedViewDescriptorRef, IViewDescriptorRef, IViewContainerModel, defaultViewIcon } from 'vs/workbench/common/views';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { assertIsDefined, isString } from 'vs/base/common/types';
import { assertIsDefined } from 'vs/base/common/types';
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
@@ -50,6 +50,8 @@ import { ScrollbarVisibility } from 'vs/base/common/scrollable';
import { URI } from 'vs/base/common/uri';
import { KeyMod, KeyCode, KeyChord } from 'vs/base/common/keyCodes';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { registerIcon } from 'vs/platform/theme/common/iconRegistry';
import { Codicon } from 'vs/base/common/codicons';
export interface IPaneColors extends IColorMapping {
dropBackground?: ColorIdentifier;
@@ -70,6 +72,9 @@ type WelcomeActionClassification = {
uri: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
};
const viewPaneContainerExpandedIcon = registerIcon('view-pane-container-expanded', Codicon.chevronDown, nls.localize('viewPaneContainerExpandedIcon', 'Icon for an expanded view pane container.'));
const viewPaneContainerCollapsedIcon = registerIcon('view-pane-container-collapsed', Codicon.chevronRight, nls.localize('viewPaneContainerCollapsedIcon', 'Icon for a collapsed view pane container.'));
const viewsRegistry = Registry.as<IViewsRegistry>(ViewContainerExtensions.ViewsRegistry);
interface IItem {
@@ -263,7 +268,10 @@ export abstract class ViewPane extends Pane implements IView {
if (changed) {
this._onDidChangeBodyVisibility.fire(expanded);
}
if (this.twistiesContainer) {
this.twistiesContainer.classList.remove(...ThemeIcon.asClassNameArray(this.getTwistyIcon(!expanded)));
this.twistiesContainer.classList.add(...ThemeIcon.asClassNameArray(this.getTwistyIcon(expanded)));
}
return changed;
}
@@ -288,7 +296,7 @@ export abstract class ViewPane extends Pane implements IView {
protected renderHeader(container: HTMLElement): void {
this.headerContainer = container;
this.renderTwisties(container);
this.twistiesContainer = append(container, $(ThemeIcon.asCSSSelector(this.getTwistyIcon(this.isExpanded()))));
this.renderHeaderTitle(container, this.title);
@@ -316,8 +324,8 @@ export abstract class ViewPane extends Pane implements IView {
this.updateActionsVisibility();
}
protected renderTwisties(container: HTMLElement): void {
this.twistiesContainer = append(container, $('.twisties.codicon.codicon-chevron-right'));
protected getTwistyIcon(expanded: boolean): ThemeIcon {
return expanded ? viewPaneContainerExpandedIcon : viewPaneContainerCollapsedIcon;
}
style(styles: IPaneStyles): void {
@@ -338,8 +346,8 @@ export abstract class ViewPane extends Pane implements IView {
}
}
private getIcon(): string | URI {
return this.viewDescriptorService.getViewDescriptorById(this.id)?.containerIcon || 'codicon-window';
private getIcon(): ThemeIcon | URI {
return this.viewDescriptorService.getViewDescriptorById(this.id)?.containerIcon || defaultViewIcon;
}
protected renderHeaderTitle(container: HTMLElement, title: string): void {
@@ -357,9 +365,8 @@ export abstract class ViewPane extends Pane implements IView {
-webkit-mask: ${asCSSUrl(icon)} no-repeat 50% 50%;
-webkit-mask-size: 16px;
`);
} else if (isString(icon)) {
this.iconContainer.classList.add('codicon');
cssClass = icon;
} else if (ThemeIcon.isThemeIcon(icon)) {
cssClass = ThemeIcon.asClassName(icon);
}
if (cssClass) {
@@ -556,9 +563,7 @@ export abstract class ViewPane extends Pane implements IView {
this.bodyContainer.classList.add('welcome');
this.viewWelcomeContainer.innerText = '';
let buttonIndex = 0;
for (const { content, preconditions } of contents) {
for (const { content, precondition } of contents) {
const lines = content.split('\n');
for (let line of lines) {
@@ -581,21 +586,15 @@ export abstract class ViewPane extends Pane implements IView {
disposables.add(button);
disposables.add(attachButtonStyler(button, this.themeService));
if (preconditions) {
const precondition = preconditions[buttonIndex];
if (precondition) {
const updateEnablement = () => button.enabled = this.contextKeyService.contextMatchesRules(precondition);
updateEnablement();
if (precondition) {
const updateEnablement = () => button.enabled = this.contextKeyService.contextMatchesRules(precondition);
updateEnablement();
const keys = new Set();
precondition.keys().forEach(key => keys.add(key));
const onDidChangeContext = Event.filter(this.contextKeyService.onDidChangeContext, e => e.affectsSome(keys));
onDidChangeContext(updateEnablement, null, disposables);
}
const keys = new Set();
precondition.keys().forEach(key => keys.add(key));
const onDidChangeContext = Event.filter(this.contextKeyService.onDidChangeContext, e => e.affectsSome(keys));
onDidChangeContext(updateEnablement, null, disposables);
}
buttonIndex++;
} else {
const p = append(this.viewWelcomeContainer, $('p'));
@@ -1319,7 +1318,7 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer {
saveState(): void {
this.panes.forEach((view) => view.saveState());
this.storageService.store(this.visibleViewsStorageId, this.length, StorageScope.WORKSPACE);
this.storageService.store(this.visibleViewsStorageId, this.length, StorageScope.WORKSPACE, StorageTarget.USER);
}
private onContextMenu(event: StandardMouseEvent, viewDescriptor: IViewDescriptor): void {