File: SolutionExplorer\AnalyzersCommandHandler.cs
Web Access
Project: src\src\VisualStudio\Core\Impl\Microsoft.VisualStudio.LanguageServices.Implementation.csproj (Microsoft.VisualStudio.LanguageServices.Implementation)
// 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.
 
#nullable disable
 
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.ComponentModel.Composition;
using System.ComponentModel.Design;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using EnvDTE;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.ErrorReporting;
using Microsoft.CodeAnalysis.Notification;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Progress;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Shared.TestHooks;
using Microsoft.Internal.VisualStudio.PlatformUI;
using Microsoft.VisualStudio.CodeAnalysis;
using Microsoft.VisualStudio.ComponentModelHost;
using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem;
using Microsoft.VisualStudio.LanguageServices.Implementation.Utilities;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using Roslyn.Utilities;
using VSLangProj140;
using VSUtilities = Microsoft.VisualStudio.Utilities;
using Workspace = Microsoft.CodeAnalysis.Workspace;
 
namespace Microsoft.VisualStudio.LanguageServices.Implementation.SolutionExplorer;
 
[Export]
internal sealed class AnalyzersCommandHandler : IAnalyzersCommandHandler, IVsUpdateSolutionEvents
{
    private readonly AnalyzerItemsTracker _tracker;
    private readonly AnalyzerReferenceManager _analyzerReferenceManager;
    private readonly IThreadingContext _threadingContext;
    private readonly IServiceProvider _serviceProvider;
    private readonly IAsynchronousOperationListener _listener;
    private ContextMenuController _analyzerFolderContextMenuController;
    private ContextMenuController _analyzerContextMenuController;
    private ContextMenuController _diagnosticContextMenuController;
 
    // Analyzers folder context menu items
    private MenuCommand _addMenuItem;
 
    // Analyzer context menu items
    private MenuCommand _removeMenuItem;
 
    // Diagnostic context menu items
    private MenuCommand _setSeverityDefaultMenuItem;
    private MenuCommand _setSeverityErrorMenuItem;
    private MenuCommand _setSeverityWarningMenuItem;
    private MenuCommand _setSeverityInfoMenuItem;
    private MenuCommand _setSeverityHiddenMenuItem;
    private MenuCommand _setSeverityNoneMenuItem;
    private MenuCommand _openHelpLinkMenuItem;
 
    // Other menu items
    private MenuCommand _projectAddMenuItem;
    private MenuCommand _projectContextAddMenuItem;
    private MenuCommand _referencesContextAddMenuItem;
    private MenuCommand _setActiveRuleSetMenuItem;
 
    private Workspace _workspace;
 
    private bool _allowProjectSystemOperations = true;
    private bool _initialized;
 
