Mal.OnyxTemplate
1.3.3-alpha
See the version list below for details.
dotnet add package Mal.OnyxTemplate --version 1.3.3-alpha
NuGet\Install-Package Mal.OnyxTemplate -Version 1.3.3-alpha
<PackageReference Include="Mal.OnyxTemplate" Version="1.3.3-alpha" />
paket add Mal.OnyxTemplate --version 1.3.3-alpha
#r "nuget: Mal.OnyxTemplate, 1.3.3-alpha"
// Install Mal.OnyxTemplate as a Cake Addin #addin nuget:?package=Mal.OnyxTemplate&version=1.3.3-alpha&prerelease // Install Mal.OnyxTemplate as a Cake Tool #tool nuget:?package=Mal.OnyxTemplate&version=1.3.3-alpha&prerelease
Onyx Template
Onyx Template is a very simple Source Generator powered runtime text template system.
- Note: Only really tested with modern project format + modern nuget. No guarantees of
proper function in old .NET Framework projects etc.
Usage:
- Add the nuget package Mal.OnyxTemplate.
- Add a file with an
.onyx
extension.
(It should be added automatically as AdditionalFiles. If not - change it to that).
The macros are fairly simple.
{{ YourMacroName }}
Let's create our first .onyx file. Let's call it WelcomeText.onyx
.
Hello, {{ UserName }}, welcome to the show.
The source generator will have generated a class for you. Every macro you make in your .onyx file will get its own property string (or boolean) you can set for that macro.
... But I need it to be public / $template
There is a special configuration macro you can add to the very top of your .onyx file
to configure it. Let's mke our WelcomeText macro generate a public
base class
instead of the default internal
.
{{ $template public }}
Hello, {{ UserName }}, welcome to the show.
That's all it takes.
Repeating data / $foreach
To generate lists of data you need to utilize the special $foreach
macro:
Animals:
{{ $foreach animal in animals }}
{{ animal }}
{{ $next }}
Providing we give it the following data provider:
var template = new Template();
template.Animals = new[] {
"Dog",
"Cat",
"Tiger",
"Bear"
}
The result will be
Animals:
Dog
Cat
Tiger
Bear
You may reference a field in an outer loop by prefixing your field with a period .
:
{{ $foreach animal in animals }}
{{ animal }}:
{{ $foreach name in names }}
- {{ name }}{{ $if ..showSurNames}}{{ surname }}{{ $end }}
{{ $next }}
{{ $next }}
then, if we provide the following data:
var template = new Template();
template.Animals = new[]
{
new Template.AnimalItem
{
Type = "Dog",
Names = new[] {
new Template.NameItem
{
Name = "Rex",
Surname = "The Dog"
},
new Template.NameItem
{
Name = "Fido",
Surname = "Fidocious"
}
}
},
new Template.AnimalItem
{
Type = "Cat",
Names = new[] {
new Template.NameItem
{
Name = "Whiskers",
Surname = "Wheezy"
},
new Template.NameItem
{
Name = "Fluffy",
Surname = "McFluffington"
}
}
}
};
Then we get two different results, based on whether:
template.ShowSurNames = true;
==>
Dog:
- Rex The Dog
- Fido Fidocious
Cat:
- Whiskers Wheezy
- Fluffy McFluffington
or
template.ShowSurNames = false;
==>
Dog:
- Rex
- Fido
Cat:
- Whiskers
- Fluffy
Note that we're referencing the showSurNames
field in the root of the template,
by prefixing it with ..
. Each .
jumps a single level up in the macro hierarchy,
so in this example we are skipping over the animal
loop and going straight to the
top.
See also item state conditionals
Dealing with multiline macros
But what if the animal
macro returned multiple lines? Let's try it:
var template = new Template();
template.Animals = new[] {
"Dog\n- Canidae",
"Cat\n- Felidae",
"Tiger\n- Felidae",
"Bear\n- Ursidae"
}
Result:
Animals:
Dog
- Canidae
Cat
- Felidae
Tiger
- Felidae
Bear
- Ursidae
So yes, that worked, but... it's not exactly what we're after, is it. We'd prefer it if the new lines were aligned with the first macro.
There's two ways we can solve this problem. We can set an indented
flag
in the template header
{{ $template indented }}
or we can tell the generator that only this particular use of the macro needs to be indented:
{{ animal:indent }}
Result:
Animals:
Dog
- Canidae
Cat
- Felidae
Tiger
- Felidae
Bear
- Ursidae
That's better!
Complex macros
We could make it even better though. Rather than formatting the whole thing in the data provider itself we could do this:
Animals:
{{ $foreach animal in animals }}
Animal Name: {{ name }}
Animal Family: {{ type }}
{{ $next }}
Now this is detected to be a complex macro. This means we'll have to change our data provider again:
var template = new Template();
template.Animals = new[]
{
new Template.Animal { Name = "Dog", Type = "Canidae" },
new Template.Animal { Name = "Cat", Type = "Felidae" },
new Template.Animal { Name = "Tiger", Type = "Felidae" },
new Template.Animal { Name = "Bear", Type = "Ursidae" }
}
The generator has produced an item class we can use to produce data for the complex macro.
Now, our results are:
Animals:
Animal Name: Dog
Animal Family: Canidae
Animal Name: Cat
Animal Family: Felidae
Animal Name: Tiger
Animal Family: Felidae
Animal Name: Bear
Animal Family: Ursidae
Conditionals
Sometimes you want templates that may or may not generate parts of itself
based on a condition. We can do that by using the {{ $if }}
, {{ $elseif }}
,
{{ $else }}
macros.
Mr. Jenkins, you're {{ $if isfired }}fired!{{ $else }}hired.{{ $end }}
You can invert a condition by including the not
modifier
Mr. Jenkins, you're {{ $if not isfired }}hired.{{ $else }}fired!{{ $end }}
Item state conditionals
You can query a set of states about items in a $foreach
by using a specialized set of fields:
$first
The item is the first item of the list.$last
The item is the last item of the list.$middle
The item is neither the first nor the last item in the list.$odd
The item is of odd numbered index.$even
The item is of even numbered index.
{{ $foreach item in list }}
{{ item }}{{ $if not $last }},{{ $end }}
{{ $next}}
And that's it
This library was primarily designed 1. to help me get better at making source generators and 2. because I needed something simple to make my other source generators cleaner, and without all the StringBuilder shenanigans all over the place.
Known Issue with Jetbrains Rider
There seems to be some kind of caching going on with Rider which prevents it from detecting changes in the .onyx files now and again. I have yet to find a solution for it. A Rebuild All forces the issue.
Watch your whitespace
If you have whitespace before or after the {{ $template }}
,
{{ $foreach item in source }}
and {{ $next }}
macros, you might get
newlines you don't want.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
.NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
.NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
.NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
MonoAndroid | monoandroid was computed. |
MonoMac | monomac was computed. |
MonoTouch | monotouch was computed. |
Tizen | tizen40 was computed. tizen60 was computed. |
Xamarin.iOS | xamarinios was computed. |
Xamarin.Mac | xamarinmac was computed. |
Xamarin.TVOS | xamarintvos was computed. |
Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated | |
---|---|---|---|
1.3.6-alpha | 400 | 8/5/2024 | |
1.3.5-alpha | 60 | 8/5/2024 | |
1.3.4-alpha | 65 | 8/5/2024 | |
1.3.3-alpha | 66 | 8/4/2024 | |
1.3.1-alpha | 71 | 8/4/2024 | |
1.3.0-alpha | 61 | 7/31/2024 | |
1.2.1 | 4,002 | 11/19/2023 | |
1.2.1-alpha | 1,101 | 10/10/2023 | |
1.2.0-alpha | 95 | 10/10/2023 | |
1.1.7 | 138 | 11/5/2023 | |
1.1.7-alpha | 106 | 10/9/2023 | |
1.1.6-alpha | 95 | 10/9/2023 | |
1.1.5-alpha | 106 | 10/9/2023 | |
1.1.3-alpha | 109 | 10/9/2023 | |
1.1.2-alpha | 94 | 10/9/2023 | |
1.1.1-alpha | 117 | 9/17/2023 | |
1.1.0-alpha | 97 | 9/17/2023 | |
1.0.0-alpha | 106 | 9/17/2023 |
v.1.3.3-alpha
- :facepalm: You have tests, Mal. _Use_ them (fixed bugs brought to light by tests).
v.1.3.2-alpha
- Forgot to replace .Any() with .Count > 0 when changing from IEnumerable to IReadOnlyList.
- Collection item names used the variable name instead of the collection name, breaking backwards compatibility.
v.1.3.1-alpha
- I stupidly had implicit usings in my test project, which meant that code that shouldn't have compiled, compiled. Fixed that.
v.1.3.0-alpha
- Complete rewrite of the generator to handle _so_ many bugs...
- Breaking change: Lists are no longer IEnumerable, they are now IReadOnlyList to support indexing and count without evaluating the list multiple times.
- Added meta-macro support: You can now reference items in a scope higher up by prefixing fields with a period. Example: {{ .Name }} will reference the Name field in the parent scope, and {{ $if .$first }} will check if the current item is in the first item in the parent scope. You can add multiple periods to reference higher scopes.
v.1.2.1-alpha
- Missing a using...
v.1.2.0-alpha
- BREAKING CHANGE: Completely changed the way the template is generated and used code wise, to simplify even further.
- Should now support environments with nullable support a little better.
v.1.1.7-alpha
- Learned how to include release notes!
v.1.1.6-alpha
- Added "not" support to conditionals
- Now supports item states {{ $if <state> }} where <state> can be $first $last $middle $odd $even
- Fixed bad end check, fixed bad location of Else macro type check
- Fixed a rather ugly infinite loop