Skip to content

Commit 4467477

Browse files
committed
Add show local option for Save As on remote
Fixes microsoft/vscode-remote-release#712
1 parent 0ad6f54 commit 4467477

7 files changed

Lines changed: 59 additions & 5 deletions

File tree

src/vs/platform/dialogs/common/dialogs.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,11 @@ export interface IFileDialogService {
206206
*/
207207
pickWorkspaceAndOpen(options: IPickAndOpenOptions): Promise<void>;
208208

209+
/**
210+
* Shows a save file file dialog and save the file at the chosen file URI.
211+
*/
212+
pickFileToSave(options: ISaveDialogOptions): Promise<URI | undefined>;
213+
209214
/**
210215
* Shows a save file dialog and returns the chosen file URI.
211216
*/

src/vs/workbench/browser/actions/workspaceActions.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,24 @@ export class OpenLocalFileAction extends Action {
5454
}
5555
}
5656

57+
export class SaveLocalFileAction extends Action {
58+
59+
static readonly ID = 'workbench.action.files.saveLocalFile';
60+
static LABEL = nls.localize('saveLocalFile', "Save Local File...");
61+
62+
constructor(
63+
id: string,
64+
label: string,
65+
@IFileDialogService private readonly dialogService: IFileDialogService
66+
) {
67+
super(id, label);
68+
}
69+
70+
run(event?: any, data?: ITelemetryData): Promise<any> {
71+
return this.dialogService.pickFileToSave({ availableFileSystems: [Schemas.file] });
72+
}
73+
}
74+
5775
export class OpenFolderAction extends Action {
5876

5977
static readonly ID = 'workbench.action.files.openFolder';

src/vs/workbench/electron-browser/main.contribution.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { isWindows, isLinux, isMacintosh } from 'vs/base/common/platform';
1414
import { KeybindingsReferenceAction, OpenDocumentationUrlAction, OpenIntroductoryVideosUrlAction, OpenTipsAndTricksUrlAction, OpenTwitterUrlAction, OpenRequestFeatureUrlAction, OpenPrivacyStatementUrlAction, OpenLicenseUrlAction, OpenNewsletterSignupUrlAction } from 'vs/workbench/electron-browser/actions/helpActions';
1515
import { ToggleSharedProcessAction, InspectContextKeysAction, ToggleScreencastModeAction, ToggleDevToolsAction } from 'vs/workbench/electron-browser/actions/developerActions';
1616
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';
17-
import { AddRootFolderAction, GlobalRemoveRootFolderAction, OpenWorkspaceAction, SaveWorkspaceAsAction, OpenWorkspaceConfigFileAction, DuplicateWorkspaceInNewWindowAction, OpenFileFolderAction, OpenFileAction, OpenFolderAction, CloseWorkspaceAction, OpenLocalFileAction, OpenLocalFolderAction, OpenLocalFileFolderAction } from 'vs/workbench/browser/actions/workspaceActions';
17+
import { AddRootFolderAction, GlobalRemoveRootFolderAction, OpenWorkspaceAction, SaveWorkspaceAsAction, OpenWorkspaceConfigFileAction, DuplicateWorkspaceInNewWindowAction, OpenFileFolderAction, OpenFileAction, OpenFolderAction, CloseWorkspaceAction, OpenLocalFileAction, OpenLocalFolderAction, OpenLocalFileFolderAction, SaveLocalFileAction } from 'vs/workbench/browser/actions/workspaceActions';
1818
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
1919
import { inQuickOpenContext, getQuickNavigateHandler } from 'vs/workbench/browser/parts/quickopen/quickopen';
2020
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
@@ -45,6 +45,7 @@ import product from 'vs/platform/product/node/product';
4545
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);
4646
}
4747

48+
registry.registerWorkbenchAction(new SyncActionDescriptor(SaveLocalFileAction, SaveLocalFileAction.ID, SaveLocalFileAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_S }, RemoteFileDialogContext), 'File: Save Local File...', fileCategory);
4849
registry.registerWorkbenchAction(new SyncActionDescriptor(QuickOpenRecentAction, QuickOpenRecentAction.ID, QuickOpenRecentAction.LABEL), 'File: Quick Open Recent...', fileCategory);
4950
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);
5051
registry.registerWorkbenchAction(new SyncActionDescriptor(CloseWorkspaceAction, CloseWorkspaceAction.ID, CloseWorkspaceAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_F) }), 'File: Close Workspace', fileCategory);

