Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inheritance Margin UI #52145

Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
0d74257
Add clickable margin
Cosifne Mar 22, 2021
df3d720
Initial commit
Cosifne Mar 23, 2021
6af1508
pops up correctly for single member case
Cosifne Mar 24, 2021
fe32999
Display correctly for multiple members
Cosifne Mar 25, 2021
ab48a80
Set up the navigation function with no cancallationToken
Cosifne Mar 25, 2021
4aa8e71
Add a few comments
Cosifne Mar 25, 2021
fe97af3
Use wait indicator to do navigation
Cosifne Mar 25, 2021
685d588
Extract all the string to resources file
Cosifne Mar 25, 2021
bcc29c2
Remove unused scrollviewer
Cosifne Mar 25, 2021
6a13e33
Update moniker
Cosifne Mar 26, 2021
3b3ccf0
Add style from editor
Cosifne Mar 26, 2021
3505dc6
Add comments, change the order of glyphfactory
Cosifne Mar 26, 2021
f5e21ba
Remove tooltip
Cosifne Mar 26, 2021
f6fded1
Remove the tooltip and only reference workspace instead of document
Cosifne Mar 26, 2021
2532d12
Change the display content, moniker and add containing information fo…
Cosifne Mar 29, 2021
9a5be86
Renaming
Cosifne Mar 29, 2021
8da7e11
Fix the mistake in resource
Cosifne Mar 29, 2021
b54140c
Add a check for global namespace
Cosifne Mar 29, 2021
dd2087b
Handle the global namespace change in language service
Cosifne Mar 29, 2021
d48adbe
Remove the unnatural factories in the margin
Cosifne Mar 30, 2021
078f3b8
Add a color change effect when the margin is selected
Cosifne Mar 30, 2021
a3f339f
Refactor the code and add color effect
Cosifne Mar 31, 2021
00448d9
Cleaning the code
Cosifne Mar 31, 2021
05695ac
Add telemetry
Cosifne Mar 31, 2021
71bdbbb
Fix the temp enum change
Cosifne Mar 31, 2021
e26d93f
Change a unneeded classfied definition
Cosifne Mar 31, 2021
7e05ff5
Fix accessiblity issue
Cosifne Apr 1, 2021
8c5abe6
Merge branch 'dev/shech/inheritanceMarginUI' of https://github.com/Co…
Cosifne Apr 1, 2021
2d4edce
Simplify the style
Cosifne Apr 1, 2021
931d0c8
Add tooltip font brush to let it display correctly in dark mode
Cosifne Apr 1, 2021
b6d75a1
Use scrollviewer key from vsshell
Cosifne Apr 1, 2021
301a64e
Correct the log
Cosifne Apr 1, 2021
7f16273
Fix format
Cosifne Apr 1, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/EditorFeatures/Core.Wpf/EditorFeaturesWpfResources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -180,4 +180,13 @@
<data name="Interactive_host_process_platform" xml:space="preserve">
<value>Interactive host process platform</value>
</data>
<data name="Click_to_select_member" xml:space="preserve">
<value>Click to select member</value>
</data>
Cosifne marked this conversation as resolved.
Show resolved Hide resolved
<data name="Click_to_view_all_inheritance_targets_for_0" xml:space="preserve">
<value>Click to view all inheritance targets for '{0}'</value>
</data>
<data name="Navigate_to_0" xml:space="preserve">
<value>Navigate to '{0}'</value>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,61 @@
// See the LICENSE file in the project root for more information.

using System.Windows;
using Microsoft.CodeAnalysis.Editor.Host;
using Microsoft.CodeAnalysis.Editor.InheritanceMargin.MarginGlyph;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
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 IWaitIndicator _waitIndicator;

public InheritanceGlyphFactory(
IThreadingContext threadingContext,
IStreamingFindUsagesPresenter streamingFindUsagesPresenter,
IWaitIndicator waitIndicator)
{
_threadingContext = threadingContext;
_streamingFindUsagesPresenter = streamingFindUsagesPresenter;
_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);

var solution = inheritanceMarginTag.Document.Project.Solution;
if (membersOnLine.Length == 1)
{
var viewModel = new SingleMemberMarginViewModel(inheritanceMarginTag);
return MarginGlyph.InheritanceMargin.CreateForSingleMember(
_threadingContext,
_streamingFindUsagesPresenter,
_waitIndicator,
solution,
viewModel);
}
else
{
var viewModel = new MultipleMembersMarginViewModel(inheritanceMarginTag);
return MarginGlyph.InheritanceMargin.CreateForMultipleMembers(
_threadingContext,
_streamingFindUsagesPresenter,
_waitIndicator,
solution,
viewModel);
}
}

