org.metaborg.intellij.idea.languages.DefaultIdeaLanguageManager.java Source code

Java tutorial

Introduction

Here is the source code for org.metaborg.intellij.idea.languages.DefaultIdeaLanguageManager.java

Source

/*
 * Copyright  2015-2016
 *
 * This file is part of Spoofax for IntelliJ.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 */

package org.metaborg.intellij.idea.languages;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import com.intellij.lang.Language;
import com.intellij.lang.ParserDefinition;
import com.intellij.openapi.actionSystem.Anchor;
import com.intellij.openapi.actionSystem.DefaultActionGroup;
import com.intellij.openapi.actionSystem.IdeActions;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.tree.IFileElementType;
import javassist.util.proxy.ProxyFactory;
import org.apache.commons.vfs2.FileObject;
import org.apache.commons.vfs2.FileSystemException;
import org.metaborg.core.MetaborgException;
import org.metaborg.core.language.*;
import org.metaborg.intellij.UnhandledException;
import org.metaborg.intellij.discovery.ILanguageSource;
import org.metaborg.intellij.idea.SpoofaxIdeaPlugin;
import org.metaborg.intellij.idea.actions.ActionUtils;
import org.metaborg.intellij.idea.actions.BuilderMenuBuilder;
import org.metaborg.intellij.idea.extensions.ExtensionIds;
import org.metaborg.intellij.idea.extensions.ExtensionUtils;
import org.metaborg.intellij.idea.extensions.InstanceLanguageExtensionPoint;
import org.metaborg.intellij.idea.extensions.InstanceSyntaxHighlighterFactoryExtensionPoint;
import org.metaborg.intellij.idea.filetypes.FileTypeUtils;
import org.metaborg.intellij.idea.filetypes.MetaborgLanguageFileType;
import org.metaborg.intellij.idea.parsing.IParserDefinitionFactory;
import org.metaborg.intellij.idea.parsing.SpoofaxSyntaxHighlighterFactory;
import org.metaborg.intellij.idea.parsing.annotations.MetaborgSourceAnnotator;
import org.metaborg.intellij.idea.parsing.elements.IFileElementTypeFactory;
import org.metaborg.intellij.idea.parsing.elements.SpoofaxTokenTypeManager;
import org.metaborg.intellij.idea.projects.IdeaLanguageSpec;
import org.metaborg.intellij.languages.DefaultLanguageManager;
import org.metaborg.intellij.languages.ILanguageManager;
import org.metaborg.intellij.languages.LanguageLoadingFailedException;
import org.metaborg.intellij.languages.LanguageUtils2;
import org.metaborg.intellij.logging.InjectLogger;
import org.metaborg.intellij.logging.LoggerUtils2;
import org.metaborg.intellij.resources.IIntelliJResourceService;
import org.metaborg.util.log.ILogger;

import javax.annotation.Nullable;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
import java.util.Collections;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException;

/**
 * Default implementation of the {@link ILanguageManager} interface.
 *
 * This implementation is thread-safe.
 */
