Skip to content

Commit e38b9d0

Browse files
committed
add ExtHostFileSystemInfo which knows what schemes are reserved and which are used, #91697
1 parent 9a2a941 commit e38b9d0

7 files changed

+69
-31
lines changed

src/vs/workbench/api/browser/mainThreadFileSystem.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
*--------------------------------------------------------------------------------------------*/
55

66
import { Emitter, Event } from 'vs/base/common/event';
7-
import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle';
7+
import { IDisposable, dispose, toDisposable, DisposableStore } from 'vs/base/common/lifecycle';
88
import { URI, UriComponents } from 'vs/base/common/uri';
99
import { FileWriteOptions, FileSystemProviderCapabilities, IFileChange, IFileService, IStat, IWatchOptions, FileType, FileOverwriteOptions, FileDeleteOptions, FileOpenOptions, IFileStat, FileOperationError, FileOperationResult, FileSystemProviderErrorCode, IFileSystemProviderWithOpenReadWriteCloseCapability, IFileSystemProviderWithFileReadWriteCapability, IFileSystemProviderWithFileFolderCopyCapability } from 'vs/platform/files/common/files';
1010
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
@@ -16,15 +16,22 @@ export class MainThreadFileSystem implements MainThreadFileSystemShape {
1616

1717
private readonly _proxy: ExtHostFileSystemShape;
1818
private readonly _fileProvider = new Map<number, RemoteFileSystemProvider>();
19+
private readonly _disposables = new DisposableStore();
1920

2021
constructor(
2122
extHostContext: IExtHostContext,
2223
@IFileService private readonly _fileService: IFileService,
2324
) {
2425
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostFileSystem);
26+
27+
const infoProxy = extHostContext.getProxy(ExtHostContext.ExtHostFileSystemInfo);
28+
29+
this._disposables.add(_fileService.onDidChangeFileSystemProviderRegistrations(e => infoProxy.$acceptProviderInfos(e.scheme, e.provider?.capabilities ?? null)));
30+
this._disposables.add(_fileService.onDidChangeFileSystemProviderCapabilities(e => infoProxy.$acceptProviderInfos(e.scheme, e.provider.capabilities)));
2531
}
2632

2733
dispose(): void {
34+
this._disposables.dispose();
2835
dispose(this._fileProvider.values());
2936
this._fileProvider.clear();
3037
}

src/vs/workbench/api/common/extHost.api.impl.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ import { ExtHostWebviewViews } from 'vs/workbench/api/common/extHostWebviewView'
8080
import { ExtHostCustomEditors } from 'vs/workbench/api/common/extHostCustomEditors';
8181
import { ExtHostWebviewPanels } from 'vs/workbench/api/common/extHostWebviewPanels';
8282
import { ExtHostBulkEdits } from 'vs/workbench/api/common/extHostBulkEdits';
83+
import { IExtHostFileSystemInfo } from 'vs/workbench/api/common/extHostFileSystemInfo';
8384

