How to Handle Asynchronous Scoping #1308
-
I am working on a scope provider that will load an imported scope. It will do so by calling a remote endpoint via a network call and asking for the code in the imported package. It will then parse the package contents and provide the scope. The problem I'm seeing is that parsing is an asynchronous activity and the ScopeProvider interface expects a return type of Is there a recommended way to do this? |
Beta Was this translation helpful? Give feedback.
Replies: 4 comments 11 replies
-
After looking at this a bit further, it appears that all I need to do is obtain the
I assume that once I get this document, I can run validation on it to make sure it's valid. |
Beta Was this translation helpful? Give feedback.
-
Parsing is synchronous in Langium, but loading something from a remote resource likely isn't. How we usually do this (i.e. additional resources from something like a network resource), is by overriding the |
Beta Was this translation helpful? Give feedback.
-
@msujew, a couple of questions. Sorry for my lack of expertise here.
Also, my general design on this feature is to follow the methodology described in the docs here. I assume that this is the approach that would be recommended for my case, with the exception of how I retrieve the resource for reference from my remote endpoint. |
Beta Was this translation helpful? Give feedback.
-
Hey, we're doing something similar: loading documents that were not in the originally defined workspace.
Feedback appreciated! Hope it helps others that go down that path as well! Below a code sample: export function createJayveeServices(context: DefaultSharedModuleContext): {
shared: LangiumSharedServices;
Jayvee: JayveeServices;
} {
const shared = inject(
createDefaultSharedModule(context),
JayveeGeneratedSharedModule,
JayveeSharedModule,
);
const Jayvee = inject(
createDefaultModule({ shared }),
JayveeGeneratedModule,
JayveeModule,
);
shared.ServiceRegistry.register(Jayvee);
addDynamicFileImport(Jayvee); // <<< adds the hook
return { shared, Jayvee };
}
export function addDynamicFileImport(services: JayveeServices): void {
const documentBuilder = services.shared.workspace.DocumentBuilder;
const importResolver = services.ImportResolver;
documentBuilder.onBuildPhase(DocumentState.IndexedContent, async (docs) => {
for (const doc of docs) {
const model = doc.parseResult.value;
if (!isJayveeModel(model)) {
return;
}
const importURIs = importResolver.findUnresolvedImportURIs(model);
for (const importURI of importURIs) {
await loadDocumentFromFs(importURI, services);
}
}
});
}
async function loadDocumentFromFs(
importURI: URI,
services: JayveeServices,
): Promise<void> {
const allowedFileExtensions = services.shared.ServiceRegistry.all.flatMap(
(e) => e.LanguageMetaData.fileExtensions,
);
const hasAllowedFileExtension = allowedFileExtensions.some((ext) =>
importURI.fsPath.endsWith(ext),
);
if (!hasAllowedFileExtension) {
return;
}
const langiumDocuments = services.shared.workspace.LangiumDocuments;
const documentBuilder = services.shared.workspace.DocumentBuilder;
const documentFactory = services.shared.workspace.LangiumDocumentFactory;
const file = await loadFileFromUri(importURI, services);
if (file === undefined) {
return;
}
const document = documentFactory.fromString<JayveeModel>(file, importURI);
await documentBuilder.build([document], { validation: true });
await services.shared.workspace.WorkspaceLock.write(() => {
if (!langiumDocuments.hasDocument(document.uri)) {
langiumDocuments.addDocument(document);
}
});
}
async function loadFileFromUri(
uri: URI,
services: JayveeServices,
): Promise<string | undefined> {
const fileSystemProvider = services.shared.workspace.FileSystemProvider;
try {
return await fileSystemProvider.readFile(uri);
} catch (e) {
return undefined;
}
} |
Beta Was this translation helpful? Give feedback.
An unresolved import points to a document (or multiple documents) that hasn't been loaded yet.
I've updated the code snippet above. There was a small error. It was supposed to be used for the
documentBuilder.update
call.