mirror of
https://github.com/microsoft/vscode.git
synced 2026-09-12 20:12:23 +01:00
Revert "Revert "use dom api to resolve file schema on web""
This reverts commit 1292b973d0.
This commit is contained in:
@@ -0,0 +1,192 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IFileSystemProviderWithFileReadWriteCapability, FileSystemProviderCapabilities, IFileChange, IWatchOptions, IStat, FileOverwriteOptions, FileType, FileDeleteOptions, FileWriteOptions } from 'vs/platform/files/common/files';
|
||||
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { extUri } from 'vs/base/common/resources';
|
||||
|
||||
function split(path: string): [string, string] | undefined {
|
||||
const match = /^(.*)\/([^/]+)$/.exec(path);
|
||||
|
||||
if (!match) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const [, parentPath, name] = match;
|
||||
return [parentPath, name];
|
||||
}
|
||||
|
||||
function isRoot(uri: URI): boolean {
|
||||
return /^(\/[^/]+)\/?$/.test(uri.path);
|
||||
}
|
||||
|
||||
export class HTMLFileSystemProvider implements IFileSystemProviderWithFileReadWriteCapability {
|
||||
|
||||
private readonly files = new Map<string, FileSystemFileHandle>();
|
||||
private readonly directories = new Map<string, FileSystemDirectoryHandle>();
|
||||
|
||||
readonly capabilities: FileSystemProviderCapabilities =
|
||||
FileSystemProviderCapabilities.FileReadWrite
|
||||
| FileSystemProviderCapabilities.PathCaseSensitive;
|
||||
|
||||
readonly onDidChangeCapabilities = Event.None;
|
||||
|
||||
private readonly _onDidChangeFile = new Emitter<readonly IFileChange[]>();
|
||||
readonly onDidChangeFile = this._onDidChangeFile.event;
|
||||
|
||||
private readonly _onDidErrorOccur = new Emitter<string>();
|
||||
readonly onDidErrorOccur = this._onDidErrorOccur.event;
|
||||
|
||||
async readFile(resource: URI): Promise<Uint8Array> {
|
||||
const handle = await this.getFileHandle(resource);
|
||||
|
||||
if (!handle) {
|
||||
throw new Error('File not found.');
|
||||
}
|
||||
|
||||
const file = await handle.getFile();
|
||||
return new Uint8Array(await file.arrayBuffer());
|
||||
}
|
||||
|
||||
async writeFile(resource: URI, content: Uint8Array, opts: FileWriteOptions): Promise<void> {
|
||||
const handle = await this.getFileHandle(resource);
|
||||
|
||||
if (!handle) {
|
||||
throw new Error('File not found.');
|
||||
}
|
||||
|
||||
const writable = await handle.createWritable();
|
||||
await writable.write(content);
|
||||
await writable.close();
|
||||
}
|
||||
|
||||
watch(resource: URI, opts: IWatchOptions): IDisposable {
|
||||
return Disposable.None;
|
||||
}
|
||||
|
||||
async stat(resource: URI): Promise<IStat> {
|
||||
const handler = this.files.get(resource.authority);
|
||||
|
||||
if (handler) {
|
||||
const file = await handler.getFile();
|
||||
|
||||
return {
|
||||
type: FileType.File,
|
||||
mtime: file.lastModified,
|
||||
ctime: 0,
|
||||
size: file.size
|
||||
};
|
||||
}
|
||||
|
||||
if (isRoot(resource)) {
|
||||
return {
|
||||
type: FileType.Directory,
|
||||
mtime: 0,
|
||||
ctime: 0,
|
||||
size: 0
|
||||
};
|
||||
}
|
||||
|
||||
const parent = await this.getParentDirectoryHandle(resource);
|
||||
|
||||
if (!parent) {
|
||||
throw new Error('Stat error: no parent found');
|
||||
}
|
||||
|
||||
const name = extUri.basename(resource);
|
||||
for await (const [childName, child] of parent) {
|
||||
if (childName === name) {
|
||||
if (child.kind === 'file') {
|
||||
const file = await child.getFile();
|
||||
|
||||
return {
|
||||
type: FileType.File,
|
||||
mtime: file.lastModified,
|
||||
ctime: 0,
|
||||
size: file.size
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
type: FileType.Directory,
|
||||
mtime: 0,
|
||||
ctime: 0,
|
||||
size: 0
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error('Stat error: entry not found');
|
||||
}
|
||||
|
||||
mkdir(resource: URI): Promise<void> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
async readdir(resource: URI): Promise<[string, FileType][]> {
|
||||
const parent = await this.getDirectoryHandle(resource);
|
||||
|
||||
if (!parent) {
|
||||
throw new Error('Stat error: no parent found');
|
||||
}
|
||||
|
||||
const result: [string, FileType][] = [];
|
||||
|
||||
for await (const [name, child] of parent) {
|
||||
result.push([name, child.kind === 'file' ? FileType.File : FileType.Directory]);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
delete(resource: URI, opts: FileDeleteOptions): Promise<void> {
|
||||
throw new Error('Method not implemented: delete');
|
||||
}
|
||||
|
||||
rename(from: URI, to: URI, opts: FileOverwriteOptions): Promise<void> {
|
||||
throw new Error('Method not implemented: rename');
|
||||
}
|
||||
|
||||
private async getDirectoryHandle(uri: URI): Promise<FileSystemDirectoryHandle | undefined> {
|
||||
if (isRoot(uri)) {
|
||||
return this.directories.get(uri.authority);
|
||||
}
|
||||
|
||||
const splitResult = split(uri.path);
|
||||
|
||||
if (!splitResult) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const parent = await this.getDirectoryHandle(URI.from({ ...uri, path: splitResult[0] }));
|
||||
return await parent?.getDirectoryHandle(extUri.basename(uri));
|
||||
}
|
||||
|
||||
private async getParentDirectoryHandle(uri: URI): Promise<FileSystemDirectoryHandle | undefined> {
|
||||
return this.getDirectoryHandle(URI.from({ ...uri, path: extUri.dirname(uri).path }));
|
||||
}
|
||||
|
||||
private async getFileHandle(uri: URI): Promise<FileSystemFileHandle | undefined> {
|
||||
const result = this.files.get(uri.authority);
|
||||
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
const parent = await this.getParentDirectoryHandle(uri);
|
||||
const name = extUri.basename(uri);
|
||||
return await parent?.getFileHandle(name);
|
||||
}
|
||||
|
||||
registerFileHandle(uuid: string, handle: FileSystemFileHandle): void {
|
||||
this.files.set(uuid, handle);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._onDidChangeFile.dispose();
|
||||
}
|
||||
}
|
||||
@@ -62,6 +62,7 @@ import { BrowserWindow } from 'vs/workbench/browser/window';
|
||||
import { ITimerService } from 'vs/workbench/services/timer/browser/timerService';
|
||||
import { WorkspaceTrustManagementService } from 'vs/workbench/services/workspaces/common/workspaceTrust';
|
||||
import { IWorkspaceTrustManagementService } from 'vs/platform/workspace/common/workspaceTrust';
|
||||
import { HTMLFileSystemProvider } from 'vs/platform/files/browser/htmlFileSystemProvider';
|
||||
|
||||
class BrowserMain extends Disposable {
|
||||
|
||||
@@ -307,6 +308,8 @@ class BrowserMain extends Disposable {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fileService.registerProvider(Schemas.file, new HTMLFileSystemProvider());
|
||||
}
|
||||
|
||||
private async createStorageService(payload: IWorkspaceInitializationPayload, environmentService: IWorkbenchEnvironmentService, fileService: IFileService, logService: ILogService): Promise<BrowserStorageService> {
|
||||
|
||||
@@ -8,9 +8,17 @@ import { URI } from 'vs/base/common/uri';
|
||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||
import { AbstractFileDialogService } from 'vs/workbench/services/dialogs/browser/abstractFileDialogService';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { memoize } from 'vs/base/common/decorators';
|
||||
import { HTMLFileSystemProvider } from 'vs/platform/files/browser/htmlFileSystemProvider';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
|
||||
export class FileDialogService extends AbstractFileDialogService implements IFileDialogService {
|
||||
|
||||
@memoize
|
||||
private get fileSystemProvider(): HTMLFileSystemProvider {
|
||||
return this.fileService.getProvider(Schemas.file) as HTMLFileSystemProvider;
|
||||
}
|
||||
|
||||
async pickFileFolderAndOpen(options: IPickAndOpenOptions): Promise<any> {
|
||||
const schema = this.getFileSystemSchema(options);
|
||||
|
||||
@@ -18,7 +26,11 @@ export class FileDialogService extends AbstractFileDialogService implements IFil
|
||||
options.defaultUri = await this.defaultFilePath(schema);
|
||||
}
|
||||
|
||||
return this.pickFileFolderAndOpenSimplified(schema, options, false);
|
||||
if (this.shouldUseSimplified(schema)) {
|
||||
return this.pickFileFolderAndOpenSimplified(schema, options, false);
|
||||
}
|
||||
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
async pickFileAndOpen(options: IPickAndOpenOptions): Promise<any> {
|
||||
@@ -28,7 +40,17 @@ export class FileDialogService extends AbstractFileDialogService implements IFil
|
||||
options.defaultUri = await this.defaultFilePath(schema);
|
||||
}
|
||||
|
||||
return this.pickFileAndOpenSimplified(schema, options, false);
|
||||
if (this.shouldUseSimplified(schema)) {
|
||||
return this.pickFileAndOpenSimplified(schema, options, false);
|
||||
}
|
||||
|
||||
const [handle] = await window.showOpenFilePicker({ multiple: false });
|
||||
const uuid = generateUuid();
|
||||
const uri = URI.from({ scheme: Schemas.file, authority: uuid, path: `/${handle.name}` });
|
||||
|
||||
this.fileSystemProvider.registerFileHandle(uuid, handle);
|
||||
|
||||
await this.openerService.open(uri, { fromUserGesture: true, editorOptions: { pinned: true } });
|
||||
}
|
||||
|
||||
async pickFolderAndOpen(options: IPickAndOpenOptions): Promise<any> {
|
||||
@@ -38,7 +60,11 @@ export class FileDialogService extends AbstractFileDialogService implements IFil
|
||||
options.defaultUri = await this.defaultFolderPath(schema);
|
||||
}
|
||||
|
||||
return this.pickFolderAndOpenSimplified(schema, options);
|
||||
if (this.shouldUseSimplified(schema)) {
|
||||
return this.pickFolderAndOpenSimplified(schema, options);
|
||||
}
|
||||
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
async pickWorkspaceAndOpen(options: IPickAndOpenOptions): Promise<void> {
|
||||
@@ -48,27 +74,50 @@ export class FileDialogService extends AbstractFileDialogService implements IFil
|
||||
options.defaultUri = await this.defaultWorkspacePath(schema);
|
||||
}
|
||||
|
||||
return this.pickWorkspaceAndOpenSimplified(schema, options);
|
||||
if (this.shouldUseSimplified(schema)) {
|
||||
return this.pickWorkspaceAndOpenSimplified(schema, options);
|
||||
}
|
||||
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
async pickFileToSave(defaultUri: URI, availableFileSystems?: string[]): Promise<URI | undefined> {
|
||||
const schema = this.getFileSystemSchema({ defaultUri, availableFileSystems });
|
||||
return this.pickFileToSaveSimplified(schema, this.getPickFileToSaveDialogOptions(defaultUri, availableFileSystems));
|
||||
|
||||
if (this.shouldUseSimplified(schema)) {
|
||||
return this.pickFileToSaveSimplified(schema, this.getPickFileToSaveDialogOptions(defaultUri, availableFileSystems));
|
||||
}
|
||||
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
async showSaveDialog(options: ISaveDialogOptions): Promise<URI | undefined> {
|
||||
const schema = this.getFileSystemSchema(options);
|
||||
return this.showSaveDialogSimplified(schema, options);
|
||||
|
||||
if (this.shouldUseSimplified(schema)) {
|
||||
return this.showSaveDialogSimplified(schema, options);
|
||||
}
|
||||
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
async showOpenDialog(options: IOpenDialogOptions): Promise<URI[] | undefined> {
|
||||
const schema = this.getFileSystemSchema(options);
|
||||
return this.showOpenDialogSimplified(schema, options);
|
||||
|
||||
if (this.shouldUseSimplified(schema)) {
|
||||
return this.showOpenDialogSimplified(schema, options);
|
||||
}
|
||||
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
protected addFileSchemaIfNeeded(schema: string): string[] {
|
||||
return schema === Schemas.untitled ? [Schemas.file] : [schema];
|
||||
}
|
||||
|
||||
private shouldUseSimplified(schema: string): boolean {
|
||||
return schema !== Schemas.file;
|
||||
}
|
||||
}
|
||||
|
||||
registerSingleton(IFileDialogService, FileDialogService, true);
|
||||
|
||||
@@ -39,7 +39,7 @@ function defaultUriScheme(environmentService: IWorkbenchEnvironmentService, cont
|
||||
return configuration.scheme;
|
||||
}
|
||||
|
||||
throw new Error('Empty workspace is not supported in browser when there is no remote connection.');
|
||||
return Schemas.file;
|
||||
}
|
||||
|
||||
registerSingleton(IPathService, BrowserPathService, true);
|
||||
|
||||
Reference in New Issue
Block a user