Add badge for remote explorer

Fixes microsoft/vscode-remote-release#3824
This commit is contained in:
Alex Ross
2020-10-12 14:44:45 +02:00
parent 0dfa897c22
commit 69821795e8
6 changed files with 91 additions and 23 deletions
@@ -19,7 +19,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import { VIEWLET_ID as EXPLORER } from 'vs/workbench/contrib/files/common/files';
import { VIEWLET_ID as SCM } from 'vs/workbench/contrib/scm/common/scm';
import { VIEWLET_ID as DEBUG } from 'vs/workbench/contrib/debug/common/debug';
import { VIEWLET_ID as REMOTE } from 'vs/workbench/contrib/remote/common/remote.contribution';
import { VIEWLET_ID as REMOTE } from 'vs/workbench/contrib/remote/browser/remoteExplorer';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { URI } from 'vs/base/common/uri';
import { ViewletRegistry, Extensions as ViewletExtensions, ShowViewletAction } from 'vs/workbench/browser/viewlet';
@@ -17,9 +17,9 @@ import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { FilterViewPaneContainer } from 'vs/workbench/browser/parts/views/viewsViewlet';
import { VIEWLET_ID } from 'vs/workbench/contrib/remote/common/remote.contribution';
import { ForwardedPortsView, VIEWLET_ID } from 'vs/workbench/contrib/remote/browser/remoteExplorer';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IViewDescriptor, IViewsRegistry, Extensions, ViewContainerLocation, IViewContainersRegistry, IViewDescriptorService, IAddedViewDescriptorRef, IViewsService } from 'vs/workbench/common/views';
import { IViewDescriptor, IViewsRegistry, Extensions, ViewContainerLocation, IViewContainersRegistry, IViewDescriptorService, IViewsService } from 'vs/workbench/common/views';
import { Registry } from 'vs/platform/registry/common/platform';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { IOpenerService } from 'vs/platform/opener/common/opener';
@@ -44,7 +44,7 @@ import { Action, IActionViewItem, IAction } from 'vs/base/common/actions';
import { isStringArray } from 'vs/base/common/types';
import { IRemoteExplorerService, MakeAddress, mapHasTunnelLocalhostOrAllInterfaces } from 'vs/workbench/services/remote/common/remoteExplorerService';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { TunnelPanelDescriptor, TunnelViewModel, forwardedPortsViewEnabled, OpenPortInBrowserAction } from 'vs/workbench/contrib/remote/browser/tunnelView';
import { forwardedPortsViewEnabled, OpenPortInBrowserAction } from 'vs/workbench/contrib/remote/browser/tunnelView';
import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer';
import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
import { ITreeRenderer, ITreeNode, IAsyncDataSource } from 'vs/base/browser/ui/tree/tree';
@@ -471,7 +471,6 @@ export class RemoteViewPaneContainer extends FilterViewPaneContainer implements
private helpPanelDescriptor = new HelpPanelDescriptor(this);
helpInformation: HelpInformation[] = [];
private actions: IAction[] | undefined;
private tunnelPanelDescriptor: TunnelPanelDescriptor | undefined;
constructor(
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
@@ -483,8 +482,8 @@ export class RemoteViewPaneContainer extends FilterViewPaneContainer implements
@IThemeService themeService: IThemeService,
@IContextMenuService contextMenuService: IContextMenuService,
@IExtensionService extensionService: IExtensionService,
@IRemoteExplorerService private readonly remoteExplorerService: IRemoteExplorerService,
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
@IRemoteExplorerService readonly remoteExplorerService: IRemoteExplorerService,
@IWorkbenchEnvironmentService readonly environmentService: IWorkbenchEnvironmentService,
@IContextKeyService private readonly contextKeyService: IContextKeyService,
@IViewDescriptorService viewDescriptorService: IViewDescriptorService
) {
@@ -555,18 +554,6 @@ export class RemoteViewPaneContainer extends FilterViewPaneContainer implements
return title;
}
onDidAddViewDescriptors(added: IAddedViewDescriptorRef[]): ViewPane[] {
// Call to super MUST be first, since registering the additional view will cause this to be called again.
const panels: ViewPane[] = super.onDidAddViewDescriptors(added);
// This context key is set to false in the constructor, but is expected to be changed by resolver extensions to enable the forwarded ports view.
const viewEnabled: boolean = !!forwardedPortsViewEnabled.getValue(this.contextKeyService);
if (this.environmentService.remoteAuthority && !this.tunnelPanelDescriptor && viewEnabled) {
this.tunnelPanelDescriptor = new TunnelPanelDescriptor(new TunnelViewModel(this.remoteExplorerService), this.environmentService);
const viewsRegistry = Registry.as<IViewsRegistry>(Extensions.ViewsRegistry);
viewsRegistry.registerViews([this.tunnelPanelDescriptor!], this.viewContainer);
}
return panels;
}
}
Registry.as<IViewContainersRegistry>(Extensions.ViewContainersRegistry).registerViewContainer(
@@ -928,4 +915,5 @@ class AutomaticPortForwarding extends Disposable implements IWorkbenchContributi
const workbenchContributionsRegistry = Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench);
workbenchContributionsRegistry.registerWorkbenchContribution(RemoteAgentConnectionStatusListener, LifecyclePhase.Eventually);
workbenchContributionsRegistry.registerWorkbenchContribution(RemoteStatusIndicator, LifecyclePhase.Starting);
workbenchContributionsRegistry.registerWorkbenchContribution(ForwardedPortsView, LifecyclePhase.Eventually);
workbenchContributionsRegistry.registerWorkbenchContribution(AutomaticPortForwarding, LifecyclePhase.Eventually);
@@ -0,0 +1,81 @@
/*---------------------------------------------------------------------------------------------
* 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 { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { Extensions, IViewDescriptorService, IViewsRegistry } from 'vs/workbench/common/views';
import { IActivityService, NumberBadge } from 'vs/workbench/services/activity/common/activity';
import { IRemoteExplorerService, TUNNEL_VIEW_ID } from 'vs/workbench/services/remote/common/remoteExplorerService';
import { forwardedPortsViewEnabled, TunnelPanelDescriptor, TunnelViewModel } from 'vs/workbench/contrib/remote/browser/tunnelView';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { Registry } from 'vs/platform/registry/common/platform';
export const VIEWLET_ID = 'workbench.view.remote';
export class ForwardedPortsView extends Disposable implements IWorkbenchContribution {
private contextKeyListener?: IDisposable;
private _activityBadge?: IDisposable;
constructor(
@IContextKeyService private readonly contextKeyService: IContextKeyService,
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
@IRemoteExplorerService private readonly remoteExplorerService: IRemoteExplorerService,
@IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService,
@IActivityService private readonly activityService: IActivityService
) {
super();
this.enableForwardedPortsView();
this.enableBadge();
}
private enableForwardedPortsView() {
if (this.contextKeyListener) {
this.contextKeyListener.dispose();
this.contextKeyListener = undefined;
}
const viewEnabled: boolean = !!forwardedPortsViewEnabled.getValue(this.contextKeyService);
if (this.environmentService.remoteAuthority && viewEnabled) {
const tunnelPanelDescriptor = new TunnelPanelDescriptor(new TunnelViewModel(this.remoteExplorerService), this.environmentService);
const viewsRegistry = Registry.as<IViewsRegistry>(Extensions.ViewsRegistry);
const viewContainer = this.viewDescriptorService.getViewContainerById(VIEWLET_ID);
if (viewContainer) {
viewsRegistry.registerViews([tunnelPanelDescriptor!], viewContainer);
}
} else if (this.environmentService.remoteAuthority) {
this.contextKeyListener = this.contextKeyService.onDidChangeContext(e => {
if (e.affectsSome(new Set(forwardedPortsViewEnabled.keys()))) {
this.enableForwardedPortsView();
}
});
}
}
private enableBadge() {
this._register(this.remoteExplorerService.tunnelModel.onForwardPort(() => this.updateActivityBadge()));
this._register(this.remoteExplorerService.tunnelModel.onClosePort(() => this.updateActivityBadge()));
const disposable = Registry.as<IViewsRegistry>(Extensions.ViewsRegistry).onViewsRegistered(e => {
if (e.find(view => view.views.find(viewDescriptor => viewDescriptor.id === TUNNEL_VIEW_ID))) {
this.updateActivityBadge();
disposable.dispose();
}
});
}
private updateActivityBadge() {
if (this._activityBadge) {
this._activityBadge.dispose();
}
if (this.remoteExplorerService.tunnelModel.forwarded.size > 0) {
const viewContainer = this.viewDescriptorService.getViewContainerByViewId(TUNNEL_VIEW_ID);
if (viewContainer) {
this._activityBadge = this.activityService.showViewContainerActivity(viewContainer.id, {
badge: new NumberBadge(this.remoteExplorerService.tunnelModel.forwarded.size, n => n === 1 ? nls.localize('1forwardedPort', "1 forwarded port") : nls.localize('nForwardedPorts', "{0} forwarded ports", n))
});
}
}
}
}
@@ -26,7 +26,7 @@ import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel';
import { ActionRunner, IAction } from 'vs/base/common/actions';
import { IMenuService, MenuId, IMenu, MenuRegistry, MenuItemAction, ILocalizedString, SubmenuItemAction } from 'vs/platform/actions/common/actions';
import { createAndFillInContextMenuActions, createAndFillInActionBarActions, MenuEntryActionViewItem, SubmenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem';
import { IRemoteExplorerService, TunnelModel, MakeAddress, TunnelType, ITunnelItem, Tunnel, mapHasTunnelLocalhostOrAllInterfaces } from 'vs/workbench/services/remote/common/remoteExplorerService';
import { IRemoteExplorerService, TunnelModel, MakeAddress, TunnelType, ITunnelItem, Tunnel, mapHasTunnelLocalhostOrAllInterfaces, TUNNEL_VIEW_ID } from 'vs/workbench/services/remote/common/remoteExplorerService';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
import { InputBox, MessageType } from 'vs/base/browser/ui/inputbox/inputBox';
@@ -438,7 +438,7 @@ const PortChangableContextKey = new RawContextKey<boolean>('portChangable', fals
class TunnelDataTree extends WorkbenchAsyncDataTree<any, any, any> { }
export class TunnelPanel extends ViewPane {
static readonly ID = '~remote.forwardedPorts';
static readonly ID = TUNNEL_VIEW_ID;
static readonly TITLE = nls.localize('remote.tunnel', "Forwarded Ports");
private tree!: TunnelDataTree;
private tunnelTypeContext: IContextKey<TunnelType>;
@@ -21,8 +21,6 @@ import { ShowCandidateContribution } from 'vs/workbench/contrib/remote/common/sh
import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry';
import { IJSONSchema } from 'vs/base/common/jsonSchema';
export const VIEWLET_ID = 'workbench.view.remote';
export class LabelContribution implements IWorkbenchContribution {
constructor(
@ILabelService private readonly labelService: ILabelService,
@@ -18,6 +18,7 @@ import { IAddressProvider } from 'vs/platform/remote/common/remoteAgentConnectio
export const IRemoteExplorerService = createDecorator<IRemoteExplorerService>('remoteExplorerService');
export const REMOTE_EXPLORER_TYPE_KEY: string = 'remote.explorerType';
const TUNNELS_TO_RESTORE = 'remote.tunnels.toRestore';
export const TUNNEL_VIEW_ID = '~remote.forwardedPorts';
export enum TunnelType {
Candidate = 'Candidate',