Path: blob/main/src/vs/workbench/services/dialogs/electron-browser/fileDialogService.ts
3296 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';2930export class FileDialogService extends AbstractFileDialogService implements IFileDialogService {3132constructor(33@IHostService hostService: IHostService,34@IWorkspaceContextService contextService: IWorkspaceContextService,35@IHistoryService historyService: IHistoryService,36@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService,37@IInstantiationService instantiationService: IInstantiationService,38@IConfigurationService configurationService: IConfigurationService,39@IFileService fileService: IFileService,40@IOpenerService openerService: IOpenerService,41@INativeHostService private readonly nativeHostService: INativeHostService,42@IDialogService dialogService: IDialogService,43@ILanguageService languageService: ILanguageService,44@IWorkspacesService workspacesService: IWorkspacesService,45@ILabelService labelService: ILabelService,46@IPathService pathService: IPathService,47@ICommandService commandService: ICommandService,48@IEditorService editorService: IEditorService,49@ICodeEditorService codeEditorService: ICodeEditorService,50@ILogService logService: ILogService51) {52super(hostService, contextService, historyService, environmentService, instantiationService,53configurationService, fileService, openerService, dialogService, languageService, workspacesService, labelService, pathService, commandService, editorService, codeEditorService, logService);54}5556private toNativeOpenDialogOptions(options: IPickAndOpenOptions): INativeOpenDialogOptions {57return {58forceNewWindow: options.forceNewWindow,59telemetryExtraData: options.telemetryExtraData,60defaultPath: options.defaultUri?.fsPath61};62}6364private shouldUseSimplified(schema: string): { useSimplified: boolean; isSetting: boolean } {65const setting = (this.configurationService.getValue('files.simpleDialog.enable') === true);66const newWindowSetting = (this.configurationService.getValue('window.openFilesInNewWindow') === 'on');67return {68// - Only real files can be shown in the native file picker69// - If the simple file dialog is enabled70// - driver automation (like smoke tests) can use the simple file dialog but not native71useSimplified: ((schema !== Schemas.file) && (schema !== Schemas.vscodeUserData)) || setting || !!this.environmentService.enableSmokeTestDriver,72isSetting: newWindowSetting73};74}7576async pickFileFolderAndOpen(options: IPickAndOpenOptions): Promise<void> {77const schema = this.getFileSystemSchema(options);7879if (!options.defaultUri) {80options.defaultUri = await this.defaultFilePath(schema);81}8283const shouldUseSimplified = this.shouldUseSimplified(schema);84if (shouldUseSimplified.useSimplified) {85return this.pickFileFolderAndOpenSimplified(schema, options, shouldUseSimplified.isSetting);86}87return this.nativeHostService.pickFileFolderAndOpen(this.toNativeOpenDialogOptions(options));88}8990async pickFileAndOpen(options: IPickAndOpenOptions): Promise<void> {91const schema = this.getFileSystemSchema(options);9293if (!options.defaultUri) {94options.defaultUri = await this.defaultFilePath(schema);95}9697const shouldUseSimplified = this.shouldUseSimplified(schema);98if (shouldUseSimplified.useSimplified) {99return this.pickFileAndOpenSimplified(schema, options, shouldUseSimplified.isSetting);100}101return this.nativeHostService.pickFileAndOpen(this.toNativeOpenDialogOptions(options));102}103104async pickFolderAndOpen(options: IPickAndOpenOptions): Promise<void> {105const schema = this.getFileSystemSchema(options);106107if (!options.defaultUri) {108options.defaultUri = await this.defaultFolderPath(schema);109}110111if (this.shouldUseSimplified(schema).useSimplified) {112return this.pickFolderAndOpenSimplified(schema, options);113}114return this.nativeHostService.pickFolderAndOpen(this.toNativeOpenDialogOptions(options));115}116117async pickWorkspaceAndOpen(options: IPickAndOpenOptions): Promise<void> {118options.availableFileSystems = this.getWorkspaceAvailableFileSystems(options);119const schema = this.getFileSystemSchema(options);120121if (!options.defaultUri) {122options.defaultUri = await this.defaultWorkspacePath(schema);123}124125if (this.shouldUseSimplified(schema).useSimplified) {126return this.pickWorkspaceAndOpenSimplified(schema, options);127}128return this.nativeHostService.pickWorkspaceAndOpen(this.toNativeOpenDialogOptions(options));129}130131async pickFileToSave(defaultUri: URI, availableFileSystems?: string[]): Promise<URI | undefined> {132const schema = this.getFileSystemSchema({ defaultUri, availableFileSystems });133const options = this.getPickFileToSaveDialogOptions(defaultUri, availableFileSystems);134if (this.shouldUseSimplified(schema).useSimplified) {135return this.pickFileToSaveSimplified(schema, options);136} else {137const result = await this.nativeHostService.showSaveDialog(this.toNativeSaveDialogOptions(options));138if (result && !result.canceled && result.filePath) {139const uri = URI.file(result.filePath);140141this.addFileToRecentlyOpened(uri);142143return uri;144}145}146return;147}148149private toNativeSaveDialogOptions(options: ISaveDialogOptions): SaveDialogOptions & INativeHostOptions {150options.defaultUri = options.defaultUri ? URI.file(options.defaultUri.path) : undefined;151return {152defaultPath: options.defaultUri?.fsPath,153buttonLabel: typeof options.saveLabel === 'string' ? options.saveLabel : options.saveLabel?.withMnemonic,154filters: options.filters,155title: options.title,156targetWindowId: getActiveWindow().vscodeWindowId157};158}159160async showSaveDialog(options: ISaveDialogOptions): Promise<URI | undefined> {161const schema = this.getFileSystemSchema(options);162if (this.shouldUseSimplified(schema).useSimplified) {163return this.showSaveDialogSimplified(schema, options);164}165166const result = await this.nativeHostService.showSaveDialog(this.toNativeSaveDialogOptions(options));167if (result && !result.canceled && result.filePath) {168return URI.file(result.filePath);169}170171return;172}173174async showOpenDialog(options: IOpenDialogOptions): Promise<URI[] | undefined> {175const schema = this.getFileSystemSchema(options);176if (this.shouldUseSimplified(schema).useSimplified) {177return this.showOpenDialogSimplified(schema, options);178}179180const newOptions: OpenDialogOptions & { properties: string[] } & INativeHostOptions = {181title: options.title,182defaultPath: options.defaultUri?.fsPath,183buttonLabel: typeof options.openLabel === 'string' ? options.openLabel : options.openLabel?.withMnemonic,184filters: options.filters,185properties: [],186targetWindowId: getActiveWindow().vscodeWindowId187};188189newOptions.properties.push('createDirectory');190191if (options.canSelectFiles) {192newOptions.properties.push('openFile');193}194195if (options.canSelectFolders) {196newOptions.properties.push('openDirectory');197}198199if (options.canSelectMany) {200newOptions.properties.push('multiSelections');201}202203const result = await this.nativeHostService.showOpenDialog(newOptions);204return result && Array.isArray(result.filePaths) && result.filePaths.length > 0 ? result.filePaths.map(URI.file) : undefined;205}206}207208registerSingleton(IFileDialogService, FileDialogService, InstantiationType.Delayed);209210211