import { inject, injectable } from '@theia/core/shared/inversify'; import URI from '@theia/core/lib/common/uri'; import { Event } from '@theia/core/lib/common/event'; import { Disposable, DisposableCollection, } from '@theia/core/lib/common/disposable'; import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application'; import { Stat, FileType, FileChange, FileWriteOptions, FileDeleteOptions, FileOverwriteOptions, FileSystemProvider, FileSystemProviderError, FileSystemProviderErrorCode, FileSystemProviderCapabilities, WatchOptions, } from '@theia/filesystem/lib/common/files'; import { FileService, FileServiceContribution, } from '@theia/filesystem/lib/browser/file-service'; import { AuthenticationClientService } from '../auth/authentication-client-service'; import { CreateApi } from './create-api'; import { CreateUri } from './create-uri'; import { SketchesService } from '../../common/protocol'; import { ArduinoPreferences } from '../arduino-preferences'; import { Create } from './typings'; import { stringToUint8Array } from '../../common/utils'; @injectable() export class CreateFsProvider implements FileSystemProvider, FrontendApplicationContribution, FileServiceContribution { @inject(AuthenticationClientService) protected readonly authenticationService: AuthenticationClientService; @inject(CreateApi) protected readonly createApi: CreateApi; @inject(SketchesService) protected readonly sketchesService: SketchesService; @inject(ArduinoPreferences) protected readonly arduinoPreferences: ArduinoPreferences; protected readonly toDispose = new DisposableCollection(); readonly onFileWatchError: Event<void> = Event.None; readonly onDidChangeFile: Event<readonly FileChange[]> = Event.None; readonly onDidChangeCapabilities: Event<void> = Event.None; readonly capabilities: FileSystemProviderCapabilities = FileSystemProviderCapabilities.FileReadWrite | FileSystemProviderCapabilities.PathCaseSensitive | FileSystemProviderCapabilities.Access; onStop(): void { this.toDispose.dispose(); } registerFileSystemProviders(service: FileService): void { service.onWillActivateFileSystemProvider((event) => { if (event.scheme === CreateUri.scheme) { event.waitUntil( (async () => { service.registerProvider(CreateUri.scheme, this); })() ); } }); } watch(uri: URI, opts: WatchOptions): Disposable { return Disposable.NULL; } async stat(uri: URI): Promise<Stat> { if (CreateUri.equals(CreateUri.root, uri)) { this.getCreateApi; // This will throw when not logged in. return { type: FileType.Directory, ctime: 0, mtime: 0, size: 0, }; } const resource = await this.getCreateApi.stat(uri.path.toString()); const mtime = Date.parse(resource.modified_at); return { type: this.toFileType(resource.type), ctime: mtime, mtime, size: 0, }; } async mkdir(uri: URI): Promise<void> { await this.getCreateApi.createDirectory(uri.path.toString()); } async readdir(uri: URI): Promise<[string, FileType][]> { const resources = await this.getCreateApi.readDirectory( uri.path.toString() ); return resources.map(({ name, type }) => [name, this.toFileType(type)]); } async delete(uri: URI, opts: FileDeleteOptions): Promise<void> { if (!opts.recursive) { throw new Error( 'Arduino Create file-system provider does not support non-recursive deletion.' ); } const stat = await this.stat(uri); if (!stat) { throw new FileSystemProviderError( 'File not found.', FileSystemProviderErrorCode.FileNotFound ); } switch (stat.type) { case FileType.Directory: { await this.getCreateApi.deleteDirectory(uri.path.toString()); break; } case FileType.File: { await this.getCreateApi.deleteFile(uri.path.toString()); break; } default: { throw new FileSystemProviderError( `Unexpected file type '${stat.type}' for resource: ${uri.toString()}`, FileSystemProviderErrorCode.Unknown ); } } } async rename( oldUri: URI, newUri: URI, options: FileOverwriteOptions ): Promise<void> { await this.getCreateApi.rename( oldUri.path.toString(), newUri.path.toString() ); } async readFile(uri: URI): Promise<Uint8Array> { const content = await this.getCreateApi.readFile(uri.path.toString()); return stringToUint8Array(content); } async writeFile( uri: URI, content: Uint8Array, options: FileWriteOptions ): Promise<void> { await this.getCreateApi.writeFile(uri.path.toString(), content); } async access(uri: URI, mode?: number): Promise<void> { this.getCreateApi; // Will throw if not logged in. } public toFileType(type: Create.ResourceType): FileType { switch (type) { case 'file': return FileType.File; case 'sketch': case 'folder': return FileType.Directory; default: return FileType.Unknown; } } private get getCreateApi(): CreateApi { const { session } = this.authenticationService; if (!session) { throw new FileSystemProviderError( 'Not logged in.', FileSystemProviderErrorCode.NoPermissions ); } return this.createApi; } }