Drizin / CodegenCS

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

Null adds an empty line #27

Closed vnetonline closed 1 month ago

vnetonline commented 1 month ago

I have the following method, which contains some conditional logic

        public FormattableString RenderEntityDbSets(Solution solution, Entity entity)
        {
            if (IsEntityModelExists(solution, entity))
            {
                return $$"""

                    public virtual DbSet<Models.{{entity.Name}}> {{entity.Name}} { get; set; }
                    """;
            } 
            else
            {
                return null;
            }

        }

When I return a null, I have noticed that the ICodegenTextWriter adds an empty line.

Is there a better way to get around this?

Drizin commented 1 month ago

I suppose you're interpolating an IEnumerable like {{ entities.Select(e => RenderEntityDbSets(solution, e) }}, right?

Currently IEnumerable doesn't have any special behavior if some item renders a null (and I don't think it should have any special behavior anyway) so probably it's same behavior as an empty item (by default each item has its own line).

If that's the case then I suggest that you filter the IEnumerable, like this: {{ entities.Where(e => IsEntityModelExists(solution, e).Select(e => RenderEntityDbSets(solution, e) }}

Or if that doesn't work you can try something like return $$"{{TLW}}".TLW` means "trim leading whitespace", which basically means it will "do backspace until it finds a non-whitespace character" (even if that is in a previous line) - so basically it's like discarding the empty line.

Another possible workaround would be use .Render() extension with an empty separator between items (I think it's like list.Render(new RenderEnumerableOptions() { BetweenItemsBehavior = ItemsSeparatorBehavior.WriteCustomSeparator, CustomSeparator = "" })- and then you add the extra line on your own after the DbSet - but in this case you would get an extra line after the last item - so I think the first solution is best.

Let me now how it goes.

vnetonline commented 1 month ago

Thank you for the feedback. I will try it out ! and revert back

Drizin commented 1 month ago

Please reopen if not fixed

vnetonline commented 1 week ago

I am still having issues with EmptyLines

        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}}""";
            }

        }

The result is as follows

    private int _id;
    private string _firstName;

    private string _lastName;

    private string _sex = "True";

    private DateTime? _dOB;
    private DateTime? _dOBTime;

    private int _mRN;