8485
export interface IExtensionApiFactory {
8586
(extension: IExtensionDescription, registry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): typeof vscode;
@@ -92,6 +93,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
9293

9394
// services
9495
const initData = accessor.get(IExtHostInitDataService);
96+
const extHostFileSystemInfo = accessor.get(IExtHostFileSystemInfo);
9597
const extHostConsumerFileSystem = accessor.get(IExtHostConsumerFileSystem);
9698
const extensionService = accessor.get(IExtHostExtensionService);
9799
const extHostWorkspace = accessor.get(IExtHostWorkspace);
@@ -106,6 +108,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
106108
const extHostWindow = accessor.get(IExtHostWindow);
107109

108110
// register addressable instances
111+
rpcProtocol.set(ExtHostContext.ExtHostFileSystemInfo, extHostFileSystemInfo);
109112
rpcProtocol.set(ExtHostContext.ExtHostLogService, <ExtHostLogServiceShape><any>extHostLogService);
110113
rpcProtocol.set(ExtHostContext.ExtHostWorkspace, extHostWorkspace);
111114
rpcProtocol.set(ExtHostContext.ExtHostConfiguration, extHostConfiguration);
@@ -135,7 +138,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
135138
const extHostEditorInsets = rpcProtocol.set(ExtHostContext.ExtHostEditorInsets, new ExtHostEditorInsets(rpcProtocol.getProxy(MainContext.MainThreadEditorInsets), extHostEditors, initData.environment));
136139
const extHostDiagnostics = rpcProtocol.set(ExtHostContext.ExtHostDiagnostics, new ExtHostDiagnostics(rpcProtocol, extHostLogService));
137140
const extHostLanguageFeatures = rpcProtocol.set(ExtHostContext.ExtHostLanguageFeatures, new ExtHostLanguageFeatures(rpcProtocol, uriTransformer, extHostDocuments, extHostCommands, extHostDiagnostics, extHostLogService, extHostApiDeprecation));
138-
const extHostFileSystem = rpcProtocol.set(ExtHostContext.ExtHostFileSystem, new ExtHostFileSystem(rpcProtocol, extHostLanguageFeatures, extHostConsumerFileSystem));
141+
const extHostFileSystem = rpcProtocol.set(ExtHostContext.ExtHostFileSystem, new ExtHostFileSystem(rpcProtocol, extHostLanguageFeatures, extHostFileSystemInfo));
139142
const extHostFileSystemEvent = rpcProtocol.set(ExtHostContext.ExtHostFileSystemEventService, new ExtHostFileSystemEventService(rpcProtocol, extHostLogService, extHostDocumentsAndEditors));
140143
const extHostQuickOpen = rpcProtocol.set(ExtHostContext.ExtHostQuickOpen, new ExtHostQuickOpen(rpcProtocol, extHostWorkspace, extHostCommands));
141144
const extHostSCM = rpcProtocol.set(ExtHostContext.ExtHostSCM, new ExtHostSCM(rpcProtocol, extHostCommands, extHostLogService));

src/vs/workbench/api/common/extHost.common.services.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ import { IExtHostStorage, ExtHostStorage } from 'vs/workbench/api/common/extHost
1919
import { IExtHostTunnelService, ExtHostTunnelService } from 'vs/workbench/api/common/extHostTunnelService';
2020
import { IExtHostApiDeprecationService, ExtHostApiDeprecationService, } from 'vs/workbench/api/common/extHostApiDeprecationService';
2121
import { IExtHostWindow, ExtHostWindow } from 'vs/workbench/api/common/extHostWindow';
22-
import { ExtHostConsumerFileSystem, IExtHostConsumerFileSystem } from 'vs/workbench/api/common/extHostFileSystemConsumer';
22+
import { IExtHostConsumerFileSystem, ExtHostConsumerFileSystem } from 'vs/workbench/api/common/extHostFileSystemConsumer';
23+
import { IExtHostFileSystemInfo, ExtHostFileSystemInfo } from 'vs/workbench/api/common/extHostFileSystemInfo';
2324

2425
registerSingleton(IExtensionStoragePaths, ExtensionStoragePaths);
2526
registerSingleton(IExtHostApiDeprecationService, ExtHostApiDeprecationService);
@@ -29,6 +30,7 @@ registerSingleton(IExtHostConsumerFileSystem, ExtHostConsumerFileSystem);
2930
registerSingleton(IExtHostDebugService, WorkerExtHostDebugService);
3031
registerSingleton(IExtHostDecorations, ExtHostDecorations);
3132
registerSingleton(IExtHostDocumentsAndEditors, ExtHostDocumentsAndEditors);
33+
registerSingleton(IExtHostFileSystemInfo, ExtHostFileSystemInfo);
3234
registerSingleton(IExtHostOutputService, ExtHostOutputService);
3335
registerSingleton(IExtHostSearch, ExtHostSearch);
3436
registerSingleton(IExtHostStorage, ExtHostStorage);

src/vs/workbench/api/common/extHost.protocol.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1036,6 +1036,10 @@ export interface ExtHostWorkspaceShape {
10361036
$handleTextSearchResult(result: search.IRawFileMatch2, requestId: number): void;
10371037
}
10381038

1039+
export interface ExtHostFileSystemInfoShape {
1040+
$acceptProviderInfos(scheme: string, capabilities: number | null): void;
1041+
}
1042+
10391043
export interface ExtHostFileSystemShape {
10401044
$stat(handle: number, resource: UriComponents): Promise<files.IStat>;
10411045
$readdir(handle: number, resource: UriComponents): Promise<[string, files.FileType][]>;
@@ -1787,6 +1791,7 @@ export const ExtHostContext = {
17871791
ExtHostEditors: createExtId<ExtHostEditorsShape>('ExtHostEditors'),
17881792
ExtHostTreeViews: createExtId<ExtHostTreeViewsShape>('ExtHostTreeViews'),
17891793
ExtHostFileSystem: createExtId<ExtHostFileSystemShape>('ExtHostFileSystem'),
1794+
ExtHostFileSystemInfo: createExtId<ExtHostFileSystemInfoShape>('ExtHostFileSystemInfo'),
17901795
ExtHostFileSystemEventService: createExtId<ExtHostFileSystemEventServiceShape>('ExtHostFileSystemEventService'),
17911796
ExtHostLanguageFeatures: createExtId<ExtHostLanguageFeaturesShape>('ExtHostLanguageFeatures'),
17921797
ExtHostQuickOpen: createExtId<ExtHostQuickOpenShape>('ExtHostQuickOpen'),

src/vs/workbench/api/common/extHostFileSystem.ts

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,11 @@ import { IDisposable, toDisposable } from 'vs/base/common/lifecycle';
1111
import { FileChangeType } from 'vs/workbench/api/common/extHostTypes';
1212
import * as typeConverter from 'vs/workbench/api/common/extHostTypeConverters';
1313
import { ExtHostLanguageFeatures } from 'vs/workbench/api/common/extHostLanguageFeatures';
14-
import { Schemas } from 'vs/base/common/network';
1514
import { State, StateMachine, LinkComputer, Edge } from 'vs/editor/common/modes/linkComputer';
1615
import { commonPrefixLength } from 'vs/base/common/strings';
1716
import { CharCode } from 'vs/base/common/charCode';
1817
import { VSBuffer } from 'vs/base/common/buffer';
19-
import { IExtHostConsumerFileSystem } from 'vs/workbench/api/common/extHostFileSystemConsumer';
18+
import { IExtHostFileSystemInfo } from 'vs/workbench/api/common/extHostFileSystemInfo';
2019

2120
class FsLinkProvider {
2221

@@ -114,17 +113,14 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape {
114113
private readonly _proxy: MainThreadFileSystemShape;
115114
private readonly _linkProvider = new FsLinkProvider();
116115
private readonly _fsProvider = new Map<number, vscode.FileSystemProvider>();
117-
private readonly _usedSchemes = new Set<string>();
116+
private readonly _registeredSchemes = new Set<string>();
118117
private readonly _watches = new Map<number, IDisposable>();
119118

120119
private _linkProviderRegistration?: IDisposable;
121120
private _handlePool: number = 0;
122121

123-
constructor(mainContext: IMainContext, private _extHostLanguageFeatures: ExtHostLanguageFeatures, private readonly _fileSystemConsumer: IExtHostConsumerFileSystem) {
122+
constructor(mainContext: IMainContext, private _extHostLanguageFeatures: ExtHostLanguageFeatures, private _extHostFileSystemInfo: IExtHostFileSystemInfo) {
124123
this._proxy = mainContext.getProxy(MainContext.MainThreadFileSystem);
125-
126-
// register used schemes
127-
Object.keys(Schemas).forEach(scheme => this._usedSchemes.add(scheme));
128124
}
129125

130126
dispose(): void {
@@ -139,18 +135,16 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape {
139135

140136
registerFileSystemProvider(scheme: string, provider: vscode.FileSystemProvider, options: { isCaseSensitive?: boolean, isReadonly?: boolean } = {}) {
141137

142-
if (this._usedSchemes.has(scheme)) {
138+
if (this._registeredSchemes.has(scheme) || !this._extHostFileSystemInfo.isFreeScheme(scheme)) {
143139
throw new Error(`a provider for the scheme '${scheme}' is already registered`);
144140
}
145141

146-
const schemeRegistration = this._fileSystemConsumer._registerScheme(scheme, options);
147-
148142
//
149143
this._registerLinkProviderIfNotYetRegistered();
150144

151145
const handle = this._handlePool++;
152146
this._linkProvider.add(scheme);
153-
this._usedSchemes.add(scheme);
147+
this._registeredSchemes.add(scheme);
154148
this._fsProvider.set(handle, provider);
155149

156150
let capabilities = files.FileSystemProviderCapabilities.FileReadWrite;
@@ -200,9 +194,8 @@ export class ExtHostFileSystem implements ExtHostFileSystemShape {
200194

201195
return toDisposable(() => {
202196
subscription.dispose();
203-
schemeRegistration.dispose();
204197
this._linkProvider.delete(scheme);
205-
this._usedSchemes.delete(scheme);
198+
this._registeredSchemes.delete(scheme);
206199
this._fsProvider.delete(handle);
207200
this._proxy.$unregisterProvider(handle);
208201
});

src/vs/workbench/api/common/extHostFileSystemConsumer.ts

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,18 @@ import { FileSystemError } from 'vs/workbench/api/common/extHostTypes';
1010
import { VSBuffer } from 'vs/base/common/buffer';
1111
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
1212
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
13-
import { IDisposable, toDisposable } from 'vs/base/common/lifecycle';
13+
import { IExtHostFileSystemInfo } from 'vs/workbench/api/common/extHostFileSystemInfo';
1414

1515
export class ExtHostConsumerFileSystem implements vscode.FileSystem {
1616

1717
readonly _serviceBrand: undefined;
1818

1919
private readonly _proxy: MainThreadFileSystemShape;
2020

21-
private readonly _schemes = new Map<string, { readonly isReadonly?: boolean }>();
22-
23-
constructor(@IExtHostRpcService extHostRpc: IExtHostRpcService) {
21+
constructor(
22+
@IExtHostRpcService extHostRpc: IExtHostRpcService,
23+
@IExtHostFileSystemInfo private readonly _fileSystemInfo: IExtHostFileSystemInfo,
24+
) {
2425
this._proxy = extHostRpc.getProxy(MainContext.MainThreadFileSystem);
2526
}
2627

@@ -49,9 +50,9 @@ export class ExtHostConsumerFileSystem implements vscode.FileSystem {
4950
return this._proxy.$copy(source, destination, { ...{ overwrite: false }, ...options }).catch(ExtHostConsumerFileSystem._handleError);
5051
}
5152
isWritableFileSystem(scheme: string): boolean | undefined {
52-
const entry = this._schemes.get(scheme);
53-
if (entry) {
54-
return !entry.isReadonly;
53+
const capabilities = this._fileSystemInfo.getCapabilities(scheme);
54+
if (typeof capabilities === 'number') {
55+
return !(capabilities & files.FileSystemProviderCapabilities.Readonly);
5556
}
5657
return undefined;
5758
}
@@ -79,14 +80,6 @@ export class ExtHostConsumerFileSystem implements vscode.FileSystem {
7980
default: throw new FileSystemError(err.message, err.name as files.FileSystemProviderErrorCode);
8081
}
8182
}
82-
83-
/* internal */ _registerScheme(scheme: string, options: { readonly isReadonly?: boolean }): IDisposable {
84-
this._schemes.set(scheme, options);
85-
86-
return toDisposable(() => {
87-
return this._schemes.delete(scheme);
88-
});
89-
}
9083
}
9184

9285
export interface IExtHostConsumerFileSystem extends ExtHostConsumerFileSystem { }
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
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 { Schemas } from 'vs/base/common/network';
7+
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
8+
import { ExtHostFileSystemInfoShape } from 'vs/workbench/api/common/extHost.protocol';
9+
10+
export class ExtHostFileSystemInfo implements ExtHostFileSystemInfoShape {
11+
12+
declare readonly _serviceBrand: undefined;
13+
14+
private readonly _systemSchemes = new Set(Object.keys(Schemas));
15+
private readonly _providerInfo = new Map<string, number>();
16+
17+
$acceptProviderInfos(scheme: string, capabilities: number | null): void {
18+
if (capabilities === null) {
19+
this._providerInfo.delete(scheme);
20+
} else {
21+
this._providerInfo.set(scheme, capabilities);
22+
}
23+
}
24+
25+
isFreeScheme(scheme: string): boolean {
26+
return !this._providerInfo.has(scheme) && !this._systemSchemes.has(scheme);
27+
}
28+
29+
getCapabilities(scheme: string): number | undefined {
30+
return this._providerInfo.get(scheme);
31+
}
32+
}
33+
34+
export interface IExtHostFileSystemInfo extends ExtHostFileSystemInfo { }
35+
export const IExtHostFileSystemInfo = createDecorator<IExtHostFileSystemInfo>('IExtHostFileSystemInfo');

0 commit comments

Comments
 (0)