diff --git a/src/EditorFeatures/Core.Wpf/EditorFeaturesWpfResources.resx b/src/EditorFeatures/Core.Wpf/EditorFeaturesWpfResources.resx
index 3d96f63a2048a..3c4c2e3f754e0 100644
--- a/src/EditorFeatures/Core.Wpf/EditorFeaturesWpfResources.resx
+++ b/src/EditorFeatures/Core.Wpf/EditorFeaturesWpfResources.resx
@@ -180,4 +180,19 @@
Interactive host process platform
+
+ Multiple members are inherited
+
+
+ is inherited
+
+
+ Navigate to '{0}'
+
+
+ '{0}' in '{1}'
+
+
+ Multiple members are inherited on line {0}
+
\ No newline at end of file
diff --git a/src/EditorFeatures/Core.Wpf/InheritanceMargin/InheritanceGlyphFactory.cs b/src/EditorFeatures/Core.Wpf/InheritanceMargin/InheritanceGlyphFactory.cs
index 1a86842e54720..32a9256309a87 100644
--- a/src/EditorFeatures/Core.Wpf/InheritanceMargin/InheritanceGlyphFactory.cs
+++ b/src/EditorFeatures/Core.Wpf/InheritanceMargin/InheritanceGlyphFactory.cs
@@ -3,16 +3,52 @@
// See the LICENSE file in the project root for more information.
using System.Windows;
+using Microsoft.CodeAnalysis.Editor.Host;
+using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
+using Microsoft.VisualStudio.Text.Classification;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Text.Formatting;
+using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Editor.InheritanceMargin
{
internal sealed class InheritanceGlyphFactory : IGlyphFactory
{
+ private readonly IThreadingContext _threadingContext;
+ private readonly IStreamingFindUsagesPresenter _streamingFindUsagesPresenter;
+ private readonly ClassificationTypeMap _classificationTypeMap;
+ private readonly IClassificationFormatMap _classificationFormatMap;
+ private readonly IWaitIndicator _waitIndicator;
+
+ public InheritanceGlyphFactory(
+ IThreadingContext threadingContext,
+ IStreamingFindUsagesPresenter streamingFindUsagesPresenter,
+ ClassificationTypeMap classificationTypeMap,
+ IClassificationFormatMap classificationFormatMap,
+ IWaitIndicator waitIndicator)
+ {
+ _threadingContext = threadingContext;
+ _streamingFindUsagesPresenter = streamingFindUsagesPresenter;
+ _classificationTypeMap = classificationTypeMap;
+ _classificationFormatMap = classificationFormatMap;
+ _waitIndicator = waitIndicator;
+ }
+
public UIElement? GenerateGlyph(IWpfTextViewLine line, IGlyphTag tag)
{
- // TODO: Add UI
+ if (tag is InheritanceMarginTag inheritanceMarginTag)
+ {
+ var membersOnLine = inheritanceMarginTag.MembersOnLine;
+ Contract.ThrowIfTrue(membersOnLine.IsEmpty);
+ return new MarginGlyph.InheritanceMargin(
+ _threadingContext,
+ _streamingFindUsagesPresenter,
+ _classificationTypeMap,
+ _classificationFormatMap,
+ _waitIndicator,
+ inheritanceMarginTag);
+ }
+
return null;
}
}
diff --git a/src/EditorFeatures/Core.Wpf/InheritanceMargin/InheritanceGlyphFactoryProvider.cs b/src/EditorFeatures/Core.Wpf/InheritanceMargin/InheritanceGlyphFactoryProvider.cs
index bb15b72cfe72e..ad5ebca150623 100644
--- a/src/EditorFeatures/Core.Wpf/InheritanceMargin/InheritanceGlyphFactoryProvider.cs
+++ b/src/EditorFeatures/Core.Wpf/InheritanceMargin/InheritanceGlyphFactoryProvider.cs
@@ -4,7 +4,10 @@
using System;
using System.ComponentModel.Composition;
+using Microsoft.CodeAnalysis.Editor.Host;
+using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.Host.Mef;
+using Microsoft.VisualStudio.Text.Classification;
using Microsoft.VisualStudio.Text.Editor;
using Microsoft.VisualStudio.Text.Tagging;
using Microsoft.VisualStudio.Utilities;
@@ -16,18 +19,35 @@ namespace Microsoft.CodeAnalysis.Editor.InheritanceMargin
[ContentType(ContentTypeNames.CSharpContentType)]
[ContentType(ContentTypeNames.VisualBasicContentType)]
[TagType(typeof(InheritanceMarginTag))]
- [Order]
+ // This would ensure the margin is clickable.
+ [Order(After = "VsTextMarker")]
internal class InheritanceGlyphFactoryProvider : IGlyphFactoryProvider
{
+ private readonly IThreadingContext _threadingContext;
+ private readonly IStreamingFindUsagesPresenter _streamingFindUsagesPresenter;
+ private readonly ClassificationTypeMap _classificationTypeMap;
+ private readonly IClassificationFormatMap _classificationFormatMap;
+ private readonly IWaitIndicator _waitIndicator;
+
[ImportingConstructor]
[Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
- public InheritanceGlyphFactoryProvider()
+ public InheritanceGlyphFactoryProvider(
+ IThreadingContext threadingContext,
+ IStreamingFindUsagesPresenter streamingFindUsagesPresenter,
+ ClassificationTypeMap classificationTypeMap,
+ IClassificationFormatMapService classificationFormatMapService,
+ IWaitIndicator waitIndicator)
{
+ _threadingContext = threadingContext;
+ _streamingFindUsagesPresenter = streamingFindUsagesPresenter;
+ _classificationTypeMap = classificationTypeMap;
+ _classificationFormatMap = classificationFormatMapService.GetClassificationFormatMap("tooltip");
+ _waitIndicator = waitIndicator;
}
public IGlyphFactory GetGlyphFactory(IWpfTextView view, IWpfTextViewMargin margin)
{
- return new InheritanceGlyphFactory();
+ return new InheritanceGlyphFactory(_threadingContext, _streamingFindUsagesPresenter, _classificationTypeMap, _classificationFormatMap, _waitIndicator);
}
}
}
diff --git a/src/EditorFeatures/Core.Wpf/InheritanceMargin/InheritanceMarginHelpers.cs b/src/EditorFeatures/Core.Wpf/InheritanceMargin/InheritanceMarginHelpers.cs
new file mode 100644
index 0000000000000..01b5dac5526af
--- /dev/null
+++ b/src/EditorFeatures/Core.Wpf/InheritanceMargin/InheritanceMarginHelpers.cs
@@ -0,0 +1,55 @@
+// 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.
+
+using Microsoft.CodeAnalysis.InheritanceMargin;
+using Microsoft.VisualStudio.Imaging;
+using Microsoft.VisualStudio.Imaging.Interop;
+using Roslyn.Utilities;
+
+namespace Microsoft.CodeAnalysis.Editor.InheritanceMargin
+{
+ internal static class InheritanceMarginHelpers
+ {
+ ///
+ /// Decide which moniker should be shown.
+ ///
+ public static ImageMoniker GetMoniker(InheritanceRelationship inheritanceRelationship)
+ {
+ // If there are multiple targets and we have the corresponding compound image, use it
+ if (inheritanceRelationship.HasFlag(InheritanceRelationship.ImplementingOverriding))
+ {
+ return KnownMonikers.ImplementingOverriding;
+ }
+
+ if (inheritanceRelationship.HasFlag(InheritanceRelationship.ImplementingOverridden))
+ {
+ return KnownMonikers.ImplementingOverridden;
+ }
+
+ // Otherwise, show the image based on this preference
+ if (inheritanceRelationship.HasFlag(InheritanceRelationship.Implemented))
+ {
+ return KnownMonikers.Implemented;
+ }
+
+ if (inheritanceRelationship.HasFlag(InheritanceRelationship.Implementing))
+ {
+ return KnownMonikers.Implementing;
+ }
+
+ if (inheritanceRelationship.HasFlag(InheritanceRelationship.Overridden))
+ {
+ return KnownMonikers.Overridden;
+ }
+
+ if (inheritanceRelationship.HasFlag(InheritanceRelationship.Overriding))
+ {
+ return KnownMonikers.Overriding;
+ }
+
+ // The relationship is None. Don't know what image should be shown, throws
+ throw ExceptionUtilities.UnexpectedValue(inheritanceRelationship);
+ }
+ }
+}
diff --git a/src/EditorFeatures/Core.Wpf/InheritanceMargin/InheritanceMarginTag.cs b/src/EditorFeatures/Core.Wpf/InheritanceMargin/InheritanceMarginTag.cs
index 157464edc8cd4..a82eb3b247925 100644
--- a/src/EditorFeatures/Core.Wpf/InheritanceMargin/InheritanceMarginTag.cs
+++ b/src/EditorFeatures/Core.Wpf/InheritanceMargin/InheritanceMarginTag.cs
@@ -3,10 +3,8 @@
// See the LICENSE file in the project root for more information.
using System.Collections.Immutable;
-using System.Diagnostics;
using System.Linq;
using Microsoft.CodeAnalysis.InheritanceMargin;
-using Microsoft.VisualStudio.Imaging;
using Microsoft.VisualStudio.Imaging.Interop;
using Microsoft.VisualStudio.Text.Editor;
using Roslyn.Utilities;
@@ -18,7 +16,7 @@ internal class InheritanceMarginTag : IGlyphTag
///
/// Margin moniker.
///
- public readonly ImageMoniker Moniker;
+ public ImageMoniker Moniker { get; }
///
/// Members needs to be shown on this line. There might be multiple members.
@@ -28,10 +26,19 @@ internal class InheritanceMarginTag : IGlyphTag
///
public readonly ImmutableArray MembersOnLine;
- public InheritanceMarginTag(ImmutableArray membersOnLine)
+ ///
+ /// Used for accessibility purpose.
+ ///
+ public readonly int LineNumber;
+
+ public readonly Workspace Workspace;
+
+ public InheritanceMarginTag(Workspace workspace, int lineNumber, ImmutableArray membersOnLine)
{
Contract.ThrowIfTrue(membersOnLine.IsEmpty);
+ Workspace = workspace;
+ LineNumber = lineNumber;
MembersOnLine = membersOnLine;
// The common case, one line has one member, avoid to use select & aggregate
if (membersOnLine.Length == 1)
@@ -44,7 +51,7 @@ public InheritanceMarginTag(ImmutableArray membersOnLine)
relationship |= target.RelationToMember;
}
- Moniker = GetMoniker(relationship);
+ Moniker = InheritanceMarginHelpers.GetMoniker(relationship);
}
else
{
@@ -52,49 +59,8 @@ public InheritanceMarginTag(ImmutableArray membersOnLine)
var aggregateRelationship = membersOnLine
.SelectMany(member => member.TargetItems.Select(target => target.RelationToMember))
.Aggregate((r1, r2) => r1 | r2);
- Moniker = GetMoniker(aggregateRelationship);
- }
- }
-
- ///
- /// Decide which moniker should be shown.
- ///
- private static ImageMoniker GetMoniker(InheritanceRelationship inheritanceRelationship)
- {
- // If there are multiple targets and we have the corresponding compound image, use it
- if (inheritanceRelationship.HasFlag(InheritanceRelationship.ImplementingOverriding))
- {
- return KnownMonikers.ImplementingOverriding;
- }
-
- if (inheritanceRelationship.HasFlag(InheritanceRelationship.ImplementingOverridden))
- {
- return KnownMonikers.ImplementingOverridden;
+ Moniker = InheritanceMarginHelpers.GetMoniker(aggregateRelationship);
}
-
- // Otherwise, show the image based on this preference
- if (inheritanceRelationship.HasFlag(InheritanceRelationship.Implemented))
- {
- return KnownMonikers.Implemented;
- }
-
- if (inheritanceRelationship.HasFlag(InheritanceRelationship.Implementing))
- {
- return KnownMonikers.Implementing;
- }
-
- if (inheritanceRelationship.HasFlag(InheritanceRelationship.Overridden))
- {
- return KnownMonikers.Overridden;
- }
-
- if (inheritanceRelationship.HasFlag(InheritanceRelationship.Overriding))
- {
- return KnownMonikers.Overriding;
- }
-
- // The relationship is None. Don't know what image should be shown, throws
- throw ExceptionUtilities.UnexpectedValue(inheritanceRelationship);
}
}
}
diff --git a/src/EditorFeatures/Core.Wpf/InheritanceMargin/InheritanceMarginTaggerProvider.cs b/src/EditorFeatures/Core.Wpf/InheritanceMargin/InheritanceMarginTaggerProvider.cs
index 565aacb16641e..bc9a5170530d1 100644
--- a/src/EditorFeatures/Core.Wpf/InheritanceMargin/InheritanceMarginTaggerProvider.cs
+++ b/src/EditorFeatures/Core.Wpf/InheritanceMargin/InheritanceMarginTaggerProvider.cs
@@ -118,6 +118,7 @@ protected override async Task ProduceTagsAsync(
.GroupBy(item => item.LineNumber);
var snapshot = spanToTag.SnapshotSpan.Snapshot;
+
foreach (var (lineNumber, membersOnTheLine) in lineToMembers)
{
var membersOnTheLineArray = membersOnTheLine.ToImmutableArray();
@@ -129,7 +130,7 @@ protected override async Task ProduceTagsAsync(
// We only care about the line, so just tag the start.
context.AddTag(new TagSpan(
new SnapshotSpan(snapshot, line.Start, length: 0),
- new InheritanceMarginTag(membersOnTheLineArray)));
+ new InheritanceMarginTag(document.Project.Solution.Workspace, lineNumber, membersOnTheLineArray)));
}
}
}
diff --git a/src/EditorFeatures/Core.Wpf/InheritanceMargin/MarginGlyph/InheritanceContextMenuItemViewModel.cs b/src/EditorFeatures/Core.Wpf/InheritanceMargin/MarginGlyph/InheritanceContextMenuItemViewModel.cs
new file mode 100644
index 0000000000000..c586d99360c84
--- /dev/null
+++ b/src/EditorFeatures/Core.Wpf/InheritanceMargin/MarginGlyph/InheritanceContextMenuItemViewModel.cs
@@ -0,0 +1,33 @@
+// 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.
+
+using Microsoft.VisualStudio.Imaging.Interop;
+
+namespace Microsoft.CodeAnalysis.Editor.InheritanceMargin.MarginGlyph
+{
+ internal abstract class InheritanceContextMenuItemViewModel
+ {
+ ///
+ /// Display content for the target.
+ ///
+ public string DisplayContent { get; }
+
+ ///
+ /// ImageMoniker shown before the display name.
+ ///
+ public ImageMoniker ImageMoniker { get; }
+
+ ///
+ /// AutomationName for the MenuItem.
+ ///
+ public string AutomationName { get; }
+
+ protected InheritanceContextMenuItemViewModel(string displayContent, ImageMoniker imageMoniker, string automationName)
+ {
+ DisplayContent = displayContent;
+ ImageMoniker = imageMoniker;
+ AutomationName = automationName;
+ }
+ }
+}
diff --git a/src/EditorFeatures/Core.Wpf/InheritanceMargin/MarginGlyph/InheritanceMargin.xaml b/src/EditorFeatures/Core.Wpf/InheritanceMargin/MarginGlyph/InheritanceMargin.xaml
new file mode 100644
index 0000000000000..42ff27119c620
--- /dev/null
+++ b/src/EditorFeatures/Core.Wpf/InheritanceMargin/MarginGlyph/InheritanceMargin.xaml
@@ -0,0 +1,221 @@
+
\ No newline at end of file
diff --git a/src/EditorFeatures/Core.Wpf/InheritanceMargin/MarginGlyph/InheritanceMargin.xaml.cs b/src/EditorFeatures/Core.Wpf/InheritanceMargin/MarginGlyph/InheritanceMargin.xaml.cs
new file mode 100644
index 0000000000000..18fa5f28923ee
--- /dev/null
+++ b/src/EditorFeatures/Core.Wpf/InheritanceMargin/MarginGlyph/InheritanceMargin.xaml.cs
@@ -0,0 +1,130 @@
+// 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.
+
+using System.Collections.Immutable;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Input;
+using System.Windows.Media;
+using Microsoft.CodeAnalysis.Editor.GoToDefinition;
+using Microsoft.CodeAnalysis.Editor.Host;
+using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
+using Microsoft.CodeAnalysis.Internal.Log;
+using Microsoft.VisualStudio.Text.Classification;
+
+namespace Microsoft.CodeAnalysis.Editor.InheritanceMargin.MarginGlyph
+{
+ internal partial class InheritanceMargin
+ {
+ private readonly IThreadingContext _threadingContext;
+ private readonly IStreamingFindUsagesPresenter _streamingFindUsagesPresenter;
+ private readonly IWaitIndicator _waitIndicator;
+ private readonly Workspace _workspace;
+
+ ///
+ /// Note: This name is used in xaml file.
+ ///
+ private const string SingleMemberContextMenuStyle = nameof(SingleMemberContextMenuStyle);
+
+ ///
+ /// Note: This name is used in xaml file.
+ ///
+ private const string MultipleMembersContextMenuStyle = nameof(MultipleMembersContextMenuStyle);
+
+ public InheritanceMargin(
+ IThreadingContext threadingContext,
+ IStreamingFindUsagesPresenter streamingFindUsagesPresenter,
+ ClassificationTypeMap classificationTypeMap,
+ IClassificationFormatMap classificationFormatMap,
+ IWaitIndicator waitIndicator,
+ InheritanceMarginTag tag)
+ {
+ _threadingContext = threadingContext;
+ _streamingFindUsagesPresenter = streamingFindUsagesPresenter;
+ _workspace = tag.Workspace;
+ _waitIndicator = waitIndicator;
+ InitializeComponent();
+
+ var viewModel = InheritanceMarginViewModel.Create(classificationTypeMap, classificationFormatMap, tag);
+ DataContext = viewModel;
+ ContextMenu.DataContext = viewModel;
+ ToolTip = new ToolTip { Content = viewModel.ToolTipTextBlock, Style = (Style)FindResource("ToolTipStyle") };
+ }
+
+ private void InheritanceMargin_OnClick(object sender, RoutedEventArgs e)
+ {
+ if (this.ContextMenu != null)
+ {
+ this.ContextMenu.IsOpen = true;
+ e.Handled = true;
+ }
+ }
+
+ private void TargetMenuItem_OnClick(object sender, RoutedEventArgs e)
+ {
+ if (e.OriginalSource is MenuItem { DataContext: TargetMenuItemViewModel viewModel })
+ {
+ Logger.Log(FunctionId.InheritanceMargin_NavigateToTarget, KeyValueLogMessage.Create(LogType.UserAction));
+ _waitIndicator.Wait(
+ title: EditorFeaturesResources.Navigating,
+ message: string.Format(EditorFeaturesWpfResources.Navigate_to_0, viewModel.DisplayContent),
+ allowCancel: true,
+ context => GoToDefinitionHelpers.TryGoToDefinition(
+ ImmutableArray.Create(viewModel.DefinitionItem),
+ _workspace,
+ string.Format(EditorFeaturesResources._0_declarations, viewModel.DisplayContent),
+ _threadingContext,
+ _streamingFindUsagesPresenter,
+ context.CancellationToken));
+ }
+ }
+
+ private void ChangeBorderToHoveringColor()
+ {
+ SetResourceReference(BackgroundProperty, "VsBrush.CommandBarMenuBackgroundGradient");
+ SetResourceReference(BorderBrushProperty, "VsBrush.CommandBarMenuBorder");
+ }
+
+ private void InheritanceMargin_OnMouseEnter(object sender, MouseEventArgs e)
+ {
+ ChangeBorderToHoveringColor();
+ }
+
+ private void InheritanceMargin_OnMouseLeave(object sender, MouseEventArgs e)
+ {
+ // If the context menu is open, then don't reset the color of the button because we need
+ // the margin looks like being pressed.
+ if (!ContextMenu.IsOpen)
+ {
+ ResetBorderToInitialColor();
+ }
+ }
+
+ private void ContextMenu_OnClose(object sender, RoutedEventArgs e)
+ {
+ ResetBorderToInitialColor();
+ }
+
+ private void ContextMenu_OnOpen(object sender, RoutedEventArgs e)
+ {
+ // If this context menu just has one member, then if the context menu open, it means all inheritance targets are shown.
+ if (e.OriginalSource is ContextMenu { DataContext: InheritanceMarginViewModel { HasMultipleMembers: false } })
+ {
+ Logger.Log(FunctionId.InheritanceMargin_TargetsMenuOpen, KeyValueLogMessage.Create(LogType.UserAction));
+ }
+ }
+
+ private void TargetsMenu_OnOpen(object sender, RoutedEventArgs e)
+ {
+ Logger.Log(FunctionId.InheritanceMargin_TargetsMenuOpen, KeyValueLogMessage.Create(LogType.UserAction));
+ }
+
+ private void ResetBorderToInitialColor()
+ {
+ this.Background = Brushes.Transparent;
+ this.BorderBrush = Brushes.Transparent;
+ }
+ }
+}
+
diff --git a/src/EditorFeatures/Core.Wpf/InheritanceMargin/MarginGlyph/InheritanceMarginViewModel.cs b/src/EditorFeatures/Core.Wpf/InheritanceMargin/MarginGlyph/InheritanceMarginViewModel.cs
new file mode 100644
index 0000000000000..7d2d314457e0b
--- /dev/null
+++ b/src/EditorFeatures/Core.Wpf/InheritanceMargin/MarginGlyph/InheritanceMarginViewModel.cs
@@ -0,0 +1,96 @@
+// 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.
+
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Windows.Controls;
+using System.Windows.Documents;
+using Microsoft.CodeAnalysis.Editor.Shared.Extensions;
+using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
+using Microsoft.VisualStudio.Imaging.Interop;
+using Microsoft.VisualStudio.Text.Classification;
+
+namespace Microsoft.CodeAnalysis.Editor.InheritanceMargin.MarginGlyph
+{
+ internal class InheritanceMarginViewModel
+ {
+ ///
+ /// ImageMoniker used for the margin.
+ ///
+ public ImageMoniker ImageMoniker { get; }
+
+ ///
+ /// Tooltip for the margin.
+ ///
+ public TextBlock ToolTipTextBlock { get; }
+
+ ///
+ /// Text used for automation.
+ ///
+ public string AutomationName { get; }
+
+ ///
+ /// ViewModels for the context menu items.
+ ///
+ public ImmutableArray MenuItemViewModels { get; }
+
+ public bool HasMultipleMembers { get; }
+
+ private InheritanceMarginViewModel(
+ ImageMoniker imageMoniker,
+ TextBlock toolTipTextBlock,
+ string automationName,
+ ImmutableArray menuItemViewModels,
+ bool hasMultipleMembers)
+ {
+ ImageMoniker = imageMoniker;
+ ToolTipTextBlock = toolTipTextBlock;
+ AutomationName = automationName;
+ MenuItemViewModels = menuItemViewModels;
+ HasMultipleMembers = hasMultipleMembers;
+ }
+
+ public static InheritanceMarginViewModel Create(
+ ClassificationTypeMap classificationTypeMap,
+ IClassificationFormatMap classificationFormatMap,
+ InheritanceMarginTag tag)
+ {
+ var members = tag.MembersOnLine;
+ if (members.Length == 1)
+ {
+ var member = tag.MembersOnLine[0];
+ var textAppended = " " + EditorFeaturesWpfResources.is_inherited;
+ var inlines = member.DisplayTexts.ToInlines(classificationFormatMap, classificationTypeMap);
+ WrapMemberWithinApostrophe(inlines);
+ inlines.Add(new Run(textAppended));
+ var toolTipTextBlock = inlines.ToTextBlock(classificationFormatMap);
+
+ var automationName = member.DisplayTexts.JoinText() + textAppended;
+ var menuItemViewModels = member.TargetItems
+ .SelectAsArray(TargetMenuItemViewModel.Create).CastArray();
+ return new InheritanceMarginViewModel(tag.Moniker, toolTipTextBlock, automationName, menuItemViewModels, false);
+ }
+ else
+ {
+ var textBlock = new TextBlock
+ {
+ Text = EditorFeaturesWpfResources.Multiple_members_are_inherited
+ };
+
+ // Same automation name can't be set for control. So add the line number info.
+ var automationName = string.Format(EditorFeaturesWpfResources.Multiple_members_are_inherited_on_line_0, tag.LineNumber);
+ var menuItemViewModels = tag.MembersOnLine
+ .SelectAsArray(MemberMenuItemViewModel.Create)
+ .CastArray();
+ return new InheritanceMarginViewModel(tag.Moniker, textBlock, automationName, menuItemViewModels, true);
+ }
+ }
+
+ private static void WrapMemberWithinApostrophe(IList memberInlines)
+ {
+ memberInlines.Insert(0, new Run("'"));
+ memberInlines.Add(new Run("'"));
+ }
+ }
+}
diff --git a/src/EditorFeatures/Core.Wpf/InheritanceMargin/MarginGlyph/MemberMenuItemViewModel.cs b/src/EditorFeatures/Core.Wpf/InheritanceMargin/MarginGlyph/MemberMenuItemViewModel.cs
new file mode 100644
index 0000000000000..a8f0568d63051
--- /dev/null
+++ b/src/EditorFeatures/Core.Wpf/InheritanceMargin/MarginGlyph/MemberMenuItemViewModel.cs
@@ -0,0 +1,41 @@
+// 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.
+
+using System.Collections.Immutable;
+using Microsoft.CodeAnalysis.Editor.Wpf;
+using Microsoft.CodeAnalysis.InheritanceMargin;
+using Microsoft.VisualStudio.Imaging.Interop;
+
+namespace Microsoft.CodeAnalysis.Editor.InheritanceMargin.MarginGlyph
+{
+ ///
+ /// View model used to display a member in MenuItem. Only used when there are multiple members on the same line.
+ ///
+ internal class MemberMenuItemViewModel : InheritanceContextMenuItemViewModel
+ {
+ ///
+ /// Inheritance Targets for this member.
+ ///
+ public ImmutableArray Targets { get; }
+
+ public MemberMenuItemViewModel(
+ string displayContent,
+ ImageMoniker imageMoniker,
+ string automationName,
+ ImmutableArray targets) : base(displayContent, imageMoniker, automationName)
+ {
+ Targets = targets;
+ }
+
+ public static MemberMenuItemViewModel Create(InheritanceMarginItem member)
+ {
+ var displayName = member.DisplayTexts.JoinText();
+ return new MemberMenuItemViewModel(
+ displayName,
+ member.Glyph.GetImageMoniker(),
+ displayName,
+ member.TargetItems.SelectAsArray(TargetMenuItemViewModel.Create));
+ }
+ }
+}
diff --git a/src/EditorFeatures/Core.Wpf/InheritanceMargin/MarginGlyph/TargetMenuItemViewModel.cs b/src/EditorFeatures/Core.Wpf/InheritanceMargin/MarginGlyph/TargetMenuItemViewModel.cs
new file mode 100644
index 0000000000000..cd8afaf5e9f46
--- /dev/null
+++ b/src/EditorFeatures/Core.Wpf/InheritanceMargin/MarginGlyph/TargetMenuItemViewModel.cs
@@ -0,0 +1,39 @@
+// 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.
+
+using Microsoft.CodeAnalysis.Editor.Wpf;
+using Microsoft.CodeAnalysis.FindUsages;
+using Microsoft.CodeAnalysis.InheritanceMargin;
+using Microsoft.VisualStudio.Imaging.Interop;
+
+namespace Microsoft.CodeAnalysis.Editor.InheritanceMargin.MarginGlyph
+{
+ ///
+ /// View model used to show the MenuItem for inheritance target.
+ ///
+ internal class TargetMenuItemViewModel : InheritanceContextMenuItemViewModel
+ {
+ ///
+ /// DefinitionItem used for navigation.
+ ///
+ public DefinitionItem DefinitionItem { get; }
+
+ private TargetMenuItemViewModel(
+ string displayContent,
+ ImageMoniker imageMoniker,
+ string automationName,
+ DefinitionItem definitionItem) : base(displayContent, imageMoniker, automationName)
+ {
+ DefinitionItem = definitionItem;
+ }
+
+ public static TargetMenuItemViewModel Create(InheritanceTargetItem target)
+ {
+ var targetName = target.DefinitionItem.DisplayParts.JoinText();
+ var displayContent = string.Format(EditorFeaturesWpfResources._0_in_1, targetName, target.DisplayNameForContainingType);
+ var imageMoniker = target.Glyph.GetImageMoniker();
+ return new TargetMenuItemViewModel(displayContent, imageMoniker, displayContent, target.DefinitionItem);
+ }
+ }
+}
diff --git a/src/EditorFeatures/Core.Wpf/Microsoft.CodeAnalysis.EditorFeatures.Wpf.csproj b/src/EditorFeatures/Core.Wpf/Microsoft.CodeAnalysis.EditorFeatures.Wpf.csproj
index 20b9352283106..56562e9e18f9a 100644
--- a/src/EditorFeatures/Core.Wpf/Microsoft.CodeAnalysis.EditorFeatures.Wpf.csproj
+++ b/src/EditorFeatures/Core.Wpf/Microsoft.CodeAnalysis.EditorFeatures.Wpf.csproj
@@ -37,6 +37,7 @@
+
diff --git a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.cs.xlf b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.cs.xlf
index 91c72a02780df..f8ad04a1a9a80 100644
--- a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.cs.xlf
+++ b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.cs.xlf
@@ -27,6 +27,21 @@
Interactive host process platform
+
+
+ Multiple members are inherited
+
+
+
+
+ Multiple members are inherited on line {0}
+
+
+
+
+ Navigate to '{0}'
+
+
Print a list of referenced assemblies.
@@ -107,6 +122,16 @@
The references command is not supported in this Interactive Window implementation.
+
+
+ '{0}' in '{1}'
+
+
+
+
+ is inherited
+
+