Provide virtual files to Navigator

Hi. I want to display the files that are in the archives in the navigator view. But Theia have so many different FileSystemProviders that I am confused. What is the easiest way to implement an idea?

They’re required to be compatible with VS Code’s FS Provider API.

You have to implement your custom FS provider API. If you do it in a VS Code extension, you have to drop it into the plugins folder, and you’re done. If it lives in a Theia extension, you have to register it with its scheme explicitly. Check this here as an example.

You can try this VS Code extension; maybe it works out of the box. If no, it’s a good starting point to list the content of an archive in the Explorer.

I figured out how I need to create my own FileSystemProvider that can read data from an archive or other files, and attach it with a new scheme. But I did not understand how to change the URI of the archive file so that my provider would be loaded. Since I want the archive file to be expanded in the navigator as a folder with its content. And it is the Theia extension.

To be honest, I absolutely do not understand how to do it. I want to add the ability to expand some types of files.


I need this to be able to open separate editors for the inner file and for the whole archive file. And at the same time, keep the ability to create files and folders in side this archives.
As I understand it, I need to create an implementation of a file system provider that can read the insides of the archive. And register it on my custom URI scheme. For example, abc://file://project/file.abc/inner-file.txt
And after or while reading the files, replace the original file scheme with my own. But I don’t understand how to do it.
In FileService.toFileStat used URI.resolve(string) method, so the inner file inherits the folder scheme. I don’t understand how to break the chain.
Additionally, if I want the file to be a container with other files and at the same time can run its own editor on it, then the file must be FileType.File and FileType.Directory at the same time. So I need to change original FileSystemProvider readdir(). Or not?

Hi guys. I still need help with this.

@Berlizov unfortunately it is difficult to help without any concrete questions, and/or code repository as it is quite a big undertaking and use-case. I’d suggest looking closely at both the FileSystemProvider interface, and implementations of it:

If the implementation is too difficult, as Akos mentioned it may be worthwhile to look at the simpler vscode API (without all the possibilities an extension provides), in order to implement the feature:

You can look at the following fs-provider extension as an example of how to implement it as a vscode extension.

Ok. Then a small example. I want to just display 2 files inside all .my-extension files.
So, I just have to create a class like this.

class MyFileSystemProvider implements FileSystemProvider {
  readdir(resource: URI): Promise<[string, FileType][]> {
   return new Promise(resolve => resolve([["file1", FileType.File], ["file2", FileType.File]]));
  }
}

How to bind it to make it activate for ‘file://.*.my-extension’ files?
If I understand correctly, then there is no way to do it. Since the File Service class has only a binding to the schema.

It seems that the easiest way is to just extend RemoteFileSystemProvider and re-bind it to our class without editing RemoteFileServiceContribution.

unbind(RemoteFileSystemProvider)
bind(RemoteFileSystemProvider).to(MyFileSystemProvider).inSingletonScope();

class MyFileSystemProvider extends RemoteFileSystemProvider {
  readdir(resource: URI): Promise<[string, FileType][]> {
    if (resource.path.ext == 'my-extension') {
      return new Promise(resolve => resolve([["file1", FileType.File], ["file2", FileType.File]]));
    } else {
      return super.readdir(resource).then(files => {
              return files.map(file => {
                if (file[0].endsWith('my-extension')) {
                  file[1] = FileType.Directory;
                }
                return file;
              }) 
            });
        }
    }
}

But there is a problem if there are two or more extensions that are binded to the RemoteFileSystemProvider. Therefore, it would be more correct not to extend, but to add delegate:FileSystemProvider field to our class, and replace return super.readdir(... => return this.delegate.readdir(....

const delegate = await fileService.activateProvider(‘file’);
fileService.registerProvider(‘file’, new MyFileSystemProvider(delegate))

But even here it is not so simple. Error: A filesystem provider for the scheme ‘file’ is already registered.
How to bind a provider in a proper way that it inherits the functionality of the lower provider and adds its own behavior?