Path: blob/main/src/vs/workbench/services/dialogs/electron-browser/fileDialogService.ts
5251 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { SaveDialogOptions, OpenDialogOptions } from '../../../../base/parts/sandbox/common/electronTypes.js';6import { IHostService } from '../../host/browser/host.js';7import { IPickAndOpenOptions, ISaveDialogOptions, IOpenDialogOptions, IFileDialogService, IDialogService, INativeOpenDialogOptions } from '../../../../platform/dialogs/common/dialogs.js';8import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js';9import { IHistoryService } from '../../history/common/history.js';10import { IWorkbenchEnvironmentService } from '../../environment/common/environmentService.js';11import { URI } from '../../../../base/common/uri.js';12import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';13import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';14import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js';15import { IFileService } from '../../../../platform/files/common/files.js';16import { IOpenerService } from '../../../../platform/opener/common/opener.js';17import { INativeHostOptions, INativeHostService } from '../../../../platform/native/common/native.js';18import { AbstractFileDialogService } from '../browser/abstractFileDialogService.js';19import { Schemas } from '../../../../base/common/network.js';20import { ILanguageService } from '../../../../editor/common/languages/language.js';21import { IWorkspacesService } from '../../../../platform/workspaces/common/workspaces.js';22import { ILabelService } from '../../../../platform/label/common/label.js';23import { IPathService } from '../../path/common/pathService.js';24import { ICommandService } from '../../../../platform/commands/common/commands.js';25import { ICodeEditorService } from '../../../../editor/browser/services/codeEditorService.js';26import { IEditorService } from '../../editor/common/editorService.js';27import { ILogService } from '../../../../platform/log/common/log.js';28import { getActiveWindow } from '../../../../base/browser/dom.js';29import { IRemoteAgentService } from '../../remote/common/remoteAgentService.js';3031export class FileDialogService extends AbstractFileDialogService implements IFileDialogService {3233constructor(34@IHostService hostService: IHostService,35@IWorkspaceContextService contextService: IWorkspaceContextService,36@IHistoryService historyService: IHistoryService,37@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService,38@IInstantiationService instantiationService: IInstantiationService,39@IConfigurationService configurationService: IConfigurationService,40@IFileService fileService: IFileService,41@IOpenerService openerService: IOpenerService,42@INativeHostService private readonly nativeHostService: INativeHostService,43@IDialogService dialogService: IDialogService,44@ILanguageService languageService: ILanguageService,45@IWorkspacesService workspacesService: IWorkspacesService,46@ILabelService labelService: ILabelService,47@IPathService pathService: IPathService,48@ICommandService commandService: ICommandService,49@IEditorService editorService: IEditorService,50@ICodeEditorService codeEditorService: ICodeEditorService,51@ILogService logService: ILogService,52@IRemoteAgentService remoteAgentService: IRemoteAgentService53) {54super(hostService, contextService, historyService, environmentService, instantiationService,55configurationService, fileService, openerService, dialogService, languageService, workspacesService, labelService, pathService, commandService, editorService, codeEditorService, logService, remoteAgentService);56}5758private toNativeOpenDialogOptions(options: IPickAndOpenOptions): INativeOpenDialogOptions {59return {60forceNewWindow: options.forceNewWindow,61telemetryExtraData: options.telemetryExtraData,62defaultPath: options.defaultUri?.fsPath63};64}6566private shouldUseSimplified(schema: string): { useSimplified: boolean; isSetting: boolean } {67const setting = (this.configurationService.getValue('files.simpleDialog.enable') === true);68const newWindowSetting = (this.configurationService.getValue('window.openFilesInNewWindow') === 'on');69return {70// - Only real files can be shown in the native file picker71// - If the simple file dialog is enabled72// - driver automation (like smoke tests) can use the simple file dialog but not native73useSimplified: ((schema !== Schemas.file) && (schema !== Schemas.vscodeUserData)) || setting || !!this.environmentService.enableSmokeTestDriver,74isSetting: newWindowSetting75};76}7778async pickFileFolderAndOpen(options: IPickAndOpenOptions): Promise<void> {79const schema = this.getFileSystemSchema(options);8081if (!options.defaultUri) {82options.defaultUri = await this.defaultFilePath(schema);83}8485const shouldUseSimplified = this.shouldUseSimplified(schema);86if (shouldUseSimplified.useSimplified) {87return this.pickFileFolderAndOpenSimplified(schema, options, shouldUseSimplified.isSetting);88}89return this.nativeHostService.pickFileFolderAndOpen(this.toNativeOpenDialogOptions(options));90}9192async pickFileAndOpen(options: IPickAndOpenOptions): Promise<void> {93const schema = this.getFileSystemSchema(options);9495if (!options.defaultUri) {96options.defaultUri = await this.defaultFilePath(schema);97}9899const shouldUseSimplified = this.shouldUseSimplified(schema);100if (shouldUseSimplified.useSimplified) {101return this.pickFileAndOpenSimplified(schema, options, shouldUseSimplified.isSetting);102}103return this.nativeHostService.pickFileAndOpen(this.toNativeOpenDialogOptions(options));104}105106async pickFolderAndOpen(options: IPickAndOpenOptions): Promise<void> {107const schema = this.getFileSystemSchema(options);108109if (!options.defaultUri) {110options.defaultUri = await this.defaultFolderPath(schema);111}112113if (this.shouldUseSimplified(schema).useSimplified) {114return this.pickFolderAndOpenSimplified(schema, options);115}116return this.nativeHostService.pickFolderAndOpen(this.toNativeOpenDialogOptions(options));117}118119async pickWorkspaceAndOpen(options: IPickAndOpenOptions): Promise<void> {120options.availableFileSystems = this.getWorkspaceAvailableFileSystems(options);121const schema = this.getFileSystemSchema(options);122123if (!options.defaultUri) {124options.defaultUri = await this.defaultWorkspacePath(schema);125}126127if (this.shouldUseSimplified(schema).useSimplified) {128return this.pickWorkspaceAndOpenSimplified(schema, options);129}130return this.nativeHostService.pickWorkspaceAndOpen(this.toNativeOpenDialogOptions(options));131}132133async pickFileToSave(defaultUri: URI, availableFileSystems?: string[]): Promise<URI | undefined> {134const schema = this.getFileSystemSchema({ defaultUri, availableFileSystems });135const options = this.getPickFileToSaveDialogOptions(defaultUri, availableFileSystems);136if (this.shouldUseSimplified(schema).useSimplified) {137return this.pickFileToSaveSimplified(schema, options);138} else {139const result = await this.nativeHostService.showSaveDialog(this.toNativeSaveDialogOptions(options));140if (result && !result.canceled && result.filePath) {141const uri = URI.file(result.filePath);142143this.addFileToRecentlyOpened(uri);144145return uri;146}147}148return;149}150151private toNativeSaveDialogOptions(options: ISaveDialogOptions): SaveDialogOptions & INativeHostOptions {152options.defaultUri = options.defaultUri ? URI.file(options.defaultUri.path) : undefined;153return {154defaultPath: options.defaultUri?.fsPath,155buttonLabel: typeof options.saveLabel === 'string' ? options.saveLabel : options.saveLabel?.withMnemonic,156filters: options.filters,157title: options.title,158targetWindowId: getActiveWindow().vscodeWindowId159};160}161162async showSaveDialog(options: ISaveDialogOptions): Promise<URI | undefined> {163const schema = this.getFileSystemSchema(options);164if (this.shouldUseSimplified(schema).useSimplified) {165return this.showSaveDialogSimplified(schema, options);166}167168const result = await this.nativeHostService.showSaveDialog(this.toNativeSaveDialogOptions(options));169if (result && !result.canceled && result.filePath) {170return URI.file(result.filePath);171}172173return;174}175176async showOpenDialog(options: IOpenDialogOptions): Promise<URI[] | undefined> {177const schema = this.getFileSystemSchema(options);178if (this.shouldUseSimplified(schema).useSimplified) {179return this.showOpenDialogSimplified(schema, options);180}181182const newOptions: OpenDialogOptions & { properties: string[] } & INativeHostOptions = {183title: options.title,184defaultPath: options.defaultUri?.fsPath,185buttonLabel: typeof options.openLabel === 'string' ? options.openLabel : options.openLabel?.withMnemonic,186filters: options.filters,187properties: [],188targetWindowId: getActiveWindow().vscodeWindowId189};190191newOptions.properties.push('createDirectory');192193if (options.canSelectFiles) {194newOptions.properties.push('openFile');195}196197if (options.canSelectFolders) {198newOptions.properties.push('openDirectory');199}200201if (options.canSelectMany) {202newOptions.properties.push('multiSelections');203}204205const result = await this.nativeHostService.showOpenDialog(newOptions);206return result && Array.isArray(result.filePaths) && result.filePaths.length > 0 ? result.filePaths.map(URI.file) : undefined;207}208}209210registerSingleton(IFileDialogService, FileDialogService, InstantiationType.Delayed);211212213