    [ImportingConstructor]
    [SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")]
    public AnalyzersCommandHandler(
        AnalyzerItemsTracker tracker,
        AnalyzerReferenceManager analyzerReferenceManager,
        IThreadingContext threadingContext,
        IAsynchronousOperationListenerProvider listenerProvider,
        [Import(typeof(SVsServiceProvider))] IServiceProvider serviceProvider)
    {
        _tracker = tracker;
        _analyzerReferenceManager = analyzerReferenceManager;
        _threadingContext = threadingContext;
        _serviceProvider = serviceProvider;
        _listener = listenerProvider.GetListener(FeatureAttribute.RuleSetEditor);
    }
 
    /// <summary>
    /// Hook up the context menu handlers.
    /// </summary>
    public async Task InitializeAsync(IMenuCommandService menuCommandService, CancellationToken cancellationToken)
    {
        if (menuCommandService != null)
        {
            await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);
 
            // Analyzers folder context menu items
            _addMenuItem = AddCommandHandler(menuCommandService, ID.RoslynCommands.AddAnalyzer, AddAnalyzerHandler);
            _ = AddCommandHandler(menuCommandService, ID.RoslynCommands.OpenRuleSet, OpenRuleSetHandler);
 
            // Analyzer context menu items
            _removeMenuItem = AddCommandHandler(menuCommandService, ID.RoslynCommands.RemoveAnalyzer, RemoveAnalyzerHandler);
 
            // Diagnostic context menu items
            _setSeverityDefaultMenuItem = AddCommandHandler(menuCommandService, ID.RoslynCommands.SetSeverityDefault, SetSeverityHandler);
            _setSeverityErrorMenuItem = AddCommandHandler(menuCommandService, ID.RoslynCommands.SetSeverityError, SetSeverityHandler);
            _setSeverityWarningMenuItem = AddCommandHandler(menuCommandService, ID.RoslynCommands.SetSeverityWarning, SetSeverityHandler);
            _setSeverityInfoMenuItem = AddCommandHandler(menuCommandService, ID.RoslynCommands.SetSeverityInfo, SetSeverityHandler);
            _setSeverityHiddenMenuItem = AddCommandHandler(menuCommandService, ID.RoslynCommands.SetSeverityHidden, SetSeverityHandler);
            _setSeverityNoneMenuItem = AddCommandHandler(menuCommandService, ID.RoslynCommands.SetSeverityNone, SetSeverityHandler);
            _openHelpLinkMenuItem = AddCommandHandler(menuCommandService, ID.RoslynCommands.OpenDiagnosticHelpLink, OpenDiagnosticHelpLinkHandler);
 
            // Other menu items
            _projectAddMenuItem = AddCommandHandler(menuCommandService, ID.RoslynCommands.ProjectAddAnalyzer, AddAnalyzerHandler);
            _projectContextAddMenuItem = AddCommandHandler(menuCommandService, ID.RoslynCommands.ProjectContextAddAnalyzer, AddAnalyzerHandler);
            _referencesContextAddMenuItem = AddCommandHandler(menuCommandService, ID.RoslynCommands.ReferencesContextAddAnalyzer, AddAnalyzerHandler);
            _setActiveRuleSetMenuItem = AddCommandHandler(menuCommandService, ID.RoslynCommands.SetActiveRuleSet, SetActiveRuleSetHandler);
 
            UpdateOtherMenuItemsVisibility();
 
            if (_tracker != null)
            {
                _tracker.SelectedHierarchyItemChanged += SelectedHierarchyItemChangedHandler;
            }
 
            var buildManager = (IVsSolutionBuildManager)_serviceProvider.GetService(typeof(SVsSolutionBuildManager));
            buildManager.AdviseUpdateSolutionEvents(this, out var cookie);
 
            _initialized = true;
        }
    }
 
    public IContextMenuController AnalyzerFolderContextMenuController
    {
        get
        {
            _analyzerFolderContextMenuController ??= new ContextMenuController(
                    ID.RoslynCommands.AnalyzerFolderContextMenu,
                    ShouldShowAnalyzerFolderContextMenu,
                    UpdateAnalyzerFolderContextMenu);
 
            return _analyzerFolderContextMenuController;
        }
    }
 
    private bool ShouldShowAnalyzerFolderContextMenu(IEnumerable<object> items)
    {
        return items.Count() == 1;
    }
 
    private void UpdateAnalyzerFolderContextMenu()
    {
        if (_addMenuItem != null)
        {
            _addMenuItem.Visible = SelectedProjectSupportsAnalyzers();
            _addMenuItem.Enabled = _allowProjectSystemOperations;
        }
    }
 
    public IContextMenuController AnalyzerContextMenuController
    {
        get
        {
            _analyzerContextMenuController ??= new ContextMenuController(
                    ID.RoslynCommands.AnalyzerContextMenu,
                    ShouldShowAnalyzerContextMenu,
                    UpdateAnalyzerContextMenu);
 
            return _analyzerContextMenuController;
        }
    }
 
    private bool ShouldShowAnalyzerContextMenu(IEnumerable<object> items)
    {
        return items.All(item => item is AnalyzerItem);
    }
 
    private void UpdateAnalyzerContextMenu()
    {
        _removeMenuItem?.Enabled = _allowProjectSystemOperations;
    }
 
    public IContextMenuController DiagnosticContextMenuController
    {
        get
        {
            _diagnosticContextMenuController ??= new ContextMenuController(
                    ID.RoslynCommands.DiagnosticContextMenu,
                    ShouldShowDiagnosticContextMenu,
                    UpdateDiagnosticContextMenu);
 
            return _diagnosticContextMenuController;
        }
    }
 
    private bool ShouldShowDiagnosticContextMenu(IEnumerable<object> items)
    {
        return _initialized && items.All(item => item is DiagnosticItem);
    }
 
    private void UpdateDiagnosticContextMenu()
    {
        Debug.Assert(_initialized);
 
        UpdateSeverityMenuItemsChecked();
        UpdateSeverityMenuItemsEnabled();
        UpdateOpenHelpLinkMenuItemVisibility();
    }
 
    private static MenuCommand AddCommandHandler(IMenuCommandService menuCommandService, int roslynCommand, EventHandler handler)
    {
        var commandID = new CommandID(Guids.RoslynGroupId, roslynCommand);
        var menuCommand = new MenuCommand(handler, commandID);
        menuCommandService.AddCommand(menuCommand);
 
        return menuCommand;
    }
 
    private void SelectedHierarchyItemChangedHandler(object sender, EventArgs e)
    {
        UpdateOtherMenuItemsVisibility();
    }
 
    private void UpdateOtherMenuItemsVisibility()
    {
        var selectedProjectSupportsAnalyzers = SelectedProjectSupportsAnalyzers();
        _projectAddMenuItem.Visible = selectedProjectSupportsAnalyzers;
        _projectContextAddMenuItem.Visible = selectedProjectSupportsAnalyzers && _tracker.SelectedItemId == VSConstants.VSITEMID_ROOT;
        _referencesContextAddMenuItem.Visible = selectedProjectSupportsAnalyzers;
        _setActiveRuleSetMenuItem.Visible = selectedProjectSupportsAnalyzers &&
                                            _tracker.SelectedHierarchy.TryGetItemName(_tracker.SelectedItemId, out var itemName) &&
                                            Path.GetExtension(itemName).Equals(".ruleset", StringComparison.OrdinalIgnoreCase);
    }
 
    private void UpdateOtherMenuItemsEnabled()
    {
        _projectAddMenuItem.Enabled = _allowProjectSystemOperations;
        _projectContextAddMenuItem.Enabled = _allowProjectSystemOperations;
        _referencesContextAddMenuItem.Enabled = _allowProjectSystemOperations;
        _removeMenuItem.Enabled = _allowProjectSystemOperations;
    }
 
    private void UpdateOpenHelpLinkMenuItemVisibility()
    {
        _openHelpLinkMenuItem.Visible = _tracker.SelectedDiagnosticItems.Length == 1 &&
                                        _tracker.SelectedDiagnosticItems[0].Descriptor.GetValidHelpLinkUri() != null;
    }
 
    private void UpdateSeverityMenuItemsChecked()
    {
        _setSeverityDefaultMenuItem.Checked = false;
        _setSeverityErrorMenuItem.Checked = false;
        _setSeverityWarningMenuItem.Checked = false;
        _setSeverityInfoMenuItem.Checked = false;
        _setSeverityHiddenMenuItem.Checked = false;
        _setSeverityNoneMenuItem.Checked = false;
 
        if (TryGetWorkspace() is not VisualStudioWorkspace workspace)
        {
            return;
        }
 
        var selectedItemSeverities = new HashSet<ReportDiagnostic>();
 
        var groups = _tracker.SelectedDiagnosticItems.GroupBy(item => item.ProjectId);
 
        foreach (var group in groups)
        {
            var project = workspace.CurrentSolution.GetProject(group.Key);
            if (project == null)
            {
                continue;
            }
 
            var analyzerConfigOptions = project.GetAnalyzerConfigOptions();
 
            foreach (var diagnosticItem in group)
            {
                // Currently only project analyzers show in Solution Explorer, so we never need to consider fallback options.
                var severity = diagnosticItem.Descriptor.GetEffectiveSeverity(project.CompilationOptions, analyzerConfigOptions?.ConfigOptionsWithoutFallback, analyzerConfigOptions?.TreeOptions);
                selectedItemSeverities.Add(severity);
            }
        }
 
        if (selectedItemSeverities.Count != 1)
        {
            return;
        }
 
        switch (selectedItemSeverities.Single())
        {
            case ReportDiagnostic.Default:
                _setSeverityDefaultMenuItem.Checked = true;
                break;
            case ReportDiagnostic.Error:
                _setSeverityErrorMenuItem.Checked = true;
                break;
            case ReportDiagnostic.Warn:
                _setSeverityWarningMenuItem.Checked = true;
                break;
            case ReportDiagnostic.Info:
                _setSeverityInfoMenuItem.Checked = true;
                break;
            case ReportDiagnostic.Hidden:
                _setSeverityHiddenMenuItem.Checked = true;
                break;
            case ReportDiagnostic.Suppress:
                _setSeverityNoneMenuItem.Checked = true;
                break;
            default:
                break;
        }
    }
 
    private void UpdateSeverityMenuItemsEnabled()
    {
        var configurable = !_tracker.SelectedDiagnosticItems.Any(static item => item.Descriptor.ImmutableCustomTags().Contains(WellKnownDiagnosticTags.NotConfigurable));
 
        _setSeverityDefaultMenuItem.Enabled = configurable;
        _setSeverityErrorMenuItem.Enabled = configurable;
        _setSeverityWarningMenuItem.Enabled = configurable;
        _setSeverityInfoMenuItem.Enabled = configurable;
        _setSeverityHiddenMenuItem.Enabled = configurable;
        _setSeverityNoneMenuItem.Enabled = configurable;
    }
 
    private bool SelectedProjectSupportsAnalyzers()
    {
        return _tracker != null &&
               _tracker.SelectedHierarchy != null &&
               _tracker.SelectedHierarchy.TryGetProject(out var project) &&
               project.Object is VSProject3;
    }
 
    /// <summary>
    /// Handler for "Add Analyzer..." context menu on Analyzers folder node.
    /// </summary>
    internal void AddAnalyzerHandler(object sender, EventArgs args)
    {
        _analyzerReferenceManager?.ShowDialog();
    }
 
    /// <summary>
    /// Handler for "Remove" context menu on individual Analyzer items.
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="args"></param>
    internal void RemoveAnalyzerHandler(object sender, EventArgs args)
    {
        foreach (var item in _tracker.SelectedAnalyzerItems)
        {
            item.Remove();
        }
    }
 
    internal void OpenRuleSetHandler(object sender, EventArgs args)
    {
        if (_tracker.SelectedFolder != null &&
            _serviceProvider != null)
        {
            var projectId = _tracker.SelectedFolder.ProjectId;
            if (_tracker.SelectedFolder.Workspace is VisualStudioWorkspace workspace)
            {
                var ruleSetFile = workspace.TryGetRuleSetPathForProject(projectId);
 
                if (ruleSetFile == null)
                {
                    SendUnableToOpenRuleSetNotification(workspace, SolutionExplorerShim.No_rule_set_file_is_specified_or_the_file_does_not_exist);
                    return;
                }
 
                try
                {
                    var dte = (EnvDTE.DTE)_serviceProvider.GetService(typeof(EnvDTE.DTE));
                    dte.ItemOperations.OpenFile(ruleSetFile);
                }
                catch (Exception e)
                {
                    SendUnableToOpenRuleSetNotification(workspace, e.Message);
                }
            }
        }
    }
 
    private void SetSeverityHandler(object sender, EventArgs args)
    {
        _threadingContext.JoinableTaskFactory.RunAsync(async () =>
        {
            using var asyncToken = _listener.BeginAsyncOperation(nameof(SetSeverityHandler));
            if (TryGetWorkspace() is not VisualStudioWorkspaceImpl workspace)
                return;
 
            // Actually try to set the severity for this command.  This will pop up a threaded-wait-dialog
            // while we do the work.  If we receive any failure notifications, we'll return them here so we
            // can report them once the dialog has been dismissed.
            using var _ = ArrayBuilder<string>.GetInstance(out var notificationMessages);
            await SetSeverityHandlerAsync(workspace, (MenuCommand)sender, notificationMessages).ConfigureAwait(false);
 
            if (notificationMessages.Count > 0)
            {
                var totalMessage = string.Join(Environment.NewLine, notificationMessages);
                await _threadingContext.JoinableTaskFactory.SwitchToMainThreadAsync(_threadingContext.DisposalToken);
 
                SendErrorNotification(
                    workspace,
                    SolutionExplorerShim.The_rule_set_file_could_not_be_updated,
                    totalMessage);
            }
        }).Task.ReportNonFatalErrorUnlessCancelledAsync(_threadingContext.DisposalToken);
    }
 
    private async Task SetSeverityHandlerAsync(VisualStudioWorkspaceImpl workspace, MenuCommand selectedItem, ArrayBuilder<string> notificationMessages)
    {
        var componentModel = (IComponentModel)_serviceProvider.GetService(typeof(SComponentModel));
        var uiThreadOperationExecutor = componentModel.GetService<VSUtilities.IUIThreadOperationExecutor>();
 
        using var context = uiThreadOperationExecutor.BeginExecute(
            title: ServicesVSResources.Updating_severity,
            defaultDescription: "",
            allowCancellation: true,
            showProgress: true);
 
        var originalSolution = workspace.CurrentSolution;
        var selectedAction = MapSelectedItemToReportDiagnostic(selectedItem);
        if (!selectedAction.HasValue)
            return;
 
        foreach (var selectedDiagnostic in _tracker.SelectedDiagnosticItems)
        {
            var projectId = selectedDiagnostic.ProjectId;
            var pathToRuleSet = workspace.TryGetRuleSetPathForProject(projectId);
 
            var project = workspace.CurrentSolution.GetProject(projectId);
            var pathToAnalyzerConfigDoc = project?.TryGetAnalyzerConfigPathForProjectConfiguration();
 
            if (pathToRuleSet == null && pathToAnalyzerConfigDoc == null)
            {
                notificationMessages.Add(SolutionExplorerShim.No_rule_set_file_is_specified_or_the_file_does_not_exist);
                continue;
            }
 
            var editHandlerService = componentModel.GetService<ICodeActionEditHandlerService>();
 
            try
            {
                var envDteProject = workspace.TryGetDTEProject(projectId);
 
                if (pathToRuleSet == null || SdkUiUtilities.IsBuiltInRuleSet(pathToRuleSet, _serviceProvider))
                {
                    // If project is using the default built-in ruleset or no ruleset, then prefer .editorconfig for severity configuration.
                    if (pathToAnalyzerConfigDoc != null)
                    {
                        using var scope1 = context.AddScope(allowCancellation: true, description: "");
                        var newSolution = await selectedDiagnostic.GetSolutionWithUpdatedAnalyzerConfigSeverityAsync(selectedAction.Value, project, context.UserCancellationToken).ConfigureAwait(false);
                        var operations = ImmutableArray.Create<CodeActionOperation>(new ApplyChangesOperation(newSolution));
                        await editHandlerService.ApplyAsync(
                            _workspace,
                            originalSolution,
                            fromDocument: null,
                            operations,
                            title: ServicesVSResources.Updating_severity,
                            scope1.GetCodeAnalysisProgress(),
                            context.UserCancellationToken).ConfigureAwait(true);
                        continue;
                    }
 
                    // Otherwise, fall back to using ruleset.
                    if (pathToRuleSet == null)
                    {
                        notificationMessages.Add(SolutionExplorerShim.No_rule_set_file_is_specified_or_the_file_does_not_exist);
                        continue;
                    }
 
                    pathToRuleSet = CreateCopyOfRuleSetForProject(pathToRuleSet, envDteProject);
                    if (pathToRuleSet == null)
                    {
                        notificationMessages.Add(string.Format(SolutionExplorerShim.Could_not_create_a_rule_set_for_project_0, envDteProject.Name));
                        continue;
                    }
 
                    var fileInfo = new FileInfo(pathToRuleSet);
                    fileInfo.IsReadOnly = false;
                }
 
                using var scope2 = context.AddScope(
                    allowCancellation: false,
                    string.Format(SolutionExplorerShim.Checking_out_0_for_editing, Path.GetFileName(pathToRuleSet)));
 
                if (envDteProject.DTE.SourceControl.IsItemUnderSCC(pathToRuleSet))
                    envDteProject.DTE.SourceControl.CheckOutItem(pathToRuleSet);
 
                selectedDiagnostic.SetRuleSetSeverity(selectedAction.Value, pathToRuleSet);
            }
            catch (Exception e)
            {
                notificationMessages.Add(e.Message);
            }
        }
    }
 
