UNPKG

langium

Version:

A language engineering tool for the Language Server Protocol

118 lines (101 loc) 4.53 kB
/****************************************************************************** * Copyright 2021 TypeFox GmbH * This program and the accompanying materials are made available under the * terms of the MIT License, which is available in the project root. ******************************************************************************/ import type { LangiumCoreServices, LangiumSharedCoreServices } from './services.js'; import type { TextDocumentProvider } from './workspace/documents.js'; import { UriUtils, type URI } from './utils/uri-utils.js'; /** * The service registry provides access to the language-specific {@link LangiumCoreServices} optionally including LSP-related services. * These are resolved via the URI of a text document. */ export interface ServiceRegistry { /** * Register a language via its injected services. */ register(language: LangiumCoreServices): void; /** * Retrieve the language-specific services for the given URI. In case only one language is * registered, it may be used regardless of the URI format. */ getServices(uri: URI): LangiumCoreServices; /** * Check whether services are available for the given URI. */ hasServices(uri: URI): boolean; /** * The full set of registered language services. */ readonly all: readonly LangiumCoreServices[]; } /** * Generic registry for Langium services, but capable of being used with extending service sets as well (such as the lsp-complete LangiumCoreServices set) */ export class DefaultServiceRegistry implements ServiceRegistry { protected readonly languageIdMap = new Map<string, LangiumCoreServices>(); protected readonly fileExtensionMap = new Map<string, LangiumCoreServices>(); protected readonly fileNameMap = new Map<string, LangiumCoreServices>(); /** * @deprecated Since 3.1.0. Use the new `fileExtensionMap` (or `languageIdMap`) property instead. */ protected get map(): Map<string, LangiumCoreServices> | undefined { return this.fileExtensionMap; } protected readonly textDocuments?: TextDocumentProvider; constructor(services?: LangiumSharedCoreServices) { this.textDocuments = services?.workspace.TextDocuments; } register(language: LangiumCoreServices): void { const data = language.LanguageMetaData; for (const ext of data.fileExtensions) { if (this.fileExtensionMap.has(ext)) { console.warn(`The file extension ${ext} is used by multiple languages. It is now assigned to '${data.languageId}'.`); } this.fileExtensionMap.set(ext, language); } if (data.fileNames) { for (const name of data.fileNames) { if (this.fileNameMap.has(name)) { console.warn(`The file name ${name} is used by multiple languages. It is now assigned to '${data.languageId}'.`); } this.fileNameMap.set(name, language); } } this.languageIdMap.set(data.languageId, language); } getServices(uri: URI): LangiumCoreServices { if (this.languageIdMap.size === 0) { throw new Error('The service registry is empty. Use `register` to register the services of a language.'); } const languageId = this.textDocuments?.get(uri)?.languageId; if (languageId !== undefined) { const services = this.languageIdMap.get(languageId); if (services) { return services; } } const ext = UriUtils.extname(uri); const name = UriUtils.basename(uri); const services = this.fileNameMap.get(name) ?? this.fileExtensionMap.get(ext); if (!services) { if (languageId) { throw new Error(`The service registry contains no services for the extension '${ext}' for language '${languageId}'.`); } else { throw new Error(`The service registry contains no services for the extension '${ext}'.`); } } return services; } hasServices(uri: URI): boolean { try { this.getServices(uri); return true; } catch { return false; } } get all(): readonly LangiumCoreServices[] { return Array.from(this.languageIdMap.values()); } }