@theia/monaco
Version:
Theia - Monaco Extension
232 lines • 14.4 kB
JavaScript
;
// *****************************************************************************
// Copyright (C) 2023 STMicroelectronics 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
// *****************************************************************************
Object.defineProperty(exports, "__esModule", { value: true });
exports.MonacoInit = exports.contentHoverWidgetPatcher = void 0;
const tslib_1 = require("tslib");
/*
* The code in this file is responsible for overriding service implementations in the Monaco editor with our own Theia-based implementations.
* Since we only get a single chance to call `StandaloneServices.initialize()` with our overrides, we need to make sure that initialize is called before the first call to
* `StandaloneServices.get()` or `StandaloneServices.initialize()`. As we do not control the mechanics of Inversify instance constructions, the approach here is to call
* `MonacoInit.init()` from the `index.js` file after all container modules are loaded, but before the first object is fetched from it.
* `StandaloneServices.initialize()` is called with service descriptors, not service instances. This lets us finish all overrides before any inversify object is constructed and
* might call `initialize()` while being constructed.
* The service descriptors require a constructor function, so we declare dummy class for each Monaco service we override. But instead of returning an instance of the dummy class,
* we fetch the implementation of the monaco service from the inversify container.
* The inversify-constructed services must not call StandaloneServices.get() or StandaloneServices.initialize() from their constructors. Calling `get`()` in postConstruct methods
* is allowed.
*/
// Monaco's localization override is handled by a webpack alias that replaces
// @theia/monaco-editor-core/esm/vs/nls with packages/monaco/src/browser/monaco-nls.ts.
// See webpack-generator.ts for the alias configuration.
const inversify_1 = require("@theia/core/shared/inversify");
const codeEditorService_1 = require("@theia/monaco-editor-core/esm/vs/editor/browser/services/codeEditorService");
const standaloneServices_1 = require("@theia/monaco-editor-core/esm/vs/editor/standalone/browser/standaloneServices");
const descriptors_1 = require("@theia/monaco-editor-core/esm/vs/platform/instantiation/common/descriptors");
const instantiation_1 = require("@theia/monaco-editor-core/esm/vs/platform/instantiation/common/instantiation");
const monaco_editor_service_1 = require("./monaco-editor-service");
const configuration_1 = require("@theia/monaco-editor-core/esm/vs/platform/configuration/common/configuration");
const resolverService_1 = require("@theia/monaco-editor-core/esm/vs/editor/common/services/resolverService");
const monaco_frontend_module_1 = require("./monaco-frontend-module");
const monaco_text_model_service_1 = require("./monaco-text-model-service");
const monaco_context_menu_1 = require("./monaco-context-menu");
const contextView_1 = require("@theia/monaco-editor-core/esm/vs/platform/contextview/browser/contextView");
const contextkey_1 = require("@theia/monaco-editor-core/esm/vs/platform/contextkey/common/contextkey");
const themeService_1 = require("@theia/monaco-editor-core/esm/vs/platform/theme/common/themeService");
const monaco_bulk_edit_service_1 = require("./monaco-bulk-edit-service");
const monaco_command_service_1 = require("./monaco-command-service");
const bulkEditService_1 = require("@theia/monaco-editor-core/esm/vs/editor/browser/services/bulkEditService");
const commands_1 = require("@theia/monaco-editor-core/esm/vs/platform/commands/common/commands");
const monaco_quick_input_service_1 = require("./monaco-quick-input-service");
const quickInput_1 = require("@theia/monaco-editor-core/esm/vs/platform/quickinput/common/quickInput");
const standaloneTheme_1 = require("@theia/monaco-editor-core/esm/vs/editor/standalone/common/standaloneTheme");
const monaco_standalone_theme_service_1 = require("./monaco-standalone-theme-service");
const content_hover_widget_patcher_1 = require("./content-hover-widget-patcher");
const hover_1 = require("@theia/monaco-editor-core/esm/vs/platform/hover/browser/hover");
const hoverDelegate2_1 = require("@theia/monaco-editor-core/esm/vs/base/browser/ui/hover/hoverDelegate2");
const workspace_1 = require("@theia/monaco-editor-core/esm/vs/platform/workspace/common/workspace");
const monaco_workspace_context_service_1 = require("./monaco-workspace-context-service");
const layoutService_1 = require("@theia/monaco-editor-core/esm/vs/platform/layout/browser/layoutService");
const event_1 = require("@theia/monaco-editor-core/esm/vs/base/common/event");
const dom = require("@theia/monaco-editor-core/esm/vs/base/browser/dom");
const window_1 = require("@theia/monaco-editor-core/esm/vs/base/browser/window");
exports.contentHoverWidgetPatcher = (0, content_hover_widget_patcher_1.createContentHoverWidgetPatcher)();
let MonacoEditorServiceConstructor = class MonacoEditorServiceConstructor {
/**
* MonacoEditorService needs other Monaco services as constructor parameters, so we need to do use a factory for constructing the service. If we want the singleton instance,
* we need to fetch it from the `StandaloneServices` class instead of injecting it.
* @param container
* @param contextKeyService
* @param themeService
*/
constructor(container, contextKeyService, themeService) {
return container.get(monaco_editor_service_1.MonacoEditorServiceFactory)(contextKeyService, themeService);
}
;
};
MonacoEditorServiceConstructor = tslib_1.__decorate([
tslib_1.__param(1, contextkey_1.IContextKeyService),
tslib_1.__param(2, themeService_1.IThemeService),
tslib_1.__metadata("design:paramtypes", [inversify_1.Container, Object, Object])
], MonacoEditorServiceConstructor);
class MonacoConfigurationServiceConstructor {
constructor(container) {
return container.get(monaco_frontend_module_1.MonacoConfigurationService);
}
}
class MonacoTextModelServiceConstructor {
constructor(container) {
return container.get(monaco_text_model_service_1.MonacoTextModelService);
}
}
class MonacoContextMenuServiceConstructor {
constructor(container) {
return container.get(monaco_context_menu_1.MonacoContextMenuService);
}
}
class MonacoBulkEditServiceConstructor {
constructor(container) {
return container.get(monaco_bulk_edit_service_1.MonacoBulkEditService);
}
}
class MonacoCommandServiceConstructor {
constructor(container) {
return container.get(monaco_command_service_1.MonacoCommandService);
}
}
class MonacoQuickInputImplementationConstructor {
constructor(container) {
return container.get(monaco_quick_input_service_1.MonacoQuickInputImplementation);
}
}
class MonacoStandaloneThemeServiceConstructor {
constructor(container) {
return new monaco_standalone_theme_service_1.MonacoStandaloneThemeService();
}
}
class MonacoWorkspaceContextServiceConstructor {
constructor(container) {
return container.get(monaco_workspace_context_service_1.MonacoWorkspaceContextService);
}
}
/**
* Layout service that returns the Theia application shell as the main container
* instead of the first Monaco editor's container DOM node. This ensures that
* Monaco UI elements like the quick input are positioned relative to the full
* application layout rather than a single editor.
*/
class MonacoLayoutService {
constructor() {
this.onDidLayoutMainContainer = event_1.Event.None;
this.onDidLayoutActiveContainer = event_1.Event.None;
this.onDidLayoutContainer = event_1.Event.None;
this.onDidChangeActiveContainer = event_1.Event.None;
this.onDidAddContainer = event_1.Event.None;
this.mainContainerOffset = { top: 0, quickPickTop: 0 };
this.activeContainerOffset = { top: 0, quickPickTop: 0 };
}
get mainContainer() {
return window_1.mainWindow.document.getElementById('theia-app-shell') ?? window_1.mainWindow.document.body;
}
get activeContainer() {
return this.mainContainer;
}
get mainContainerDimension() {
return dom.getClientArea(this.mainContainer);
}
get activeContainerDimension() {
return dom.getClientArea(this.activeContainer);
}
get containers() {
return [this.mainContainer];
}
getContainer() {
return this.activeContainer;
}
whenContainerStylesLoaded() {
return undefined;
}
focus() {
this.mainContainer.focus();
}
}
class MonacoLayoutServiceConstructor {
constructor() {
return new MonacoLayoutService();
}
}
var MonacoInit;
(function (MonacoInit) {
function init(container) {
const overrides = {
[codeEditorService_1.ICodeEditorService.toString()]: new descriptors_1.SyncDescriptor(MonacoEditorServiceConstructor, [container]),
[configuration_1.IConfigurationService.toString()]: new descriptors_1.SyncDescriptor(MonacoConfigurationServiceConstructor, [container]),
[resolverService_1.ITextModelService.toString()]: new descriptors_1.SyncDescriptor(MonacoTextModelServiceConstructor, [container]),
[contextView_1.IContextMenuService.toString()]: new descriptors_1.SyncDescriptor(MonacoContextMenuServiceConstructor, [container]),
[bulkEditService_1.IBulkEditService.toString()]: new descriptors_1.SyncDescriptor(MonacoBulkEditServiceConstructor, [container]),
[commands_1.ICommandService.toString()]: new descriptors_1.SyncDescriptor(MonacoCommandServiceConstructor, [container]),
[quickInput_1.IQuickInputService.toString()]: new descriptors_1.SyncDescriptor(MonacoQuickInputImplementationConstructor, [container]),
[standaloneTheme_1.IStandaloneThemeService.toString()]: new descriptors_1.SyncDescriptor(MonacoStandaloneThemeServiceConstructor, []),
[workspace_1.IWorkspaceContextService.toString()]: new descriptors_1.SyncDescriptor(MonacoWorkspaceContextServiceConstructor, [container]),
[layoutService_1.ILayoutService.toString()]: new descriptors_1.SyncDescriptor(MonacoLayoutServiceConstructor, [])
};
// Try the standard initialization path first.
standaloneServices_1.StandaloneServices.initialize(overrides);
// If StandaloneServices was already initialized (e.g., by a premature StandaloneServices.get() call
// triggered as a side-effect during module loading), the call above is a no-op and our overrides are
// silently dropped. Detect this situation, warn about it, and inject our service descriptors directly
// into the internal service collection so that they are used when the services are next resolved.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const instantiationService = standaloneServices_1.StandaloneServices.get(instantiation_1.IInstantiationService);
const serviceCollection = instantiationService?._services;
if (serviceCollection) {
const patchedServices = [];
const alreadyInstantiatedServices = [];
for (const serviceId of Object.keys(overrides)) {
const serviceIdentifier = (0, instantiation_1.createDecorator)(serviceId);
const existing = serviceCollection.get(serviceIdentifier);
if (existing instanceof descriptors_1.SyncDescriptor && existing !== overrides[serviceId]) {
// The override was not applied by initialize() – patch it in manually.
serviceCollection.set(serviceIdentifier, overrides[serviceId]);
patchedServices.push(serviceId);
}
else if (existing !== undefined && !(existing instanceof descriptors_1.SyncDescriptor) && existing !== overrides[serviceId]) {
// The service was already instantiated – we cannot override it anymore.
alreadyInstantiatedServices.push(serviceId);
}
}
if (patchedServices.length > 0) {
console.warn('StandaloneServices was already initialized before MonacoInit.init() was called. '
+ 'This typically happens when a StandaloneServices.get() call is triggered as a side-effect during module loading. '
+ 'The following Theia service overrides had to be patched in after the fact: '
+ patchedServices.join(', ')
+ '. Investigate the module loading order to prevent premature initialization.');
}
if (alreadyInstantiatedServices.length > 0) {
console.error('StandaloneServices was already initialized and the following services were already instantiated '
+ 'before MonacoInit.init() could apply Theia overrides: '
+ alreadyInstantiatedServices.join(', ')
+ '. These services are using the default Monaco implementations instead of Theia\'s. '
+ 'This may cause unexpected behavior. Investigate which code triggers premature service resolution.');
}
}
// Make sure the global base hover delegate is initialized as otherwise the quick input will throw an error and not update correctly
// in case no Monaco editor was constructed before and items with keybindings are shown. See #15042.
(0, hoverDelegate2_1.setBaseLayerHoverDelegate)(standaloneServices_1.StandaloneServices.get(hover_1.IHoverService));
}
MonacoInit.init = init;
})(MonacoInit || (exports.MonacoInit = MonacoInit = {}));
//# sourceMappingURL=monaco-init.js.map