Skip to content
This repository has been 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 all 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
25 changes: 25 additions & 0 deletions src/GitHub.InlineReviews/GitHub.InlineReviews.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,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 @@ -122,6 +123,16 @@
</Compile>
<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>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\submodules\octokit.graphql.net\Octokit.GraphQL.Core\Octokit.GraphQL.Core.csproj">
<Project>{3321ce72-26ed-4d1e-a8f5-6901fb783007}</Project>
Expand Down Expand Up @@ -304,6 +315,20 @@
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(VSToolsPath)\VSSDK\Microsoft.VsSDK.targets" Condition="'$(VSToolsPath)' != '' And '$(NCrunch)' != '1'" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\packages\LibGit2Sharp.NativeBinaries.1.0.164\build\LibGit2Sharp.NativeBinaries.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\LibGit2Sharp.NativeBinaries.1.0.164\build\LibGit2Sharp.NativeBinaries.props'))" />
<Error Condition="!Exists('..\..\packages\Microsoft.VisualStudio.Threading.Analyzers.15.8.122\build\Microsoft.VisualStudio.Threading.Analyzers.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.VisualStudio.Threading.Analyzers.15.8.122\build\Microsoft.VisualStudio.Threading.Analyzers.targets'))" />
<Error Condition="!Exists('..\..\packages\Microsoft.VisualStudio.SDK.Analyzers.15.8.33\build\Microsoft.VisualStudio.SDK.Analyzers.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.VisualStudio.SDK.Analyzers.15.8.33\build\Microsoft.VisualStudio.SDK.Analyzers.targets'))" />
<Error Condition="!Exists('..\..\packages\Microsoft.VSSDK.BuildTools.15.8.3252\build\Microsoft.VSSDK.BuildTools.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.VSSDK.BuildTools.15.8.3252\build\Microsoft.VSSDK.BuildTools.props'))" />
<Error Condition="!Exists('..\..\packages\Microsoft.VSSDK.BuildTools.15.8.3252\build\Microsoft.VSSDK.BuildTools.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\packages\Microsoft.VSSDK.BuildTools.15.8.3252\build\Microsoft.VSSDK.BuildTools.targets'))" />
</Target>
<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">
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="pullrequestfilter" Value="1" />
</Symbols>
<Images>
<Image Guid="$(guidGitHubInlineReviews)" ID="$(pullrequestfilter)">
<Source Uri="$(Resources)/git_pull_request_filter.xaml" />
</Image>
</Images>
<ImageLists />
</ImageManifest>
24 changes: 17 additions & 7 deletions src/GitHub.InlineReviews/InlineReviewsPackage.vsct
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,16 @@
<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="pullrequestfilter" />
<CommandFlag>IconIsMoniker</CommandFlag>
<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 +101,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="pullrequestfilter" 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();
jcansdale marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
}
4 changes: 4 additions & 0 deletions src/GitHub.VisualStudio/GitHub.VisualStudio.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,10 @@
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Resources\icons\git_pull_request_filter.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Resources\icons\link_external.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--This file is compatible with Silverlight-->
<Canvas xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" Name="svg72" Width="48" Height="48">
<Canvas>
<Path Fill="#FF1B1F23" Stroke="#FFF6F6F6" Data="M14.547 13.257V23.5h-3.233V13.258L3.5 6.886V4.5h18.86v2.386l-7.813 6.37z"/>
<Path Fill="#FFF6F6F6" Stroke="#FFF6F6F6" Data="M42.5 35.992c1.225.84 2 2.278 2 3.82 0 2.563-2 4.626-4.5 4.626s-4.5-2.063-4.5-4.626a4.63 4.63 0 0 1 2-3.82V23.33c-.016-.45-.155-.776-.47-1.086-.33-.324-.637-.465-1.03-.496h-1.5v4.86l-7.196-7.422 7.196-7.422v4.86h1.52c1.674.066 3.156.768 4.456 2.087 1.28 1.298 1.96 2.837 2.024 4.6v12.679zm-8-19.367v1H34v-1h.501zm-8 6.384V35.99a4.66 4.66 0 0 1 2 3.82c0 2.563-2 4.626-4.5 4.626s-4.5-2.063-4.5-4.626a4.63 4.63 0 0 1 2-3.82V29.1v-6.094a4.66 4.66 0 0 1-2-3.82c0-2.563 2-4.625 4.5-4.625s4.5 2.062 4.5 4.624a4.63 4.63 0 0 1-2 3.822z"/>
<Path Fill="#FF1B1F23" Data="M42 36.265V23.312c-.06-1.608-.68-3.03-1.88-4.248-1.2-1.217-2.56-1.877-4.12-1.94h-2V13l-6 6.188 6 6.187V21.25h2c.54.04.96.227 1.38.64.42.412.6.866.62 1.422v12.953c-1.18.7-2 2.02-2 3.547 0 2.29 1.78 4.126 4 4.126 2.22 0 4-1.836 4-4.126a4.15 4.15 0 0 0-2-3.547zm-2 6.023c-1.32 0-2.4-1.135-2.4-2.475s1.1-2.476 2.4-2.476c1.3 0 2.4 1.135 2.4 2.475s-1.1 2.476-2.4 2.476zm-12-23.1c0-2.29-1.78-4.125-4-4.125-2.22 0-4 1.835-4 4.124a4.15 4.15 0 0 0 2 3.548v13.53c-1.18.7-2 2.02-2 3.547 0 2.29 1.78 4.126 4 4.126 2.22 0 4-1.836 4-4.126a4.15 4.15 0 0 0-2-3.547v-13.53c1.18-.7 2-2.02 2-3.547zm-1.6 20.625c0 1.36-1.1 2.474-2.4 2.474-1.3 0-2.4-1.134-2.4-2.474 0-1.34 1.1-2.476 2.4-2.476 1.3 0 2.4 1.135 2.4 2.475zM24 21.663c-1.32 0-2.4-1.135-2.4-2.476 0-1.34 1.1-2.475 2.4-2.475 1.3 0 2.4 1.135 2.4 2.476 0 1.34-1.1 2.475-2.4 2.475z"/>
</Canvas>
</Canvas>