Testura/Testura.Code

Regions, Comments, and Attributes?

Closed this issue · 6 comments

Hello! I'm rebuilding one of my old Entity Framework code generators, and this looks like an interesting toolset that might fit the bill. I wanted to find out, does this API allow for wrapping things like Properties and Methods in Regions? I'd also like to wire in XML documentation comments, and decorate properties with Attributes. Are they supported in this framework?

Thanks so much!

Hey!

We support attribute for properties but not xml documentation and regions. But it seems pretty easy to add so I can try to add it to the framework if you want? Should just take a couple of days depending on how much time I have.

@MilleBo is this something that you have started working on? I do think the project I'm working on will need these as well. It will also need support for pragma's. If you haven't started working on this I might try and add support for some of these.

@weshaggard I haven't started on it yet.

  • Comments should be pretty straightforward to add as it's just an additional value (and modification of the generation of course).
  • The regions would require some changes of the class builder I can think on a couple of different ways to do it:

Solution 1: Just a single WithRegions() method:

var @class = new ClassBuilder("Program", "HelloWorld")
	.WithUsings("System") 
	.WithModifiers(Modifiers.Public)
        .WithRegions(new RegionBuilder()
                .WithFields(..)
                .WithMethods(..)
               .Build())
	.Build();

The problem with this solution is the order of the regions. Where should the regions be generated? At the top or the bottom of the class? I guess at the bottom but it would be pretty ugly to have a region of fields after constructors, methods, etc.

Solution 2: Specific region for every type:

var @class = new ClassBuilder("Program", "HelloWorld")
	.WithUsings("System") 
	.WithModifiers(Modifiers.Public)
        .WithFields(new FieldRegion("NameOfRegions", fields[]))
        .WithMethods(new MethodRegion("NameOfRegions", fields[]))
	.Build();

This one is a bit less flexible as you can only have one type (field, property or method) in a single region but it would generate code in a "correct" order.

Solution 3: We change the class builder so the order you add things is the order we generate.

I think this is the best one as the user have total control over the order and we don't have to think about it. Then we also can use solution 1 for regions.

I can start to experiment a bit with solution 3 if you think it's a good idea? But you can try to add support for pragma and xml comments if you want.

I do think preserving order is a good idea. We should keep in mind though that regions aren't scoped to just around types or members you can have them in the middle of a method body as well. So I kind of think regions will be similar to comments or other preprocessor elements like pragmas, in that they can be put anywhere.

Another missing thing I'm actually working on right now is a NamespaceBuilder as I want to be able to generate multiple classes in the same namespace.

That's true, I guess we should support regions with the body generator (I can look at that too).

I haven't thought about a namespace builder but that sounds like a great thing to have. Looking forward to see it.

I have now added support for region (on class level). Example how it looks:

This code:

            var classBuilder = new ClassBuilder("Cat", "Models");
            var @class = classBuilder
                .WithUsings("System")
                .WithRegions(new RegionBuilder("MyRegion")
                    .WithFields(
                        new Field("_name", typeof(string), new List<Modifiers>() { Modifiers.Private }),
                        new Field("_age", typeof(int), new List<Modifiers>() { Modifiers.Private }))
                    .WithProperties(PropertyGenerator.Create(new AutoProperty("Name", typeof(string), PropertyTypes.GetAndSet, new List<Modifiers> { Modifiers.Public })))
                    .WithConstructor(
                        ConstructorGenerator.Create(
                            "Cat",
                            BodyGenerator.Create(
                                Statement.Declaration.Assign("Name", ReferenceGenerator.Create(new VariableReference("name"))),
                                Statement.Declaration.Assign("Age", ReferenceGenerator.Create(new VariableReference("age")))),
                            new List<Parameter> { new Parameter("name", typeof(string)), new Parameter("age", typeof(int)) },
                            new List<Modifiers> { Modifiers.Public }))
                    .Build())
                .Build();

Will generate this code:

using System;

namespace Models
{
    public class Cat
    {
        #region MyRegion 
        private string _name;
        private int _age;

        public string Name { get; set; }

        public Cat(string name, int age)
        {
            Name = name;
            Age = age;
        }
        #endregion
    }
}

It turned out fine except for a weird hack to get the leading trivia to work. The class builder interface is also unchanged and the only difference a current user should see is that the order matter (as we now preserv the order that you insert members in).