return null;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

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.Editor;
using Microsoft.VisualStudio.Text.Tagging;
Expand All @@ -16,18 +18,28 @@ namespace Microsoft.CodeAnalysis.Editor.InheritanceMargin
[ContentType(ContentTypeNames.CSharpContentType)]
[ContentType(ContentTypeNames.VisualBasicContentType)]
[TagType(typeof(InheritanceMarginTag))]
[Order]
[Order(After = "VsTextMarker")]
Cosifne marked this conversation as resolved.
Show resolved Hide resolved
internal class InheritanceGlyphFactoryProvider : IGlyphFactoryProvider
{
private readonly IThreadingContext _threadingContext;
private readonly IStreamingFindUsagesPresenter _streamingFindUsagesPresenter;
private readonly IWaitIndicator _waitIndicator;

[ImportingConstructor]
[Obsolete(MefConstruction.ImportingConstructorMessage, error: true)]
public InheritanceGlyphFactoryProvider()
public InheritanceGlyphFactoryProvider(
IThreadingContext threadingContext,
IStreamingFindUsagesPresenter streamingFindUsagesPresenter,
IWaitIndicator waitIndicator)
{
_threadingContext = threadingContext;
_streamingFindUsagesPresenter = streamingFindUsagesPresenter;
_waitIndicator = waitIndicator;
}

public IGlyphFactory GetGlyphFactory(IWpfTextView view, IWpfTextViewMargin margin)
{
return new InheritanceGlyphFactory();
return new InheritanceGlyphFactory(_threadingContext, _streamingFindUsagesPresenter, _waitIndicator);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// 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.InheritanceMargin;
using Microsoft.VisualStudio.Imaging;
using Microsoft.VisualStudio.Imaging.Interop;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.Editor.InheritanceMargin
{
internal static class InheritanceMarginHelpers
{
/// <summary>
/// Decide which moniker should be shown.
/// </summary>
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))
{
// TODO: Change this to the updated image moniker until VS get updated
return KnownMonikers.Overriding;
}

if (inheritanceRelationship.HasFlag(InheritanceRelationship.ImplementingOverridden))
{
// TODO: Change this to the updated image moniker until VS get updated
return KnownMonikers.Overridden;
}

// 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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -18,7 +16,7 @@ internal class InheritanceMarginTag : IGlyphTag
/// <summary>
/// Margin moniker.
/// </summary>
public readonly ImageMoniker Moniker;
public ImageMoniker Moniker { get; }

/// <summary>
/// Members needs to be shown on this line. There might be multiple members.
Expand All @@ -28,10 +26,13 @@ internal class InheritanceMarginTag : IGlyphTag
/// </summary>
public readonly ImmutableArray<InheritanceMarginItem> MembersOnLine;

public InheritanceMarginTag(ImmutableArray<InheritanceMarginItem> membersOnLine)
public readonly Document Document;
Cosifne marked this conversation as resolved.
Show resolved Hide resolved

public InheritanceMarginTag(Document document, ImmutableArray<InheritanceMarginItem> membersOnLine)
{
Contract.ThrowIfTrue(membersOnLine.IsEmpty);

Document = document;
MembersOnLine = membersOnLine;
// The common case, one line has one member, avoid to use select & aggregate
if (membersOnLine.Length == 1)
Expand All @@ -44,57 +45,16 @@ public InheritanceMarginTag(ImmutableArray<InheritanceMarginItem> membersOnLine)
relationship |= target.RelationToMember;
}

Moniker = GetMoniker(relationship);
Moniker = InheritanceMarginHelpers.GetMoniker(relationship);
}
else
{
// Multiple members on same line.
var aggregateRelationship = membersOnLine
.SelectMany(member => member.TargetItems.Select(target => target.RelationToMember))
.Aggregate((r1, r2) => r1 | r2);
Moniker = GetMoniker(aggregateRelationship);
}
}

/// <summary>
/// Decide which moniker should be shown.
/// </summary>
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;
}

// 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;
Moniker = InheritanceMarginHelpers.GetMoniker(aggregateRelationship);
}

