|
// 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;
}
}
|