Skip to content
This repository was archived by the owner on Jun 21, 2023. It is now read-only.

[WIP] Add Pull Request filter button to Visual Studio solution explorer #1667

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
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
8 changes: 7 additions & 1 deletion src/GitHub.InlineReviews/GitHub.InlineReviews.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
<Compile Include="Glyph\GlyphMargin.cs" />
<Compile Include="Glyph\GlyphMarginVisualManager.cs" />
<Compile Include="Glyph\IGlyphFactory.cs" />
<Compile Include="PullRequestFilterProvider.cs" />
<Compile Include="Margins\InlineCommentMargin.cs" />
<Compile Include="Margins\InlineCommentMarginVisible.cs" />
<Compile Include="Margins\InlineCommentMarginEnabled.cs" />
Expand Down Expand Up @@ -140,6 +141,11 @@
<Compile Include="VisualStudioExtensions.cs" />
</ItemGroup>
<ItemGroup>
<Content Include="GitHub.InlineReviews.imagemanifest">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<IncludeInVSIX>true</IncludeInVSIX>
<SubType>Designer</SubType>
</Content>
<None Include="packages.config">
<SubType>Designer</SubType>
</None>
Expand Down Expand Up @@ -465,7 +471,7 @@
<Import Project="..\..\packages\Microsoft.VisualStudio.Threading.Analyzers.15.8.122\build\Microsoft.VisualStudio.Threading.Analyzers.targets" Condition="Exists('..\..\packages\Microsoft.VisualStudio.Threading.Analyzers.15.8.122\build\Microsoft.VisualStudio.Threading.Analyzers.targets')" />
<Import Project="..\..\packages\Microsoft.VisualStudio.SDK.Analyzers.15.8.33\build\Microsoft.VisualStudio.SDK.Analyzers.targets" Condition="Exists('..\..\packages\Microsoft.VisualStudio.SDK.Analyzers.15.8.33\build\Microsoft.VisualStudio.SDK.Analyzers.targets')" />
<Import Project="..\..\packages\Microsoft.VSSDK.BuildTools.15.8.3252\build\Microsoft.VSSDK.BuildTools.targets" Condition="Exists('..\..\packages\Microsoft.VSSDK.BuildTools.15.8.3252\build\Microsoft.VSSDK.BuildTools.targets')" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
Expand Down
17 changes: 17 additions & 0 deletions src/GitHub.InlineReviews/GitHub.InlineReviews.imagemanifest
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- This file was generated by the ManifestFromResources tool.-->
<!-- Version: 14.0.50929.2 -->
<ImageManifest xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.microsoft.com/VisualStudio/ImageManifestSchema/2014">
<Symbols>
<String Name="Resources" Value="/GitHub.VisualStudio;Component/Resources/icons" />

<Guid Name="guidGitHubInlineReviews" Value="{7b2a62fb-6aaa-4893-82c2-4895716a390c}" />
<ID Name="pullrequest" Value="1" />
</Symbols>
<Images>
<Image Guid="$(guidGitHubInlineReviews)" ID="$(pullrequest)">
<Source Uri="$(Resources)/git_pull_request.xaml" />
</Image>
</Images>
<ImageLists />
</ImageManifest>
23 changes: 16 additions & 7 deletions src/GitHub.InlineReviews/InlineReviewsPackage.vsct
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,15 @@
<LocCanonicalName>.GitHub.PreviousComment</LocCanonicalName>
</Strings>
</Button>
<Button guid="guidPullRequestFilterCommandPackageCmdSet" id="PullRequestFilterCommandId" priority="0x0400" type="Button">
<Parent guid="guidSHLMainMenu" id="IDG_VS_TOOLBAR_PROJWIN_FILTERS" />
<Icon guid="guidGitHubInlineReviews" id="pullrequest" />
<Strings>
<ButtonText>Pull Request Filter</ButtonText>
</Strings>
<CommandFlag>DefaultInvisible</CommandFlag>
<CommandFlag>DynamicVisibility</CommandFlag>
</Button>
<Button guid="guidGitHubCommandSet" id="ToggleInlineCommentMarginId" type="Button">
<CommandFlag>DefaultDisabled</CommandFlag>
<CommandFlag>DynamicVisibility</CommandFlag>
Expand Down Expand Up @@ -91,13 +100,13 @@
<IDSymbol name="ToggleInlineCommentMarginId" value="0x1003" />
</GuidSymbol>

<GuidSymbol name="guidImages" value="{775aa523-6c52-4c11-9c28-823c99d15613}" >
<IDSymbol name="bmpPic1" value="1" />
<IDSymbol name="bmpPic2" value="2" />
<IDSymbol name="bmpPicSearch" value="3" />
<IDSymbol name="bmpPicX" value="4" />
<IDSymbol name="bmpPicArrows" value="5" />
<IDSymbol name="bmpPicStrikethrough" value="6" />
<GuidSymbol name="guidPullRequestFilterCommandPackageCmdSet" value="{7cde2dfc-43c9-41ff-bf2e-bef41cd99e09}">
<IDSymbol name="MyMenuGroup" value="0x1020" />
<IDSymbol name="PullRequestFilterCommandId" value="0x0100" />
</GuidSymbol>

