diff --git a/CodeLensOopSample/LICENSE b/CodeLensOopSample/LICENSE new file mode 100644 index 0000000..49d2166 --- /dev/null +++ b/CodeLensOopSample/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Microsoft + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/CodeLensOopSample/README.md b/CodeLensOopSample/README.md new file mode 100644 index 0000000..a5b994f --- /dev/null +++ b/CodeLensOopSample/README.md @@ -0,0 +1,23 @@ +# CodeLensOopProvider example +A example for demonstrating how to use the public CodeLens API to create an out-of-proc extesnion that provides a CodeLens indictor showing most recent Git commits made to the source code. + +* Technologies: Visual Studio 2017 SDK +* Topics: CodeLens + +**Description** + +This example generates a VSIX extension that packs two components: +* CodeLensOopProvider.dll: This assembly contains a CodeLens data point provider that retrieves most recent commits from git repo where the source code are commited. This assembly is loaded by the CodeLens service which runs out of the Visual Studio process. +* CodeLensOopProvidervsix.dll: This assembly provides a VSPackage which handles the command invoked when a users clicks on a commit from the commit indicator detail pane. This assembly is loaded by the Visual Studio process. + +![image](src/CodeLensOopProvider.jpg) + +**Getting Started** +1. Clone the repo + `git clone https://github.com/Microsoft/VSSDK-Extensibility-Samples.git` +2. To run the example, hit F5 or choose the **Debug > Start Debugging** menu command. A new instance of Visual Studio will launch under the experimental hive. +3. Open a solution from a local git repo, for example, this example solution. +4. Open a source code file, you will see the git commit lens indictor along with other CodeLens indicators in the editor. + +**How it works** +1. diff --git a/CodeLensOopSample/nuget.config b/CodeLensOopSample/nuget.config new file mode 100644 index 0000000..d01fbb3 --- /dev/null +++ b/CodeLensOopSample/nuget.config @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/CodeLensOopSample/src/CodeLensOopProvider.jpg b/CodeLensOopSample/src/CodeLensOopProvider.jpg new file mode 100644 index 0000000..c207b14 Binary files /dev/null and b/CodeLensOopSample/src/CodeLensOopProvider.jpg differ diff --git a/CodeLensOopSample/src/CodeLensOopProvider/CodeLensOopProvider.csproj b/CodeLensOopSample/src/CodeLensOopProvider/CodeLensOopProvider.csproj new file mode 100644 index 0000000..ec95898 --- /dev/null +++ b/CodeLensOopSample/src/CodeLensOopProvider/CodeLensOopProvider.csproj @@ -0,0 +1,112 @@ + + + + + + Debug + AnyCPU + {31EB1B96-34E8-4E37-872D-4ED7363C95D9} + Library + Properties + CodeLensOopProvider + CodeLensOopProvider + v4.6.1 + 512 + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\..\packages\LibGit2Sharp.0.24.1\lib\net40\LibGit2Sharp.dll + + + ..\..\packages\Microsoft.VisualStudio.CoreUtility.15.8.414-preview\lib\net46\Microsoft.VisualStudio.CoreUtility.dll + + + ..\..\packages\Microsoft.VisualStudio.Language.15.8.414-preview\lib\net46\Microsoft.VisualStudio.Language.dll + + + ..\..\packages\Microsoft.VisualStudio.Text.Data.15.8.414-preview\lib\net46\Microsoft.VisualStudio.Text.Data.dll + + + ..\..\packages\Microsoft.VisualStudio.Text.Logic.15.8.414-preview\lib\net46\Microsoft.VisualStudio.Text.Logic.dll + + + ..\..\packages\Microsoft.VisualStudio.Text.UI.15.8.414-preview\lib\net46\Microsoft.VisualStudio.Text.UI.dll + + + ..\..\packages\Microsoft.VisualStudio.Threading.15.3.23\lib\net45\Microsoft.VisualStudio.Threading.dll + + + ..\..\packages\Microsoft.VisualStudio.Validation.15.3.15\lib\net45\Microsoft.VisualStudio.Validation.dll + + + ..\..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll + + + ..\..\packages\StreamJsonRpc.1.3.6\lib\net45\StreamJsonRpc.dll + + + ..\..\packages\System.Collections.Immutable.1.3.1\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll + + + ..\..\packages\System.ValueTuple.4.3.0\lib\netstandard1.0\System.ValueTuple.dll + + + + + + + + + + + + + + + + + + True + True + Resources.resx + + + + + Designer + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + + + 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}. + + + + \ No newline at end of file diff --git a/CodeLensOopSample/src/CodeLensOopProvider/GitCommitDataPointProvider.cs b/CodeLensOopSample/src/CodeLensOopProvider/GitCommitDataPointProvider.cs new file mode 100644 index 0000000..d2337f5 --- /dev/null +++ b/CodeLensOopSample/src/CodeLensOopProvider/GitCommitDataPointProvider.cs @@ -0,0 +1,191 @@ +using LibGit2Sharp; +using Microsoft.VisualStudio.Core.Imaging; +using Microsoft.VisualStudio.Language.CodeLens.Remoting; +using Microsoft.VisualStudio.Threading; +using Microsoft.VisualStudio.Utilities; +using System; +using System.Collections.Generic; +using System.ComponentModel.Composition; +using System.Diagnostics; +using System.Globalization; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace CodeLensOopProvider +{ + [Export(typeof(IAsyncCodeLensDataPointProvider))] + [Name(Id)] + [ContentType("code")] + [LocalizedName(typeof(Resources), "GitCommitCodeLensProvider")] + [Priority(200)] + internal class GitCommitDataPointProvider : IAsyncCodeLensDataPointProvider + { + internal const string Id = "GitCommit"; + + public Task CanCreateDataPointAsync(CodeLensDescriptor descriptor, CancellationToken token) + { + Debug.Assert(descriptor != null); + var gitRepo = GitUtil.ProbeGitRepository(descriptor.FilePath, out string repoRoot); + return Task.FromResult(gitRepo != null); + } + + public Task CreateDataPointAsync(CodeLensDescriptor descriptor, CancellationToken token) + { + return Task.FromResult(new GitCommitDataPoint(descriptor)); + } + + private class GitCommitDataPoint : IAsyncCodeLensDataPoint + { + private readonly CodeLensDescriptor descriptor; + private readonly Repository gitRepo; + private readonly string gitRepoRootPath; + + public GitCommitDataPoint(CodeLensDescriptor descriptor) + { + this.descriptor = descriptor ?? throw new ArgumentNullException(nameof(descriptor)); + this.gitRepo = GitUtil.ProbeGitRepository(descriptor.FilePath, out this.gitRepoRootPath); + } + + public event AsyncEventHandler InvalidatedAsync; + + public CodeLensDescriptor Descriptor => this.descriptor; + + public Task GetDataAsync(CancellationToken token) + { + // get the most recent commit + Commit commit = GitUtil.GetCommits(this.gitRepo, this.descriptor.FilePath, 1).FirstOrDefault(); + if (commit == null) + { + return Task.FromResult(null); + } + + CodeLensDataPointDescriptor response = new CodeLensDataPointDescriptor() + { + Description = commit.Author.Name, + TooltipText = $"Last change committed by {commit.Author.Name} at {commit.Author.When.ToString(CultureInfo.CurrentCulture)}", + IntValue = null, // no int value + ImageId = GetCommitTypeIcon(commit), + }; + + return Task.FromResult(response); + } + + public Task GetDetailsAsync(CancellationToken token) + { + // get the most recent 5 commits + var commits = GitUtil.GetCommits(this.gitRepo, this.descriptor.FilePath, 5).AsEnumerable(); + if (commits == null || commits.Count() == 0) + { + return Task.FromResult(null); + } + + var headers = new List() + { + new CodeLensDetailHeaderDescriptor() + { + UniqueName = "CommitType", + Width = 22, + }, + new CodeLensDetailHeaderDescriptor() + { + UniqueName = "CommitId", + DisplayName = "Commit Id", + Width = 100, // fixed width + }, + new CodeLensDetailHeaderDescriptor() + { + UniqueName = "CommitDescription", + DisplayName = "Description", + Width = 0.66666, // use 2/3 of the remaining width + }, + new CodeLensDetailHeaderDescriptor() + { + UniqueName = "CommitAuthor", + DisplayName = "Author", + Width = 0.33333, // use 1/3 of the remaining width + }, + new CodeLensDetailHeaderDescriptor() + { + UniqueName = "CommitDate", + DisplayName = "Date", + Width = 85, // fixed width + } + }; + + var entries = commits.Select( + commit => new CodeLensDetailEntryDescriptor() + { + Fields = new List() + { + new CodeLensDetailEntryField() + { + ImageId = GetCommitTypeIcon(commit), + }, + new CodeLensDetailEntryField() + { + Text = commit.Id.Sha.Substring(0, 8), + }, + new CodeLensDetailEntryField() + { + Text = commit.MessageShort, + }, + new CodeLensDetailEntryField() + { + Text = commit.Author.Name, + }, + new CodeLensDetailEntryField() + { + Text = commit.Author.When.ToString(@"MM\/dd\/yyyy", CultureInfo.CurrentCulture), + }, + }, + Tooltip = commit.Message, + NavigationCommand = new CodeLensDetailEntryCommand() + { + CommandSet = new Guid("f3cb9f10-281b-444f-a14e-de5de36177cd"), + CommandId = 0x0100, + CommandName = "Git.NavigateToCommit", + }, + NavigationCommandArgs = new List() {commit.Id.Sha }, + }); + + var result = new CodeLensDetailsDescriptor() + { + Headers = headers, + Entries = entries, + PaneNavigationCommands = new List() + { + new CodeLensDetailPaneCommand() + { + CommandDisplayName = "Show History", + } + }, + }; + + return Task.FromResult(result); + } + + /// + /// Raises event. + /// + /// + /// This is not part of the IAsyncCodeLensDataPoint interface. + /// The data point source can call this method to notify the client proxy that data for this data point has changed. + /// + public void Invalidate() + { + this.InvalidatedAsync?.Invoke(this, EventArgs.Empty).ConfigureAwait(false); + } + + private static ImageId GetCommitTypeIcon(Commit commit) + { + var imageCatalog = new Guid("{ae27a6b0-e345-4288-96df-5eaf394ee369}"); + int merge = 1855; + int changeset = 425; + int imageId = commit.Parents.Count() > 1 ? merge : changeset; + + return new ImageId(imageCatalog, imageId); + } + } + } +} diff --git a/CodeLensOopSample/src/CodeLensOopProvider/GitUtil.cs b/CodeLensOopSample/src/CodeLensOopProvider/GitUtil.cs new file mode 100644 index 0000000..f8eb30c --- /dev/null +++ b/CodeLensOopSample/src/CodeLensOopProvider/GitUtil.cs @@ -0,0 +1,76 @@ +using LibGit2Sharp; +using System; +using System.Collections.Immutable; +using System.Diagnostics; +using System.IO; +using System.Linq; + +namespace CodeLensOopProvider +{ + public static class GitUtil + { + /// + /// Given a directory path, probes a Git repo and root path to the repo. + /// + public static Repository ProbeGitRepository(string path, out string repoRoot) + { + repoRoot = null; + + // if path is neither a directory nor a file path + if (!Directory.Exists(path) && !File.Exists(path)) + return null; + + try + { + repoRoot = Repository.Discover(path); + if (repoRoot != null) + { + var repo = new Repository(repoRoot); + return repo; + } + } + catch (Exception ex) + { + Debug.Fail(ex.Message); + return null; + } + + return null; + } + + public static Branch GetCurrentBranch(Repository repo) + { + return repo.Head; + } + + public static Branch GetTrackedBranch(Repository repo) + { + return repo.Head.TrackedBranch; + } + + public static Commit GetLastCommit(Repository repo) + { + return repo.Commits.FirstOrDefault(); + } + + public static ImmutableArray GetCommits(Repository repo, string filePath, int count) + { + var workingDir = repo.Info.WorkingDirectory; + var relativePath = filePath.Replace(workingDir, string.Empty); + + var filter = new Func( + commit => + { + var commitId = commit.Tree[relativePath]?.Target?.Sha; + var parentId = commit.Parents?.FirstOrDefault()?[relativePath]?.Target?.Sha; + return commitId != parentId; + }); + + var commits = repo.Commits.Where(filter); + + var last5 = commits.OrderByDescending(c => c.Author.When).Take(count); + + return ImmutableArray.ToImmutableArray(last5); + } + } +} diff --git a/CodeLensOopSample/src/CodeLensOopProvider/Properties/AssemblyInfo.cs b/CodeLensOopSample/src/CodeLensOopProvider/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..1bc41c3 --- /dev/null +++ b/CodeLensOopSample/src/CodeLensOopProvider/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("CodeLensOopProvider")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("CodeLensOopProvider")] +[assembly: AssemblyCopyright("Copyright © 2018")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("31eb1b96-34e8-4e37-872d-4ed7363c95d9")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/CodeLensOopSample/src/CodeLensOopProvider/Resources.Designer.cs b/CodeLensOopSample/src/CodeLensOopProvider/Resources.Designer.cs new file mode 100644 index 0000000..6a59414 --- /dev/null +++ b/CodeLensOopSample/src/CodeLensOopProvider/Resources.Designer.cs @@ -0,0 +1,99 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace CodeLensOopProvider { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("CodeLensOopProvider.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Code Health Indicator Provider. + /// + internal static string CodeHealthCodeLensProvider { + get { + return ResourceManager.GetString("CodeHealthCodeLensProvider", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Fake References Provider. + /// + internal static string FakeReferencesCodeLensProvider { + get { + return ResourceManager.GetString("FakeReferencesCodeLensProvider", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} fault hit counts. + /// + internal static string FaultHitCounts { + get { + return ResourceManager.GetString("FaultHitCounts", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Remote Git Commit Provider. + /// + internal static string GitCommitCodeLensProvider { + get { + return ResourceManager.GetString("GitCommitCodeLensProvider", resourceCulture); + } + } + } +} diff --git a/CodeLensOopSample/src/CodeLensOopProvider/Resources.resx b/CodeLensOopSample/src/CodeLensOopProvider/Resources.resx new file mode 100644 index 0000000..93ce73c --- /dev/null +++ b/CodeLensOopSample/src/CodeLensOopProvider/Resources.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Code Health Indicator Provider + + + Fake References Provider + + + {0} fault hit counts + + + Remote Git Commit Provider + + \ No newline at end of file diff --git a/CodeLensOopSample/src/CodeLensOopProvider/app.config b/CodeLensOopSample/src/CodeLensOopProvider/app.config new file mode 100644 index 0000000..b359fa1 --- /dev/null +++ b/CodeLensOopSample/src/CodeLensOopProvider/app.config @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/CodeLensOopSample/src/CodeLensOopProvider/packages.config b/CodeLensOopSample/src/CodeLensOopProvider/packages.config new file mode 100644 index 0000000..0d2b0d6 --- /dev/null +++ b/CodeLensOopSample/src/CodeLensOopProvider/packages.config @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/CodeLensOopSample/src/CodeLensOopProviderPackage.cs b/CodeLensOopSample/src/CodeLensOopProviderPackage.cs new file mode 100644 index 0000000..37fe77b --- /dev/null +++ b/CodeLensOopSample/src/CodeLensOopProviderPackage.cs @@ -0,0 +1,156 @@ +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.OLE.Interop; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; +using System.Threading; +using IServiceProvider = System.IServiceProvider; +using Task = System.Threading.Tasks.Task; + +namespace CodeLensOopProviderVsix +{ + /// + /// This is the class that implements the package exposed by this assembly. + /// + /// + /// + /// The minimum requirement for a class to be considered a valid package for Visual Studio + /// is to implement the IVsPackage interface and register itself with the shell. + /// This package uses the helper classes defined inside the Managed Package Framework (MPF) + /// to do it: it derives from the Package class that provides the implementation of the + /// IVsPackage interface and uses the registration attributes defined in the framework to + /// register itself and its components with the shell. These attributes tell the pkgdef creation + /// utility what data to put into .pkgdef file. + /// + /// + /// To get loaded into VS, the package must be referred by <Asset Type="Microsoft.VisualStudio.VsPackage" ...> in .vsixmanifest file. + /// + /// + [PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)] + [InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)] // Info on this package for Help/About + [ProvideMenuResource("Menus.ctmenu", 1)] + [Guid(GuidAndCmdID.PackageGuidString)] + [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "pkgdef, VS and vsixmanifest are valid VS terms")] + public sealed class CodeLensOopProviderPackage : AsyncPackage, IOleCommandTarget + { + private IOleCommandTarget pkgCommandTarget; + + /// + /// Initializes a new instance of the class. + /// + public CodeLensOopProviderPackage() + { + // Inside this method you can place any initialization code that does not require + // any Visual Studio service because at this point the package object is created but + // not sited yet inside Visual Studio environment. The place to do all the other + // initialization is the Initialize method. + } + + #region Package Members + + /// + /// Initialization of the package; this method is called right after the package is sited, so this is the place + /// where you can put all the initialization code that rely on services provided by VisualStudio. + /// + /// A cancellation token to monitor for initialization cancellation, which can occur when VS is shutting down. + /// A provider for progress updates. + /// A task representing the async work of package initialization, or an already completed task if there is none. Do not return null from this method. + protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress progress) + { + await base.InitializeAsync(cancellationToken, progress); + + // When initialized asynchronously, the current thread may be a background thread at this point. + // Do any initialization that requires the UI thread after switching to the UI thread. + await this.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); + + this.pkgCommandTarget = await this.GetServiceAsync(typeof(IOleCommandTarget)) as IOleCommandTarget; + } + + #endregion + + #region IOleCommandTarget + + int IOleCommandTarget.QueryStatus(ref Guid pguidCmdGroup, uint cCmds, OLECMD[] prgCmds, IntPtr pCmdText) + { + if (pguidCmdGroup == GuidAndCmdID.guidCmdSet) + { + switch (prgCmds[0].cmdID) + { + case GuidAndCmdID.cmdidNavigateToGitCommit: + prgCmds[0].cmdf |= (uint)(OLECMDF.OLECMDF_SUPPORTED | OLECMDF.OLECMDF_ENABLED | OLECMDF.OLECMDF_INVISIBLE); + return VSConstants.S_OK; + } + } + + return this.pkgCommandTarget.QueryStatus(ref pguidCmdGroup, cCmds, prgCmds, pCmdText); + } + + int IOleCommandTarget.Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut) + { + if (pguidCmdGroup == GuidAndCmdID.guidCmdSet) + { + switch (nCmdID) + { + case GuidAndCmdID.cmdidNavigateToGitCommit: + if (IsQueryParameterList(pvaIn, pvaOut, nCmdexecopt)) + { + Marshal.GetNativeVariantForObject("p", pvaOut); + return VSConstants.S_OK; + } + else + { + // no args + if (pvaIn == IntPtr.Zero) + return VSConstants.S_FALSE; + + object vaInObject = Marshal.GetObjectForNativeVariant(pvaIn); + if (vaInObject == null || vaInObject.GetType() != typeof(string)) + return VSConstants.E_INVALIDARG; + + if ((vaInObject is string commitId) && !string.IsNullOrEmpty(commitId)) + { + NavigateToCommit(commitId, this as IServiceProvider); + } + } + return VSConstants.S_OK; + } + } + + return this.pkgCommandTarget.Exec(ref pguidCmdGroup, nCmdID, nCmdexecopt, pvaIn, pvaOut); + } + + #endregion + + private static void NavigateToCommit(string commitId, IServiceProvider serviceProvider) + { + string title = "CodeLens OOP Extension"; + string message = $"Commit Id is: {commitId}"; + + // Show a message box to prove we were here + VsShellUtilities.ShowMessageBox( + serviceProvider, + message, + title, + OLEMSGICON.OLEMSGICON_INFO, + OLEMSGBUTTON.OLEMSGBUTTON_OK, + OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST); + } + + private static bool IsQueryParameterList(IntPtr pvaIn, IntPtr pvaOut, uint nCmdexecopt) + { + ushort lo = (ushort)(nCmdexecopt & (uint)0xffff); + ushort hi = (ushort)(nCmdexecopt >> 16); + if (lo == (ushort)OLECMDEXECOPT.OLECMDEXECOPT_SHOWHELP) + { + if (hi == VsMenus.VSCmdOptQueryParameterList) + { + return true; + } + } + + return false; + } + } +} diff --git a/CodeLensOopSample/src/CodeLensOopProviderPackage.vsct b/CodeLensOopSample/src/CodeLensOopProviderPackage.vsct new file mode 100644 index 0000000..4eaa2f6 --- /dev/null +++ b/CodeLensOopSample/src/CodeLensOopProviderPackage.vsct @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CodeLensOopSample/src/CodeLensOopProviderVsix.csproj b/CodeLensOopSample/src/CodeLensOopProviderVsix.csproj new file mode 100644 index 0000000..d8655c3 --- /dev/null +++ b/CodeLensOopSample/src/CodeLensOopProviderVsix.csproj @@ -0,0 +1,230 @@ + + + + + 15.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + true + + + + Debug + AnyCPU + 2.0 + {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + {B896FF3C-FB27-4F5F-BD50-E0AFA5C82328} + Library + Properties + CodeLensOopProviderVsix + CodeLensOopProviderVsix + v4.6.1 + true + true + true + false + false + true + true + Program + $(DevEnvDir)devenv.exe + /rootsuffix Exp + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + Designer + + + Designer + + + Designer + + + + + Menus.ctmenu + Designer + + + + + + + + + + False + + + False + + + False + + + False + + + + False + + + ..\packages\Microsoft.VisualStudio.CoreUtility.15.8.414-preview\lib\net46\Microsoft.VisualStudio.CoreUtility.dll + + + ..\packages\Microsoft.VisualStudio.Imaging.15.0.26201\lib\net45\Microsoft.VisualStudio.Imaging.dll + + + ..\packages\Microsoft.VisualStudio.Imaging.Interop.14.0.DesignTime.14.3.25408\lib\net20\Microsoft.VisualStudio.Imaging.Interop.14.0.DesignTime.dll + True + + + ..\packages\Microsoft.VisualStudio.Language.15.8.414-preview\lib\net46\Microsoft.VisualStudio.Language.dll + + + ..\packages\Microsoft.VisualStudio.OLE.Interop.7.10.6071\lib\Microsoft.VisualStudio.OLE.Interop.dll + + + ..\packages\Microsoft.VisualStudio.Shell.15.0.15.0.26201\lib\Microsoft.VisualStudio.Shell.15.0.dll + + + ..\packages\Microsoft.VisualStudio.Shell.Framework.15.0.26201\lib\net45\Microsoft.VisualStudio.Shell.Framework.dll + + + ..\packages\Microsoft.VisualStudio.Shell.Interop.7.10.6071\lib\Microsoft.VisualStudio.Shell.Interop.dll + + + ..\packages\Microsoft.VisualStudio.Shell.Interop.10.0.10.0.30319\lib\Microsoft.VisualStudio.Shell.Interop.10.0.dll + True + + + ..\packages\Microsoft.VisualStudio.Shell.Interop.11.0.11.0.61030\lib\Microsoft.VisualStudio.Shell.Interop.11.0.dll + True + + + ..\packages\Microsoft.VisualStudio.Shell.Interop.12.0.12.0.30110\lib\Microsoft.VisualStudio.Shell.Interop.12.0.dll + True + + + ..\packages\Microsoft.VisualStudio.Shell.Interop.14.0.DesignTime.14.3.25407\lib\Microsoft.VisualStudio.Shell.Interop.14.0.DesignTime.dll + True + + + ..\packages\Microsoft.VisualStudio.Shell.Interop.8.0.8.0.50727\lib\Microsoft.VisualStudio.Shell.Interop.8.0.dll + + + ..\packages\Microsoft.VisualStudio.Shell.Interop.9.0.9.0.30729\lib\Microsoft.VisualStudio.Shell.Interop.9.0.dll + + + ..\packages\Microsoft.VisualStudio.Text.Data.15.8.414-preview\lib\net46\Microsoft.VisualStudio.Text.Data.dll + + + ..\packages\Microsoft.VisualStudio.Text.Logic.15.8.414-preview\lib\net46\Microsoft.VisualStudio.Text.Logic.dll + + + ..\packages\Microsoft.VisualStudio.Text.UI.15.8.414-preview\lib\net46\Microsoft.VisualStudio.Text.UI.dll + + + ..\packages\Microsoft.VisualStudio.TextManager.Interop.7.10.6070\lib\Microsoft.VisualStudio.TextManager.Interop.dll + + + ..\packages\Microsoft.VisualStudio.TextManager.Interop.8.0.8.0.50727\lib\Microsoft.VisualStudio.TextManager.Interop.8.0.dll + + + ..\packages\Microsoft.VisualStudio.Threading.15.3.23\lib\net45\Microsoft.VisualStudio.Threading.dll + + + ..\packages\Microsoft.VisualStudio.Utilities.15.0.26201\lib\net45\Microsoft.VisualStudio.Utilities.dll + + + ..\packages\Microsoft.VisualStudio.Validation.15.3.15\lib\net45\Microsoft.VisualStudio.Validation.dll + + + ..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll + + + False + + + ..\packages\StreamJsonRpc.1.3.6\lib\net45\StreamJsonRpc.dll + + + ..\packages\System.Collections.Immutable.1.3.1\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll + + + ..\packages\System.ValueTuple.4.3.0\lib\netstandard1.0\System.ValueTuple.dll + + + + + + + + + + + + + true + VSPackage + Designer + + + + + {31eb1b96-34e8-4e37-872d-4ed7363c95d9} + CodeLensOopProvider + BuiltProjectOutputGroup%3bBuiltProjectOutputGroupDependencies%3bGetCopyToOutputDirectoryItems%3bSatelliteDllsProjectOutputGroup%3bDebugSymbolsProjectOutputGroup%3b + DebugSymbolsProjectOutputGroup%3b + + + + + + + + + + + + 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}. + + + + + + + + + + \ No newline at end of file diff --git a/CodeLensOopSample/src/CodeLensOopProviderVsix.sln b/CodeLensOopSample/src/CodeLensOopProviderVsix.sln new file mode 100644 index 0000000..c83daaa --- /dev/null +++ b/CodeLensOopSample/src/CodeLensOopProviderVsix.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27625.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeLensOopProviderVsix", "CodeLensOopProviderVsix.csproj", "{B896FF3C-FB27-4F5F-BD50-E0AFA5C82328}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeLensOopProvider", "CodeLensOopProvider\CodeLensOopProvider.csproj", "{31EB1B96-34E8-4E37-872D-4ED7363C95D9}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B896FF3C-FB27-4F5F-BD50-E0AFA5C82328}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B896FF3C-FB27-4F5F-BD50-E0AFA5C82328}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B896FF3C-FB27-4F5F-BD50-E0AFA5C82328}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B896FF3C-FB27-4F5F-BD50-E0AFA5C82328}.Release|Any CPU.Build.0 = Release|Any CPU + {31EB1B96-34E8-4E37-872D-4ED7363C95D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {31EB1B96-34E8-4E37-872D-4ED7363C95D9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {31EB1B96-34E8-4E37-872D-4ED7363C95D9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {31EB1B96-34E8-4E37-872D-4ED7363C95D9}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {C2E28001-B12D-47B9-85BF-CF6A2D4AE7EE} + EndGlobalSection +EndGlobal diff --git a/CodeLensOopSample/src/GuidAndCmdID.cs b/CodeLensOopSample/src/GuidAndCmdID.cs new file mode 100644 index 0000000..d93aa32 --- /dev/null +++ b/CodeLensOopSample/src/GuidAndCmdID.cs @@ -0,0 +1,15 @@ +using System; + +namespace CodeLensOopProviderVsix +{ + class GuidAndCmdID + { + public const string PackageGuidString = "9a7d41ec-0f34-460a-9499-8356b559ff89"; + public const string PackageCmdSetGuidString = "f3cb9f10-281b-444f-a14e-de5de36177cd"; + + public static readonly Guid guidPackage = new Guid(PackageGuidString); + public static readonly Guid guidCmdSet = new Guid(PackageCmdSetGuidString); + + public const uint cmdidNavigateToGitCommit = 0x0100; + } +} diff --git a/CodeLensOopSample/src/Properties/AssemblyInfo.cs b/CodeLensOopSample/src/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..05b83c1 --- /dev/null +++ b/CodeLensOopSample/src/Properties/AssemblyInfo.cs @@ -0,0 +1,33 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("CodeLensOopProviderVsix")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("CodeLensOopProviderVsix")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/CodeLensOopSample/src/Resources/CodeLensOopProvider.png b/CodeLensOopSample/src/Resources/CodeLensOopProvider.png new file mode 100644 index 0000000..b22d975 Binary files /dev/null and b/CodeLensOopSample/src/Resources/CodeLensOopProvider.png differ diff --git a/CodeLensOopSample/src/Resources/CodeLensOopProviderPackage.ico b/CodeLensOopSample/src/Resources/CodeLensOopProviderPackage.ico new file mode 100644 index 0000000..d323b07 Binary files /dev/null and b/CodeLensOopSample/src/Resources/CodeLensOopProviderPackage.ico differ diff --git a/CodeLensOopSample/src/VSPackage.resx b/CodeLensOopSample/src/VSPackage.resx new file mode 100644 index 0000000..3748328 --- /dev/null +++ b/CodeLensOopSample/src/VSPackage.resx @@ -0,0 +1,140 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + CodeLensOopProviderCommands Extension + + + CodeLensOopProviderCommands Visual Studio Extension Detailed Info + + + Resources\CodeLensOopProviderPackage.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + \ No newline at end of file diff --git a/CodeLensOopSample/src/app.config b/CodeLensOopSample/src/app.config new file mode 100644 index 0000000..2529709 --- /dev/null +++ b/CodeLensOopSample/src/app.config @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/CodeLensOopSample/src/packages.config b/CodeLensOopSample/src/packages.config new file mode 100644 index 0000000..7dbbcd5 --- /dev/null +++ b/CodeLensOopSample/src/packages.config @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/CodeLensOopSample/src/source.extension.vsixmanifest b/CodeLensOopSample/src/source.extension.vsixmanifest new file mode 100644 index 0000000..118d647 --- /dev/null +++ b/CodeLensOopSample/src/source.extension.vsixmanifest @@ -0,0 +1,23 @@ + + + + + CodeLensOopProviderVsix + An example for CodeLens provider implpented as an VS out-of-proc extension. + + + + + + + + + + + + + + + + +