In addition to blogging, I'm also using Twitter. Follow me @matthawley
This post is long overdue, but as I'm preparing the v1.3 release of WikiPlex and working on documentation (yes, I did say documentation) I realized that another extension point with WikiPlex was not covered. This last, important (but rarely used) part is extending with Scope Augmenters. Scope Augmenters allow you to post process the collection of scopes to further augment, or insert/remove, new scopes prior to being rendered. WikiPlex comes with 3 out-of-the-box Scope Augmenters that it uses for indentation, tables, and lists. For reference, I'll be explaining how and why the IndentationScopeAugmenter was created.
Why Its Needed
The IndentationMacro allows for an arbitrary indentation level indicated by the number of colons that are placed a the beginning of the line. Let's take a look at the primary macro rule:
new MacroRule(@"(^:+\s)[^\r\n]+((?:\r\n)?)$",
new Dictionary<int, string>
{
{1, ScopeName.IndentationBegin},
{2, ScopeName.IndentationEnd}
});
As you can see, we're capturing any number of colons at the beginning, but our ending scope knows nothing of the how many defined levels there are. If you can imagine, knowing nothing about your the beginning scope when rendering to correctly render the ending is not a trivial task without context. This is the exact reason a Scope Augmenter is used, it has that context.
Ultimately, we would like the input
:: I am content
to be rendered as
<blockquote><blockquote>I am content</blockquote></blockquote>
Create a Scope Augmenter
Scope Augmenters can be as easy as you need to make it, but can also be fairly difficult - point of example, the supplied ListScopeAugmenter requires a complex algorithm to correctly identify levels, nested levels, and determining when to start new lists. When creating a Scope Augmenter, it will take in the associated macro, the current list of scopes, and the current content returning a new list of scopes to be rendered. In your solution, create a class called IndentationScopeAugmenter and extend it from WikIPlex.Parsing.IScopeAugmenter. You'll then implement the Augment method.
1: public IList<Scope> Augment(IMacro macro, IList<Scope> capturedScopes, string content)
2: {
3: var augmentedScopes = new List<Scope>(capturedScopes.Count);
4:
5: int insertAt = 0;
6: for (int i = 0; i < capturedScopes.Count; i = i + 2)
7: {
8: Scope begin = capturedScopes[i];
9: Scope end = capturedScopes[i + 1];
10:
11: string beginContent = content.Substring(begin.Index, begin.Length);
12: int depth = Utility.CountChars(':', beginContent);
13:
14: for (int j = 0; j < depth; j++)
15: {
16: augmentedScopes.Insert(insertAt, begin);
17: augmentedScopes.Add(end);
18: }
19:
20: insertAt = augmentedScopes.Count;
21: }
22:
23: return augmentedScopes;
24: }
The Indentation begin / end scopes always come in a sequential pair, which is why I'm able to grab the begin and end scope in lines 8 and 9. Next, you'll see that we need to determine the depth to indent, so we grab the beginning content (which ultimately will be a string containing only colons). In line 12, we count the number of colons there are, which gives us our depth count. Lines 14 - 18 are adding the N-1 listing of IndentationBegin and IndentationEnd scopes. The method then returns this, newly augmented, list of scopes. Basically the augmentation goes from
ScopeName.IndentationBegin,
ScopeName.IndentationEnd
to
ScopeName.IndentationBegin,
ScopeName.IndentationBegin,
ScopeName.IndentationEnd,
ScopeName.IndentationEnd
Registering a Scope Augmenter
Just as registering macros and renderers, there is (only) a static endpoint. Since augmenters should not rely on runtime dependencies, there is no runtime equivalent of using scope augmenters. When you register a Scope Augmenter, it is always associated with a single macro type, and during parsing, the WikiPlex parser will query for the Scope Augmenter that is associated with the current macro being used. To register your Scope Augmenter, have the following code in your application startup method
ScopeAugmenters.Register<IndentationMacro, IndentationScopeMacro>();
When you call the WikiEngine.Render("content"), it will automatically pick up all registered Scope Augmenters and use them during parsing.
Summary
You now have a fully functioning macro / augmenter / renderer that will take an arbitrary depth and have it render correctly. As previously stated, WikiPlex also ships two other Scope Augmenters, ListScopeAugmenter and TableScopeAugmenter, which have a bit more logic associated with them. While Scope Augmenters allow you to manipulate the list of scopes prior to rendering, they should only be used in certain situations in which you cannot capture the correct set of conditions or are unable to contextually determine rendering based on separate scopes.
Download WikiPlex now!
ea71e045-faf0-4c80-997e-df3ae368b56b|1|5.0