fluid
This commit is contained in:
parent
310b0da3de
commit
35fcd147d8
@ -6,25 +6,22 @@ namespace refl
|
|||||||
internal class CmdBuildOption
|
internal class CmdBuildOption
|
||||||
{
|
{
|
||||||
[Value(0, MetaName = "", Required = true, Min = 2, HelpText = "Input file(s) for parsing.")]
|
[Value(0, MetaName = "", Required = true, Min = 2, HelpText = "Input file(s) for parsing.")]
|
||||||
public IEnumerable<string> InputFiles { get; set; }
|
public IEnumerable<string>? InputFiles { get; set; }
|
||||||
|
|
||||||
[Option('o', "output", Required = false, HelpText = "Output file for generated code.")]
|
[Option('o', "output", Required = false, HelpText = "Output file for generated code.")]
|
||||||
public string OutputFile { get; set; }
|
public string OutputFile { get; set; } = "";
|
||||||
|
|
||||||
[Option('t', "type", Required = false, HelpText = "Type of code to generate.")]
|
[Option('t', "type", Required = false, HelpText = "Type of code to generate.")]
|
||||||
public string CodeType { get; set; }
|
public string CodeType { get; set; } = "";
|
||||||
|
|
||||||
[Option('l', "link", Required = false, HelpText = "link to the include dir")]
|
[Option('l', "link", Required = false, HelpText = "link to the include dir")]
|
||||||
public string Link { get; set; }
|
public string Link { get; set; } = "";
|
||||||
|
|
||||||
[Option('d', "define", Required = false, HelpText = "define a macro on the command line")]
|
[Option('d', "define", Required = false, HelpText = "define a macro on the command line")]
|
||||||
public string Define { get; set; }
|
public string Define { get; set; } = "";
|
||||||
|
|
||||||
[Option('n', "namespace", Required = false, HelpText = "Namespace for generated code.")]
|
|
||||||
public string Namespace { get; set; }
|
|
||||||
|
|
||||||
[Option('h', "help", HelpText = "Display this help message.")]
|
[Option('h', "help", HelpText = "Display this help message.")]
|
||||||
public bool Help { get; set; }
|
public bool Help { get; set; } = false;
|
||||||
|
|
||||||
static CppParserOptions MakeParserOptions(CmdBuildOption opt)
|
static CppParserOptions MakeParserOptions(CmdBuildOption opt)
|
||||||
{
|
{
|
||||||
@ -54,13 +51,19 @@ namespace refl
|
|||||||
}
|
}
|
||||||
public static int CMD_CheckBuildOption(CmdBuildOption opt)
|
public static int CMD_CheckBuildOption(CmdBuildOption opt)
|
||||||
{
|
{
|
||||||
|
if(opt.InputFiles == null)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
var parse_opt = MakeParserOptions(opt);
|
var parse_opt = MakeParserOptions(opt);
|
||||||
var compilation = CppAst.CppParser.ParseFiles(opt.InputFiles.Skip(1).ToList(), parse_opt);
|
var compilation = CppAst.CppParser.ParseFiles(opt.InputFiles.Skip(1).ToList(), parse_opt);
|
||||||
// Print diagnostic messages
|
// Print diagnostic messages
|
||||||
foreach (var message in compilation.Diagnostics.Messages)
|
foreach (var message in compilation.Diagnostics.Messages)
|
||||||
if(message.Type.Equals(CppLogMessageType.Error))
|
if(message.Type.Equals(CppLogMessageType.Error))
|
||||||
Console.WriteLine(message);
|
Console.WriteLine(message);
|
||||||
ModuleMeta.ParseCompileInfo(compilation);
|
var module = ModuleMeta.ParseCompileInfo(compilation);
|
||||||
|
ClassMetaGen.GenNameMeta();
|
||||||
|
ClassMetaGen.GenCppMeta(module);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,4 @@
|
|||||||
using CppAst;
|
using CppAst;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace refl
|
namespace refl
|
||||||
{
|
{
|
||||||
@ -33,13 +28,15 @@ namespace refl
|
|||||||
internal class ClassMeta
|
internal class ClassMeta
|
||||||
{
|
{
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
|
public string ParentName { get; set; }
|
||||||
public string Path { get; set; }
|
public string Path { get; set; }
|
||||||
|
|
||||||
public Dictionary<string, FieldMeta> Fields { get; set; }
|
public Dictionary<string, FieldMeta> Fields { get; set; }
|
||||||
|
|
||||||
public ClassMeta(string name)
|
public ClassMeta(string name, string parentName)
|
||||||
{
|
{
|
||||||
Name = name;
|
Name = name;
|
||||||
|
ParentName = parentName;
|
||||||
Path = "";
|
Path = "";
|
||||||
Fields = new Dictionary<string, FieldMeta>();
|
Fields = new Dictionary<string, FieldMeta>();
|
||||||
}
|
}
|
||||||
@ -58,7 +55,17 @@ namespace refl
|
|||||||
}
|
}
|
||||||
public static ClassMeta ParseCppClass(CppClass cppClass)
|
public static ClassMeta ParseCppClass(CppClass cppClass)
|
||||||
{
|
{
|
||||||
ClassMeta cls_meta = new ClassMeta(cppClass.Name);
|
string parentName = "void";
|
||||||
|
if (cppClass.BaseTypes.Count == 1)
|
||||||
|
{
|
||||||
|
var type = cppClass.BaseTypes[0].Type;
|
||||||
|
CppClass? parent = type as CppClass;
|
||||||
|
if (parent != null)
|
||||||
|
{
|
||||||
|
parentName = parent.Name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ClassMeta cls_meta = new ClassMeta(cppClass.Name, parentName);
|
||||||
foreach (var field in cppClass.Fields)
|
foreach (var field in cppClass.Fields)
|
||||||
{
|
{
|
||||||
if (field.Attributes.Count == 0)
|
if (field.Attributes.Count == 0)
|
||||||
|
|||||||
@ -1,34 +1,120 @@
|
|||||||
using System;
|
using System.Text;
|
||||||
using System.Collections.Generic;
|
using Fluid;
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace refl
|
namespace refl
|
||||||
{
|
{
|
||||||
|
internal class FieldMetaGenData
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string Meta { get; set; }
|
||||||
|
public bool IsMethod { get; set; }
|
||||||
|
|
||||||
|
public FieldMetaGenData(string name, string meta, bool isMethod)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
Meta = meta;
|
||||||
|
IsMethod = isMethod;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
internal class ClassMetaGenData
|
||||||
|
{
|
||||||
|
public string Name { get; set; } = "";
|
||||||
|
public string NameSpace { get; set; } = "";
|
||||||
|
public string ParentName { get; set; } = "";
|
||||||
|
public string Indent { get; set; } = "";
|
||||||
|
public string MetaName { get; set; } = "";
|
||||||
|
public List<FieldMetaGenData> FieldList = new List<FieldMetaGenData>();
|
||||||
|
|
||||||
|
public ClassMetaGenData(string indent, string nameSpace)
|
||||||
|
{
|
||||||
|
Indent = indent;
|
||||||
|
NameSpace = nameSpace;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal class ClassMetaGen
|
internal class ClassMetaGen
|
||||||
{
|
{
|
||||||
public static Dictionary<string, StringBuilder> FileList = new Dictionary<string, StringBuilder>();
|
public static Dictionary<string, StringBuilder> FileList = new Dictionary<string, StringBuilder>();
|
||||||
|
public static Fluid.IFluidTemplate? GenTemplate;
|
||||||
|
public static TemplateOptions GenOptions = new TemplateOptions();
|
||||||
|
public static void GenNameMeta()
|
||||||
|
{
|
||||||
|
foreach (var pair in ModuleMeta.NameMaps)
|
||||||
|
{
|
||||||
|
var build = new StringBuilder();
|
||||||
|
build.Append("namespace ");
|
||||||
|
build.Append(pair.Key);
|
||||||
|
build.Append("_Name {\n");
|
||||||
|
foreach(var kv in pair.Value)
|
||||||
|
{
|
||||||
|
build.Append(" constexpr const Name ");
|
||||||
|
build.Append(kv);
|
||||||
|
build.Append("=\"");
|
||||||
|
build.Append(kv);
|
||||||
|
build.Append("\";\n");
|
||||||
|
}
|
||||||
|
build.Append("}\n");
|
||||||
|
Console.WriteLine(build.ToString());
|
||||||
|
build.Clear();
|
||||||
|
FileList.Add(pair.Key, build);
|
||||||
|
}
|
||||||
|
}
|
||||||
public static void GenCppMeta(ModuleMeta module)
|
public static void GenCppMeta(ModuleMeta module)
|
||||||
{
|
{
|
||||||
foreach(var pair in ModuleMeta.NameMaps)
|
var template_string = File.ReadAllText("template/refl.liquid");
|
||||||
|
var parser = new FluidParser();
|
||||||
|
parser.TryParse(template_string, out GenTemplate, out var error);
|
||||||
|
GenOptions.MemberAccessStrategy.Register<FieldMetaGenData>();
|
||||||
|
if (!string.IsNullOrEmpty(error))
|
||||||
{
|
{
|
||||||
FileList.Add(pair.Key, new StringBuilder());
|
Console.WriteLine(error);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
foreach(var cls in module.ClassList)
|
GenModuleMeta(module, null);
|
||||||
|
foreach (var pair in FileList)
|
||||||
{
|
{
|
||||||
|
Console.WriteLine(pair.Key);
|
||||||
|
Console.WriteLine(pair.Value.ToString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public static void GenClassMeta(ModuleMeta module)
|
public static void GenModuleMeta(ModuleMeta module, string? prefix)
|
||||||
{
|
{
|
||||||
|
var gen = new ClassMetaGenData(prefix+"", module.NameSpace);
|
||||||
foreach (var cls in module.ClassList)
|
foreach (var cls in module.ClassList)
|
||||||
{
|
{
|
||||||
foreach (var field in cls.Fields)
|
GenClassMeta(cls, gen);
|
||||||
{
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
foreach (var child in module.ChildList)
|
||||||
|
{
|
||||||
|
if(prefix == null)
|
||||||
|
{
|
||||||
|
GenModuleMeta(child, prefix + "\r");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GenModuleMeta(child, prefix + "\t");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static void GenClassMeta(ClassMeta cls, ClassMetaGenData gen)
|
||||||
|
{
|
||||||
|
gen.Name = cls.Name;
|
||||||
|
gen.ParentName = cls.ParentName;
|
||||||
|
foreach (var pair in cls.Fields)
|
||||||
|
{
|
||||||
|
gen.FieldList.Clear();
|
||||||
|
foreach (var field in pair.Value.MemberList)
|
||||||
|
{
|
||||||
|
gen.FieldList.Add(new FieldMetaGenData(field.Name, field.Meta, false));
|
||||||
|
}
|
||||||
|
foreach (var field in pair.Value.MethodList)
|
||||||
|
{
|
||||||
|
gen.FieldList.Add(new FieldMetaGenData(field.Name, field.Meta, true));
|
||||||
|
}
|
||||||
|
gen.MetaName = pair.Key;
|
||||||
|
var context = new TemplateContext(gen, GenOptions);
|
||||||
|
var res = GenTemplate.Render(context);
|
||||||
|
FileList[pair.Key].Append(res);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,4 @@
|
|||||||
using System;
|
using System.Text;
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
namespace refl
|
namespace refl
|
||||||
{
|
{
|
||||||
internal class MetaToken
|
internal class MetaToken
|
||||||
|
|||||||
@ -1,9 +1,13 @@
|
|||||||
// Parse C++ files
|
// Parse C++ files
|
||||||
using CppAst;
|
|
||||||
using CommandLine;
|
using CommandLine;
|
||||||
using refl;
|
using refl;
|
||||||
|
class Program
|
||||||
Parser.Default.ParseArguments<CmdBuildOption>(args)
|
{
|
||||||
|
static void Main(string[] args)
|
||||||
|
{
|
||||||
|
Parser.Default.ParseArguments<CmdBuildOption>(args)
|
||||||
.MapResult(
|
.MapResult(
|
||||||
(CmdBuildOption o) => CmdBuildOption.CMD_CheckBuildOption(o),
|
(CmdBuildOption o) => CmdBuildOption.CMD_CheckBuildOption(o),
|
||||||
error => 1);
|
error => 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2,7 +2,8 @@
|
|||||||
"profiles": {
|
"profiles": {
|
||||||
"refl": {
|
"refl": {
|
||||||
"commandName": "Project",
|
"commandName": "Project",
|
||||||
"commandLineArgs": "build F:\\csharp\\CppAst.NET\\src\\refl\\cpp\\vertex.h "
|
"commandLineArgs": "build D:\\csharp\\cppast\\src\\refl\\cpp\\vertex.h ",
|
||||||
|
"workingDirectory": "D:\\csharp\\cppast\\src\\refl"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,6 +1,6 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
using namespace std;
|
using namespace std;
|
||||||
namespace test{
|
|
||||||
struct vec3_parent {
|
struct vec3_parent {
|
||||||
virtual int norm(int x1, int& x2) {
|
virtual int norm(int x1, int& x2) {
|
||||||
x2 = x1 * x2;
|
x2 = x1 * x2;
|
||||||
@ -42,5 +42,4 @@ namespace test{
|
|||||||
cout << x1 << "::norm3" << endl;
|
cout << x1 << "::norm3" << endl;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
|
||||||
#include "meta.vertex.inl"
|
#include "meta.vertex.inl"
|
||||||
@ -8,10 +8,12 @@
|
|||||||
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
|
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
|
||||||
<!-- Workaround for issue https://github.com/microsoft/ClangSharp/issues/129 -->
|
<!-- Workaround for issue https://github.com/microsoft/ClangSharp/issues/129 -->
|
||||||
<RuntimeIdentifier Condition="'$(RuntimeIdentifier)' == '' AND '$(PackAsTool)' != 'true'">$(NETCoreSdkRuntimeIdentifier)</RuntimeIdentifier>
|
<RuntimeIdentifier Condition="'$(RuntimeIdentifier)' == '' AND '$(PackAsTool)' != 'true'">$(NETCoreSdkRuntimeIdentifier)</RuntimeIdentifier>
|
||||||
|
<StartupObject>Program</StartupObject>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="CommandLineParser" Version="2.9.1" />
|
<PackageReference Include="CommandLineParser" Version="2.9.1" />
|
||||||
|
<PackageReference Include="Fluid.Core" Version="2.8.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@ -19,7 +19,6 @@ struct vec3_Static_Meta {
|
|||||||
return fetch_meta_size<vec3_Static_Meta>();
|
return fetch_meta_size<vec3_Static_Meta>();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
constexpr int size = vec3_Static_Meta::Size();
|
|
||||||
struct vec3_Meta : public Meta{
|
struct vec3_Meta : public Meta{
|
||||||
using MyStatic = vec3_Static_Meta;
|
using MyStatic = vec3_Static_Meta;
|
||||||
using MyUClass = UClass_Meta<vec3, vec3_parent>;
|
using MyUClass = UClass_Meta<vec3, vec3_parent>;
|
||||||
|
|||||||
48
src/refl/template/refl.liquid
Normal file
48
src/refl/template/refl.liquid
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
{%- assign indent = Indent -%}
|
||||||
|
{%- if NameSpace != blank -%}
|
||||||
|
{{ indent }}namespace {{ NameSpace }} {
|
||||||
|
{% capture newIndent %}{{ indent }} {% endcapture %}
|
||||||
|
{%- assign indent = newIndent -%}
|
||||||
|
{%- endif -%}
|
||||||
|
{{ indent }}struct {{ Name }}_Static_{{MetaName}} {
|
||||||
|
{{ indent }} consteval static auto __StaticFields() {
|
||||||
|
{{ indent }} return std::make_tuple({% for field in FieldList %}&{{Name}}::{{field.Name}}{% unless forloop.last %}, {% endunless %}{% endfor %});
|
||||||
|
{{ indent }} };
|
||||||
|
|
||||||
|
{{ indent }} consteval static auto __MakeStaticFields() {
|
||||||
|
{{ indent }} return std::array{
|
||||||
|
{{ indent }} {%- for field in FieldList -%}
|
||||||
|
{{ indent }} {%- if field.IsMethod -%}
|
||||||
|
{{ indent }} StaticMethodField(&{{Name}}::{{field.Name}}, {{MetaName}}_Name::{{field.Name}}{% if field.Meta %}, {{field.Meta}}{% endif %}),
|
||||||
|
{{ indent }} {%- else -%}
|
||||||
|
{{ indent }} StaticMemberField(&{{Name}}::{{field.Name}}, {{MetaName}}_Name::{{field.Name}}{% if field.Meta %}, {{field.Meta}}{% endif %}),
|
||||||
|
{{ indent }} {%- endif -%}
|
||||||
|
{{ indent }} {%- endfor -%}
|
||||||
|
{{ indent }} };
|
||||||
|
{{ indent }} };
|
||||||
|
|
||||||
|
{{ indent }} consteval static int Size() {
|
||||||
|
{{ indent }} return fetch_meta_size<{{ Name }}_Static_{{MetaName}}>();
|
||||||
|
{{ indent }} };
|
||||||
|
{{ indent }}};
|
||||||
|
|
||||||
|
{{ indent }}struct {{ Name }}_{{MetaName}} : public Meta {
|
||||||
|
{{ indent }} using MyStatic = {{ Name }}_Static_{{MetaName}};
|
||||||
|
{{ indent }} using MyUClass = UClass_Meta<{{ Name }}, {{ ParentName }}>;;
|
||||||
|
{{ indent }} inline static char s_data[MyStatic::Size()]{};
|
||||||
|
{{ indent }} static auto __MakeFields() {
|
||||||
|
{{ indent }} char* memory = &s_data[0];
|
||||||
|
{{ indent }} return std::array{
|
||||||
|
{{ indent }} {%- for field in FieldList -%}
|
||||||
|
{{ indent }} {%- if field.IsMethod -%}
|
||||||
|
{{ indent }} StaticMethodField(&{{Name}}::{{field.Name}}, {{MetaName}}_Name::{{field.Name}}{% if field.Meta %}, {{field.Meta}}{% endif %}),
|
||||||
|
{{ indent }} {%- else -%}
|
||||||
|
{{ indent }} StaticMemberField(&{{Name}}::{{field.Name}}, {{MetaName}}_Name::{{field.Name}}{% if field.Meta %}, {{field.Meta}}{% endif %}),
|
||||||
|
{{ indent }} {%- endif -%}
|
||||||
|
{{ indent }} {%- endfor -%}
|
||||||
|
{{ indent }} };
|
||||||
|
{{ indent }} };
|
||||||
|
{{ indent }}};
|
||||||
|
{% if NameSpace != blank %}
|
||||||
|
{{ Indent }}}
|
||||||
|
{% endif %}
|
||||||
52
src/refl/template/test
Normal file
52
src/refl/template/test
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
{% assign indent = Indent %}
|
||||||
|
{% if NameSpace %}
|
||||||
|
{{ indent }}namespace {{ NameSpace }} {
|
||||||
|
{% capture newIndent %}{{ indent }} {% endcapture %}
|
||||||
|
{% assign indent = newIndent %}
|
||||||
|
{% endif %}
|
||||||
|
{{ indent }}struct {{ Name }}_Static_{{MetaName}} {
|
||||||
|
{{ indent }} consteval static auto __StaticFields() {
|
||||||
|
{{ indent }} return std::make_tuple({% for field in FieldList %}&{{Name}}::{{field.Name}}{% unless forloop.last %}, {% endunless %}{% endfor %});
|
||||||
|
{{ indent }} };
|
||||||
|
|
||||||
|
{{ indent }} consteval static auto __MakeStaticFields() {
|
||||||
|
{{ indent }} return std::array{
|
||||||
|
{{ indent }} {% for field in FieldList %}
|
||||||
|
{{ indent }} {% if field.IsMethod %}
|
||||||
|
{{ indent }} StaticMethodField(&{{field.Name}}, "{{field.Name}}"{% if field.Meta %}, {{field.Meta}}{% endif %}),
|
||||||
|
{{ indent }} {% else %}
|
||||||
|
{{ indent }} StaticMemberField(&{{field.Name}}, "{{field.Name}}"{% if field.Meta %}, {{field.Meta}}{% endif %}),
|
||||||
|
{{ indent }} {% endif %}
|
||||||
|
{{ indent }} {% endfor %}
|
||||||
|
{{ indent }} };
|
||||||
|
{{ indent }} };
|
||||||
|
|
||||||
|
{{ indent }} consteval static int Size() {
|
||||||
|
{{ indent }} return fetch_meta_size<{{ staticMetaStructName }}>();
|
||||||
|
{{ indent }} };
|
||||||
|
{{ indent }}};
|
||||||
|
|
||||||
|
{{ indent }}struct {{ Name }}_{{MetaName}} : public Meta {
|
||||||
|
{{ indent }} using MyStatic = {{ Name }}_Static_{{MetaName}};
|
||||||
|
{{ indent }} using MyUClass = UClass_Meta<{{ Name }}, {{ ParentName }}>;;
|
||||||
|
{{ indent }} inline static char s_data[MyStatic::Size()]{};
|
||||||
|
{{ indent }} static auto __MakeFields() {
|
||||||
|
{{ indent }} char* memory = &s_data[0];
|
||||||
|
{{ indent }} return std::array{
|
||||||
|
{{ indent }} {% for field in FieldList %}
|
||||||
|
{{ indent }} {% if field.IsMethod %}
|
||||||
|
{{ indent }} StaticMethodField(&{{field.Name}}, "{{field.Name}}"{% if field.Meta %}, {{field.Meta}}{% endif %}),
|
||||||
|
{{ indent }} {% else %}
|
||||||
|
{{ indent }} StaticMemberField(&{{field.Name}}, "{{field.Name}}"{% if field.Meta %}, {{field.Meta}}{% endif %}),
|
||||||
|
{{ indent }} {% endif %}
|
||||||
|
{{ indent }} {% endfor %}
|
||||||
|
{{ indent }} };
|
||||||
|
{{ indent }} };
|
||||||
|
{{ indent }}};
|
||||||
|
{% if namespace %}
|
||||||
|
{% for ns in namespace %}
|
||||||
|
{{ indent }}}
|
||||||
|
{% capture newIndent %}{{ indent | slice: 0, -4 }}{% endcapture %}
|
||||||
|
{% assign indent = newIndent %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
Loading…
Reference in New Issue
Block a user