    private void OpenDiagnosticHelpLinkHandler(object sender, EventArgs e)
    {
        if (_tracker.SelectedDiagnosticItems.Length != 1)
        {
            return;
        }
 
        var uri = _tracker.SelectedDiagnosticItems[0].Descriptor.GetValidHelpLinkUri();
        if (uri != null)
        {
            VisualStudioNavigateToLinkService.StartBrowser(uri);
        }
    }
 
    private void SetActiveRuleSetHandler(object sender, EventArgs e)
    {
        if (_tracker.SelectedHierarchy.TryGetProject(out var project) &&
            _tracker.SelectedHierarchy.TryGetCanonicalName(_tracker.SelectedItemId, out var ruleSetFileFullPath))
        {
            var projectDirectoryFullPath = Path.GetDirectoryName(project.FullName);
            var ruleSetFileRelativePath = PathUtilities.GetRelativePath(projectDirectoryFullPath, ruleSetFileFullPath);
 
            UpdateProjectConfigurationsToUseRuleSetFile(project, ruleSetFileRelativePath);
        }
    }
 
    private static string CreateCopyOfRuleSetForProject(string pathToRuleSet, EnvDTE.Project envDteProject)
    {
        var fileName = GetNewRuleSetFileNameForProject(envDteProject);
        var projectDirectory = Path.GetDirectoryName(envDteProject.FullName);
        var fullFilePath = Path.Combine(projectDirectory, fileName);
        File.Copy(pathToRuleSet, fullFilePath);
        UpdateProjectConfigurationsToUseRuleSetFile(envDteProject, fileName);
        envDteProject.ProjectItems.AddFromFile(fullFilePath);
 
        return fullFilePath;
    }
 
    private static void UpdateProjectConfigurationsToUseRuleSetFile(EnvDTE.Project envDteProject, string fileName)
    {
        foreach (EnvDTE.Configuration config in envDteProject.ConfigurationManager)
        {
            var properties = config.Properties;
 
            try
            {
                var codeAnalysisRuleSetFileProperty = properties?.Item("CodeAnalysisRuleSet");
 
                codeAnalysisRuleSetFileProperty?.Value = fileName;
            }
            catch (ArgumentException)
            {
                // Unfortunately the properties collection sometimes throws an ArgumentException
                // instead of returning null if the current configuration doesn't support CodeAnalysisRuleSet.
                // Ignore it and move on.
            }
        }
    }
 
