tl;dr
- ソリューションエクスプローラの表示を、Gitの中身を比較して変更があったファイルでフィルタする拡張を作った
- フィルタする拡張機能は、
HierarchyTreeFilterProvider
を実装してVisual Studio側に公開するといい感じで動くらしい - フィルタ機能をソリューションエクスプローラに統合するときは、
<Parent guid="guidSHLMainMenu" id="IDG_VS_TOOLBAR_PROJWIN_FILTERS" />
にするといい
Gitの状態でソリューションエクスプローラをフィルタしたい
Visual Studioの標準状態にはGitのunstagedなファイルのみ表示するフィルタはあって、これを使いたいからできればコミットしたくない。みたいな話を聞いた。
普通にコミットして、git diff --stats origin/master
的なコマンドをSource Treeとかで実行したら十分なのでは?っていう話をしたら、それVSのフィルタ機能に統合してほしい。って言われたので作ってみたよ。
ソリューションエクスプローラのフィルタ機能を拡張する
まず、ソリューションエクスプローラのフィルタ機能を実現するにはHierarchyTreeFilterProvider
というクラスを実装すればいい。
大まかにはdocs.com に書いてるのを読めばだいたい動く。
まず、HierarchyTreeFilterProvider
を実装して、SolutionTreeFilterProvider
属性を付けて、protectedで定義されているHierarchyTreeFilter
を実装すればOK。
HierarchyTreeFilterProvider
はフィルタの実装をnewするためのファクトリクラスで、フィルタとしての実体はHierarchyTreeFilter
。
Visual Studioがフィルタを実行したい気分になると、HierarchyTreeFilter.GetIncludedItemsAsync
が呼び出されるので、このメソッドのなかにフィルタロジックを書いていけばOK。
次に、フィルタを呼び出すボタンをVSCTファイルに書く。基本的にはボタンをツールバーに追加する時と同じで、親として設定するグループを適切に設定する。適切というのは、
<Parent guid="guidSHLMainMenu" id="IDG_VS_TOOLBAR_PROJWIN_FILTERS" />
こう書く。このIDG_VS_TOOLBAR_PROJWIN_FILTERS
はどこから出てきたのはは気にしてはいけない。こう書け。と先のdocs.comにも書いてあるのでこう書く。気にしてはいけない。
Gitの状態にアクセスする
LibGit2Sharpを使ってアクセスした。Visual Studioが開いているソリューションは DTE.Solution.FullName
プロパティにアクセスすると得られるので、ここからディレクトリを上方向にたどった最寄りの.git をソリューションが参照すべきGitリポジトリとした。
このパスを、LibGit2Sharpで開いて、Diff.Compare<TreeChanges>
を呼び出す。差分がある(Add, Delete, Modifiedなど)ファイルのみを返すのでこの一覧をソリューションエクスプローラに表示すべきファイルとして使った。
この一覧ができてしまえばあとは、_hierarchyCollectionProvider.GetFilteredHierarchyItemsAsync
を呼び出して、コールバックにフィルタすべきかどうかを判定する項目が入っているので、CanonicalName
に含まれているパスをチェックすればOK。
LibGit2SharpをVS拡張から使う
LibGit2Sharpはlibgit2のC#バインディングで実行にはネイティブ実装が必要。で、本来はNuGetから自動でインストールされてbinに出力されるけれども、VSIXの場合はVSIXに明示的に含めるようにしないとだめ。なのでcsprojを編集して次を書き足しておく。
<ItemGroup> <PackageReference Include="LibGit2Sharp.NativeBinaries" GeneratePathProperty="true"> <Version>2.0.306</Version> </PackageReference> <Content Include="$(PkgLibGit2Sharp_NativeBinaries)\runtimes\win-x64\native\git2-106a5f2.dll"> <Link>lib\win32\x64\git2-106a5f2.dll</Link> <CopyToOutputDirectory>Always</CopyToOutputDirectory> <IncludeInVSIX>true</IncludeInVSIX> </Content> <Content Include="$(PkgLibGit2Sharp_NativeBinaries)\runtimes\win-x64\native\git2-106a5f2.pdb"> <Link>lib\win32\x64\git2-106a5f2.pdb</Link> <CopyToOutputDirectory>Always</CopyToOutputDirectory> <IncludeInVSIX>true</IncludeInVSIX> </Content> <Content Include="$(PkgLibGit2Sharp_NativeBinaries)\runtimes\win-x86\native\git2-106a5f2.dll"> <Link>lib\win32\x86\git2-106a5f2.dll</Link> <IncludeInVSIX>true</IncludeInVSIX> <CopyToOutputDirectory>Always</CopyToOutputDirectory> </Content> <Content Include="$(PkgLibGit2Sharp_NativeBinaries)\runtimes\win-x86\native\git2-106a5f2.pdb"> <Link>lib\win32\x86\git2-106a5f2.pdb</Link> <IncludeInVSIX>true</IncludeInVSIX> <CopyToOutputDirectory>Always</CopyToOutputDirectory> </Content> <Content Include="Resources\SolutionFilter.png" /> </ItemGroup>
これで、NuGetからLibGit2Sharp.NativeBinariesがインストールされて、x86, x64 Windows向けバイナリがVSIXに含まれるようになる。
ベースブランチを選択する
どこかをDiffの基準点にする必要がある。今回は、WPFの非表示のWindowとContextMenuを動的に生成して、ボタンを押したときにポップアップメニューが表示されるようにした。
ソリューションエクスプローラのこのツールバーにボタンを追加する方法は探しても見つからないけれども、VSCTの親設定にこう書けば追加できる。
<Parent guid="guidSharedMenuGroup" id="IDG_VS_PROJ_TOOLBAR2" />
これをどうやって探すかはdocs.comに詳しくはないけど書いてある。
SharedCmdPlace.vsct
このファイルに定義されているのが定義済みのコマンドバーなどなどの場所で、このファイルはこのあたりに入っていた。
C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VSSDK\VisualStudioIntegration\Common\Inc
この中から、ソリューションエクスプローラのツールバーを探すと、IDG_VS_PROJ_TOOLBAR2
がみつかる。
おしまい
成果物はGitHubとVisual Studio Marketplaceに公開してあります。ローカライズもうまくいかなくてだいぶ悩んだけどそれはまた別の機会に…