<GuidSymbol name="guidGitHubInlineReviews" value="{7b2a62fb-6aaa-4893-82c2-4895716a390c}" >
<IDSymbol name="pullrequest" value="1" />
</GuidSymbol>
</Symbols>
</CommandTable>
100 changes: 100 additions & 0 deletions src/GitHub.InlineReviews/PullRequestFilterProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.IO;
using System.Threading.Tasks;
using GitHub.Services;
using GitHub.VisualStudio;
using Microsoft.Internal.VisualStudio.PlatformUI;
using Microsoft.VisualStudio.Shell;

namespace GitHub.InlineReviews
{
public static class PullRequestFilterPackageGuids
{
public const string GuidPullRequestFilterPackageCmdSetString = "7cde2dfc-43c9-41ff-bf2e-bef41cd99e09";
public const int PullRequestFilterId = 0x0100;
}

[SolutionTreeFilterProvider(PullRequestFilterPackageGuids.GuidPullRequestFilterPackageCmdSetString, PullRequestFilterPackageGuids.PullRequestFilterId)]
[Export]
public class PullRequestFilterProvider : HierarchyTreeFilterProvider
{
private readonly IVsHierarchyItemCollectionProvider hierarchyCollectionProvider;
private readonly IGitHubServiceProvider githubServiceProvider;

[ImportingConstructor]
public PullRequestFilterProvider(IVsHierarchyItemCollectionProvider hierarchyCollectionProvider, IGitHubServiceProvider githubServiceProvider)
{
this.hierarchyCollectionProvider = hierarchyCollectionProvider;
this.githubServiceProvider = githubServiceProvider;
}

protected override HierarchyTreeFilter CreateFilter()
{
return new PullRequestFilter(hierarchyCollectionProvider, githubServiceProvider);
}

private sealed class PullRequestFilter : HierarchyTreeFilter
{
private readonly IVsHierarchyItemCollectionProvider hierarchyCollectionProvider;
private readonly IGitHubServiceProvider githubServiceProvider;
private IPullRequestSessionManager sessionManager;
private HashSet<string> pullRequestSessionFiles;

public PullRequestFilter(IVsHierarchyItemCollectionProvider hierarchyCollectionProvider, IGitHubServiceProvider githubServiceProvider)
{
this.hierarchyCollectionProvider = hierarchyCollectionProvider;
this.githubServiceProvider = githubServiceProvider;
}

IPullRequestSessionManager SessionManager
{
get
{
// Lazily load the pull request session manager to prevent all of our assemblies
// being loaded on VS startup.
if (sessionManager == null)
{
sessionManager = githubServiceProvider.GetService<IPullRequestSessionManager>();
}

return sessionManager;
}
}

// Gets the items to be included from this filter provider.
// rootItems is a collection that contains the root of your solution
// Returns a collection of items to be included as part of the filter
protected override async Task<IReadOnlyObservableSet> GetIncludedItemsAsync(IEnumerable<IVsHierarchyItem> rootItems)
{
var root = HierarchyUtilities.FindCommonAncestor(rootItems);
var sourceItems = await hierarchyCollectionProvider.GetDescendantsAsync(root.HierarchyIdentity.NestedHierarchy, CancellationToken);

var vsSolution = githubServiceProvider.GetSolution();
string solutionDirectory;
string _;
vsSolution.GetSolutionInfo(out solutionDirectory, out _, out _);

this.pullRequestSessionFiles = new HashSet<string>();
if (SessionManager.CurrentSession != null)
{
var requestSessionFiles = await SessionManager.CurrentSession.GetAllFiles();
requestSessionFiles.ForEach(file => this.pullRequestSessionFiles.Add(BuildAbsolutePath(solutionDirectory, file.RelativePath)));
}

return await hierarchyCollectionProvider.GetFilteredHierarchyItemsAsync(sourceItems, ShouldIncludeInFilter, CancellationToken);
}

// Returns true if filters hierarchy item name for given filter; otherwise, false</returns>
private bool ShouldIncludeInFilter(IVsHierarchyItem hierarchyItem)
{
return hierarchyItem?.CanonicalName != null && pullRequestSessionFiles.Contains(hierarchyItem.CanonicalName.ToUpperInvariant());
}

private static string BuildAbsolutePath(string solutionDirectory, string fileRelativePath)
{
return Path.Combine(solutionDirectory, fileRelativePath.Replace("/", @"\")).ToUpperInvariant();
}
}
}
}