Drizin / CodegenCS

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

Namespace of the generated code depending on the path of the folder location of the .csx file #10

Closed fortnum closed 1 year ago

fortnum commented 1 year ago

As I understand it, at the moment the code-generating class does not have any access, so that it becomes possible to orient (generate code depending on) its position in the project. All I can get inside the generator is the name of the resulting file. Not the path, unfortunately, namely that only the name, although it is called "relativePath", but it contains just the name of the output file.

The name of input file, template, is also not available. But the input file name can be output back from the output file name. But not path.

For example, it is impossible to generate the namespace of the generated class depending on the relative position of the file in the project (I mean nested folders, and somewhere at the end there is a .csx file; to get the final .cs file with the namespace corresponding to the breadcrumbs of these nested folders). Maybe I'm wrong and yet somehow it is possible to solve this problem now?

PS. I mean VSExtension. At least I have considered so far only how this part works. But actually, it is only she who is needed.

Drizin commented 1 year ago

Good question (and looks like an important feature request).

First, let me explain ICodegenOutputFile.RelativePath: this is a relative path of where the file will be saved, relative to the base folder where all files (ICodegenContext.OutputFiles) will be saved. It supports absolute paths, but I guess the most common scenario is that templates will just define a relative path (like just a filename), and then when ICodegenContext is saving the OutputFiles we combine the target folder with this relative paths - that's why it's called RelativePath.

If I understand correctly you're reading from ICodegenContext.DefaultOutputFile.RelativePath to get the output filename, but as explained above this wasn't exactly the purpose of this property, and as you have just noticed it doesn't give the absolute path that you were expecting. Basically this property is based on the CSX filename (without the path), and it's defined by the VS Extension. The idea here is that templates can just start writing to the DefaultOutput (without having to define a filename) and in case the output file is named after the template name (in other words, MyTemplate.csx would by default write to MyTemplate.generated.cs).

So yeah, I understand your use case (looks like an important feature request) but currently there's no good way of getting the full path of the CSX file. I guess this information (template full path, as well as some info about the parent project like getting the namespace) could be made available (through dependency injection) in some IInvokationContext class. Templates would be able to either inject IInvokationContext invokationContext or get it like codegenContext.InvokationContext. How does that sound?

I can do it at the next weekend, or else feel free to create a PR for that.

Drizin commented 1 year ago

I've pushed a commit with this new feature where you can inject VSExecutionContext into your template. Please take a look and let me know if it works. The VS Extension (RunTemplate project) wasn't republished but you should be able to run it from sources using Visual Studio. I should update the documentation when possible.

Drizin commented 1 year ago

I've just published Visual Studio 2022 Extension v3.2 where you should be able to inject what you need:

public class MyTemplate
{
    void Main(ICodegenContext context, IModelFactory factory, VSExecutionContext executionContext)
    {
        // THIS
        context["Test.cs"]
            .WriteLine(executionContext.ProjectPath)
            .WriteLine(executionContext.SolutionPath)
            .WriteLine(executionContext.TemplateRelativePath);

        // There's also a factory that can load files (path is relative to the template)
        var model = factory.LoadModelFromFile<OpenApiDocument>("petstore-openapi3.json");
        //foreach (var entity in model.Definitions)
        //    context[entity.Key + ".cs"].WriteLine(...);
    }
}

Let me know how it goes for you.

Drizin commented 1 year ago

I've just double-checked the latest version (3.3) and it is working fine. Please reopen if you have any issues.