@theia/filesystem 1.65.0-next.6 → 1.65.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/browser/download/file-download-command-contribution.d.ts +1 -1
- package/lib/browser/download/file-download-command-contribution.d.ts.map +1 -1
- package/lib/browser/download/file-download-command-contribution.js +3 -3
- package/lib/browser/download/file-download-command-contribution.js.map +1 -1
- package/lib/browser/download/file-download-frontend-module.d.ts.map +1 -1
- package/lib/browser/download/file-download-frontend-module.js +2 -1
- package/lib/browser/download/file-download-frontend-module.js.map +1 -1
- package/lib/browser/download/file-download-service.d.ts +2 -10
- package/lib/browser/download/file-download-service.d.ts.map +1 -1
- package/lib/browser/download/file-download-service.js +8 -7
- package/lib/browser/download/file-download-service.js.map +1 -1
- package/lib/browser/file-resource.js +1 -1
- package/lib/browser/file-resource.js.map +1 -1
- package/lib/browser/file-service.d.ts +1 -1
- package/lib/browser/file-service.d.ts.map +1 -1
- package/lib/browser/file-service.js +1 -1
- package/lib/browser/file-service.js.map +1 -1
- package/lib/browser/file-tree/file-tree-widget.d.ts +1 -1
- package/lib/browser/file-tree/file-tree-widget.d.ts.map +1 -1
- package/lib/browser/file-tree/file-tree-widget.js +3 -3
- package/lib/browser/file-tree/file-tree-widget.js.map +1 -1
- package/lib/browser/filesystem-frontend-contribution.d.ts +5 -4
- package/lib/browser/filesystem-frontend-contribution.d.ts.map +1 -1
- package/lib/browser/filesystem-frontend-contribution.js +5 -5
- package/lib/browser/filesystem-frontend-contribution.js.map +1 -1
- package/lib/browser/filesystem-frontend-module.d.ts.map +1 -1
- package/lib/browser/filesystem-frontend-module.js +4 -3
- package/lib/browser/filesystem-frontend-module.js.map +1 -1
- package/lib/browser/filesystem-saveable-service.d.ts.map +1 -1
- package/lib/browser/filesystem-saveable-service.js +5 -1
- package/lib/browser/filesystem-saveable-service.js.map +1 -1
- package/lib/browser/index.d.ts +0 -1
- package/lib/browser/index.d.ts.map +1 -1
- package/lib/browser/index.js +0 -1
- package/lib/browser/index.js.map +1 -1
- package/lib/browser/{file-upload-service.d.ts → upload/file-upload-service-impl.d.ts} +16 -52
- package/lib/browser/upload/file-upload-service-impl.d.ts.map +1 -0
- package/lib/browser/{file-upload-service.js → upload/file-upload-service-impl.js} +27 -27
- package/lib/browser/upload/file-upload-service-impl.js.map +1 -0
- package/lib/browser-only/browser-only-filesystem-frontend-module.d.ts.map +1 -1
- package/lib/browser-only/browser-only-filesystem-frontend-module.js +8 -0
- package/lib/browser-only/browser-only-filesystem-frontend-module.js.map +1 -1
- package/lib/browser-only/download/file-download-command-contribution.d.ts +15 -0
- package/lib/browser-only/download/file-download-command-contribution.d.ts.map +1 -0
- package/lib/browser-only/download/file-download-command-contribution.js +55 -0
- package/lib/browser-only/download/file-download-command-contribution.js.map +1 -0
- package/lib/browser-only/download/file-download-frontend-module.d.ts +4 -0
- package/lib/browser-only/download/file-download-frontend-module.d.ts.map +1 -0
- package/lib/browser-only/download/file-download-frontend-module.js +27 -0
- package/lib/browser-only/download/file-download-frontend-module.js.map +1 -0
- package/lib/browser-only/download/file-download-service.d.ts +86 -0
- package/lib/browser-only/download/file-download-service.d.ts.map +1 -0
- package/lib/browser-only/download/file-download-service.js +551 -0
- package/lib/browser-only/download/file-download-service.js.map +1 -0
- package/lib/browser-only/file-search.d.ts +38 -0
- package/lib/browser-only/file-search.d.ts.map +1 -0
- package/lib/browser-only/file-search.js +153 -0
- package/lib/browser-only/file-search.js.map +1 -0
- package/lib/browser-only/opfs-filesystem-initialization.d.ts +4 -2
- package/lib/browser-only/opfs-filesystem-initialization.d.ts.map +1 -1
- package/lib/browser-only/opfs-filesystem-initialization.js +4 -1
- package/lib/browser-only/opfs-filesystem-initialization.js.map +1 -1
- package/lib/browser-only/opfs-filesystem-provider.d.ts +89 -12
- package/lib/browser-only/opfs-filesystem-provider.d.ts.map +1 -1
- package/lib/browser-only/opfs-filesystem-provider.js +345 -181
- package/lib/browser-only/opfs-filesystem-provider.js.map +1 -1
- package/lib/browser-only/upload/file-upload-service-impl.d.ts +67 -0
- package/lib/browser-only/upload/file-upload-service-impl.d.ts.map +1 -0
- package/lib/browser-only/upload/file-upload-service-impl.js +328 -0
- package/lib/browser-only/upload/file-upload-service-impl.js.map +1 -0
- package/lib/common/download/file-download.d.ts +17 -0
- package/lib/common/download/file-download.d.ts.map +1 -0
- package/lib/common/download/{file-download-data.js → file-download.js} +3 -2
- package/lib/common/download/file-download.js.map +1 -0
- package/lib/common/files.d.ts +8 -1
- package/lib/common/files.d.ts.map +1 -1
- package/lib/common/files.js +35 -1
- package/lib/common/files.js.map +1 -1
- package/lib/{browser → common}/filesystem-preferences.d.ts +3 -1
- package/lib/common/filesystem-preferences.d.ts.map +1 -0
- package/lib/{browser → common}/filesystem-preferences.js +17 -11
- package/lib/common/filesystem-preferences.js.map +1 -0
- package/lib/common/index.d.ts +1 -0
- package/lib/common/index.d.ts.map +1 -1
- package/lib/common/index.js +1 -0
- package/lib/common/index.js.map +1 -1
- package/lib/common/io.js +7 -1
- package/lib/common/io.js.map +1 -1
- package/lib/common/upload/file-upload.d.ts +45 -0
- package/lib/common/upload/file-upload.d.ts.map +1 -0
- package/{src/common/download/file-download-data.ts → lib/common/upload/file-upload.js} +6 -13
- package/lib/common/upload/file-upload.js.map +1 -0
- package/lib/node/disk-file-system-provider.d.ts.map +1 -1
- package/lib/node/disk-file-system-provider.js +2 -4
- package/lib/node/disk-file-system-provider.js.map +1 -1
- package/lib/node/download/file-download-handler.js +2 -2
- package/lib/node/download/file-download-handler.js.map +1 -1
- package/lib/node/filesystem-backend-module.d.ts.map +1 -1
- package/lib/node/filesystem-backend-module.js +3 -1
- package/lib/node/filesystem-backend-module.js.map +1 -1
- package/lib/node/parcel-watcher/parcel-filesystem-service.d.ts +2 -2
- package/lib/node/parcel-watcher/parcel-filesystem-service.d.ts.map +1 -1
- package/lib/node/parcel-watcher/parcel-filesystem-service.js.map +1 -1
- package/lib/node/upload/node-file-upload-service.d.ts.map +1 -0
- package/lib/node/{node-file-upload-service.js → upload/node-file-upload-service.js} +1 -1
- package/lib/node/upload/node-file-upload-service.js.map +1 -0
- package/package.json +11 -5
- package/src/browser/download/file-download-command-contribution.ts +1 -1
- package/src/browser/download/file-download-frontend-module.ts +3 -2
- package/src/browser/download/file-download-service.ts +7 -12
- package/src/browser/file-resource.ts +1 -1
- package/src/browser/file-service.ts +1 -1
- package/src/browser/file-tree/file-tree-widget.tsx +1 -1
- package/src/browser/filesystem-frontend-contribution.ts +4 -5
- package/src/browser/filesystem-frontend-module.ts +4 -3
- package/src/browser/filesystem-saveable-service.ts +5 -1
- package/src/browser/index.ts +0 -1
- package/src/browser/{file-upload-service.ts → upload/file-upload-service-impl.ts} +31 -72
- package/src/browser-only/browser-only-filesystem-frontend-module.ts +10 -0
- package/src/browser-only/download/file-download-command-contribution.ts +56 -0
- package/src/browser-only/download/file-download-frontend-module.ts +26 -0
- package/src/browser-only/download/file-download-service.ts +726 -0
- package/src/browser-only/file-search.ts +170 -0
- package/src/browser-only/opfs-filesystem-initialization.ts +7 -4
- package/src/browser-only/opfs-filesystem-provider.ts +402 -189
- package/src/browser-only/upload/file-upload-service-impl.ts +408 -0
- package/src/common/download/file-download.ts +40 -0
- package/src/common/files.ts +42 -1
- package/src/{browser → common}/filesystem-preferences.ts +14 -14
- package/src/common/index.ts +1 -0
- package/src/common/io.ts +6 -1
- package/src/common/upload/file-upload.ts +65 -0
- package/src/node/disk-file-system-provider.ts +3 -4
- package/src/node/download/file-download-handler.ts +1 -1
- package/src/node/filesystem-backend-module.ts +3 -1
- package/src/node/parcel-watcher/parcel-filesystem-service.ts +2 -2
- package/src/node/{node-file-upload-service.ts → upload/node-file-upload-service.ts} +1 -1
- package/lib/browser/file-upload-service.d.ts.map +0 -1
- package/lib/browser/file-upload-service.js.map +0 -1
- package/lib/browser/filesystem-preferences.d.ts.map +0 -1
- package/lib/browser/filesystem-preferences.js.map +0 -1
- package/lib/common/download/file-download-data.d.ts +0 -7
- package/lib/common/download/file-download-data.d.ts.map +0 -1
- package/lib/common/download/file-download-data.js.map +0 -1
- package/lib/node/node-file-upload-service.d.ts.map +0 -1
- package/lib/node/node-file-upload-service.js.map +0 -1
- /package/lib/node/{node-file-upload-service.d.ts → upload/node-file-upload-service.d.ts} +0 -0
|
@@ -21,36 +21,76 @@ import {
|
|
|
21
21
|
FileSystemProviderError,
|
|
22
22
|
FileSystemProviderErrorCode,
|
|
23
23
|
FileSystemProviderWithFileReadWriteCapability,
|
|
24
|
-
|
|
24
|
+
FileSystemProviderWithFileFolderCopyCapability,
|
|
25
|
+
FileSystemProviderWithOpenReadWriteCloseCapability,
|
|
26
|
+
FileType, FileWriteOptions, Stat, WatchOptions, createFileSystemProviderError,
|
|
27
|
+
FileOpenOptions, FileUpdateOptions, FileUpdateResult,
|
|
28
|
+
type FileReadStreamOptions
|
|
25
29
|
} from '../common/files';
|
|
26
|
-
import { Emitter, Event, URI, Disposable,
|
|
30
|
+
import { Emitter, Event, URI, Disposable, DisposableCollection, type CancellationToken } from '@theia/core';
|
|
31
|
+
import { EncodingService } from '@theia/core/lib/common/encoding-service';
|
|
32
|
+
import { BinaryBuffer } from '@theia/core/lib/common/buffer';
|
|
33
|
+
import { TextDocumentContentChangeEvent } from '@theia/core/shared/vscode-languageserver-protocol';
|
|
34
|
+
import { TextDocument } from 'vscode-languageserver-textdocument';
|
|
35
|
+
import { OPFSFileSystem, WatchEventType, type FileStat, type OPFSError, type WatchEvent } from 'opfs-worker';
|
|
27
36
|
import { OPFSInitialization } from './opfs-filesystem-initialization';
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
isDirectory?: boolean;
|
|
32
|
-
create?: boolean;
|
|
33
|
-
}
|
|
37
|
+
import { ReadableStreamEvents, newWriteableStream } from '@theia/core/lib/common/stream';
|
|
38
|
+
import { readFileIntoStream } from '../common/io';
|
|
39
|
+
import { FileUri } from '@theia/core/lib/common/file-uri';
|
|
34
40
|
|
|
35
41
|
@injectable()
|
|
36
|
-
export class OPFSFileSystemProvider implements
|
|
37
|
-
|
|
42
|
+
export class OPFSFileSystemProvider implements Disposable,
|
|
43
|
+
FileSystemProviderWithFileReadWriteCapability,
|
|
44
|
+
FileSystemProviderWithOpenReadWriteCloseCapability,
|
|
45
|
+
FileSystemProviderWithFileFolderCopyCapability {
|
|
46
|
+
|
|
47
|
+
private readonly BUFFER_SIZE = 64 * 1024;
|
|
48
|
+
|
|
49
|
+
capabilities: FileSystemProviderCapabilities =
|
|
50
|
+
FileSystemProviderCapabilities.FileReadWrite |
|
|
51
|
+
FileSystemProviderCapabilities.FileOpenReadWriteClose |
|
|
52
|
+
FileSystemProviderCapabilities.FileFolderCopy |
|
|
53
|
+
FileSystemProviderCapabilities.Update;
|
|
54
|
+
|
|
38
55
|
onDidChangeCapabilities: Event<void> = Event.None;
|
|
39
56
|
|
|
40
57
|
private readonly onDidChangeFileEmitter = new Emitter<readonly FileChange[]>();
|
|
58
|
+
|
|
41
59
|
readonly onDidChangeFile = this.onDidChangeFileEmitter.event;
|
|
42
|
-
onFileWatchError: Event<void> = Event.None;
|
|
60
|
+
readonly onFileWatchError: Event<void> = Event.None;
|
|
43
61
|
|
|
44
62
|
@inject(OPFSInitialization)
|
|
45
63
|
protected readonly initialization: OPFSInitialization;
|
|
46
64
|
|
|
47
|
-
|
|
65
|
+
@inject(EncodingService)
|
|
66
|
+
protected readonly encodingService: EncodingService;
|
|
67
|
+
|
|
68
|
+
private fs!: OPFSFileSystem;
|
|
48
69
|
private initialized: Promise<true>;
|
|
49
70
|
|
|
71
|
+
protected readonly toDispose = new DisposableCollection(
|
|
72
|
+
this.onDidChangeFileEmitter
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Initializes the OPFS file system provider
|
|
77
|
+
*/
|
|
50
78
|
@postConstruct()
|
|
51
79
|
protected init(): void {
|
|
52
80
|
const setup = async (): Promise<true> => {
|
|
53
|
-
|
|
81
|
+
const root = await this.initialization.getRootDirectory();
|
|
82
|
+
const broadcastChannel = this.initialization.getBroadcastChannel();
|
|
83
|
+
|
|
84
|
+
// Set up file change listening via BroadcastChannel
|
|
85
|
+
broadcastChannel.onmessage = this.handleFileSystemChange.bind(this);
|
|
86
|
+
|
|
87
|
+
// Initialize the file system
|
|
88
|
+
this.fs = new OPFSFileSystem({
|
|
89
|
+
root,
|
|
90
|
+
broadcastChannel,
|
|
91
|
+
hashAlgorithm: false,
|
|
92
|
+
});
|
|
93
|
+
|
|
54
94
|
await this.initialization.initializeFS(new Proxy(this, {
|
|
55
95
|
get(target, prop, receiver): unknown {
|
|
56
96
|
if (prop === 'initialized') {
|
|
@@ -61,286 +101,459 @@ export class OPFSFileSystemProvider implements FileSystemProviderWithFileReadWri
|
|
|
61
101
|
}));
|
|
62
102
|
return true;
|
|
63
103
|
};
|
|
104
|
+
|
|
64
105
|
this.initialized = setup();
|
|
65
106
|
}
|
|
66
107
|
|
|
67
|
-
|
|
68
|
-
|
|
108
|
+
/**
|
|
109
|
+
* Watches a resource for file system changes
|
|
110
|
+
*/
|
|
111
|
+
watch(resource: URI, opts: WatchOptions): Disposable {
|
|
112
|
+
if (!resource || !resource.path) {
|
|
113
|
+
return Disposable.NULL;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const unwatch = this.fs.watch(formatPath(resource), {
|
|
117
|
+
recursive: opts.recursive,
|
|
118
|
+
exclude: opts.excludes,
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
return Disposable.create(unwatch);
|
|
69
122
|
}
|
|
70
123
|
|
|
71
|
-
|
|
124
|
+
/**
|
|
125
|
+
* Creates an index from the map of entries
|
|
126
|
+
*/
|
|
127
|
+
async createIndex(entries: Map<URI, Uint8Array>): Promise<void> {
|
|
128
|
+
const arrayEntries: [string, Uint8Array][] = [];
|
|
129
|
+
for (const [uri, content] of entries) {
|
|
130
|
+
arrayEntries.push([formatPath(uri), content]);
|
|
131
|
+
}
|
|
132
|
+
await this.fs.createIndex(arrayEntries);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Retrieves the current file system index
|
|
137
|
+
*/
|
|
138
|
+
async index(): Promise<Map<URI, Stat>> {
|
|
139
|
+
const opfsIndex = await this.fs.index();
|
|
140
|
+
const index = new Map<URI, Stat>();
|
|
141
|
+
|
|
142
|
+
for (const [path, stats] of opfsIndex.entries()) {
|
|
143
|
+
const uri = new URI(path);
|
|
144
|
+
index.set(uri, formatStat(stats));
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return index;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Clears the file system
|
|
152
|
+
*/
|
|
153
|
+
async clear(): Promise<void> {
|
|
72
154
|
try {
|
|
73
|
-
await this.
|
|
74
|
-
await this.toFileSystemHandle(resource);
|
|
75
|
-
return true;
|
|
155
|
+
await this.fs.clear();
|
|
76
156
|
} catch (error) {
|
|
157
|
+
throw toFileSystemProviderError(error as Error | OPFSError);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Checks if a resource exists
|
|
163
|
+
*/
|
|
164
|
+
async exists(resource: URI): Promise<boolean> {
|
|
165
|
+
if (!resource || !resource.path) {
|
|
77
166
|
return false;
|
|
78
167
|
}
|
|
168
|
+
|
|
169
|
+
await this.initialized;
|
|
170
|
+
|
|
171
|
+
try {
|
|
172
|
+
return await this.fs.exists(formatPath(resource));
|
|
173
|
+
} catch (error) {
|
|
174
|
+
throw toFileSystemProviderError(error as Error | OPFSError);
|
|
175
|
+
}
|
|
79
176
|
}
|
|
80
177
|
|
|
178
|
+
/**
|
|
179
|
+
* Gets file system statistics for a resource
|
|
180
|
+
*/
|
|
81
181
|
async stat(resource: URI): Promise<Stat> {
|
|
182
|
+
if (!resource || !resource.path) {
|
|
183
|
+
throw createFileSystemProviderError('Invalid resource URI', FileSystemProviderErrorCode.FileNotFound);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
await this.initialized;
|
|
187
|
+
|
|
82
188
|
try {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
const handle = await this.toFileSystemHandle(resource);
|
|
86
|
-
|
|
87
|
-
if (handle.kind === 'file') {
|
|
88
|
-
const fileHandle = handle as FileSystemFileHandle;
|
|
89
|
-
const file = await fileHandle.getFile();
|
|
90
|
-
return {
|
|
91
|
-
type: FileType.File,
|
|
92
|
-
ctime: file.lastModified,
|
|
93
|
-
mtime: file.lastModified,
|
|
94
|
-
size: file.size
|
|
95
|
-
};
|
|
96
|
-
} else if (handle.kind === 'directory') {
|
|
97
|
-
return {
|
|
98
|
-
type: FileType.Directory,
|
|
99
|
-
ctime: 0,
|
|
100
|
-
mtime: 0,
|
|
101
|
-
size: 0
|
|
102
|
-
};
|
|
103
|
-
}
|
|
189
|
+
const path = formatPath(resource);
|
|
190
|
+
const stats = await this.fs.stat(path);
|
|
104
191
|
|
|
105
|
-
|
|
192
|
+
return formatStat(stats);
|
|
106
193
|
} catch (error) {
|
|
107
|
-
throw toFileSystemProviderError(error);
|
|
194
|
+
throw toFileSystemProviderError(error as Error | OPFSError);
|
|
108
195
|
}
|
|
109
196
|
}
|
|
110
197
|
|
|
198
|
+
/**
|
|
199
|
+
* Creates a directory
|
|
200
|
+
*/
|
|
111
201
|
async mkdir(resource: URI): Promise<void> {
|
|
202
|
+
if (!resource || !resource.path) {
|
|
203
|
+
throw createFileSystemProviderError('Invalid resource URI', FileSystemProviderErrorCode.FileNotFound);
|
|
204
|
+
}
|
|
205
|
+
|
|
112
206
|
await this.initialized;
|
|
207
|
+
|
|
113
208
|
try {
|
|
114
|
-
|
|
209
|
+
const path = formatPath(resource);
|
|
210
|
+
|
|
211
|
+
await this.fs.mkdir(path, { recursive: true });
|
|
115
212
|
this.onDidChangeFileEmitter.fire([{ resource, type: FileChangeType.ADDED }]);
|
|
116
213
|
} catch (error) {
|
|
117
|
-
throw toFileSystemProviderError(error
|
|
214
|
+
throw toFileSystemProviderError(error as Error | OPFSError);
|
|
118
215
|
}
|
|
119
216
|
}
|
|
120
217
|
|
|
218
|
+
/**
|
|
219
|
+
* Reads directory contents
|
|
220
|
+
*/
|
|
121
221
|
async readdir(resource: URI): Promise<[string, FileType][]> {
|
|
222
|
+
if (!resource || !resource.path) {
|
|
223
|
+
throw createFileSystemProviderError('Invalid resource URI', FileSystemProviderErrorCode.FileNotFound);
|
|
224
|
+
}
|
|
225
|
+
|
|
122
226
|
await this.initialized;
|
|
123
227
|
|
|
124
228
|
try {
|
|
125
|
-
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
const result: [string, FileType][] = [];
|
|
129
|
-
|
|
130
|
-
// Iterate through the entries in the directory (files and subdirectories)
|
|
131
|
-
for await (const [name, handle] of directoryHandle.entries()) {
|
|
132
|
-
// Determine the type of the entry (file or directory)
|
|
133
|
-
if (handle.kind === 'file') {
|
|
134
|
-
result.push([name, FileType.File]);
|
|
135
|
-
} else if (handle.kind === 'directory') {
|
|
136
|
-
result.push([name, FileType.Directory]);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
229
|
+
const path = formatPath(resource);
|
|
230
|
+
const entries = await this.fs.readDir(path);
|
|
139
231
|
|
|
140
|
-
return
|
|
232
|
+
return entries.map(entry => [
|
|
233
|
+
entry.name,
|
|
234
|
+
entry.isFile ? FileType.File : FileType.Directory
|
|
235
|
+
]);
|
|
141
236
|
} catch (error) {
|
|
142
|
-
throw toFileSystemProviderError(error
|
|
237
|
+
throw toFileSystemProviderError(error as Error | OPFSError);
|
|
143
238
|
}
|
|
144
239
|
}
|
|
145
240
|
|
|
146
|
-
|
|
241
|
+
/**
|
|
242
|
+
* Deletes a resource
|
|
243
|
+
*/
|
|
244
|
+
async delete(resource: URI, opts: FileDeleteOptions): Promise<void> {
|
|
245
|
+
if (!resource || !resource.path) {
|
|
246
|
+
throw createFileSystemProviderError('Invalid resource URI', FileSystemProviderErrorCode.FileNotFound);
|
|
247
|
+
}
|
|
248
|
+
|
|
147
249
|
await this.initialized;
|
|
250
|
+
|
|
148
251
|
try {
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
throw createFileSystemProviderError(new Error('Parent is not a directory'), FileSystemProviderErrorCode.FileNotADirectory);
|
|
153
|
-
}
|
|
154
|
-
const name = resource.path.base;
|
|
155
|
-
return (parentHandle as FileSystemDirectoryHandle).removeEntry(name, { recursive: _opts.recursive });
|
|
252
|
+
const path = formatPath(resource);
|
|
253
|
+
|
|
254
|
+
await this.fs.remove(path, { recursive: opts.recursive });
|
|
156
255
|
} catch (error) {
|
|
157
|
-
throw toFileSystemProviderError(error);
|
|
158
|
-
} finally {
|
|
159
|
-
this.onDidChangeFileEmitter.fire([{ resource, type: FileChangeType.DELETED }]);
|
|
256
|
+
throw toFileSystemProviderError(error as Error | OPFSError);
|
|
160
257
|
}
|
|
161
258
|
}
|
|
162
259
|
|
|
260
|
+
/**
|
|
261
|
+
* Renames a resource from one location to another
|
|
262
|
+
*/
|
|
163
263
|
async rename(from: URI, to: URI, opts: FileOverwriteOptions): Promise<void> {
|
|
264
|
+
if (!from || !from.path || !to || !to.path) {
|
|
265
|
+
throw createFileSystemProviderError('Invalid source or destination URI', FileSystemProviderErrorCode.FileNotFound);
|
|
266
|
+
}
|
|
267
|
+
|
|
164
268
|
await this.initialized;
|
|
165
269
|
|
|
166
270
|
try {
|
|
167
|
-
const
|
|
168
|
-
|
|
169
|
-
if (fromHandle.kind === 'directory') {
|
|
170
|
-
// Create the new directory and get the handle
|
|
171
|
-
await this.mkdir(to);
|
|
172
|
-
const toHandle = await this.toFileSystemHandle(to) as FileSystemDirectoryHandle;
|
|
173
|
-
await copyDirectoryContents(fromHandle as FileSystemDirectoryHandle, toHandle);
|
|
174
|
-
|
|
175
|
-
// Delete the old directory
|
|
176
|
-
await this.delete(from, { recursive: true, useTrash: false });
|
|
177
|
-
} else {
|
|
178
|
-
const content = await this.readFile(from);
|
|
179
|
-
await this.writeFile(to, content, { create: true, overwrite: opts.overwrite });
|
|
180
|
-
await this.delete(from, { recursive: true, useTrash: false });
|
|
181
|
-
}
|
|
271
|
+
const fromPath = formatPath(from);
|
|
272
|
+
const toPath = formatPath(to);
|
|
182
273
|
|
|
183
|
-
this.
|
|
274
|
+
await this.fs.rename(fromPath, toPath, {
|
|
275
|
+
overwrite: opts.overwrite,
|
|
276
|
+
});
|
|
184
277
|
} catch (error) {
|
|
185
|
-
throw toFileSystemProviderError(error);
|
|
278
|
+
throw toFileSystemProviderError(error as Error | OPFSError);
|
|
186
279
|
}
|
|
187
280
|
}
|
|
188
281
|
|
|
189
|
-
|
|
282
|
+
/**
|
|
283
|
+
* Copies a resource from one location to another
|
|
284
|
+
*/
|
|
285
|
+
async copy(from: URI, to: URI, opts: FileOverwriteOptions): Promise<void> {
|
|
286
|
+
if (!from || !from.path || !to || !to.path) {
|
|
287
|
+
throw createFileSystemProviderError('Invalid source or destination URI', FileSystemProviderErrorCode.FileNotFound);
|
|
288
|
+
}
|
|
289
|
+
|
|
190
290
|
await this.initialized;
|
|
191
291
|
|
|
192
292
|
try {
|
|
193
|
-
|
|
194
|
-
const
|
|
293
|
+
const fromPath = formatPath(from);
|
|
294
|
+
const toPath = formatPath(to);
|
|
195
295
|
|
|
196
|
-
|
|
197
|
-
|
|
296
|
+
await this.fs.copy(fromPath, toPath, {
|
|
297
|
+
overwrite: opts.overwrite,
|
|
298
|
+
recursive: true,
|
|
299
|
+
});
|
|
300
|
+
} catch (error) {
|
|
301
|
+
throw toFileSystemProviderError(error as Error | OPFSError);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
198
304
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
305
|
+
/**
|
|
306
|
+
* Reads file content as binary data
|
|
307
|
+
*/
|
|
308
|
+
async readFile(resource: URI): Promise<Uint8Array> {
|
|
309
|
+
if (!resource || !resource.path) {
|
|
310
|
+
throw createFileSystemProviderError('Invalid resource URI', FileSystemProviderErrorCode.FileNotFound);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
await this.initialized;
|
|
314
|
+
|
|
315
|
+
try {
|
|
316
|
+
return await this.fs.readFile(formatPath(resource), 'binary') as Uint8Array;
|
|
202
317
|
} catch (error) {
|
|
203
|
-
throw toFileSystemProviderError(error
|
|
318
|
+
throw toFileSystemProviderError(error as Error | OPFSError);
|
|
204
319
|
}
|
|
205
320
|
}
|
|
206
321
|
|
|
322
|
+
/**
|
|
323
|
+
* Reads file content as a stream
|
|
324
|
+
*/
|
|
325
|
+
readFileStream(resource: URI, opts: FileReadStreamOptions, token: CancellationToken): ReadableStreamEvents<Uint8Array> {
|
|
326
|
+
const stream = newWriteableStream<Uint8Array>(chunks => BinaryBuffer.concat(chunks.map(chunk => BinaryBuffer.wrap(chunk))).buffer);
|
|
327
|
+
|
|
328
|
+
readFileIntoStream(this, resource, stream, data => data.buffer, {
|
|
329
|
+
...opts,
|
|
330
|
+
bufferSize: this.BUFFER_SIZE
|
|
331
|
+
}, token);
|
|
332
|
+
|
|
333
|
+
return stream;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Writes binary content to a file
|
|
338
|
+
*/
|
|
207
339
|
async writeFile(resource: URI, content: Uint8Array, opts: FileWriteOptions): Promise<void> {
|
|
208
340
|
await this.initialized;
|
|
209
|
-
|
|
341
|
+
|
|
342
|
+
let handle: number | undefined = undefined;
|
|
343
|
+
|
|
344
|
+
if (!resource || !resource.path) {
|
|
345
|
+
throw createFileSystemProviderError('Invalid resource URI', FileSystemProviderErrorCode.FileNotFound);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
if (!content || !(content instanceof Uint8Array)) {
|
|
349
|
+
throw createFileSystemProviderError('Invalid content: must be Uint8Array', FileSystemProviderErrorCode.Unknown);
|
|
350
|
+
}
|
|
351
|
+
|
|
210
352
|
try {
|
|
211
|
-
|
|
353
|
+
const path = formatPath(resource);
|
|
354
|
+
|
|
212
355
|
if (!opts.create || !opts.overwrite) {
|
|
213
|
-
const fileExists = await this.
|
|
356
|
+
const fileExists = await this.fs.exists(path);
|
|
357
|
+
|
|
214
358
|
if (fileExists) {
|
|
215
359
|
if (!opts.overwrite) {
|
|
216
360
|
throw createFileSystemProviderError('File already exists', FileSystemProviderErrorCode.FileExists);
|
|
217
361
|
}
|
|
218
|
-
} else {
|
|
219
|
-
|
|
220
|
-
throw createFileSystemProviderError('File does not exist', FileSystemProviderErrorCode.FileNotFound);
|
|
221
|
-
}
|
|
362
|
+
} else if (!opts.create) {
|
|
363
|
+
throw createFileSystemProviderError('File does not exist', FileSystemProviderErrorCode.FileNotFound);
|
|
222
364
|
}
|
|
223
365
|
}
|
|
224
366
|
|
|
225
|
-
const handle = await this.toFileSystemHandle(resource, { create: true, isDirectory: false }) as FileSystemFileHandle;
|
|
226
|
-
|
|
227
367
|
// Open
|
|
228
|
-
|
|
368
|
+
handle = await this.open(resource, { create: true });
|
|
229
369
|
|
|
230
370
|
// Write content at once
|
|
231
|
-
await
|
|
232
|
-
|
|
233
|
-
this.onDidChangeFileEmitter.fire([{ resource: resource, type: FileChangeType.UPDATED }]);
|
|
371
|
+
await this.write(handle, 0, content, 0, content.byteLength);
|
|
234
372
|
} catch (error) {
|
|
235
|
-
throw toFileSystemProviderError(error
|
|
373
|
+
throw toFileSystemProviderError(error as Error | OPFSError);
|
|
236
374
|
} finally {
|
|
237
|
-
if (typeof
|
|
238
|
-
await
|
|
375
|
+
if (typeof handle === 'number') {
|
|
376
|
+
await this.close(handle);
|
|
239
377
|
}
|
|
240
378
|
}
|
|
241
379
|
}
|
|
242
380
|
|
|
381
|
+
// #region Open/Read/Write/Close Operations
|
|
382
|
+
|
|
243
383
|
/**
|
|
244
|
-
*
|
|
245
|
-
* @param resource URI/path of the resource
|
|
246
|
-
* @param options Options for the creation of the handle while traversing the path
|
|
247
|
-
* @returns FileSystemHandle for the given resource
|
|
384
|
+
* Opens a file and returns a file descriptor
|
|
248
385
|
*/
|
|
249
|
-
|
|
250
|
-
|
|
386
|
+
async open(resource: URI, opts: FileOpenOptions): Promise<number> {
|
|
387
|
+
await this.initialized;
|
|
251
388
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
}
|
|
389
|
+
if (!resource || !resource.path) {
|
|
390
|
+
throw createFileSystemProviderError('Invalid resource URI', FileSystemProviderErrorCode.FileNotFound);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
try {
|
|
394
|
+
const path = formatPath(resource);
|
|
395
|
+
const fileExists = await this.fs.exists(path);
|
|
396
|
+
|
|
397
|
+
if (!opts.create && !fileExists) {
|
|
398
|
+
throw createFileSystemProviderError('File does not exist', FileSystemProviderErrorCode.FileNotFound);
|
|
399
|
+
}
|
|
255
400
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
401
|
+
const fd = await this.fs.open(path, {
|
|
402
|
+
create: opts.create,
|
|
403
|
+
truncate: opts.create
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
return fd;
|
|
407
|
+
} catch (error) {
|
|
408
|
+
throw toFileSystemProviderError(error as Error | OPFSError);
|
|
409
|
+
}
|
|
261
410
|
}
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Closes a file descriptor
|
|
414
|
+
*/
|
|
415
|
+
async close(fd: number): Promise<void> {
|
|
416
|
+
try {
|
|
417
|
+
await this.fs.close(fd);
|
|
418
|
+
} catch (error) {
|
|
419
|
+
throw toFileSystemProviderError(error as Error | OPFSError);
|
|
420
|
+
}
|
|
265
421
|
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Reads data from a file descriptor
|
|
425
|
+
*/
|
|
426
|
+
async read(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise<number> {
|
|
427
|
+
try {
|
|
428
|
+
const result = await this.fs.read(fd, data, offset, length, pos);
|
|
429
|
+
|
|
430
|
+
return result.bytesRead;
|
|
431
|
+
} catch (error) {
|
|
432
|
+
throw toFileSystemProviderError(error as Error | OPFSError);
|
|
273
433
|
}
|
|
274
434
|
}
|
|
275
435
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
return
|
|
436
|
+
/**
|
|
437
|
+
* Writes data to a file descriptor
|
|
438
|
+
*/
|
|
439
|
+
async write(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise<number> {
|
|
440
|
+
try {
|
|
441
|
+
return await this.fs.write(fd, data, offset, length, pos, true);
|
|
442
|
+
} catch (error) {
|
|
443
|
+
throw toFileSystemProviderError(error as Error | OPFSError);
|
|
282
444
|
}
|
|
283
445
|
}
|
|
284
446
|
|
|
285
|
-
//
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
447
|
+
// #endregion
|
|
448
|
+
|
|
449
|
+
// #region Text File Updates
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* Updates a text file with content changes
|
|
453
|
+
*/
|
|
454
|
+
async updateFile(resource: URI, changes: TextDocumentContentChangeEvent[], opts: FileUpdateOptions): Promise<FileUpdateResult> {
|
|
455
|
+
try {
|
|
456
|
+
const content = await this.readFile(resource);
|
|
457
|
+
const decoded = this.encodingService.decode(BinaryBuffer.wrap(content), opts.readEncoding);
|
|
458
|
+
const newContent = TextDocument.update(TextDocument.create('', '', 1, decoded), changes, 2).getText();
|
|
459
|
+
const encoding = await this.encodingService.toResourceEncoding(opts.writeEncoding, {
|
|
460
|
+
overwriteEncoding: opts.overwriteEncoding,
|
|
461
|
+
read: async length => {
|
|
462
|
+
const fd = await this.open(resource, { create: false });
|
|
463
|
+
try {
|
|
464
|
+
const data = new Uint8Array(length);
|
|
465
|
+
await this.read(fd, 0, data, 0, length);
|
|
466
|
+
return data;
|
|
467
|
+
} finally {
|
|
468
|
+
await this.close(fd);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
const encoded = this.encodingService.encode(newContent, encoding);
|
|
474
|
+
|
|
475
|
+
await this.writeFile(resource, encoded.buffer, { create: false, overwrite: true });
|
|
476
|
+
|
|
477
|
+
const stat = await this.stat(resource);
|
|
478
|
+
|
|
479
|
+
return Object.assign(stat, { encoding: encoding.encoding });
|
|
480
|
+
} catch (error) {
|
|
481
|
+
throw toFileSystemProviderError(error as Error | OPFSError);
|
|
482
|
+
}
|
|
289
483
|
}
|
|
290
484
|
|
|
291
|
-
|
|
292
|
-
}
|
|
485
|
+
// #endregion
|
|
293
486
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
487
|
+
/**
|
|
488
|
+
* Handles file system change events from BroadcastChannel
|
|
489
|
+
*/
|
|
490
|
+
private async handleFileSystemChange(event: MessageEvent<WatchEvent>): Promise<void> {
|
|
491
|
+
if (!event.data?.path) {
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
const resource = new URI('file://' + event.data.path);
|
|
496
|
+
let changeType: FileChangeType;
|
|
497
|
+
|
|
498
|
+
if (event.data.type === WatchEventType.Added) {
|
|
499
|
+
changeType = FileChangeType.ADDED;
|
|
500
|
+
} else if (event.data.type === WatchEventType.Removed) {
|
|
501
|
+
changeType = FileChangeType.DELETED;
|
|
502
|
+
} else {
|
|
503
|
+
changeType = FileChangeType.UPDATED;
|
|
309
504
|
}
|
|
505
|
+
|
|
506
|
+
this.onDidChangeFileEmitter.fire([{ resource, type: changeType }]);
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* Disposes the file system provider
|
|
511
|
+
*/
|
|
512
|
+
dispose(): void {
|
|
513
|
+
this.toDispose.dispose();
|
|
310
514
|
}
|
|
311
515
|
}
|
|
312
516
|
|
|
313
|
-
|
|
517
|
+
/**
|
|
518
|
+
* Formats a URI or string resource to a file system path
|
|
519
|
+
*/
|
|
520
|
+
function formatPath(resource: URI | string): string {
|
|
521
|
+
return FileUri.fsPath(resource);
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
/**
|
|
525
|
+
* Creates a Stat object from OPFS stats
|
|
526
|
+
*/
|
|
527
|
+
function formatStat(stats: FileStat): Stat {
|
|
528
|
+
return {
|
|
529
|
+
type: stats.isDirectory ? FileType.Directory : FileType.File,
|
|
530
|
+
ctime: new Date(stats.ctime).getTime(),
|
|
531
|
+
mtime: new Date(stats.mtime).getTime(),
|
|
532
|
+
size: stats.size
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
/**
|
|
537
|
+
* Converts OPFS errors to file system provider errors
|
|
538
|
+
*/
|
|
539
|
+
function toFileSystemProviderError(error: OPFSError | Error): FileSystemProviderError {
|
|
314
540
|
if (error instanceof FileSystemProviderError) {
|
|
315
|
-
return error;
|
|
541
|
+
return error;
|
|
316
542
|
}
|
|
317
543
|
|
|
318
544
|
let code: FileSystemProviderErrorCode;
|
|
319
|
-
switch (error.name) {
|
|
320
|
-
case 'NotFoundError':
|
|
321
|
-
code = FileSystemProviderErrorCode.FileNotFound;
|
|
322
|
-
break;
|
|
323
|
-
case 'InvalidModificationError':
|
|
324
|
-
code = FileSystemProviderErrorCode.FileExists;
|
|
325
|
-
break;
|
|
326
|
-
case 'NotAllowedError':
|
|
327
|
-
code = FileSystemProviderErrorCode.NoPermissions;
|
|
328
|
-
break;
|
|
329
|
-
case 'TypeMismatchError':
|
|
330
|
-
if (!is_dir) {
|
|
331
|
-
code = FileSystemProviderErrorCode.FileIsADirectory;
|
|
332
|
-
} else {
|
|
333
|
-
code = FileSystemProviderErrorCode.FileNotADirectory;
|
|
334
|
-
}
|
|
335
545
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
546
|
+
if (error.name === 'NotFoundError' || error.name === 'ENOENT') {
|
|
547
|
+
code = FileSystemProviderErrorCode.FileNotFound;
|
|
548
|
+
} else if (error.name === 'NotAllowedError' || error.name === 'SecurityError' || error.name === 'EACCES') {
|
|
549
|
+
code = FileSystemProviderErrorCode.NoPermissions;
|
|
550
|
+
} else if (error.name === 'QuotaExceededError' || error.name === 'ENOSPC') {
|
|
551
|
+
code = FileSystemProviderErrorCode.FileTooLarge;
|
|
552
|
+
} else if (error.name === 'PathError' || error.name === 'INVALID_PATH') {
|
|
553
|
+
code = FileSystemProviderErrorCode.FileNotADirectory;
|
|
554
|
+
} else {
|
|
555
|
+
code = FileSystemProviderErrorCode.Unknown;
|
|
342
556
|
}
|
|
343
557
|
|
|
344
558
|
return createFileSystemProviderError(error, code);
|
|
345
559
|
}
|
|
346
|
-
// #endregion
|