|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
Contents
Introductionx.doc is short for "External Documentation." It is a Visual Studio (VS) 2005 add-in that provides a means to manage and visualize source-code comment documentation interactively in the IDE. Its main feature is that it provides an easy way to 'externalize' the documentation comments using the documentation XML Good source-code documentation can be very useful and is considered imperative in many commercial development environments. VS provides a very efficient method for placing documentation directly in the code using special comment delimiter tags and embedding the text in special XML tags. An overview of how this works can be found at [1] (listed in the Resources section below). VS2005 now provides even smarter code-completion for the inputting of documentation, as well as Intellisense prompting of XML tag keywords. This makes it very easy for documentation to be directly 'coded' into the source-code files using XML. However, large blocks of documentation delimited with XML tags are cumbersome and difficult to read directly in the code, and can even make the readability of the code itself difficult. The answer is to replace the entire block of documentation with a single If using DisclaimerThe add-in provides comprehensive functionality and parts of the code maybe considered complex. The objective of this article and its accompanying source-code is to provide a fairly complete example of how to write such an add-in and to demonstrate the way the add-in should interact with the VS IDE. It is not guaranteed to be code-complete, nor is the code guaranteed to be bug-free. Although care was taken in the design and coding of the add-in and industry-accepted standards were used in the development, the code should be regarded as demonstrative only, and not necessarily of the quality considered to be "industrial strength." Resources
FeaturesThe figure below shows the x.doc windows within the VS IDE:
x.doc provides two windows:
x.doc provides a menu tool-strip above the Visualizer window. The available commands are described below, as they are ordered on the tool-strip from left to right:
x.doc is completely NDoc compatible, in that the XML files that are generated by VS when you compile with the /doc:file option can be used as-is with NDoc. This applies whether the documentation is read directly from the source-code or from an external file. Installation and usageThe installer package is found in the file xdocsetup.zip. It contains the file setup.exe and setup.msi. To install the add-in, run the setup.exe application and follow the install instructions. The add-in files are installed in the [PersonalFolder]\Visual Studio 2005\Addins folder on your computer. This normally equates to something like C:\Documents and Settings\John\My Documents\Visual Studio 2005\Addins if your computer logon name is "John," and you are running Windows XP. the following files are installed in the add-in folder:
Once installation has completed, VS can be started. The x.doc Visualizer window should appear as a floating window. If the x.doc window is not visible, the VS Add-in Manager window can be used to activate the x.doc add-in. This is done from the Tools/Add-in Manager... menu item of VS. In the Add-in Manager dialog, tick the "Available Add-ins" and "Startup" columns for the x.doc add-in. To ensure that these settings are permanent, open the x.doc.Addin file in a text-editor, and check that the following text is present: ..
<Extensibility ..>
<Addin>
<LoadBehavior>1</LoadBehavior>
<CommandPreload>1</CommandPreload>
</Addin>
</Extensibility>
..
The above settings indicate that the add-in is available and that it is loaded when VS is started. The zip file xdocsource.zip contains the source-code for the add-in and comprises a C# project -- x.doc.csproj, which is the build project for the x.doc add-in -- and Setup.vdproj, which is the x.doc installer project. In order to debug the add-in, read the text-file readme.txt in the x.doc project and follow the listed instructions. The VS solution file x.doc.sln contains these projects and provides the complete build environment for x.doc. The codeThis section highlights important implementation details of the x.doc add-in. The more obvious and simpler code sections are not discussed and should be easy to follow in the source-code files. Add-in componentsThe x.doc add-in window is implemented as a VS tool window that can be docked and floated exactly as the built-in tool windows of the IDE. It is defined in a class private Window _windowToolWindow;
..
..
// Create the tool window
object programmableObject = null;
string guidstr = "{FCBF911C-17BA-4260-88A5-9BD84C5C1188}";
EnvDTE80.Windows2 windows2
= (EnvDTE80.Windows2)_applicationDTE2Object.Windows;
System.Reflection.Assembly asm
= System.Reflection.Assembly.GetExecutingAssembly();
_windowToolWindow = windows2.CreateToolWindow2(_addInInstance, asm.Location,
x.doc.ToolWindowControl.ToolWindowClassName,
x.doc.ToolWindowControl.ToolWindowName,
guidstr, ref programmableObject);
// Set the picture displayed when the window is tab docked
_windowToolWindow.SetTabPicture(Resource.x_doc_tool_bmp.GetHbitmap());
_windowToolWindow.Visible = true;
..
The x.doc ToolWindow consists of a // Create the output window
OutputWindow outputWindow = (OutputWindow)_applicationDTE2Object.Windows.Item(
Constants.vsWindowKindOutput).Object;
// Add the x.doc output window as a selectable pane.
_outputWindowPane = outputWindow.OutputWindowPanes.Add(
x.doc.ToolWindowControl.ToolWindowName);
Text editor caret tracking and DTE eventsThe most important function of the add-in is to track the caret (text cursor) in the VS active text-editor window as the user clicks or types in the window. This is done in order to interactively display the rendered documentation XML in the Visualizer window. The way this works is as follows:
The code below shows the control method that provides the above functionality: private void UpdateCaretResponse(bool updateUI, bool ignoreElapsedTime)
{
// Get the caret information from the DTE.
TextCaretTracker.CaretInformation caretInfo = _caretTracker.TrackCaret();
// If the caret has changed:
if (_lastCaretInfoUsed != caretInfo)
{
// Set the 'Changed' state.
_caretChangeIndicator = CaretChangeState.Changed;
if (caretInfo.ActiveDocument != _lastCaretInfoUsed.ActiveDocument)
{
// If the document has changed,
//switch the external documentation set.
OnCaretDocumentChange(caretInfo);
}
// Set the last update ticks.
_lastCaretInfoTicks = DateTime.Now.Ticks;
}
else
{
// If the caret info is in the 'Changed' state:
if (_caretChangeIndicator == CaretChangeState.Changed)
{
// Check if the user has paused for 'PauseInterval' msecs.
if ((DateTime.Now.Ticks - _lastCaretInfoTicks
>= PauseInterval * 10000) ||
(ignoreElapsedTime))
{
// User has paused: Analyse and visualize documentation.
OnCaretChange(caretInfo);
// Set state to 'Unchanged'.
_caretChangeIndicator = CaretChangeState.Unchanged;
if (updateUI)
{
// Update the x.doc window state.
UpdateWindowState(caretInfo, _lastDocumentation);
}
// Set the last update ticks.
_lastCaretInfoTicks = DateTime.Now.Ticks;
}
}
}
// Save the current caret info.
_lastCaretInfoUsed.CopyFrom(caretInfo);
}
The Besides using polling to ascertain the caret position, x.doc also handles some events generated by the VS extensibility interface. Again, code from [4] was used from the EventWatcher project in order to do this. The following code illustrates the setting of event handler delegates: ..
// Get the DTE Events object.
EnvDTE.Events events = _applicationDTE2Object.Events;
// Get the WindowEvents object.
_windowsEvents = (EnvDTE.WindowEvents)events.get_WindowEvents(null);
// Set the WindowActivated event delegate.
_windowsEvents.WindowActivated +=
new _dispWindowEvents_WindowActivatedEventHandler(OnDTEWindowActivated);
// Get the DocumentEvents object.
_documentEvents = (EnvDTE.DocumentEvents)events.get_DocumentEvents(null);
// Set the DocumentClosing event delegate.
_documentEvents.DocumentClosing +=
new _dispDocumentEvents_DocumentClosingEventHandler(OnDTEDocumentClosing);
// Set the DocumentSaved event delegate.
_documentEvents.DocumentSaved +=
new _dispDocumentEvents_DocumentSavedEventHandler(OnDTEDocumentSaved);
// Get the EnvDTE80.TextDocumentKeyPressEvents object.
_textDocumentKeyPressEvents =
((EnvDTE80.Events2)events).get_TextDocumentKeyPressEvents(null);
// Set the AfterKeyPress event delegate.
_textDocumentKeyPressEvents.AfterKeyPress +=
new _dispTextDocumentKeyPressEvents_AfterKeyPressEventHandler(
OnDTEAfterKeyPress);
..
The private void OnDTEWindowActivated(EnvDTE.Window gotFocus,
EnvDTE.Window lostFocus)
{
// If the any other window gains focus, force a context switch.
if (gotFocus.Caption != x.doc.ToolWindowControl.ToolWindowName)
{
ForceCaretUpdate();
}
}
The A similar response method is used for the The Visualizer windowOnce the XML content of the active documentation is extracted, it is transformed into HTML: public string RenderHtml(string xml)
{
if (xml == null) return null;
string html = null;
if (styleIndex != -1)
{
// Retrieve the style as selected.
RenderStyler style = renderStyles[styleIndex];
const string uriPrefix = "file:///";
// Wrap the XML with the root member tag:
// <x.member> + xml + </x.member>
xml = SetXmlRootTag(xml, MemberTagName);
StringReader stringReader = new StringReader(xml);
StreamReader streamReader = null;
StringWriter stringWriter = null;
XmlTextReader xmlReader = null;
try
{
// Create new xpath document.
XPathDocument xpathDocument = new XPathDocument(stringReader);
// Create new compiled xsl transform.
XslCompiledTransform xslTransform = new XslCompiledTransform();
// Load the transform from the .xsl file.
streamReader = new StreamReader(style.XslPath);
xmlReader = new XmlTextReader(streamReader);
xslTransform.Load(xmlReader);
// Create a .css file-path parameter to be passed to the transform.
XsltArgumentList args = new XsltArgumentList();
args.AddParam("csspath", string.Empty, uriPrefix + style.CssPath);
// Create a navigator and transform the XML.
XPathNavigator xpathNavigator = xpathDocument.CreateNavigator();
// Write the HTML to a string.
stringWriter = new StringWriter();
xslTransform.Transform(xpathNavigator, args, stringWriter);
html = stringWriter.ToString();
}
catch (Exception e) {..}
finally {..}
}
else {..}
return html;
}
The path parameter is passed to the transform, where it is used to generate the link to the .css file as the following excerpt from the .xsl file shows: ..
<xsl:param name="csspath"/>
..
<xsl:template match="x.member">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<link rel="stylesheet" type="text/css" href="{$csspath}"/>
</head>
..
</html>
</xsl:template>
..
Since a new HTML page is created for each documentation member, the template should match the this.wbDoc.DocumentText = html;
// wbDoc is of type System.Windows.Forms.WebBrowser
One of the problems with the .xsl is that it generates live links if link-generating documentation tags like ..
this.wbDoc.Navigating +=
new System.Windows.Forms.
WebBrowserNavigatingEventHandler(this.wbDoc_Navigating);
..
..
private void wbDoc_Navigating(object sender, WebBrowserNavigatingEventArgs e)
{
// If the Url is not set through the DocumentText property,
// cancel the navigation. This means that the HTML links are
// disabled in the WebBrowser control.
if(e.Url.AbsolutePath != "blank") e.Cancel = true;
}
The Output windowBy by double-clicking the error line, the VS IDE output window can be used to navigate from a reported error in the output window to the text-position in the text-editor document where the error was caused. This is used by the VS Build window. x.doc provides the same feature to report XML errors, XML-to-HTML transformation errors and other errors that are produced as a result of the x.doc add-in causing an exception. The format of such an error text line is as follows: {full-filepath}(line,position): {message-text}
where full-path is the full file-path of the source-code file,
line is the source-code file line-number in the text-editor,
position is the character position offset in the line, and
message-text is the actual error message.
Example:
D:\Development\x.doc\Samples\DocumentationSample\
DocumentationSample.cs(13,31):
'value' is an unexpected token. The expected token is '>'.
Line 13, position 37.
The errors are reported with line and position numbers based on the offset from the start of the XML string extracted from the documentation block. The External documentationA powerful feature of documentation comments is that the documentation can be saved in an external XML text file, and referenced using a single comment line from the source-code file. This feature is built into x.doc and the reference works as follows, shown in a normal comment block for C#: /// <include file='xdoc\{code-file-name}.xdoc'
// path='x.doc/x.member[@name="{code-element-name}"]/*'/>
The
An excerpt is shown from a typical external documentation file: <?xml version="1.0" encoding="utf-8"?>
<x.doc>
..
..
<x.member name="x.doc.CSharp.Samples.DocumentationSample.DocumentationSample">
<summary>
Initializes a new instance of a
<c>DocumentationSample</c> type.
</summary>
<example>
The following is an example of initializing a
<c>DocumentationSample</c> type:
<code>
// Create the type.
DocumentationSample ds = new DocumentationSample();
if (null == ds)
return;
return ds.SomeMethod("someString");
</code>
</example>
</x.member>
..
..
</x.doc>
The required <include file='xdoc\DocumentationSample.xdoc' path='x.doc/x.member[
@name="x.doc.CSharp.Samples.DocumentationSample.DocumentationSample"]/*'/>
From the element defined above, it can be seen that the file-name of the source-code file is DocumentationSample and that the external documentation file is DocumentationSample.xdoc. The most interesting part of the automatic {code-namespace-name}.{
declared-type-name}[.{[method-name] |
[property-name] | [event-name] | [enum-value] | ...}]
This construct is the formal or full-name of the construct being commented. It can obtained from the Another interesting problem was encountered when replacing the ..
// Do a copy/paste of the replacing code
// to avoid typematic formatting of documentation
// comments, which happens when setting the Text
// property or doing a DestructiveInsert.
Clipboard.SetData(DataFormats.Text, commentSection);
selection.Paste();
..
Update wish-listIf time permitted, the following may have also worked:
Release history17 May 2007: Version 1.0.1
17 May 2006: Version 1.0.1
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||