// ***************************************************************************** // Copyright (C) 2018 Red Hat, Inc. and others. // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License v. 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0. // // This Source Code may also be made available under the following Secondary // Licenses when the conditions for such availability set forth in the Eclipse // Public License v. 2.0 are satisfied: GNU General Public License, version 2 // with the GNU Classpath Exception which is available at // https://www.gnu.org/software/classpath/license.html. // // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** import { injectable, inject, named } from '@theia/core/shared/inversify'; import { ContributionProvider } from '@theia/core'; import { DebugConfiguration } from './debug-configuration'; import { DebuggerDescription, DebugError } from './debug-service'; import { DebugAdapterContribution, DebugAdapterExecutable, DebugAdapterSessionFactory } from './debug-model'; import { IJSONSchema, IJSONSchemaSnippet } from '@theia/core/lib/common/json-schema'; /** * Contributions registry. */ @injectable() export class DebugAdapterContributionRegistry { @inject(ContributionProvider) @named(DebugAdapterContribution) protected readonly contributions: ContributionProvider; protected *getContributions(debugType: string): IterableIterator { for (const contribution of this.contributions.getContributions()) { if (contribution.type === debugType || contribution.type === '*' || debugType === '*') { yield contribution; } } } /** * Finds and returns an array of registered debug types. * @returns An array of registered debug types */ protected _debugTypes: string[] | undefined; debugTypes(): string[] { if (!this._debugTypes) { const result = new Set(); for (const contribution of this.contributions.getContributions()) { result.add(contribution.type); } this._debugTypes = [...result]; } return this._debugTypes; } async getDebuggersForLanguage(language: string): Promise { const debuggers: DebuggerDescription[] = []; for (const contribution of this.contributions.getContributions()) { if (contribution.languages && contribution.label) { const label = await contribution.label; if (label && (await contribution.languages || []).indexOf(language) !== -1) { debuggers.push({ type: contribution.type, label }); } } } return debuggers; } /** * Provides initial [debug configuration](#DebugConfiguration). * @param debugType The registered debug type * @returns An array of [debug configurations](#DebugConfiguration) */ async provideDebugConfigurations(debugType: string, workspaceFolderUri?: string): Promise { const configurations: DebugConfiguration[] = []; for (const contribution of this.getContributions(debugType)) { if (contribution.provideDebugConfigurations) { try { const result = await contribution.provideDebugConfigurations(workspaceFolderUri); configurations.push(...result); } catch (e) { console.error('provideDebugConfigurations failed:', e); } } } return configurations; } /** * Resolves a [debug configuration](#DebugConfiguration) by filling in missing values * or by adding/changing/removing attributes before variable substitution. * @param debugConfiguration The [debug configuration](#DebugConfiguration) to resolve. * @returns The resolved debug configuration. */ async resolveDebugConfiguration(config: DebugConfiguration, workspaceFolderUri?: string): Promise { let current = config; for (const contribution of this.getContributions(config.type)) { if (contribution.resolveDebugConfiguration) { try { const next = await contribution.resolveDebugConfiguration(config, workspaceFolderUri); if (next) { current = next; } else { return current; } } catch (e) { console.error('resolveDebugConfiguration failed:', e); } } } return current; } /** * Resolves a [debug configuration](#DebugConfiguration) by filling in missing values * or by adding/changing/removing attributes with substituted variables. * @param debugConfiguration The [debug configuration](#DebugConfiguration) to resolve. * @returns The resolved debug configuration. */ async resolveDebugConfigurationWithSubstitutedVariables(config: DebugConfiguration, workspaceFolderUri?: string): Promise { let current = config; for (const contribution of this.getContributions(config.type)) { if (contribution.resolveDebugConfigurationWithSubstitutedVariables) { try { const next = await contribution.resolveDebugConfigurationWithSubstitutedVariables(config, workspaceFolderUri); if (next) { current = next; } else { return current; } } catch (e) { console.error('resolveDebugConfigurationWithSubstitutedVariables failed:', e); } } } return current; } /** * Provides schema attributes. * @param debugType The registered debug type * @returns Schema attributes for the given debug type */ async getSchemaAttributes(debugType: string): Promise { const schemas: IJSONSchema[] = []; for (const contribution of this.getContributions(debugType)) { if (contribution.getSchemaAttributes) { try { schemas.push(...await contribution.getSchemaAttributes()); } catch (e) { console.error('getSchemaAttributes failed:', e); } } } return schemas; } async getConfigurationSnippets(): Promise { const schemas: IJSONSchemaSnippet[] = []; for (const contribution of this.getContributions('*')) { if (contribution.getConfigurationSnippets) { try { schemas.push(...await contribution.getConfigurationSnippets()); } catch (e) { console.error('getConfigurationSnippets failed:', e); } } } return schemas; } /** * Provides a [debug adapter executable](#DebugAdapterExecutable) * based on [debug configuration](#DebugConfiguration) to launch a new debug adapter. * @param config The resolved [debug configuration](#DebugConfiguration). * @returns The [debug adapter executable](#DebugAdapterExecutable). */ async provideDebugAdapterExecutable(config: DebugConfiguration): Promise { for (const contribution of this.getContributions(config.type)) { if (contribution.provideDebugAdapterExecutable) { const executable = await contribution.provideDebugAdapterExecutable(config); if (executable) { return executable; } } } throw DebugError.NotFound(config.type); } /** * Returns a [debug adapter session factory](#DebugAdapterSessionFactory). * @param debugType The registered debug type * @returns An [debug adapter session factory](#DebugAdapterSessionFactory) */ debugAdapterSessionFactory(debugType: string): DebugAdapterSessionFactory | undefined { for (const contribution of this.getContributions(debugType)) { if (contribution.debugAdapterSessionFactory) { return contribution.debugAdapterSessionFactory; } } return undefined; } }