From 38b9a27fa5cb2ee92967cb8e26e7f70cdc9912b5 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 15 Mar 2019 11:18:36 +0100 Subject: [PATCH] nodeless - lift file dialog service to /browser (#70541) --- src/tsconfig.strictNullChecks.json | 3 +- .../browser/nodeless.simpleservices.ts | 49 +--- .../dialogs/browser/fileDialogService.ts | 256 ++++++++++++++++++ .../media/dark/accept.svg | 0 .../media/dark/folder.svg | 0 .../media/light/accept.svg | 0 .../media/light/folder.svg | 0 .../remoteFileDialog.ts | 29 +- .../dialogs/electron-browser/dialogService.ts | 250 +---------------- src/vs/workbench/workbench.main.ts | 1 + src/vs/workbench/workbench.nodeless.main.ts | 1 + 11 files changed, 277 insertions(+), 312 deletions(-) create mode 100644 src/vs/workbench/services/dialogs/browser/fileDialogService.ts rename src/vs/workbench/services/dialogs/{electron-browser => browser}/media/dark/accept.svg (100%) rename src/vs/workbench/services/dialogs/{electron-browser => browser}/media/dark/folder.svg (100%) rename src/vs/workbench/services/dialogs/{electron-browser => browser}/media/light/accept.svg (100%) rename src/vs/workbench/services/dialogs/{electron-browser => browser}/media/light/folder.svg (100%) rename src/vs/workbench/services/dialogs/{electron-browser => browser}/remoteFileDialog.ts (95%) diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index c7006e033f8..f6cfebb0ca4 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -299,8 +299,9 @@ "./vs/workbench/services/decorations/browser/decorations.ts", "./vs/workbench/services/decorations/browser/decorationsService.ts", "./vs/workbench/services/decorations/test/browser/decorationsService.test.ts", + "./vs/workbench/services/dialogs/browser/remoteFileDialog.ts", + "./vs/workbench/services/dialogs/browser/fileDialogService.ts", "./vs/workbench/services/dialogs/electron-browser/dialogService.ts", - "./vs/workbench/services/dialogs/electron-browser/remoteFileDialog.ts", "./vs/workbench/services/editor/browser/codeEditorService.ts", "./vs/workbench/services/editor/browser/editorService.ts", "./vs/workbench/services/editor/common/editorGroupsService.ts", diff --git a/src/vs/workbench/browser/nodeless.simpleservices.ts b/src/vs/workbench/browser/nodeless.simpleservices.ts index 527ddcd40ce..d83aaaf69ed 100644 --- a/src/vs/workbench/browser/nodeless.simpleservices.ts +++ b/src/vs/workbench/browser/nodeless.simpleservices.ts @@ -15,7 +15,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; // tslint:disable-next-line: import-patterns no-standalone-editor import { SimpleConfigurationService as StandaloneEditorConfigurationService, SimpleDialogService as StandaloneEditorDialogService, StandaloneKeybindingService, SimpleResourcePropertiesService } from 'vs/editor/standalone/browser/simpleServices'; -import { IDialogService, IFileDialogService, IPickAndOpenOptions, ISaveDialogOptions, IOpenDialogOptions } from 'vs/platform/dialogs/common/dialogs'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IDownloadService } from 'vs/platform/download/common/download'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IEnvironmentService, IExtensionHostDebugParams, IDebugParams } from 'vs/platform/environment/common/environment'; @@ -442,53 +442,6 @@ registerSingleton(IExtensionUrlHandler, SimpleExtensionURLHandler, true); //#endregion -//#region File Dialog - -export class SimpleFileDialogService implements IFileDialogService { - - _serviceBrand: any; - - defaultFilePath(schemeFilter?: string): URI { - throw new Error('Method not implemented.'); - } - - defaultFolderPath(schemeFilter?: string): URI { - throw new Error('Method not implemented.'); - } - - defaultWorkspacePath(schemeFilter?: string): URI { - throw new Error('Method not implemented.'); - } - - pickFileFolderAndOpen(options: IPickAndOpenOptions): Promise { - throw new Error('Method not implemented.'); - } - - pickFileAndOpen(options: IPickAndOpenOptions): Promise { - throw new Error('Method not implemented.'); - } - - pickFolderAndOpen(options: IPickAndOpenOptions): Promise { - throw new Error('Method not implemented.'); - } - - pickWorkspaceAndOpen(options: IPickAndOpenOptions): Promise { - throw new Error('Method not implemented.'); - } - - showSaveDialog(options: ISaveDialogOptions): Promise { - throw new Error('Method not implemented.'); - } - - showOpenDialog(options: IOpenDialogOptions): Promise { - throw new Error('Method not implemented.'); - } -} - -registerSingleton(IFileDialogService, SimpleFileDialogService, true); - -//#endregion - //#region JSON Editing export class SimpleJSONEditingService implements IJSONEditingService { diff --git a/src/vs/workbench/services/dialogs/browser/fileDialogService.ts b/src/vs/workbench/services/dialogs/browser/fileDialogService.ts new file mode 100644 index 00000000000..3e9da451222 --- /dev/null +++ b/src/vs/workbench/services/dialogs/browser/fileDialogService.ts @@ -0,0 +1,256 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from 'vs/nls'; +import { 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 { IEnvironmentService } from 'vs/platform/environment/common/environment'; +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'; + +export class FileDialogService implements IFileDialogService { + + _serviceBrand: any; + + constructor( + @IWindowService private readonly windowService: IWindowService, + @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, + @IHistoryService private readonly historyService: IHistoryService, + @IEnvironmentService private readonly environmentService: IEnvironmentService, + @IInstantiationService private readonly instantiationService: IInstantiationService, + @IConfigurationService private readonly configurationService: IConfigurationService + ) { } + + 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); + } + + return candidate && resources.dirname(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 folder path + return this.defaultFolderPath(schemeFilter); + } + + private toNativeOpenDialogOptions(options: IPickAndOpenOptions): INativeOpenDialogOptions { + return { + forceNewWindow: options.forceNewWindow, + telemetryExtraData: options.telemetryExtraData, + dialogOptions: { + defaultPath: options.defaultUri && options.defaultUri.fsPath + } + }; + } + + private shouldUseSimplified(schema: string): boolean { + return (schema !== Schemas.file) || (this.configurationService.getValue('workbench.dialogs.useSimplified') === 'true'); + } + + private ensureFileSchema(schema: string): string[] { + return schema !== Schemas.file ? [schema, Schemas.file] : [schema]; + } + + pickFileFolderAndOpen(options: IPickAndOpenOptions): Promise { + const schema = this.getFileSystemSchema(options); + + if (!options.defaultUri) { + options.defaultUri = this.defaultFilePath(schema); + } + + if (this.shouldUseSimplified(schema)) { + const title = nls.localize('openFileOrFolder.title', 'Open File Or Folder'); + const availableFileSystems = this.ensureFileSchema(schema); // always allow file as well + return this.pickRemoteResourceAndOpen({ canSelectFiles: true, canSelectFolders: true, canSelectMany: false, defaultUri: options.defaultUri, title, availableFileSystems }, !!options.forceNewWindow, true); + } + + return this.windowService.pickFileFolderAndOpen(this.toNativeOpenDialogOptions(options)); + } + + pickFileAndOpen(options: IPickAndOpenOptions): Promise { + const schema = this.getFileSystemSchema(options); + + if (!options.defaultUri) { + options.defaultUri = this.defaultFilePath(schema); + } + + if (this.shouldUseSimplified(schema)) { + const title = nls.localize('openFile.title', 'Open File'); + const availableFileSystems = this.ensureFileSchema(schema); // always allow file as well + return this.pickRemoteResourceAndOpen({ canSelectFiles: true, canSelectFolders: false, canSelectMany: false, defaultUri: options.defaultUri, title, availableFileSystems }, !!options.forceNewWindow, true); + } + + return this.windowService.pickFileAndOpen(this.toNativeOpenDialogOptions(options)); + } + + pickFolderAndOpen(options: IPickAndOpenOptions): Promise { + const schema = this.getFileSystemSchema(options); + + if (!options.defaultUri) { + options.defaultUri = this.defaultFolderPath(schema); + } + + if (this.shouldUseSimplified(schema)) { + const title = nls.localize('openFolder.title', 'Open Folder'); + const availableFileSystems = this.ensureFileSchema(schema); // always allow file as well + return this.pickRemoteResourceAndOpen({ canSelectFiles: false, canSelectFolders: true, canSelectMany: false, defaultUri: options.defaultUri, title, availableFileSystems }, !!options.forceNewWindow, false); + } + + return this.windowService.pickFolderAndOpen(this.toNativeOpenDialogOptions(options)); + } + + pickWorkspaceAndOpen(options: IPickAndOpenOptions): Promise { + const schema = this.getFileSystemSchema(options); + + if (!options.defaultUri) { + options.defaultUri = this.defaultWorkspacePath(schema); + } + + if (this.shouldUseSimplified(schema)) { + const title = nls.localize('openWorkspace.title', 'Open Workspace'); + const filters: FileFilter[] = [{ name: nls.localize('filterName.workspace', 'Workspace'), extensions: [WORKSPACE_EXTENSION] }]; + const availableFileSystems = this.ensureFileSchema(schema); // always allow file as well + return this.pickRemoteResourceAndOpen({ canSelectFiles: true, canSelectFolders: false, canSelectMany: false, defaultUri: options.defaultUri, title, filters, availableFileSystems }, !!options.forceNewWindow, false); + } + + return this.windowService.pickWorkspaceAndOpen(this.toNativeOpenDialogOptions(options)); + } + + private toNativeSaveDialogOptions(options: ISaveDialogOptions): Electron.SaveDialogOptions { + return { + defaultPath: options.defaultUri && options.defaultUri.fsPath, + buttonLabel: options.saveLabel, + filters: options.filters, + title: options.title + }; + } + + showSaveDialog(options: ISaveDialogOptions): Promise { + const schema = this.getFileSystemSchema(options); + if (this.shouldUseSimplified(schema)) { + if (!options.availableFileSystems) { + options.availableFileSystems = [schema]; // by default only allow saving in the own file system + } + return this.saveRemoteResource(options); + } + + return this.windowService.showSaveDialog(this.toNativeSaveDialogOptions(options)).then(result => { + if (result) { + return URI.file(result); + } + + return undefined; + }); + } + + showOpenDialog(options: IOpenDialogOptions): Promise { + const schema = this.getFileSystemSchema(options); + if (schema !== Schemas.file) { + if (!options.availableFileSystems) { + options.availableFileSystems = [schema]; // by default only allow loading in the own file system + } + return this.pickRemoteResource(options).then(urisToOpen => { + return urisToOpen && urisToOpen.map(uto => uto.uri); + }); + } + + 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'); + } + + return this.windowService.showOpenDialog(newOptions).then(result => result ? result.map(URI.file) : undefined); + } + + private pickRemoteResourceAndOpen(options: IOpenDialogOptions, forceNewWindow: boolean, forceOpenWorkspaceAsFile: boolean) { + return this.pickRemoteResource(options).then(urisToOpen => { + if (urisToOpen) { + return this.windowService.openWindow(urisToOpen, { forceNewWindow, forceOpenWorkspaceAsFile }); + } + return undefined; + }); + } + + private pickRemoteResource(options: IOpenDialogOptions): Promise { + const remoteFileDialog = this.instantiationService.createInstance(RemoteFileDialog); + return remoteFileDialog.showOpenDialog(options); + } + + private saveRemoteResource(options: ISaveDialogOptions): Promise { + const remoteFileDialog = this.instantiationService.createInstance(RemoteFileDialog); + return remoteFileDialog.showSaveDialog(options); + } + + private getSchemeFilterForWindow() { + return !this.windowService.getConfiguration().remoteAuthority ? Schemas.file : REMOTE_HOST_SCHEME; + } + + private getFileSystemSchema(options: { availableFileSystems?: string[], defaultUri?: URI }): string { + return options.availableFileSystems && options.availableFileSystems[0] || options.defaultUri && options.defaultUri.scheme || this.getSchemeFilterForWindow(); + } + +} + +function isUntitledWorkspace(path: URI, environmentService: IEnvironmentService): boolean { + return resources.isEqualOrParent(path, environmentService.untitledWorkspacesHome); +} + +registerSingleton(IFileDialogService, FileDialogService, true); \ No newline at end of file diff --git a/src/vs/workbench/services/dialogs/electron-browser/media/dark/accept.svg b/src/vs/workbench/services/dialogs/browser/media/dark/accept.svg similarity index 100% rename from src/vs/workbench/services/dialogs/electron-browser/media/dark/accept.svg rename to src/vs/workbench/services/dialogs/browser/media/dark/accept.svg diff --git a/src/vs/workbench/services/dialogs/electron-browser/media/dark/folder.svg b/src/vs/workbench/services/dialogs/browser/media/dark/folder.svg similarity index 100% rename from src/vs/workbench/services/dialogs/electron-browser/media/dark/folder.svg rename to src/vs/workbench/services/dialogs/browser/media/dark/folder.svg diff --git a/src/vs/workbench/services/dialogs/electron-browser/media/light/accept.svg b/src/vs/workbench/services/dialogs/browser/media/light/accept.svg similarity index 100% rename from src/vs/workbench/services/dialogs/electron-browser/media/light/accept.svg rename to src/vs/workbench/services/dialogs/browser/media/light/accept.svg diff --git a/src/vs/workbench/services/dialogs/electron-browser/media/light/folder.svg b/src/vs/workbench/services/dialogs/browser/media/light/folder.svg similarity index 100% rename from src/vs/workbench/services/dialogs/electron-browser/media/light/folder.svg rename to src/vs/workbench/services/dialogs/browser/media/light/folder.svg diff --git a/src/vs/workbench/services/dialogs/electron-browser/remoteFileDialog.ts b/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts similarity index 95% rename from src/vs/workbench/services/dialogs/electron-browser/remoteFileDialog.ts rename to src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts index 090eb7388a9..7df4d7bd74d 100644 --- a/src/vs/workbench/services/dialogs/electron-browser/remoteFileDialog.ts +++ b/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts @@ -6,7 +6,6 @@ import * as nls from 'vs/nls'; import * as resources from 'vs/base/common/resources'; import * as objects from 'vs/base/common/objects'; -import { RemoteFileService } from 'vs/workbench/services/files/node/remoteFileService'; import { IFileService, IFileStat, FileKind } from 'vs/platform/files/common/files'; import { IQuickInputService, IQuickPickItem, IQuickPick, IQuickInputButton } from 'vs/platform/quickinput/common/quickInput'; import { URI } from 'vs/base/common/uri'; @@ -48,7 +47,7 @@ export class RemoteFileDialog { private shouldOverwriteFile: boolean = false; constructor( - @IFileService private readonly remoteFileService: RemoteFileService, + @IFileService private readonly fileService: IFileService, @IQuickInputService private readonly quickInputService: IQuickInputService, @IWindowService private readonly windowService: IWindowService, @ILabelService private readonly labelService: ILabelService, @@ -77,7 +76,7 @@ export class RemoteFileDialog { return this.pickResource().then(async fileFolderUri => { if (fileFolderUri) { - const stat = await this.remoteFileService.resolveFile(fileFolderUri); + const stat = await this.fileService.resolveFile(fileFolderUri); return [{ uri: fileFolderUri, typeHint: stat.isDirectory ? 'folder' : 'file' }]; } @@ -106,7 +105,7 @@ export class RemoteFileDialog { private getOptions(options: ISaveDialogOptions | IOpenDialogOptions): IOpenDialogOptions | undefined { const defaultUri = options.defaultUri ? options.defaultUri : URI.from({ scheme: this.scheme, authority: this.remoteAuthority, path: '/' }); - if ((this.scheme !== Schemas.file) && !this.remoteFileService.canHandleResource(defaultUri)) { + if ((this.scheme !== Schemas.file) && !this.fileService.canHandleResource(defaultUri)) { this.notificationService.info(nls.localize('remoteFileDialog.notConnectedToRemote', 'File system provider for {0} is not available.', defaultUri.toString())); return undefined; } @@ -141,7 +140,7 @@ export class RemoteFileDialog { let ext: string = resources.extname(homedir); if (this.options.defaultUri) { try { - stat = await this.remoteFileService.resolveFile(this.options.defaultUri); + stat = await this.fileService.resolveFile(this.options.defaultUri); } catch (e) { // The file or folder doesn't exist } @@ -267,8 +266,8 @@ export class RemoteFileDialog { let stat: IFileStat | undefined; let statDirname: IFileStat | undefined; try { - statDirname = await this.remoteFileService.resolveFile(inputUriDirname); - stat = await this.remoteFileService.resolveFile(inputUri); + statDirname = await this.fileService.resolveFile(inputUriDirname); + stat = await this.fileService.resolveFile(inputUri); } catch (e) { // do nothing } @@ -310,7 +309,7 @@ export class RemoteFileDialog { if (this.endsWithSlash(value) || (!resources.isEqual(this.currentFolder, resources.dirname(valueUri), true) && resources.isEqualOrParent(this.currentFolder, resources.dirname(valueUri), true))) { let stat: IFileStat | undefined; try { - stat = await this.remoteFileService.resolveFile(valueUri); + stat = await this.fileService.resolveFile(valueUri); } catch (e) { // do nothing } @@ -321,7 +320,7 @@ export class RemoteFileDialog { if (!resources.isEqual(this.currentFolder, inputUriDirname, true)) { let statWithoutTrailing: IFileStat | undefined; try { - statWithoutTrailing = await this.remoteFileService.resolveFile(inputUriDirname); + statWithoutTrailing = await this.fileService.resolveFile(inputUriDirname); } catch (e) { // do nothing } @@ -358,8 +357,8 @@ export class RemoteFileDialog { let stat: IFileStat | undefined; let statDirname: IFileStat | undefined; try { - statDirname = await this.remoteFileService.resolveFile(resources.dirname(uri)); - stat = await this.remoteFileService.resolveFile(uri); + statDirname = await this.fileService.resolveFile(resources.dirname(uri)); + stat = await this.fileService.resolveFile(uri); } catch (e) { // do nothing } @@ -480,7 +479,7 @@ export class RemoteFileDialog { const backDir = this.createBackItem(currentFolder); try { - const fileNames = await this.remoteFileService.readFolder(currentFolder); + const fileNames = await this.fileService.readFolder(currentFolder); const items = await Promise.all(fileNames.map(fileName => this.createItem(fileName, currentFolder))); for (let item of items) { if (item) { @@ -528,7 +527,7 @@ export class RemoteFileDialog { private async createItem(filename: string, parent: URI): Promise { let fullPath = resources.joinPath(parent, filename); try { - const stat = await this.remoteFileService.resolveFile(fullPath); + const stat = await this.fileService.resolveFile(fullPath); if (stat.isDirectory) { filename = this.basenameWithTrailingSlash(fullPath); return { label: filename, uri: fullPath, isFolder: true, iconClasses: getIconClasses(this.modelService, this.modeService, fullPath || undefined, FileKind.FOLDER) }; @@ -543,8 +542,8 @@ export class RemoteFileDialog { private getDialogIcons(name: string): { light: URI, dark: URI } { return { - dark: URI.parse(require.toUrl(`vs/workbench/services/dialogs/electron-browser/media/dark/${name}.svg`)), - light: URI.parse(require.toUrl(`vs/workbench/services/dialogs/electron-browser/media/light/${name}.svg`)) + dark: URI.parse(require.toUrl(`vs/workbench/services/dialogs/browser/media/dark/${name}.svg`)), + light: URI.parse(require.toUrl(`vs/workbench/services/dialogs/browser/media/light/${name}.svg`)) }; } } \ No newline at end of file diff --git a/src/vs/workbench/services/dialogs/electron-browser/dialogService.ts b/src/vs/workbench/services/dialogs/electron-browser/dialogService.ts index 71c66bf9209..1eb57c6c736 100644 --- a/src/vs/workbench/services/dialogs/electron-browser/dialogService.ts +++ b/src/vs/workbench/services/dialogs/electron-browser/dialogService.ts @@ -7,21 +7,10 @@ import * as nls from 'vs/nls'; import product from 'vs/platform/product/node/product'; import Severity from 'vs/base/common/severity'; import { isLinux, isWindows } from 'vs/base/common/platform'; -import { IWindowService, INativeOpenDialogOptions, OpenDialogOptions, IURIToOpen, FileFilter } from 'vs/platform/windows/common/windows'; +import { IWindowService } from 'vs/platform/windows/common/windows'; import { mnemonicButtonLabel } from 'vs/base/common/labels'; -import { IDialogService, IConfirmation, IConfirmationResult, IDialogOptions, IPickAndOpenOptions, ISaveDialogOptions, IOpenDialogOptions, IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { IDialogService, IConfirmation, IConfirmationResult, IDialogOptions } from 'vs/platform/dialogs/common/dialogs'; import { ILogService } from 'vs/platform/log/common/log'; -import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; -import { IHistoryService } from 'vs/workbench/services/history/common/history'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -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/electron-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 { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; import { DialogChannel } from 'vs/platform/dialogs/node/dialogIpc'; @@ -162,239 +151,4 @@ export class DialogService implements IDialogService { } } -export class FileDialogService implements IFileDialogService { - - _serviceBrand: any; - - constructor( - @IWindowService private readonly windowService: IWindowService, - @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, - @IHistoryService private readonly historyService: IHistoryService, - @IEnvironmentService private readonly environmentService: IEnvironmentService, - @IInstantiationService private readonly instantiationService: IInstantiationService, - @IConfigurationService private readonly configurationService: IConfigurationService - ) { } - - 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); - } - - return candidate && resources.dirname(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 folder path - return this.defaultFolderPath(schemeFilter); - } - - private toNativeOpenDialogOptions(options: IPickAndOpenOptions): INativeOpenDialogOptions { - return { - forceNewWindow: options.forceNewWindow, - telemetryExtraData: options.telemetryExtraData, - dialogOptions: { - defaultPath: options.defaultUri && options.defaultUri.fsPath - } - }; - } - - private shouldUseSimplified(schema: string): boolean { - return (schema !== Schemas.file) || (this.configurationService.getValue('workbench.dialogs.useSimplified') === 'true'); - } - - private ensureFileSchema(schema: string): string[] { - return schema !== Schemas.file ? [schema, Schemas.file] : [schema]; - } - - pickFileFolderAndOpen(options: IPickAndOpenOptions): Promise { - const schema = this.getFileSystemSchema(options); - - if (!options.defaultUri) { - options.defaultUri = this.defaultFilePath(schema); - } - - if (this.shouldUseSimplified(schema)) { - const title = nls.localize('openFileOrFolder.title', 'Open File Or Folder'); - const availableFileSystems = this.ensureFileSchema(schema); // always allow file as well - return this.pickRemoteResourceAndOpen({ canSelectFiles: true, canSelectFolders: true, canSelectMany: false, defaultUri: options.defaultUri, title, availableFileSystems }, !!options.forceNewWindow, true); - } - - return this.windowService.pickFileFolderAndOpen(this.toNativeOpenDialogOptions(options)); - } - - pickFileAndOpen(options: IPickAndOpenOptions): Promise { - const schema = this.getFileSystemSchema(options); - - if (!options.defaultUri) { - options.defaultUri = this.defaultFilePath(schema); - } - - if (this.shouldUseSimplified(schema)) { - const title = nls.localize('openFile.title', 'Open File'); - const availableFileSystems = this.ensureFileSchema(schema); // always allow file as well - return this.pickRemoteResourceAndOpen({ canSelectFiles: true, canSelectFolders: false, canSelectMany: false, defaultUri: options.defaultUri, title, availableFileSystems }, !!options.forceNewWindow, true); - } - - return this.windowService.pickFileAndOpen(this.toNativeOpenDialogOptions(options)); - } - - pickFolderAndOpen(options: IPickAndOpenOptions): Promise { - const schema = this.getFileSystemSchema(options); - - if (!options.defaultUri) { - options.defaultUri = this.defaultFolderPath(schema); - } - - if (this.shouldUseSimplified(schema)) { - const title = nls.localize('openFolder.title', 'Open Folder'); - const availableFileSystems = this.ensureFileSchema(schema); // always allow file as well - return this.pickRemoteResourceAndOpen({ canSelectFiles: false, canSelectFolders: true, canSelectMany: false, defaultUri: options.defaultUri, title, availableFileSystems }, !!options.forceNewWindow, false); - } - - return this.windowService.pickFolderAndOpen(this.toNativeOpenDialogOptions(options)); - } - - pickWorkspaceAndOpen(options: IPickAndOpenOptions): Promise { - const schema = this.getFileSystemSchema(options); - - if (!options.defaultUri) { - options.defaultUri = this.defaultWorkspacePath(schema); - } - - if (this.shouldUseSimplified(schema)) { - const title = nls.localize('openWorkspace.title', 'Open Workspace'); - const filters: FileFilter[] = [{ name: nls.localize('filterName.workspace', 'Workspace'), extensions: [WORKSPACE_EXTENSION] }]; - const availableFileSystems = this.ensureFileSchema(schema); // always allow file as well - return this.pickRemoteResourceAndOpen({ canSelectFiles: true, canSelectFolders: false, canSelectMany: false, defaultUri: options.defaultUri, title, filters, availableFileSystems }, !!options.forceNewWindow, false); - } - - return this.windowService.pickWorkspaceAndOpen(this.toNativeOpenDialogOptions(options)); - } - - private toNativeSaveDialogOptions(options: ISaveDialogOptions): Electron.SaveDialogOptions { - return { - defaultPath: options.defaultUri && options.defaultUri.fsPath, - buttonLabel: options.saveLabel, - filters: options.filters, - title: options.title - }; - } - - showSaveDialog(options: ISaveDialogOptions): Promise { - const schema = this.getFileSystemSchema(options); - if (this.shouldUseSimplified(schema)) { - if (!options.availableFileSystems) { - options.availableFileSystems = [schema]; // by default only allow saving in the own file system - } - return this.saveRemoteResource(options); - } - - return this.windowService.showSaveDialog(this.toNativeSaveDialogOptions(options)).then(result => { - if (result) { - return URI.file(result); - } - - return undefined; - }); - } - - showOpenDialog(options: IOpenDialogOptions): Promise { - const schema = this.getFileSystemSchema(options); - if (schema !== Schemas.file) { - if (!options.availableFileSystems) { - options.availableFileSystems = [schema]; // by default only allow loading in the own file system - } - return this.pickRemoteResource(options).then(urisToOpen => { - return urisToOpen && urisToOpen.map(uto => uto.uri); - }); - } - - 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'); - } - - return this.windowService.showOpenDialog(newOptions).then(result => result ? result.map(URI.file) : undefined); - } - - private pickRemoteResourceAndOpen(options: IOpenDialogOptions, forceNewWindow: boolean, forceOpenWorkspaceAsFile: boolean) { - return this.pickRemoteResource(options).then(urisToOpen => { - if (urisToOpen) { - return this.windowService.openWindow(urisToOpen, { forceNewWindow, forceOpenWorkspaceAsFile }); - } - return undefined; - }); - } - - private pickRemoteResource(options: IOpenDialogOptions): Promise { - const remoteFileDialog = this.instantiationService.createInstance(RemoteFileDialog); - return remoteFileDialog.showOpenDialog(options); - } - - private saveRemoteResource(options: ISaveDialogOptions): Promise { - const remoteFileDialog = this.instantiationService.createInstance(RemoteFileDialog); - return remoteFileDialog.showSaveDialog(options); - } - - private getSchemeFilterForWindow() { - return !this.windowService.getConfiguration().remoteAuthority ? Schemas.file : REMOTE_HOST_SCHEME; - } - - private getFileSystemSchema(options: { availableFileSystems?: string[], defaultUri?: URI }): string { - return options.availableFileSystems && options.availableFileSystems[0] || options.defaultUri && options.defaultUri.scheme || this.getSchemeFilterForWindow(); - } - -} - -function isUntitledWorkspace(path: URI, environmentService: IEnvironmentService): boolean { - return resources.isEqualOrParent(path, environmentService.untitledWorkspacesHome); -} - -registerSingleton(IFileDialogService, FileDialogService, true); registerSingleton(IDialogService, DialogService, true); \ No newline at end of file diff --git a/src/vs/workbench/workbench.main.ts b/src/vs/workbench/workbench.main.ts index 6305e882827..2ccd4a21bbf 100644 --- a/src/vs/workbench/workbench.main.ts +++ b/src/vs/workbench/workbench.main.ts @@ -111,6 +111,7 @@ import 'vs/workbench/services/output/node/outputChannelModelService'; import 'vs/workbench/services/configuration/node/jsonEditingService'; import 'vs/workbench/services/textmodelResolver/common/textModelResolverService'; import 'vs/workbench/services/textfile/common/textFileService'; +import 'vs/workbench/services/dialogs/browser/fileDialogService'; import 'vs/workbench/services/dialogs/electron-browser/dialogService'; import 'vs/workbench/services/backup/node/backupFileService'; import 'vs/workbench/services/editor/browser/editorService'; diff --git a/src/vs/workbench/workbench.nodeless.main.ts b/src/vs/workbench/workbench.nodeless.main.ts index ae1b852393c..9cba8d9a4d2 100644 --- a/src/vs/workbench/workbench.nodeless.main.ts +++ b/src/vs/workbench/workbench.nodeless.main.ts @@ -114,6 +114,7 @@ import 'vs/workbench/services/output/common/outputChannelModelService'; // import 'vs/workbench/services/configuration/node/jsonEditingService'; import 'vs/workbench/services/textmodelResolver/common/textModelResolverService'; import 'vs/workbench/services/textfile/common/textFileService'; +import 'vs/workbench/services/dialogs/browser/fileDialogService'; // import 'vs/workbench/services/dialogs/electron-browser/dialogService'; // import 'vs/workbench/services/backup/node/backupFileService'; import 'vs/workbench/services/editor/browser/editorService'; -- GitLab