"Fossies" - the Fresh Open Source Software Archive

Member "vscode-1.49.1/src/vs/workbench/services/dialogs/browser/abstractFileDialogService.ts" (16 Sep 2020, 13945 Bytes) of package /linux/misc/vscode-1.49.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) TypeScript source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the last Fossies "Diffs" side-by-side code changes report for "abstractFileDialogService.ts": 1.47.3_vs_1.48.0.

    1 /*---------------------------------------------------------------------------------------------
    2  *  Copyright (c) Microsoft Corporation. All rights reserved.
    3  *  Licensed under the MIT License. See License.txt in the project root for license information.
    4  *--------------------------------------------------------------------------------------------*/
    5 
    6 import * as nls from 'vs/nls';
    7 import { IWindowOpenable, isWorkspaceToOpen, isFileToOpen } from 'vs/platform/windows/common/windows';
    8 import { IPickAndOpenOptions, ISaveDialogOptions, IOpenDialogOptions, FileFilter, IFileDialogService, IDialogService, ConfirmResult, getFileNamesMessage } from 'vs/platform/dialogs/common/dialogs';
    9 import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
   10 import { IHistoryService } from 'vs/workbench/services/history/common/history';
   11 import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
   12 import { URI } from 'vs/base/common/uri';
   13 import { Schemas } from 'vs/base/common/network';
   14 import * as resources from 'vs/base/common/resources';
   15 import { IInstantiationService, } from 'vs/platform/instantiation/common/instantiation';
   16 import { SimpleFileDialog } from 'vs/workbench/services/dialogs/browser/simpleFileDialog';
   17 import { WORKSPACE_EXTENSION, isUntitledWorkspace, IWorkspacesService } from 'vs/platform/workspaces/common/workspaces';
   18 import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts';
   19 import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
   20 import { IFileService } from 'vs/platform/files/common/files';
   21 import { IOpenerService } from 'vs/platform/opener/common/opener';
   22 import { IHostService } from 'vs/workbench/services/host/browser/host';
   23 import Severity from 'vs/base/common/severity';
   24 import { coalesce } from 'vs/base/common/arrays';
   25 import { trim } from 'vs/base/common/strings';
   26 import { IModeService } from 'vs/editor/common/services/modeService';
   27 import { ILabelService } from 'vs/platform/label/common/label';
   28 
   29 export abstract class AbstractFileDialogService implements IFileDialogService {
   30 
   31     declare readonly _serviceBrand: undefined;
   32 
   33     constructor(
   34         @IHostService protected readonly hostService: IHostService,
   35         @IWorkspaceContextService protected readonly contextService: IWorkspaceContextService,
   36         @IHistoryService protected readonly historyService: IHistoryService,
   37         @IWorkbenchEnvironmentService protected readonly environmentService: IWorkbenchEnvironmentService,
   38         @IInstantiationService protected readonly instantiationService: IInstantiationService,
   39         @IConfigurationService protected readonly configurationService: IConfigurationService,
   40         @IFileService protected readonly fileService: IFileService,
   41         @IOpenerService protected readonly openerService: IOpenerService,
   42         @IDialogService private readonly dialogService: IDialogService,
   43         @IModeService private readonly modeService: IModeService,
   44         @IWorkspacesService private readonly workspacesService: IWorkspacesService,
   45         @ILabelService private readonly labelService: ILabelService
   46     ) { }
   47 
   48     defaultFilePath(schemeFilter = this.getSchemeFilterForWindow()): URI | undefined {
   49 
   50         // Check for last active file first...
   51         let candidate = this.historyService.getLastActiveFile(schemeFilter);
   52 
   53         // ...then for last active file root
   54         if (!candidate) {
   55             candidate = this.historyService.getLastActiveWorkspaceRoot(schemeFilter);
   56         } else {
   57             candidate = candidate && resources.dirname(candidate);
   58         }
   59 
   60         return candidate || undefined;
   61     }
   62 
   63     defaultFolderPath(schemeFilter = this.getSchemeFilterForWindow()): URI | undefined {
   64 
   65         // Check for last active file root first...
   66         let candidate = this.historyService.getLastActiveWorkspaceRoot(schemeFilter);
   67 
   68         // ...then for last active file
   69         if (!candidate) {
   70             candidate = this.historyService.getLastActiveFile(schemeFilter);
   71         }
   72 
   73         return candidate && resources.dirname(candidate) || undefined;
   74     }
   75 
   76     defaultWorkspacePath(schemeFilter = this.getSchemeFilterForWindow()): URI | undefined {
   77 
   78         // Check for current workspace config file first...
   79         if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) {
   80             const configuration = this.contextService.getWorkspace().configuration;
   81             if (configuration && !isUntitledWorkspace(configuration, this.environmentService)) {
   82                 return resources.dirname(configuration) || undefined;
   83             }
   84         }
   85 
   86         // ...then fallback to default file path
   87         return this.defaultFilePath(schemeFilter);
   88     }
   89 
   90     async showSaveConfirm(fileNamesOrResources: (string | URI)[]): Promise<ConfirmResult> {
   91         if (this.environmentService.isExtensionDevelopment && this.environmentService.extensionTestsLocationURI) {
   92             return ConfirmResult.DONT_SAVE; // no veto when we are in extension dev testing mode because we cannot assume we run interactive
   93         }
   94 
   95         return this.doShowSaveConfirm(fileNamesOrResources);
   96     }
   97 
   98     protected async doShowSaveConfirm(fileNamesOrResources: (string | URI)[]): Promise<ConfirmResult> {
   99         if (fileNamesOrResources.length === 0) {
  100             return ConfirmResult.DONT_SAVE;
  101         }
  102 
  103         let message: string;
  104         let detail = nls.localize('saveChangesDetail', "Your changes will be lost if you don't save them.");
  105         if (fileNamesOrResources.length === 1) {
  106             message = nls.localize('saveChangesMessage', "Do you want to save the changes you made to {0}?", typeof fileNamesOrResources[0] === 'string' ? fileNamesOrResources[0] : resources.basename(fileNamesOrResources[0]));
  107         } else {
  108             message = nls.localize('saveChangesMessages', "Do you want to save the changes to the following {0} files?", fileNamesOrResources.length);
  109             detail = getFileNamesMessage(fileNamesOrResources) + '\n' + detail;
  110         }
  111 
  112         const buttons: string[] = [
  113             fileNamesOrResources.length > 1 ? nls.localize({ key: 'saveAll', comment: ['&& denotes a mnemonic'] }, "&&Save All") : nls.localize({ key: 'save', comment: ['&& denotes a mnemonic'] }, "&&Save"),
  114             nls.localize({ key: 'dontSave', comment: ['&& denotes a mnemonic'] }, "Do&&n't Save"),
  115             nls.localize('cancel', "Cancel")
  116         ];
  117 
  118         const { choice } = await this.dialogService.show(Severity.Warning, message, buttons, {
  119             cancelId: 2,
  120             detail
  121         });
  122 
  123         switch (choice) {
  124             case 0: return ConfirmResult.SAVE;
  125             case 1: return ConfirmResult.DONT_SAVE;
  126             default: return ConfirmResult.CANCEL;
  127         }
  128     }
  129 
  130     protected abstract addFileSchemaIfNeeded(schema: string): string[];
  131 
  132     protected async pickFileFolderAndOpenSimplified(schema: string, options: IPickAndOpenOptions, preferNewWindow: boolean): Promise<any> {
  133         const title = nls.localize('openFileOrFolder.title', 'Open File Or Folder');
  134         const availableFileSystems = this.addFileSchemaIfNeeded(schema);
  135 
  136         const uri = await this.pickResource({ canSelectFiles: true, canSelectFolders: true, canSelectMany: false, defaultUri: options.defaultUri, title, availableFileSystems });
  137 
  138         if (uri) {
  139             const stat = await this.fileService.resolve(uri);
  140 
  141             const toOpen: IWindowOpenable = stat.isDirectory ? { folderUri: uri } : { fileUri: uri };
  142             if (!isWorkspaceToOpen(toOpen) && isFileToOpen(toOpen)) {
  143                 // add the picked file into the list of recently opened
  144                 this.workspacesService.addRecentlyOpened([{ fileUri: toOpen.fileUri, label: this.labelService.getUriLabel(toOpen.fileUri) }]);
  145             }
  146 
  147             if (stat.isDirectory || options.forceNewWindow || preferNewWindow) {
  148                 return this.hostService.openWindow([toOpen], { forceNewWindow: options.forceNewWindow });
  149             } else {
  150                 return this.openerService.open(uri, { fromUserGesture: true });
  151             }
  152         }
  153     }
  154 
  155     protected async pickFileAndOpenSimplified(schema: string, options: IPickAndOpenOptions, preferNewWindow: boolean): Promise<any> {
  156         const title = nls.localize('openFile.title', 'Open File');
  157         const availableFileSystems = this.addFileSchemaIfNeeded(schema);
  158 
  159         const uri = await this.pickResource({ canSelectFiles: true, canSelectFolders: false, canSelectMany: false, defaultUri: options.defaultUri, title, availableFileSystems });
  160         if (uri) {
  161             // add the picked file into the list of recently opened
  162             this.workspacesService.addRecentlyOpened([{ fileUri: uri, label: this.labelService.getUriLabel(uri) }]);
  163 
  164             if (options.forceNewWindow || preferNewWindow) {
  165                 return this.hostService.openWindow([{ fileUri: uri }], { forceNewWindow: options.forceNewWindow });
  166             } else {
  167                 return this.openerService.open(uri, { fromUserGesture: true });
  168             }
  169         }
  170     }
  171 
  172     protected async pickFolderAndOpenSimplified(schema: string, options: IPickAndOpenOptions): Promise<any> {
  173         const title = nls.localize('openFolder.title', 'Open Folder');
  174         const availableFileSystems = this.addFileSchemaIfNeeded(schema);
  175 
  176         const uri = await this.pickResource({ canSelectFiles: false, canSelectFolders: true, canSelectMany: false, defaultUri: options.defaultUri, title, availableFileSystems });
  177         if (uri) {
  178             return this.hostService.openWindow([{ folderUri: uri }], { forceNewWindow: options.forceNewWindow });
  179         }
  180     }
  181 
  182     protected async pickWorkspaceAndOpenSimplified(schema: string, options: IPickAndOpenOptions): Promise<any> {
  183         const title = nls.localize('openWorkspace.title', 'Open Workspace');
  184         const filters: FileFilter[] = [{ name: nls.localize('filterName.workspace', 'Workspace'), extensions: [WORKSPACE_EXTENSION] }];
  185         const availableFileSystems = this.addFileSchemaIfNeeded(schema);
  186 
  187         const uri = await this.pickResource({ canSelectFiles: true, canSelectFolders: false, canSelectMany: false, defaultUri: options.defaultUri, title, filters, availableFileSystems });
  188         if (uri) {
  189             return this.hostService.openWindow([{ workspaceUri: uri }], { forceNewWindow: options.forceNewWindow });
  190         }
  191     }
  192 
  193     protected async pickFileToSaveSimplified(schema: string, options: ISaveDialogOptions): Promise<URI | undefined> {
  194         if (!options.availableFileSystems) {
  195             options.availableFileSystems = this.addFileSchemaIfNeeded(schema);
  196         }
  197 
  198         options.title = nls.localize('saveFileAs.title', 'Save As');
  199         return this.saveRemoteResource(options);
  200     }
  201 
  202     protected async showSaveDialogSimplified(schema: string, options: ISaveDialogOptions): Promise<URI | undefined> {
  203         if (!options.availableFileSystems) {
  204             options.availableFileSystems = this.addFileSchemaIfNeeded(schema);
  205         }
  206 
  207         return this.saveRemoteResource(options);
  208     }
  209 
  210     protected async showOpenDialogSimplified(schema: string, options: IOpenDialogOptions): Promise<URI[] | undefined> {
  211         if (!options.availableFileSystems) {
  212             options.availableFileSystems = this.addFileSchemaIfNeeded(schema);
  213         }
  214 
  215         const uri = await this.pickResource(options);
  216 
  217         return uri ? [uri] : undefined;
  218     }
  219 
  220     private pickResource(options: IOpenDialogOptions): Promise<URI | undefined> {
  221         const simpleFileDialog = this.instantiationService.createInstance(SimpleFileDialog);
  222 
  223         return simpleFileDialog.showOpenDialog(options);
  224     }
  225 
  226     private saveRemoteResource(options: ISaveDialogOptions): Promise<URI | undefined> {
  227         const remoteFileDialog = this.instantiationService.createInstance(SimpleFileDialog);
  228 
  229         return remoteFileDialog.showSaveDialog(options);
  230     }
  231 
  232     protected getSchemeFilterForWindow(defaultUriScheme?: string): string {
  233         return !this.environmentService.configuration.remoteAuthority ? (!defaultUriScheme || defaultUriScheme === Schemas.file ? Schemas.file : defaultUriScheme) : REMOTE_HOST_SCHEME;
  234     }
  235 
  236     protected getFileSystemSchema(options: { availableFileSystems?: readonly string[], defaultUri?: URI }): string {
  237         return options.availableFileSystems && options.availableFileSystems[0] || this.getSchemeFilterForWindow(options.defaultUri?.scheme);
  238     }
  239 
  240     abstract pickFileFolderAndOpen(options: IPickAndOpenOptions): Promise<void>;
  241     abstract pickFileAndOpen(options: IPickAndOpenOptions): Promise<void>;
  242     abstract pickFolderAndOpen(options: IPickAndOpenOptions): Promise<void>;
  243     abstract pickWorkspaceAndOpen(options: IPickAndOpenOptions): Promise<void>;
  244     abstract showSaveDialog(options: ISaveDialogOptions): Promise<URI | undefined>;
  245     abstract showOpenDialog(options: IOpenDialogOptions): Promise<URI[] | undefined>;
  246 
  247     abstract pickFileToSave(defaultUri: URI, availableFileSystems?: string[]): Promise<URI | undefined>;
  248 
  249     protected getPickFileToSaveDialogOptions(defaultUri: URI, availableFileSystems?: string[]): ISaveDialogOptions {
  250         const options: ISaveDialogOptions = {
  251             defaultUri,
  252             title: nls.localize('saveAsTitle', "Save As"),
  253             availableFileSystems
  254         };
  255 
  256         interface IFilter { name: string; extensions: string[]; }
  257 
  258         // Build the file filter by using our known languages
  259         const ext: string | undefined = defaultUri ? resources.extname(defaultUri) : undefined;
  260         let matchingFilter: IFilter | undefined;
  261         const registeredLanguageFilters: IFilter[] = coalesce(this.modeService.getRegisteredLanguageNames().map(languageName => {
  262             const extensions = this.modeService.getExtensions(languageName);
  263             if (!extensions || !extensions.length) {
  264                 return null;
  265             }
  266 
  267             const filter: IFilter = { name: languageName, extensions: extensions.slice(0, 10).map(e => trim(e, '.')) };
  268 
  269             if (ext && extensions.indexOf(ext) >= 0) {
  270                 matchingFilter = filter;
  271 
  272                 return null; // matching filter will be added last to the top
  273             }
  274 
  275             return filter;
  276         }));
  277 
  278         // We have no matching filter, e.g. because the language
  279         // is unknown. We still add the extension to the list of
  280         // filters though so that it can be picked
  281         // (https://github.com/microsoft/vscode/issues/96283)
  282         if (!matchingFilter && ext) {
  283             matchingFilter = { name: trim(ext, '.').toUpperCase(), extensions: [trim(ext, '.')] };
  284         }
  285 
  286         // Order of filters is
  287         // - All Files (we MUST do this to fix macOS issue https://github.com/microsoft/vscode/issues/102713)
  288         // - File Extension Match (if any)
  289         // - All Languages
  290         // - No Extension
  291         options.filters = coalesce([
  292             { name: nls.localize('allFiles', "All Files"), extensions: ['*'] },
  293             matchingFilter,
  294             ...registeredLanguageFilters,
  295             { name: nls.localize('noExt', "No Extension"), extensions: [''] }
  296         ]);
  297 
  298         return options;
  299     }
  300 }