Skip to content

Commit

Permalink
Merge pull request #19366 from 333fred/info-bar-refactor
Browse files Browse the repository at this point in the history
Refactored IErrorReportingService
  • Loading branch information
333fred authored May 10, 2017
2 parents a404917 + eddbafc commit ab924b3
Show file tree
Hide file tree
Showing 15 changed files with 309 additions and 235 deletions.
2 changes: 1 addition & 1 deletion Roslyn.sln
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26228.4
VisualStudioVersion = 15.0.26507.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Core", "Core", "{A41D1B99-F489-4C43-BBDF-96D61B19A6B9}"
EndProject
Expand Down
1 change: 1 addition & 0 deletions src/EditorFeatures/Core/EditorFeatures.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@
<Compile Include="Implementation\Structure\BlockTagState.cs" />
<Compile Include="Implementation\Suggestions\SuggestedActionSetComparer.cs" />
<Compile Include="Implementation\Suggestions\SuggestedActionsSource.cs" />
<Compile Include="Implementation\InfoBar\EditorInfoBarService.cs" />
<Compile Include="SymbolSearch\IAddReferenceDatabaseWrapper.cs" />
<Compile Include="SymbolSearch\IDatabaseFactoryService.cs" />
<Compile Include="SymbolSearch\IDelayService.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,10 @@ public override void HandleException(object provider, Exception exception)
base.HandleException(provider, exception);

_errorReportingService?.ShowErrorInfoInActiveView(String.Format(WorkspacesResources._0_encountered_an_error_and_has_been_disabled, provider.GetType().Name),
new ErrorReportingUI(WorkspacesResources.Show_Stack_Trace, ErrorReportingUI.UIKind.HyperLink, () => ShowDetailedErrorInfo(exception), closeAfterAction: false),
new ErrorReportingUI(WorkspacesResources.Enable, ErrorReportingUI.UIKind.Button, () => { EnableProvider(provider); LogEnableProvider(provider); }),
new ErrorReportingUI(WorkspacesResources.Enable_and_ignore_future_errors, ErrorReportingUI.UIKind.Button, () => { EnableProvider(provider); LogEnableProvider(provider); }),
new ErrorReportingUI(String.Empty, ErrorReportingUI.UIKind.Close, () => LogLeaveDisabled(provider)));
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); LogEnableProvider(provider); }),
new InfoBarUI(String.Empty, InfoBarUI.UIKind.Close, () => LogLeaveDisabled(provider)));
}
else
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Composition;
using Microsoft.CodeAnalysis.Extensions;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Internal.Log;

