Drizin / CodegenCS

C# Toolkit for Code Generation (T4 alternative!)
MIT License
223 stars 30 forks source link

Still having issues with Emptylines #32

Closed vnetonline closed 1 week ago

vnetonline commented 1 week ago

I am still having issues with EmptyLines just upgraded to 3.5

        public string GenerateOqtaneEntityEdit(CodeGenSchema codeGenSchema)
        {

            ICodegenTextWriter writer = new CodegenTextWriter();
            writer.RemoveWhitespaceFromEmptyLines = false;
            writer.LoadTemplate<OqtaneEntityEditTemplate>().Render(codeGenSchema);
            var contents = writer.GetContents();

            var isClientModuleFolderExists = Directory.Exists(Utilities.PathCombine(codeGenSchema.Solution.Location, $"Client/Modules/{codeGenSchema.Solution.Namespace}.{codeGenSchema.Entity.Name}"));

            if (!isClientModuleFolderExists)
            {
                Directory.CreateDirectory(Utilities.PathCombine(codeGenSchema.Solution.Location, $"Client/Modules/{codeGenSchema.Solution.Namespace}.{codeGenSchema.Entity.Name}"));
            }

            var path = Utilities.PathCombine(codeGenSchema.Solution.Location, $"Client/Modules/{codeGenSchema.Solution.Namespace}.{codeGenSchema.Entity.Name}", $"Edit.razor");
            System.IO.File.WriteAllText(path, contents);

            return writer.GetContents();
        }
        public FormattableString Render(CodeGenSchema codeGenSchema)
        {

            var solution = codeGenSchema.Solution;
            var entity = codeGenSchema.Entity;
            var camelEntityName = entity.Name.Camelize();
            var camelPluralEntityName = entity.PluralName.Camelize();

            return $$"""
                    @using Oqtane.Modules.Controls
                    @using {{solution.Namespace}}.Services
                    @using {{solution.Namespace}}.Models
                    @using Oqtane

                    @namespace {{solution.Namespace}}.{{entity.Name}}
                    @inherits ModuleBase
                    @inject I{{entity.Name}}Service {{entity.Name}}Service
                    @inject NavigationManager NavigationManager
                    @inject IStringLocalizer<Index> Localizer
                    @inject IStringLocalizer<SharedResources> SharedLocalizer

                    <form @ref="form" class="@(validated ? " was-validated" : "needs-validation" )" novalidate>
                        <div class="container">
                            {{entity.Properties.Select(p => RenderFormControls(entity, p))}}
                        </div>
                        <button type="button" class="btn btn-success" @onclick="Save">@Localizer["Save"]</button>
                        <NavLink class="btn btn-secondary" href="@NavigateUrl()">@Localizer["Cancel"]</NavLink>
                        <br /><br />
                        @if (PageState.Action == "Edit")
                        {
                            <AuditInfo CreatedBy="@_createdby" CreatedOn="@_createdon" ModifiedBy="@_modifiedby" ModifiedOn="@_modifiedon"></AuditInfo>
                        }
                    </form>

                    @code {
                        // uncomment the following line to use Static render mode for this component
                        // public override string RenderMode => RenderModes.Static;
                        // uncomment the following line to Prerender on Server NOTE: This may effect SOE so should only be used in backend pages
                        // public override bool? Prerender => true

                        public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Edit;

                        public override string Actions => "Add,Edit";

                        public override string Title => "Manage {{entity.Name}}";

                        public override List<Resource> Resources => new List<Resource>()
                        {
                            new Resource { ResourceType = ResourceType.Stylesheet, Url = ModulePath() + "Module.css" }
                        };

                        private ElementReference form;
                        private bool validated = false;

                        private int _id;
                        {{entity.Properties.Select(p => RenderProperties(entity, p))}}
                        private string _createdby;
                        private DateTime _createdon;
                        private string _modifiedby;
                        private DateTime _modifiedon;

                        {{RenderOnInitializeAsyncMethod(entity)}}

                        {{RenderSaveMethod(entity)}}

                    }
                    """;

        }
        public FormattableString RenderProperties(Entity entity, Property property) 
        {

            if (property.ShowOnEditAction || property.ShowOnCreateAction)
            {

                if (property.Type == "DateTime")
                {
                    return $$"""
                        private {{property.Type}}? _{{property.Name.Camelize()}};
                        private {{property.Type}}? _{{property.Name.Camelize()}}Time;
                        """;
                }
                if (property.Type == "bool")
                {
                    return $$"""
                        private string _{{property.Name.Camelize()}} = "True";
                        """;
                }
                else
                {
                    return $$"""
                        private {{property.Type}} _{{property.Name.Camelize()}};
                        """;
                }

            }
            else
            {
                return $$"""{{Symbols.TLW}}""";
            }

        }
    private int _id;
    private string _firstName;

    private string _lastName;

    private string _sex = "True";

    private DateTime? _dOB;
    private DateTime? _dOBTime;

    private int _mRN;
Drizin commented 1 week ago

Looks like the problem is that the raw string literals returned by RenderProperties they all have an ending line break. Probably I should add a new extension to enrich/signal that the IEnumerable should trim trailing whitespace. Or maybe some extension to enrich the FormattableString itself. For now try adding {{Symbols.TLW}} to the last line (the empty closing line) of each multi line string.

vnetonline commented 1 week ago

