0
# File Resource Management
1
2
Resource abstraction for files providing content management, encoding detection, version control, and seamless integration with Theia's resource framework. Enables treating files as managed resources with automatic encoding handling and change tracking.
3
4
## Capabilities
5
6
### FileResource Class
7
8
Core file resource implementation providing complete resource interface with file-specific functionality including encoding detection and conditional save operations.
9
10
```typescript { .api }
11
/**
12
* File resource implementation with encoding and version management
13
*/
14
class FileResource implements Resource {
15
/** File URI */
16
readonly uri: URI;
17
18
/** Resource version information with encoding and timestamps */
19
readonly version: FileResourceVersion | undefined;
20
21
/** Detected or specified file encoding */
22
readonly encoding: string | undefined;
23
24
/** Whether file is read-only (boolean or MarkdownString with reason) */
25
readonly readOnly: boolean | MarkdownString;
26
27
/** Read entire file contents as string with encoding handling */
28
readContents(options?: { encoding?: string }): Promise<string>;
29
30
/** Read file contents as stream with encoding handling */
31
readStream(options?: { encoding?: string }): Promise<ReadableStream<string>>;
32
33
/** Detect file encoding using heuristics and BOM detection */
34
guessEncoding(): Promise<string>;
35
36
/** Save string content to file (available when not read-only) */
37
saveContents?: (content: string, options?: SaveOptions) => Promise<void>;
38
39
/** Save content stream to file (available when not read-only) */
40
saveStream?: (content: ReadableStream<string>, options?: SaveOptions) => Promise<void>;
41
42
/** Apply content changes to file (available when not read-only) */
43
saveContentChanges?: (changes: TextDocumentContentChangeEvent[], options?: SaveOptions) => Promise<void>;
44
45
/** Resource disposal for cleanup */
46
dispose(): void;
47
}
48
```
49
50
### FileResourceVersion Interface
51
52
Version information for file resources including encoding, modification time, and entity tags for change detection.
53
54
```typescript { .api }
55
/**
56
* File resource version with encoding and timing information
57
*/
58
interface FileResourceVersion extends ResourceVersion {
59
/** Character encoding of the file */
60
readonly encoding: string;
61
62
/** Last modification time (Unix timestamp) */
63
readonly mtime: number;
64
65
/** Entity tag for change detection */
66
readonly etag: string;
67
}
68
69
interface ResourceVersion {
70
/** Unique version identifier */
71
readonly version: string;
72
}
73
```
74
75
### FileResourceResolver
76
77
Service for resolving URIs to FileResource instances with proper lifecycle management and caching.
78
79
```typescript { .api }
80
/**
81
* Resolver for creating FileResource instances from URIs
82
*/
83
@injectable()
84
class FileResourceResolver implements ResourceResolver {
85
/** Priority for this resolver (lower numbers = higher priority) */
86
readonly priority: number = -10;
87
88
/** Check if this resolver can handle the given URI */
89
canHandle(uri: URI): boolean;
90
91
/** Resolve URI to FileResource instance */
92
resolve(uri: URI): Promise<FileResource>;
93
94
/** Event fired when resources are created or disposed */
95
readonly onDidChangeContents: Event<URI>;
96
}
97
```
98
99
### Save Options
100
101
Configuration options for save operations including encoding, backup, and formatting preferences.
102
103
```typescript { .api }
104
/**
105
* Options for save operations
106
*/
107
interface SaveOptions {
108
/** Override encoding for this save operation */
109
encoding?: string;
110
111
/** Create backup before saving */
112
backup?: boolean;
113
114
/** Format document before saving */
115
format?: boolean;
116
117
/** Trim trailing whitespace */
118
trimTrailingWhitespace?: boolean;
119
120
/** Insert final newline if missing */
121
insertFinalNewline?: boolean;
122
123
/** Force save even if file hasn't changed */
124
force?: boolean;
125
}
126
```
127
128
### Text Document Content Changes
129
130
Change event types for incremental updates to file content.
131
132
```typescript { .api }
133
/**
134
* Incremental content change event
135
*/
136
interface TextDocumentContentChangeEvent {
137
/** Range being replaced (undefined for full document changes) */
138
range?: Range;
139
140
/** Length of replaced text */
141
rangeLength?: number;
142
143
/** New text content */
144
text: string;
145
}
146
147
interface Range {
148
/** Starting position */
149
start: Position;
150
151
/** Ending position */
152
end: Position;
153
}
154
155
interface Position {
156
/** Line number (zero-based) */
157
line: number;
158
159
/** Character offset (zero-based) */
160
character: number;
161
}
162
```
163
164
### Encoding Detection
165
166
Utility functions and constants for character encoding detection and handling.
167
168
```typescript { .api }
169
/**
170
* Common character encodings supported by FileResource
171
*/
172
enum Encoding {
173
UTF8 = 'utf8',
174
UTF16BE = 'utf16be',
175
UTF16LE = 'utf16le',
176
UTF32BE = 'utf32be',
177
UTF32LE = 'utf32le',
178
ASCII = 'ascii',
179
ISO88591 = 'iso88591',
180
WINDOWS1252 = 'windows1252'
181
}
182
183
/**
184
* Encoding detection result
185
*/
186
interface EncodingResult {
187
/** Detected encoding */
188
encoding: string;
189
190
/** Confidence level (0-1) */
191
confidence: number;
192
193
/** Whether BOM was detected */
194
hasBOM: boolean;
195
}
196
197
/**
198
* Detect encoding from binary data
199
*/
200
function detectEncoding(buffer: Uint8Array): EncodingResult;
201
202
/**
203
* Check if encoding is valid/supported
204
*/
205
function isValidEncoding(encoding: string): boolean;
206
```
207
208
**Usage Examples:**
209
210
```typescript
211
import { FileResource, FileResourceResolver } from "@theia/filesystem/lib/browser";
212
import { URI } from "@theia/core/lib/common/uri";
213
214
// Get resource resolver
215
const resolver = container.get(FileResourceResolver);
216
217
// Resolve file to resource
218
const fileUri = URI.parse('file:///workspace/src/main.ts');
219
const resource = await resolver.resolve(fileUri);
220
221
// Read file contents
222
const content = await resource.readContents();
223
console.log(`File content: ${content}`);
224
225
// Read with specific encoding
226
const contentUtf16 = await resource.readContents({ encoding: 'utf16le' });
227
228
// Check if file is read-only
229
if (typeof resource.readOnly === 'boolean' && !resource.readOnly) {
230
// File is writable
231
await resource.saveContents?.('console.log("Hello, World!");');
232
} else {
233
console.log('File is read-only');
234
if (typeof resource.readOnly !== 'boolean') {
235
console.log(`Reason: ${resource.readOnly.value}`);
236
}
237
}
238
239
// Stream reading for large files
240
const stream = await resource.readStream();
241
const reader = stream.getReader();
242
243
try {
244
while (true) {
245
const { done, value } = await reader.read();
246
if (done) break;
247
console.log('Chunk:', value);
248
}
249
} finally {
250
reader.releaseLock();
251
}
252
253
// Encoding detection
254
const encoding = await resource.guessEncoding();
255
console.log(`Detected encoding: ${encoding}`);
256
257
// Version information
258
if (resource.version) {
259
console.log(`Version: ${resource.version.version}`);
260
console.log(`Encoding: ${resource.version.encoding}`);
261
console.log(`Modified: ${new Date(resource.version.mtime)}`);
262
console.log(`ETag: ${resource.version.etag}`);
263
}
264
265
// Incremental updates
266
if (resource.saveContentChanges) {
267
await resource.saveContentChanges([{
268
range: { start: { line: 0, character: 0 }, end: { line: 0, character: 0 } },
269
text: '// Added comment\n'
270
}]);
271
}
272
273
// Save with options
274
await resource.saveContents?.('new content', {
275
encoding: 'utf8',
276
trimTrailingWhitespace: true,
277
insertFinalNewline: true,
278
format: true
279
});
280
281
// Resource cleanup
282
resource.dispose();
283
```
284
285
### Advanced Resource Operations
286
287
```typescript
288
import { ResourceProvider, Resource } from "@theia/core/lib/common/resource";
289
290
// Working with resource provider
291
const resourceProvider = container.get(ResourceProvider);
292
293
// Get or create resource
294
const resource = await resourceProvider.get(fileUri);
295
296
// Listen for content changes
297
resource.onDidChangeContents(uri => {
298
console.log(`Resource changed: ${uri.toString()}`);
299
});
300
301
// Batch operations with multiple resources
302
const resources = await Promise.all([
303
resourceProvider.get(URI.parse('file:///file1.txt')),
304
resourceProvider.get(URI.parse('file:///file2.txt')),
305
resourceProvider.get(URI.parse('file:///file3.txt'))
306
]);
307
308
// Read all contents in parallel
309
const contents = await Promise.all(
310
resources.map(resource => resource.readContents())
311
);
312
313
// Save all with same options
314
const saveOptions = { trimTrailingWhitespace: true, insertFinalNewline: true };
315
await Promise.all(
316
resources.map((resource, index) =>
317
resource.saveContents?.(contents[index].toUpperCase(), saveOptions)
318
)
319
);
320
321
// Binary file handling
322
const binaryResource = await resourceProvider.get(URI.parse('file:///image.png'));
323
const binaryContent = await binaryResource.readContents({ encoding: 'binary' });
324
```
325
326
### Custom Resource Implementations
327
328
```typescript
329
// Custom resource with additional functionality
330
class CustomFileResource extends FileResource {
331
332
// Add custom metadata
333
async getCustomMetadata(): Promise<any> {
334
// Custom implementation
335
return {};
336
}
337
338
// Override encoding detection with custom logic
339
async guessEncoding(): Promise<string> {
340
// Custom encoding detection logic
341
const detected = await super.guessEncoding();
342
343
// Apply custom rules
344
if (this.uri.path.endsWith('.log')) {
345
return 'ascii'; // Force ASCII for log files
346
}
347
348
return detected;
349
}
350
351
// Add validation before save
352
async saveContents(content: string, options?: SaveOptions): Promise<void> {
353
// Validate content before saving
354
if (!this.validateContent(content)) {
355
throw new Error('Invalid content format');
356
}
357
358
return super.saveContents!(content, options);
359
}
360
361
private validateContent(content: string): boolean {
362
// Custom validation logic
363
return content.length > 0;
364
}
365
}
366
```