    private static string GetNewRuleSetFileNameForProject(EnvDTE.Project envDteProject)
    {
        var projectName = envDteProject.Name;
 
        var projectItemNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
        foreach (ProjectItem item in envDteProject.ProjectItems)
        {
            projectItemNames.Add(item.Name);
        }
 
        var ruleSetName = projectName + ".ruleset";
        if (!projectItemNames.Contains(ruleSetName))
        {
            return ruleSetName;
        }
 
        for (var i = 1; i < int.MaxValue; i++)
        {
            ruleSetName = projectName + i + ".ruleset";
            if (!projectItemNames.Contains(ruleSetName))
            {
                return ruleSetName;
            }
        }
 
        return null;
    }
 
    private static ReportDiagnostic? MapSelectedItemToReportDiagnostic(MenuCommand selectedItem)
    {
        ReportDiagnostic? selectedAction = null;
 
        if (selectedItem.CommandID.Guid == Guids.RoslynGroupId)
        {
            switch (selectedItem.CommandID.ID)
            {
                case ID.RoslynCommands.SetSeverityDefault:
                    selectedAction = ReportDiagnostic.Default;
                    break;
 
                case ID.RoslynCommands.SetSeverityError:
                    selectedAction = ReportDiagnostic.Error;
                    break;
 
                case ID.RoslynCommands.SetSeverityWarning:
                    selectedAction = ReportDiagnostic.Warn;
                    break;
 
                case ID.RoslynCommands.SetSeverityInfo:
                    selectedAction = ReportDiagnostic.Info;
                    break;
 
                case ID.RoslynCommands.SetSeverityHidden:
                    selectedAction = ReportDiagnostic.Hidden;
                    break;
 
                case ID.RoslynCommands.SetSeverityNone:
                    selectedAction = ReportDiagnostic.Suppress;
                    break;
 
                default:
                    selectedAction = null;
                    break;
            }
        }
 
        return selectedAction;
    }
 
    private static void SendUnableToOpenRuleSetNotification(Workspace workspace, string message)
    {
        SendErrorNotification(
            workspace,
            SolutionExplorerShim.The_rule_set_file_could_not_be_opened,
            message);
    }
 
    private static void SendErrorNotification(Workspace workspace, string message1, string message2)
    {
        var notificationService = workspace.Services.GetService<INotificationService>();
 
        notificationService.SendNotification(message1 + Environment.NewLine + Environment.NewLine + message2, severity: NotificationSeverity.Error);
    }
 
    int IVsUpdateSolutionEvents.UpdateSolution_Begin(ref int pfCancelUpdate)
    {
        _allowProjectSystemOperations = false;
        UpdateOtherMenuItemsEnabled();
 
        return VSConstants.S_OK;
    }
 
    int IVsUpdateSolutionEvents.UpdateSolution_Done(int fSucceeded, int fModified, int fCancelCommand)
    {
        _allowProjectSystemOperations = true;
        UpdateOtherMenuItemsEnabled();
 
        return VSConstants.S_OK;
    }
 
    int IVsUpdateSolutionEvents.UpdateSolution_StartUpdate(ref int pfCancelUpdate)
    {
        return VSConstants.S_OK;
    }
 
    int IVsUpdateSolutionEvents.UpdateSolution_Cancel()
    {
        _allowProjectSystemOperations = true;
        UpdateOtherMenuItemsEnabled();
 
        return VSConstants.S_OK;
    }
 
    int IVsUpdateSolutionEvents.OnActiveProjectCfgChange(IVsHierarchy pIVsHierarchy)
    {
        return VSConstants.S_OK;
    }
 
    private Workspace TryGetWorkspace()
    {
        if (_workspace == null)
        {
            var componentModel = (IComponentModel)_serviceProvider.GetService(typeof(SComponentModel));
            _workspace = componentModel.DefaultExportProvider.GetExportedValueOrDefault<VisualStudioWorkspace>();
        }
 
        return _workspace;
    }
}