mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 18:46:40 -05:00
Merge from vscode 817eb6b0c720a4ecbc13c020afbbebfed667aa09 (#7356)
This commit is contained in:
@@ -7,9 +7,6 @@ import { IConfigurationCache, ConfigurationKey } from 'vs/workbench/services/con
|
||||
|
||||
export class ConfigurationCache implements IConfigurationCache {
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
async read(key: ConfigurationKey): Promise<string> {
|
||||
return '';
|
||||
}
|
||||
@@ -19,4 +16,4 @@ export class ConfigurationCache implements IConfigurationCache {
|
||||
|
||||
async remove(key: ConfigurationKey): Promise<void> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -507,17 +507,17 @@ export class WorkspaceService extends Disposable implements IConfigurationServic
|
||||
private registerConfigurationSchemas(): void {
|
||||
if (this.workspace) {
|
||||
const jsonRegistry = Registry.as<IJSONContributionRegistry>(JSONExtensions.JSONContribution);
|
||||
const allSettingsSchema: IJSONSchema = { properties: allSettings.properties, patternProperties: allSettings.patternProperties, additionalProperties: true, allowsTrailingCommas: true, allowComments: true };
|
||||
const userSettingsSchema: IJSONSchema = this.remoteUserConfiguration ? { properties: { ...applicationSettings.properties, ...windowSettings.properties, ...resourceSettings.properties }, patternProperties: allSettings.patternProperties, additionalProperties: true, allowsTrailingCommas: true, allowComments: true } : allSettingsSchema;
|
||||
const machineSettingsSchema: IJSONSchema = { properties: { ...machineSettings.properties, ...machineOverridableSettings.properties, ...windowSettings.properties, ...resourceSettings.properties }, patternProperties: allSettings.patternProperties, additionalProperties: true, allowsTrailingCommas: true, allowComments: true };
|
||||
const workspaceSettingsSchema: IJSONSchema = { properties: { ...machineOverridableSettings.properties, ...windowSettings.properties, ...resourceSettings.properties }, patternProperties: allSettings.patternProperties, additionalProperties: true, allowsTrailingCommas: true, allowComments: true };
|
||||
const allSettingsSchema: IJSONSchema = { properties: allSettings.properties, patternProperties: allSettings.patternProperties, additionalProperties: true, allowTrailingCommas: true, allowComments: true };
|
||||
const userSettingsSchema: IJSONSchema = this.remoteUserConfiguration ? { properties: { ...applicationSettings.properties, ...windowSettings.properties, ...resourceSettings.properties }, patternProperties: allSettings.patternProperties, additionalProperties: true, allowTrailingCommas: true, allowComments: true } : allSettingsSchema;
|
||||
const machineSettingsSchema: IJSONSchema = { properties: { ...machineSettings.properties, ...machineOverridableSettings.properties, ...windowSettings.properties, ...resourceSettings.properties }, patternProperties: allSettings.patternProperties, additionalProperties: true, allowTrailingCommas: true, allowComments: true };
|
||||
const workspaceSettingsSchema: IJSONSchema = { properties: { ...machineOverridableSettings.properties, ...windowSettings.properties, ...resourceSettings.properties }, patternProperties: allSettings.patternProperties, additionalProperties: true, allowTrailingCommas: true, allowComments: true };
|
||||
|
||||
jsonRegistry.registerSchema(defaultSettingsSchemaId, allSettingsSchema);
|
||||
jsonRegistry.registerSchema(userSettingsSchemaId, userSettingsSchema);
|
||||
jsonRegistry.registerSchema(machineSettingsSchemaId, machineSettingsSchema);
|
||||
|
||||
if (WorkbenchState.WORKSPACE === this.getWorkbenchState()) {
|
||||
const folderSettingsSchema: IJSONSchema = { properties: { ...machineOverridableSettings.properties, ...resourceSettings.properties }, patternProperties: allSettings.patternProperties, additionalProperties: true, allowsTrailingCommas: true, allowComments: true };
|
||||
const folderSettingsSchema: IJSONSchema = { properties: { ...machineOverridableSettings.properties, ...resourceSettings.properties }, patternProperties: allSettings.patternProperties, additionalProperties: true, allowTrailingCommas: true, allowComments: true };
|
||||
jsonRegistry.registerSchema(workspaceSettingsSchemaId, workspaceSettingsSchema);
|
||||
jsonRegistry.registerSchema(folderSettingsSchemaId, folderSettingsSchema);
|
||||
} else {
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { IWindowService, IURIToOpen, FileFilter } from 'vs/platform/windows/common/windows';
|
||||
import { IPickAndOpenOptions, ISaveDialogOptions, IOpenDialogOptions } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
import { IHistoryService } from 'vs/workbench/services/history/common/history';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import * as resources from 'vs/base/common/resources';
|
||||
import { IInstantiationService, } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { SimpleFileDialog } from 'vs/workbench/services/dialogs/browser/simpleFileDialog';
|
||||
import { WORKSPACE_EXTENSION } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { isWeb } from 'vs/base/common/platform';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
|
||||
export class AbstractFileDialogService {
|
||||
|
||||
_serviceBrand: undefined;
|
||||
|
||||
constructor(
|
||||
@IWindowService protected readonly windowService: IWindowService,
|
||||
@IWorkspaceContextService protected readonly contextService: IWorkspaceContextService,
|
||||
@IHistoryService protected readonly historyService: IHistoryService,
|
||||
@IWorkbenchEnvironmentService protected readonly environmentService: IWorkbenchEnvironmentService,
|
||||
@IInstantiationService protected readonly instantiationService: IInstantiationService,
|
||||
@IConfigurationService protected readonly configurationService: IConfigurationService,
|
||||
@IFileService protected readonly fileService: IFileService,
|
||||
@IOpenerService protected readonly openerService: IOpenerService,
|
||||
) { }
|
||||
|
||||
defaultFilePath(schemeFilter = this.getSchemeFilterForWindow()): URI | undefined {
|
||||
|
||||
// Check for last active file first...
|
||||
let candidate = this.historyService.getLastActiveFile(schemeFilter);
|
||||
|
||||
// ...then for last active file root
|
||||
if (!candidate) {
|
||||
candidate = this.historyService.getLastActiveWorkspaceRoot(schemeFilter);
|
||||
} else {
|
||||
candidate = candidate && resources.dirname(candidate);
|
||||
}
|
||||
|
||||
return candidate || undefined;
|
||||
}
|
||||
|
||||
defaultFolderPath(schemeFilter = this.getSchemeFilterForWindow()): URI | undefined {
|
||||
|
||||
// Check for last active file root first...
|
||||
let candidate = this.historyService.getLastActiveWorkspaceRoot(schemeFilter);
|
||||
|
||||
// ...then for last active file
|
||||
if (!candidate) {
|
||||
candidate = this.historyService.getLastActiveFile(schemeFilter);
|
||||
}
|
||||
|
||||
return candidate && resources.dirname(candidate) || undefined;
|
||||
}
|
||||
|
||||
defaultWorkspacePath(schemeFilter = this.getSchemeFilterForWindow()): URI | undefined {
|
||||
|
||||
// Check for current workspace config file first...
|
||||
if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) {
|
||||
const configuration = this.contextService.getWorkspace().configuration;
|
||||
if (configuration && !isUntitledWorkspace(configuration, this.environmentService)) {
|
||||
return resources.dirname(configuration) || undefined;
|
||||
}
|
||||
}
|
||||
|
||||
// ...then fallback to default file path
|
||||
return this.defaultFilePath(schemeFilter);
|
||||
}
|
||||
|
||||
protected addFileSchemaIfNeeded(schema: string): string[] {
|
||||
// Include File schema unless the schema is web
|
||||
// Don't allow untitled schema through.
|
||||
if (isWeb) {
|
||||
return schema === Schemas.untitled ? [Schemas.file] : [schema];
|
||||
} else {
|
||||
return schema === Schemas.untitled ? [Schemas.file] : (schema !== Schemas.file ? [schema, Schemas.file] : [schema]);
|
||||
}
|
||||
}
|
||||
|
||||
protected async pickFileFolderAndOpenSimplified(schema: string, options: IPickAndOpenOptions, preferNewWindow: boolean): Promise<any> {
|
||||
const title = nls.localize('openFileOrFolder.title', 'Open File Or Folder');
|
||||
const availableFileSystems = this.addFileSchemaIfNeeded(schema);
|
||||
|
||||
const uri = await this.pickResource({ canSelectFiles: true, canSelectFolders: true, canSelectMany: false, defaultUri: options.defaultUri, title, availableFileSystems });
|
||||
|
||||
if (uri) {
|
||||
const stat = await this.fileService.resolve(uri);
|
||||
|
||||
const toOpen: IURIToOpen = stat.isDirectory ? { folderUri: uri } : { fileUri: uri };
|
||||
if (stat.isDirectory || options.forceNewWindow || preferNewWindow) {
|
||||
return this.windowService.openWindow([toOpen], { forceNewWindow: options.forceNewWindow });
|
||||
} else {
|
||||
return this.openerService.open(uri);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected async pickFileAndOpenSimplified(schema: string, options: IPickAndOpenOptions, preferNewWindow: boolean): Promise<any> {
|
||||
const title = nls.localize('openFile.title', 'Open File');
|
||||
const availableFileSystems = this.addFileSchemaIfNeeded(schema);
|
||||
|
||||
const uri = await this.pickResource({ canSelectFiles: true, canSelectFolders: false, canSelectMany: false, defaultUri: options.defaultUri, title, availableFileSystems });
|
||||
if (uri) {
|
||||
if (options.forceNewWindow || preferNewWindow) {
|
||||
return this.windowService.openWindow([{ fileUri: uri }], { forceNewWindow: options.forceNewWindow });
|
||||
} else {
|
||||
return this.openerService.open(uri);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected async pickFolderAndOpenSimplified(schema: string, options: IPickAndOpenOptions): Promise<any> {
|
||||
const title = nls.localize('openFolder.title', 'Open Folder');
|
||||
const availableFileSystems = this.addFileSchemaIfNeeded(schema);
|
||||
|
||||
const uri = await this.pickResource({ canSelectFiles: false, canSelectFolders: true, canSelectMany: false, defaultUri: options.defaultUri, title, availableFileSystems });
|
||||
if (uri) {
|
||||
return this.windowService.openWindow([{ folderUri: uri }], { forceNewWindow: options.forceNewWindow });
|
||||
}
|
||||
}
|
||||
|
||||
protected async pickWorkspaceAndOpenSimplified(schema: string, options: IPickAndOpenOptions): Promise<any> {
|
||||
const title = nls.localize('openWorkspace.title', 'Open Workspace');
|
||||
const filters: FileFilter[] = [{ name: nls.localize('filterName.workspace', 'Workspace'), extensions: [WORKSPACE_EXTENSION] }];
|
||||
const availableFileSystems = this.addFileSchemaIfNeeded(schema);
|
||||
|
||||
const uri = await this.pickResource({ canSelectFiles: true, canSelectFolders: false, canSelectMany: false, defaultUri: options.defaultUri, title, filters, availableFileSystems });
|
||||
if (uri) {
|
||||
return this.windowService.openWindow([{ workspaceUri: uri }], { forceNewWindow: options.forceNewWindow });
|
||||
}
|
||||
}
|
||||
|
||||
protected async pickFileToSaveSimplified(schema: string, options: ISaveDialogOptions): Promise<URI | undefined> {
|
||||
if (!options.availableFileSystems) {
|
||||
options.availableFileSystems = this.addFileSchemaIfNeeded(schema);
|
||||
}
|
||||
|
||||
options.title = nls.localize('saveFileAs.title', 'Save As');
|
||||
return this.saveRemoteResource(options);
|
||||
}
|
||||
|
||||
protected async showSaveDialogSimplified(schema: string, options: ISaveDialogOptions): Promise<URI | undefined> {
|
||||
if (!options.availableFileSystems) {
|
||||
options.availableFileSystems = this.addFileSchemaIfNeeded(schema);
|
||||
}
|
||||
|
||||
return this.saveRemoteResource(options);
|
||||
}
|
||||
|
||||
protected async showOpenDialogSimplified(schema: string, options: IOpenDialogOptions): Promise<URI[] | undefined> {
|
||||
if (!options.availableFileSystems) {
|
||||
options.availableFileSystems = this.addFileSchemaIfNeeded(schema);
|
||||
}
|
||||
|
||||
const uri = await this.pickResource(options);
|
||||
|
||||
return uri ? [uri] : undefined;
|
||||
}
|
||||
|
||||
private pickResource(options: IOpenDialogOptions): Promise<URI | undefined> {
|
||||
const simpleFileDialog = this.instantiationService.createInstance(SimpleFileDialog);
|
||||
|
||||
return simpleFileDialog.showOpenDialog(options);
|
||||
}
|
||||
|
||||
private saveRemoteResource(options: ISaveDialogOptions): Promise<URI | undefined> {
|
||||
const remoteFileDialog = this.instantiationService.createInstance(SimpleFileDialog);
|
||||
|
||||
return remoteFileDialog.showSaveDialog(options);
|
||||
}
|
||||
|
||||
protected getSchemeFilterForWindow(): string {
|
||||
return !this.environmentService.configuration.remoteAuthority ? Schemas.file : REMOTE_HOST_SCHEME;
|
||||
}
|
||||
|
||||
protected getFileSystemSchema(options: { availableFileSystems?: string[], defaultUri?: URI }): string {
|
||||
return options.availableFileSystems && options.availableFileSystems[0] || this.getSchemeFilterForWindow();
|
||||
}
|
||||
}
|
||||
|
||||
function isUntitledWorkspace(path: URI, environmentService: IWorkbenchEnvironmentService): boolean {
|
||||
return resources.isEqualOrParent(path, environmentService.untitledWorkspacesHome);
|
||||
}
|
||||
@@ -3,105 +3,12 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { IWindowService, INativeOpenDialogOptions, OpenDialogOptions, IURIToOpen, FileFilter } from 'vs/platform/windows/common/windows';
|
||||
import { IPickAndOpenOptions, ISaveDialogOptions, IOpenDialogOptions, IFileDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
import { IHistoryService } from 'vs/workbench/services/history/common/history';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import * as resources from 'vs/base/common/resources';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { RemoteFileDialog } from 'vs/workbench/services/dialogs/browser/remoteFileDialog';
|
||||
import { WORKSPACE_EXTENSION } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { isWeb } from 'vs/base/common/platform';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { AbstractFileDialogService } from 'vs/workbench/services/dialogs/browser/abstractFileDialogService';
|
||||
|
||||
export class FileDialogService implements IFileDialogService {
|
||||
|
||||
_serviceBrand: undefined;
|
||||
|
||||
constructor(
|
||||
@IWindowService private readonly windowService: IWindowService,
|
||||
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
|
||||
@IHistoryService private readonly historyService: IHistoryService,
|
||||
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@IFileService private readonly fileService: IFileService,
|
||||
@IOpenerService private readonly openerService: IOpenerService
|
||||
) { }
|
||||
|
||||
defaultFilePath(schemeFilter = this.getSchemeFilterForWindow()): URI | undefined {
|
||||
|
||||
// Check for last active file first...
|
||||
let candidate = this.historyService.getLastActiveFile(schemeFilter);
|
||||
|
||||
// ...then for last active file root
|
||||
if (!candidate) {
|
||||
candidate = this.historyService.getLastActiveWorkspaceRoot(schemeFilter);
|
||||
} else {
|
||||
candidate = candidate && resources.dirname(candidate);
|
||||
}
|
||||
|
||||
return candidate || undefined;
|
||||
}
|
||||
|
||||
defaultFolderPath(schemeFilter = this.getSchemeFilterForWindow()): URI | undefined {
|
||||
|
||||
// Check for last active file root first...
|
||||
let candidate = this.historyService.getLastActiveWorkspaceRoot(schemeFilter);
|
||||
|
||||
// ...then for last active file
|
||||
if (!candidate) {
|
||||
candidate = this.historyService.getLastActiveFile(schemeFilter);
|
||||
}
|
||||
|
||||
return candidate && resources.dirname(candidate) || undefined;
|
||||
}
|
||||
|
||||
defaultWorkspacePath(schemeFilter = this.getSchemeFilterForWindow()): URI | undefined {
|
||||
|
||||
// Check for current workspace config file first...
|
||||
if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) {
|
||||
const configuration = this.contextService.getWorkspace().configuration;
|
||||
if (configuration && !isUntitledWorkspace(configuration, this.environmentService)) {
|
||||
return resources.dirname(configuration) || undefined;
|
||||
}
|
||||
}
|
||||
|
||||
// ...then fallback to default file path
|
||||
return this.defaultFilePath(schemeFilter);
|
||||
}
|
||||
|
||||
private toNativeOpenDialogOptions(options: IPickAndOpenOptions): INativeOpenDialogOptions {
|
||||
return {
|
||||
forceNewWindow: options.forceNewWindow,
|
||||
telemetryExtraData: options.telemetryExtraData,
|
||||
defaultPath: options.defaultUri && options.defaultUri.fsPath
|
||||
};
|
||||
}
|
||||
|
||||
private shouldUseSimplified(schema: string): { useSimplified: boolean, isSetting: boolean } {
|
||||
const setting = (this.configurationService.getValue('files.simpleDialog.enable') === true);
|
||||
|
||||
return { useSimplified: (schema !== Schemas.file) || setting, isSetting: (schema === Schemas.file) && setting };
|
||||
}
|
||||
|
||||
private addFileSchemaIfNeeded(schema: string): string[] {
|
||||
// Include File schema unless the schema is web
|
||||
// Don't allow untitled schema through.
|
||||
if (isWeb) {
|
||||
return schema === Schemas.untitled ? [Schemas.file] : [schema];
|
||||
} else {
|
||||
return schema === Schemas.untitled ? [Schemas.file] : (schema !== Schemas.file ? [schema, Schemas.file] : [schema]);
|
||||
}
|
||||
}
|
||||
export class FileDialogService extends AbstractFileDialogService implements IFileDialogService {
|
||||
|
||||
async pickFileFolderAndOpen(options: IPickAndOpenOptions): Promise<any> {
|
||||
const schema = this.getFileSystemSchema(options);
|
||||
@@ -110,28 +17,7 @@ export class FileDialogService implements IFileDialogService {
|
||||
options.defaultUri = this.defaultFilePath(schema);
|
||||
}
|
||||
|
||||
const shouldUseSimplified = this.shouldUseSimplified(schema);
|
||||
if (shouldUseSimplified.useSimplified) {
|
||||
const title = nls.localize('openFileOrFolder.title', 'Open File Or Folder');
|
||||
const availableFileSystems = this.addFileSchemaIfNeeded(schema);
|
||||
|
||||
const uri = await this.pickRemoteResource({ canSelectFiles: true, canSelectFolders: true, canSelectMany: false, defaultUri: options.defaultUri, title, availableFileSystems });
|
||||
|
||||
if (uri) {
|
||||
const stat = await this.fileService.resolve(uri);
|
||||
|
||||
const toOpen: IURIToOpen = stat.isDirectory ? { folderUri: uri } : { fileUri: uri };
|
||||
if (stat.isDirectory || options.forceNewWindow || shouldUseSimplified.isSetting) {
|
||||
return this.windowService.openWindow([toOpen], { forceNewWindow: options.forceNewWindow });
|
||||
} else {
|
||||
return this.openerService.open(uri);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
return this.windowService.pickFileFolderAndOpen(this.toNativeOpenDialogOptions(options));
|
||||
return this.pickFileFolderAndOpenSimplified(schema, options, false);
|
||||
}
|
||||
|
||||
async pickFileAndOpen(options: IPickAndOpenOptions): Promise<any> {
|
||||
@@ -141,24 +27,7 @@ export class FileDialogService implements IFileDialogService {
|
||||
options.defaultUri = this.defaultFilePath(schema);
|
||||
}
|
||||
|
||||
const shouldUseSimplified = this.shouldUseSimplified(schema);
|
||||
if (shouldUseSimplified.useSimplified) {
|
||||
const title = nls.localize('openFile.title', 'Open File');
|
||||
const availableFileSystems = this.addFileSchemaIfNeeded(schema);
|
||||
|
||||
const uri = await this.pickRemoteResource({ canSelectFiles: true, canSelectFolders: false, canSelectMany: false, defaultUri: options.defaultUri, title, availableFileSystems });
|
||||
if (uri) {
|
||||
if (options.forceNewWindow || shouldUseSimplified.isSetting) {
|
||||
return this.windowService.openWindow([{ fileUri: uri }], { forceNewWindow: options.forceNewWindow });
|
||||
} else {
|
||||
return this.openerService.open(uri);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
return this.windowService.pickFileAndOpen(this.toNativeOpenDialogOptions(options));
|
||||
return this.pickFileAndOpenSimplified(schema, options, false);
|
||||
}
|
||||
|
||||
async pickFolderAndOpen(options: IPickAndOpenOptions): Promise<any> {
|
||||
@@ -168,19 +37,7 @@ export class FileDialogService implements IFileDialogService {
|
||||
options.defaultUri = this.defaultFolderPath(schema);
|
||||
}
|
||||
|
||||
if (this.shouldUseSimplified(schema).useSimplified) {
|
||||
const title = nls.localize('openFolder.title', 'Open Folder');
|
||||
const availableFileSystems = this.addFileSchemaIfNeeded(schema);
|
||||
|
||||
const uri = await this.pickRemoteResource({ canSelectFiles: false, canSelectFolders: true, canSelectMany: false, defaultUri: options.defaultUri, title, availableFileSystems });
|
||||
if (uri) {
|
||||
return this.windowService.openWindow([{ folderUri: uri }], { forceNewWindow: options.forceNewWindow });
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
return this.windowService.pickFolderAndOpen(this.toNativeOpenDialogOptions(options));
|
||||
return this.pickFolderAndOpenSimplified(schema, options);
|
||||
}
|
||||
|
||||
async pickWorkspaceAndOpen(options: IPickAndOpenOptions): Promise<void> {
|
||||
@@ -190,133 +47,23 @@ export class FileDialogService implements IFileDialogService {
|
||||
options.defaultUri = this.defaultWorkspacePath(schema);
|
||||
}
|
||||
|
||||
if (this.shouldUseSimplified(schema).useSimplified) {
|
||||
const title = nls.localize('openWorkspace.title', 'Open Workspace');
|
||||
const filters: FileFilter[] = [{ name: nls.localize('filterName.workspace', 'Workspace'), extensions: [WORKSPACE_EXTENSION] }];
|
||||
const availableFileSystems = this.addFileSchemaIfNeeded(schema);
|
||||
|
||||
const uri = await this.pickRemoteResource({ canSelectFiles: true, canSelectFolders: false, canSelectMany: false, defaultUri: options.defaultUri, title, filters, availableFileSystems });
|
||||
if (uri) {
|
||||
return this.windowService.openWindow([{ workspaceUri: uri }], { forceNewWindow: options.forceNewWindow });
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
return this.windowService.pickWorkspaceAndOpen(this.toNativeOpenDialogOptions(options));
|
||||
return this.pickWorkspaceAndOpenSimplified(schema, options);
|
||||
}
|
||||
|
||||
async pickFileToSave(options: ISaveDialogOptions): Promise<URI | undefined> {
|
||||
const schema = this.getFileSystemSchema(options);
|
||||
if (this.shouldUseSimplified(schema).useSimplified) {
|
||||
if (!options.availableFileSystems) {
|
||||
options.availableFileSystems = this.addFileSchemaIfNeeded(schema);
|
||||
}
|
||||
|
||||
options.title = nls.localize('saveFileAs.title', 'Save As');
|
||||
return this.saveRemoteResource(options);
|
||||
}
|
||||
|
||||
const result = await this.windowService.showSaveDialog(this.toNativeSaveDialogOptions(options));
|
||||
if (result) {
|
||||
return URI.file(result);
|
||||
}
|
||||
|
||||
return undefined; // {{SQL CARBON EDIT}} strict-null-check
|
||||
}
|
||||
|
||||
private toNativeSaveDialogOptions(options: ISaveDialogOptions): Electron.SaveDialogOptions {
|
||||
options.defaultUri = options.defaultUri ? URI.file(options.defaultUri.path) : undefined;
|
||||
return {
|
||||
defaultPath: options.defaultUri && options.defaultUri.fsPath,
|
||||
buttonLabel: options.saveLabel,
|
||||
filters: options.filters,
|
||||
title: options.title
|
||||
};
|
||||
return this.pickFileToSaveSimplified(schema, options);
|
||||
}
|
||||
|
||||
async showSaveDialog(options: ISaveDialogOptions): Promise<URI | undefined> {
|
||||
const schema = this.getFileSystemSchema(options);
|
||||
if (this.shouldUseSimplified(schema).useSimplified) {
|
||||
if (!options.availableFileSystems) {
|
||||
options.availableFileSystems = this.addFileSchemaIfNeeded(schema);
|
||||
}
|
||||
|
||||
return this.saveRemoteResource(options);
|
||||
}
|
||||
|
||||
const result = await this.windowService.showSaveDialog(this.toNativeSaveDialogOptions(options));
|
||||
if (result) {
|
||||
return URI.file(result);
|
||||
}
|
||||
|
||||
return undefined; // {{SQL CARBON EDIT}} @anthonydresser strict-null-check
|
||||
return this.showSaveDialogSimplified(schema, options);
|
||||
}
|
||||
|
||||
async showOpenDialog(options: IOpenDialogOptions): Promise<URI[] | undefined> {
|
||||
const schema = this.getFileSystemSchema(options);
|
||||
if (this.shouldUseSimplified(schema).useSimplified) {
|
||||
if (!options.availableFileSystems) {
|
||||
options.availableFileSystems = this.addFileSchemaIfNeeded(schema);
|
||||
}
|
||||
|
||||
const uri = await this.pickRemoteResource(options);
|
||||
|
||||
return uri ? [uri] : undefined;
|
||||
}
|
||||
|
||||
const defaultUri = options.defaultUri;
|
||||
|
||||
const newOptions: OpenDialogOptions = {
|
||||
title: options.title,
|
||||
defaultPath: defaultUri && defaultUri.fsPath,
|
||||
buttonLabel: options.openLabel,
|
||||
filters: options.filters,
|
||||
properties: []
|
||||
};
|
||||
|
||||
newOptions.properties!.push('createDirectory');
|
||||
|
||||
if (options.canSelectFiles) {
|
||||
newOptions.properties!.push('openFile');
|
||||
}
|
||||
|
||||
if (options.canSelectFolders) {
|
||||
newOptions.properties!.push('openDirectory');
|
||||
}
|
||||
|
||||
if (options.canSelectMany) {
|
||||
newOptions.properties!.push('multiSelections');
|
||||
}
|
||||
|
||||
const result = await this.windowService.showOpenDialog(newOptions);
|
||||
|
||||
return result ? result.map(URI.file) : undefined;
|
||||
return this.showOpenDialogSimplified(schema, options);
|
||||
}
|
||||
|
||||
private pickRemoteResource(options: IOpenDialogOptions): Promise<URI | undefined> {
|
||||
const remoteFileDialog = this.instantiationService.createInstance(RemoteFileDialog);
|
||||
|
||||
return remoteFileDialog.showOpenDialog(options);
|
||||
}
|
||||
|
||||
private saveRemoteResource(options: ISaveDialogOptions): Promise<URI | undefined> {
|
||||
const remoteFileDialog = this.instantiationService.createInstance(RemoteFileDialog);
|
||||
|
||||
return remoteFileDialog.showSaveDialog(options);
|
||||
}
|
||||
|
||||
private getSchemeFilterForWindow(): string {
|
||||
return !this.environmentService.configuration.remoteAuthority ? Schemas.file : REMOTE_HOST_SCHEME;
|
||||
}
|
||||
|
||||
private getFileSystemSchema(options: { availableFileSystems?: string[], defaultUri?: URI }): string {
|
||||
return options.availableFileSystems && options.availableFileSystems[0] || this.getSchemeFilterForWindow();
|
||||
}
|
||||
}
|
||||
|
||||
function isUntitledWorkspace(path: URI, environmentService: IWorkbenchEnvironmentService): boolean {
|
||||
return resources.isEqualOrParent(path, environmentService.untitledWorkspacesHome);
|
||||
}
|
||||
|
||||
registerSingleton(IFileDialogService, FileDialogService, true);
|
||||
|
||||
@@ -98,7 +98,7 @@ enum UpdateResult {
|
||||
InvalidPath
|
||||
}
|
||||
|
||||
export class RemoteFileDialog {
|
||||
export class SimpleFileDialog {
|
||||
private options!: IOpenDialogOptions;
|
||||
private currentFolder!: URI;
|
||||
private filePickBox!: IQuickPick<FileQuickPickItem>;
|
||||
@@ -117,7 +117,7 @@ export class RemoteFileDialog {
|
||||
private badPath: string | undefined;
|
||||
private remoteAgentEnvironment: IRemoteAgentEnvironment | null | undefined;
|
||||
private separator: string = '/';
|
||||
private onBusyChangeEmitter = new Emitter<boolean>();
|
||||
private readonly onBusyChangeEmitter = new Emitter<boolean>();
|
||||
private updatingPromise: CancelablePromise<void> | undefined;
|
||||
|
||||
protected disposables: IDisposable[] = [
|
||||
@@ -303,7 +303,7 @@ export class RemoteFileDialog {
|
||||
this.filePickBox.valueSelection = [this.filePickBox.value.length, this.filePickBox.value.length];
|
||||
this.filePickBox.items = [];
|
||||
|
||||
function doResolve(dialog: RemoteFileDialog, uri: URI | undefined) {
|
||||
function doResolve(dialog: SimpleFileDialog, uri: URI | undefined) {
|
||||
if (uri) {
|
||||
uri = resources.removeTrailingPathSeparator(uri);
|
||||
}
|
||||
@@ -335,7 +335,7 @@ export class RemoteFileDialog {
|
||||
}
|
||||
});
|
||||
|
||||
function handleAccept(dialog: RemoteFileDialog) {
|
||||
function handleAccept(dialog: SimpleFileDialog) {
|
||||
if (dialog.busy) {
|
||||
// Save the accept until the file picker is not busy.
|
||||
dialog.onBusyChangeEmitter.event((busy: boolean) => {
|
||||
@@ -8,7 +8,6 @@ import * as os from 'os';
|
||||
import product from 'vs/platform/product/common/product';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { isLinux, isWindows } from 'vs/base/common/platform';
|
||||
import { IWindowService } from 'vs/platform/windows/common/windows';
|
||||
import { mnemonicButtonLabel } from 'vs/base/common/labels';
|
||||
import { IDialogService, IConfirmation, IConfirmationResult, IDialogOptions, IShowResult } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { DialogService as HTMLDialogService } from 'vs/workbench/services/dialogs/browser/dialogService';
|
||||
@@ -23,13 +22,14 @@ 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 { IElectronService } from 'vs/platform/electron/node/electron';
|
||||
import { MessageBoxOptions } from 'electron';
|
||||
|
||||
interface IMassagedMessageBoxOptions {
|
||||
|
||||
/**
|
||||
* OS massaged message box options.
|
||||
*/
|
||||
options: Electron.MessageBoxOptions;
|
||||
options: MessageBoxOptions;
|
||||
|
||||
/**
|
||||
* Since the massaged result of the message box options potentially
|
||||
@@ -50,7 +50,6 @@ export class DialogService implements IDialogService {
|
||||
@ILogService logService: ILogService,
|
||||
@ILayoutService layoutService: ILayoutService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IWindowService windowService: IWindowService,
|
||||
@ISharedProcessService sharedProcessService: ISharedProcessService,
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@IProductService productService: IProductService,
|
||||
@@ -64,7 +63,7 @@ export class DialogService implements IDialogService {
|
||||
}
|
||||
// Electron dialog service
|
||||
else {
|
||||
this.impl = new NativeDialogService(windowService, logService, sharedProcessService, electronService, clipboardService);
|
||||
this.impl = new NativeDialogService(logService, sharedProcessService, electronService, clipboardService);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,7 +85,6 @@ class NativeDialogService implements IDialogService {
|
||||
_serviceBrand: undefined;
|
||||
|
||||
constructor(
|
||||
@IWindowService private readonly windowService: IWindowService,
|
||||
@ILogService private readonly logService: ILogService,
|
||||
@ISharedProcessService sharedProcessService: ISharedProcessService,
|
||||
@IElectronService private readonly electronService: IElectronService,
|
||||
@@ -100,14 +98,14 @@ class NativeDialogService implements IDialogService {
|
||||
|
||||
const { options, buttonIndexMap } = this.massageMessageBoxOptions(this.getConfirmOptions(confirmation));
|
||||
|
||||
const result = await this.windowService.showMessageBox(options);
|
||||
const result = await this.electronService.showMessageBox(options);
|
||||
return {
|
||||
confirmed: buttonIndexMap[result.button] === 0 ? true : false,
|
||||
confirmed: buttonIndexMap[result.response] === 0 ? true : false,
|
||||
checkboxChecked: result.checkboxChecked
|
||||
};
|
||||
}
|
||||
|
||||
private getConfirmOptions(confirmation: IConfirmation): Electron.MessageBoxOptions {
|
||||
private getConfirmOptions(confirmation: IConfirmation): MessageBoxOptions {
|
||||
const buttons: string[] = [];
|
||||
if (confirmation.primaryButton) {
|
||||
buttons.push(confirmation.primaryButton);
|
||||
@@ -121,7 +119,7 @@ class NativeDialogService implements IDialogService {
|
||||
buttons.push(nls.localize('cancelButton', "Cancel"));
|
||||
}
|
||||
|
||||
const opts: Electron.MessageBoxOptions = {
|
||||
const opts: MessageBoxOptions = {
|
||||
title: confirmation.title,
|
||||
message: confirmation.message,
|
||||
buttons,
|
||||
@@ -157,11 +155,11 @@ class NativeDialogService implements IDialogService {
|
||||
checkboxChecked: dialogOptions && dialogOptions.checkbox ? dialogOptions.checkbox.checked : undefined
|
||||
});
|
||||
|
||||
const result = await this.windowService.showMessageBox(options);
|
||||
return { choice: buttonIndexMap[result.button], checkboxChecked: result.checkboxChecked };
|
||||
const result = await this.electronService.showMessageBox(options);
|
||||
return { choice: buttonIndexMap[result.response], checkboxChecked: result.checkboxChecked };
|
||||
}
|
||||
|
||||
private massageMessageBoxOptions(options: Electron.MessageBoxOptions): IMassagedMessageBoxOptions {
|
||||
private massageMessageBoxOptions(options: MessageBoxOptions): IMassagedMessageBoxOptions {
|
||||
let buttonIndexMap = (options.buttons || []).map((button, index) => index);
|
||||
let buttons = (options.buttons || []).map(button => mnemonicButtonLabel(button));
|
||||
let cancelId = options.cancelId;
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IWindowService, OpenDialogOptions, SaveDialogOptions, INativeOpenDialogOptions } from 'vs/platform/windows/common/windows';
|
||||
import { IPickAndOpenOptions, ISaveDialogOptions, IOpenDialogOptions, IFileDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { IHistoryService } from 'vs/workbench/services/history/common/history';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { IElectronService } from 'vs/platform/electron/node/electron';
|
||||
import { AbstractFileDialogService } from 'vs/workbench/services/dialogs/browser/abstractFileDialogService';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
|
||||
export class FileDialogService extends AbstractFileDialogService implements IFileDialogService {
|
||||
|
||||
_serviceBrand: undefined;
|
||||
|
||||
constructor(
|
||||
@IWindowService windowService: IWindowService,
|
||||
@IWorkspaceContextService contextService: IWorkspaceContextService,
|
||||
@IHistoryService historyService: IHistoryService,
|
||||
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
@IFileService fileService: IFileService,
|
||||
@IOpenerService openerService: IOpenerService,
|
||||
@IElectronService private readonly electronService: IElectronService
|
||||
) { super(windowService, contextService, historyService, environmentService, instantiationService, configurationService, fileService, openerService); }
|
||||
|
||||
private toNativeOpenDialogOptions(options: IPickAndOpenOptions): INativeOpenDialogOptions {
|
||||
return {
|
||||
forceNewWindow: options.forceNewWindow,
|
||||
telemetryExtraData: options.telemetryExtraData,
|
||||
defaultPath: options.defaultUri && options.defaultUri.fsPath
|
||||
};
|
||||
}
|
||||
|
||||
private shouldUseSimplified(schema: string): { useSimplified: boolean, isSetting: boolean } {
|
||||
const setting = (this.configurationService.getValue('files.simpleDialog.enable') === true);
|
||||
|
||||
return { useSimplified: (schema !== Schemas.file) || setting, isSetting: (schema === Schemas.file) && setting };
|
||||
}
|
||||
|
||||
async pickFileFolderAndOpen(options: IPickAndOpenOptions): Promise<any> {
|
||||
const schema = this.getFileSystemSchema(options);
|
||||
|
||||
if (!options.defaultUri) {
|
||||
options.defaultUri = this.defaultFilePath(schema);
|
||||
}
|
||||
|
||||
const shouldUseSimplified = this.shouldUseSimplified(schema);
|
||||
if (shouldUseSimplified.useSimplified) {
|
||||
return this.pickFileFolderAndOpenSimplified(schema, options, shouldUseSimplified.isSetting);
|
||||
}
|
||||
return this.electronService.pickFileFolderAndOpen(this.toNativeOpenDialogOptions(options));
|
||||
}
|
||||
|
||||
async pickFileAndOpen(options: IPickAndOpenOptions): Promise<any> {
|
||||
const schema = this.getFileSystemSchema(options);
|
||||
|
||||
if (!options.defaultUri) {
|
||||
options.defaultUri = this.defaultFilePath(schema);
|
||||
}
|
||||
|
||||
const shouldUseSimplified = this.shouldUseSimplified(schema);
|
||||
if (shouldUseSimplified.useSimplified) {
|
||||
return this.pickFileAndOpenSimplified(schema, options, shouldUseSimplified.isSetting);
|
||||
}
|
||||
return this.electronService.pickFileAndOpen(this.toNativeOpenDialogOptions(options));
|
||||
}
|
||||
|
||||
async pickFolderAndOpen(options: IPickAndOpenOptions): Promise<any> {
|
||||
const schema = this.getFileSystemSchema(options);
|
||||
|
||||
if (!options.defaultUri) {
|
||||
options.defaultUri = this.defaultFolderPath(schema);
|
||||
}
|
||||
|
||||
if (this.shouldUseSimplified(schema).useSimplified) {
|
||||
return this.pickFolderAndOpenSimplified(schema, options);
|
||||
}
|
||||
return this.electronService.pickFolderAndOpen(this.toNativeOpenDialogOptions(options));
|
||||
}
|
||||
|
||||
async pickWorkspaceAndOpen(options: IPickAndOpenOptions): Promise<void> {
|
||||
const schema = this.getFileSystemSchema(options);
|
||||
|
||||
if (!options.defaultUri) {
|
||||
options.defaultUri = this.defaultWorkspacePath(schema);
|
||||
}
|
||||
|
||||
if (this.shouldUseSimplified(schema).useSimplified) {
|
||||
return this.pickWorkspaceAndOpenSimplified(schema, options);
|
||||
}
|
||||
return this.electronService.pickWorkspaceAndOpen(this.toNativeOpenDialogOptions(options));
|
||||
}
|
||||
|
||||
async pickFileToSave(options: ISaveDialogOptions): Promise<URI | undefined> {
|
||||
const schema = this.getFileSystemSchema(options);
|
||||
if (this.shouldUseSimplified(schema).useSimplified) {
|
||||
return this.pickFileToSaveSimplified(schema, options);
|
||||
} else {
|
||||
const result = await this.electronService.showSaveDialog(this.toNativeSaveDialogOptions(options));
|
||||
if (result && !result.canceled && result.filePath) {
|
||||
return URI.file(result.filePath);
|
||||
}
|
||||
}
|
||||
return undefined; // {{SQL CARBON EDIT}} strict-null-check
|
||||
}
|
||||
|
||||
private toNativeSaveDialogOptions(options: ISaveDialogOptions): SaveDialogOptions {
|
||||
options.defaultUri = options.defaultUri ? URI.file(options.defaultUri.path) : undefined;
|
||||
return {
|
||||
defaultPath: options.defaultUri && options.defaultUri.fsPath,
|
||||
buttonLabel: options.saveLabel,
|
||||
filters: options.filters,
|
||||
title: options.title
|
||||
};
|
||||
}
|
||||
|
||||
async showSaveDialog(options: ISaveDialogOptions): Promise<URI | undefined> {
|
||||
const schema = this.getFileSystemSchema(options);
|
||||
if (this.shouldUseSimplified(schema).useSimplified) {
|
||||
return this.showSaveDialogSimplified(schema, options);
|
||||
}
|
||||
|
||||
const result = await this.electronService.showSaveDialog(this.toNativeSaveDialogOptions(options));
|
||||
if (result && !result.canceled && result.filePath) {
|
||||
return URI.file(result.filePath);
|
||||
}
|
||||
|
||||
return undefined; // {{SQL CARBON EDIT}} strict-null-check
|
||||
}
|
||||
|
||||
async showOpenDialog(options: IOpenDialogOptions): Promise<URI[] | undefined> {
|
||||
const schema = this.getFileSystemSchema(options);
|
||||
if (this.shouldUseSimplified(schema).useSimplified) {
|
||||
return this.showOpenDialogSimplified(schema, options);
|
||||
}
|
||||
|
||||
const defaultUri = options.defaultUri;
|
||||
|
||||
const newOptions: OpenDialogOptions = {
|
||||
title: options.title,
|
||||
defaultPath: defaultUri && defaultUri.fsPath,
|
||||
buttonLabel: options.openLabel,
|
||||
filters: options.filters,
|
||||
properties: []
|
||||
};
|
||||
|
||||
newOptions.properties!.push('createDirectory');
|
||||
|
||||
if (options.canSelectFiles) {
|
||||
newOptions.properties!.push('openFile');
|
||||
}
|
||||
|
||||
if (options.canSelectFolders) {
|
||||
newOptions.properties!.push('openDirectory');
|
||||
}
|
||||
|
||||
if (options.canSelectMany) {
|
||||
newOptions.properties!.push('multiSelections');
|
||||
}
|
||||
|
||||
const result = await this.electronService.showOpenDialog(newOptions);
|
||||
return result && Array.isArray(result.filePaths) && result.filePaths.length > 0 ? result.filePaths.map(URI.file) : undefined;
|
||||
}
|
||||
}
|
||||
|
||||
registerSingleton(IFileDialogService, FileDialogService, true);
|
||||
@@ -22,7 +22,7 @@ export class CodeEditorService extends CodeEditorServiceImpl {
|
||||
super(themeService);
|
||||
}
|
||||
|
||||
getActiveCodeEditor(): ICodeEditor | null {
|
||||
getActiveCodeEditor(): ICodeEditor | undefined {
|
||||
const activeTextEditorWidget = this.editorService.activeTextEditorWidget;
|
||||
if (isCodeEditor(activeTextEditorWidget)) {
|
||||
return activeTextEditorWidget;
|
||||
@@ -32,10 +32,10 @@ export class CodeEditorService extends CodeEditorServiceImpl {
|
||||
return activeTextEditorWidget.getModifiedEditor();
|
||||
}
|
||||
|
||||
return null;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
openCodeEditor(input: IResourceInput, source: ICodeEditor | null, sideBySide?: boolean): Promise<ICodeEditor | null> {
|
||||
openCodeEditor(input: IResourceInput, source: ICodeEditor | undefined, sideBySide?: boolean): Promise<ICodeEditor | undefined> {
|
||||
|
||||
// Special case: If the active editor is a diff editor and the request to open originates and
|
||||
// targets the modified side of it, we just apply the request there to prevent opening the modified
|
||||
@@ -62,7 +62,7 @@ export class CodeEditorService extends CodeEditorServiceImpl {
|
||||
return this.doOpenCodeEditor(input, source, sideBySide);
|
||||
}
|
||||
|
||||
private async doOpenCodeEditor(input: IResourceInput, source: ICodeEditor | null, sideBySide?: boolean): Promise<ICodeEditor | null> {
|
||||
private async doOpenCodeEditor(input: IResourceInput, _source: ICodeEditor | undefined, sideBySide?: boolean): Promise<ICodeEditor | undefined> {
|
||||
const control = await this.editorService.openEditor(input, sideBySide ? SIDE_GROUP : ACTIVE_GROUP);
|
||||
if (control) {
|
||||
const widget = control.getControl();
|
||||
@@ -71,8 +71,8 @@ export class CodeEditorService extends CodeEditorServiceImpl {
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
registerSingleton(ICodeEditorService, CodeEditorService, true);
|
||||
registerSingleton(ICodeEditorService, CodeEditorService, true);
|
||||
|
||||
@@ -61,8 +61,6 @@ suite.skip('EditorGroupsService', () => { // {{SQL CARBON EDIT}} skip suite
|
||||
|
||||
class TestEditorInputFactory implements IEditorInputFactory {
|
||||
|
||||
constructor() { }
|
||||
|
||||
serialize(editorInput: EditorInput): string {
|
||||
const testEditorInput = <TestEditorInput>editorInput;
|
||||
const testInput: ISerializedTestEditorInput = {
|
||||
|
||||
@@ -26,7 +26,7 @@ export class ExtensionEnablementService extends Disposable implements IExtension
|
||||
|
||||
_serviceBrand: undefined;
|
||||
|
||||
private _onEnablementChanged = new Emitter<IExtension[]>();
|
||||
private readonly _onEnablementChanged = new Emitter<IExtension[]>();
|
||||
public readonly onEnablementChanged: Event<IExtension[]> = this._onEnablementChanged.event;
|
||||
|
||||
private readonly storageManger: StorageManager;
|
||||
@@ -148,8 +148,11 @@ export class ExtensionEnablementService extends Disposable implements IExtension
|
||||
|
||||
private _isDisabledByExtensionKind(extension: IExtension): boolean {
|
||||
if (this.extensionManagementServerService.localExtensionManagementServer && this.extensionManagementServerService.remoteExtensionManagementServer) {
|
||||
const server = isUIExtension(extension.manifest, this.productService, this.configurationService) ? this.extensionManagementServerService.localExtensionManagementServer : this.extensionManagementServerService.remoteExtensionManagementServer;
|
||||
return this.extensionManagementServerService.getExtensionManagementServer(extension.location) !== server;
|
||||
if (!isUIExtension(extension.manifest, this.productService, this.configurationService)) {
|
||||
// workspace extensions must run on the remote, but UI extensions can run on either side
|
||||
const server = this.extensionManagementServerService.remoteExtensionManagementServer;
|
||||
return this.extensionManagementServerService.getExtensionManagementServer(extension.location) !== server;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -520,8 +520,8 @@ suite('ExtensionEnablementService Test', () => {
|
||||
instantiationService.stub(IExtensionManagementServerService, aMultiExtensionManagementServerService(instantiationService));
|
||||
const localWorkspaceExtension = aLocalExtension2('pub.a', { extensionKind: 'ui' }, { location: URI.file(`pub.a`).with({ scheme: Schemas.vscodeRemote }) });
|
||||
testObject = new TestExtensionEnablementService(instantiationService);
|
||||
assert.ok(!testObject.isEnabled(localWorkspaceExtension));
|
||||
assert.deepEqual(testObject.getEnablementState(localWorkspaceExtension), EnablementState.DisabledByExtensionKind);
|
||||
assert.ok(testObject.isEnabled(localWorkspaceExtension));
|
||||
assert.deepEqual(testObject.getEnablementState(localWorkspaceExtension), EnablementState.EnabledGlobally);
|
||||
});
|
||||
|
||||
test('test remote workspace extension is not disabled by kind', async () => {
|
||||
@@ -536,7 +536,7 @@ suite('ExtensionEnablementService Test', () => {
|
||||
instantiationService.stub(IExtensionManagementServerService, aMultiExtensionManagementServerService(instantiationService));
|
||||
const localWorkspaceExtension = aLocalExtension2('pub.a', { extensionKind: 'ui' }, { location: URI.file(`pub.a`).with({ scheme: Schemas.vscodeRemote }) });
|
||||
testObject = new TestExtensionEnablementService(instantiationService);
|
||||
assert.equal(testObject.canChangeEnablement(localWorkspaceExtension), false);
|
||||
assert.equal(testObject.canChangeEnablement(localWorkspaceExtension), true);
|
||||
});
|
||||
|
||||
test('test canChangeEnablement return true for remote workspace extension', () => {
|
||||
|
||||
@@ -16,7 +16,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'
|
||||
import { INotificationHandle, INotificationService, Severity } from 'vs/platform/notification/common/notification';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { IURLHandler, IURLService } from 'vs/platform/url/common/url';
|
||||
import { IWindowService } from 'vs/platform/windows/common/windows';
|
||||
import { IHostService } from 'vs/workbench/services/host/browser/host';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
@@ -65,7 +65,7 @@ class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler {
|
||||
@INotificationService private readonly notificationService: INotificationService,
|
||||
@IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService,
|
||||
@IExtensionEnablementService private readonly extensionEnablementService: IExtensionEnablementService,
|
||||
@IWindowService private readonly windowService: IWindowService,
|
||||
@IHostService private readonly hostService: IHostService,
|
||||
@IExtensionGalleryService private readonly galleryService: IExtensionGalleryService,
|
||||
@IStorageService private readonly storageService: IStorageService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService
|
||||
@@ -277,7 +277,7 @@ class ExtensionUrlHandler implements IExtensionUrlHandler, IURLHandler {
|
||||
|
||||
private async reloadAndHandle(url: URI): Promise<void> {
|
||||
this.storageService.store(URL_TO_HANDLE, JSON.stringify(url.toJSON()), StorageScope.WORKSPACE);
|
||||
await this.windowService.reloadWindow();
|
||||
await this.hostService.reload();
|
||||
}
|
||||
|
||||
// forget about all uris buffered more than 5 minutes ago
|
||||
@@ -24,6 +24,7 @@ import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensio
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { parseExtensionDevOptions } from 'vs/workbench/services/extensions/common/extensionDevOptions';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import { ExtensionActivationReason } from 'vs/workbench/api/common/extHostExtensionActivator';
|
||||
|
||||
const hasOwnProperty = Object.hasOwnProperty;
|
||||
const NO_OP_VOID_PROMISE = Promise.resolve<void>(undefined);
|
||||
@@ -290,17 +291,21 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
|
||||
}
|
||||
|
||||
protected _isEnabled(extension: IExtensionDescription): boolean {
|
||||
return !this._isDisabled(extension);
|
||||
}
|
||||
|
||||
protected _isDisabled(extension: IExtensionDescription): boolean {
|
||||
if (this._isExtensionUnderDevelopment(extension)) {
|
||||
// Never disable extensions under development
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ExtensionIdentifier.equals(extension.identifier, BetterMergeId)) {
|
||||
// Check if this is the better merge extension which was migrated to a built-in extension
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return this._extensionEnablementService.isEnabled(toExtension(extension));
|
||||
return !this._extensionEnablementService.isEnabled(toExtension(extension));
|
||||
}
|
||||
|
||||
protected _doHandleExtensionPoints(affectedExtensions: IExtensionDescription[]): void {
|
||||
@@ -406,9 +411,9 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
|
||||
}
|
||||
}
|
||||
|
||||
public async _activateById(extensionId: ExtensionIdentifier, activationEvent: string): Promise<void> {
|
||||
public async _activateById(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<void> {
|
||||
const results = await Promise.all(
|
||||
this._extensionHostProcessManagers.map(manager => manager.activate(extensionId, activationEvent))
|
||||
this._extensionHostProcessManagers.map(manager => manager.activate(extensionId, reason))
|
||||
);
|
||||
const activated = results.some(e => e);
|
||||
if (!activated) {
|
||||
@@ -420,8 +425,8 @@ export abstract class AbstractExtensionService extends Disposable implements IEx
|
||||
this._extensionHostActiveExtensions.set(ExtensionIdentifier.toKey(extensionId), extensionId);
|
||||
}
|
||||
|
||||
public _onDidActivateExtension(extensionId: ExtensionIdentifier, startup: boolean, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationEvent: string): void {
|
||||
this._extensionHostProcessActivationTimes.set(ExtensionIdentifier.toKey(extensionId), new ActivationTimes(startup, codeLoadingTime, activateCallTime, activateResolvedTime, activationEvent));
|
||||
public _onDidActivateExtension(extensionId: ExtensionIdentifier, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationReason: ExtensionActivationReason): void {
|
||||
this._extensionHostProcessActivationTimes.set(ExtensionIdentifier.toKey(extensionId), new ActivationTimes(codeLoadingTime, activateCallTime, activateResolvedTime, activationReason));
|
||||
this._onDidChangeExtensionsStatus.fire([extensionId]);
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ import { IUntitledResourceInput } from 'vs/workbench/common/editor';
|
||||
import { StopWatch } from 'vs/base/common/stopwatch';
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { IExtensionHostStarter } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { ExtensionActivationReason } from 'vs/workbench/api/common/extHostExtensionActivator';
|
||||
|
||||
// Enable to see detailed message communication between window and extension host
|
||||
const LOG_EXTENSION_HOST_COMMUNICATION = false;
|
||||
@@ -215,12 +216,12 @@ export class ExtensionHostProcessManager extends Disposable {
|
||||
return this._extensionHostProcessRPCProtocol.getProxy(ExtHostContext.ExtHostExtensionService);
|
||||
}
|
||||
|
||||
public async activate(extension: ExtensionIdentifier, activationEvent: string): Promise<boolean> {
|
||||
public async activate(extension: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<boolean> {
|
||||
const proxy = await this._getExtensionHostProcessProxy();
|
||||
if (!proxy) {
|
||||
return false;
|
||||
}
|
||||
return proxy.$activate(extension, activationEvent);
|
||||
return proxy.$activate(extension, reason);
|
||||
}
|
||||
|
||||
public activateByEvent(activationEvent: string): Promise<void> {
|
||||
|
||||
@@ -15,6 +15,10 @@ export interface IExtHostSocketMessage {
|
||||
skipWebSocketFrames: boolean;
|
||||
}
|
||||
|
||||
export interface IExtHostReduceGraceTimeMessage {
|
||||
type: 'VSCODE_EXTHOST_IPC_REDUCE_GRACE_TIME';
|
||||
}
|
||||
|
||||
export const enum MessageType {
|
||||
Initialized,
|
||||
Ready,
|
||||
@@ -44,4 +48,4 @@ export function isMessageOfType(message: VSBuffer, type: MessageType): boolean {
|
||||
case 3: return type === MessageType.Terminate;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import { IExtensionPoint } from 'vs/workbench/services/extensions/common/extensi
|
||||
import { ExtensionIdentifier, IExtension, ExtensionType, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
|
||||
import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc';
|
||||
import { ExtensionActivationReason } from 'vs/workbench/api/common/extHostExtensionActivator';
|
||||
|
||||
export const nullExtensionDescription = Object.freeze(<IExtensionDescription>{
|
||||
identifier: new ExtensionIdentifier('nullExtensionDescription'),
|
||||
@@ -99,11 +100,10 @@ export type ProfileSegmentId = string | 'idle' | 'program' | 'gc' | 'self';
|
||||
|
||||
export class ActivationTimes {
|
||||
constructor(
|
||||
public readonly startup: boolean,
|
||||
public readonly codeLoadingTime: number,
|
||||
public readonly activateCallTime: number,
|
||||
public readonly activateResolvedTime: number,
|
||||
public readonly activationEvent: string
|
||||
public readonly activationReason: ExtensionActivationReason
|
||||
) {
|
||||
}
|
||||
}
|
||||
@@ -226,9 +226,9 @@ export interface IExtensionService {
|
||||
setRemoteEnvironment(env: { [key: string]: string | null }): Promise<void>;
|
||||
|
||||
_logOrShowMessage(severity: Severity, msg: string): void;
|
||||
_activateById(extensionId: ExtensionIdentifier, activationEvent: string): Promise<void>;
|
||||
_activateById(extensionId: ExtensionIdentifier, reason: ExtensionActivationReason): Promise<void>;
|
||||
_onWillActivateExtension(extensionId: ExtensionIdentifier): void;
|
||||
_onDidActivateExtension(extensionId: ExtensionIdentifier, startup: boolean, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationEvent: string): void;
|
||||
_onDidActivateExtension(extensionId: ExtensionIdentifier, codeLoadingTime: number, activateCallTime: number, activateResolvedTime: number, activationReason: ExtensionActivationReason): void;
|
||||
_onExtensionRuntimeError(extensionId: ExtensionIdentifier, err: Error): void;
|
||||
_onExtensionHostExit(code: number): void;
|
||||
}
|
||||
@@ -276,9 +276,9 @@ export class NullExtensionService implements IExtensionService {
|
||||
canAddExtension(): boolean { return false; }
|
||||
canRemoveExtension(): boolean { return false; }
|
||||
_logOrShowMessage(_severity: Severity, _msg: string): void { }
|
||||
_activateById(_extensionId: ExtensionIdentifier, _activationEvent: string): Promise<void> { return Promise.resolve(); }
|
||||
_activateById(_extensionId: ExtensionIdentifier, _reason: ExtensionActivationReason): Promise<void> { return Promise.resolve(); }
|
||||
_onWillActivateExtension(_extensionId: ExtensionIdentifier): void { }
|
||||
_onDidActivateExtension(_extensionId: ExtensionIdentifier, _startup: boolean, _codeLoadingTime: number, _activateCallTime: number, _activateResolvedTime: number, _activationEvent: string): void { }
|
||||
_onDidActivateExtension(_extensionId: ExtensionIdentifier, _codeLoadingTime: number, _activateCallTime: number, _activateResolvedTime: number, _activationReason: ExtensionActivationReason): void { }
|
||||
_onExtensionRuntimeError(_extensionId: ExtensionIdentifier, _err: Error): void { }
|
||||
_onExtensionHostExit(code: number): void { }
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ import { IExtensionEnablementService } from 'vs/workbench/services/extensionMana
|
||||
import { BUILTIN_MANIFEST_CACHE_FILE, MANIFEST_CACHE_FOLDER, USER_MANIFEST_CACHE_FILE, ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import product from 'vs/platform/product/common/product';
|
||||
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
|
||||
import { IWindowService } from 'vs/platform/windows/common/windows';
|
||||
import { IHostService } from 'vs/workbench/services/host/browser/host';
|
||||
import { ExtensionScanner, ExtensionScannerInput, IExtensionReference, IExtensionResolver, IRelaxedExtensionDescription } from 'vs/workbench/services/extensions/node/extensionPoints';
|
||||
import { Translations, ILog } from 'vs/workbench/services/extensions/common/extensionPoints';
|
||||
|
||||
@@ -55,7 +55,7 @@ export class CachedExtensionScanner {
|
||||
@INotificationService private readonly _notificationService: INotificationService,
|
||||
@IEnvironmentService private readonly _environmentService: IEnvironmentService,
|
||||
@IExtensionEnablementService private readonly _extensionEnablementService: IExtensionEnablementService,
|
||||
@IWindowService private readonly _windowService: IWindowService,
|
||||
@IHostService private readonly _hostService: IHostService,
|
||||
) {
|
||||
this.scannedExtensions = new Promise<IExtensionDescription[]>((resolve, reject) => {
|
||||
this._scannedExtensionsResolve = resolve;
|
||||
@@ -78,7 +78,7 @@ export class CachedExtensionScanner {
|
||||
public async startScanningExtensions(log: ILog): Promise<void> {
|
||||
try {
|
||||
const translations = await this.translationConfig;
|
||||
const { system, user, development } = await CachedExtensionScanner._scanInstalledExtensions(this._windowService, this._notificationService, this._environmentService, this._extensionEnablementService, log, translations);
|
||||
const { system, user, development } = await CachedExtensionScanner._scanInstalledExtensions(this._hostService, this._notificationService, this._environmentService, this._extensionEnablementService, log, translations);
|
||||
|
||||
let result = new Map<string, IExtensionDescription>();
|
||||
system.forEach((systemExtension) => {
|
||||
@@ -111,7 +111,7 @@ export class CachedExtensionScanner {
|
||||
}
|
||||
}
|
||||
|
||||
private static async _validateExtensionsCache(windowService: IWindowService, notificationService: INotificationService, environmentService: IEnvironmentService, cacheKey: string, input: ExtensionScannerInput): Promise<void> {
|
||||
private static async _validateExtensionsCache(hostService: IHostService, notificationService: INotificationService, environmentService: IEnvironmentService, cacheKey: string, input: ExtensionScannerInput): Promise<void> {
|
||||
const cacheFolder = path.join(environmentService.userDataPath, MANIFEST_CACHE_FOLDER);
|
||||
const cacheFile = path.join(cacheFolder, cacheKey);
|
||||
|
||||
@@ -141,7 +141,7 @@ export class CachedExtensionScanner {
|
||||
nls.localize('extensionCache.invalid', "Extensions have been modified on disk. Please reload the window."),
|
||||
[{
|
||||
label: nls.localize('reloadWindow', "Reload Window"),
|
||||
run: () => windowService.reloadWindow()
|
||||
run: () => hostService.reload()
|
||||
}]
|
||||
);
|
||||
}
|
||||
@@ -177,7 +177,7 @@ export class CachedExtensionScanner {
|
||||
}
|
||||
}
|
||||
|
||||
private static async _scanExtensionsWithCache(windowService: IWindowService, notificationService: INotificationService, environmentService: IEnvironmentService, cacheKey: string, input: ExtensionScannerInput, log: ILog): Promise<IExtensionDescription[]> {
|
||||
private static async _scanExtensionsWithCache(hostService: IHostService, notificationService: INotificationService, environmentService: IEnvironmentService, cacheKey: string, input: ExtensionScannerInput, log: ILog): Promise<IExtensionDescription[]> {
|
||||
if (input.devMode) {
|
||||
// Do not cache when running out of sources...
|
||||
return ExtensionScanner.scanExtensions(input, log);
|
||||
@@ -195,7 +195,7 @@ export class CachedExtensionScanner {
|
||||
// Validate the cache asynchronously after 5s
|
||||
setTimeout(async () => {
|
||||
try {
|
||||
await this._validateExtensionsCache(windowService, notificationService, environmentService, cacheKey, input);
|
||||
await this._validateExtensionsCache(hostService, notificationService, environmentService, cacheKey, input);
|
||||
} catch (err) {
|
||||
errors.onUnexpectedError(err);
|
||||
}
|
||||
@@ -234,7 +234,7 @@ export class CachedExtensionScanner {
|
||||
}
|
||||
|
||||
private static _scanInstalledExtensions(
|
||||
windowService: IWindowService,
|
||||
hostService: IHostService,
|
||||
notificationService: INotificationService,
|
||||
environmentService: IEnvironmentService,
|
||||
extensionEnablementService: IExtensionEnablementService,
|
||||
@@ -248,7 +248,7 @@ export class CachedExtensionScanner {
|
||||
const locale = platform.language;
|
||||
|
||||
const builtinExtensions = this._scanExtensionsWithCache(
|
||||
windowService,
|
||||
hostService,
|
||||
notificationService,
|
||||
environmentService,
|
||||
BUILTIN_MANIFEST_CACHE_FILE,
|
||||
@@ -282,7 +282,7 @@ export class CachedExtensionScanner {
|
||||
extensionEnablementService.allUserExtensionsDisabled || !environmentService.extensionsPath
|
||||
? Promise.resolve([])
|
||||
: this._scanExtensionsWithCache(
|
||||
windowService,
|
||||
hostService,
|
||||
notificationService,
|
||||
environmentService,
|
||||
USER_MANIFEST_CACHE_FILE,
|
||||
|
||||
@@ -27,7 +27,7 @@ import { ILogService } from 'vs/platform/log/common/log';
|
||||
import product from 'vs/platform/product/common/product';
|
||||
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IWindowService } from 'vs/platform/windows/common/windows';
|
||||
import { IElectronService } from 'vs/platform/electron/node/electron';
|
||||
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
import { IInitData, UIKind } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { MessageType, createMessageOfType, isMessageOfType } from 'vs/workbench/services/extensions/common/extensionHostProtocol';
|
||||
@@ -38,6 +38,7 @@ import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { IExtensionHostDebugService } from 'vs/platform/debug/common/extensionHostDebug';
|
||||
import { IExtensionHostStarter } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { isEqualOrParent } from 'vs/base/common/resources';
|
||||
import { IHostService } from 'vs/workbench/services/host/browser/host';
|
||||
|
||||
export class ExtensionHostProcessWorker implements IExtensionHostStarter {
|
||||
|
||||
@@ -68,13 +69,14 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter {
|
||||
private readonly _extensionHostLogsLocation: URI,
|
||||
@IWorkspaceContextService private readonly _contextService: IWorkspaceContextService,
|
||||
@INotificationService private readonly _notificationService: INotificationService,
|
||||
@IWindowService private readonly _windowService: IWindowService,
|
||||
@IElectronService private readonly _electronService: IElectronService,
|
||||
@ILifecycleService private readonly _lifecycleService: ILifecycleService,
|
||||
@IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService,
|
||||
@ITelemetryService private readonly _telemetryService: ITelemetryService,
|
||||
@ILogService private readonly _logService: ILogService,
|
||||
@ILabelService private readonly _labelService: ILabelService,
|
||||
@IExtensionHostDebugService private readonly _extensionHostDebugService: IExtensionHostDebugService
|
||||
@IExtensionHostDebugService private readonly _extensionHostDebugService: IExtensionHostDebugService,
|
||||
@IHostService private readonly _hostService: IHostService
|
||||
) {
|
||||
const devOpts = parseExtensionDevOptions(this._environmentService);
|
||||
this._isExtensionDevHost = devOpts.isExtensionDevHost;
|
||||
@@ -96,12 +98,12 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter {
|
||||
this._toDispose.add(this._lifecycleService.onShutdown(reason => this.terminate()));
|
||||
this._toDispose.add(this._extensionHostDebugService.onClose(event => {
|
||||
if (this._isExtensionDevHost && this._environmentService.debugExtensionHost.debugId === event.sessionId) {
|
||||
this._windowService.closeWindow();
|
||||
this._electronService.closeWindow();
|
||||
}
|
||||
}));
|
||||
this._toDispose.add(this._extensionHostDebugService.onReload(event => {
|
||||
if (this._isExtensionDevHost && this._environmentService.debugExtensionHost.debugId === event.sessionId) {
|
||||
this._windowService.reloadWindow();
|
||||
this._hostService.reload();
|
||||
}
|
||||
}));
|
||||
|
||||
@@ -234,7 +236,7 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter {
|
||||
this._notificationService.prompt(Severity.Warning, msg,
|
||||
[{
|
||||
label: nls.localize('reloadWindow', "Reload Window"),
|
||||
run: () => this._windowService.reloadWindow()
|
||||
run: () => this._hostService.reload()
|
||||
}],
|
||||
{ sticky: true }
|
||||
);
|
||||
|
||||
@@ -19,13 +19,14 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
|
||||
import { IInitDataProvider, RemoteExtensionHostClient } from 'vs/workbench/services/extensions/common/remoteExtensionHostClient';
|
||||
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
|
||||
import { IRemoteAuthorityResolverService, RemoteAuthorityResolverError, ResolverResult } from 'vs/platform/remote/common/remoteAuthorityResolver';
|
||||
import { isUIExtension } from 'vs/workbench/services/extensions/common/extensionsUtil';
|
||||
import { isUIExtension as isUIExtensionFunc } from 'vs/workbench/services/extensions/common/extensionsUtil';
|
||||
import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows';
|
||||
import { IWindowService } from 'vs/platform/windows/common/windows';
|
||||
import { IHostService } from 'vs/workbench/services/host/browser/host';
|
||||
import { IExtensionService, toExtension } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { ExtensionHostProcessManager } from 'vs/workbench/services/extensions/common/extensionHostProcessManager';
|
||||
import { ExtensionIdentifier, IExtension, ExtensionType, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
@@ -36,6 +37,7 @@ import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import { Logger } from 'vs/workbench/services/extensions/common/extensionPoints';
|
||||
import { flatten } from 'vs/base/common/arrays';
|
||||
import { IStaticExtensionsService } from 'vs/workbench/services/extensions/common/staticExtensions';
|
||||
import { IElectronService } from 'vs/platform/electron/node/electron';
|
||||
|
||||
class DeltaExtensionsQueueItem {
|
||||
constructor(
|
||||
@@ -67,6 +69,8 @@ export class ExtensionService extends AbstractExtensionService implements IExten
|
||||
@ILifecycleService private readonly _lifecycleService: ILifecycleService,
|
||||
@IWindowService protected readonly _windowService: IWindowService,
|
||||
@IStaticExtensionsService private readonly _staticExtensions: IStaticExtensionsService,
|
||||
@IElectronService private readonly _electronService: IElectronService,
|
||||
@IHostService private readonly _hostService: IHostService
|
||||
) {
|
||||
super(
|
||||
instantiationService,
|
||||
@@ -82,7 +86,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten
|
||||
this._notificationService.prompt(Severity.Info, nls.localize('extensionsDisabled', "All installed extensions are temporarily disabled. Reload the window to return to the previous state."), [{
|
||||
label: nls.localize('Reload', "Reload"),
|
||||
run: () => {
|
||||
this._windowService.reloadWindow();
|
||||
this._hostService.reload();
|
||||
}
|
||||
}]);
|
||||
}
|
||||
@@ -326,7 +330,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten
|
||||
|
||||
if (shouldActivate) {
|
||||
await Promise.all(
|
||||
this._extensionHostProcessManagers.map(extHostManager => extHostManager.activate(extensionDescription.identifier, shouldActivateReason!))
|
||||
this._extensionHostProcessManagers.map(extHostManager => extHostManager.activate(extensionDescription.identifier, { startup: false, extensionId: extensionDescription.identifier, activationEvent: shouldActivateReason! }))
|
||||
).then(() => { });
|
||||
}
|
||||
}
|
||||
@@ -384,8 +388,8 @@ export class ExtensionService extends AbstractExtensionService implements IExten
|
||||
label: nls.localize('relaunch', "Relaunch VS Code"),
|
||||
run: () => {
|
||||
this._instantiationService.invokeFunction((accessor) => {
|
||||
const windowsService = accessor.get(IWindowsService);
|
||||
windowsService.relaunch({});
|
||||
const hostService = accessor.get(IHostService);
|
||||
hostService.restart();
|
||||
});
|
||||
}
|
||||
}]
|
||||
@@ -396,7 +400,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten
|
||||
this._notificationService.prompt(Severity.Error, nls.localize('extensionService.crash', "Extension host terminated unexpectedly."),
|
||||
[{
|
||||
label: nls.localize('devTools', "Open Developer Tools"),
|
||||
run: () => this._windowService.openDevTools()
|
||||
run: () => this._electronService.openDevTools()
|
||||
},
|
||||
{
|
||||
label: nls.localize('restart', "Restart Extension Host"),
|
||||
@@ -435,6 +439,8 @@ export class ExtensionService extends AbstractExtensionService implements IExten
|
||||
}
|
||||
|
||||
protected async _scanAndHandleExtensions(): Promise<void> {
|
||||
const isUIExtension = (extension: IExtensionDescription) => isUIExtensionFunc(extension, this._productService, this._configurationService);
|
||||
|
||||
this._extensionScanner.startScanningExtensions(this.createLogger());
|
||||
|
||||
const remoteAuthority = this._environmentService.configuration.remoteAuthority;
|
||||
@@ -446,7 +452,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten
|
||||
this._checkEnableProposedApi(localExtensions);
|
||||
|
||||
// remove disabled extensions
|
||||
localExtensions = localExtensions.filter(extension => this._isEnabled(extension));
|
||||
localExtensions = remove(localExtensions, extension => this._isDisabled(extension));
|
||||
|
||||
if (remoteAuthority) {
|
||||
let resolvedAuthority: ResolverResult;
|
||||
@@ -502,18 +508,16 @@ export class ExtensionService extends AbstractExtensionService implements IExten
|
||||
this._checkEnableProposedApi(remoteEnv.extensions);
|
||||
|
||||
// remove disabled extensions
|
||||
remoteEnv.extensions = remoteEnv.extensions.filter(extension => this._isEnabled(extension));
|
||||
|
||||
// remove UI extensions from the remote extensions
|
||||
remoteEnv.extensions = remoteEnv.extensions.filter(extension => !isUIExtension(extension, this._productService, this._configurationService));
|
||||
remoteEnv.extensions = remove(remoteEnv.extensions, extension => this._isDisabled(extension));
|
||||
|
||||
// remove non-UI extensions from the local extensions
|
||||
localExtensions = localExtensions.filter(extension => extension.isBuiltin || isUIExtension(extension, this._productService, this._configurationService));
|
||||
localExtensions = remove(localExtensions, extension => !extension.isBuiltin && !isUIExtension(extension));
|
||||
|
||||
// in case of overlap, the remote wins
|
||||
const isRemoteExtension = new Set<string>();
|
||||
remoteEnv.extensions.forEach(extension => isRemoteExtension.add(ExtensionIdentifier.toKey(extension.identifier)));
|
||||
localExtensions = localExtensions.filter(extension => !isRemoteExtension.has(ExtensionIdentifier.toKey(extension.identifier)));
|
||||
// in case of UI extensions overlap, the local extension wins
|
||||
remoteEnv.extensions = remove(remoteEnv.extensions, localExtensions.filter(extension => isUIExtension(extension)));
|
||||
|
||||
// in case of other extensions overlap, the remote extension wins
|
||||
localExtensions = remove(localExtensions, remoteEnv.extensions);
|
||||
|
||||
// save for remote extension's init data
|
||||
this._remoteExtensionsEnvironmentData.set(remoteAuthority, remoteEnv);
|
||||
@@ -548,7 +552,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten
|
||||
public _onExtensionHostExit(code: number): void {
|
||||
// Expected development extension termination: When the extension host goes down we also shutdown the window
|
||||
if (!this._isExtensionDevTestFromCli) {
|
||||
this._windowService.closeWindow();
|
||||
this._electronService.closeWindow();
|
||||
}
|
||||
|
||||
// When CLI testing make sure to exit with proper exit code
|
||||
@@ -558,4 +562,23 @@ export class ExtensionService extends AbstractExtensionService implements IExten
|
||||
}
|
||||
}
|
||||
|
||||
function remove(arr: IExtensionDescription[], predicate: (item: IExtensionDescription) => boolean): IExtensionDescription[];
|
||||
function remove(arr: IExtensionDescription[], toRemove: IExtensionDescription[]): IExtensionDescription[];
|
||||
function remove(arr: IExtensionDescription[], arg2: ((item: IExtensionDescription) => boolean) | IExtensionDescription[]): IExtensionDescription[] {
|
||||
if (typeof arg2 === 'function') {
|
||||
return _removePredicate(arr, arg2);
|
||||
}
|
||||
return _removeSet(arr, arg2);
|
||||
}
|
||||
|
||||
function _removePredicate(arr: IExtensionDescription[], predicate: (item: IExtensionDescription) => boolean): IExtensionDescription[] {
|
||||
return arr.filter(extension => !predicate(extension));
|
||||
}
|
||||
|
||||
function _removeSet(arr: IExtensionDescription[], toRemove: IExtensionDescription[]): IExtensionDescription[] {
|
||||
const toRemoveSet = new Set<string>();
|
||||
toRemove.forEach(extension => toRemoveSet.add(ExtensionIdentifier.toKey(extension.identifier)));
|
||||
return arr.filter(extension => !toRemoveSet.has(ExtensionIdentifier.toKey(extension.identifier)));
|
||||
}
|
||||
|
||||
registerSingleton(IExtensionService, ExtensionService);
|
||||
|
||||
@@ -13,7 +13,7 @@ import { PersistentProtocol, ProtocolConstants, BufferedEmitter } from 'vs/base/
|
||||
import { NodeSocket, WebSocketNodeSocket } from 'vs/base/parts/ipc/node/ipc.net';
|
||||
import product from 'vs/platform/product/common/product';
|
||||
import { IInitData } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { MessageType, createMessageOfType, isMessageOfType, IExtHostSocketMessage, IExtHostReadyMessage } from 'vs/workbench/services/extensions/common/extensionHostProtocol';
|
||||
import { MessageType, createMessageOfType, isMessageOfType, IExtHostSocketMessage, IExtHostReadyMessage, IExtHostReduceGraceTimeMessage } from 'vs/workbench/services/extensions/common/extensionHostProtocol';
|
||||
import { ExtensionHostMain, IExitFn } from 'vs/workbench/services/extensions/common/extensionHostMain';
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { IURITransformer, URITransformer, IRawURITransformer } from 'vs/base/common/uriIpc';
|
||||
@@ -21,6 +21,7 @@ import { exists } from 'vs/base/node/pfs';
|
||||
import { realpath } from 'vs/base/node/extpath';
|
||||
import { IHostUtils } from 'vs/workbench/api/common/extHostExtensionService';
|
||||
import 'vs/workbench/api/node/extHost.services';
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
|
||||
interface ParsedExtHostArgs {
|
||||
uriTransformerPath?: string;
|
||||
@@ -91,9 +92,12 @@ function _createExtHostProtocol(): Promise<IMessagePassingProtocol> {
|
||||
reject(new Error('VSCODE_EXTHOST_IPC_SOCKET timeout'));
|
||||
}, 60000);
|
||||
|
||||
let disconnectWaitTimer: NodeJS.Timeout | null = null;
|
||||
const reconnectionGraceTime = ProtocolConstants.ReconnectionGraceTime;
|
||||
const reconnectionShortGraceTime = ProtocolConstants.ReconnectionShortGraceTime;
|
||||
const disconnectRunner1 = new RunOnceScheduler(() => onTerminate(), reconnectionGraceTime);
|
||||
const disconnectRunner2 = new RunOnceScheduler(() => onTerminate(), reconnectionShortGraceTime);
|
||||
|
||||
process.on('message', (msg: IExtHostSocketMessage, handle: net.Socket) => {
|
||||
process.on('message', (msg: IExtHostSocketMessage | IExtHostReduceGraceTimeMessage, handle: net.Socket) => {
|
||||
if (msg && msg.type === 'VSCODE_EXTHOST_IPC_SOCKET') {
|
||||
const initialDataChunk = VSBuffer.wrap(Buffer.from(msg.initialDataChunk, 'base64'));
|
||||
let socket: NodeSocket | WebSocketNodeSocket;
|
||||
@@ -104,10 +108,8 @@ function _createExtHostProtocol(): Promise<IMessagePassingProtocol> {
|
||||
}
|
||||
if (protocol) {
|
||||
// reconnection case
|
||||
if (disconnectWaitTimer) {
|
||||
clearTimeout(disconnectWaitTimer);
|
||||
disconnectWaitTimer = null;
|
||||
}
|
||||
disconnectRunner1.cancel();
|
||||
disconnectRunner2.cancel();
|
||||
protocol.beginAcceptReconnection(socket, initialDataChunk);
|
||||
protocol.endAcceptReconnection();
|
||||
} else {
|
||||
@@ -116,21 +118,21 @@ function _createExtHostProtocol(): Promise<IMessagePassingProtocol> {
|
||||
protocol.onClose(() => onTerminate());
|
||||
resolve(protocol);
|
||||
|
||||
if (msg.skipWebSocketFrames) {
|
||||
// Wait for rich client to reconnect
|
||||
protocol.onSocketClose(() => {
|
||||
// The socket has closed, let's give the renderer a certain amount of time to reconnect
|
||||
disconnectWaitTimer = setTimeout(() => {
|
||||
disconnectWaitTimer = null;
|
||||
onTerminate();
|
||||
}, ProtocolConstants.ReconnectionGraceTime);
|
||||
});
|
||||
} else {
|
||||
// Do not wait for web companion to reconnect
|
||||
protocol.onSocketClose(() => {
|
||||
onTerminate();
|
||||
});
|
||||
}
|
||||
// Wait for rich client to reconnect
|
||||
protocol.onSocketClose(() => {
|
||||
// The socket has closed, let's give the renderer a certain amount of time to reconnect
|
||||
disconnectRunner1.schedule();
|
||||
});
|
||||
}
|
||||
}
|
||||
if (msg && msg.type === 'VSCODE_EXTHOST_IPC_REDUCE_GRACE_TIME') {
|
||||
if (disconnectRunner2.isScheduled()) {
|
||||
// we are disconnected and already running the short reconnection timer
|
||||
return;
|
||||
}
|
||||
if (disconnectRunner1.isScheduled()) {
|
||||
// we are disconnected and running the long reconnection timer
|
||||
disconnectRunner2.schedule();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -5,16 +5,75 @@
|
||||
|
||||
import { IHostService } from 'vs/workbench/services/host/browser/host';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
|
||||
export class BrowserHostService implements IHostService {
|
||||
|
||||
_serviceBrand: undefined;
|
||||
|
||||
constructor(@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService) { }
|
||||
|
||||
//#region Window
|
||||
|
||||
readonly windowCount = Promise.resolve(1);
|
||||
|
||||
async openEmptyWindow(options?: { reuse?: boolean, remoteAuthority?: string }): Promise<void> {
|
||||
// TODO@Ben delegate to embedder
|
||||
const targetHref = `${document.location.origin}${document.location.pathname}?ew=true`;
|
||||
if (options && options.reuse) {
|
||||
window.location.href = targetHref;
|
||||
} else {
|
||||
window.open(targetHref);
|
||||
}
|
||||
}
|
||||
|
||||
async toggleFullScreen(): Promise<void> {
|
||||
const target = this.layoutService.getWorkbenchElement();
|
||||
|
||||
// Chromium
|
||||
if (document.fullscreen !== undefined) {
|
||||
if (!document.fullscreen) {
|
||||
try {
|
||||
return await target.requestFullscreen();
|
||||
} catch (error) {
|
||||
console.warn('Toggle Full Screen failed'); // https://developer.mozilla.org/en-US/docs/Web/API/Element/requestFullscreen
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
return await document.exitFullscreen();
|
||||
} catch (error) {
|
||||
console.warn('Exit Full Screen failed');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Safari and Edge 14 are all using webkit prefix
|
||||
if ((<any>document).webkitIsFullScreen !== undefined) {
|
||||
try {
|
||||
if (!(<any>document).webkitIsFullScreen) {
|
||||
(<any>target).webkitRequestFullscreen(); // it's async, but doesn't return a real promise.
|
||||
} else {
|
||||
(<any>document).webkitExitFullscreen(); // it's async, but doesn't return a real promise.
|
||||
}
|
||||
} catch {
|
||||
console.warn('Enter/Exit Full Screen failed');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
async restart(): Promise<void> {
|
||||
this.reload();
|
||||
}
|
||||
|
||||
async reload(): Promise<void> {
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
async closeWorkspace(): Promise<void> {
|
||||
return this.openEmptyWindow({ reuse: true });
|
||||
}
|
||||
}
|
||||
|
||||
registerSingleton(IHostService, BrowserHostService, true);
|
||||
|
||||
@@ -18,5 +18,36 @@ export interface IHostService {
|
||||
*/
|
||||
readonly windowCount: Promise<number>;
|
||||
|
||||
/**
|
||||
* Opens an empty window. The optional parameter allows to define if
|
||||
* a new window should open or the existing one change to an empty.
|
||||
*/
|
||||
openEmptyWindow(options?: { reuse?: boolean, remoteAuthority?: string }): Promise<void>;
|
||||
|
||||
/**
|
||||
* Switch between fullscreen and normal window.
|
||||
*/
|
||||
toggleFullScreen(): Promise<void>;
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Lifecycle
|
||||
|
||||
/**
|
||||
* Restart the entire application.
|
||||
*/
|
||||
restart(): Promise<void>;
|
||||
|
||||
/**
|
||||
* Reload the currently active window.
|
||||
*/
|
||||
reload(): Promise<void>;
|
||||
|
||||
/**
|
||||
* Closes the currently opened folder/workspace and returns to an empty
|
||||
* window.
|
||||
*/
|
||||
closeWorkspace(): Promise<void>;
|
||||
|
||||
//#endregion
|
||||
}
|
||||
|
||||
@@ -17,7 +17,27 @@ export class DesktopHostService implements IHostService {
|
||||
|
||||
get windowCount() { return this.electronService.windowCount(); }
|
||||
|
||||
openEmptyWindow(options?: { reuse?: boolean, remoteAuthority?: string }): Promise<void> {
|
||||
return this.electronService.openEmptyWindow(options);
|
||||
}
|
||||
|
||||
toggleFullScreen(): Promise<void> {
|
||||
return this.electronService.toggleFullScreen();
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
restart(): Promise<void> {
|
||||
return this.electronService.relaunch();
|
||||
}
|
||||
|
||||
reload(): Promise<void> {
|
||||
return this.electronService.reload();
|
||||
}
|
||||
|
||||
closeWorkspace(): Promise<void> {
|
||||
return this.electronService.closeWorkpsace();
|
||||
}
|
||||
}
|
||||
|
||||
registerSingleton(IHostService, DesktopHostService, true);
|
||||
|
||||
@@ -603,7 +603,7 @@ let schema: IJSONSchema = {
|
||||
id: schemaId,
|
||||
type: 'array',
|
||||
title: nls.localize('keybindings.json.title', "Keybindings configuration"),
|
||||
allowsTrailingCommas: true,
|
||||
allowTrailingCommas: true,
|
||||
allowComments: true,
|
||||
definitions: {
|
||||
'editorGroupsSchema': {
|
||||
|
||||
@@ -27,11 +27,6 @@ export const enum Position {
|
||||
BOTTOM
|
||||
}
|
||||
|
||||
export interface ILayoutOptions {
|
||||
toggleMaximizedPanel?: boolean;
|
||||
source?: Parts;
|
||||
}
|
||||
|
||||
export interface IWorkbenchLayoutService extends ILayoutService {
|
||||
|
||||
_serviceBrand: undefined;
|
||||
|
||||
@@ -44,8 +44,6 @@ class TestProgressBar {
|
||||
fInfinite: boolean = false;
|
||||
fDone: boolean = false;
|
||||
|
||||
constructor() { }
|
||||
|
||||
infinite() {
|
||||
this.fDone = null!;
|
||||
this.fInfinite = true;
|
||||
|
||||
@@ -88,12 +88,24 @@ class NodeRemoteTunnel extends Disposable implements RemoteTunnel {
|
||||
export class TunnelService implements ITunnelService {
|
||||
_serviceBrand: undefined;
|
||||
|
||||
private readonly _tunnels = new Map</* port */ number, { refcount: number, readonly value: Promise<RemoteTunnel> }>();
|
||||
|
||||
public constructor(
|
||||
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
|
||||
@IRemoteAuthorityResolverService private readonly remoteAuthorityResolverService: IRemoteAuthorityResolverService,
|
||||
@ISignService private readonly signService: ISignService,
|
||||
@ILogService private readonly logService: ILogService
|
||||
) {
|
||||
@ILogService private readonly logService: ILogService,
|
||||
) { }
|
||||
|
||||
public get tunnels(): Promise<readonly RemoteTunnel[]> {
|
||||
return Promise.all(Array.from(this._tunnels.values()).map(x => x.value));
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
for (const { value } of this._tunnels.values()) {
|
||||
value.then(tunnel => tunnel.dispose());
|
||||
}
|
||||
this._tunnels.clear();
|
||||
}
|
||||
|
||||
openTunnel(remotePort: number): Promise<RemoteTunnel> | undefined {
|
||||
@@ -102,6 +114,33 @@ export class TunnelService implements ITunnelService {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const resolvedTunnel = this.retainOrCreateTunnel(remoteAuthority, remotePort);
|
||||
if (!resolvedTunnel) {
|
||||
return resolvedTunnel;
|
||||
}
|
||||
|
||||
return resolvedTunnel.then(tunnel => ({
|
||||
tunnelRemotePort: tunnel.tunnelRemotePort,
|
||||
tunnelLocalPort: tunnel.tunnelLocalPort,
|
||||
dispose: () => {
|
||||
const existing = this._tunnels.get(remotePort);
|
||||
if (existing) {
|
||||
if (--existing.refcount <= 0) {
|
||||
existing.value.then(tunnel => tunnel.dispose());
|
||||
this._tunnels.delete(remotePort);
|
||||
}
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private retainOrCreateTunnel(remoteAuthority: string, remotePort: number): Promise<RemoteTunnel> | undefined {
|
||||
const existing = this._tunnels.get(remotePort);
|
||||
if (existing) {
|
||||
++existing.refcount;
|
||||
return existing.value;
|
||||
}
|
||||
|
||||
const options: IConnectionOptions = {
|
||||
commit: product.commit,
|
||||
socketFactory: nodeSocketFactory,
|
||||
@@ -114,7 +153,10 @@ export class TunnelService implements ITunnelService {
|
||||
signService: this.signService,
|
||||
logService: this.logService
|
||||
};
|
||||
return createRemoteTunnel(options, remotePort);
|
||||
|
||||
const tunnel = createRemoteTunnel(options, remotePort);
|
||||
this._tunnels.set(remotePort, { refcount: 1, value: tunnel });
|
||||
return tunnel;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,9 +9,11 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { RequestChannelClient } from 'vs/platform/request/common/requestIpc';
|
||||
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
|
||||
import { RequestService as BrowserRequestService } from 'vs/platform/request/browser/requestService';
|
||||
import { RequestService } from 'vs/platform/request/browser/requestService';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { IRequestService } from 'vs/platform/request/common/request';
|
||||
|
||||
export class RequestService extends BrowserRequestService {
|
||||
export class BrowserRequestService extends RequestService {
|
||||
|
||||
private readonly remoteRequestChannel: RequestChannelClient | null;
|
||||
|
||||
@@ -40,5 +42,6 @@ export class RequestService extends BrowserRequestService {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
registerSingleton(IRequestService, BrowserRequestService, true);
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { RequestService } from 'vs/platform/request/browser/requestService';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { IRequestService } from 'vs/platform/request/common/request';
|
||||
import { IElectronService } from 'vs/platform/electron/node/electron';
|
||||
|
||||
export class NativeRequestService extends RequestService {
|
||||
|
||||
constructor(
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
@ILogService logService: ILogService,
|
||||
@IElectronService private electronService: IElectronService
|
||||
) {
|
||||
super(configurationService, logService);
|
||||
}
|
||||
|
||||
async resolveProxy(url: string): Promise<string | undefined> {
|
||||
return this.electronService.resolveProxy(url);
|
||||
}
|
||||
}
|
||||
|
||||
registerSingleton(IRequestService, NativeRequestService, true);
|
||||
@@ -170,8 +170,6 @@ export abstract class AbstractTextFileService extends Disposable implements ITex
|
||||
}
|
||||
|
||||
private async backupBeforeShutdown(dirtyToBackup: URI[], reason: ShutdownReason): Promise<boolean> {
|
||||
const windowCount = await this.hostService.windowCount;
|
||||
|
||||
// When quit is requested skip the confirm callback and attempt to backup all workspaces.
|
||||
// When quit is not requested the confirm callback should be shown when the window being
|
||||
// closed is the only VS Code window open, except for on Mac where hot exit is only
|
||||
@@ -182,7 +180,7 @@ export abstract class AbstractTextFileService extends Disposable implements ITex
|
||||
case ShutdownReason.CLOSE:
|
||||
if (this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY && this.configuredHotExit === HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE) {
|
||||
doBackup = true; // backup if a folder is open and onExitAndWindowClose is configured
|
||||
} else if (windowCount > 1 || platform.isMacintosh) {
|
||||
} else if (await this.hostService.windowCount > 1 || platform.isMacintosh) {
|
||||
doBackup = false; // do not backup if a window is closed that does not cause quitting of the application
|
||||
} else {
|
||||
doBackup = true; // backup if last window is closed on win/linux where the application quits right after
|
||||
|
||||
@@ -192,7 +192,7 @@ export const colorThemeSchemaId = 'vscode://schemas/color-theme';
|
||||
const colorThemeSchema: IJSONSchema = {
|
||||
type: 'object',
|
||||
allowComments: true,
|
||||
allowsTrailingCommas: true,
|
||||
allowTrailingCommas: true,
|
||||
properties: {
|
||||
colors: {
|
||||
description: nls.localize('schema.workbenchColors', 'Colors in the workbench'),
|
||||
|
||||
@@ -12,7 +12,7 @@ const schemaId = 'vscode://schemas/icon-theme';
|
||||
const schema: IJSONSchema = {
|
||||
type: 'object',
|
||||
allowComments: true,
|
||||
allowsTrailingCommas: true,
|
||||
allowTrailingCommas: true,
|
||||
definitions: {
|
||||
folderExpanded: {
|
||||
type: 'string',
|
||||
|
||||
@@ -7,7 +7,7 @@ import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { IUpdateService, State, UpdateType } from 'vs/platform/update/common/update';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
import { IWindowService } from 'vs/platform/windows/common/windows';
|
||||
import { IHostService } from 'vs/workbench/services/host/browser/host';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
export interface IUpdate {
|
||||
@@ -40,7 +40,7 @@ export class UpdateService extends Disposable implements IUpdateService {
|
||||
|
||||
constructor(
|
||||
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
|
||||
@IWindowService private readonly windowService: IWindowService
|
||||
@IHostService private readonly hostService: IHostService
|
||||
) {
|
||||
super();
|
||||
|
||||
@@ -84,11 +84,11 @@ export class UpdateService extends Disposable implements IUpdateService {
|
||||
}
|
||||
|
||||
async applyUpdate(): Promise<void> {
|
||||
this.windowService.reloadWindow();
|
||||
this.hostService.reload();
|
||||
}
|
||||
|
||||
async quitAndInstall(): Promise<void> {
|
||||
this.windowService.reloadWindow();
|
||||
this.hostService.reload();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@ import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { ISettingsMergeService } from 'vs/platform/userDataSync/common/userDataSync';
|
||||
import { values } from 'vs/base/common/map';
|
||||
import { IStringDictionary } from 'vs/base/common/collections';
|
||||
|
||||
class SettingsMergeService implements ISettingsMergeService {
|
||||
|
||||
@@ -22,27 +24,27 @@ class SettingsMergeService implements ISettingsMergeService {
|
||||
|
||||
constructor(
|
||||
@IModelService private readonly modelService: IModelService,
|
||||
@IModeService private readonly modeService: IModeService
|
||||
@IModeService private readonly modeService: IModeService,
|
||||
) { }
|
||||
|
||||
async merge(localContent: string, remoteContent: string, baseContent: string | null): Promise<{ mergeContent: string, hasChanges: boolean, hasConflicts: boolean }> {
|
||||
async merge(localContent: string, remoteContent: string, baseContent: string | null, ignoredSettings: IStringDictionary<boolean>): Promise<{ mergeContent: string, hasChanges: boolean, hasConflicts: boolean }> {
|
||||
const local = parse(localContent);
|
||||
const remote = parse(remoteContent);
|
||||
const base = baseContent ? parse(baseContent) : null;
|
||||
|
||||
const localToRemote = this.compare(local, remote);
|
||||
const localToRemote = this.compare(local, remote, ignoredSettings);
|
||||
if (localToRemote.added.size === 0 && localToRemote.removed.size === 0 && localToRemote.updated.size === 0) {
|
||||
// No changes found between local and remote.
|
||||
return { mergeContent: localContent, hasChanges: false, hasConflicts: false };
|
||||
}
|
||||
|
||||
const conflicts: Set<string> = new Set<string>();
|
||||
const baseToLocal = base ? this.compare(base, local) : { added: Object.keys(local).reduce((r, k) => { r.add(k); return r; }, new Set<string>()), removed: new Set<string>(), updated: new Set<string>() };
|
||||
const baseToRemote = base ? this.compare(base, remote) : { added: Object.keys(remote).reduce((r, k) => { r.add(k); return r; }, new Set<string>()), removed: new Set<string>(), updated: new Set<string>() };
|
||||
const baseToLocal = base ? this.compare(base, local, ignoredSettings) : { added: Object.keys(local).reduce((r, k) => { r.add(k); return r; }, new Set<string>()), removed: new Set<string>(), updated: new Set<string>() };
|
||||
const baseToRemote = base ? this.compare(base, remote, ignoredSettings) : { added: Object.keys(remote).reduce((r, k) => { r.add(k); return r; }, new Set<string>()), removed: new Set<string>(), updated: new Set<string>() };
|
||||
const settingsPreviewModel = this.modelService.createModel(localContent, this.modeService.create('jsonc'));
|
||||
|
||||
// Removed settings in Local
|
||||
for (const key of baseToLocal.removed.keys()) {
|
||||
for (const key of values(baseToLocal.removed)) {
|
||||
// Got updated in remote
|
||||
if (baseToRemote.updated.has(key)) {
|
||||
conflicts.add(key);
|
||||
@@ -50,7 +52,7 @@ class SettingsMergeService implements ISettingsMergeService {
|
||||
}
|
||||
|
||||
// Removed settings in Remote
|
||||
for (const key of baseToRemote.removed.keys()) {
|
||||
for (const key of values(baseToRemote.removed)) {
|
||||
if (conflicts.has(key)) {
|
||||
continue;
|
||||
}
|
||||
@@ -63,7 +65,7 @@ class SettingsMergeService implements ISettingsMergeService {
|
||||
}
|
||||
|
||||
// Added settings in Local
|
||||
for (const key of baseToLocal.added.keys()) {
|
||||
for (const key of values(baseToLocal.added)) {
|
||||
if (conflicts.has(key)) {
|
||||
continue;
|
||||
}
|
||||
@@ -77,7 +79,7 @@ class SettingsMergeService implements ISettingsMergeService {
|
||||
}
|
||||
|
||||
// Added settings in remote
|
||||
for (const key of baseToRemote.added.keys()) {
|
||||
for (const key of values(baseToRemote.added)) {
|
||||
if (conflicts.has(key)) {
|
||||
continue;
|
||||
}
|
||||
@@ -93,7 +95,7 @@ class SettingsMergeService implements ISettingsMergeService {
|
||||
}
|
||||
|
||||
// Updated settings in Local
|
||||
for (const key of baseToLocal.updated.keys()) {
|
||||
for (const key of values(baseToLocal.updated)) {
|
||||
if (conflicts.has(key)) {
|
||||
continue;
|
||||
}
|
||||
@@ -107,7 +109,7 @@ class SettingsMergeService implements ISettingsMergeService {
|
||||
}
|
||||
|
||||
// Updated settings in Remote
|
||||
for (const key of baseToRemote.updated.keys()) {
|
||||
for (const key of values(baseToRemote.updated)) {
|
||||
if (conflicts.has(key)) {
|
||||
continue;
|
||||
}
|
||||
@@ -122,7 +124,7 @@ class SettingsMergeService implements ISettingsMergeService {
|
||||
}
|
||||
}
|
||||
|
||||
for (const key of conflicts.keys()) {
|
||||
for (const key of values(conflicts)) {
|
||||
const tree = parseTree(settingsPreviewModel.getValue());
|
||||
const valueNode = findNodeAtLocation(tree, [key]);
|
||||
const eol = settingsPreviewModel.getEOL();
|
||||
@@ -149,6 +151,18 @@ class SettingsMergeService implements ISettingsMergeService {
|
||||
return { mergeContent: settingsPreviewModel.getValue(), hasChanges: true, hasConflicts: conflicts.size > 0 };
|
||||
}
|
||||
|
||||
async computeRemoteContent(localContent: string, remoteContent: string, ignoredSettings: IStringDictionary<boolean>): Promise<string> {
|
||||
const remote = parse(remoteContent);
|
||||
const remoteModel = this.modelService.createModel(localContent, this.modeService.create('jsonc'));
|
||||
for (const key of Object.keys(ignoredSettings)) {
|
||||
if (ignoredSettings[key]) {
|
||||
this.editSetting(remoteModel, key, undefined);
|
||||
this.editSetting(remoteModel, key, remote[key]);
|
||||
}
|
||||
}
|
||||
return remoteModel.getValue();
|
||||
}
|
||||
|
||||
private editSetting(model: ITextModel, key: string, value: any | undefined): void {
|
||||
const insertSpaces = false;
|
||||
const tabSize = 4;
|
||||
@@ -166,9 +180,9 @@ class SettingsMergeService implements ISettingsMergeService {
|
||||
}
|
||||
}
|
||||
|
||||
private compare(from: { [key: string]: any }, to: { [key: string]: any }): { added: Set<string>, removed: Set<string>, updated: Set<string> } {
|
||||
const fromKeys = Object.keys(from);
|
||||
const toKeys = Object.keys(to);
|
||||
private compare(from: IStringDictionary<any>, to: IStringDictionary<any>, ignored: IStringDictionary<boolean>): { added: Set<string>, removed: Set<string>, updated: Set<string> } {
|
||||
const fromKeys = Object.keys(from).filter(key => !ignored[key]);
|
||||
const toKeys = Object.keys(to).filter(key => !ignored[key]);
|
||||
const added = toKeys.filter(key => fromKeys.indexOf(key) === -1).reduce((r, key) => { r.add(key); return r; }, new Set<string>());
|
||||
const removed = fromKeys.filter(key => toKeys.indexOf(key) === -1).reduce((r, key) => { r.add(key); return r; }, new Set<string>());
|
||||
const updated: Set<string> = new Set<string>();
|
||||
|
||||
@@ -3,12 +3,13 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { SyncStatus, SyncSource, IUserDataSyncService } from 'vs/platform/userDataSync/common/userDataSync';
|
||||
import { SyncStatus, SyncSource, IUserDataSyncService, ISyncExtension } from 'vs/platform/userDataSync/common/userDataSync';
|
||||
import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { IChannel } from 'vs/base/parts/ipc/common/ipc';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { IExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
||||
|
||||
export class UserDataSyncService extends Disposable implements IUserDataSyncService {
|
||||
|
||||
@@ -31,13 +32,24 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
|
||||
) {
|
||||
super();
|
||||
this.channel = sharedProcessService.getChannel('userDataSync');
|
||||
this._register(this.channel.listen<SyncStatus>('onDidChangeStatus')(status => this.updateStatus(status)));
|
||||
this.channel.call<SyncStatus>('_getInitialStatus').then(status => {
|
||||
this.updateStatus(status);
|
||||
this._register(this.channel.listen<SyncStatus>('onDidChangeStatus')(status => this.updateStatus(status)));
|
||||
});
|
||||
}
|
||||
|
||||
sync(_continue?: boolean): Promise<boolean> {
|
||||
return this.channel.call('sync', [_continue]);
|
||||
}
|
||||
|
||||
getRemoteExtensions(): Promise<ISyncExtension[]> {
|
||||
return this.channel.call('getRemoteExtensions');
|
||||
}
|
||||
|
||||
removeExtension(identifier: IExtensionIdentifier): Promise<void> {
|
||||
return this.channel.call('removeExtension', [identifier]);
|
||||
}
|
||||
|
||||
private async updateStatus(status: SyncStatus): Promise<void> {
|
||||
this._conflictsSource = await this.channel.call<SyncSource>('getConflictsSource');
|
||||
this._status = status;
|
||||
|
||||
@@ -4,10 +4,8 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { IWindowService, IWindowsService, INativeOpenDialogOptions, IEnterWorkspaceResult, IMessageBoxResult, IDevToolsOptions, IOpenSettings, IURIToOpen, isFolderToOpen, isWorkspaceToOpen } from 'vs/platform/windows/common/windows';
|
||||
import { IWindowService, IWindowsService, IOpenSettings, IURIToOpen, isFolderToOpen, isWorkspaceToOpen } from 'vs/platform/windows/common/windows';
|
||||
import { IRecentlyOpened, IRecent } from 'vs/platform/history/common/history';
|
||||
import { ISerializableCommandAction } from 'vs/platform/actions/common/actions';
|
||||
import { ParsedArgs } from 'vs/platform/environment/common/environment';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
@@ -53,50 +51,6 @@ export class WindowService extends Disposable implements IWindowService {
|
||||
return this._windowId;
|
||||
}
|
||||
|
||||
pickFileFolderAndOpen(options: INativeOpenDialogOptions): Promise<void> {
|
||||
options.windowId = this.windowId;
|
||||
|
||||
return this.windowsService.pickFileFolderAndOpen(options);
|
||||
}
|
||||
|
||||
pickFileAndOpen(options: INativeOpenDialogOptions): Promise<void> {
|
||||
options.windowId = this.windowId;
|
||||
|
||||
return this.windowsService.pickFileAndOpen(options);
|
||||
}
|
||||
|
||||
pickFolderAndOpen(options: INativeOpenDialogOptions): Promise<void> {
|
||||
options.windowId = this.windowId;
|
||||
|
||||
return this.windowsService.pickFolderAndOpen(options);
|
||||
}
|
||||
|
||||
pickWorkspaceAndOpen(options: INativeOpenDialogOptions): Promise<void> {
|
||||
options.windowId = this.windowId;
|
||||
|
||||
return this.windowsService.pickWorkspaceAndOpen(options);
|
||||
}
|
||||
|
||||
reloadWindow(args?: ParsedArgs): Promise<void> {
|
||||
return this.windowsService.reloadWindow(this.windowId, args);
|
||||
}
|
||||
|
||||
openDevTools(options?: IDevToolsOptions): Promise<void> {
|
||||
return this.windowsService.openDevTools(this.windowId, options);
|
||||
}
|
||||
|
||||
toggleDevTools(): Promise<void> {
|
||||
return this.windowsService.toggleDevTools(this.windowId);
|
||||
}
|
||||
|
||||
closeWorkspace(): Promise<void> {
|
||||
return this.windowsService.closeWorkspace(this.windowId);
|
||||
}
|
||||
|
||||
enterWorkspace(path: URI): Promise<IEnterWorkspaceResult | undefined> {
|
||||
return this.windowsService.enterWorkspace(this.windowId, path);
|
||||
}
|
||||
|
||||
openWindow(uris: IURIToOpen[], options: IOpenSettings = {}): Promise<void> {
|
||||
if (!!this.remoteAuthority) {
|
||||
uris.forEach(u => u.label = u.label || this.getRecentLabel(u));
|
||||
@@ -105,18 +59,6 @@ export class WindowService extends Disposable implements IWindowService {
|
||||
return this.windowsService.openWindow(this.windowId, uris, options);
|
||||
}
|
||||
|
||||
closeWindow(): Promise<void> {
|
||||
return this.windowsService.closeWindow(this.windowId);
|
||||
}
|
||||
|
||||
toggleFullScreen(target?: HTMLElement): Promise<void> {
|
||||
return this.windowsService.toggleFullScreen(this.windowId);
|
||||
}
|
||||
|
||||
setRepresentedFilename(fileName: string): Promise<void> {
|
||||
return this.windowsService.setRepresentedFilename(this.windowId, fileName);
|
||||
}
|
||||
|
||||
getRecentlyOpened(): Promise<IRecentlyOpened> {
|
||||
return this.windowsService.getRecentlyOpened(this.windowId);
|
||||
}
|
||||
@@ -137,50 +79,6 @@ export class WindowService extends Disposable implements IWindowService {
|
||||
return this.windowsService.isFocused(this.windowId);
|
||||
}
|
||||
|
||||
isMaximized(): Promise<boolean> {
|
||||
return this.windowsService.isMaximized(this.windowId);
|
||||
}
|
||||
|
||||
maximizeWindow(): Promise<void> {
|
||||
return this.windowsService.maximizeWindow(this.windowId);
|
||||
}
|
||||
|
||||
unmaximizeWindow(): Promise<void> {
|
||||
return this.windowsService.unmaximizeWindow(this.windowId);
|
||||
}
|
||||
|
||||
minimizeWindow(): Promise<void> {
|
||||
return this.windowsService.minimizeWindow(this.windowId);
|
||||
}
|
||||
|
||||
onWindowTitleDoubleClick(): Promise<void> {
|
||||
return this.windowsService.onWindowTitleDoubleClick(this.windowId);
|
||||
}
|
||||
|
||||
setDocumentEdited(flag: boolean): Promise<void> {
|
||||
return this.windowsService.setDocumentEdited(this.windowId, flag);
|
||||
}
|
||||
|
||||
showMessageBox(options: Electron.MessageBoxOptions): Promise<IMessageBoxResult> {
|
||||
return this.windowsService.showMessageBox(this.windowId, options);
|
||||
}
|
||||
|
||||
showSaveDialog(options: Electron.SaveDialogOptions): Promise<string> {
|
||||
return this.windowsService.showSaveDialog(this.windowId, options);
|
||||
}
|
||||
|
||||
showOpenDialog(options: Electron.OpenDialogOptions): Promise<string[]> {
|
||||
return this.windowsService.showOpenDialog(this.windowId, options);
|
||||
}
|
||||
|
||||
updateTouchBar(items: ISerializableCommandAction[][]): Promise<void> {
|
||||
return this.windowsService.updateTouchBar(this.windowId, items);
|
||||
}
|
||||
|
||||
resolveProxy(url: string): Promise<string | undefined> {
|
||||
return this.windowsService.resolveProxy(this.windowId, url);
|
||||
}
|
||||
|
||||
private getRecentLabel(u: IURIToOpen): string {
|
||||
if (isFolderToOpen(u)) {
|
||||
return this.labelService.getWorkspaceLabel(u.folderUri, { verbose: true });
|
||||
@@ -192,4 +90,4 @@ export class WindowService extends Disposable implements IWindowService {
|
||||
}
|
||||
}
|
||||
|
||||
registerSingleton(IWindowService, WindowService);
|
||||
registerSingleton(IWindowService, WindowService);
|
||||
|
||||
@@ -7,7 +7,7 @@ import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import * as nls from 'vs/nls';
|
||||
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
import { IWindowService, MessageBoxOptions, IWindowsService } from 'vs/platform/windows/common/windows';
|
||||
import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows';
|
||||
import { IJSONEditingService, JSONEditingError, JSONEditingErrorCode } from 'vs/workbench/services/configuration/common/jsonEditing';
|
||||
import { IWorkspaceIdentifier, IWorkspaceFolderCreationData, IWorkspacesService, rewriteWorkspaceFileForNewLocation, WORKSPACE_FILTER } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { WorkspaceService } from 'vs/workbench/services/configuration/browser/configurationService';
|
||||
@@ -50,7 +50,7 @@ export class WorkspaceEditingService implements IWorkspaceEditingService {
|
||||
@IFileService private readonly fileService: IFileService,
|
||||
@ITextFileService private readonly textFileService: ITextFileService,
|
||||
@IWindowsService private readonly windowsService: IWindowsService,
|
||||
@IWorkspacesService private readonly workspaceService: IWorkspacesService,
|
||||
@IWorkspacesService private readonly workspacesService: IWorkspacesService,
|
||||
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
|
||||
@IFileDialogService private readonly fileDialogService: IFileDialogService,
|
||||
@IDialogService private readonly dialogService: IDialogService,
|
||||
@@ -123,7 +123,7 @@ export class WorkspaceEditingService implements IWorkspaceEditingService {
|
||||
|
||||
// Don't Save: delete workspace
|
||||
case ConfirmResult.DONT_SAVE:
|
||||
this.workspaceService.deleteUntitledWorkspace(workspaceIdentifier);
|
||||
this.workspacesService.deleteUntitledWorkspace(workspaceIdentifier);
|
||||
return false;
|
||||
|
||||
// Save: save workspace, but do not veto unload if path provided
|
||||
@@ -136,12 +136,12 @@ export class WorkspaceEditingService implements IWorkspaceEditingService {
|
||||
try {
|
||||
await this.saveWorkspaceAs(workspaceIdentifier, newWorkspacePath);
|
||||
|
||||
const newWorkspaceIdentifier = await this.workspaceService.getWorkspaceIdentifier(newWorkspacePath);
|
||||
const newWorkspaceIdentifier = await this.workspacesService.getWorkspaceIdentifier(newWorkspacePath);
|
||||
|
||||
const label = this.labelService.getWorkspaceLabel(newWorkspaceIdentifier, { verbose: true });
|
||||
this.windowService.addRecentlyOpened([{ label, workspace: newWorkspaceIdentifier }]);
|
||||
|
||||
this.workspaceService.deleteUntitledWorkspace(workspaceIdentifier);
|
||||
this.workspacesService.deleteUntitledWorkspace(workspaceIdentifier);
|
||||
} catch (error) {
|
||||
// ignore
|
||||
}
|
||||
@@ -293,7 +293,7 @@ export class WorkspaceEditingService implements IWorkspaceEditingService {
|
||||
}
|
||||
|
||||
const remoteAuthority = this.environmentService.configuration.remoteAuthority;
|
||||
const untitledWorkspace = await this.workspaceService.createUntitledWorkspace(folders, remoteAuthority);
|
||||
const untitledWorkspace = await this.workspacesService.createUntitledWorkspace(folders, remoteAuthority);
|
||||
if (path) {
|
||||
await this.saveWorkspaceAs(untitledWorkspace, path);
|
||||
} else {
|
||||
@@ -323,14 +323,14 @@ export class WorkspaceEditingService implements IWorkspaceEditingService {
|
||||
|
||||
// Prevent overwriting a workspace that is currently opened in another window
|
||||
if (windows.some(window => !!window.workspace && isEqual(window.workspace.configPath, path))) {
|
||||
const options: MessageBoxOptions = {
|
||||
type: 'info',
|
||||
buttons: [nls.localize('ok', "OK")],
|
||||
message: nls.localize('workspaceOpenedMessage', "Unable to save workspace '{0}'", basename(path)),
|
||||
detail: nls.localize('workspaceOpenedDetail', "The workspace is already opened in another window. Please close that window first and then try again."),
|
||||
noLink: true
|
||||
};
|
||||
await this.windowService.showMessageBox(options);
|
||||
await this.dialogService.show(
|
||||
Severity.Info,
|
||||
nls.localize('workspaceOpenedMessage', "Unable to save workspace '{0}'", basename(path)),
|
||||
[nls.localize('ok', "OK")],
|
||||
{
|
||||
detail: nls.localize('workspaceOpenedDetail', "The workspace is already opened in another window. Please close that window first and then try again.")
|
||||
}
|
||||
);
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -389,7 +389,7 @@ export class WorkspaceEditingService implements IWorkspaceEditingService {
|
||||
throw new Error('Entering a new workspace is not possible in tests.');
|
||||
}
|
||||
|
||||
const workspace = await this.workspaceService.getWorkspaceIdentifier(path);
|
||||
const workspace = await this.workspacesService.getWorkspaceIdentifier(path);
|
||||
|
||||
// Settings migration (only if we come from a folder workspace)
|
||||
if (this.contextService.getWorkbenchState() === WorkbenchState.FOLDER) {
|
||||
@@ -399,7 +399,7 @@ export class WorkspaceEditingService implements IWorkspaceEditingService {
|
||||
const workspaceImpl = this.contextService as WorkspaceService;
|
||||
await workspaceImpl.initialize(workspace);
|
||||
|
||||
const result = await this.windowService.enterWorkspace(path);
|
||||
const result = await this.workspacesService.enterWorkspace(path);
|
||||
if (result) {
|
||||
|
||||
// Migrate storage to new workspace
|
||||
@@ -415,7 +415,7 @@ export class WorkspaceEditingService implements IWorkspaceEditingService {
|
||||
|
||||
// TODO@aeschli: workaround until restarting works
|
||||
if (this.environmentService.configuration.remoteAuthority) {
|
||||
this.windowService.reloadWindow();
|
||||
this.hostService.reload();
|
||||
}
|
||||
|
||||
// Restart the extension host: entering a workspace means a new location for
|
||||
|
||||
@@ -6,20 +6,25 @@
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { IWorkspacesService, IWorkspaceFolderCreationData, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IEnterWorkspaceResult } from 'vs/platform/windows/common/windows';
|
||||
|
||||
export class WorkspacesService implements IWorkspacesService {
|
||||
|
||||
_serviceBrand: undefined;
|
||||
|
||||
async createUntitledWorkspace(folders?: IWorkspaceFolderCreationData[], remoteAuthority?: string): Promise<IWorkspaceIdentifier> {
|
||||
enterWorkspace(path: URI): Promise<IEnterWorkspaceResult | undefined> {
|
||||
throw new Error('Untitled workspaces are currently unsupported in Web');
|
||||
}
|
||||
|
||||
async deleteUntitledWorkspace(workspace: IWorkspaceIdentifier): Promise<void> {
|
||||
createUntitledWorkspace(folders?: IWorkspaceFolderCreationData[], remoteAuthority?: string): Promise<IWorkspaceIdentifier> {
|
||||
throw new Error('Untitled workspaces are currently unsupported in Web');
|
||||
}
|
||||
|
||||
async getWorkspaceIdentifier(workspacePath: URI): Promise<IWorkspaceIdentifier> {
|
||||
deleteUntitledWorkspace(workspace: IWorkspaceIdentifier): Promise<void> {
|
||||
throw new Error('Untitled workspaces are currently unsupported in Web');
|
||||
}
|
||||
|
||||
getWorkspaceIdentifier(workspacePath: URI): Promise<IWorkspaceIdentifier> {
|
||||
throw new Error('Untitled workspaces are currently unsupported in Web');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import { IWorkspacesService, IWorkspaceIdentifier, IWorkspaceFolderCreationData,
|
||||
import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { IWindowService, IEnterWorkspaceResult } from 'vs/platform/windows/common/windows';
|
||||
|
||||
export class WorkspacesService implements IWorkspacesService {
|
||||
|
||||
@@ -15,10 +16,22 @@ export class WorkspacesService implements IWorkspacesService {
|
||||
|
||||
private channel: IChannel;
|
||||
|
||||
constructor(@IMainProcessService mainProcessService: IMainProcessService) {
|
||||
constructor(
|
||||
@IMainProcessService mainProcessService: IMainProcessService,
|
||||
@IWindowService private readonly windowService: IWindowService
|
||||
) {
|
||||
this.channel = mainProcessService.getChannel('workspaces');
|
||||
}
|
||||
|
||||
async enterWorkspace(path: URI): Promise<IEnterWorkspaceResult | undefined> {
|
||||
const result: IEnterWorkspaceResult = await this.channel.call('enterWorkspace', [this.windowService.windowId, path]);
|
||||
if (result) {
|
||||
result.workspace = reviveWorkspaceIdentifier(result.workspace);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
createUntitledWorkspace(folders?: IWorkspaceFolderCreationData[], remoteAuthority?: string): Promise<IWorkspaceIdentifier> {
|
||||
return this.channel.call('createUntitledWorkspace', [folders, remoteAuthority]).then(reviveWorkspaceIdentifier);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user