// The relationship is None. Don't know what image should be shown, throws
throw ExceptionUtilities.UnexpectedValue(inheritanceRelationship);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ protected override async Task ProduceTagsAsync(
// We only care about the line, so just tag the start.
context.AddTag(new TagSpan<InheritanceMarginTag>(
new SnapshotSpan(snapshot, line.Start, length: 0),
new InheritanceMarginTag(membersOnTheLineArray)));
new InheritanceMarginTag(document, membersOnTheLineArray)));
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<Button x:Class="Microsoft.CodeAnalysis.Editor.InheritanceMargin.MarginGlyph.InheritanceMargin"
x:ClassModifier="internal"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:imaging="clr-namespace:Microsoft.VisualStudio.Imaging;assembly=Microsoft.VisualStudio.Imaging"
xmlns:vsshell="clr-namespace:Microsoft.VisualStudio.Shell;assembly=Microsoft.VisualStudio.Shell.15.0"
xmlns:vsui="clr-namespace:Microsoft.VisualStudio.PlatformUI;assembly=Microsoft.VisualStudio.Shell.15.0"
ToolTip="{Binding ToolTip}"
AutomationProperties.Name="{Binding AutomationName}"
Click="Margin_OnClick">

<Button.Resources>
<!-- <Style TargetType="ScrollViewer" BasedOn="{StaticResource {x:Static vsshell:VsResourceKeys.ScrollViewerStyleKey}}" /> -->
<Style TargetType="ScrollViewer">
<Setter Property="Background" Value="Blue"/>
</Style>

<Style TargetType="MenuItem">
<Setter Property="Foreground" Value="{DynamicResource {x:Static vsui:EnvironmentColors.CommandBarTextActiveBrushKey}}"/>
<Setter Property="Background" Value="Transparent"/>
</Style>

<!-- The image shown in the menu item, mark this is non-shared to make sure each menu item has its own image -->
<imaging:CrispImage x:Key="NonSharedIcon" x:Shared="False" Moniker="{Binding ImageMoniker}"/>

<!-- Style used to display a single target menu item -->
<Style x:Key="TargetMenuItemStyle" TargetType="{x:Type MenuItem}">
<Setter Property="Icon" Value="{StaticResource NonSharedIcon}"/>
<Setter Property="Header" Value="{Binding DisplayName}"/>
<Setter Property="ToolTip" Value="{Binding ToolTip}"/>
<Setter Property="AutomationProperties.Name" Value="{Binding AutomationName}"/>
<Setter Property="IsCheckable" Value="False"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static vsui:EnvironmentColors.CommandBarTextActiveBrushKey}}"/>
<Setter Property="Background" Value="Transparent"/>
<EventSetter Event="Click" Handler="TargetMenuItem_OnClick"/>
</Style>

<!-- The key is referenced from the code behind -->
<!-- Style used to display a single member's information, if click the button, it pops up a menu contains targets' information (one level menu)-->
<Style x:Key="SingleMemberContextMenuStyle" TargetType="{x:Type ContextMenu}">
<Setter Property="ItemContainerStyle" Value="{StaticResource TargetMenuItemStyle}"/>
<Setter Property="ItemsSource" Value="{Binding Targets}"/>
<Setter Property="StaysOpen" Value="True"/>
</Style>

<!-- Style used to display one member's information and pops up a submenu show all the targets -->
<Style x:Key="MemberMenuItemStyle" TargetType="{x:Type MenuItem}">
<Setter Property="Icon" Value="{StaticResource NonSharedIcon}"/>
<Setter Property="Header" Value="{Binding DisplayName}"/>
<Setter Property="ToolTip" Value="{Binding ToolTip}"/>
<Setter Property="ItemsSource" Value="{Binding Targets}"/>
<Setter Property="ItemContainerStyle" Value="{StaticResource TargetMenuItemStyle}"/>
<Setter Property="IsCheckable" Value="False"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static vsui:EnvironmentColors.CommandBarTextActiveBrushKey}}"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="AutomationProperties.Name" Value="{Binding AutomationName}"/>
</Style>

<!-- The key is referenced from the code behind -->
<!-- Style to show multiple members, when click the button it first pops a menu for all the members on this line (first level menu),
then each member has a second level of context menu, show its targerts -->
<Style x:Key="MultipleMembersContextMenuStyle" TargetType="ContextMenu">
<Setter Property="ItemContainerStyle" Value="{StaticResource MemberMenuItemStyle}"/>
Cosifne marked this conversation as resolved.
Show resolved Hide resolved
<Setter Property="ItemsSource" Value="{Binding Members}"/>
<Setter Property="StaysOpen" Value="True"/>
</Style>

</Button.Resources>

<!-- Control template only shows the image -->
<Button.Template>
<ControlTemplate>
<imaging:CrispImage Moniker="{Binding Path=ImageMoniker}"/>
</ControlTemplate>
</Button.Template>

<Button.ContextMenu>
<!-- Ensure the context menu is not null -->
<ContextMenu/>
</Button.ContextMenu>
</Button>
Cosifne marked this conversation as resolved.
Show resolved Hide resolved

Loading