Contents tagged with Tools
-
Looking for a New Year's Resolution?
There are many exciting .NET topics waiting for us in 2004. Simply too much stuff to be tried and understood, too much knowledge to be gained. If you're making plans for 2004, here's one single thing I can fully recommend:
If you're not already doing it, start writing unit tests using a test framework.
Unit testing is one of these "I really should be doing this" concepts. Even though I read and heard about unit testing years ago, I only started doing it in 2003. Sure, I always had some kind of test applications for e.g. a library - who hasn't written one of those infamous programs consisting of a form and dozens of buttons ("button1", "button2", ...), each starting some test code. And it's not as if my software was of poor quality. But looking back, unit testing was the thing in 2003 that made me a better developer.
I'm using NUnit for my unit tests; it's so easy to use that a typical reaction of developers being introduced to NUnit is "What? That's all I have to do?". To get started, visit this page on the NUnit website, and follow the steps in the first paragraph "Getting Started with NUnit".
When I began writing unit tests in early 2003, I wrote tests for existing code. If this code (e.g. a library) is already successfully in use, this can be pretty frustrating, because the most basic tests are all likely to succeed. My first tests where pretty coarse, testing too much at once - maybe because the trivial tests (e.g. create a class instance, set a property and test whether reading it has the expected result) seemed like a waste of time.
In the course of time I moved more towards "test driven development", i.e. writing tests along with the code, often even before the implementation is ready. Now, if I create a new project, I always add a test project to the solution. This way my code and the corresponding tests never run out of sync. If I make a breaking change, the solution won't compile - it's that easy.
If you take this approach (writing test very early), even testing the most basic stuff can be pretty rewarding:
- Sometime typos or copy/paste mistakes are not caught by the compiler (e.g. when the property getter accesses a different member variable than the property setter) - one bug like this found by a unit test written in 5 Minutes can save you hours of debugging through a complete application.
- It's a very good test for the usability e.g. of your API. If it turns out that even a simple task requires many lines of code, you definitely should re-design your API (which is less of a problem at that early stage of development).
- The unit tests is some sort of documentation of how your code is used - don't underestimate how helpful this can be (by the way: I wrote a simple tool for generating examples for online documentation from unit tests).
- Last but not least: I know that some of the unit-testing folks don't like debuggers, but fine-grained unit tests are very good entry points for single stepping through your code.
So... what about a New Year's Resolution to start writing unit tests?
-
GhostDoc News
My project GhostDoc is now a VS.Net Addin. But before I release the next version, there are a couple of things on my ToDo list I want to finish first:
- A rewrite of the configuration storage code
- A GUI for editing the configuration
The actual documentation generation will be the same for the next release; there are already a lot of things that can be done with the existing features, just by using the capabilities of the configuration settings.
More information on what's planned for GhostDoc after the release (no specific date yet).
-
Introducing: GhostDoc
[2004-08-22] As this old post has a pretty high Google rating, I'd like to emphasise that it does not contain up-to-date information. If you are interested in GhostDoc, I'd suggest to take a look at the GhostDoc homepage./// <summary>
/// GhostDoc is a VS.Net macro for automatically generating those
/// parts of a C# doc comment that can be deduced from the name
/// and/or type of a method or property.
/// </summary>In my previous post I already mentioned my current project "GhostDoc"; now a first public release is ready. Before I start explaining a lot of things, why don't just watch the small video showing GhostDoc in action (400k).
Now here's the basic idea for GhostDoc: When writing documentation comments (e.g. for an API) I tend to use "documentation patterns", e.g. the infamous "Gets or sets a value indicating whether" for a boolean property. And my summary often consists of what I call "method/property name as a sentence plus X", with X being something that adds some value. Example: if I have e.g. a method "void CreateDocumentBuffer(int bytes)", I write "Creates the document buffer with the specified size in bytes". Of this sentence, typing the words "Creates the document buffer" is pretty brainless labor (because I already typed the method name). So wouldn't it be cool if some macro would do that for me? Well, that's what GhostDoc is about; or to be exact, what the very first version started with.
GhostDoc works the following way: When you hit a hotkey inside the c# source editor of VS.Net, GhostDoc finds out what kind of code element the cursor is currently in (e.g. a method), determines the name and (return) type and uses these to look up a matching template for the documentation comment (the templates are stored in an XML configuration file).
The current version of GhostDoc is definitely a "technical preview". It's the result of a macro that grew bigger and bigger until it had to be supported by code in a C# assembly. Now all code is in the assembly and the macro consists only of a single method call. So GhostDoc is not (yet) a VS.Net addin. It's pretty stable; stable enough for me to use it both at home and at work. The good thing is that because of the way GhostDoc works, it simply can't mess up your source code (unless VS.Net has some serious problems) and undo works nicely, too.
Installation
The installation is currently completely manual
- Download GhostDoc_0.5.zip
- Unzip it to a directory of your choice
- Move "Weigelt.Tools.GhostDoc.dll" and "Weigelt.Tools.GhostDoc.Texts.xml" to the "Common7\IDE\PublicAssemblies" subdirectory inside your VS.Net 2003 installation directory.
- Start Visual Studio and go to the Macros IDE (Tools - Macros - Macros IDE)
- Open the MyMacros node in the project explorer
- Add a reference to the "GhostDoc macro support" assembly (it will appear automatically in the list of the "Add Reference" dialog)
- Right click "MyMacros" and "Add existing item", browsing for the "GhostDoc.vb" file in the directory you unzipped to.
- Save and leave the Macros IDE.
- Open the options dialog (Tools - Options) and define the keyboard bindings; simply type "ghost" in the "show commands containing" input field and you'll see the two commands (I use Ctrl-D for CreateComment, and Ctrl-Shift-D for RefreshConfiguration, but it's completely up to you)
Getting started
Move the cursor into a method or a property without a documentation comment, and hit e.g. Ctrl-D (if a documentation comment already exists, nothing will happen). Note that your code should use .NET naming conventions, otherwise the results can be pretty strange, sometimes even funny. GhostDoc is highly configurable, but in this version the default configuration is far from being finished. Simply take a look into the file "Weigelt.Tools.GhostDoc.Texts.xml" and you'll get an idea how things work.
Known Issues
- As already mentioned, the generated summary can sometimes be pretty off. In many cases, a new rule in the configuration will help - if you add such a rule, please send me a copy.
- There is virtually no user documentation. Some information can be found inside comments in the configuration file.
Plans for the future
- More entries in the configuration file
- A GUI for editing the configuration
- Better installation
- A release including source code, maybe opening a GDN workspace.
- ...
Update 01.12.2003: I'm now working on turning GhostDoc into a Visual Studio .NET addin. So if you're put off by the ugly installation of the current version, you may want wait just a little.
-
Coming soon... GhostDoc
It's pretty late here in Germany, but I cannot go to bed without posting screenshots of the first successful runs of my new VS.Net macro (with a little help from some C# code):
Before:
After:
(Macro called three times)GhostDoc helps writing API documentation by generating the "obvious" parts of the documentation from the identifier names. This is not a replacement for real documentation, but it's a good starting point. The text generation can be customized, here's an excerpt from the configuration file:
<Properties>
<!-- Default -->
<Property>
<summary>
$$NameAsSentence$$
</summary>
</Property>
<!-- Default for all boolean properties -->
<Property type="System.Boolean">
<summary>
$$Access$$ a value indicating whether [$$NameAsWords$$]
</summary>
</Property>
<!-- Specific definition for the "Enabled" property -->
<Property type="System.Boolean" name="Enabled">
<summary>
$$Access$$ a value indicating whether this $$Class$$ instance is enabled
</summary>
</Property>
</Properties>I'll have a beta ready soon - stay tuned.
-
ExtractXml: Including Examples in Your API Documentation
When using an API it's always nice to have some examples showing how the different classes and their methods and properties are actually used. Examples usually come in two flavors:
- Full projects (more or less) ready to compile
- Code snippets in the documentation (e.g. generated by NDoc)
Ok, I have to admit that I tend to ignore the full projects. When I need an example to understand something, I'm often looking for quick answers - not easy to get in an example project sometimes showing several concepts at once.
The small examples usually consist of maybe only a few lines of code, that in many cases are not ready to be compiled (at least not without adding some more code). But often these examples are exactly what is required to get quick help, getting straight to the point.
Unfortunately for the API developer, sample code inside the <example> tag of the XML Doc comments is a pain to create and maintain.
- The code has to be tested.
A missing semicolon in an example is definitely not the best way to convince the API users of the quality of the documentation. And if the code shows something that simply is not possible (e.g. a feature that was planned, but later cut from the release), people get really upset. - The code has to reflect the current state of the API.
As the work on the API continues, sample code copied and pasted into your documentation has to be updated over and over again.
With a large number of examples, the only real solution is to take sample code from actually working code (e.g. from some of the NUnit tests for the API) and to automatically insert these code snippets into the doc comments.
The <include> tag of the XML doc comments allows the inclusion of XML fragments stored in an external XML file. I wrote a small command line tool (ExtractXml) that generates an XML file from a ".cs" file. Read more about it here.
-
ExtractXml: Including Examples in Your API Documentation
When using an API it's always nice to have some examples showing how the different classes and their methods and properties are actually used. Examples usually come in two flavors:
- Full projects (more or less) ready to compile
- Code snippets in the documentation (e.g. generated by NDoc)
Ok, I have to admit that I tend to ignore the full projects. When I need an example to understand something, I'm often looking for quick answers - not easy to get in an example project sometimes showing several concepts at once.
The small examples usually consist of maybe only a few lines of code, that in many cases are not ready to be compiled (at least not without adding some more code). But often these examples are exactly what is required to get quick help, getting straight to the point.
Unfortunately for the API developer, sample code inside the <example> tag of the XML Doc comments is a pain to create and maintain.
- The code has to be tested.
A missing semicolon in an example is definitely not the best way to convince the API users of the quality of the documentation. And if the code shows something that simply is not possible (e.g. a feature that was planned, but later cut from the release), people get really upset. - The code has to reflect the current state of the API.
As the work on the API continues, sample code copied and pasted into your documentation has to be updated over and over again.
With a large number of examples, the only real solution is to take sample code from actually working code (e.g. from some of the NUnit tests for the API) and to automatically insert these code snippets into the doc comments.
The <include> tag of the XML doc comments allows the inclusion of XML fragments stored in an external XML file. I wrote a small command line tool (ExtractXml, download here) that generates an XML file from a ".cs" file. It's pretty much of a quick hack without much planning, but it's good enough for my own work.
This is how it works: the ".cs" file is decorated with "////" comments containing XML processing instructions and tags:
//// <?xml version="1.0" encoding="utf-16" ?> //// <Documentation><!-- using System; using System.Diagnostics; namespace ExtractXml { /// <summary> /// Summary description for Example. /// </summary> public class Example { /// <summary> /// Does something really cool. /// </summary> public void DoSomething() { //// --> //// <Example name="HowToAddStrings"> //// <example> //// <b>How add two strings:</b> //// <code> //// <![CDATA[ string str1="Hello "; string str2="World"; string str3=str1+str2; //// ]]></code> //// </example> //// </Example><!-- Debug.WriteLine(str3); } } } //// --></Documentation>
The tool is called like this:
ExtractXml inputFile.cs outputFile.xml
It removes the "////" comments, loads the resulting text into an XML document (detecting errors in the XML), removes the XML "<!-- ... -->" comments and saves the result:
<?xml version="1.0" encoding="utf-16"?> <Documentation> <Example name="HowToAddStrings"> <example> <b>How add two strings:</b> <code> <![CDATA[ string str1="Hello "; string str2="World"; string str3=str1+str2; ]]></code> </example> </Example> </Documentation>
In your API code you reference the example like this:
/// ... /// <include file="Example.xml" path="Documentation/Example[@name='HowToAddStrings']/*"/> /// ...
Note that you are completely free regarding the structure of the XML tags, as long as the result is a valid XML document.
Download
The download contains the executable (for .NET Framework 1.1) and the source (project file for VS.Net 2003).
Recommended Links
- XML Comments Let You Build Documentation Directly From Your Visual Studio .NET Source Files (MSDN Magazine, June 2002)
- <include> (C# programmer's reference)
-
Better Keyboard Support for #region ... #endregion
Of all the elements that can be collapsed in the VS.Net editor (namespaces, classes, methods, comments, ...), only regions are of actual importance for my everyday work. Here are three macros that are quite helpful for expanding/collapsing regions using the keyboard:
Macro Purpose Keys (example) ExpandAllRegions expands all regions in the current document Ctrl-Shift-'+' CollapseAllRegions collapses all regions in the current document Ctrl-Shift-'-' ToggleParentRegion expands/collapses the region the cursor is currently in Ctrl-'+' and Ctrl-'-' Installation:
- Open the Macros IDE (Tools - Macros - Macros IDE)
- Create a new module, replace the code in the created file with the posted source code, save file
- Assign keys in VS.Net (Tools - Options - Keyboard).
Tip: type "Region" in the input field under "show commands containing" to limit the list of shown commands (don't laugh, it's always amazing how many people don't know about this rather obvious feature...)
Known Issues
- The code runs fine in VS.Net 2003; in VS.Net 2002 I had strange problems with "Selection.FindText" not working reliably.
- ToggleParentRegion is "good enough", but not perfect; e.g. when the cursor is at the end of a line containing a collapsed region, it won't expand the region, but move the cursor to the start of the line (where a second invocation will work fine).
- (Update 14.08.2003): The macro ExpandAllRegions does not work in VB.Net (see comments below). The macro ToggleParentRegion didn't work correctly, either, but could be fixed (see "Updated" comment in code).
Source Code
Imports EnvDTE
Imports System.Diagnostics
' Macros for improving keyboard support for "#region ... #endregion"
Public Module RegionTools
' Toggles the current region surrounding the cursor
Sub ToggleParentRegion()
Dim objSelection As TextSelection
objSelection = DTE.ActiveDocument.Selection
Dim objPosition As EnvDTE.TextPoint
objPosition = objSelection.AnchorPoint
objSelection.SelectLine()
If (InStr(objSelection.Text.ToLower(), "#region") <> 0) Then ' Updated 14.08.2003
objSelection.MoveToPoint(objPosition)
DTE.ExecuteCommand("Edit.ToggleOutliningExpansion")
DTE.ActiveDocument.Selection.StartOfLine(vsStartOfLineOptions.vsStartOfLineOptionsFirstColumn)
ElseIf (objSelection.FindText("#region", vsFindOptions.vsFindOptionsBackwards)) Then
DTE.ActiveDocument.Selection.StartOfLine(vsStartOfLineOptions.vsStartOfLineOptionsFirstColumn)
DTE.ExecuteCommand("Edit.ToggleOutliningExpansion")
DTE.ActiveDocument.Selection.StartOfLine(vsStartOfLineOptions.vsStartOfLineOptionsFirstColumn)
Else
objSelection.MoveToPoint(objPosition)
End If
End Sub
' Expands all regions in the current document
Sub ExpandAllRegions()
DTE.ExecuteCommand("Edit.StopOutlining")
DTE.ExecuteCommand("Edit.StartAutomaticOutlining")
End Sub
' Collapses all regions in the current document
Sub CollapseAllRegions()
ExpandAllRegions()
Dim objSelection As TextSelection
objSelection = DTE.ActiveDocument.Selection
objSelection.StartOfDocument()
While (objSelection.FindText("#region"))
objSelection.StartOfLine(vsStartOfLineOptions.vsStartOfLineOptionsFirstColumn)
DTE.ExecuteCommand("Edit.ToggleOutliningExpansion")
objSelection.StartOfDocument()
End While
DTE.ActiveDocument.Selection.StartOfDocument()
End Sub
End Module