I just tried to add {{Symbols.TLW}}

        public FormattableString RenderProperties(Entity entity, Property property) 
        {

            if (property.ShowOnEditAction || property.ShowOnCreateAction)
            {

                if (property.Type == "DateTime")
                {
                    return $$"""
                            private {{property.Type}}? _{{property.Name.Camelize()}};
                            private {{property.Type}}? _{{property.Name.Camelize()}}Time;
                            {{Symbols.TLW}}
                            """;
                }
                if (property.Type == "bool")
                {
                    return $$"""
                            private string _{{property.Name.Camelize()}} = "True";
                            {{Symbols.TLW}}
                            """;
                }
                else
                {
                    return $$"""
                            private {{property.Type}} _{{property.Name.Camelize()}};
                            {{Symbols.TLW}}
                            """;
                }

            }
            else
            {
                return $$"""
                        {{Symbols.TLW}}
                        """;
            }

        }
    private bool validated = false;

    private int _id;
    private string _firstName;

    private string _lastName;

    private string _sex = "True";

    private DateTime? _dOB;
    private DateTime? _dOBTime;

    private int _mRN;
vnetonline commented 1 week ago

Just to add if my method returns only one line raw literal then the extra line breaks are not there

        public FormattableString RenderProperties(Entity entity, Property property) 
        {

            //if (property.ShowOnEditAction || property.ShowOnCreateAction)
            //{

            //    if (property.Type == "DateTime")
            //    {
            //        return $$"""
            //                private {{property.Type}}? _{{property.Name.Camelize()}};
            //                private {{property.Type}}? _{{property.Name.Camelize()}}Time;{{Symbols.TLW}}
            //                """;
            //    }
            //    if (property.Type == "bool")
            //    {
            //        return $$"""
            //                private string _{{property.Name.Camelize()}} = "True";{{Symbols.TLW}}
            //                """;
            //    }
            //    else
            //    {
                    return $$"""
                            private {{property.Type}} _{{property.Name.Camelize()}};
                            """;
                //}

            //}
            //else
            //{
            //    return $$"""
            //            {{Symbols.TLW}}
            //            """;
            //}

        }
    private int _id;
    private string _firstName;
    private string _lastName;
    private bool _sex;
    private DateTime _dOB;
    private int _mRN;
Drizin commented 1 week ago

Oh. Whenever an IEnumerable item writes multiple lines then by default CodenTextWriter will ensure that there is an empty line between this item and the next one. So if your items are all single-liners there's no extra spacer, but if your items are all multi-line methods then there's a blank line between those methods.

That's controlled by CodegenTextWriter.DefaultIEnumerableRenderOptions (defaulting to RenderEnumerableOptions.LineBreaksWithAutoSpacer).

Try writer.DefaultIEnumerableRenderOptions = RenderEnumerableOptions.LineBreaksWithSpacer or {{entity.Properties.Select(p => RenderProperties(entity, p)).Render(RenderEnumerableOptions.LineBreaksWithSpacer)}}

vnetonline commented 1 week ago

I used writer.DefaultIEnumerableRenderOptions = RenderEnumerableOptions.LineBreaksWithoutSpacer;

as follows

        public string GenerateOqtaneEntityEdit(CodeGenSchema codeGenSchema)
        {

            ICodegenTextWriter writer = new CodegenTextWriter();
            writer.RemoveWhitespaceFromEmptyLines = false;
            writer.DefaultIEnumerableRenderOptions = RenderEnumerableOptions.LineBreaksWithoutSpacer;
            writer.LoadTemplate<OqtaneEntityEditTemplate>().Render(codeGenSchema);
            var contents = writer.GetContents();

            var isClientModuleFolderExists = Directory.Exists(Utilities.PathCombine(codeGenSchema.Solution.Location, $"Client/Modules/{codeGenSchema.Solution.Namespace}.{codeGenSchema.Entity.Name}"));

            if (!isClientModuleFolderExists)
            {
                Directory.CreateDirectory(Utilities.PathCombine(codeGenSchema.Solution.Location, $"Client/Modules/{codeGenSchema.Solution.Namespace}.{codeGenSchema.Entity.Name}"));
            }

            var path = Utilities.PathCombine(codeGenSchema.Solution.Location, $"Client/Modules/{codeGenSchema.Solution.Namespace}.{codeGenSchema.Entity.Name}", $"Edit.razor");
            System.IO.File.WriteAllText(path, contents);

            return writer.GetContents();
        }
    private int _id;
    private string _firstName;
    private string _lastName;
    private string _sex = "True";
    private DateTime? _dOB;
    private DateTime? _dOBTime;
    private int _mRN;

Thank you for your help much appreciated

Drizin commented 1 week ago

My first suggestion was just plain wrong: I forgot that the first and last lines of a multiline raw string literal are just discarded.

$$"""
  test
  """ 
.Equals("test")

I had forgotten about this behavior of multi-line ienumerable items.

Drizin commented 1 week ago

Thank you for your help much appreciated

I'm glad it worked. Thanks for sharing some snippets of your templates, it's nice to see how people are using my project :-)

vnetonline commented 1 week ago

I have created a whole set of templates for Oqtane which is offered as a SaaS service you can check out here

image

https://amazingoqtanestudio.com

Drizin commented 1 week ago

Yeah, I saw that. At some point I've also considered myself creating some a marketplace for premium-templates (running on top of the opensource project, with model-editor with json-schema validation, etc), but unfortunately I have too many ideas and too little available time :-) Nice project, good luck on that.