DocNet Markdown extensions

Docnet defines the following markdown extensions to make writing documentation easier.

Alert boxes

To quickly define alert boxes, Docnet defines the @alert element. Three types of alerts are defined: danger (displayed in red), warning or important (displayed in yellow) and info or neutral, which is displayed in blue. You specify the type of the alert after the @alert statement using @alert name. Close the @alert with @end.

Below are examples for each alert box and the markdown used to create them.

The markdown:

@alert danger
This is a dangerous text, it will be displayed in a danger alert box!
@end

results in

Danger!

This is a dangerous text, it will be displayed in a danger alert box!

The markdown:

@alert warning
This is a warning text, it will be displayed in a warning alert box!
@end

results in

Warning!

This is a warning text, it will be displayed in a warning alert box!

The markdown:

@alert important
This is an important text, it will be displayed in a warning/important alert box!
@end

results in

Important!

This is an important text, it will be displayed in a warning/important alert box!

The markdown:

@alert info
This is an info text, it will be displayed in an info alert box!
@end

Results in

Info

This is an info text, it will be displayed in an info alert box!

The markdown:

@alert tip
This is a tip! It will be displayed in a tip alert box!
@end

Results in

Tip

This is a tip! It will be displayed in a tip alert box!

Font Awesome icons

To specify a font-awesome icon, use @fa-iconname, where iconname is the name of the font-awesome icon.

Example: To specify the font awesome icon for GitHub, use @fa-github, which will result in:

Tabs

It's very easy with Docnet to add a tab control with one or more tabs to the HTML with a simple set of markdown statements. The tab statements are converted into pure CSS3/HTML tabs, based on the work of Joseph Fusco.

To start a Tab control, start with @tabs and end the tabs definition with @endtabs. Between those two statements, which each need to be suffixed with a newline, you define one or more tabs using @tab followed by the label text for that tab, followed by a newline. End your tab contents with @end.

The following example shows two tabs, one with label 'First Tab' and one with 'Second Tab':

@tabs
@tab First Tab
This is the text for the first tab. It's nothing special

As you can see, it can deal with newlines as well. 
@end
@tab Second Tab
Now, the second tab however is very interesting. At least let's pretend it is!
@end
@endtabs

will result in:

This is the text for the first tab. It's nothing special

As you can see, it can deal with newlines as well.

Now, the second tab however is very interesting. At least let's pretend it is!

Snippets

You can include snippets from other files as fenced code blocks using the directive @snippet which has the following syntax:

@snippet language [file specification] pattern

Here, language can be one of cs, txt or xml. If an unknown language is specified, txt is chosen. Pattern is used to determine which part of the file specified between [] is to be embedded at the spot of the @snippet fragment. This code is based on Projbook's extractor feature and follows the same pattern system.

Below, the method GenerateToCFragment is embedded in a C# fenced code block. This method is a DocNet method and is obtained directly from the source code. This shows the @snippet feature's power as it keeps the documentation in sync with the embedded code without the necessity of updating things.

The following snippet, if the DocNet sourcecode is located at the spot reachable by the path below:

@snippet cs [../../DocNet/src/DocNet/NavigationLevel.cs] GenerateToCFragment

will result in:

/// <summary>
/// Generates the ToC fragment for this element, which can either be a simple line or a full expanded menu.
/// </summary>
/// <param name="navigatedPath">The navigated path to the current element, which doesn't necessarily have to be this element.</param>
/// <param name="relativePathToRoot">The relative path back to the URL root, e.g. ../.., so it can be used for links to elements in this path.</param>
/// <param name="navigationContext">The navigation context.</param>
/// <returns></returns>
public override string GenerateToCFragment(NavigatedPath navigatedPath, string relativePathToRoot, NavigationContext navigationContext)
{
    var fragments = new List<string>();
    if (!this.IsRoot)
    {
        fragments.Add("<li class=\"tocentry\">");
    }
    if (navigatedPath.Contains(this))
    {
        // we're expanded. If we're not root and on the top of the navigated path stack, our index page is the page we're currently generating the ToC for, so 
        // we have to mark the entry as 'current'
        if (navigatedPath.Peek() == this && !this.IsRoot)
        {
            fragments.Add("<ul class=\"current\">");
        }
        else
        {
            fragments.Add("<ul>");
        }

        // first render the level header, which is the index element, if present or a label. The root always has an __index element otherwise we'd have stopped at load.
        var elementStartTag = "<li><span class=\"navigationgroup\"><i class=\"fa fa-caret-down\"></i> ";
        var indexElement = this.GetIndexElement(navigationContext);
        if (indexElement == null)
        {
            fragments.Add(string.Format("{0}{1}</span></li>", elementStartTag, this.Name));
        }
        else
        {
            if (this.IsRoot)
            {
                fragments.Add(indexElement.PerformGenerateToCFragment(navigatedPath, relativePathToRoot, navigationContext));
            }
            else
            {
                fragments.Add(string.Format("{0}<a href=\"{1}{2}\">{3}</a></span></li>", 
                    elementStartTag, relativePathToRoot, indexElement.GetFinalTargetUrl(navigationContext), this.Name));
            }
        }
        // then the elements in the container. Index elements are skipped here.
        foreach (var element in this.Value)
        {
            fragments.Add(element.GenerateToCFragment(navigatedPath, relativePathToRoot, navigationContext));
        }
        fragments.Add("</ul>");
    }
    else
    {
        // just a link
        fragments.Add(string.Format("<span class=\"navigationgroup\"><i class=\"fa fa-caret-right\"></i> <a href=\"{0}{1}\">{2}</a></span>",
                                    relativePathToRoot, this.GetFinalTargetUrl(navigationContext), this.Name));
    }
    if (!this.IsRoot)
    {
        fragments.Add("</li>");
    }
    return string.Join(Environment.NewLine, fragments.ToArray());
}

Include files

You can include other files in your markdown files using the directive @@include("filename"), where filename is the name of the file to include. The include system isn't recursive. The files to include are read from a special folder, specified under IncludeSource in the docnet.json file. If no IncludeSource directive is specified in the docnet.json file, the folder Includes is assumed.

The directive @@include("somehtmlinclude.htm")

results in the contents of somehtmlinclude.htm being included at the spot where the @@include statement is given, as shown below:

@@include("includedhtml.htm")

You can also include markdown, which is then processed with the other markdown as if it's part of it.

@@include("includedmarkdown.md")