I was looking around for a spell-checker that I could use in a continuous integration process. An ideal candidate would be integrated into Sandcastle. It seems that such a spell-checker is on its way. Unfortunately, I didn’t see any recent news, and, more important, no download links. Besides, I do not need a full-blown editor. All I need is a spell-checker for Visual Studio XML comments, which can be integrated with MSBuild. I figured I can write one myself to meet my limited requirements.
Following is a tutorial on getting the Visual Studio Comments Spell Checker working. (VSCSC; that is a big name for a small tool.)
VSCSC is based on NHunspell, the free spell-checker used in OpenOffice, Firefox, and Chrome. The tool does not use reflection and does not integrate into any development environment.
VSCSC consists of three projects.
VSCSC.Lib contains the
SpellChecker class, which is a wrapper for the
NHunspell library. VSCSC.MSBuild contains the
SpellCheckerTask class, which is an MSBuild Task. VSCSC.Ui is a user interface containing a couple of buttons and textboxes to allow for a quick test of an XML file or debugging. An MSBuild project file is included to demonstrate the usage of the
Also included is
TestLib: a library that contains a documented
Circle class. XML documentation file generated for
TestLib is used as input to VSCSC.
Obtaining NHunspell and Dictionaries
VSCSC download comes with precompiled release of NHunspell 0.9.6.0. Latest versions of the library are available on NHunspell homepage.
The spell-checker requires dictionaries to function. NHunspell works with OpenOffice dictionaries available on the OpenOffice website. Note that the original dictionary and affix file names contain an underscore, such as “en_US.dic”. VSCSC expects dictionaries to be named by their culture name, such as “en-US.dic” and “en-US.aff”. Rename the downloaded dictionary files, replacing the underscore with a hyphen. VSCSC download comes with English(United States) dictionary and affix files.
Visual Studio XML Comments File
To generate XML comments file for a project, open the Build tab of Project Properties and check the ‘XML Documentation file’ checkbox. XML comments file contains multiple ‘member’ elements that describe each member of a class. For example, circle class documentation looks as follows:
VSCSC parses the XML comments file. It searches for a predefined set of elements in each ‘member’ XML element. This set of elements is defined in CommentsSpellChecker\Lib\SpellChecker.cs as:
private List<string> _knownTags = new List<string>
"summary", "remarks", "param", "returns", "exception"
Once such element is found, its contents are parsed for text node types. Any non-text nodes are ignored. As an example, let’s take ‘
summary’ element of
Circle constructor from the screenshot above. It contains the following:
Initializes aa new <see cref="T:TestLib.Circle" />.
<see cref…/> is not a text node and will not be spell-checked.
Building the project produces two DLLs and an executable. The DLLs are the VSCSC library and MSBuild Task. The executable can be used for a quick spell check or testing code changes. It reports errors as follows:
NHunspell implements the
IDisposable interface, and so does the VSCSC wrapper.
Using SpellCheckerTask with MSBuild
SpellCheckerTask is an MSBuild task. It has the following properties:
CultureName. The culture name to use. Optional. Default is “
DictionaryLocation. Directory that contains dictionary and affix files. Optional.
Default is an empty string. Leaving
DictionaryLocation empty instructs
SpellCheckerTask to look for dictionaries in the parent directory of the SpellCheckerTask DLL.
ContinueOnError. Instructs whether spelling mistakes result in build errors or warnings. Optional. Default is ‘
false’, which is to fail the build when spelling errors are found.
Documents. List of comments files to check. Required.
There are two essential steps to take in order to use the
SpellCheckerTask. First, add the
<UsingTask> node to the MSBuild project file to declare the task and its assembly. For example, like this:
Second, call the task inside a target, like this:
CultureName="en-US" DictionaryLocation="E:\test\Dictionaries" ContinueOnError="false" />
XmlCommentFiles above is an
Item, which may include multiple files. In order to create the list of XML comments files, one may use the
FindUnder Task provided in MSBuild Extension Pack. One may adopt a certain naming convention for the XML comments files and search for a specific pattern using the
FindUnder task. For example, like this:
<FindUnder TaskAction="FindFiles" Path="E:\test\TestLib\TestLib\bin\Debug"
<Output ItemName="XmlCommentFiles" TaskParameter="FoundItems"/>
An example MSBuild project file, build.proj, is part of the download. Executing that file yields results similar to the following screenshot:
On a side note, it was rather challenging to get the
SpellCheckerTask going. The original implementation kept failing when executed by MSBuild. It would fail in the
SpellChecker constructor when trying to create an instance of Hunspell. Error message was:
“E:\test\CommentsSpellChecker\MSBuild\build.proj(22,2): error :
Hunspell AMD 64Bit DLL not found:
At first, I got confused into thinking that MSBuild is not able to resolve binary references to NHunspell DLLs. Modifying
AssemblySearchPaths as described in article Resolving Binary References in MSBuild had no effect in my case. After scratching my head a little, I realized that it is not MSBuild that fails to resolve the reference; it is my
SpellChecker class. The problem was in the
AppDomain.BaseDirectory property. That is where assembly resolver probes for assemblies. When called from MSBuild, its value is set to the parent directory of MSBuild.exe, which is C:\Windows\Microsoft.NET\Framework64\v4.0.30319 in my case.
AppDomain.BaseDirectory is a read-only property. My first hope was inheriting from
AppDomainIsolatedTask instead of a
Task. That did nothing for me. I was on the brink of executing the
SpellChecker in a separate
AppDomain just so I could set this property using
AppDomainSetup. Thankfully, Gods were kind to me, and I found a magical incantation that saved the day:
Using SpellCheckerTask with Visual Studio
SpellCheckerTask can be used in Visual Studio’s
AfterBuild event. Right-click the project in Solution Explorer and select ‘Unload Project’ context menu. Once unloaded, right-click the grayed-out ‘ProjectName (unavailable)’ item, and select ‘Edit ProjectName.csproj’.
As with an MSBuild project file, the Visual Studio project file needs to reference the
SpellCheckerTask. It is done using the
UsingTask just like before.
AfterBuild event. The event may be commented out near the end of the file. Uncomment it if necessary, and modify as needed. I suggest something similar to the following example:
<Target Name="CheckSpelling" Condition=" '$(DocumentationFile)' != '' ">
<XmlCommentsFile Include="$(ProjectDir)\$(DocumentationFile)" />
DictionaryLocation="E:\test\Dictionaries" CultureName="en-US" ContinueOnError="true" />
<Target Name="AfterBuild" DependsOnTargets="CheckSpelling">
This looks similar to the MSBuild project file discussed before. The differences are: an extra Target, Condition in the new
CheckSpelling target, and the
The condition verifies that
DocumentationFile property is not empty. This property is defined when user chooses to create the XML Comments file on the Build tab of Project Properties. The condition makes sure that
CheckSpelling target runs only in case there are comments to check.
DocumentationFile combined with
ProjectDir make up the full path to the XML comments file. This full path is assigned to the
XmlCommentsFile item, which is then passed to the
SpellCheckerTask. In case of MSBuild project file, we found XML comments files using the
FindUnder task; in the latter case, we provide full filename instead.
Save the project file, reload the project, and rebuild it. The end result, given the example above, is as follows:
This tool has already found spelling mistakes in my comments. Until I find a Sandcastle-integrated tool, and I hope one appears soon, VSCSC is a decent temporary alternative.
- 6th August, 2010: Initial post