Skip to content

Commit a5ea66d

Browse files
authored
Refactor file dialog service to fix layer breaker (microsoft#81171)
Fixes microsoft#81161
1 parent de03947 commit a5ea66d

8 files changed

Lines changed: 386 additions & 288 deletions

File tree

src/vs/workbench/contrib/remote/electron-browser/remote.contribution.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remot
3737
import { IHostService } from 'vs/workbench/services/host/browser/host';
3838
import { RemoteConnectionState, Deprecated_RemoteAuthorityContext, RemoteFileDialogContext } from 'vs/workbench/browser/contextkeys';
3939
import { IDownloadService } from 'vs/platform/download/common/download';
40-
import { OpenLocalFileFolderCommand, OpenLocalFileCommand, OpenLocalFolderCommand, SaveLocalFileCommand } from 'vs/workbench/services/dialogs/browser/remoteFileDialog';
40+
import { OpenLocalFileFolderCommand, OpenLocalFileCommand, OpenLocalFolderCommand, SaveLocalFileCommand } from 'vs/workbench/services/dialogs/browser/simpleFileDialog';
4141

4242
const WINDOW_ACTIONS_COMMAND_ID = 'remote.showActions';
4343
const CLOSE_REMOTE_COMMAND_ID = 'remote.closeRemote';
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
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 { IWindowService, IURIToOpen, FileFilter } from 'vs/platform/windows/common/windows';
8+
import { IPickAndOpenOptions, ISaveDialogOptions, IOpenDialogOptions } 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 } 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 { isWeb } from 'vs/base/common/platform';
22+
import { IOpenerService } from 'vs/platform/opener/common/opener';
23+
24+
export class AbstractFileDialogService {
25+
26+
_serviceBrand: undefined;
27+
28+
constructor(
29+
@IWindowService protected readonly windowService: IWindowService,
30+
@IWorkspaceContextService protected readonly contextService: IWorkspaceContextService,
31+
@IHistoryService protected readonly historyService: IHistoryService,
32+
@IWorkbenchEnvironmentService protected readonly environmentService: IWorkbenchEnvironmentService,
33+
@IInstantiationService protected readonly instantiationService: IInstantiationService,
34+
@IConfigurationService protected readonly configurationService: IConfigurationService,
35+
@IFileService protected readonly fileService: IFileService,
36+
@IOpenerService protected readonly openerService: IOpenerService,
37+
) { }
38+
39+
defaultFilePath(schemeFilter = this.getSchemeFilterForWindow()): URI | undefined {
40+
41+
// Check for last active file first...
42+
let candidate = this.historyService.getLastActiveFile(schemeFilter);
43+
44+
// ...then for last active file root
45+
if (!candidate) {
46+
candidate = this.historyService.getLastActiveWorkspaceRoot(schemeFilter);
47+
} else {
48+
candidate = candidate && resources.dirname(candidate);
49+
}
50+
51+
return candidate || undefined;
52+
}
53+
54+
defaultFolderPath(schemeFilter = this.getSchemeFilterForWindow()): URI | undefined {
55+
56+
// Check for last active file root first...
57+
let candidate = this.historyService.getLastActiveWorkspaceRoot(schemeFilter);
58+
59+
// ...then for last active file
60+
if (!candidate) {
61+
candidate = this.historyService.getLastActiveFile(schemeFilter);
62+
}
63+
64+
return candidate && resources.dirname(candidate) || undefined;
65+
}
66+
67+
defaultWorkspacePath(schemeFilter = this.getSchemeFilterForWindow()): URI | undefined {
68+
69+
// Check for current workspace config file first...
70+
if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) {
71+
const configuration = this.contextService.getWorkspace().configuration;
72+
if (configuration && !isUntitledWorkspace(configuration, this.environmentService)) {
73+
return resources.dirname(configuration) || undefined;
74+
}
75+
}
76+
77+
// ...then fallback to default file path
78+
return this.defaultFilePath(schemeFilter);
79+
}
80+
81+
protected addFileSchemaIfNeeded(schema: string): string[] {
82+
// Include File schema unless the schema is web
83+
// Don't allow untitled schema through.
84+
if (isWeb) {
85+
return schema === Schemas.untitled ? [Schemas.file] : [schema];
86+
} else {
87+
return schema === Schemas.untitled ? [Schemas.file] : (schema !== Schemas.file ? [schema, Schemas.file] : [schema]);
88+
}
89+
}
90+
91+
protected async pickFileFolderAndOpenSimplified(schema: string, options: IPickAndOpenOptions, preferNewWindow: boolean): Promise<any> {
92+
const title = nls.localize('openFileOrFolder.title', 'Open File Or Folder');
93+
const availableFileSystems = this.addFileSchemaIfNeeded(schema);
94+
95+
const uri = await this.pickResource({ canSelectFiles: true, canSelectFolders: true, canSelectMany: false, defaultUri: options.defaultUri, title, availableFileSystems });
96+
97+
if (uri) {
98+
const stat = await this.fileService.resolve(uri);
99+
100+
const toOpen: IURIToOpen = stat.isDirectory ? { folderUri: uri } : { fileUri: uri };
101+
if (stat.isDirectory || options.forceNewWindow || preferNewWindow) {
102+
return this.windowService.openWindow([toOpen], { forceNewWindow: options.forceNewWindow });
103+
} else {
104+
return this.openerService.open(uri);
105+
}
106+
}
107+
}
108+
109+
protected async pickFileAndOpenSimplified(schema: string, options: IPickAndOpenOptions, preferNewWindow: boolean): Promise<any> {
110+
const title = nls.localize('openFile.title', 'Open File');
111+
const availableFileSystems = this.addFileSchemaIfNeeded(schema);
112+
113+
const uri = await this.pickResource({ canSelectFiles: true, canSelectFolders: false, canSelectMany: false, defaultUri: options.defaultUri, title, availableFileSystems });
114+
if (uri) {
115+
if (options.forceNewWindow || preferNewWindow) {
116+
return this.windowService.openWindow([{ fileUri: uri }], { forceNewWindow: options.forceNewWindow });
117+
} else {
118+
return this.openerService.open(uri);
119+
}
120+
}
121+
}
122+
123+
protected async pickFolderAndOpenSimplified(schema: string, options: IPickAndOpenOptions): Promise<any> {
124+
const title = nls.localize('openFolder.title', 'Open Folder');
125+
const availableFileSystems = this.addFileSchemaIfNeeded(schema);
126+
127+
const uri = await this.pickResource({ canSelectFiles: false, canSelectFolders: true, canSelectMany: false, defaultUri: options.defaultUri, title, availableFileSystems });
128+
if (uri) {
129+
return this.windowService.openWindow([{ folderUri: uri }], { forceNewWindow: options.forceNewWindow });
130+
}
131+
}
132+
133+
protected async pickWorkspaceAndOpenSimplified(schema: string, options: IPickAndOpenOptions): Promise<any> {
134+
const title = nls.localize('openWorkspace.title', 'Open Workspace');
135+
const filters: FileFilter[] = [{ name: nls.localize('filterName.workspace', 'Workspace'), extensions: [WORKSPACE_EXTENSION] }];
136+
const availableFileSystems = this.addFileSchemaIfNeeded(schema);
137+
138+
const uri = await this.pickResource({ canSelectFiles: true, canSelectFolders: false, canSelectMany: false, defaultUri: options.defaultUri, title, filters, availableFileSystems });
139+
if (uri) {
140+
return this.windowService.openWindow([{ workspaceUri: uri }], { forceNewWindow: options.forceNewWindow });
141+
}
142+
}
143+
144+
protected async pickFileToSaveSimplified(schema: string, options: ISaveDialogOptions): Promise<URI | undefined> {
145+
if (!options.availableFileSystems) {
146+
options.availableFileSystems = this.addFileSchemaIfNeeded(schema);
147+
}
148+
149+
options.title = nls.localize('saveFileAs.title', 'Save As');
150+
return this.saveRemoteResource(options);
151+
}
152+
153+
protected async showSaveDialogSimplified(schema: string, options: ISaveDialogOptions): Promise<URI | undefined> {
154+
if (!options.availableFileSystems) {
155+
options.availableFileSystems = this.addFileSchemaIfNeeded(schema);
156+
}
157+
158+
return this.saveRemoteResource(options);
159+
}
160+
161+
protected async showOpenDialogSimplified(schema: string, options: IOpenDialogOptions): Promise<URI[] | undefined> {
162+
if (!options.availableFileSystems) {
163+
options.availableFileSystems = this.addFileSchemaIfNeeded(schema);
164+
}
165+
166+
const uri = await this.pickResource(options);
167+
168+
return uri ? [uri] : undefined;
169+
}
170+
171+
private pickResource(options: IOpenDialogOptions): Promise<URI | undefined> {
172+
const simpleFileDialog = this.instantiationService.createInstance(SimpleFileDialog);
173+
174+
return simpleFileDialog.showOpenDialog(options);
175+
}
176+
177+
private saveRemoteResource(options: ISaveDialogOptions): Promise<URI | undefined> {
178+
const remoteFileDialog = this.instantiationService.createInstance(SimpleFileDialog);
179+
180+
return remoteFileDialog.showSaveDialog(options);
181+
}
182+
183+
protected getSchemeFilterForWindow(): string {
184+
return !this.environmentService.configuration.remoteAuthority ? Schemas.file : REMOTE_HOST_SCHEME;
185+
}
186+
187+
protected getFileSystemSchema(options: { availableFileSystems?: string[], defaultUri?: URI }): string {
188+
return options.availableFileSystems && options.availableFileSystems[0] || this.getSchemeFilterForWindow();
189+
}
190+
}
191+
192+
function isUntitledWorkspace(path: URI, environmentService: IWorkbenchEnvironmentService): boolean {
193+
return resources.isEqualOrParent(path, environmentService.untitledWorkspacesHome);
194+
}

0 commit comments

Comments
 (0)