Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Remote Explorer when moved #194782

Merged
merged 1 commit into from
Oct 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export class SwitchRemoteViewItem extends Disposable {
super();
this.selectedRemoteContext = SELECTED_REMOTE_IN_EXPLORER.bindTo(contextKeyService);

this.switchRemoteMenu = new MenuId('workbench.remote.menu.switchRemoteMenu');
this.switchRemoteMenu = MenuId.for('workbench.remote.menu.switchRemoteMenu');
this._register(MenuRegistry.appendMenuItem(MenuId.ViewContainerTitle, {
submenu: this.switchRemoteMenu,
title: nls.localize('switchRemote.label', "Switch Remote"),
Expand Down
103 changes: 29 additions & 74 deletions src/vs/workbench/contrib/remote/browser/remote.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,80 +36,24 @@ import { ReloadWindowAction } from 'vs/workbench/browser/actions/windowActions';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { SwitchRemoteViewItem } from 'vs/workbench/contrib/remote/browser/explorerViewItems';
import { isStringArray } from 'vs/base/common/types';
import { IRemoteExplorerService } from 'vs/workbench/services/remote/common/remoteExplorerService';
import { HelpInformation, IRemoteExplorerService } from 'vs/workbench/services/remote/common/remoteExplorerService';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPane';
import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
import { ITreeRenderer, ITreeNode, IAsyncDataSource } from 'vs/base/browser/ui/tree/tree';
import { WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { Event, Emitter } from 'vs/base/common/event';
import { ExtensionsRegistry, IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry';
import { IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import * as icons from 'vs/workbench/contrib/remote/browser/remoteIcons';
import { ILogService } from 'vs/platform/log/common/log';
import { ITimerService } from 'vs/workbench/services/timer/browser/timerService';
import { getRemoteName } from 'vs/platform/remote/common/remoteHosts';
import { getVirtualWorkspaceLocation } from 'vs/platform/workspace/common/virtualWorkspace';
import { IJSONSchema } from 'vs/base/common/jsonSchema';
import { IWalkthroughsService } from 'vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedService';
import { Schemas } from 'vs/base/common/network';

interface HelpInformation {
extensionDescription: IExtensionDescription;
getStarted?: string | { id: string };
documentation?: string;
issues?: string;
reportIssue?: string;
remoteName?: string[] | string;
virtualWorkspace?: string;
}

const getStartedWalkthrough: IJSONSchema = {
type: 'object',
required: ['id'],
properties: {
id: {
description: nls.localize('getStartedWalkthrough.id', 'The ID of a Get Started walkthrough to open.'),
type: 'string'
},
}
};

const remoteHelpExtPoint = ExtensionsRegistry.registerExtensionPoint<HelpInformation>({
extensionPoint: 'remoteHelp',
jsonSchema: {
description: nls.localize('RemoteHelpInformationExtPoint', 'Contributes help information for Remote'),
type: 'object',
properties: {
'getStarted': {
description: nls.localize('RemoteHelpInformationExtPoint.getStarted', "The url, or a command that returns the url, to your project's Getting Started page, or a walkthrough ID contributed by your project's extension"),
oneOf: [
{ type: 'string' },
getStartedWalkthrough
]
},
'documentation': {
description: nls.localize('RemoteHelpInformationExtPoint.documentation', "The url, or a command that returns the url, to your project's documentation page"),
type: 'string'
},
'feedback': {
description: nls.localize('RemoteHelpInformationExtPoint.feedback', "The url, or a command that returns the url, to your project's feedback reporter"),
type: 'string',
markdownDeprecationMessage: nls.localize('RemoteHelpInformationExtPoint.feedback.deprecated', "Use {0} instead", '`reportIssue`')
},
'reportIssue': {
description: nls.localize('RemoteHelpInformationExtPoint.reportIssue', "The url, or a command that returns the url, to your project's issue reporter"),
type: 'string'
},
'issues': {
description: nls.localize('RemoteHelpInformationExtPoint.issues', "The url, or a command that returns the url, to your project's issues list"),
type: 'string'
}
}
}
});

interface IViewModel {
onDidChangeHelpInformation: Event<void>;
helpInformation: HelpInformation[];
Expand Down Expand Up @@ -598,24 +542,13 @@ class RemoteViewPaneContainer extends FilterViewPaneContainer implements IViewMo
super(VIEWLET_ID, remoteExplorerService.onDidChangeTargetType, configurationService, layoutService, telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService, viewDescriptorService);
this.addConstantViewDescriptors([this.helpPanelDescriptor]);
this._register(this.remoteSwitcher = this.instantiationService.createInstance(SwitchRemoteViewItem));
const viewsRegistry = Registry.as<IViewsRegistry>(Extensions.ViewsRegistry);
remoteHelpExtPoint.setHandler((extensions) => {
const helpInformation: HelpInformation[] = [];
for (const extension of extensions) {
this._handleRemoteInfoExtensionPoint(extension, helpInformation);
}
this.remoteExplorerService.onDidChangeHelpInformation(extensions => {
this._setHelpInformation(extensions);
});

this.helpInformation = helpInformation;
this._onDidChangeHelpInformation.fire();
this._setHelpInformation(this.remoteExplorerService.helpInformation);
const viewsRegistry = Registry.as<IViewsRegistry>(Extensions.ViewsRegistry);

if (this.helpInformation.length && !this.hasRegisteredHelpView) {
viewsRegistry.registerViews([this.helpPanelDescriptor], this.viewContainer);
this.hasRegisteredHelpView = true;
} else if (this.hasRegisteredHelpView) {
viewsRegistry.deregisterViews([this.helpPanelDescriptor], this.viewContainer);
this.hasRegisteredHelpView = false;
}
});
this.remoteSwitcher.createOptionItems(viewsRegistry.getViews(this.viewContainer));
this._register(viewsRegistry.onViewsRegistered(e => {
const remoteViews: IViewDescriptor[] = [];
Expand All @@ -635,6 +568,28 @@ class RemoteViewPaneContainer extends FilterViewPaneContainer implements IViewMo
}));
}

private _setHelpInformation(extensions: readonly IExtensionPointUser<HelpInformation>[]) {
const helpInformation: HelpInformation[] = [];
for (const extension of extensions) {
this._handleRemoteInfoExtensionPoint(extension, helpInformation);
}

this.helpInformation = helpInformation;
this._onDidChangeHelpInformation.fire();

const viewsRegistry = Registry.as<IViewsRegistry>(Extensions.ViewsRegistry);
if (this.helpInformation.length && !this.hasRegisteredHelpView) {
const view = viewsRegistry.getView(this.helpPanelDescriptor.id);
if (!view) {
viewsRegistry.registerViews([this.helpPanelDescriptor], this.viewContainer);
}
this.hasRegisteredHelpView = true;
} else if (this.hasRegisteredHelpView) {
viewsRegistry.deregisterViews([this.helpPanelDescriptor], this.viewContainer);
this.hasRegisteredHelpView = false;
}
}

private _handleRemoteInfoExtensionPoint(extension: IExtensionPointUser<HelpInformation>, helpInformation: HelpInformation[]) {
if (!isProposedApiEnabled(extension.description, 'contribRemoteHelp')) {
return;
Expand Down
73 changes: 73 additions & 0 deletions src/vs/workbench/services/remote/common/remoteExplorerService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import * as nls from 'vs/nls';
import { Event, Emitter } from 'vs/base/common/event';
import { IInstantiationService, createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions';
Expand All @@ -13,6 +14,9 @@ import { IEditableData } from 'vs/workbench/common/views';
import { TunnelInformation, TunnelPrivacy } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { URI } from 'vs/base/common/uri';
import { Attributes, CandidatePort, TunnelCloseReason, TunnelModel, TunnelProperties, TunnelSource } from 'vs/workbench/services/remote/common/tunnelModel';
import { ExtensionsRegistry, IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { IJSONSchema } from 'vs/base/common/jsonSchema';

export const IRemoteExplorerService = createDecorator<IRemoteExplorerService>('remoteExplorerService');
export const REMOTE_EXPLORER_TYPE_KEY: string = 'remote.explorerType';
Expand Down Expand Up @@ -57,10 +61,67 @@ export enum TunnelEditId {
LocalPort = 3
}

export interface HelpInformation {
extensionDescription: IExtensionDescription;
getStarted?: string | { id: string };
documentation?: string;
issues?: string;
reportIssue?: string;
remoteName?: string[] | string;
virtualWorkspace?: string;
}

const getStartedWalkthrough: IJSONSchema = {
type: 'object',
required: ['id'],
properties: {
id: {
description: nls.localize('getStartedWalkthrough.id', 'The ID of a Get Started walkthrough to open.'),
type: 'string'
},
}
};

const remoteHelpExtPoint = ExtensionsRegistry.registerExtensionPoint<HelpInformation>({
extensionPoint: 'remoteHelp',
jsonSchema: {
description: nls.localize('RemoteHelpInformationExtPoint', 'Contributes help information for Remote'),
type: 'object',
properties: {
'getStarted': {
description: nls.localize('RemoteHelpInformationExtPoint.getStarted', "The url, or a command that returns the url, to your project's Getting Started page, or a walkthrough ID contributed by your project's extension"),
oneOf: [
{ type: 'string' },
getStartedWalkthrough
]
},
'documentation': {
description: nls.localize('RemoteHelpInformationExtPoint.documentation', "The url, or a command that returns the url, to your project's documentation page"),
type: 'string'
},
'feedback': {
description: nls.localize('RemoteHelpInformationExtPoint.feedback', "The url, or a command that returns the url, to your project's feedback reporter"),
type: 'string',
markdownDeprecationMessage: nls.localize('RemoteHelpInformationExtPoint.feedback.deprecated', "Use {0} instead", '`reportIssue`')
},
'reportIssue': {
description: nls.localize('RemoteHelpInformationExtPoint.reportIssue', "The url, or a command that returns the url, to your project's issue reporter"),
type: 'string'
},
'issues': {
description: nls.localize('RemoteHelpInformationExtPoint.issues', "The url, or a command that returns the url, to your project's issues list"),
type: 'string'
}
}
}
});

export interface IRemoteExplorerService {
readonly _serviceBrand: undefined;
onDidChangeTargetType: Event<string[]>;
targetType: string[];
onDidChangeHelpInformation: Event<readonly IExtensionPointUser<HelpInformation>[]>;
helpInformation: IExtensionPointUser<HelpInformation>[];
readonly tunnelModel: TunnelModel;
onDidChangeEditable: Event<{ tunnel: ITunnelItem; editId: TunnelEditId } | undefined>;
setEditable(tunnelItem: ITunnelItem | undefined, editId: TunnelEditId, data: IEditableData | null): void;
Expand All @@ -82,6 +143,9 @@ class RemoteExplorerService implements IRemoteExplorerService {
private _targetType: string[] = [];
private readonly _onDidChangeTargetType: Emitter<string[]> = new Emitter<string[]>();
public readonly onDidChangeTargetType: Event<string[]> = this._onDidChangeTargetType.event;
private readonly _onDidChangeHelpInformation: Emitter<readonly IExtensionPointUser<HelpInformation>[]> = new Emitter();
public readonly onDidChangeHelpInformation: Event<readonly IExtensionPointUser<HelpInformation>[]> = this._onDidChangeHelpInformation.event;
private _helpInformation: IExtensionPointUser<HelpInformation>[] = [];
private _tunnelModel: TunnelModel;
private _editable: { tunnelItem: ITunnelItem | undefined; editId: TunnelEditId; data: IEditableData } | undefined;
private readonly _onDidChangeEditable: Emitter<{ tunnel: ITunnelItem; editId: TunnelEditId } | undefined> = new Emitter();
Expand All @@ -97,6 +161,15 @@ class RemoteExplorerService implements IRemoteExplorerService {
@IInstantiationService instantiationService: IInstantiationService,
) {
this._tunnelModel = instantiationService.createInstance(TunnelModel);

remoteHelpExtPoint.setHandler((extensions) => {
this._helpInformation.push(...extensions);
this._onDidChangeHelpInformation.fire(extensions);
});
}

get helpInformation(): IExtensionPointUser<HelpInformation>[] {
return this._helpInformation;
}

set targetType(name: string[]) {
Expand Down