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 + + + + Multiple members are inherited on line {0} + Multiple members are inherited on line {0} + + + + Navigate to '{0}' + Navigate to '{0}' + + Print a list of referenced assemblies. Print a list of referenced assemblies. @@ -107,6 +122,16 @@ The references command is not supported in this Interactive Window implementation. + + '{0}' in '{1}' + '{0}' in '{1}' + + + + is inherited + is inherited + + \ No newline at end of file diff --git a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.de.xlf b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.de.xlf index f8e81d2bcb068..a1c2c4d2fd677 100644 --- a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.de.xlf +++ b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.de.xlf @@ -27,6 +27,21 @@ Interactive host process platform + + Multiple members are inherited + Multiple members are inherited + + + + Multiple members are inherited on line {0} + Multiple members are inherited on line {0} + + + + Navigate to '{0}' + Navigate to '{0}' + + Print a list of referenced assemblies. Print a list of referenced assemblies. @@ -107,6 +122,16 @@ The references command is not supported in this Interactive Window implementation. + + '{0}' in '{1}' + '{0}' in '{1}' + + + + is inherited + is inherited + + \ No newline at end of file diff --git a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.es.xlf b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.es.xlf index 1737d10f2bea1..817709fc02b32 100644 --- a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.es.xlf +++ b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.es.xlf @@ -27,6 +27,21 @@ Interactive host process platform + + Multiple members are inherited + Multiple members are inherited + + + + Multiple members are inherited on line {0} + Multiple members are inherited on line {0} + + + + Navigate to '{0}' + Navigate to '{0}' + + Print a list of referenced assemblies. Print a list of referenced assemblies. @@ -107,6 +122,16 @@ The references command is not supported in this Interactive Window implementation. + + '{0}' in '{1}' + '{0}' in '{1}' + + + + is inherited + is inherited + + \ No newline at end of file diff --git a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.fr.xlf b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.fr.xlf index ae09f21dbfc2d..94038f234a633 100644 --- a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.fr.xlf +++ b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.fr.xlf @@ -27,6 +27,21 @@ Interactive host process platform + + Multiple members are inherited + Multiple members are inherited + + + + Multiple members are inherited on line {0} + Multiple members are inherited on line {0} + + + + Navigate to '{0}' + Navigate to '{0}' + + Print a list of referenced assemblies. Print a list of referenced assemblies. @@ -107,6 +122,16 @@ The references command is not supported in this Interactive Window implementation. + + '{0}' in '{1}' + '{0}' in '{1}' + + + + is inherited + is inherited + + \ No newline at end of file diff --git a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.it.xlf b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.it.xlf index fedb5db83797a..050ad1307baee 100644 --- a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.it.xlf +++ b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.it.xlf @@ -27,6 +27,21 @@ Interactive host process platform + + Multiple members are inherited + Multiple members are inherited + + + + Multiple members are inherited on line {0} + Multiple members are inherited on line {0} + + + + Navigate to '{0}' + Navigate to '{0}' + + Print a list of referenced assemblies. Print a list of referenced assemblies. @@ -107,6 +122,16 @@ The references command is not supported in this Interactive Window implementation. + + '{0}' in '{1}' + '{0}' in '{1}' + + + + is inherited + is inherited + + \ No newline at end of file diff --git a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.ja.xlf b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.ja.xlf index 7383d2728b5ba..fd9ee0b7eba43 100644 --- a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.ja.xlf +++ b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.ja.xlf @@ -27,6 +27,21 @@ Interactive host process platform + + Multiple members are inherited + Multiple members are inherited + + + + Multiple members are inherited on line {0} + Multiple members are inherited on line {0} + + + + Navigate to '{0}' + Navigate to '{0}' + + Print a list of referenced assemblies. Print a list of referenced assemblies. @@ -107,6 +122,16 @@ The references command is not supported in this Interactive Window implementation. + + '{0}' in '{1}' + '{0}' in '{1}' + + + + is inherited + is inherited + + \ No newline at end of file diff --git a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.ko.xlf b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.ko.xlf index 64e576357f119..b384f1d2384e7 100644 --- a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.ko.xlf +++ b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.ko.xlf @@ -27,6 +27,21 @@ Interactive host process platform + + Multiple members are inherited + Multiple members are inherited + + + + Multiple members are inherited on line {0} + Multiple members are inherited on line {0} + + + + Navigate to '{0}' + Navigate to '{0}' + + Print a list of referenced assemblies. Print a list of referenced assemblies. @@ -107,6 +122,16 @@ The references command is not supported in this Interactive Window implementation. + + '{0}' in '{1}' + '{0}' in '{1}' + + + + is inherited + is inherited + + \ No newline at end of file diff --git a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.pl.xlf b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.pl.xlf index 1b418bf47d74b..6ffa7386ee365 100644 --- a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.pl.xlf +++ b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.pl.xlf @@ -27,6 +27,21 @@ Interactive host process platform + + Multiple members are inherited + Multiple members are inherited + + + + Multiple members are inherited on line {0} + Multiple members are inherited on line {0} + + + + Navigate to '{0}' + Navigate to '{0}' + + Print a list of referenced assemblies. Print a list of referenced assemblies. @@ -107,6 +122,16 @@ The references command is not supported in this Interactive Window implementation. + + '{0}' in '{1}' + '{0}' in '{1}' + + + + is inherited + is inherited + + \ No newline at end of file diff --git a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.pt-BR.xlf b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.pt-BR.xlf index 889e6d6bce5f2..c799c64adf608 100644 --- a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.pt-BR.xlf +++ b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.pt-BR.xlf @@ -27,6 +27,21 @@ Interactive host process platform + + Multiple members are inherited + Multiple members are inherited + + + + Multiple members are inherited on line {0} + Multiple members are inherited on line {0} + + + + Navigate to '{0}' + Navigate to '{0}' + + Print a list of referenced assemblies. Print a list of referenced assemblies. @@ -107,6 +122,16 @@ The references command is not supported in this Interactive Window implementation. + + '{0}' in '{1}' + '{0}' in '{1}' + + + + is inherited + is inherited + + \ No newline at end of file diff --git a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.ru.xlf b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.ru.xlf index fb3b587951dee..beb35b9f60ab1 100644 --- a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.ru.xlf +++ b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.ru.xlf @@ -27,6 +27,21 @@ Interactive host process platform + + Multiple members are inherited + Multiple members are inherited + + + + Multiple members are inherited on line {0} + Multiple members are inherited on line {0} + + + + Navigate to '{0}' + Navigate to '{0}' + + Print a list of referenced assemblies. Print a list of referenced assemblies. @@ -107,6 +122,16 @@ The references command is not supported in this Interactive Window implementation. + + '{0}' in '{1}' + '{0}' in '{1}' + + + + is inherited + is inherited + + \ No newline at end of file diff --git a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.tr.xlf b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.tr.xlf index 23c20126ebed1..3e4c2545c7104 100644 --- a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.tr.xlf +++ b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.tr.xlf @@ -27,6 +27,21 @@ Interactive host process platform + + Multiple members are inherited + Multiple members are inherited + + + + Multiple members are inherited on line {0} + Multiple members are inherited on line {0} + + + + Navigate to '{0}' + Navigate to '{0}' + + Print a list of referenced assemblies. Print a list of referenced assemblies. @@ -107,6 +122,16 @@ The references command is not supported in this Interactive Window implementation. + + '{0}' in '{1}' + '{0}' in '{1}' + + + + is inherited + is inherited + + \ No newline at end of file diff --git a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.zh-Hans.xlf b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.zh-Hans.xlf index 17bc080fd4c95..a7d324500e4c3 100644 --- a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.zh-Hans.xlf +++ b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.zh-Hans.xlf @@ -27,6 +27,21 @@ Interactive host process platform + + Multiple members are inherited + Multiple members are inherited + + + + Multiple members are inherited on line {0} + Multiple members are inherited on line {0} + + + + Navigate to '{0}' + Navigate to '{0}' + + Print a list of referenced assemblies. Print a list of referenced assemblies. @@ -107,6 +122,16 @@ The references command is not supported in this Interactive Window implementation. + + '{0}' in '{1}' + '{0}' in '{1}' + + + + is inherited + is inherited + + \ No newline at end of file diff --git a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.zh-Hant.xlf b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.zh-Hant.xlf index 46c234a96d311..c2c62845e627e 100644 --- a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.zh-Hant.xlf +++ b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.zh-Hant.xlf @@ -27,6 +27,21 @@ Interactive host process platform + + Multiple members are inherited + Multiple members are inherited + + + + Multiple members are inherited on line {0} + Multiple members are inherited on line {0} + + + + Navigate to '{0}' + Navigate to '{0}' + + Print a list of referenced assemblies. Print a list of referenced assemblies. @@ -107,6 +122,16 @@ The references command is not supported in this Interactive Window implementation. + + '{0}' in '{1}' + '{0}' in '{1}' + + + + is inherited + is inherited + + \ No newline at end of file diff --git a/src/EditorFeatures/Core/GoToDefinition/GoToDefinitionHelpers.cs b/src/EditorFeatures/Core/GoToDefinition/GoToDefinitionHelpers.cs index 12b3ad3078122..b8e4cbd407538 100644 --- a/src/EditorFeatures/Core/GoToDefinition/GoToDefinitionHelpers.cs +++ b/src/EditorFeatures/Core/GoToDefinition/GoToDefinitionHelpers.cs @@ -75,11 +75,11 @@ public static ImmutableArray GetDefinitions( // // Passing along the classified information is valuable for OOP scenarios where we want // all that expensive computation done on the OOP side and not in the VS side. - // + // // However, Go To Definition is all in-process, and is also synchronous. So we do not - // want to fetch the classifications here. It slows down the command and leads to a + // want to fetch the classifications here. It slows down the command and leads to a // measurable delay in our perf tests. - // + // // So, if we only have a single location to go to, this does no unnecessary work. And, // if we do have multiple locations to show, it will just be done in the BG, unblocking // this command thread so it can return the user faster. @@ -129,5 +129,21 @@ public static bool TryGoToDefinition( streamingPresenter.TryNavigateToOrPresentItemsAsync( threadingContext, solution.Workspace, title, definitions, cancellationToken)); } + + public static bool TryGoToDefinition( + ImmutableArray definitions, + Workspace workspace, + string title, + IThreadingContext threadingContext, + IStreamingFindUsagesPresenter streamingPresenter, + CancellationToken cancellationToken) + { + if (definitions.IsDefaultOrEmpty) + return false; + + return threadingContext.JoinableTaskFactory.Run(() => + streamingPresenter.TryNavigateToOrPresentItemsAsync( + threadingContext, workspace, title, definitions, cancellationToken)); + } } } diff --git a/src/EditorFeatures/Core/InheritanceMargin/AbstractInheritanceMarginService.cs b/src/EditorFeatures/Core/InheritanceMargin/AbstractInheritanceMarginService.cs index b58f81d87d6a3..e0601493e0c16 100644 --- a/src/EditorFeatures/Core/InheritanceMargin/AbstractInheritanceMarginService.cs +++ b/src/EditorFeatures/Core/InheritanceMargin/AbstractInheritanceMarginService.cs @@ -169,16 +169,5 @@ private async Task AddInheritanceMemberItemsForTypeMembersAsync( builder.AddIfNotNull(item); } } - - /// - /// A set of widely used TypeSymbols that we don't want to show in margin - /// - private static bool IsUnwantedBaseType(ITypeSymbol symbol) - { - var specialType = symbol.SpecialType; - return specialType == SpecialType.System_Object - || specialType == SpecialType.System_ValueType - || specialType == SpecialType.System_Enum; - } } } diff --git a/src/EditorFeatures/Core/InheritanceMargin/AbstractInheritanceMarginService_Helpers.cs b/src/EditorFeatures/Core/InheritanceMargin/AbstractInheritanceMarginService_Helpers.cs index 70aaa051320c0..a1368cac849a9 100644 --- a/src/EditorFeatures/Core/InheritanceMargin/AbstractInheritanceMarginService_Helpers.cs +++ b/src/EditorFeatures/Core/InheritanceMargin/AbstractInheritanceMarginService_Helpers.cs @@ -9,7 +9,6 @@ using Microsoft.CodeAnalysis.FindSymbols; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.InheritanceMargin { @@ -31,9 +30,16 @@ private static async Task CreateInheritanceMemberItemAsyn .SelectAsArrayAsync(symbol => CreateInheritanceItemAsync(solution, symbol, InheritanceRelationship.Implemented, cancellationToken)) .ConfigureAwait(false); + var definitionItem = await memberSymbol.ToClassifiedDefinitionItemAsync( + solution, + isPrimary: true, + includeHiddenLocations: false, + FindReferencesSearchOptions.Default, + cancellationToken: cancellationToken).ConfigureAwait(false); + return new InheritanceMarginItem( lineNumber, - memberSymbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat), + definitionItem.DisplayParts, memberSymbol.GetGlyph(), baseSymbolItems.Concat(derivedTypeItems)); } @@ -44,15 +50,22 @@ private static async ValueTask CreateInheritanceItemAsync InheritanceRelationship inheritanceRelationship, CancellationToken cancellationToken) { - // Use non-classified currently because there is no good way to show - // colorized item in margin. - // Would like to switch to ToClassifiedDefinitionItemAsync() in the future + // Right now the targets are not shown in a classified way. var definition = await targetSymbol.ToNonClassifiedDefinitionItemAsync( solution, includeHiddenLocations: false, cancellationToken: cancellationToken).ConfigureAwait(false); - return new InheritanceTargetItem(inheritanceRelationship, definition); + var containingSymbol = targetSymbol.ContainingSymbol; + var containingSymbolName = containingSymbol is INamespaceSymbol { IsGlobalNamespace: true } + ? FeaturesResources.Global_Namespace + : containingSymbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat); + + return new InheritanceTargetItem( + inheritanceRelationship, + definition, + targetSymbol.GetGlyph(), + containingSymbolName); } private static async Task CreateInheritanceMemberInfoForMemberAsync( @@ -74,9 +87,16 @@ private static async Task CreateInheritanceMemberInfoForM var overridingMemberItems = await overridingMembers .SelectAsArrayAsync(symbol => CreateInheritanceItemAsync(solution, symbol, InheritanceRelationship.Overriding, cancellationToken)).ConfigureAwait(false); + var definitionItem = await memberSymbol.ToClassifiedDefinitionItemAsync( + solution, + isPrimary: true, + includeHiddenLocations: false, + FindReferencesSearchOptions.Default, + cancellationToken: cancellationToken).ConfigureAwait(false); + return new InheritanceMarginItem( lineNumber, - memberSymbol.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat), + definitionItem.DisplayParts, memberSymbol.GetGlyph(), implementingMemberItems.Concat(implementedMemberItems) .Concat(overridenMemberItems) diff --git a/src/EditorFeatures/Core/InheritanceMargin/IInheritanceMarginService.cs b/src/EditorFeatures/Core/InheritanceMargin/IInheritanceMarginService.cs index 62a9bbb638130..123b71ecf7aad 100644 --- a/src/EditorFeatures/Core/InheritanceMargin/IInheritanceMarginService.cs +++ b/src/EditorFeatures/Core/InheritanceMargin/IInheritanceMarginService.cs @@ -7,7 +7,6 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Text; -using Microsoft.VisualStudio.Text; namespace Microsoft.CodeAnalysis.InheritanceMargin { diff --git a/src/EditorFeatures/Core/InheritanceMargin/InheritanceMarginItem.cs b/src/EditorFeatures/Core/InheritanceMargin/InheritanceMarginItem.cs index 352c7e52845da..fa87b18869e68 100644 --- a/src/EditorFeatures/Core/InheritanceMargin/InheritanceMarginItem.cs +++ b/src/EditorFeatures/Core/InheritanceMargin/InheritanceMarginItem.cs @@ -14,9 +14,9 @@ internal readonly struct InheritanceMarginItem public readonly int LineNumber; /// - /// Member's display name. + /// Display texts for this member. /// - public readonly string DisplayName; + public readonly ImmutableArray DisplayTexts; /// /// Member's glyph. @@ -30,12 +30,12 @@ internal readonly struct InheritanceMarginItem public InheritanceMarginItem( int lineNumber, - string displayName, + ImmutableArray displayTexts, Glyph glyph, ImmutableArray targetItems) { LineNumber = lineNumber; - DisplayName = displayName; + DisplayTexts = displayTexts; Glyph = glyph; TargetItems = targetItems; } diff --git a/src/EditorFeatures/Core/InheritanceMargin/InheritanceTargetItem.cs b/src/EditorFeatures/Core/InheritanceMargin/InheritanceTargetItem.cs index eaae6a8a9f616..07945bce99bfd 100644 --- a/src/EditorFeatures/Core/InheritanceMargin/InheritanceTargetItem.cs +++ b/src/EditorFeatures/Core/InheritanceMargin/InheritanceTargetItem.cs @@ -2,8 +2,6 @@ // 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; -using System.Collections.Immutable; using Microsoft.CodeAnalysis.FindUsages; namespace Microsoft.CodeAnalysis.InheritanceMargin @@ -23,12 +21,26 @@ internal readonly struct InheritanceTargetItem /// public readonly DefinitionItem DefinitionItem; + /// + /// The glyph for this target. + /// + public readonly Glyph Glyph; + + /// + /// The name for containing type of this target. Empty if it is global namespace. + /// + public readonly string DisplayNameForContainingType; + public InheritanceTargetItem( InheritanceRelationship relationToMember, - DefinitionItem definitionItem) + DefinitionItem definitionItem, + Glyph glyph, + string displayNameForContainingType) { RelationToMember = relationToMember; DefinitionItem = definitionItem; + Glyph = glyph; + DisplayNameForContainingType = displayNameForContainingType; } } } diff --git a/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs b/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs index 063c8153c18c1..5c2dc0b9b8b99 100644 --- a/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs +++ b/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs @@ -77,7 +77,7 @@ private static async Task VerifyInSingleDocumentAsync( private static void VerifyInheritanceMember(TestWorkspace testWorkspace, TestInheritanceMemberItem expectedItem, InheritanceMarginItem actualItem) { Assert.Equal(expectedItem.LineNumber, actualItem.LineNumber); - Assert.Equal(expectedItem.MemberName, actualItem.DisplayName); + Assert.Equal(expectedItem.MemberName, actualItem.DisplayTexts.JoinText()); Assert.Equal(expectedItem.Targets.Length, actualItem.TargetItems.Length); var expectedTargets = expectedItem.Targets .SelectAsArray(info => TestInheritanceTargetItem.Create(info, testWorkspace)); @@ -231,7 +231,7 @@ public class {|target2:Bar|} : IBar var itemOnLine2 = new TestInheritanceMemberItem( lineNumber: 2, - memberName: "IBar", + memberName: "interface IBar", targets: ImmutableArray.Create(new TargetInfo( targetSymbolDisplayName: "class Bar", locationTag: "target2", @@ -239,7 +239,7 @@ public class {|target2:Bar|} : IBar var itemOnLine3 = new TestInheritanceMemberItem( lineNumber: 3, - memberName: "Bar", + memberName: "class Bar", targets: ImmutableArray.Create(new TargetInfo( targetSymbolDisplayName: "interface IBar", locationTag: "target1", @@ -262,7 +262,7 @@ interface {|target2:IBar2|} : IBar { } var itemOnLine2 = new TestInheritanceMemberItem( lineNumber: 2, - memberName: "IBar", + memberName: "interface IBar", targets: ImmutableArray.Create(new TargetInfo( targetSymbolDisplayName: "interface IBar2", locationTag: "target2", @@ -270,7 +270,7 @@ interface {|target2:IBar2|} : IBar { } ); var itemOnLine3 = new TestInheritanceMemberItem( lineNumber: 3, - memberName: "IBar2", + memberName: "interface IBar2", targets: ImmutableArray.Empty .Add(new TargetInfo( targetSymbolDisplayName: "interface IBar", @@ -285,7 +285,6 @@ interface {|target2:IBar2|} : IBar { } itemOnLine3); } - [Fact] public Task TestCSharpClassInheritsClass() { @@ -296,7 +295,7 @@ class {|target1:B|} : A { } var itemOnLine2 = new TestInheritanceMemberItem( lineNumber: 2, - memberName: "A", + memberName: "class A", targets: ImmutableArray.Create(new TargetInfo( targetSymbolDisplayName: "class B", locationTag: "target1", @@ -304,7 +303,7 @@ class {|target1:B|} : A { } ); var itemOnLine3 = new TestInheritanceMemberItem( lineNumber: 3, - memberName: "B", + memberName: "class B", targets: ImmutableArray.Create(new TargetInfo( targetSymbolDisplayName: "class A", locationTag: "target2", @@ -351,7 +350,7 @@ public class Bar : Bar1 LanguageNames.CSharp, new TestInheritanceMemberItem( lineNumber: 4, - memberName: "Bar", + memberName: "class Bar", targets: ImmutableArray.Create( new TargetInfo( targetSymbolDisplayName: "class Bar1", @@ -359,7 +358,6 @@ public class Bar : Bar1 relationship: InheritanceRelationship.Implementing)))); } - [Fact] public Task TestCSharpMetadataInterface() { @@ -373,7 +371,7 @@ public class Bar : IEnumerable LanguageNames.CSharp, new TestInheritanceMemberItem( lineNumber: 3, - memberName: "Bar", + memberName: "class Bar", targets: ImmutableArray.Create( new TargetInfo( targetSymbolDisplayName: "interface IEnumerable", @@ -399,7 +397,7 @@ public class {|target1:Bar|} : IBar }"; var itemForIBar = new TestInheritanceMemberItem( lineNumber: 3, - memberName: "IBar", + memberName: "interface IBar", ImmutableArray.Create(new TargetInfo( targetSymbolDisplayName: "class Bar", locationTag: "target1", @@ -407,7 +405,7 @@ public class {|target1:Bar|} : IBar var itemForBar = new TestInheritanceMemberItem( lineNumber: 7, - memberName: "Bar", + memberName: "class Bar", ImmutableArray.Create(new TargetInfo( targetSymbolDisplayName: "interface IBar", locationTag: "target2", @@ -452,7 +450,7 @@ public class {|target1:Bar|} : IBar }"; var itemForIBar = new TestInheritanceMemberItem( lineNumber: 2, - memberName: "IBar", + memberName: "interface IBar", ImmutableArray.Create(new TargetInfo( targetSymbolDisplayName: "class Bar", locationTag: "target1", @@ -460,7 +458,7 @@ public class {|target1:Bar|} : IBar var itemForBar = new TestInheritanceMemberItem( lineNumber: 6, - memberName: "Bar", + memberName: "class Bar", ImmutableArray.Create(new TargetInfo( targetSymbolDisplayName: "interface IBar", locationTag: "target2", @@ -546,7 +544,7 @@ public class {|target2:Bar|} : IBar var itemForPooInInterface = new TestInheritanceMemberItem( lineNumber: 5, - memberName: "int IBar.Poo", + memberName: "int IBar.Poo { get; set; }", targets: ImmutableArray.Create(new TargetInfo( targetSymbolDisplayName: "int Bar.Poo { get; set; }", locationTag: "target5", @@ -555,7 +553,7 @@ public class {|target2:Bar|} : IBar var itemForPooInClass = new TestInheritanceMemberItem( lineNumber: 11, - memberName: "int Bar.Poo", + memberName: "int Bar.Poo { get; set; }", targets: ImmutableArray.Create(new TargetInfo( targetSymbolDisplayName: "int IBar.Poo { get; set; }", locationTag: "target6", @@ -582,7 +580,7 @@ public class {|target2:Bar|} : IBar var itemForIBar = new TestInheritanceMemberItem( lineNumber: 2, - memberName: "IBar", + memberName: "interface IBar", targets: ImmutableArray.Create(new TargetInfo( targetSymbolDisplayName: "class Bar", locationTag: "target2", @@ -591,7 +589,7 @@ public class {|target2:Bar|} : IBar var itemForBar = new TestInheritanceMemberItem( lineNumber: 8, - memberName: "Bar", + memberName: "class Bar", targets: ImmutableArray.Create(new TargetInfo( targetSymbolDisplayName: "interface IBar", locationTag: "target1", @@ -633,7 +631,7 @@ public class {{|target1:Bar2|}} : Bar var itemForEooInClass = new TestInheritanceMemberItem( lineNumber: 12, - memberName: "event EventHandler Bar2.Eoo", + memberName: "override event EventHandler Bar2.Eoo", targets: ImmutableArray.Create(new TargetInfo( targetSymbolDisplayName: $"{modifier} event EventHandler Bar.Eoo", locationTag: "target8", @@ -641,7 +639,7 @@ public class {{|target1:Bar2|}} : Bar var itemForEooInAbstractClass = new TestInheritanceMemberItem( lineNumber: 6, - memberName: "event EventHandler Bar.Eoo", + memberName: $"{modifier} event EventHandler Bar.Eoo", targets: ImmutableArray.Create(new TargetInfo( targetSymbolDisplayName: "override event EventHandler Bar2.Eoo", locationTag: "target7", @@ -649,7 +647,7 @@ public class {{|target1:Bar2|}} : Bar var itemForPooInClass = new TestInheritanceMemberItem( lineNumber: 11, - memberName: "int Bar2.Poo", + memberName: "override int Bar2.Poo { get; set; }", targets: ImmutableArray.Create(new TargetInfo( targetSymbolDisplayName: $"{modifier} int Bar.Poo {{ get; set; }}", locationTag: "target6", @@ -657,7 +655,7 @@ public class {{|target1:Bar2|}} : Bar var itemForPooInAbstractClass = new TestInheritanceMemberItem( lineNumber: 5, - memberName: "int Bar.Poo", + memberName: $"{modifier} int Bar.Poo {{ get; set; }}", targets: ImmutableArray.Create(new TargetInfo( targetSymbolDisplayName: "override int Bar2.Poo { get; set; }", locationTag: "target5", @@ -665,7 +663,7 @@ public class {{|target1:Bar2|}} : Bar var itemForFooInAbstractClass = new TestInheritanceMemberItem( lineNumber: 4, - memberName: "void Bar.Foo()", + memberName: $"{modifier} void Bar.Foo()", targets: ImmutableArray.Create(new TargetInfo( targetSymbolDisplayName: "override void Bar2.Foo()", locationTag: "target3", @@ -673,7 +671,7 @@ public class {{|target1:Bar2|}} : Bar var itemForFooInClass = new TestInheritanceMemberItem( lineNumber: 10, - memberName: "void Bar2.Foo()", + memberName: "override void Bar2.Foo()", targets: ImmutableArray.Create(new TargetInfo( targetSymbolDisplayName: $"{modifier} void Bar.Foo()", locationTag: "target4", @@ -681,7 +679,7 @@ public class {{|target1:Bar2|}} : Bar var itemForBar = new TestInheritanceMemberItem( lineNumber: 2, - memberName: "Bar", + memberName: "class Bar", targets: ImmutableArray.Create(new TargetInfo( targetSymbolDisplayName: "class Bar2", locationTag: "target1", @@ -689,7 +687,7 @@ public class {{|target1:Bar2|}} : Bar var itemForBar2 = new TestInheritanceMemberItem( lineNumber: 8, - memberName: "Bar2", + memberName: "class Bar2", targets: ImmutableArray.Create(new TargetInfo( targetSymbolDisplayName: "class Bar", locationTag: "target2", @@ -723,7 +721,7 @@ Implements IBar End Class"; var itemForIBar = new TestInheritanceMemberItem( lineNumber: 2, - memberName: "IBar", + memberName: "Interface IBar", ImmutableArray.Create(new TargetInfo( targetSymbolDisplayName: "Class Bar", locationTag: "target1", @@ -731,7 +729,7 @@ Implements IBar var itemForBar = new TestInheritanceMemberItem( lineNumber: 4, - memberName: "Bar", + memberName: "Class Bar", ImmutableArray.Create(new TargetInfo( targetSymbolDisplayName: "Interface IBar", locationTag: "target2", @@ -756,7 +754,7 @@ Inherits IBar2 var itemForIBar2 = new TestInheritanceMemberItem( lineNumber: 2, - memberName: "IBar2", + memberName: "Interface IBar2", ImmutableArray.Create(new TargetInfo( targetSymbolDisplayName: "Interface IBar", locationTag: "target1", @@ -764,7 +762,7 @@ Inherits IBar2 var itemForIBar = new TestInheritanceMemberItem( lineNumber: 4, - memberName: "IBar", + memberName: "Interface IBar", ImmutableArray.Create(new TargetInfo( targetSymbolDisplayName: "Interface IBar2", locationTag: "target2", @@ -784,7 +782,7 @@ Inherits Bar2 var itemForBar2 = new TestInheritanceMemberItem( lineNumber: 2, - memberName: "Bar2", + memberName: "Class Bar2", ImmutableArray.Create(new TargetInfo( targetSymbolDisplayName: "Class Bar", locationTag: "target1", @@ -792,7 +790,7 @@ Inherits Bar2 var itemForBar = new TestInheritanceMemberItem( lineNumber: 4, - memberName: "Bar", + memberName: "Class Bar", ImmutableArray.Create(new TargetInfo( targetSymbolDisplayName: "Class Bar2", locationTag: "target2", @@ -827,7 +825,7 @@ Implements IEnumerable LanguageNames.VisualBasic, new TestInheritanceMemberItem( lineNumber: 3, - memberName: "Bar", + memberName: "Class Bar", targets: ImmutableArray.Create( new TargetInfo( targetSymbolDisplayName: "Interface IEnumerable", @@ -849,7 +847,7 @@ Implements IBar var itemForIBar = new TestInheritanceMemberItem( lineNumber: 2, - memberName: "IBar", + memberName: "Interface IBar", ImmutableArray.Create(new TargetInfo( targetSymbolDisplayName: "Class Bar", locationTag: "target1", @@ -857,7 +855,7 @@ Implements IBar var itemForBar = new TestInheritanceMemberItem( lineNumber: 5, - memberName: "Bar", + memberName: "Class Bar", ImmutableArray.Create(new TargetInfo( targetSymbolDisplayName: "Interface IBar", locationTag: "target2", @@ -902,7 +900,7 @@ End Event End Class"; var itemForIBar = new TestInheritanceMemberItem( lineNumber: 2, - memberName: "IBar", + memberName: "Interface IBar", ImmutableArray.Create(new TargetInfo( targetSymbolDisplayName: "Class Bar", locationTag: "target1", @@ -910,7 +908,7 @@ End Event var itemForBar = new TestInheritanceMemberItem( lineNumber: 5, - memberName: "Bar", + memberName: "Class Bar", ImmutableArray.Create(new TargetInfo( targetSymbolDisplayName: "Interface IBar", locationTag: "target2", @@ -965,7 +963,7 @@ End Function End Class"; var itemForIBar = new TestInheritanceMemberItem( lineNumber: 2, - memberName: "IBar", + memberName: "Interface IBar", targets: ImmutableArray.Create(new TargetInfo( targetSymbolDisplayName: "Class Bar", locationTag: "target1", @@ -973,7 +971,7 @@ End Function var itemForBar = new TestInheritanceMemberItem( lineNumber: 7, - memberName: "Bar", + memberName: "Class Bar", targets: ImmutableArray.Create(new TargetInfo( targetSymbolDisplayName: "Interface IBar", locationTag: "target2", @@ -1037,7 +1035,7 @@ End Sub End Class"; var itemForBar1 = new TestInheritanceMemberItem( lineNumber: 2, - memberName: "Bar1", + memberName: "Class Bar1", targets: ImmutableArray.Create(new TargetInfo( targetSymbolDisplayName: $"Class Bar", locationTag: "target1", @@ -1045,7 +1043,7 @@ End Sub var itemForBar = new TestInheritanceMemberItem( lineNumber: 6, - memberName: "Bar", + memberName: "Class Bar", targets: ImmutableArray.Create(new TargetInfo( targetSymbolDisplayName: "Class Bar1", locationTag: "target2", @@ -1053,7 +1051,7 @@ End Sub var itemForFooInBar1 = new TestInheritanceMemberItem( lineNumber: 3, - memberName: "Sub Bar1.Foo()", + memberName: "MustOverride Sub Bar1.Foo()", targets: ImmutableArray.Create(new TargetInfo( targetSymbolDisplayName: "Overrides Sub Bar.Foo()", locationTag: "target3", @@ -1061,7 +1059,7 @@ End Sub var itemForFooInBar = new TestInheritanceMemberItem( lineNumber: 8, - memberName: "Sub Bar.Foo()", + memberName: "Overrides Sub Bar.Foo()", targets: ImmutableArray.Create(new TargetInfo( targetSymbolDisplayName: "MustOverride Sub Bar1.Foo()", locationTag: "target4", diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/FunctionId.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/FunctionId.cs index 38654f12bd3f8..fdef26d255380 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/FunctionId.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Log/FunctionId.cs @@ -510,5 +510,8 @@ internal enum FunctionId LSP_RequestCounter = 482, LSP_RequestDuration = 483, LSP_TimeInQueue = 484, + + InheritanceMargin_TargetsMenuOpen = 485, + InheritanceMargin_NavigateToTarget = 486, } }