A collection of research and coding articles.

Collecting and emailing zipped tool logs from your user in one click 2013-11-14

Congratulations, your tool has users! But now complaints are pouring in. Unfortunately, very few developers will have the time or patience to send poke around or follow instructions for finding your debugging output to help you figure things out.

With a single click, the following code opens up a user's default email client (e.g. Outlook) addressed to you, with the file attached and a body explaining the data being sent and an option to see it. Then, they just have to click send. There are no third party dependencies.

Zipping a file

automark.VisualStudio/automark.VisualStudioPackage.cs

First, setup things on disk. builder is a StringBuilder that contains the exported contents. tempExport is the file we want to zip, as tempExportZip.

var time = DateTime.Now;
string tempExport = System.IO.Path.Combine(m_basePath, "exports", string.Format("export-{0:yyyy-MM-dd-hh-mm-tt}.export", time));
string tempExportZip = System.IO.Path.Combine(m_basePath, "exports", string.Format("export-{0:yyyy-MM-dd-hh-mm-tt}.zip", time));

var parent = System.IO.Path.GetDirectoryName(tempExport);
if (!System.IO.Directory.Exists(parent))
{
    System.IO.Directory.CreateDirectory(parent);
}

System.IO.File.WriteAllText(tempExport, builder.ToString());
Zip.ZipFile(tempExportZip, tempExport);

Creating a Visual Studio extension shim for automark 2013-10-12

automark is a tool for generating a markdown summary of a coding task from recent coding history. It designed work with another tool, autogit, which records fine-grain code changes in the IDE.

Something a little different about this blog post is that this doesn't aim to be a perfect how-to document, but instead, show incremental progress and mistakes along the way.

Coding Summary, Friday, October 11, 2013

I will be showing one example usage of automark, by showing you a blog post generated from a recent coding task. The goal of the task was to create a light-weight shim for Visual Studio to run and launch automark, which currently works as a command line tool, from the IDE itself.

Setting up VSX package

ProvideAutoLoad is necessary for getting Visual Studio package to load automatically.

automark.VisualStudio/automark.VisualStudioPackage.cs

     // This attribute registers a tool window exposed by this package.
     [ProvideToolWindow(typeof(MyToolWindow))]
     [Guid(GuidList.guidautomarkVisualStudioPkgString)]
+    [ProvideAutoLoad(VSConstants.UICONTEXT.SolutionExists_string)]
     public sealed class automarkVisualStudioPackage : Package
     {
         /// <summary>

Using Tagging and Adornments for better TODOs in Visual Studio. 2013-09-27

Visual Studio actually provides a powerful framework for making custom extensions to the editor while keeping the code surprisingly simple.

Imagine if you wanted to make a // TODO note appear more distinctive as well as provide custom actions.

TodoExample

With Visual Studio Extensions and MEF, you can make direct graphical changes to the editor with an IAdornmentLayer. But if you want to support custom editor actions and more interactivity, it is worth it to also create an ITagger. This post will show how you can insert tags into the TextBuffer and query those to render custom adornments.

Adornments

First, we create a class that will respond to text view creation events and create an IAdornmentLayer

TodoArdornmentFactory.cs

We provide MEF attributes so that the class will merge into the IWpfTextViewCreationListener MEF container.

/// <summary>
/// Establishes an <see cref="IAdornmentLayer"/> to place the adornment on and exports the <see cref="IWpfTextViewCreationListener"/>
/// that instantiates the adornment on the event of a <see cref="IWpfTextView"/>'s creation
/// </summary>
[Export(typeof(IWpfTextViewCreationListener))]
[ContentType("text")]
[TextViewRole(PredefinedTextViewRoles.Document)]
internal sealed class TodoArdornmentFactory : IWpfTextViewCreationListener
{

Deploying native binaries with Visual Studio extensions 2013-09-17

Deploying Native Binaries

If your extension depends on a native binary, you have to do some tricks to get the binary to be copied into the experimental hive directory for testing and including in your VSIX for deployment.

I was doing some manual copies in my post build event that was brittle, and had to explicitly include the native binaries as content.

Another way

nulltoken had given out a helpful hint on using msbuild directives to stream-line this process:

You might be willing to glance at https://github.com/libgit2/libgit2sharp/blob/vNext/LibGit2Sharp/CopyNativeDependencies.targets and the way it's being used in LibGit2Sharp.Tests.csproj

How to do it

I adapted this approach for my project. First, I define a reference to native binaries that live in the nuget directory. $(MSBuildProjectDirectory) refers to directory containing the .csproj file.

<PropertyGroup>
    <NativeBinariesDirectory>$(MSBuildProjectDirectory)\..\packages\LibGit2Sharp.0.13.0.0\NativeBinaries</NativeBinariesDirectory>
</PropertyGroup>

Parse git log output in C# 2013-09-05

A simple example for parsing the output of git log in pure C#.

Getting git log output

Get output of git log --name-status by specifying path of git repo.

    public static string ListShaWithFiles(string path)
    {
        var output = RunProcess(string.Format(" --git-dir={0}/.git --work-tree={1} log --name-status", path.Replace("\\", "/"), path.Replace("\\", "/")));
        return output;
    }

Helper for getting command line output

    private static string RunProcess(string command)
    {
        // Start the child process.
        Process p = new Process();
        // Redirect the output stream of the child process.
        p.StartInfo.UseShellExecute = false;
        p.StartInfo.RedirectStandardOutput = true;
        p.StartInfo.FileName = Config.GitExectuable;
        p.StartInfo.Arguments = command;
        p.Start();
        // Read the output stream first and then wait.
        string output = p.StandardOutput.ReadToEnd();
        p.WaitForExit();
        return output;
    }