提交 8e82124e 编写于 作者: S Shyam N

Display a tooltip for hyperlinks in error list and preview pane

Display a tooltip containing url destination for hyperlinks in the error list and light bulb preview pane UIs.

Also use full fidelity (i.e. substituted)  error message in the search query in the case of hyperlinks that redirect to bing search so that we can present more relevant search results.
上级 388b4e4d
......@@ -116,7 +116,7 @@ private void AssertDiagnostics(ImmutableArray<DiagnosticData> items1, ImmutableA
Assert.Equal(items1[i].Id, items2[i].Id);
Assert.Equal(items1[i].Category, items2[i].Category);
Assert.Equal(items1[i].Message, items2[i].Message);
Assert.Equal(items1[i].MessageFormat, items2[i].MessageFormat);
Assert.Equal(items1[i].ENUMessageForBingSearch, items2[i].ENUMessageForBingSearch);
Assert.Equal(items1[i].Severity, items2[i].Severity);
Assert.Equal(items1[i].IsEnabledByDefault, items2[i].IsEnabledByDefault);
Assert.Equal(items1[i].WarningLevel, items2[i].WarningLevel);
......
// 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 Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Globalization;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Diagnostics
{
......@@ -28,8 +28,7 @@ internal sealed class DiagnosticData
public readonly IReadOnlyList<string> CustomTags;
public readonly ImmutableDictionary<string, string> Properties;
// temporary until we make diagnostic data to point back to diagnostic descriptor
public readonly string MessageFormat;
public readonly string ENUMessageForBingSearch;
public readonly Workspace Workspace;
public readonly ProjectId ProjectId;
......@@ -60,7 +59,7 @@ internal sealed class DiagnosticData
string id,
string category,
string message,
string messageFormat,
string enuMessageForBingSearch,
DiagnosticSeverity severity,
bool isEnabledByDefault,
int warningLevel,
......@@ -77,7 +76,7 @@ internal sealed class DiagnosticData
string description = null,
string helpLink = null) :
this(
id, category, message, messageFormat,
id, category, message, enuMessageForBingSearch,
severity, severity, isEnabledByDefault, warningLevel,
ImmutableArray<string>.Empty, ImmutableDictionary<string, string>.Empty,
workspace, projectId, documentId, span,
......@@ -91,7 +90,7 @@ internal sealed class DiagnosticData
string id,
string category,
string message,
string messageFormat,
string enuMessageForBingSearch,
DiagnosticSeverity severity,
DiagnosticSeverity defaultSeverity,
bool isEnabledByDefault,
......@@ -119,7 +118,7 @@ internal sealed class DiagnosticData
this.Id = id;
this.Category = category;
this.Message = message;
this.MessageFormat = messageFormat;
this.ENUMessageForBingSearch = enuMessageForBingSearch;
this.Severity = severity;
this.DefaultSeverity = defaultSeverity;
......@@ -291,7 +290,7 @@ public static DiagnosticData Create(Workspace workspace, Diagnostic diagnostic)
diagnostic.Id,
diagnostic.Descriptor.Category,
diagnostic.GetMessage(CultureInfo.CurrentUICulture),
diagnostic.Descriptor.MessageFormat.ToString(USCultureInfo),
diagnostic.GetMessage(USCultureInfo), // We use the ENU version of the message for bing search.
diagnostic.Severity,
diagnostic.DefaultSeverity,
diagnostic.Descriptor.IsEnabledByDefault,
......@@ -313,7 +312,7 @@ public static DiagnosticData Create(Project project, Diagnostic diagnostic)
diagnostic.Id,
diagnostic.Descriptor.Category,
diagnostic.GetMessage(CultureInfo.CurrentUICulture),
diagnostic.Descriptor.MessageFormat.ToString(USCultureInfo),
diagnostic.GetMessage(USCultureInfo), // We use the ENU version of the message for bing search.
diagnostic.Severity,
diagnostic.DefaultSeverity,
diagnostic.Descriptor.IsEnabledByDefault,
......@@ -350,7 +349,7 @@ public static DiagnosticData Create(Document document, Diagnostic diagnostic)
diagnostic.Id,
diagnostic.Descriptor.Category,
diagnostic.GetMessage(CultureInfo.CurrentUICulture),
diagnostic.Descriptor.MessageFormat.ToString(USCultureInfo),
diagnostic.GetMessage(USCultureInfo), // We use the ENU version of the message for bing search.
diagnostic.Severity,
diagnostic.DefaultSeverity,
diagnostic.Descriptor.IsEnabledByDefault,
......
......@@ -265,7 +265,7 @@ protected override void WriteTo(Stream stream, AnalysisData data, CancellationTo
writer.WriteString(item.Category);
writer.WriteString(item.Message);
writer.WriteString(item.MessageFormat);
writer.WriteString(item.ENUMessageForBingSearch);
writer.WriteString(item.Title);
writer.WriteString(item.Description);
writer.WriteString(item.HelpLink);
......
......@@ -673,7 +673,7 @@ private DiagnosticData UpdatePosition(DiagnosticData diagnostic, SyntaxTree tree
diagnostic.Id,
diagnostic.Category,
diagnostic.Message,
diagnostic.MessageFormat,
diagnostic.ENUMessageForBingSearch,
diagnostic.Severity,
diagnostic.DefaultSeverity,
diagnostic.IsEnabledByDefault,
......
......@@ -5,7 +5,6 @@
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Navigation;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Diagnostics.Log;
using Microsoft.VisualStudio.LanguageServices.Implementation.Utilities;
using Microsoft.VisualStudio.Text.Differencing;
......@@ -17,8 +16,8 @@ internal partial class PreviewPane : UserControl, IDisposable
private static readonly string s_dummyThreeLineTitle = "A" + Environment.NewLine + "A" + Environment.NewLine + "A";
private static readonly Size s_infiniteSize = new Size(double.PositiveInfinity, double.PositiveInfinity);
private readonly string _errorId;
private readonly bool _telemetry;
private readonly string _id;
private readonly bool _logIdVerbatimInTelemetry;
private readonly IServiceProvider _serviceProvider;
......@@ -26,13 +25,16 @@ internal partial class PreviewPane : UserControl, IDisposable
private double _heightForThreeLineTitle;
private IWpfDifferenceViewer _previewDiffViewer;
public PreviewPane(Image severityIcon, string id, string title, string helpMessage, string description, string helpLink,
bool telemetry, object previewContent, IServiceProvider serviceProvider)
public PreviewPane(Image severityIcon, string id, string title, string description, Uri helpLink, string helpLinkToolTipText,
object previewContent, bool logIdVerbatimInTelemetry, IServiceProvider serviceProvider)
{
InitializeComponent();
InitializeHyperlinkStyles();
_id = id;
_logIdVerbatimInTelemetry = logIdVerbatimInTelemetry;
_serviceProvider = serviceProvider;
// Initialize header portion.
if ((severityIcon != null) && !string.IsNullOrWhiteSpace(id) && !string.IsNullOrWhiteSpace(title))
{
HeaderStackPanel.Visibility = Visibility.Visible;
......@@ -48,15 +50,7 @@ internal partial class PreviewPane : UserControl, IDisposable
// Now set the actual title text.
TitleRun.Text = title;
Uri helpUri;
if (BrowserHelper.TryGetUri(helpLink, out helpUri))
{
InitializeDiagnosticIdHyperLink(id, helpUri, bingLink: false);
}
else
{
InitializeDiagnosticIdHyperLink(id, BrowserHelper.CreateBingQueryUri(id, helpMessage), bingLink: true);
}
InitializeHyperlinks(helpLink, helpLinkToolTipText);
if (!string.IsNullOrWhiteSpace(description))
{
......@@ -64,13 +58,8 @@ internal partial class PreviewPane : UserControl, IDisposable
}
}
// Initialize preview (i.e. diff view) portion.
InitializePreviewElement(previewContent);
_serviceProvider = serviceProvider;
_errorId = id;
// save permission whether we are allowed to save data as it is or not.
_telemetry = telemetry;
}
private void InitializePreviewElement(object previewContent)
......@@ -109,20 +98,19 @@ private void InitializePreviewElement(object previewContent)
AdjustWidthAndHeight(previewElement);
}
private void InitializeHyperlinkStyles()
private void InitializeHyperlinks(Uri helpLink, string helpLinkToolTipText)
{
this.IdHyperlink.SetVSHyperLinkStyle();
this.LearnMoreHyperlink.SetVSHyperLinkStyle();
}
IdHyperlink.SetVSHyperLinkStyle();
LearnMoreHyperlink.SetVSHyperLinkStyle();
private void InitializeDiagnosticIdHyperLink(string id, Uri helpUri, bool bingLink)
{
IdHyperlink.Inlines.Add(id);
IdHyperlink.NavigateUri = helpUri;
IdHyperlink.Inlines.Add(_id);
IdHyperlink.NavigateUri = helpLink;
IdHyperlink.IsEnabled = true;
IdHyperlink.ToolTip = helpLinkToolTipText;
LearnMoreHyperlink.Inlines.Add(string.Format(ServicesVSResources.LearnMoreLinkText, id));
LearnMoreHyperlink.NavigateUri = helpUri;
LearnMoreHyperlink.Inlines.Add(string.Format(ServicesVSResources.LearnMoreLinkText, _id));
LearnMoreHyperlink.NavigateUri = helpLink;
LearnMoreHyperlink.ToolTip = helpLinkToolTipText;
}
public static Border GetPreviewForString(string previewContent, bool useItalicFontStyle = false, bool centerAlignTextHorizontally = false)
......@@ -257,7 +245,7 @@ private void LearnMoreHyperlink_RequestNavigate(object sender, RequestNavigateEv
return;
}
DiagnosticLogger.LogHyperlink(hyperlink.Name ?? "Preview", _errorId, HasDescription, _telemetry, e.Uri.AbsoluteUri);
DiagnosticLogger.LogHyperlink(hyperlink.Name ?? "Preview", _id, HasDescription, _logIdVerbatimInTelemetry, e.Uri.AbsoluteUri);
}
private void ExpanderToggleButton_CheckedChanged(object sender, RoutedEventArgs e)
......
......@@ -13,6 +13,7 @@
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.VisualStudio.Imaging;
using Microsoft.VisualStudio.Imaging.Interop;
using Microsoft.VisualStudio.LanguageServices.Implementation.Utilities;
using Microsoft.VisualStudio.Shell;
namespace Microsoft.VisualStudio.LanguageServices.Implementation.PreviewPane
......@@ -33,7 +34,7 @@ IWorkspaceService IWorkspaceServiceFactory.CreateService(HostWorkspaceServices w
return this;
}
private Image GetSeverityIconForDiagnostic(Diagnostic diagnostic)
private static Image GetSeverityIconForDiagnostic(Diagnostic diagnostic)
{
ImageMoniker? moniker = null;
switch (diagnostic.Severity)
......@@ -63,6 +64,29 @@ private Image GetSeverityIconForDiagnostic(Diagnostic diagnostic)
return null;
}
private static Uri GetHelpLink(Diagnostic diagnostic, out string helpLinkToolTipText)
{
var isBing = false;
helpLinkToolTipText = string.Empty;
Uri helpLink;
if (!BrowserHelper.TryGetUri(diagnostic.Descriptor.HelpLinkUri, out helpLink))
{
// We use the ENU version of the message for bing search.
helpLink = BrowserHelper.CreateBingQueryUri(diagnostic.Id, diagnostic.GetMessage(DiagnosticData.USCultureInfo));
isBing = true;
}
if (helpLink != null)
{
helpLinkToolTipText =
string.Format(ServicesVSResources.DiagnosticIdHyperlinkTooltipText, diagnostic.Id,
isBing ? ServicesVSResources.FromBing : null, Environment.NewLine, helpLink);
}
return helpLink;
}
object IPreviewPaneService.GetPreviewPane(Diagnostic diagnostic, object previewContent)
{
var title = diagnostic?.GetMessage();
......@@ -77,19 +101,22 @@ object IPreviewPaneService.GetPreviewPane(Diagnostic diagnostic, object previewC
}
return new PreviewPane(
severityIcon: null, id: null, title: null, helpMessage: null,
description: null, helpLink: null, telemetry: false,
previewContent: previewContent, serviceProvider: _serviceProvider);
severityIcon: null, id: null, title: null, description: null, helpLink: null, helpLinkToolTipText: null,
previewContent: previewContent, logIdVerbatimInTelemetry: false, serviceProvider: _serviceProvider);
}
var helpLinkToolTipText = string.Empty;
Uri helpLink = GetHelpLink(diagnostic, out helpLinkToolTipText);
return new PreviewPane(
GetSeverityIconForDiagnostic(diagnostic),
diagnostic.Id, title,
diagnostic.Descriptor.MessageFormat.ToString(DiagnosticData.USCultureInfo),
diagnostic.Descriptor.Description.ToString(CultureInfo.CurrentUICulture),
diagnostic.Descriptor.HelpLinkUri,
diagnostic.Descriptor.CustomTags.Contains(WellKnownDiagnosticTags.Telemetry),
previewContent, _serviceProvider);
severityIcon: GetSeverityIconForDiagnostic(diagnostic),
id: diagnostic.Id, title: title,
description: diagnostic.Descriptor.Description.ToString(CultureInfo.CurrentUICulture),
helpLink: helpLink,
helpLinkToolTipText: helpLinkToolTipText,
previewContent: previewContent,
logIdVerbatimInTelemetry: diagnostic.Descriptor.CustomTags.Contains(WellKnownDiagnosticTags.Telemetry),
serviceProvider: _serviceProvider);
}
}
}
......@@ -284,8 +284,12 @@ public override bool TryGetValue(int index, string columnName, out object conten
case StandardTableKeyNames.ErrorCode:
content = item.Id;
return true;
case StandardTableKeyNames.ErrorCodeToolTip:
content = GetHelpLinkToolTipText(item);
return content != null;
case StandardTableKeyNames.HelpLink:
content = GetHelpLink(item);
var isBing = false;
content = GetHelpLink(item, out isBing);
return content != null;
case StandardTableKeyNames.ErrorCategory:
content = item.Category;
......@@ -454,8 +458,10 @@ private static FrameworkElement GetDescriptionTextBlock(DiagnosticData item)
};
}
private string GetHelpLink(DiagnosticData item)
private static string GetHelpLink(DiagnosticData item, out bool isBing)
{
isBing = false;
Uri link;
if (BrowserHelper.TryGetUri(item.HelpLink, out link))
{
......@@ -464,8 +470,22 @@ private string GetHelpLink(DiagnosticData item)
if (!string.IsNullOrWhiteSpace(item.Id))
{
// TODO: once we link descriptor with diagnostic, get en-us message for Uri creation
return BrowserHelper.CreateBingQueryUri(item.Id, item.MessageFormat).AbsoluteUri;
isBing = true;
return BrowserHelper.CreateBingQueryUri(item.Id, item.ENUMessageForBingSearch).AbsoluteUri;
}
return null;
}
private static string GetHelpLinkToolTipText(DiagnosticData item)
{
var isBing = false;
var helpLink = GetHelpLink(item, out isBing);
if (helpLink != null)
{
return string.Format(ServicesVSResources.DiagnosticIdHyperlinkTooltipText, item.Id,
isBing ? ServicesVSResources.FromBing : null, Environment.NewLine, helpLink);
}
return null;
......
......@@ -228,7 +228,7 @@ private static DiagnosticSeverity GetDiagnosticSeverity(ExternalError error)
id: errorId,
category: WellKnownDiagnosticTags.Build,
message: message,
messageFormat: message,
enuMessageForBingSearch: message, // Unfortunately, there is no way to get ENU text for this since this is an external error.
severity: severity,
defaultSeverity: severity,
isEnabledByDefault: true,
......
......@@ -204,6 +204,15 @@ internal class ServicesVSResources {
}
}
/// <summary>
/// Looks up a localized string similar to Get help for &apos;{0}&apos;{1}{2}{3}.
/// </summary>
internal static string DiagnosticIdHyperlinkTooltipText {
get {
return ResourceManager.GetString("DiagnosticIdHyperlinkTooltipText", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to C#/VB Diagnostics Table Data Source.
/// </summary>
......@@ -366,6 +375,15 @@ internal class ServicesVSResources {
}
}
/// <summary>
/// Looks up a localized string similar to from Bing.
/// </summary>
internal static string FromBing {
get {
return ResourceManager.GetString("FromBing", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Generated name:.
/// </summary>
......
......@@ -465,4 +465,10 @@ Use the dropdown to view and switch to other projects this file may belong to.</
<data name="NoChanges" xml:space="preserve">
<value>No Changes</value>
</data>
<data name="DiagnosticIdHyperlinkTooltipText" xml:space="preserve">
<value>Get help for '{0}'{1}{2}{3}</value>
</data>
<data name="FromBing" xml:space="preserve">
<value> from Bing</value>
</data>
</root>
\ No newline at end of file
......@@ -51,7 +51,6 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.ProjectSystemShim
Assert.Equal(1, e.Diagnostics.Length)
Dim diagnostic As DiagnosticData = e.Diagnostics.First()
Assert.Equal("BC42378", diagnostic.Id)
Assert.Equal(ServicesVSResources.WRN_UnableToLoadAnalyzer, diagnostic.MessageFormat)
Assert.Contains(File, diagnostic.Message, StringComparison.Ordinal)
End Sub
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册