-
Notifications
You must be signed in to change notification settings - Fork 11
Custom Tags
With version 3.0 you can inject your own tokens into Morestachio.
This involes two steps:
- Creating an CustomDocumentItemProvider that is responsible for Parsing and Tokenizing
- Creating an IDocumentItem that will be used to render the Tag or Block
To add your custom Tag(Single statement) or Block(Single statement with Children) you have to inhert from CustomDocumentItemProvider
. The CustomDocumentItemProvider
provides methods for Parsing and Tokenizing. You can use the provided base classes BlockDocumentItemProviderBase
or TagDocumentItemProviderBase
. Those two base classes are implemented to handle most of the common cases.
public class LessCompilerDocumentItemProvider : BlockDocumentItemProviderBase
{
public LessCompilerDocumentItemProvider() : base("#Less", "/Less")
{
}
/// <inheritdoc />
public override IDocumentItem CreateDocumentItem(string tag, string value, TokenPair token, ParserOptions options)
{
return new CompileLessDocumentItem();
}
}
The less compiler block does not take any argument that needs to be parsed and creates an CompileLessDocumentItem
.
The other Part of the process is creating your own DocumentItem that inherts IDocumentItem
.
When implementing this interface you must take care of some points.
- Implement support for Serialization.
- Implement IEquality Support
Support for Serialization is rather optional and if you do not plan to Serialize the document tree you can skip this step.
Serialization includes XML and Binary and is done by 2 steps: For Binary you must provide a constructor that matches this pattern:
public .ctor(SerializationInfo info, StreamingContext context) : base(info, c)
{...}
(don't forget to call the base constructor!)
you should also overwrite the SerializeBinaryCore(SerializationInfo info, StreamingContext context)
method that will be called when serialization happens.
For xml support you can simply overwrite the SerializeXml(XmlWriter writer)
and DeSerializeXml(XmlReader reader)
methods. In the case of XML its worth noting that at the time of calling this methods, the xml element and its attributes are already written and after leaving your method the children of your DocumentItem will be written so do not close the parent tag at this point.
Morestachio has some base classes that it uses for all DocumentItems:
-
DocumentItemBase
handles most of the Serialization work for the base properties likeLocation
-
BlockDocumentItemBase
adds support for children to be added to a document item -
ExpressionDocumentItemBase
contains oneIMorestachioExpression
and handles its serialization (inherts fromDocumentItemBase
) -
ValueDocumentItemBase
contains one string property hand handles its serialization (inherts fromDocumentItemBase
)
As an extra, you can implement the IStringVisitor
interface. This allows your DocumentItem to be parsed by the ToParsableStringDocumentVisitor
/// <summary>
/// Wraps the dotless into an Document provider
/// </summary>
[Serializable]
public class CompileLessDocumentItem : BlockDocumentItemBase, ToParsableStringDocumentVisitor.IStringVisitor
{
/// <summary>
/// Binary serialization ctor
/// </summary>
internal CompileLessDocumentItem() : base(CharacterLocation.Unknown, null)
{
}
/// <summary>
///
/// </summary>
/// <param name="location"></param>
/// <param name="tagTokenOptions"></param>
public CompileLessDocumentItem(CharacterLocation location, IEnumerable<ITokenOption> tagTokenOptions)
: base(location, tagTokenOptions)
{
}
/// <summary>
/// Serialization Constructor
/// </summary>
/// <param name="info"></param>
/// <param name="c"></param>
protected CompileLessDocumentItem(SerializationInfo info, StreamingContext c)
: base(info, c)
{
}
/// <inheritdoc />
public override async ItemExecutionPromise Render(IByteCounterStream outputStream, ContextObject context, ScopeData scopeData)
{
using (var tempStream = outputStream.GetSubStream())
{
await MorestachioDocument.ProcessItemsAndChildren(Children, tempStream, context, scopeData);
var lessCode = tempStream.Read();
outputStream.Write(Less.Parse(lessCode, new DotlessConfiguration()
{
CacheEnabled = false,
}));
}
return Enumerable.Empty<DocumentItemExecution>();
}
/// <inheritdoc />
public override void Accept(IDocumentItemVisitor visitor)
{
visitor.Visit(this);
}
/// <inheritdoc />
public void Render(ToParsableStringDocumentVisitor visitor)
{
visitor.StringBuilder.Append("{{#LESS}}");
visitor.VisitChildren(this);
visitor.StringBuilder.Append("{{/LESS}}");
}
}
Morestachio is an NetStandard 2.0 lib but also wants to support the ValueTask<>
from NetCore. Because of this there are a number of preprocessor variables that switches the normal Task
with ValueTask
. If you only want to support one or the other that is fine just replace ItemExecutionPromise
with ether Task<IEnumerable<DocumentItemExecution>>
or ValueTask<IEnumerable<DocumentItemExecution>>
. if you want to support both like morestachio add this to the top of your cs file:
#if ValueTask
using ItemExecutionPromise = System.Threading.Tasks.ValueTask<System.Collections.Generic.IEnumerable<Morestachio.Document.Contracts.DocumentItemExecution>>;
using Promise = System.Threading.Tasks.ValueTask;
#else
using ItemExecutionPromise = System.Threading.Tasks.Task<System.Collections.Generic.IEnumerable<Morestachio.Document.Contracts.DocumentItemExecution>>;
using Promise = System.Threading.Tasks.Task;
#endif
and add the corresponding PropertyGroups to your csproj:
<PropertyGroup Condition="'$(TargetFramework)' == 'netcoreapp2.1'">
<DefineConstants>ValueTask</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)' == 'netcoreapp2.2'">
<DefineConstants>ValueTask</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)' == 'netcoreapp3.0'">
<DefineConstants>ValueTask; ValueTaskFromResult</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(TargetFramework)' == 'netcoreapp3.1'">
<DefineConstants>ValueTask; ValueTaskFromResult; Span</DefineConstants>
</PropertyGroup>
To enable parsing of your custom document you must add them to the ParserOptionsBuilder
you are using to parse your template by calling ParserOptionsBuilder.AddCustomDocument(new MyCustomDocumentItemProvider())