namespace Microsoft.CodeAnalysis.Editor.Implementation.Workspaces
{
[ExportWorkspaceService(typeof(IInfoBarService)), Shared]
internal class EditorInfoBarService : IInfoBarService
{
public void ShowInfoBarInActiveView(string message, params InfoBarUI[] items)
=> ShowInfoBarInGlobalView(message, items);

public void ShowInfoBarInGlobalView(string message, params InfoBarUI[] items)
=> Logger.Log(FunctionId.Extension_InfoBar, message);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ public void ShowDetailedErrorInfo(Exception exception)
Logger.Log(FunctionId.Extension_Exception, exception.StackTrace);
}

public void ShowErrorInfoInActiveView(string message, params ErrorReportingUI[] items)
public void ShowErrorInfoInActiveView(string message, params InfoBarUI[] items)
{
ShowGlobalErrorInfo(message, items);
}

public void ShowGlobalErrorInfo(string message, params ErrorReportingUI[] items)
public void ShowGlobalErrorInfo(string message, params InfoBarUI[] items)
{
Logger.Log(FunctionId.Extension_Exception, message);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Composition;
using System.Linq;
using Microsoft.CodeAnalysis.Editor;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.Extensions;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Shared.TestHooks;
using Microsoft.VisualStudio.Imaging;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using Roslyn.Utilities;

namespace Microsoft.VisualStudio.LanguageServices.Implementation
{
[ExportWorkspaceService(typeof(IInfoBarService), layer: ServiceLayer.Host), Shared]
internal class VisualStudioInfoBarService : ForegroundThreadAffinitizedObject, IInfoBarService
{
private readonly SVsServiceProvider _serviceProvider;
private readonly IForegroundNotificationService _foregroundNotificationService;
private readonly IAsynchronousOperationListener _listener;

[ImportingConstructor]
public VisualStudioInfoBarService(SVsServiceProvider serviceProvider,
IForegroundNotificationService foregroundNotificationService,
[ImportMany] IEnumerable<Lazy<IAsynchronousOperationListener, FeatureMetadata>> asyncListeners)
{
_serviceProvider = serviceProvider;
_foregroundNotificationService = foregroundNotificationService;
_listener = new AggregateAsynchronousOperationListener(asyncListeners, FeatureAttribute.InfoBar);
}

public void ShowInfoBarInActiveView(string message, params InfoBarUI[] items)
{
ThisCanBeCalledOnAnyThread();
ShowInfoBar(activeView: true, message: message, items: items);
}

public void ShowInfoBarInGlobalView(string message, params InfoBarUI[] items)
{
ThisCanBeCalledOnAnyThread();
ShowInfoBar(activeView: false, message: message, items: items);
}

private void ShowInfoBar(bool activeView, string message, params InfoBarUI[] items)
{
// We can be called from any thread since errors can occur anywhere, however we can only construct and InfoBar from the UI thread.
_foregroundNotificationService.RegisterNotification(() =>
{
if (TryGetInfoBarData(activeView, out var infoBarHost))
{
CreateInfoBar(infoBarHost, message, items);
}
}, _listener.BeginAsyncOperation(nameof(ShowInfoBar)));
}

private bool TryGetInfoBarData(bool activeView, out IVsInfoBarHost infoBarHost)
{
AssertIsForeground();

infoBarHost = null;

if (activeView)
{
var monitorSelectionService = _serviceProvider.GetService(typeof(SVsShellMonitorSelection)) as IVsMonitorSelection;

// We want to get whichever window is currently in focus (including toolbars) as we could have had an exception thrown from the error list
// or interactive window
if (monitorSelectionService == null ||
ErrorHandler.Failed(monitorSelectionService.GetCurrentElementValue((uint)VSConstants.VSSELELEMID.SEID_WindowFrame, out var value)))
{
return false;
}

var frame = value as IVsWindowFrame;
if (ErrorHandler.Failed(frame.GetProperty((int)__VSFPROPID7.VSFPROPID_InfoBarHost, out var activeViewInfoBar)))
{
return false;
}

infoBarHost = activeViewInfoBar as IVsInfoBarHost;
return infoBarHost != null;
}

// global error info, show it on main window info bar
var shell = _serviceProvider.GetService(typeof(SVsShell)) as IVsShell;
if (shell == null ||
ErrorHandler.Failed(shell.GetProperty((int)__VSSPROPID7.VSSPROPID_MainWindowInfoBarHost, out var globalInfoBar)))
{
return false;
}

infoBarHost = globalInfoBar as IVsInfoBarHost;
return infoBarHost != null;
}

private void CreateInfoBar(IVsInfoBarHost infoBarHost, string message, InfoBarUI[] items)
{
var factory = _serviceProvider.GetService(typeof(SVsInfoBarUIFactory)) as IVsInfoBarUIFactory;
if (factory == null)
{
// no info bar factory, don't do anything
return;
}

var textSpans = new List<IVsInfoBarTextSpan>()
{
new InfoBarTextSpan(message)
};

// create action item list
var actionItems = new List<IVsInfoBarActionItem>();

foreach (var item in items)
{
switch (item.Kind)
{
case InfoBarUI.UIKind.Button:
actionItems.Add(new InfoBarButton(item.Title));
break;
case InfoBarUI.UIKind.HyperLink:
actionItems.Add(new InfoBarHyperlink(item.Title));
break;
case InfoBarUI.UIKind.Close:
break;
default:
throw ExceptionUtilities.UnexpectedValue(item.Kind);
}
}

var infoBarModel = new InfoBarModel(
textSpans,
actionItems.ToArray(),
KnownMonikers.StatusInformation,
isCloseButtonVisible: true);

if (!TryCreateInfoBarUI(factory, infoBarModel, out var infoBarUI))
{
return;
}

uint? infoBarCookie = null;
var eventSink = new InfoBarEvents(items, () =>
{
// run given onClose action if there is one.
items.FirstOrDefault(i => i.Kind == InfoBarUI.UIKind.Close).Action?.Invoke();

if (infoBarCookie.HasValue)
{
infoBarUI.Unadvise(infoBarCookie.Value);
}
});

infoBarUI.Advise(eventSink, out var cookie);
infoBarCookie = cookie;

infoBarHost.AddInfoBar(infoBarUI);
}

private class InfoBarEvents : IVsInfoBarUIEvents
{
private readonly InfoBarUI[] _items;
private readonly Action _onClose;

public InfoBarEvents(InfoBarUI[] items, Action onClose)
{
Contract.ThrowIfNull(onClose);

_items = items;
_onClose = onClose;
}

public void OnActionItemClicked(IVsInfoBarUIElement infoBarUIElement, IVsInfoBarActionItem actionItem)
{
var item = _items.FirstOrDefault(i => i.Title == actionItem.Text);
if (item.IsDefault)
{
return;
}

item.Action?.Invoke();

if (!item.CloseAfterAction)
{
return;
}

infoBarUIElement.Close();
}

public void OnClosed(IVsInfoBarUIElement infoBarUIElement)
{
_onClose();
}
}

private static bool TryCreateInfoBarUI(IVsInfoBarUIFactory infoBarUIFactory, IVsInfoBar infoBar, out IVsInfoBarUIElement uiElement)
{
uiElement = infoBarUIFactory.CreateInfoBar(infoBar);
return uiElement != null;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -251,10 +251,10 @@ private void OnConnectionChanged(object sender, bool connected)
FatalError.ReportWithoutCrash(new Exception("Connection to remote host closed"));

// use info bar to show warning to users
var infoBarUIs = new List<ErrorReportingUI>();
var infoBarUIs = new List<InfoBarUI>();

infoBarUIs.Add(
new ErrorReportingUI(ServicesVSResources.Learn_more, ErrorReportingUI.UIKind.HyperLink, () =>
new InfoBarUI(ServicesVSResources.Learn_more, InfoBarUI.UIKind.HyperLink, () =>
BrowserHelper.StartBrowser(new Uri(OOPKilledMoreInfoLink)), closeAfterAction: false));

var allowRestarting = _workspace.Options.GetOption(RemoteHostOptions.RestartRemoteHostAllowed);
Expand All @@ -263,7 +263,7 @@ private void OnConnectionChanged(object sender, bool connected)
// this is hidden restart option. by default, user can't restart remote host that got killed
// by users
infoBarUIs.Add(
new ErrorReportingUI("Restart external process", ErrorReportingUI.UIKind.Button, () =>
new InfoBarUI("Restart external process", InfoBarUI.UIKind.Button, () =>
{
// start off new remote host
var unused = RequestNewRemoteHostAsync(CancellationToken.None);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,9 @@ public int OnBroadcastMessage(uint msg, IntPtr wParam, IntPtr lParam)
_workspace.Options = _workspace.Options.WithChangedOption(RuntimeOptions.FullSolutionAnalysisInfoBarShown, true);

_workspace.Services.GetService<IErrorReportingService>().ShowGlobalErrorInfo(ServicesVSResources.Visual_Studio_has_suspended_some_advanced_features_to_improve_performance,
new ErrorReportingUI(ServicesVSResources.Re_enable, ErrorReportingUI.UIKind.Button, () =>
new InfoBarUI(ServicesVSResources.Re_enable, InfoBarUI.UIKind.Button, () =>
_workspace.Options = _workspace.Options.WithChangedOption(RuntimeOptions.FullSolutionAnalysis, true)),
new ErrorReportingUI(ServicesVSResources.Learn_more, ErrorReportingUI.UIKind.HyperLink, () =>
new InfoBarUI(ServicesVSResources.Learn_more, InfoBarUI.UIKind.HyperLink, () =>
BrowserHelper.StartBrowser(new Uri(LowVMMoreInfoLink)), closeAfterAction: false));
}
}
Expand Down
Loading

0 comments on commit ab924b3

Please sign in to comment.