NeVeSpl / NTypewriter

File/code generator using Scriban text templates populated with C# code metadata from Roslyn API.
https://nevespl.github.io/NTypewriter/
MIT License
126 stars 24 forks source link

Questions #41

Closed Etchelon closed 2 years ago

Etchelon commented 3 years ago

Hello! First of all I want to say thanks for the effort you've put into this! It's not a small feat and it is tremendously helpful!

I've used TypeWriter some time ago, and I had assembled a nice set of scripts that allowed me to create TS interfaces from cs classes/enums and place them in various npm packages in the company ecosystem, and I am looking to translate those scripts into .nt scripts! I used Attributes heavily in order to achieve that, so I'm trying to figure out how to do a few things: 1) Is there a way to use type hints in functions? I'm writing a lot of helpers like

func TsPropertyName(prop)
  ret prop.Name
end

but prop has no intellisense, and since I'm learning the tool it would ease the learning 10x

2) How can I log to the NTypewriter output? Would help a lot for debugging! 3) How can I throw exceptions?

Thanks!

Etchelon commented 3 years ago

4) Have I just incurred into a bug? NT complains at line 55 that IsDto was not found in Custom, while it doesn't complain at line 45:

image

If I comment out line 55, no complaints for the usage above:

image

This is the function definition:

image

EDIT: just outdated docs, I need this in my config class:

public override IEnumerable<Type> TypesThatContainCustomFunctions
        {
            get
            {
                yield return typeof(NTEConfig);
            }
        }

not

public override IEnumerable<Type> GetTypesThatContainCustomFunctions()
        {
            yield return typeof(NTEConfig);
        }

This means that | Array.Filter @Custom.IsDto was erroring out silently! Is that right?!

NeVeSpl commented 3 years ago
1. Is there a way to use type hints in functions? I'm writing a lot of helpers like (...) but prop has no intellisense, and since I'm learning the tool it would ease the learning 10x

Unfortunately, Scriban does not have types, without types syntax completion is available only in a very few places, when type inference is possible. All helpers you can write in in C# instead, with full support of syntax completion.

2. How can I log to the NTypewriter output? Would help a lot for debugging!

There is no such an option, but I do not see why not to add it in the next version.

3. How can I throw exceptions?

Why do you need exceptions? Do you want to have a possibility to stop generating files when some condition occurs?

4. Have I just incurred into a bug? NT complains at line 55 that IsDto was not found in Custom, while it doesn't complain at line 45. (... )This means that | Array.Filter @Custom.IsDto was erroring out silently! Is that right?!

Apparently so, custom functions were not compiled when you have used a deprecated version of TypesThatContainCustomFunctions. I will need to do something about that, I do not like when something goes silently ....

Etchelon commented 3 years ago

Thanks! And yeah considering your response I think I'll write every function I can in C# to have intellisense. Regarding exceptions, not essential but I'm just used to throwing when the input of a function is something it shouldn't be to make troubleshooting of gross mistakes easier. Moving to C# will also enable this for me 😄

Etchelon commented 3 years ago

I think I've found another issue, not a blocking one. I've moved all custom functions to C# inside NTEConfig, and then I decided to split them into separate static classes for organization: Utils, Interface, Enums and Services, more or less it's always the same. The issue is seems to be with code splitting over multiple files. If I have

// Utils.cs
internal static class UtilFunctions { ... }

// NTEConfig.cs
class NTEConfig : EditorConfig
{
    public override IEnumerable<Type> TypesThatContainCustomFunctions
    {
        get { yield return typeof(UtilFunctions); }
    }
}

does not seem to work. If I rearrange the code like this:

// NTEConfig.cs
internal static class UtilFunctions { ... }

class NTEConfig : EditorConfig
{
    public override IEnumerable<Type> TypesThatContainCustomFunctions
    {
        get { yield return typeof(UtilFunctions); }
    }
}

It does work. It doesn't seem to matter whether UtilFunctions is static or not, as I would expect btw.

NeVeSpl commented 3 years ago

The key is in [NTEditorFile] attribute, only files that contain decorated classes are detected and compiled. There is nothing about that in documentation, because I am not sure about that design. But if you decorate your classes with that attribute it should work.

Etchelon commented 3 years ago

I'm all for semantically meaningful names, so I'd probably just add another attribute called NTCompiled to decorate classes that need to be included in the compilation, and leave NTEditorFile just for the class that inherits EditorConfig. Otherwise, I think the approach is fine.

NeVeSpl commented 3 years ago

But this is exactly the one and only role of this attribute, to mark files that need to be compiled. Configuration class does not need any special attribute, it is detected by implemented interface IEditorConfig.

Etchelon commented 3 years ago

Oh ok, well, not knowing the internals and just by the names, I thought that NTEditorFile was required to detect the main config file. In my mind, there should only be 1 editor config file per solution and multiple other files for spreading the custom functions. Anyway, the approach is fine as it is imho. But what other approaches could there be?

I can think of just another one: reflecting on the types returned by TypesThatContainCustomFunctions and also include those in the compilation. But it might not work for my case, unless you resolve transient dependencies. What I mean by this is that TypesThatContainCustomFunctions currently returns 2 types in my case: InterfaceCustomFunctions, EnumCustomFunctions. Those are now in separate files, and use internal static helpers from a 3rd class, UtilsCustomFunctions, via a using static MyNamespace.UtilsCustomFunctions; declaration at the top of the file, so in my setup the UtilsCustomFunctions.cs file would not be compiled if that class was not decorated with NTEditorFile and if you didn't resolve it as a transient dependency for the 2 classes returned by TypesThatContainCustomFunctions.

Etchelon commented 3 years ago

Another question: any way to get the file path of a symbol? The project I'm trying to apply NT to is structured like this:

Proj
  V1
    class1.cs
    class2.cs
  V2
    class3.cs

and so on. The problem is that class1's namespace does not match the folder structure as it should in C#. So I have no idea what version that Dto is if I can't retrieve the containing file's path.

I'm going to install Resharper to fix namespaces in the whole solution for now, but that could be useful (even for me, if my teammates don't want me to create a huge PR due to Resharper's refactor, which is something that will probably happen 😅 )

NeVeSpl commented 3 years ago

As you can see, it is not exposed right now: SymbolBase.cs but it is available in Roslyn API: ISymbol.Locations so if you want it, you need to create nice PR with tests and implementation. 😄

Etchelon commented 3 years ago

Going from fixing 2 lines in the documentation to working with Roslyn's API seems like a big leap 😅 Never worked with it, I'll give a look but I'm not making any promise. We're not using JS after all :trollface: