From 44674775e4e2ac92960312fba3ff5806b57bb7a3 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Mon, 17 Jun 2019 17:33:02 +0200 Subject: [PATCH] Add show local option for Save As on remote Fixes https://github.com/microsoft/vscode-remote-release/issues/712 --- src/vs/platform/dialogs/common/dialogs.ts | 5 +++++ .../browser/actions/workspaceActions.ts | 18 ++++++++++++++++++ .../electron-browser/main.contribution.ts | 3 ++- .../dialogs/browser/fileDialogService.ts | 19 +++++++++++++++++++ .../dialogs/browser/remoteFileDialog.ts | 14 +++++++++++--- .../textfile/common/textFileService.ts | 2 +- .../workbench/test/workbenchTestServices.ts | 3 +++ 7 files changed, 59 insertions(+), 5 deletions(-) diff --git a/src/vs/platform/dialogs/common/dialogs.ts b/src/vs/platform/dialogs/common/dialogs.ts index c86051170e0..cb2181914e7 100644 --- a/src/vs/platform/dialogs/common/dialogs.ts +++ b/src/vs/platform/dialogs/common/dialogs.ts @@ -206,6 +206,11 @@ export interface IFileDialogService { */ pickWorkspaceAndOpen(options: IPickAndOpenOptions): Promise; + /** + * Shows a save file file dialog and save the file at the chosen file URI. + */ + pickFileToSave(options: ISaveDialogOptions): Promise; + /** * Shows a save file dialog and returns the chosen file URI. */ diff --git a/src/vs/workbench/browser/actions/workspaceActions.ts b/src/vs/workbench/browser/actions/workspaceActions.ts index ceacc925f41..2c7298ebcdd 100644 --- a/src/vs/workbench/browser/actions/workspaceActions.ts +++ b/src/vs/workbench/browser/actions/workspaceActions.ts @@ -54,6 +54,24 @@ export class OpenLocalFileAction extends Action { } } +export class SaveLocalFileAction extends Action { + + static readonly ID = 'workbench.action.files.saveLocalFile'; + static LABEL = nls.localize('saveLocalFile', "Save Local File..."); + + constructor( + id: string, + label: string, + @IFileDialogService private readonly dialogService: IFileDialogService + ) { + super(id, label); + } + + run(event?: any, data?: ITelemetryData): Promise { + return this.dialogService.pickFileToSave({ availableFileSystems: [Schemas.file] }); + } +} + export class OpenFolderAction extends Action { static readonly ID = 'workbench.action.files.openFolder'; diff --git a/src/vs/workbench/electron-browser/main.contribution.ts b/src/vs/workbench/electron-browser/main.contribution.ts index 5ea030c654a..140f2039603 100644 --- a/src/vs/workbench/electron-browser/main.contribution.ts +++ b/src/vs/workbench/electron-browser/main.contribution.ts @@ -14,7 +14,7 @@ import { isWindows, isLinux, isMacintosh } from 'vs/base/common/platform'; import { KeybindingsReferenceAction, OpenDocumentationUrlAction, OpenIntroductoryVideosUrlAction, OpenTipsAndTricksUrlAction, OpenTwitterUrlAction, OpenRequestFeatureUrlAction, OpenPrivacyStatementUrlAction, OpenLicenseUrlAction, OpenNewsletterSignupUrlAction } from 'vs/workbench/electron-browser/actions/helpActions'; import { ToggleSharedProcessAction, InspectContextKeysAction, ToggleScreencastModeAction, ToggleDevToolsAction } from 'vs/workbench/electron-browser/actions/developerActions'; import { ShowAboutDialogAction, ZoomResetAction, ZoomOutAction, ZoomInAction, ToggleFullScreenAction, CloseCurrentWindowAction, SwitchWindow, NewWindowAction, QuickSwitchWindow, QuickOpenRecentAction, inRecentFilesPickerContextKey, OpenRecentAction, ReloadWindowWithExtensionsDisabledAction, NewWindowTabHandler, ReloadWindowAction, ShowPreviousWindowTabHandler, ShowNextWindowTabHandler, MoveWindowTabToNewWindowHandler, MergeWindowTabsHandlerHandler, ToggleWindowTabsBarHandler } from 'vs/workbench/electron-browser/actions/windowActions'; -import { AddRootFolderAction, GlobalRemoveRootFolderAction, OpenWorkspaceAction, SaveWorkspaceAsAction, OpenWorkspaceConfigFileAction, DuplicateWorkspaceInNewWindowAction, OpenFileFolderAction, OpenFileAction, OpenFolderAction, CloseWorkspaceAction, OpenLocalFileAction, OpenLocalFolderAction, OpenLocalFileFolderAction } from 'vs/workbench/browser/actions/workspaceActions'; +import { AddRootFolderAction, GlobalRemoveRootFolderAction, OpenWorkspaceAction, SaveWorkspaceAsAction, OpenWorkspaceConfigFileAction, DuplicateWorkspaceInNewWindowAction, OpenFileFolderAction, OpenFileAction, OpenFolderAction, CloseWorkspaceAction, OpenLocalFileAction, OpenLocalFolderAction, OpenLocalFileFolderAction, SaveLocalFileAction } from 'vs/workbench/browser/actions/workspaceActions'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { inQuickOpenContext, getQuickNavigateHandler } from 'vs/workbench/browser/parts/quickopen/quickopen'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; @@ -45,6 +45,7 @@ import product from 'vs/platform/product/node/product'; registry.registerWorkbenchAction(new SyncActionDescriptor(OpenLocalFolderAction, OpenLocalFolderAction.ID, OpenLocalFolderAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_O) }, RemoteFileDialogContext), 'File: Open Local Folder...', fileCategory); } + registry.registerWorkbenchAction(new SyncActionDescriptor(SaveLocalFileAction, SaveLocalFileAction.ID, SaveLocalFileAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_S }, RemoteFileDialogContext), 'File: Save Local File...', fileCategory); registry.registerWorkbenchAction(new SyncActionDescriptor(QuickOpenRecentAction, QuickOpenRecentAction.ID, QuickOpenRecentAction.LABEL), 'File: Quick Open Recent...', fileCategory); registry.registerWorkbenchAction(new SyncActionDescriptor(OpenRecentAction, OpenRecentAction.ID, OpenRecentAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_R, mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_R } }), 'File: Open Recent...', fileCategory); registry.registerWorkbenchAction(new SyncActionDescriptor(CloseWorkspaceAction, CloseWorkspaceAction.ID, CloseWorkspaceAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_F) }), 'File: Close Workspace', fileCategory); diff --git a/src/vs/workbench/services/dialogs/browser/fileDialogService.ts b/src/vs/workbench/services/dialogs/browser/fileDialogService.ts index e46f242e947..fefbf28bc6e 100644 --- a/src/vs/workbench/services/dialogs/browser/fileDialogService.ts +++ b/src/vs/workbench/services/dialogs/browser/fileDialogService.ts @@ -187,6 +187,25 @@ export class FileDialogService implements IFileDialogService { return this.windowService.pickWorkspaceAndOpen(this.toNativeOpenDialogOptions(options)); } + async pickFileToSave(options: ISaveDialogOptions): Promise { + const schema = this.getFileSystemSchema(options); + if (this.shouldUseSimplified(schema)) { + if (!options.availableFileSystems) { + options.availableFileSystems = this.ensureFileSchema(schema); // always allow file as well + } + + options.title = nls.localize('saveFileAs.title', 'Save As'); + return this.saveRemoteResource(options); + } + + const result = await this.windowService.showSaveDialog(this.toNativeSaveDialogOptions(options)); + if (result) { + return URI.file(result); + } + + return; + } + private toNativeSaveDialogOptions(options: ISaveDialogOptions): Electron.SaveDialogOptions { return { defaultPath: options.defaultUri && options.defaultUri.fsPath || options.defaultFileName, diff --git a/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts b/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts index f3cdff21d80..06ae69f7b9c 100644 --- a/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts +++ b/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts @@ -23,7 +23,7 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/ import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { equalsIgnoreCase, format, startsWithIgnoreCase } from 'vs/base/common/strings'; -import { OpenLocalFileAction, OpenLocalFileFolderAction, OpenLocalFolderAction } from 'vs/workbench/browser/actions/workspaceActions'; +import { OpenLocalFileAction, OpenLocalFileFolderAction, OpenLocalFolderAction, SaveLocalFileAction } from 'vs/workbench/browser/actions/workspaceActions'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment'; import { isValidBasename } from 'vs/base/common/extpath'; @@ -211,7 +211,12 @@ export class RemoteFileDialog { if (this.options && this.options.availableFileSystems && (this.options.availableFileSystems.length > 1)) { this.filePickBox.customButton = true; this.filePickBox.customLabel = nls.localize('remoteFileDialog.local', 'Show Local'); - const action = this.allowFileSelection ? (this.allowFolderSelection ? OpenLocalFileFolderAction : OpenLocalFileAction) : OpenLocalFolderAction; + let action; + if (isSave) { + action = SaveLocalFileAction; + } else { + action = this.allowFileSelection ? (this.allowFolderSelection ? OpenLocalFileFolderAction : OpenLocalFileAction) : OpenLocalFolderAction; + } const keybinding = this.keybindingService.lookupKeybinding(action.ID); if (keybinding) { const label = keybinding.getLabel(); @@ -251,7 +256,10 @@ export class RemoteFileDialog { } this.options.defaultUri = undefined; this.filePickBox.hide(); - if (this.requiresTrailing) { + if (isSave) { + // Remove defaultUri and filters to get a consistant experience with the keybinding. + this.options.defaultUri = undefined; + this.options.filters = undefined; return this.fileDialogService.showSaveDialog(this.options).then(result => { doResolve(this, result); }); diff --git a/src/vs/workbench/services/textfile/common/textFileService.ts b/src/vs/workbench/services/textfile/common/textFileService.ts index 9b1c25db695..4c092ee588b 100644 --- a/src/vs/workbench/services/textfile/common/textFileService.ts +++ b/src/vs/workbench/services/textfile/common/textFileService.ts @@ -653,7 +653,7 @@ export abstract class TextFileService extends Disposable implements ITextFileSer // Help user to find a name for the file by opening it first await this.editorService.openEditor({ resource, options: { revealIfOpened: true, preserveFocus: true, } }); - return this.fileDialogService.showSaveDialog(this.getSaveDialogOptions(defaultUri)); + return this.fileDialogService.pickFileToSave(this.getSaveDialogOptions(defaultUri)); } private getSaveDialogOptions(defaultUri: URI): ISaveDialogOptions { diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index 35d2c045b22..c33ab89e631 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -439,6 +439,9 @@ export class TestFileDialogService implements IFileDialogService { public pickWorkspaceAndOpen(_options: IPickAndOpenOptions): Promise { return Promise.resolve(0); } + public pickFileToSave(_options: ISaveDialogOptions): Promise { + return Promise.resolve(undefined); + } public showSaveDialog(_options: ISaveDialogOptions): Promise { return Promise.resolve(undefined); } -- GitLab