import { ContainerModule } from '@theia/core/shared/inversify'; import { ArduinoDaemonImpl } from './arduino-daemon-impl'; import { ArduinoFirmwareUploader, ArduinoFirmwareUploaderPath, } from '../common/protocol/arduino-firmware-uploader'; import { ILogger } from '@theia/core/lib/common/logger'; import { BackendApplicationContribution, BackendApplication as TheiaBackendApplication, } from '@theia/core/lib/node/backend-application'; import { LibraryService, LibraryServicePath, } from '../common/protocol/library-service'; import { BoardsService, BoardsServicePath, } from '../common/protocol/boards-service'; import { LibraryServiceImpl } from './library-service-impl'; import { BoardsServiceImpl } from './boards-service-impl'; import { CoreServiceImpl } from './core-service-impl'; import { CoreService, CoreServicePath } from '../common/protocol/core-service'; import { ConnectionContainerModule } from '@theia/core/lib/node/messaging/connection-container-module'; import { CoreClientProvider } from './core-client-provider'; import { ConnectionHandler, JsonRpcConnectionHandler } from '@theia/core'; import { DefaultWorkspaceServer } from './theia/workspace/default-workspace-server'; import { WorkspaceServer as TheiaWorkspaceServer } from '@theia/workspace/lib/common'; import { SketchesServiceImpl } from './sketches-service-impl'; import { SketchesService, SketchesServicePath, } from '../common/protocol/sketches-service'; import { ConfigService, ConfigServicePath, } from '../common/protocol/config-service'; import { ArduinoDaemon, ArduinoDaemonPath, } from '../common/protocol/arduino-daemon'; import { ConfigServiceImpl } from './config-service-impl'; import { EnvVariablesServer as TheiaEnvVariablesServer } from '@theia/core/lib/common/env-variables'; import { EnvVariablesServer } from './theia/env-variables/env-variables-server'; import { NodeFileSystemExt } from './node-filesystem-ext'; import { FileSystemExt, FileSystemExtPath, } from '../common/protocol/filesystem-ext'; import { BuiltInExamplesServiceImpl, ExamplesServiceImpl, } from './examples-service-impl'; import { ExamplesService, ExamplesServicePath, } from '../common/protocol/examples-service'; import { ExecutableService, ExecutableServicePath, } from '../common/protocol/executable-service'; import { ExecutableServiceImpl } from './executable-service-impl'; import { ResponseServicePath, ResponseService, } from '../common/protocol/response-service'; import { NotificationServiceServerImpl } from './notification-service-server'; import { NotificationServiceServer, NotificationServiceClient, NotificationServicePath, } from '../common/protocol'; import { BackendApplication } from './theia/core/backend-application'; import { BoardDiscovery } from './board-discovery'; import { AuthenticationServiceImpl } from './auth/authentication-service-impl'; import { AuthenticationService, AuthenticationServiceClient, AuthenticationServicePath, } from '../common/protocol/authentication-service'; import { ArduinoFirmwareUploaderImpl } from './arduino-firmware-uploader-impl'; import { PlotterBackendContribution } from './plotter/plotter-backend-contribution'; import { ArduinoLocalizationContribution } from './i18n/arduino-localization-contribution'; import { LocalizationContribution } from '@theia/core/lib/node/i18n/localization-contribution'; import { MonitorManagerProxyImpl } from './monitor-manager-proxy-impl'; import { MonitorManager, MonitorManagerName } from './monitor-manager'; import { MonitorManagerProxy, MonitorManagerProxyClient, MonitorManagerProxyPath, } from '../common/protocol/monitor-service'; import { MonitorService, MonitorServiceName } from './monitor-service'; import { MonitorSettingsProvider } from './monitor-settings/monitor-settings-provider'; import { MonitorSettingsProviderImpl } from './monitor-settings/monitor-settings-provider-impl'; import { MonitorServiceFactory, MonitorServiceFactoryOptions, } from './monitor-service-factory'; import WebSocketProviderImpl from './web-socket/web-socket-provider-impl'; import { WebSocketProvider } from './web-socket/web-socket-provider'; import { ClangFormatter } from './clang-formatter'; import { FormatterPath } from '../common/protocol/formatter'; import { LocalizationBackendContribution } from './i18n/localization-backend-contribution'; import { LocalizationBackendContribution as TheiaLocalizationBackendContribution } from '@theia/core/lib/node/i18n/localization-backend-contribution'; export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(BackendApplication).toSelf().inSingletonScope(); rebind(TheiaBackendApplication).toService(BackendApplication); // Shared config service bind(ConfigServiceImpl).toSelf().inSingletonScope(); bind(ConfigService).toService(ConfigServiceImpl); // Note: The config service must start earlier than the daemon, hence the binding order of the BA contribution does matter. bind(BackendApplicationContribution).toService(ConfigServiceImpl); bind(ConnectionHandler) .toDynamicValue( (context) => new JsonRpcConnectionHandler(ConfigServicePath, () => context.container.get(ConfigService) ) ) .inSingletonScope(); // Shared daemon bind(ArduinoDaemonImpl).toSelf().inSingletonScope(); bind(ArduinoDaemon).toService(ArduinoDaemonImpl); bind(BackendApplicationContribution).toService(ArduinoDaemonImpl); bind(ConnectionHandler) .toDynamicValue( (context) => new JsonRpcConnectionHandler(ArduinoDaemonPath, () => context.container.get(ArduinoDaemon) ) ) .inSingletonScope(); // Shared formatter bind(ClangFormatter).toSelf().inSingletonScope(); bind(ConnectionHandler) .toDynamicValue( ({ container }) => new JsonRpcConnectionHandler(FormatterPath, () => container.get(ClangFormatter) ) ) .inSingletonScope(); // Built-in examples are not board specific, so it is possible to have one shared instance. bind(BuiltInExamplesServiceImpl).toSelf().inSingletonScope(); // Examples service. One per backend, each connected FE gets a proxy. bind(ConnectionContainerModule).toConstantValue( ConnectionContainerModule.create(({ bind, bindBackendService }) => { bind(ExamplesServiceImpl).toSelf().inSingletonScope(); bind(ExamplesService).toService(ExamplesServiceImpl); bindBackendService(ExamplesServicePath, ExamplesService); }) ); // Exposes the executable paths/URIs to the frontend bind(ExecutableServiceImpl).toSelf().inSingletonScope(); bind(ExecutableService).toService(ExecutableServiceImpl); bind(ConnectionHandler) .toDynamicValue( (context) => new JsonRpcConnectionHandler(ExecutableServicePath, () => context.container.get(ExecutableService) ) ) .inSingletonScope(); // Library service. Singleton per backend, each connected FE gets its proxy. bind(ConnectionContainerModule).toConstantValue( ConnectionContainerModule.create(({ bind, bindBackendService }) => { bind(LibraryServiceImpl).toSelf().inSingletonScope(); bind(LibraryService).toService(LibraryServiceImpl); bindBackendService(LibraryServicePath, LibraryService); }) ); // Shared sketches service bind(SketchesServiceImpl).toSelf().inSingletonScope(); bind(SketchesService).toService(SketchesServiceImpl); bind(ConnectionHandler) .toDynamicValue( (context) => new JsonRpcConnectionHandler(SketchesServicePath, () => context.container.get(SketchesService) ) ) .inSingletonScope(); // Boards service. One instance per connected frontend. bind(ConnectionContainerModule).toConstantValue( ConnectionContainerModule.create(({ bind, bindBackendService }) => { bind(BoardsServiceImpl).toSelf().inSingletonScope(); bind(BoardsService).toService(BoardsServiceImpl); bindBackendService(BoardsServicePath, BoardsService); }) ); // Shared Arduino core client provider service for the backend. bind(CoreClientProvider).toSelf().inSingletonScope(); // Shared port/board discovery for the server bind(BoardDiscovery).toSelf().inSingletonScope(); bind(BackendApplicationContribution).toService(BoardDiscovery); // Core service -> `verify` and `upload`. Singleton per BE, each FE connection gets its proxy. bind(ConnectionContainerModule).toConstantValue( ConnectionContainerModule.create(({ bind, bindBackendService }) => { bind(CoreServiceImpl).toSelf().inSingletonScope(); bind(CoreService).toService(CoreServiceImpl); bindBackendService(CoreServicePath, CoreService); }) ); // #region Theia customizations bind(DefaultWorkspaceServer).toSelf().inSingletonScope(); rebind(TheiaWorkspaceServer).toService(DefaultWorkspaceServer); bind(EnvVariablesServer).toSelf().inSingletonScope(); rebind(TheiaEnvVariablesServer).toService(EnvVariablesServer); // #endregion Theia customizations // a single MonitorManager is responsible for handling the actual connections to the pluggable monitors bind(MonitorManager).toSelf().inSingletonScope(); // monitor service & factory bindings bind(MonitorSettingsProviderImpl).toSelf().inSingletonScope(); bind(MonitorSettingsProvider).toService(MonitorSettingsProviderImpl); bind(WebSocketProviderImpl).toSelf(); bind(WebSocketProvider).toService(WebSocketProviderImpl); bind(MonitorServiceFactory).toFactory( ({ container }) => (options: MonitorServiceFactoryOptions) => { const child = container.createChild(); child .bind<MonitorServiceFactoryOptions>(MonitorServiceFactoryOptions) .toConstantValue({ ...options, }); child.bind(MonitorService).toSelf(); return child.get<MonitorService>(MonitorService); } ); // Serial client provider per connected frontend. bind(ConnectionContainerModule).toConstantValue( ConnectionContainerModule.create(({ bind, bindBackendService }) => { bind(MonitorManagerProxyImpl).toSelf().inSingletonScope(); bind(MonitorManagerProxy).toService(MonitorManagerProxyImpl); bindBackendService<MonitorManagerProxy, MonitorManagerProxyClient>( MonitorManagerProxyPath, MonitorManagerProxy, (monitorMgrProxy, client) => { monitorMgrProxy.setClient(client); // when the client close the connection, the proxy is disposed. // when the MonitorManagerProxy is disposed, it informs the MonitorManager // telling him that it does not need an address/board anymore. // the MonitorManager will then dispose the actual connection if there are no proxies using it client.onDidCloseConnection(() => monitorMgrProxy.dispose()); return monitorMgrProxy; } ); }) ); // File-system extension for mapping paths to URIs bind(NodeFileSystemExt).toSelf().inSingletonScope(); bind(FileSystemExt).toService(NodeFileSystemExt); bind(ConnectionHandler) .toDynamicValue( (context) => new JsonRpcConnectionHandler(FileSystemExtPath, () => context.container.get(FileSystemExt) ) ) .inSingletonScope(); // Output service per connection. bind(ConnectionContainerModule).toConstantValue( ConnectionContainerModule.create(({ bindFrontendService }) => { bindFrontendService(ResponseServicePath, ResponseService); }) ); // Notify all connected frontend instances bind(NotificationServiceServerImpl).toSelf().inSingletonScope(); bind(NotificationServiceServer).toService(NotificationServiceServerImpl); bind(ConnectionHandler) .toDynamicValue( (context) => new JsonRpcConnectionHandler<NotificationServiceClient>( NotificationServicePath, (client) => { const server = context.container.get<NotificationServiceServer>( NotificationServiceServer ); server.setClient(client); client.onDidCloseConnection(() => server.disposeClient(client)); return server; } ) ) .inSingletonScope(); // Singleton per BE, each FE connection gets its proxy. bind(ConnectionContainerModule).toConstantValue( ConnectionContainerModule.create(({ bind, bindBackendService }) => { bind(ArduinoFirmwareUploaderImpl).toSelf().inSingletonScope(); bind(ArduinoFirmwareUploader).toService(ArduinoFirmwareUploaderImpl); bindBackendService(ArduinoFirmwareUploaderPath, ArduinoFirmwareUploader); }) ); // Logger for the Arduino daemon bind(ILogger) .toDynamicValue((ctx) => { const parentLogger = ctx.container.get<ILogger>(ILogger); return parentLogger.child('daemon'); }) .inSingletonScope() .whenTargetNamed('daemon'); // Logger for the Arduino daemon bind(ILogger) .toDynamicValue((ctx) => { const parentLogger = ctx.container.get<ILogger>(ILogger); return parentLogger.child('fwuploader'); }) .inSingletonScope() .whenTargetNamed('fwuploader'); // Logger for the "serial discovery". bind(ILogger) .toDynamicValue((ctx) => { const parentLogger = ctx.container.get<ILogger>(ILogger); return parentLogger.child('discovery-log'); // TODO: revert }) .inSingletonScope() .whenTargetNamed('discovery-log'); // TODO: revert // Logger for the CLI config service. From the CLI config (FS path aware), we make a URI-aware app config. bind(ILogger) .toDynamicValue((ctx) => { const parentLogger = ctx.container.get<ILogger>(ILogger); return parentLogger.child('config'); }) .inSingletonScope() .whenTargetNamed('config'); // Logger for the monitor manager and its services bind(ILogger) .toDynamicValue((ctx) => { const parentLogger = ctx.container.get<ILogger>(ILogger); return parentLogger.child(MonitorManagerName); }) .inSingletonScope() .whenTargetNamed(MonitorManagerName); bind(ILogger) .toDynamicValue((ctx) => { const parentLogger = ctx.container.get<ILogger>(ILogger); return parentLogger.child(MonitorServiceName); }) .inSingletonScope() .whenTargetNamed(MonitorServiceName); // Remote sketchbook bindings bind(AuthenticationServiceImpl).toSelf().inSingletonScope(); bind(AuthenticationService).toService(AuthenticationServiceImpl); bind(BackendApplicationContribution).toService(AuthenticationServiceImpl); bind(ConnectionHandler) .toDynamicValue( (context) => new JsonRpcConnectionHandler<AuthenticationServiceClient>( AuthenticationServicePath, (client) => { const server = context.container.get<AuthenticationServiceImpl>( AuthenticationServiceImpl ); server.setClient(client); client.onDidCloseConnection(() => server.disposeClient(client)); return server; } ) ) .inSingletonScope(); bind(PlotterBackendContribution).toSelf().inSingletonScope(); bind(BackendApplicationContribution).toService(PlotterBackendContribution); bind(ArduinoLocalizationContribution).toSelf().inSingletonScope(); bind(LocalizationContribution).toService(ArduinoLocalizationContribution); bind(LocalizationBackendContribution).toSelf().inSingletonScope(); rebind(TheiaLocalizationBackendContribution).toService( LocalizationBackendContribution ); });