@Singleton
public final class DefaultIdeaLanguageManager extends DefaultLanguageManager
        implements IIdeaLanguageManager, ILanguageBindingManager {

    // FIXME: The ILanguageService and ILanguageDiscoveryService implementations should be thread-safe too.

    private final Object objectLock = new Object();
    private final ProxyFactory proxyFactory;
    private final MetaborgSourceAnnotator metaborgSourceAnnotator;
    private final IFileElementTypeFactory fileElementTypeFactory;
    private final IParserDefinitionFactory parserDefinitionFactory;
    private final Provider<SpoofaxSyntaxHighlighterFactory> syntaxHighlighterFactoryProvider;
    private final BuilderMenuBuilder builderMenuBuilder;
    private final IIntelliJResourceService resourceService;
    private final ActionUtils actionUtils;
    private final ConcurrentMap<ILanguage, LanguageBindings> loadedLanguages = new ConcurrentHashMap<>();
    private final ConcurrentMap<ILanguageImpl, LanguageImplBindings> loadedLanguageImpls = new ConcurrentHashMap<>();
    private final Cache<ILanguage, LanguageBindings> ideaLanguageCache = CacheBuilder.newBuilder().weakKeys()
            .build();
    private final Cache<ILanguageImpl, LanguageImplBindings> ideaLanguageImplCache = CacheBuilder.newBuilder()
            .weakKeys().build();

    @InjectLogger
    private ILogger logger;

    /**
     * Initializes a new instance of the {@link DefaultIdeaLanguageManager} class.
     */
    @Inject
    public DefaultIdeaLanguageManager(final ILanguageService languageService, final ILanguageSource languageSource,
            final ILanguageDiscoveryService discoveryService, final IIntelliJResourceService resourceService,
            final MetaborgSourceAnnotator metaborgSourceAnnotator,
            final IFileElementTypeFactory fileElementTypeFactory,
            final IParserDefinitionFactory parserDefinitionFactory,
            final Provider<SpoofaxSyntaxHighlighterFactory> syntaxHighlighterFactoryProvider,
            final BuilderMenuBuilder builderMenuBuilder, final ActionUtils actionUtils) {
        super(languageService, languageSource, discoveryService);
        this.metaborgSourceAnnotator = metaborgSourceAnnotator;
        this.fileElementTypeFactory = fileElementTypeFactory;
        this.parserDefinitionFactory = parserDefinitionFactory;
        this.syntaxHighlighterFactoryProvider = syntaxHighlighterFactoryProvider;
        this.builderMenuBuilder = builderMenuBuilder;
        this.actionUtils = actionUtils;
        this.resourceService = resourceService;

        this.proxyFactory = new ProxyFactory();
        this.proxyFactory.setUseCache(false);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Collection<ILanguage> getActiveLanguages() {
        return Collections.unmodifiableSet(this.loadedLanguages.keySet());
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean isActive(final ILanguage language) {
        return this.loadedLanguages.containsKey(language);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void unload(final ILanguageComponent component) {
        synchronized (this.objectLock) {
            assertComponentDoesNotContributeToActiveLanguages(component);

            super.unload(component);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void activate(final ILanguage language) {
        // Check to prevent locking if there's nothing to do.
        if (!canActivate(language))
            return;

        synchronized (this.objectLock) {
            // The language may have been activated (or unloaded) before we acquired the lock.
            // So we check again, this time within the lock.
            if (!canActivate(language))
                return;

            this.logger.debug("Activating language: {}", language);

            // Ensure all bindings are created.
            final LanguageBindings activatedLanguage = getOrCreateIdeaLanguage(language);
            this.loadedLanguages.put(language, activatedLanguage);
            for (final ILanguageImpl implementation : language.impls()) {
                final LanguageImplBindings activatedLanguageImpl = getOrCreateIdeaLanguageImpl(implementation);
                this.loadedLanguageImpls.put(implementation, activatedLanguageImpl);
            }

            // Activate all languages.
            activateLanguage(getBindings(language));
            for (final ILanguageImpl implementation : language.impls()) {
                activateLanguageImpl(getBindings(implementation));
            }

            this.logger.info("Activated language: {}", language);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void deactivate(final ILanguage language) {
        // Check to prevent locking if there's nothing to do.
        if (!canDeactivate(language))
            return;

        synchronized (this.objectLock) {
            // The language may have been activated before we acquired the lock.
            // So we check again, this time within the lock.
            if (!canDeactivate(language))
                return;

            this.logger.debug("Deactivating language: {}", language);

            // Deactivate language and remove bindings.
            final LanguageBindings activatedLanguage = this.loadedLanguages.remove(language);
            for (final ILanguageImpl implementation : language.impls()) {
                final LanguageImplBindings activatedLanguageImpl = this.loadedLanguageImpls.remove(implementation);
                deactivateLanguageImpl(activatedLanguageImpl);
            }
            deactivateLanguage(activatedLanguage);
            this.logger.info("Deactivated language: {}", language);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void activateRange(final Iterable<ILanguage> languages) {
        synchronized (this.objectLock) {
            for (final ILanguage language : languages) {
                activate(language);
            }
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void deactivateRange(final Iterable<ILanguage> languages) {
        synchronized (this.objectLock) {
            for (final ILanguage language : languages) {
                deactivate(language);
            }
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public ILanguage getLanguage(final MetaborgIdeaLanguage ideaLanguage) {
        @Nullable
        final ILanguage language = this.languageService.getLanguage(ideaLanguage.getID());
        if (language == null) {
            throw LoggerUtils2.exception(this.logger, IllegalArgumentException.class,
                    "There is no language associated with the specified MetaborgIdeaLanguage: {}", ideaLanguage);
        }
        return language;
    }

    /**
     * Checks whether the given language is currently loaded.
     *
     * @param language
     *            The language to check.
     * @return <code>true</code> if the language is loaded; otherwise, <code>false</code>.
     */
    private boolean isLoadedLanguage(final ILanguage language) {
        // HACK: Reference equality check,
        // until the ILanguageService provides an isLoaded(ILanguage) method.
        return this.languageService.getLanguage(language.name()) == language;
    }

    /**
     * Asserts that the specified component does not contribute to any currently activated languages.
     *
     * @param component
     *            The component to test.
     */
    private void assertComponentDoesNotContributeToActiveLanguages(final ILanguageComponent component) {
        for (final ILanguageImpl languageImpl : component.contributesTo()) {
            if (isActive(languageImpl.belongsTo())) {
                throw LoggerUtils2.exception(this.logger, UnsupportedOperationException.class,
                        "The component contributes to at least one currently active language: {}", component);
            }
        }
    }

    /**
     * Determines whether the specified language can be activated.
     *
     * @param language
     *            The language to test.
     * @return <code>true</code> when the language can be activated; otherwise, <code>false</code>.
     */
    private boolean canActivate(final ILanguage language) {
        if (isActive(language)) {
            this.logger.info("Cannot activate language. Language already active: {}", language);
            return false;
        }
        if (!LanguageUtils2.isRealLanguage(language)) {
            this.logger.warn("Cannot activate language. Language is not a real language: {}", language);
            return false;
        }
        if (!isLoadedLanguage(language)) {
            this.logger.warn("Cannot activate language. Language is not currently loaded: {}", language);
            return false;
        }
        return true;
    }

    /**
     * Determines whether the specified language can be deactivated.
     *
     * @param language
     *            The language to test.
     * @return <code>true</code> when the language can be deactivated; otherwise, <code>false</code>.
     */
    private boolean canDeactivate(final ILanguage language) {
        if (!isActive(language)) {
            this.logger.warn("Cannot deactivate language. Language not active: {}", language);
            return false;
        }
        return true;
    }

    /**
     * Gets the IDEA language object associated with the specified language.
     *
     * If no such object exists, it is created.
     *
     * @param language
     *            The language to look up.
     * @return The associated IDEA language.
     */
    private LanguageBindings getOrCreateIdeaLanguage(final ILanguage language) {
        final LanguageBindings activatedLanguage;
        try {
            activatedLanguage = this.ideaLanguageCache.get(language, () -> createLanguageBindings(language));
        } catch (final ExecutionException ex) {
            throw LoggerUtils2.exception(this.logger, UnhandledException.class,
                    "An unhandled checked exception was thrown from createLanguageBindings().", ex);
        }
        return activatedLanguage;
    }

    /**
     * Gets the IDEA language implementation object associated with the specified language implementation.
     *
     * If no such object exists, it is created.
     *
     * @param languageImpl
     *            The language implementation to look up.
     * @return The associated IDEA language implementation.
     */
    private LanguageImplBindings getOrCreateIdeaLanguageImpl(final ILanguageImpl languageImpl) {
        final LanguageImplBindings activatedLanguageImpl;
        try {
            activatedLanguageImpl = this.ideaLanguageImplCache.get(languageImpl,
                    () -> createLanguageImplBindings(languageImpl));
        } catch (final ExecutionException ex) {
            throw LoggerUtils2.exception(this.logger, UnhandledException.class,
                    "An unhandled checked exception was thrown from createLanguageBindings().", ex);
        }
        return activatedLanguageImpl;
    }

    /**
     * Creates the IDEA language object associated with the specified language.
     *
     * @param language
     *            The language.
     * @return The created IDEA language.
     */
    private LanguageBindings createLanguageBindings(final ILanguage language) {
        final MetaborgIdeaLanguage ideaLanguage = createIdeaLanguage(language);
        final MetaborgLanguageFileType fileType = createFileType(ideaLanguage);
        final SpoofaxTokenTypeManager tokenTypeManager = createTokenTypeManager(ideaLanguage);
        final IFileElementType fileElementType = createFileElementType(ideaLanguage, tokenTypeManager);
        final ParserDefinition parserDefinition = createParserDefinition(fileType, fileElementType);
        final SpoofaxSyntaxHighlighterFactory syntaxHighlighterFactory = createSyntaxHighlighterFactory();

        final InstanceLanguageExtensionPoint<?> parserDefinitionExtension = new InstanceLanguageExtensionPoint<>(
                ExtensionIds.ParserDefinition, ideaLanguage, parserDefinition);
        final InstanceSyntaxHighlighterFactoryExtensionPoint syntaxHighlighterFactoryExtension = new InstanceSyntaxHighlighterFactoryExtensionPoint(
                ideaLanguage, syntaxHighlighterFactory);
        final InstanceLanguageExtensionPoint<?> externalAnnotatorExtension = new InstanceLanguageExtensionPoint<>(
                ExtensionIds.ExternalAnnotator, ideaLanguage, this.metaborgSourceAnnotator);

        return new LanguageBindings(tokenTypeManager, fileType, parserDefinitionExtension,
                syntaxHighlighterFactoryExtension, externalAnnotatorExtension);
    }

    /**
     * Creates the IDEA language implementation object associated with the specified language implementation.
     *
     * @param languageImpl
     *            The language implementation.
     * @return The created IDEA language implementation.
     */
    private LanguageImplBindings createLanguageImplBindings(final ILanguageImpl languageImpl) {
        final DefaultActionGroup buildActionGroup = createBuildActionGroup(languageImpl);

        return new LanguageImplBindings(buildActionGroup);
    }

    /**
     * Activates the specified language.
     *
     * @param languageBindings
     *            The bindings of the language to activate.
     */
    private void activateLanguage(final LanguageBindings languageBindings) {
        ExtensionUtils.register(languageBindings.getParserDefinitionExtension());
        ExtensionUtils.register(languageBindings.getSyntaxHighlighterFactoryExtension());
        ExtensionUtils.register(languageBindings.getExternalAnnotatorExtension());
        FileTypeUtils.register(languageBindings.getFileType());
    }

    /**
     * Activates the specified language.
     *
     * @param languageImplBindings
     *            The bindings of the language implementation to activate.
     */
    private void activateLanguageImpl(final LanguageImplBindings languageImplBindings) {
        this.actionUtils.addAndRegisterActionGroup(languageImplBindings.getBuildActionGroup(),
                IdeActions.GROUP_MAIN_MENU, "ToolsMenu", Anchor.AFTER);
    }

    /**
     * Deactivates the specified language.
     *
     * @param languageBindings
     *            The bindings of the language to deactivate.
     */
    private void deactivateLanguage(final LanguageBindings languageBindings) {
        FileTypeUtils.unregister(languageBindings.getFileType());
        ExtensionUtils.unregister(languageBindings.getExternalAnnotatorExtension());
        ExtensionUtils.unregister(languageBindings.getSyntaxHighlighterFactoryExtension());
        ExtensionUtils.unregister(languageBindings.getParserDefinitionExtension());
    }

    /**
     * Deactivates the specified language.
     *
     * @param languageImplBindings
     *            The bindings of the language implementation to deactivate.
     */
    private void deactivateLanguageImpl(final LanguageImplBindings languageImplBindings) {
        this.actionUtils.removeAndUnregisterActionGroup(languageImplBindings.getBuildActionGroup(),
                IdeActions.GROUP_MAIN_MENU);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public SpoofaxTokenTypeManager getTokenTypeManager(final ILanguage language) {
        return getBindings(language).getTokenTypeManager();
    }

    /**
     * Gets the bindings for the specified loaded language.
     *
     * @param language
     *            The language.
     * @return The bindings.
     */
    private LanguageBindings getBindings(final ILanguage language) {
        @Nullable
        final LanguageBindings bindings = this.loadedLanguages.getOrDefault(language, null);
        if (bindings == null) {
            throw LoggerUtils2.exception(this.logger, IllegalArgumentException.class,
                    "The specified language is not loaded: {}", language);
        }
        return bindings;
    }

    /**
     * Gets the bindings for the specified loaded language implementation.
     *
     * @param languageImpl
     *            The language implementation.
     * @return The bindings.
     */
    private LanguageImplBindings getBindings(final ILanguageImpl languageImpl) {
        @Nullable
        final LanguageImplBindings bindings = this.loadedLanguageImpls.getOrDefault(languageImpl, null);
        if (bindings == null) {
            throw LoggerUtils2.exception(this.logger, IllegalArgumentException.class,
                    "The specified language implementation is not loaded: {}", languageImpl);
        }
        return bindings;
    }

    /**
     * Creates a new IDEA language for a Spoofax language.
     *
     * @param language
     *            The language.
     * @return The created IDEA language.
     */
    private MetaborgIdeaLanguage createIdeaLanguage(final ILanguage language) {
        return instantiate(MetaborgIdeaLanguage.class, new Class<?>[] { ILanguage.class }, language);
    }

    /**
     * Creates a new parser definition.
     *
     * @param fileType
     *            The language file type.
     * @return The created parser definition.
     */
    private ParserDefinition createParserDefinition(final MetaborgLanguageFileType fileType,
            final IFileElementType fileElementType) {
        return this.parserDefinitionFactory.create(fileType, fileElementType);
    }

    /**
     * Creates a new syntax highlighter factory.
     *
     * @return The syntax highlighter factory.
     */
    private SpoofaxSyntaxHighlighterFactory createSyntaxHighlighterFactory() {
        return this.syntaxHighlighterFactoryProvider.get();
    }

    /**
     * Creates a new spoofax token type manager for an IDEA language.
     *
     * @param language
     *            The IDEA language.
     * @return The created token type manager.
     */
    private SpoofaxTokenTypeManager createTokenTypeManager(final MetaborgIdeaLanguage language) {
        return new SpoofaxTokenTypeManager(language);
    }

    /**
     * Creates a new file type for an IDEA language.
     *
     * @param language
     *            The IDEA language.
     * @return The created file type.
     */
    private MetaborgLanguageFileType createFileType(final MetaborgIdeaLanguage language) {
        return instantiate(MetaborgLanguageFileType.class, new Class<?>[] { MetaborgIdeaLanguage.class }, language);
    }

    /**
     * Creates a new file element type.
     *
     * @param language
     *            The language.
     * @param tokenTypesManager
     *            The token types manager.
     * @return The file element type.
     */
    private IFileElementType createFileElementType(final Language language,
            final SpoofaxTokenTypeManager tokenTypesManager) {
        return this.fileElementTypeFactory.create(language, tokenTypesManager);
    }

    /**
     * Creates the builder menu action group.
     *
     * @param languageImpl
     *            The language implementation.
     * @return The action group.
     */
    private DefaultActionGroup createBuildActionGroup(final ILanguageImpl languageImpl) {
        return this.builderMenuBuilder.build(languageImpl);
    }

    /**
     * Instantiates an abstract class with no abstract members.
     *
     * It is preferred that the object injects itself in the constructor.
     *
     * @param clazz
     *            The type of class to instantiate.
     * @param paramTypes
     *            The parameter types.
     * @param args
     *            The arguments.
     * @param <T>
     *            The type of object.
     * @return The instantiated object.
     */
    private <T> T instantiate(final Class<T> clazz, final Class<?>[] paramTypes, final Object... args) {
        return instantiate(clazz, false, paramTypes, args);
    }

    /**
     * Instantiates an abstract class with no abstract members.
     *
     * It is preferred that the object injects itself in the constructor.
     *
     * @param clazz
     *            The type of class to instantiate.
     * @param inject
     *            Whether to inject the object.
     * @param paramTypes
     *            The parameter types.
     * @param args
     *            The arguments.
     * @param <T>
     *            The type of object.
     * @return The instantiated object.
     */
    private <T> T instantiate(final Class<T> clazz, final boolean inject, final Class<?>[] paramTypes,
            final Object... args) {
        assert paramTypes.length == args.length;
        try {
            this.proxyFactory.setSuperclass(clazz);
            final T obj = (T) this.proxyFactory.create(paramTypes, args);
            if (inject) {
                SpoofaxIdeaPlugin.injector().injectMembers(obj);
            }
            return obj;
        } catch (NoSuchMethodException | IllegalArgumentException | InstantiationException | IllegalAccessException
                | InvocationTargetException e) {
            throw LoggerUtils2.exception(this.logger, UnhandledException.class, "Unexpected unhandled exception.",
                    e);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Iterable<ILanguageDiscoveryRequest> requestFromArtifact(final VirtualFile artifact) throws IOException {

        return requestFromArtifact(this.resourceService.resolve(artifact));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Iterable<ILanguageDiscoveryRequest> requestFromArtifact(final FileObject artifact) throws IOException {

        final String zipUri;
        try {
            zipUri = "zip://" + artifact.getURL().getPath();
        } catch (final FileSystemException e) {
            throw LoggerUtils2.exception(this.logger, UnhandledException.class,
                    "Unhandled exception while requesting languages from artifact: {}", e, artifact);
        }

        final FileObject file = this.resourceService.resolve(zipUri);
        return requestFromFolder(file);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Iterable<ILanguageDiscoveryRequest> requestFromFolder(final VirtualFile folder) throws IOException {

        return requestFromFolder(this.resourceService.resolve(folder));
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Iterable<ILanguageDiscoveryRequest> requestFromFolder(final FileObject folder) throws IOException {

        try {
            // Loading happens only after the user clicked OK or Apply in the settings dialog.
            return this.discoveryService.request(folder);
        } catch (final MetaborgException e) {
            throw new UnhandledException(e);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void reloadLanguageSpec(final IdeaLanguageSpec project) {
        unloadLanguageSpec(project);
        loadLanguageSpec(project);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void unloadLanguageSpec(final IdeaLanguageSpec project) {
        final Collection<ILanguageComponent> oldComponents = project.getComponents();

        deactivateRange(LanguageUtils2.getLanguagesOfComponents(oldComponents));
        unloadComponentRange(oldComponents);
        project.setComponents(Collections.emptyList());
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void loadLanguageSpec(final IdeaLanguageSpec project) {
        final Iterable<ILanguageDiscoveryRequest> requests;
        try {
            requests = this.discoveryService.request(project.location());
        } catch (final MetaborgException e) {
            this.logger.error("Language discovery failed after compilation for project: {}", e, project);
            return;
        }

        final Collection<ILanguageComponent> newComponents;
        try {
            newComponents = loadRange(requests);
        } catch (final LanguageLoadingFailedException e) {
            this.logger.error("Language loading failed after compilation for project: {}", e, project);
            return;
        }

        project.setComponents(newComponents);
        activateRange(LanguageUtils2.getLanguagesOfComponents(newComponents));
    }
}