提交 01f436af 编写于 作者: H Heejae Chang

Merge pull request #11479 from heejaechang/message

changed low memory error message to new one as we decided in the meeting
......@@ -71,8 +71,8 @@ public override void HandleException(object provider, Exception exception)
_errorReportingService?.ShowErrorInfoForCodeFix(
codefixName: provider.GetType().Name,
OnEnableClicked: () => { EnableProvider(provider); LogEnableProvider(provider); },
OnEnableAndIgnoreClicked: () => { EnableProvider(provider); IgnoreProvider(provider); LogEnableAndIgnoreProvider(provider); },
OnEnable: () => { EnableProvider(provider); LogEnableProvider(provider); },
OnEnableAndIgnore: () => { EnableProvider(provider); IgnoreProvider(provider); LogEnableAndIgnoreProvider(provider); },
OnClose: () => LogLeaveDisabled(provider));
}
else
......
......@@ -8,14 +8,14 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.Workspaces
{
internal class EditorErrorReportingService : IErrorReportingService
{
public void ShowErrorInfoForCodeFix(string codefixName, Action OnEnableClicked, Action OnEnableAndIgnoreClicked, Action OnClose)
public void ShowErrorInfoForCodeFix(string codefixName, Action OnEnable, Action OnEnableAndIgnore, Action OnClose)
{
ShowErrorInfo($"{codefixName} crashed", OnClose);
ShowErrorInfo($"{codefixName} crashed");
}
public void ShowErrorInfo(string title, Action OnClose)
public void ShowErrorInfo(string message, params ErrorReportingUI[] items)
{
Logger.Log(FunctionId.Extension_Exception, title);
Logger.Log(FunctionId.Extension_Exception, message);
}
}
}
......@@ -12,5 +12,6 @@ internal static class RuntimeOptions
public const string OptionName = "Runtime";
public static readonly Option<bool> FullSolutionAnalysis = new Option<bool>(OptionName, "Full Solution Analysis", defaultValue: true);
public static readonly Option<bool> FullSolutionAnalysisInfoBarShown = new Option<bool>(OptionName, "Full Solution Analysis Info Bar Shown", defaultValue: false);
}
}
// 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.Collections.Generic;
using System.Collections.Immutable;
using System.Composition;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Options.Providers;
......@@ -11,7 +12,8 @@ namespace Microsoft.CodeAnalysis.Shared.Options
[ExportOptionProvider, Shared]
internal class RuntimeOptionsProvider : IOptionProvider
{
private readonly IEnumerable<IOption> _options = SpecializedCollections.SingletonEnumerable(RuntimeOptions.FullSolutionAnalysis);
private readonly IEnumerable<IOption> _options = ImmutableArray.Create<IOption>(RuntimeOptions.FullSolutionAnalysis, RuntimeOptions.FullSolutionAnalysisInfoBarShown);
public IEnumerable<IOption> GetOptions() => _options;
}
}
......@@ -330,9 +330,6 @@ private void ProcessSolutionEvent(WorkspaceChangeEventArgs e, IAsyncToken asyncT
private void OnSolutionAdded(Solution solution)
{
// first make sure full solution analysis is on.
_optionService.SetOptions(solution.Workspace.Options.WithChangedOption(RuntimeOptions.FullSolutionAnalysis, true));
var asyncToken = _listener.BeginAsyncOperation("OnSolutionAdded");
_eventProcessingQueue.ScheduleTask(() =>
{
......
......@@ -2,6 +2,8 @@
using System;
using System.Composition;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Editor.Shared.Options;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.Extensions;
......@@ -10,6 +12,7 @@
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Shared.Options;
using Microsoft.VisualStudio.LanguageServices.Implementation;
using Microsoft.VisualStudio.LanguageServices.Implementation.Utilities;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
......@@ -25,6 +28,9 @@ internal sealed class VirtualMemoryNotificationListener : ForegroundThreadAffini
// memory threshold to turn off full solution analysis - 200MB
private const long MemoryThreshold = 200 * 1024 * 1024;
// low vm more info page link
private const string LowVMMoreInfoLink = "http://go.microsoft.com/fwlink/?LinkID=799402&clcid=0x409";
private readonly VisualStudioWorkspace _workspace;
private readonly WorkspaceCacheService _workspaceCacheService;
......@@ -36,13 +42,9 @@ internal sealed class VirtualMemoryNotificationListener : ForegroundThreadAffini
VisualStudioWorkspace workspace) : base(assertIsForeground: true)
{
_workspace = workspace;
_workspace.WorkspaceChanged += OnWorkspaceChanged;
_workspaceCacheService = workspace.Services.GetService<IWorkspaceCacheService>() as WorkspaceCacheService;
if (_workspaceCacheService == null)
{
// No need to hook up the event.
return;
}
var shell = (IVsShell)serviceProvider.GetService(typeof(SVsShell));
......@@ -78,18 +80,28 @@ public int OnBroadcastMessage(uint msg, IntPtr wParam, IntPtr lParam)
_alreadyLogged = true;
}
_workspaceCacheService.FlushCaches();
_workspaceCacheService?.FlushCaches();
// turn off full solution analysis
if ((long)wParam < MemoryThreshold &&
_workspace.Options.GetOption(InternalFeatureOnOffOptions.FullSolutionAnalysisMemoryMonitor) &&
_workspace.Options.GetOption(RuntimeOptions.FullSolutionAnalysis))
// turn off full solution analysis only if user option is on.
if (ShouldTurnOffFullSolutionAnalysis((long)wParam))
{
_workspace.Services.GetService<IOptionService>().SetOptions(_workspace.Options.WithChangedOption(RuntimeOptions.FullSolutionAnalysis, false));
// turn our full solution analysis option off.
// if user full solution analysis option is on, then we will show info bar to users to restore it.
// if user full solution analysis option is off, then setting this doesn't matter. full solution analysis is already off.
_workspace.Options = _workspace.Options.WithChangedOption(RuntimeOptions.FullSolutionAnalysis, false);
// let user know full analysis is turned off due to memory concern
// no close info bar action
_workspace.Services.GetService<IErrorReportingService>().ShowErrorInfo(ServicesVSResources.FullSolutionAnalysisOff, () => { });
if (IsUserOptionOn())
{
// let user know full analysis is turned off due to memory concern.
// make sure we show info bar only once for the same solution.
_workspace.Options = _workspace.Options.WithChangedOption(RuntimeOptions.FullSolutionAnalysisInfoBarShown, true);
_workspace.Services.GetService<IErrorReportingService>().ShowErrorInfo(ServicesVSResources.FullSolutionAnalysisOff,
new ErrorReportingUI(ServicesVSResources.Reenable, ErrorReportingUI.UIKind.Button, () =>
_workspace.Options = _workspace.Options.WithChangedOption(RuntimeOptions.FullSolutionAnalysis, true)),
new ErrorReportingUI(ServicesVSResources.LearnMore, ErrorReportingUI.UIKind.HyperLink, () =>
BrowserHelper.StartBrowser(new Uri(LowVMMoreInfoLink)), closeAfterAction: false));
}
}
// turn off low latency GC mode.
......@@ -104,5 +116,46 @@ public int OnBroadcastMessage(uint msg, IntPtr wParam, IntPtr lParam)
return VSConstants.S_OK;
}
private bool ShouldTurnOffFullSolutionAnalysis(long availableMemory)
{
// conditions
// 1. if available memory is less than the threshold and
// 2. if full solution analysis memory monitor is on (user can set it off using registery, when he does, we will never show info bar) and
// 3. if our full solution analysis option is on (not user full solution analysis option, but our internal one) and
// 4. if infobar is never shown to users for this solution
return availableMemory < MemoryThreshold &&
_workspace.Options.GetOption(InternalFeatureOnOffOptions.FullSolutionAnalysisMemoryMonitor) &&
_workspace.Options.GetOption(RuntimeOptions.FullSolutionAnalysis) &&
!_workspace.Options.GetOption(RuntimeOptions.FullSolutionAnalysisInfoBarShown);
}
private bool IsUserOptionOn()
{
// check languages currently on solution. since we only show info bar once, we don't need to track solution changes.
var languages = _workspace.CurrentSolution.Projects.Select(p => p.Language).Distinct();
foreach (var language in languages)
{
if (ServiceFeatureOnOffOptions.IsClosedFileDiagnosticsEnabled(_workspace, language))
{
return true;
}
}
return false;
}
private void OnWorkspaceChanged(object sender, WorkspaceChangeEventArgs e)
{
if (e.Kind != WorkspaceChangeKind.SolutionAdded)
{
return;
}
// first make sure full solution analysis is on. (not user options but our internal options. even if our option is on, if user option is off
// full solution analysis won't run. also, reset infobar state.
_workspace.Options = _workspace.Options.WithChangedOption(RuntimeOptions.FullSolutionAnalysisInfoBarShown, false)
.WithChangedOption(RuntimeOptions.FullSolutionAnalysis, true);
}
}
}
......@@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis.Editor;
using Microsoft.CodeAnalysis.Extensions;
using Microsoft.CodeAnalysis.Shared.TestHooks;
......@@ -30,7 +31,7 @@ internal class VisualStudioErrorReportingService : IErrorReportingService
_listener = listener;
}
public void ShowErrorInfoForCodeFix(string codefixName, Action OnEnableClicked, Action OnEnableAndIgnoreClicked, Action OnClose)
public void ShowErrorInfoForCodeFix(string codefixName, Action OnEnable, Action OnEnableAndIgnore, Action OnClose)
{
// 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(() =>
......@@ -39,12 +40,12 @@ public void ShowErrorInfoForCodeFix(string codefixName, Action OnEnableClicked,
IVsInfoBarUIFactory factory;
if (_workspace.TryGetInfoBarData(out frame, out factory))
{
CreateInfoBar(factory, frame, string.Format(ServicesVSResources.CodefixOrRefactoringEncounteredError, codefixName), OnClose, OnEnableClicked, OnEnableAndIgnoreClicked);
CreateInfoBarForCodeFix(factory, frame, string.Format(ServicesVSResources.CodefixOrRefactoringEncounteredError, codefixName), OnClose, OnEnable, OnEnableAndIgnore);
}
}, _listener.BeginAsyncOperation("Show InfoBar"));
}
public void ShowErrorInfo(string title, Action OnClose)
public void ShowErrorInfo(string message, params ErrorReportingUI[] 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(() =>
......@@ -53,93 +54,194 @@ public void ShowErrorInfo(string title, Action OnClose)
IVsInfoBarUIFactory factory;
if (_workspace.TryGetInfoBarData(out frame, out factory))
{
CreateInfoBar(factory, frame, title, OnClose);
CreateInfoBar(factory, frame, message, items);
}
}, _listener.BeginAsyncOperation("Show InfoBar"));
}
private void CreateInfoBar(IVsInfoBarUIFactory factory, IVsWindowFrame frame, string title, Action onClose, Action onEnableClicked = null, Action onEnableAndIgnoreClicked = null)
private void CreateInfoBar(IVsInfoBarUIFactory factory, IVsWindowFrame frame, string message, ErrorReportingUI[] items)
{
object unknown;
if (frame.GetProperty((int)__VSFPROPID7.VSFPROPID_InfoBarHost, out unknown) == VSConstants.S_OK)
if (ErrorHandler.Failed(frame.GetProperty((int)__VSFPROPID7.VSFPROPID_InfoBarHost, out unknown)))
{
var textSpans = new List<IVsInfoBarTextSpan>()
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)
{
new InfoBarTextSpan(title)
};
case ErrorReportingUI.UIKind.Button:
actionItems.Add(new InfoBarButton(item.Title));
break;
case ErrorReportingUI.UIKind.HyperLink:
actionItems.Add(new InfoBarHyperlink(item.Title));
break;
case ErrorReportingUI.UIKind.Close:
break;
default:
throw ExceptionUtilities.UnexpectedValue(item.Kind);
}
}
var infoBarModel = new InfoBarModel(
textSpans,
actionItems.ToArray(),
KnownMonikers.StatusInformation,
isCloseButtonVisible: true);
// create action item list
var actionItems = new List<IVsInfoBarActionItem>();
if (onEnableClicked != null)
IVsInfoBarUIElement infoBarUI;
if (!TryCreateInfoBarUI(factory, infoBarModel, out infoBarUI))
{
return;
}
uint? infoBarCookie = null;
var eventSink = new InfoBarEvents(items, () =>
{
// run given onClose action if there is one.
items.FirstOrDefault(i => i.Kind == ErrorReportingUI.UIKind.Close).Action?.Invoke();
if (infoBarCookie.HasValue)
{
actionItems.Add(s_enableItem);
infoBarUI.Unadvise(infoBarCookie.Value);
}
});
uint cookie;
infoBarUI.Advise(eventSink, out cookie);
infoBarCookie = cookie;
var host = (IVsInfoBarHost)unknown;
host.AddInfoBar(infoBarUI);
}
if (onEnableAndIgnoreClicked != null)
private class InfoBarEvents : IVsInfoBarUIEvents
{
private readonly ErrorReportingUI[] _items;
private readonly Action _onClose;
public InfoBarEvents(ErrorReportingUI[] 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)
{
actionItems.Add(s_enableAndIgnoreItem);
return;
}
var infoBarModel = new InfoBarModel(
textSpans,
actionItems.ToArray(),
KnownMonikers.StatusInformation,
isCloseButtonVisible: true);
item.Action?.Invoke();
IVsInfoBarUIElement infoBarUI;
if (TryCreateInfoBarUI(factory, infoBarModel, out infoBarUI))
if (!item.CloseAfterAction)
{
uint? infoBarCookie = null;
InfoBarEvents eventSink = new InfoBarEvents(() =>
{
onClose();
if (infoBarCookie.HasValue)
{
infoBarUI.Unadvise(infoBarCookie.Value);
}
}, onEnableClicked, onEnableAndIgnoreClicked);
uint cookie;
infoBarUI.Advise(eventSink, out cookie);
infoBarCookie = cookie;
IVsInfoBarHost host = (IVsInfoBarHost)unknown;
host.AddInfoBar(infoBarUI);
return;
}
infoBarUIElement.Close();
}
public void OnClosed(IVsInfoBarUIElement infoBarUIElement)
{
_onClose();
}
}
private static bool TryCreateInfoBarUI(IVsInfoBarUIFactory infoBarUIFactory, IVsInfoBar infoBar, out IVsInfoBarUIElement uiElement)
private void CreateInfoBarForCodeFix(IVsInfoBarUIFactory factory, IVsWindowFrame frame, string message, Action onClose, Action onEnable = null, Action onEnableAndIgnore = null)
{
uiElement = infoBarUIFactory.CreateInfoBar(infoBar);
return uiElement != null;
object unknown;
if (ErrorHandler.Failed(frame.GetProperty((int)__VSFPROPID7.VSFPROPID_InfoBarHost, out unknown)))
{
return;
}
var textSpans = new List<IVsInfoBarTextSpan>()
{
new InfoBarTextSpan(message)
};
// create action item list
var actionItems = new List<IVsInfoBarActionItem>();
if (onEnable != null)
{
actionItems.Add(s_enableItem);
}
if (onEnableAndIgnore != null)
{
actionItems.Add(s_enableAndIgnoreItem);
}
var infoBarModel = new InfoBarModel(
textSpans,
actionItems.ToArray(),
KnownMonikers.StatusInformation,
isCloseButtonVisible: true);
IVsInfoBarUIElement infoBarUI;
if (!TryCreateInfoBarUI(factory, infoBarModel, out infoBarUI))
{
return;
}
uint? infoBarCookie = null;
var eventSink = new CodeFixInfoBarEvents(() =>
{
onClose();
if (infoBarCookie.HasValue)
{
infoBarUI.Unadvise(infoBarCookie.Value);
}
}, onEnable, onEnableAndIgnore);
uint cookie;
infoBarUI.Advise(eventSink, out cookie);
infoBarCookie = cookie;
IVsInfoBarHost host = (IVsInfoBarHost)unknown;
host.AddInfoBar(infoBarUI);
}
private class InfoBarEvents : IVsInfoBarUIEvents
private class CodeFixInfoBarEvents : IVsInfoBarUIEvents
{
private readonly Action _onClosed;
private readonly Action _onEnableAndIgnoreClicked;
private readonly Action _onEnableClicked;
private readonly Action _onClose;
private readonly Action _onEnable;
private readonly Action _onEnableAndIgnore;
public InfoBarEvents(Action onClose, Action onEnableClicked = null, Action onEnableAndIgnoreClicked = null)
public CodeFixInfoBarEvents(Action onClose, Action onEnable = null, Action onEnableAndIgnore = null)
{
Contract.ThrowIfNull(onClose);
_onClosed = onClose;
_onEnableClicked = onEnableClicked;
_onEnableAndIgnoreClicked = onEnableAndIgnoreClicked;
_onClose = onClose;
_onEnable = onEnable;
_onEnableAndIgnore = onEnableAndIgnore;
}
public void OnActionItemClicked(IVsInfoBarUIElement infoBarUIElement, IVsInfoBarActionItem actionItem)
{
if (actionItem.Equals(s_enableItem))
{
_onEnableClicked?.Invoke();
_onEnable?.Invoke();
}
if (actionItem.Equals(s_enableAndIgnoreItem))
{
_onEnableAndIgnoreClicked?.Invoke();
_onEnableAndIgnore?.Invoke();
}
infoBarUIElement.Close();
......@@ -147,8 +249,14 @@ public void OnActionItemClicked(IVsInfoBarUIElement infoBarUIElement, IVsInfoBar
public void OnClosed(IVsInfoBarUIElement infoBarUIElement)
{
_onClosed();
_onClose();
}
}
private static bool TryCreateInfoBarUI(IVsInfoBarUIFactory infoBarUIFactory, IVsInfoBar infoBar, out IVsInfoBarUIElement uiElement)
{
uiElement = infoBarUIFactory.CreateInfoBar(infoBar);
return uiElement != null;
}
}
}
......@@ -430,7 +430,7 @@ internal class ServicesVSResources {
}
/// <summary>
/// Looks up a localized string similar to Low memory detected. Full solution analysis disabled for this solution..
/// Looks up a localized string similar to Visual Studio suspended some advanced features to improve performance..
/// </summary>
internal static string FullSolutionAnalysisOff {
get {
......@@ -646,6 +646,15 @@ internal class ServicesVSResources {
}
}
/// <summary>
/// Looks up a localized string similar to Learn more.
/// </summary>
internal static string LearnMore {
get {
return ResourceManager.GetString("LearnMore", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to More about {0}.
/// </summary>
......@@ -1066,6 +1075,15 @@ internal class ServicesVSResources {
}
}
/// <summary>
/// Looks up a localized string similar to Re-enable.
/// </summary>
internal static string Reenable {
get {
return ResourceManager.GetString("Reenable", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to {0} references.
/// </summary>
......
......@@ -508,7 +508,7 @@ Use the dropdown to view and switch to other projects this file may belong to.</
<value>Synchronizing with {0}...</value>
</data>
<data name="FullSolutionAnalysisOff" xml:space="preserve">
<value>Low memory detected. Full solution analysis disabled for this solution.</value>
<value>Visual Studio suspended some advanced features to improve performance.</value>
</data>
<data name="Installing_0" xml:space="preserve">
<value>Installing '{0}'</value>
......@@ -583,4 +583,10 @@ Additional information: {1}</value>
<value>Restore {0}</value>
<comment>{0} is a parameter description</comment>
</data>
<data name="Reenable" xml:space="preserve">
<value>Re-enable</value>
</data>
<data name="LearnMore" xml:space="preserve">
<value>Learn more</value>
</data>
</root>
\ No newline at end of file
......@@ -2,12 +2,40 @@
using System;
using Microsoft.CodeAnalysis.Host;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Extensions
{
internal interface IErrorReportingService : IWorkspaceService
{
void ShowErrorInfoForCodeFix(string codefixName, Action OnEnableClicked, Action OnEnableAndIgnoreClicked, Action OnClose);
void ShowErrorInfo(string title, Action OnClose);
void ShowErrorInfoForCodeFix(string codefixName, Action OnEnable, Action OnEnableAndIgnore, Action OnClose);
void ShowErrorInfo(string message, params ErrorReportingUI[] items);
}
internal struct ErrorReportingUI
{
public readonly string Title;
public readonly UIKind Kind;
public readonly Action Action;
public readonly bool CloseAfterAction;
public ErrorReportingUI(string title, UIKind kind, Action action, bool closeAfterAction = true)
{
Contract.ThrowIfNull(title);
Title = title;
Kind = kind;
Action = action;
CloseAfterAction = closeAfterAction;
}
public bool IsDefault => Title == null;
internal enum UIKind
{
Button,
HyperLink,
Close
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册