File: Editor\EditorLayerExtensionManager.cs
Web Access
Project: ..\..\..\src\EditorFeatures\Core\Microsoft.CodeAnalysis.EditorFeatures.csproj (Microsoft.CodeAnalysis.EditorFeatures)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System;
using System.Collections.Generic;
using System.Composition;
using System.Linq;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CodeRefactorings;
using Microsoft.CodeAnalysis.Editor.Options;
using Microsoft.CodeAnalysis.ErrorLogger;
using Microsoft.CodeAnalysis.ErrorReporting;
using Microsoft.CodeAnalysis.Extensions;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Internal.Log;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Telemetry;
using Microsoft.VisualStudio.Text;
using Roslyn.Utilities;
using static Microsoft.CodeAnalysis.Internal.Log.FunctionId;
using static Microsoft.CodeAnalysis.Internal.Log.Logger;
using static Microsoft.CodeAnalysis.RoslynAssemblyHelper;
 
namespace Microsoft.CodeAnalysis.Editor
{
    [ExportWorkspaceServiceFactory(typeof(IExtensionManager), ServiceLayer.Editor), Shared]
    internal class EditorLayerExtensionManager : IWorkspaceServiceFactory
    {
        private readonly List<IExtensionErrorHandler> _errorHandlers;
        private readonly IGlobalOptionService _optionService;
 
        [ImportingConstructor]
        [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
        public EditorLayerExtensionManager(
            IGlobalOptionService optionService,
            [ImportMany] IEnumerable<IExtensionErrorHandler> errorHandlers)
        {
            _optionService = optionService;
            _errorHandlers = errorHandlers.ToList();
        }
 
        public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices)
        {
            var errorReportingService = workspaceServices.GetRequiredService<IErrorReportingService>();
            var errorLoggerService = workspaceServices.GetRequiredService<IErrorLoggerService>();
            return new ExtensionManager(_optionService, errorReportingService, errorLoggerService, _errorHandlers);
        }
 
        internal class ExtensionManager : AbstractExtensionManager
        {
            private readonly List<IExtensionErrorHandler> _errorHandlers;
            private readonly IGlobalOptionService _globalOptions;
            private readonly IErrorReportingService _errorReportingService;
            private readonly IErrorLoggerService _errorLoggerService;
 
            public ExtensionManager(
                IGlobalOptionService globalOptions,
                IErrorReportingService errorReportingService,
                IErrorLoggerService errorLoggerService,
                List<IExtensionErrorHandler> errorHandlers)
            {
                _globalOptions = globalOptions;
                _errorHandlers = errorHandlers;
                _errorReportingService = errorReportingService;
                _errorLoggerService = errorLoggerService;
            }
 
            public override void HandleException(object provider, Exception exception)
            {
                if (provider is CodeFixProvider or CodeFixes.FixAllProvider or CodeRefactoringProvider or CodeRefactorings.FixAllProvider)
                {
                    if (!IsIgnored(provider) &&
                        _globalOptions.GetOption(ExtensionManagerOptions.DisableCrashingExtensions))
                    {
                        base.HandleException(provider, exception);
 
                        var providerType = provider.GetType();
 
                        _errorReportingService?.ShowGlobalErrorInfo(
                            message: string.Format(WorkspacesResources._0_encountered_an_error_and_has_been_disabled, providerType.Name),
                            TelemetryFeatureName.GetExtensionName(providerType),
                            exception,
                            new InfoBarUI(WorkspacesResources.Show_Stack_Trace, InfoBarUI.UIKind.HyperLink, () => ShowDetailedErrorInfo(exception), closeAfterAction: false),
                            new InfoBarUI(WorkspacesResources.Enable, InfoBarUI.UIKind.Button, () =>
                            {
                                EnableProvider(provider);
                                LogEnableProvider(provider);
                            }),
                            new InfoBarUI(WorkspacesResources.Enable_and_ignore_future_errors, InfoBarUI.UIKind.Button, () =>
                            {
                                EnableProvider(provider);
                                IgnoreProvider(provider);
                                LogEnableAndIgnoreProvider(provider);
                            }),
                            new InfoBarUI(string.Empty, InfoBarUI.UIKind.Close, () => LogLeaveDisabled(provider)));
                    }
                    else
                    {
                        LogAction(CodefixInfobar_ErrorIgnored, provider);
                    }
                }
                else
                {
                    if (_globalOptions.GetOption(ExtensionManagerOptions.DisableCrashingExtensions))
                    {
                        base.HandleException(provider, exception);
                    }
 
                    _errorHandlers.Do(h => h.HandleError(provider, exception));
                }
 
                _errorLoggerService?.LogException(provider, exception);
            }
 
            private void ShowDetailedErrorInfo(Exception exception)
                => _errorReportingService.ShowDetailedErrorInfo(exception);
 
            private static void LogLeaveDisabled(object provider)
                => LogAction(CodefixInfobar_LeaveDisabled, provider);
 
            private static void LogEnableAndIgnoreProvider(object provider)
                => LogAction(CodefixInfobar_EnableAndIgnoreFutureErrors, provider);
 
            private static void LogEnableProvider(object provider)
                => LogAction(CodefixInfobar_Enable, provider);
 
            private static void LogAction(FunctionId functionId, object provider)
            {
                if (IsRoslynCodefix(provider))
                {
                    Log(functionId, $"Name: {provider.GetType().FullName} Assembly Version: {provider.GetType().Assembly.GetName().Version}");
                }
                else
                {
                    Log(functionId);
                }
            }
 
            private static bool IsRoslynCodefix(object source) => HasRoslynPublicKey(source);
        }
    }
}