Search code examples
visual-studio-codevscode-extensions

VSCode: Create a document in memory with URI for automated testing?


Background

I created an extension that interacts with documents. In order to test the extension I need to create documents, that the extension can work with. The extension has to access the document via uri.

Currently I'm using vscode.workspace.openTextDocument({content: _content, language: _language}); for document creation. The problem is, it does not have a valid URI.

Question

How can I create a virtual document in memory, that has a valid URI?


Solution

  • As there was no native solution to this, I created my and I'd like to share it here:

    A TextDocumentContentProvider for files in memory. Example usage shown below

    memoryfile.ts

    import * as vscode from 'vscode';
    
    const _SCHEME = "inmemoryfile";
    
    /**
     *  Registration function for In-Memory files.
     *  You need to call this once, if you want to make use of
     *  `MemoryFile`s.
     **/
    export function register_memoryFileProvider ({ subscriptions }: vscode.ExtensionContext)
    {
      const myProvider = new (class implements vscode.TextDocumentContentProvider
            {
                provideTextDocumentContent(uri: vscode.Uri): string
                {
                    let memDoc = MemoryFile.getDocument (uri);
                    if (memDoc == null)
                        return "";
                    return memDoc.read ();
                }
      })();
      subscriptions.push(vscode.workspace.registerTextDocumentContentProvider(
            _SCHEME, myProvider));
    }
    
    
    
    
    /**
     *  Management class for in-memory files.
     **/
    class MemoryFileManagement
    {
        private static _documents: {[key: string]: MemoryFile} = {};
        private static _lastDocId: number = 0;
    
    
    
        public static getDocument(uri: vscode.Uri) : MemoryFile | null
        {
            return MemoryFileManagement._documents[uri.path];
        }
    
    
    
        private static _getNextDocId(): string{
            MemoryFileManagement._lastDocId++;
            return "_" + MemoryFileManagement._lastDocId + "_";
        }
    
    
    
        public static createDocument(extension = "")
        {
          let path = MemoryFileManagement._getNextDocId ();
    
            if (extension != "")
                path += "." + extension;
    
            let self = new MemoryFile(path);
    
            MemoryFileManagement._documents[path] = self;
    
            return self;
        }
    }
    
    
    
    /**
     * A file in memory
     **/
    export class MemoryFile
    {
        /******************
         ** Static Area  **
         ******************/
    
        public static getDocument(uri: vscode.Uri) : MemoryFile | null {
            return MemoryFileManagement.getDocument (uri);
        }
    
        public static createDocument(extension = "") {
            return MemoryFileManagement.createDocument (extension);
        }
    
    
    
        /******************
         ** Object Area  **
         ******************/
    
        public content: string = "";
        public uri: vscode.Uri;
    
        constructor (path: string)
        {
            this.uri = vscode.Uri.from ({scheme: _SCHEME, path: path})
        }
    
    
        public write(strContent: string){
            this.content += strContent;
        }
    
    
        public read(): string {
            return this.content;
        }
    
    
        public getUri(): vscode.Uri {
            return this.uri;
        }
    }
    

    Example usage

    Register the provider

    You need to register the provider somewhere in the beginning of your test code (I do it in index.ts before Mocha is instantiated):

    register_memoryFileProvider (extensionContext);

    (How do I get the extension context?)

    Create a document

    Creating and using a file works as follows:

    // create the in-memory document
    let memfile = MemoryFile.createDocument ("ts");
    memfile.write ("my content");
    
    // create a vscode.TextDocument from the in-memory document.
    let doc = await vscode.workspace.openTextDocument (memfile.getUri ());
    

    Notes

    • Be aware, that LSP commands might not work with with approach, because they might be registered to a certain specific schema.