src/vs/workbench/services/dialogs/browser/fileDialogService.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,25 @@ export class FileDialogService implements IFileDialogService {
187187
return this.windowService.pickWorkspaceAndOpen(this.toNativeOpenDialogOptions(options));
188188
}
189189

190+
async pickFileToSave(options: ISaveDialogOptions): Promise<URI | undefined> {
191+
const schema = this.getFileSystemSchema(options);
192+
if (this.shouldUseSimplified(schema)) {
193+
if (!options.availableFileSystems) {
194+
options.availableFileSystems = this.ensureFileSchema(schema); // always allow file as well
195+
}
196+
197+
options.title = nls.localize('saveFileAs.title', 'Save As');
198+
return this.saveRemoteResource(options);
199+
}
200+
201+
const result = await this.windowService.showSaveDialog(this.toNativeSaveDialogOptions(options));
202+
if (result) {
203+
return URI.file(result);
204+
}
205+
206+
return;
207+
}
208+
190209
private toNativeSaveDialogOptions(options: ISaveDialogOptions): Electron.SaveDialogOptions {
191210
return {
192211
defaultPath: options.defaultUri && options.defaultUri.fsPath || options.defaultFileName,

src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/
2323
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
2424
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
2525
import { equalsIgnoreCase, format, startsWithIgnoreCase } from 'vs/base/common/strings';
26-
import { OpenLocalFileAction, OpenLocalFileFolderAction, OpenLocalFolderAction } from 'vs/workbench/browser/actions/workspaceActions';
26+
import { OpenLocalFileAction, OpenLocalFileFolderAction, OpenLocalFolderAction, SaveLocalFileAction } from 'vs/workbench/browser/actions/workspaceActions';
2727
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
2828
import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment';
2929
import { isValidBasename } from 'vs/base/common/extpath';
@@ -211,7 +211,12 @@ export class RemoteFileDialog {
211211
if (this.options && this.options.availableFileSystems && (this.options.availableFileSystems.length > 1)) {
212212
this.filePickBox.customButton = true;
213213
this.filePickBox.customLabel = nls.localize('remoteFileDialog.local', 'Show Local');
214-
const action = this.allowFileSelection ? (this.allowFolderSelection ? OpenLocalFileFolderAction : OpenLocalFileAction) : OpenLocalFolderAction;
214+
let action;
215+
if (isSave) {
216+
action = SaveLocalFileAction;
217+
} else {
218+
action = this.allowFileSelection ? (this.allowFolderSelection ? OpenLocalFileFolderAction : OpenLocalFileAction) : OpenLocalFolderAction;
219+
}
215220
const keybinding = this.keybindingService.lookupKeybinding(action.ID);
216221
if (keybinding) {
217222
const label = keybinding.getLabel();
@@ -251,7 +256,10 @@ export class RemoteFileDialog {
251256
}
252257
this.options.defaultUri = undefined;
253258
this.filePickBox.hide();
254-
if (this.requiresTrailing) {
259+
if (isSave) {
260+
// Remove defaultUri and filters to get a consistant experience with the keybinding.
261+
this.options.defaultUri = undefined;
262+
this.options.filters = undefined;
255263
return this.fileDialogService.showSaveDialog(this.options).then(result => {
256264
doResolve(this, result);
257265
});

src/vs/workbench/services/textfile/common/textFileService.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -653,7 +653,7 @@ export abstract class TextFileService extends Disposable implements ITextFileSer
653653
// Help user to find a name for the file by opening it first
654654
await this.editorService.openEditor({ resource, options: { revealIfOpened: true, preserveFocus: true, } });
655655

656-
return this.fileDialogService.showSaveDialog(this.getSaveDialogOptions(defaultUri));
656+
return this.fileDialogService.pickFileToSave(this.getSaveDialogOptions(defaultUri));
657657
}
658658

659659
private getSaveDialogOptions(defaultUri: URI): ISaveDialogOptions {

src/vs/workbench/test/workbenchTestServices.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,9 @@ export class TestFileDialogService implements IFileDialogService {
439439
public pickWorkspaceAndOpen(_options: IPickAndOpenOptions): Promise<any> {
440440
return Promise.resolve(0);
441441
}
442+
public pickFileToSave(_options: ISaveDialogOptions): Promise<URI | undefined> {
443+
return Promise.resolve(undefined);
444+
}
442445
public showSaveDialog(_options: ISaveDialogOptions): Promise<URI | undefined> {
443446
return Promise.resolve(undefined);
444447
}

0 commit comments

Comments
 (0)