mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-12 02:58:31 -05:00
297 lines
13 KiB
TypeScript
297 lines
13 KiB
TypeScript
/*---------------------------------------------------------------------------------------------
|
|
* 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 { IWindowOpenable, isWorkspaceToOpen, isFileToOpen } from 'vs/platform/windows/common/windows';
|
|
import { IPickAndOpenOptions, ISaveDialogOptions, IOpenDialogOptions, FileFilter, IFileDialogService, IDialogService, ConfirmResult, getFileNamesMessage } 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, isUntitledWorkspace, IWorkspacesService } 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 { IOpenerService } from 'vs/platform/opener/common/opener';
|
|
import { IHostService } from 'vs/workbench/services/host/browser/host';
|
|
import Severity from 'vs/base/common/severity';
|
|
import { coalesce } from 'vs/base/common/arrays';
|
|
import { trim } from 'vs/base/common/strings';
|
|
import { IModeService } from 'vs/editor/common/services/modeService';
|
|
import { ILabelService } from 'vs/platform/label/common/label';
|
|
|
|
export abstract class AbstractFileDialogService implements IFileDialogService {
|
|
|
|
_serviceBrand: undefined;
|
|
|
|
constructor(
|
|
@IHostService protected readonly hostService: IHostService,
|
|
@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,
|
|
@IDialogService private readonly dialogService: IDialogService,
|
|
@IModeService private readonly modeService: IModeService,
|
|
@IWorkspacesService private readonly workspacesService: IWorkspacesService,
|
|
@ILabelService private readonly labelService: ILabelService
|
|
) { }
|
|
|
|
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);
|
|
}
|
|
|
|
async showSaveConfirm(fileNamesOrResources: (string | URI)[]): Promise<ConfirmResult> {
|
|
if (this.environmentService.isExtensionDevelopment && this.environmentService.extensionTestsLocationURI) {
|
|
return ConfirmResult.DONT_SAVE; // no veto when we are in extension dev testing mode because we cannot assume we run interactive
|
|
}
|
|
|
|
return this.doShowSaveConfirm(fileNamesOrResources);
|
|
}
|
|
|
|
protected async doShowSaveConfirm(fileNamesOrResources: (string | URI)[]): Promise<ConfirmResult> {
|
|
if (fileNamesOrResources.length === 0) {
|
|
return ConfirmResult.DONT_SAVE;
|
|
}
|
|
|
|
let message: string;
|
|
let detail = nls.localize('saveChangesDetail', "Your changes will be lost if you don't save them.");
|
|
if (fileNamesOrResources.length === 1) {
|
|
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]));
|
|
} else {
|
|
message = nls.localize('saveChangesMessages', "Do you want to save the changes to the following {0} files?", fileNamesOrResources.length);
|
|
detail = getFileNamesMessage(fileNamesOrResources) + '\n' + detail;
|
|
}
|
|
|
|
const buttons: string[] = [
|
|
fileNamesOrResources.length > 1 ? nls.localize({ key: 'saveAll', comment: ['&& denotes a mnemonic'] }, "&&Save All") : nls.localize({ key: 'save', comment: ['&& denotes a mnemonic'] }, "&&Save"),
|
|
nls.localize({ key: 'dontSave', comment: ['&& denotes a mnemonic'] }, "Do&&n't Save"),
|
|
nls.localize('cancel', "Cancel")
|
|
];
|
|
|
|
const { choice } = await this.dialogService.show(Severity.Warning, message, buttons, {
|
|
cancelId: 2,
|
|
detail
|
|
});
|
|
|
|
switch (choice) {
|
|
case 0: return ConfirmResult.SAVE;
|
|
case 1: return ConfirmResult.DONT_SAVE;
|
|
default: return ConfirmResult.CANCEL;
|
|
}
|
|
}
|
|
|
|
protected abstract addFileSchemaIfNeeded(schema: string): string[];
|
|
|
|
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: IWindowOpenable = stat.isDirectory ? { folderUri: uri } : { fileUri: uri };
|
|
if (!isWorkspaceToOpen(toOpen) && isFileToOpen(toOpen)) {
|
|
// add the picked file into the list of recently opened
|
|
this.workspacesService.addRecentlyOpened([{ fileUri: toOpen.fileUri, label: this.labelService.getUriLabel(toOpen.fileUri) }]);
|
|
}
|
|
|
|
if (stat.isDirectory || options.forceNewWindow || preferNewWindow) {
|
|
return this.hostService.openWindow([toOpen], { forceNewWindow: options.forceNewWindow });
|
|
} else {
|
|
return this.openerService.open(uri, { fromUserGesture: true });
|
|
}
|
|
}
|
|
}
|
|
|
|
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) {
|
|
// add the picked file into the list of recently opened
|
|
this.workspacesService.addRecentlyOpened([{ fileUri: uri, label: this.labelService.getUriLabel(uri) }]);
|
|
|
|
if (options.forceNewWindow || preferNewWindow) {
|
|
return this.hostService.openWindow([{ fileUri: uri }], { forceNewWindow: options.forceNewWindow });
|
|
} else {
|
|
return this.openerService.open(uri, { fromUserGesture: true });
|
|
}
|
|
}
|
|
}
|
|
|
|
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.hostService.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.hostService.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?: readonly string[], defaultUri?: URI }): string {
|
|
return options.availableFileSystems && options.availableFileSystems[0] || this.getSchemeFilterForWindow();
|
|
}
|
|
|
|
abstract pickFileFolderAndOpen(options: IPickAndOpenOptions): Promise<void>;
|
|
abstract pickFileAndOpen(options: IPickAndOpenOptions): Promise<void>;
|
|
abstract pickFolderAndOpen(options: IPickAndOpenOptions): Promise<void>;
|
|
abstract pickWorkspaceAndOpen(options: IPickAndOpenOptions): Promise<void>;
|
|
abstract showSaveDialog(options: ISaveDialogOptions): Promise<URI | undefined>;
|
|
abstract showOpenDialog(options: IOpenDialogOptions): Promise<URI[] | undefined>;
|
|
|
|
abstract pickFileToSave(defaultUri: URI, availableFileSystems?: string[]): Promise<URI | undefined>;
|
|
|
|
protected getPickFileToSaveDialogOptions(defaultUri: URI, availableFileSystems?: string[]): ISaveDialogOptions {
|
|
const options: ISaveDialogOptions = {
|
|
defaultUri,
|
|
title: nls.localize('saveAsTitle', "Save As"),
|
|
availableFileSystems,
|
|
};
|
|
|
|
interface IFilter { name: string; extensions: string[]; }
|
|
|
|
// Build the file filter by using our known languages
|
|
const ext: string | undefined = defaultUri ? resources.extname(defaultUri) : undefined;
|
|
let matchingFilter: IFilter | undefined;
|
|
const filters: IFilter[] = coalesce(this.modeService.getRegisteredLanguageNames().map(languageName => {
|
|
const extensions = this.modeService.getExtensions(languageName);
|
|
if (!extensions || !extensions.length) {
|
|
return null;
|
|
}
|
|
|
|
const filter: IFilter = { name: languageName, extensions: extensions.slice(0, 10).map(e => trim(e, '.')) };
|
|
|
|
if (ext && extensions.indexOf(ext) >= 0) {
|
|
matchingFilter = filter;
|
|
|
|
return null; // matching filter will be added last to the top
|
|
}
|
|
|
|
return filter;
|
|
}));
|
|
|
|
// Filters are a bit weird on Windows, based on having a match or not:
|
|
// Match: we put the matching filter first so that it shows up selected and the all files last
|
|
// No match: we put the all files filter first
|
|
const allFilesFilter = { name: nls.localize('allFiles', "All Files"), extensions: ['*'] };
|
|
if (matchingFilter) {
|
|
filters.unshift(matchingFilter);
|
|
filters.unshift(allFilesFilter);
|
|
} else {
|
|
filters.unshift(allFilesFilter);
|
|
}
|
|
|
|
// Allow to save file without extension
|
|
filters.push({ name: nls.localize('noExt', "No Extension"), extensions: [''] });
|
|
|
|
options.filters = filters;
|
|
|
|
return options;
|
|
}
|
|
}
|