I’m creating a vscode extension that adds some custom autofill capabilities to the script tags inside an html file. It’s based on this example from vscodes extension tutorial page.
I took that example and changed it to look at script tags. So overall the flow of the extension is something like:
- A user goes to an .html file, the extension starts
- When the user starts typing and vscode generates autocomplete items, it calls provideCompletionItem from my extension
- My extension checks if the document is currently located inside a script tag before continuing
- Create a virtual document which replaces everything in the file with spaces except for script tag content
- Append
;class ExampleClass{method1(){}};
to the end of the virtual document string - Run the default behavior for getting the autocompletion list in the virtual document
The current result of this is when you go to a script tag and start typing, ExampleClass will show up like I want. However when you type ExampleClass.|
(‘|’ represents where the cursor is) it only displays the default completion elements. It also produces an error:
[Extension Host] Unexpected resource embedded-content://js/path/to/file.html.js
Before this error pops up it isn’t running the registerTextDocumentContentProvider
which returns the virtual content when given a virtual URI. Presumably this means I’m missing some special case for when some path saved in the CompletionItem is accessed but I can’t figure out exactly what that is.
Here is some of the relevant code:
the function that creates virtual file content:
// the above code is unchanged from the vscode example
let content = documentText
.split('n')
.map(line => {
return ' '.repeat(line.length);
}).join('n');
regions.forEach(r => {
if (r.languageId === languageId) { // languageId == javascript
content = content.slice(0, r.start) + documentText.slice(r.start, r.end) + content.slice(r.end);
}
});
let inject = ';class ExampleClass{method1(){}};';
return content + inject; // does not run on error
}
serves up the virtual documents:
const virtualDocumentContents = new Map<string, string>();
workspace.registerTextDocumentContentProvider('embedded-content', {
provideTextDocumentContent: uri => {
console.log('get virt file'); // in the case of the error this never logs
// Remove leading `/` and ending `.js` to get original URI
const originalUri = uri.path.slice(1).slice(0, -3);
const decodedUri = decodeURIComponent(originalUri);
if (!virtualDocumentContents.has(decodedUri)) console.warn(`no such virtual file ${decodedUri}`); // this warning never goes off
return virtualDocumentContents.get(decodedUri);
}
});
Provide CompletionItems
const clientOptions: LanguageClientOptions = {
documentSelector: [{ scheme: 'file', language: 'html' }],
middleware: {
provideCompletionItem: async (document, position, context, token, next) => {
console.log('auto completion...'); // does not run on error
if (!isInsideScriptRegion(htmlLanguageService, document.getText(), document.offsetAt(position))) {
return await next(document, position, context, token);
}
const originalUri = document.uri.toString(true);
const virtContent = getVirtualContent(htmlLanguageService, document.getText(), 'javascript');
virtualDocumentContents.set(originalUri, virtContent);
const vdocUriString = `embedded-content://js/${encodeURIComponent(
originalUri
)}.js`;
const vdocUri = Uri.parse(vdocUriString);
const compArr = await commands.executeCommand<CompletionList>(
'vscode.executeCompletionItemProvider',
vdocUri,
position,
context.triggerCharacter
);
env.clipboard.writeText(virtContent); // for debugging, this of course also does not run when the error occurs
console.log('write board');
return compArr;
}
}
}