Giter Site home page Giter Site logo

Comments (3)

cskeppstedt avatar cskeppstedt commented on August 18, 2024

Hey, that would be very cool. Unfortunately I haven't been able to focus on t4ts lately, but that's definitely worth checking out.

from t4ts.

aersam avatar aersam commented on August 18, 2024

I've implemented it in a very basic way. Have not done a lot ot testing. Here is my tt file:

<#@ template language="C#" debug="true" hostspecific="true" #>
<#@ output extension=".d.ts" #>
<#@ assembly name="System.Core" #>
<#@ assembly name="Microsoft.VisualStudio.Shell.Interop.8.0" #>
<#@ assembly name="EnvDTE" #>
<#@ assembly name="EnvDTE80" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="EnvDTE" #>
<#@ import namespace="Microsoft.VisualStudio.Shell.Interop" #>
<#@ import namespace="Microsoft.VisualStudio.TextTemplating" #>
<#@ Include File="T4TS.tt.settings.t4" #><#= 
    OutputFormatter.GetOutput(GetDataToRender(), GetSettings()) #><#+ 

Settings settings = null;
Settings GetSettings()
{
    if (settings== null)
        settings = Settings.Parse(SettingsValues);

    return settings;
}

List<TypeScriptModule> GetDataToRender() {
    DTE dte = null;

    // Get the DTE service from the host
    var serviceProvider = Host as IServiceProvider;
    if (serviceProvider != null)
        dte = serviceProvider.GetService(typeof(SDTE)) as DTE;

    // Fail if we couldn't get the DTE. This can happen when trying to run in TextTransform.exe
    if (dte == null)
        throw new Exception("Can only execute through the Visual Studio host");

    var project = GetProjectContainingT4File(dte);

    if (project == null)
        throw new Exception("Could not find the VS project containing the T4TS file.");

    var generator = new CodeTraverser(project, GetSettings());
    return generator.GetAllInterfaces().ToList();
}

Project GetProjectContainingT4File(DTE dte) {

    // Find the .tt file's ProjectItem
    ProjectItem projectItem = dte.Solution.FindProjectItem(Host.TemplateFile);

    // If the .tt file is not opened, open it
    if (projectItem.Document == null)
        projectItem.Open(Constants.vsViewKindCode);

    // Mark the .tt file as unsaved. This way it will be saved and update itself next time the
    // project is built. Basically, it keeps marking itself as unsaved to make the next build work.
    // Note: this is certainly hacky, but is the best I could come up with so far.
    projectItem.Document.Saved = false;

    return projectItem.ContainingProject;
}

    public class InterfaceOutputAppender : OutputAppender<TypeScriptInterface>
    {
        private bool InGlobalModule { get; set; }

        public InterfaceOutputAppender(StringBuilder output, int baseIndentation, Settings settings, bool inGlobalModule)
            : base(output, baseIndentation, settings)
        {
            this.InGlobalModule = inGlobalModule;
        }

        public override void AppendOutput(TypeScriptInterface tsInterface)
        {
            BeginInterface(tsInterface);

            AppendMembers(tsInterface);

            if (tsInterface.IndexedType != null)
                AppendIndexer(tsInterface);

            EndInterface();
        }

        private void AppendMembers(TypeScriptInterface tsInterface)
        {
            var appender = new MemberOutputAppender(Output, BaseIndentation + 4, Settings);
            foreach (var member in tsInterface.Members)
                appender.AppendOutput(member);
        }

        private void BeginInterface(TypeScriptInterface tsInterface)
        {
            AppendIndentedLine("/** Generated from " + tsInterface.FullName + " **/");

            if (InGlobalModule)
                AppendIndented("interface " + tsInterface.Name);
            else
                AppendIndented("export interface " + tsInterface.Name);

            if (tsInterface.Parent != null)
                Output.Append(" extends " + (tsInterface.Parent.Module.IsGlobal ? "" : tsInterface.Parent.Module.QualifiedName + ".") + tsInterface.Parent.Name);

            Output.AppendLine(" {");
        }

        private void EndInterface()
        {
            AppendIndentedLine("}");
        }

        private void AppendIndexer(TypeScriptInterface tsInterface)
        {
            AppendIndendation();
            Output.AppendFormat("    [index: number]: {0};", tsInterface.IndexedType);
            Output.AppendLine();
        }
    }

    public class MemberOutputAppender : OutputAppender<TypeScriptInterfaceMember>
    {
        public MemberOutputAppender(StringBuilder output, int baseIndentation, Settings settings)
            : base(output, baseIndentation, settings)
        {
        }

        public override void AppendOutput(TypeScriptInterfaceMember member)
        {
            AppendIndendation();

            bool isOptional = member.Optional || (member.Type is NullableType);
            string type = member.Type.ToString();

            if (member.Type is BoolType)
            {
                if (Settings.CompatibilityVersion != null && Settings.CompatibilityVersion < new Version(0, 9, 0))
                    type = "bool";
                else
                    type = "boolean";
            }

            Output.AppendFormat("{0}{1}: {2}",
                member.Name,
                (isOptional ? "?" : ""),
                type
            );

            Output.AppendLine(";");
        }
    }

    public class ModuleOutputAppender : OutputAppender<TypeScriptModule>
    {
        public ModuleOutputAppender(StringBuilder output, int baseIndentation, Settings settings)
            : base(output, baseIndentation, settings)
        {
        }

        public override void AppendOutput(TypeScriptModule module)
        {
            BeginModule(module);

            var interfaceAppender = new InterfaceOutputAppender(Output, BaseIndentation + 4, Settings, module.IsGlobal);
            foreach (var tsInterface in module.Interfaces)
                interfaceAppender.AppendOutput(tsInterface);

            EndModule(module);
        }

        private void BeginModule(TypeScriptModule module)
        {
            if (module.IsGlobal)
            {
                Output.AppendLine("// -- Begin global interfaces");
            }
            else
            {
                if (Settings.CompatibilityVersion != null && Settings.CompatibilityVersion < new Version(0, 9, 0))
                    Output.Append("module ");
                else
                    Output.Append("declare module ");

                Output.Append(module.QualifiedName);
                Output.AppendLine(" {");
            }
        }

        private void EndModule(TypeScriptModule module)
        {
            if (module.IsGlobal)
                Output.AppendLine("// -- End global interfaces");
            else
                Output.AppendLine("}");
        }
    }

    public abstract class OutputAppender<TSegment> where TSegment: class
    {
        protected StringBuilder Output { get; private set; }
        protected int BaseIndentation { get; private set; }
        protected Settings Settings { get; private set; }

        public OutputAppender(StringBuilder output, int baseIndentation, Settings settings)
        {
            if (output == null)
                throw new ArgumentNullException("output");

            if (settings == null)
                throw new ArgumentNullException("settings");

            this.Output = output;
            this.BaseIndentation = baseIndentation;
            this.Settings = settings;
        }

        public abstract void AppendOutput(TSegment segment);

        protected void AppendIndented(string text)
        {
            AppendIndendation();
            Output.Append(text);
        }

        protected void AppendIndentedLine(string line)
        {
            AppendIndendation();
            Output.AppendLine(line);
        }

        protected void AppendIndendation()
        {
            Output.Append(' ', BaseIndentation);
        }

        public override string ToString()
        {
            return Output.ToString();
        }
    }

    public static class OutputFormatter
    {
        public static string GetOutput(List<TypeScriptModule> modules, Settings settings)
        {
            var output = new StringBuilder();

            output.AppendLine("/****************************************************************************");
            output.AppendLine("  Generated by T4TS.tt - don't make any changes in this file");
            output.AppendLine("****************************************************************************/");

            var moduleAppender = new ModuleOutputAppender(output, 0, settings);
            foreach (var module in modules)
            {
                output.AppendLine();
                moduleAppender.AppendOutput(module);
            }

            return output.ToString();
        }
    }

    public class TypeScriptInterface
    {
        public string Name { get; set; }
        public string FullName { get; set; }

        public List<TypeScriptInterfaceMember> Members { get; set; }
        public TypescriptType IndexedType { get; set; }
        public TypeScriptInterface Parent { get; set; }
        public TypeScriptModule Module { get; set; }

        public TypeScriptInterface()
        {
            Members = new List<TypeScriptInterfaceMember>();
        }
    }

    public class TypeScriptInterfaceAttributeValues
    {
        public string Module { get; set; }
        public string Name { get; set; }
        public string NamePrefix { get; set; }
    }

    public class TypeScriptInterfaceMember
    {
        public string Name { get; set; }
        public TypescriptType Type { get; set; }
        public bool Optional { get; set; }
        public string FullName { get; set; }
        public bool Ignore { get; set; }
    }

    public class TypeScriptMemberAttributeValues
    {
        public string Name { get; set; }
        public bool Optional { get; set; }
        public string Type { get; set; }
        public bool CamelCase { get; set; }
        public bool Ignore { get; set; }
    }

    public class TypeScriptModule
    {
        public string QualifiedName { get; set; }
        public List<TypeScriptInterface> Interfaces { get; set; }

        /// <summary>
        /// Returns true if this is the global namespace (ie. no module name)
        /// </summary>
        public bool IsGlobal
        {
            get { return string.IsNullOrWhiteSpace(QualifiedName); }
        }

        public TypeScriptModule()
        {
            Interfaces = new List<TypeScriptInterface>();
        }
    }

    public class ClassTraverser
    {
        public CodeClass CodeClass { get; private set; }
        public Action<CodeProperty> WithProperty { get; set; }

        public ClassTraverser(CodeClass codeClass, Action<CodeProperty> withProperty)
        {
            if (codeClass == null)
                throw new ArgumentNullException("codeClass");

            if (withProperty == null)
                throw new ArgumentNullException("withProperty");

            this.CodeClass = codeClass;
            this.WithProperty = withProperty;

            if (codeClass.Members != null)
                Traverse(codeClass.Members);
        }

        private void Traverse(CodeElements members)
        {
            foreach (var property in members.OfType<CodeProperty>())
                WithProperty(property);
        }
    }

    public class NamespaceTraverser
    {
        public Action<CodeClass> WithCodeClass { get; private set; }

        public NamespaceTraverser(CodeNamespace ns, Action<CodeClass> withCodeClass)
        {
            if (ns == null)
                throw new ArgumentNullException("ns");

            if (withCodeClass == null)
                throw new ArgumentNullException("withCodeClass");

            WithCodeClass = withCodeClass;

            if (ns.Members != null)
                Traverse(ns.Members);
        }

        private void Traverse(CodeElements members)
        {
            foreach (var codeClass in members.OfType<CodeClass>())
                WithCodeClass(codeClass);
        }
    }

    public class ProjectTraverser
    {
        public Action<CodeNamespace> WithNamespace { get; private set; }

        public ProjectTraverser(Project project, Action<CodeNamespace> withNamespace)
        {
            if (project == null)
                throw new ArgumentNullException("project");

            if (withNamespace == null)
                throw new ArgumentNullException("withNamespace");

            WithNamespace = withNamespace;

            if (project.ProjectItems != null)
                Traverse(project.ProjectItems);
        }

        private void Traverse(ProjectItems items)
        {
            foreach (ProjectItem pi in items)
            {
                if (pi.FileCodeModel != null)
                {
                    var codeElements = pi.FileCodeModel.CodeElements;
                    foreach (var ns in codeElements.OfType<CodeNamespace>())
                        WithNamespace(ns);
                }

                if (pi.ProjectItems != null)
                    Traverse(pi.ProjectItems);
            }
        }
    }

    public class ArrayType: TypescriptType
    {
        public TypescriptType ElementType { get; set; }

        public override string ToString()
        {
            return ElementType.ToString() + "[]";
        }
    }

    public class BoolType: TypescriptType
    {
        public override string Name
        {
            get { return "boolean"; }
        }
    }

    public class DateTimeType : TypescriptType
    {
        public override string Name
        {
            get { return "Date"; }
        }
    }

    public class GuidType : TypescriptType
    {
        public override string Name
        {
            get { return "string"; }
        }
    }

    public class InterfaceType : TypescriptType
    {
        public TypeScriptInterfaceAttributeValues AttributeValues { get; private set; }

        public string QualifedModule 
        { 
            get 
            {
                if (AttributeValues == null)
                    return null;

                return AttributeValues.Module; 
            } 
        }

        public override string Name
        {
            get
            {
                if (!string.IsNullOrEmpty(AttributeValues.NamePrefix))
                    return AttributeValues.NamePrefix + AttributeValues.Name;

                return AttributeValues.Name;
            }
        }

        public InterfaceType(TypeScriptInterfaceAttributeValues values)
        {
            AttributeValues = values;
        }

        public InterfaceType(string name)
        {
            AttributeValues = new TypeScriptInterfaceAttributeValues
            {
                Name = name
            };
        }

        public override string ToString()
        {
            if (string.IsNullOrWhiteSpace(QualifedModule))
                return base.ToString();

            return QualifedModule + "." + base.ToString();
        }
    }

    public class NullableType : TypescriptType
    {
        public TypescriptType WrappedType { get; set; }

        public override string ToString()
        {
            return WrappedType.ToString();
        }
    }

    public class NumberType : TypescriptType
    {
        public override string Name
        {
            get { return "number"; }
        }
    }

    public class StringType: TypescriptType
    {
        public override string Name
        {
            get { return "string"; }
        }
    }

    public class TypescriptType
    {
        public virtual string Name { get { return "any"; } }

        public override string ToString()
        {
            return Name;
        }
    }

    public class DebugType : TypescriptType
    {
        private string name;
        public DebugType(string name)
        {
            this.name=name;
        }

        public override string Name { get { return "any /*" + this.name + "*/"; } }

        public override string ToString()
        {
            return Name;
        }
    }

    public class CustomType : TypescriptType
    {
        private string name;
        public CustomType(string name)
        {
            this.name=name;
        } 

        public override string Name { get { return this.name; } }

        public override string ToString()
        {
            return Name;
        }
    }

    public class CodeTraverser
    {
        public Project Project { get; private set; }
        public Settings Settings { get; private set; }

        private static readonly string InterfaceAttributeFullName = "T4TS.TypeScriptInterfaceAttribute";
        private static readonly string MemberAttributeFullName = "T4TS.TypeScriptMemberAttribute";

        public CodeTraverser(Project project, Settings settings)
        {
            if (project == null)
                throw new ArgumentNullException("project");

            if (settings == null)
                throw new ArgumentNullException("settings");

            this.Project = project;
            this.Settings = settings;
        }

        public TypeContext BuildContext()
        {
            var typeContext = new TypeContext(this.Settings);
            var partialClasses = new Dictionary<string, CodeClass>();

            new ProjectTraverser(this.Project, (ns) =>
            {
                new NamespaceTraverser(ns, (codeClass) =>
                {
                    CodeAttribute attribute;
                    if (!TryGetAttribute(codeClass.Attributes, InterfaceAttributeFullName, out attribute))
                        return;

                    var values = GetInterfaceValues(codeClass, attribute);
                    var interfaceType = new InterfaceType(values);

                    if (!typeContext.ContainsInterfaceType(codeClass.FullName))
                        typeContext.AddInterfaceType(codeClass.FullName, interfaceType);
                });
            });

            return typeContext;
        }

        public IEnumerable<TypeScriptModule> GetAllInterfaces()
        {
            var typeContext = BuildContext();
            var byModuleName = new Dictionary<string, TypeScriptModule>();
            var tsMap = new Dictionary<CodeClass, TypeScriptInterface>();

            new ProjectTraverser(this.Project, (ns) =>
            {
                new NamespaceTraverser(ns, (codeClass) =>
                {
                    InterfaceType interfaceType;
                    if (!typeContext.TryGetInterfaceType(codeClass.FullName, out interfaceType))
                        return;

                    var values = interfaceType.AttributeValues;

                    TypeScriptModule module;
                    if (!byModuleName.TryGetValue(values.Module, out module))
                    {
                        module = new TypeScriptModule { QualifiedName = values.Module };
                        byModuleName.Add(values.Module, module);
                    }

                    var tsInterface = BuildInterface(codeClass, values, typeContext);
                    tsMap.Add(codeClass, tsInterface);
                    tsInterface.Module = module;
                    module.Interfaces.Add(tsInterface);
                });
            });

            var tsInterfaces = tsMap.Values.ToList();
            tsMap.Keys.ToList().ForEach(codeClass =>
            {
                CodeElements baseClasses = codeClass.Bases;
                if (baseClasses.Count > 0)
                {
                    CodeElement baseClass = baseClasses.Item(1);
                    if (baseClass != null)
                    {
                        var parent = tsInterfaces.SingleOrDefault(intf => intf.FullName == baseClass.FullName);
                        if (parent != null)
                        {
                            tsMap[codeClass].Parent = parent;
                        }
                    }
                }
            });

            return byModuleName.Values
                .OrderBy(m => m.QualifiedName)
                .ToList();
        }

        private string GetInterfaceName(TypeScriptInterfaceAttributeValues attributeValues)
        {
            if (!string.IsNullOrEmpty(attributeValues.NamePrefix))
                return attributeValues.NamePrefix + attributeValues.Name;

            return attributeValues.Name;
        }

        private TypeScriptInterface BuildInterface(CodeClass codeClass, TypeScriptInterfaceAttributeValues attributeValues, TypeContext typeContext)
        {
            var tsInterface = new TypeScriptInterface
            {
                FullName = codeClass.FullName,
                Name = GetInterfaceName(attributeValues)
            };

            TypescriptType indexedType;
            if (TryGetIndexedType(codeClass, typeContext, out indexedType))
                tsInterface.IndexedType = indexedType;

            new ClassTraverser(codeClass, (property) =>
            {
                TypeScriptInterfaceMember member;
                if (TryGetMember(property, typeContext, out member))
                    tsInterface.Members.Add(member);
            });

            return tsInterface;
        }

        private bool TryGetAttribute(CodeElements attributes, string attributeFullName, out CodeAttribute attribute)
        {
            foreach (CodeAttribute attr in attributes)
            {
                if (attr.FullName == attributeFullName)
                {
                    attribute = attr;
                    return true;
                }
            }

            attribute = null;
            return false;
        }

        private bool TryGetIndexedType(CodeClass codeClass, TypeContext typeContext, out TypescriptType indexedType)
        {
            indexedType = null;
            if (codeClass.Bases == null || codeClass.Bases.Count == 0)
                return false;

            foreach (CodeElement baseClass in codeClass.Bases)
            {
                if (typeContext.IsGenericEnumerable(baseClass.FullName))
                {
                    string fullName = typeContext.UnwrapGenericType(baseClass.FullName);
                    indexedType = typeContext.GetTypeScriptType(fullName);
                    return true;
                }
            }

            return false;
        }

        private TypeScriptInterfaceAttributeValues GetInterfaceValues(CodeClass codeClass, CodeAttribute interfaceAttribute)
        {
            var values = GetAttributeValues(interfaceAttribute);

            return new TypeScriptInterfaceAttributeValues
            {
                Name = values.ContainsKey("Name") ? values["Name"] : GetClassName(codeClass.FullName),
                Module = values.ContainsKey("Module") ? values["Module"] : Settings.DefaultModule ?? "T4TS",
                NamePrefix = values.ContainsKey("NamePrefix") ? values["NamePrefix"] : Settings.DefaultInterfaceNamePrefix ?? string.Empty
            };
        }

        private string GetClassName(string fullName)
        {
            if(fullName.Contains("."))
            {
                var parts= fullName.Split('.');
                return parts[parts.Length-1];
            }
            return fullName;
        }

        private bool TryGetMember(CodeProperty property, TypeContext typeContext, out TypeScriptInterfaceMember member)
        {
            member = null;
            if (property.Access != vsCMAccess.vsCMAccessPublic)
                return false;

            var getter = property.Getter;
            if (getter == null)
                return false;

            var values = GetMemberValues(property, typeContext);
            member = new TypeScriptInterfaceMember
            {
                Name = values.Name ?? property.Name,
                FullName = property.FullName,
                Optional = values.Optional,
                Ignore = values.Ignore,
                Type = (string.IsNullOrWhiteSpace(values.Type))
                    ? typeContext.GetTypeScriptType(getter.Type)
                    : new InterfaceType(values.Type)
            };

            if (member.Ignore)
            {
                return false;
            }

            if (values.CamelCase && values.Name == null)
                member.Name = member.Name.Substring(0, 1).ToLowerInvariant() + member.Name.Substring(1);

            return true;
        }

        private TypeScriptMemberAttributeValues GetMemberValues(CodeProperty property, TypeContext typeContext)
        {
            bool? attributeOptional = null;
            bool? attributeCamelCase = null;
            bool attributeIgnore = false;
            string attributeName = null;
            string attributeType = null;

            CodeAttribute attribute;
            if (TryGetAttribute(property.Attributes, MemberAttributeFullName, out attribute))
            {
                var values = GetAttributeValues(attribute);
                if (values.ContainsKey("Optional"))
                    attributeOptional = values["Optional"] == "true";

                if (values.ContainsKey("CamelCase"))
                    attributeCamelCase = values["CamelCase"] == "true";

                if (values.ContainsKey("Ignore"))
                    attributeIgnore = values["Ignore"] == "true";

                values.TryGetValue("Name", out attributeName);
                values.TryGetValue("Type", out attributeType);
            }

            return new TypeScriptMemberAttributeValues
            {
                Optional = attributeOptional.HasValue ? attributeOptional.Value : Settings.DefaultOptional,
                Name = attributeName,
                Type = attributeType,
                CamelCase = attributeCamelCase ?? Settings.DefaultCamelCaseMemberNames,
                Ignore = attributeIgnore
            };
        }

        private Dictionary<string, string> GetAttributeValues(CodeAttribute codeAttribute)
        {
            var values = new Dictionary<string, string>();
            foreach (CodeElement child in codeAttribute.Children)
            {
                var property = (EnvDTE80.CodeAttributeArgument)child;
                if (property == null || property.Value == null)
                    continue;

                // remove quotes if the property is a string
                string val = property.Value ?? string.Empty;
                if (val.StartsWith("\"") && val.EndsWith("\""))
                    val = val.Substring(1, val.Length - 2);

                values.Add(property.Name, val);
            }

            return values;
        }
    }

    public class Settings
    {
        /// <summary>
        /// The default module of the generated interface, if not specified by the TypeScriptInterfaceAttribute
        /// </summary>
        public string DefaultModule { get; set; }

        /// <summary>
        /// The default value for Optional, if not specified by the TypeScriptMemberAttribute
        /// </summary>
        public bool DefaultOptional { get; set; }

        /// <summary>
        /// The default value for the CamelCase flag for an interface member name, if not specified by the TypeScriptMemberAttribute
        /// </summary>
        public bool DefaultCamelCaseMemberNames { get; set; }

        /// <summary>
        /// The default string to prefix interface names with. For instance, you might want to prefix the names with an "I" to get conventional interface names.
        /// </summary>
        public string DefaultInterfaceNamePrefix { get; set; }

        /// <summary>
        /// The version of Typescript that is targeted
        /// </summary>
        public Version CompatibilityVersion { get; set; }

        /// <summary>
        /// If true translates System.DateTime to native date
        /// </summary>
        public bool UseNativeDates { get; set; }

        public static Settings Parse(Dictionary<string,object> settingsValues)
        {
            // Read settings from T4TS.tt.settings.tt
            return new Settings
            {
                DefaultModule = ParseSettingReferenceType(settingsValues, "DefaultModule", s => s as string, "T4TS"),
                DefaultOptional = ParseSettingNullableType(settingsValues, "DefaultOptional", false),
                DefaultCamelCaseMemberNames = ParseSettingNullableType(settingsValues, "DefaultCamelCaseMemberNames", false),
                DefaultInterfaceNamePrefix = ParseSettingReferenceType(settingsValues, "DefaultInterfaceNamePrefix", s => s as string, string.Empty),
                CompatibilityVersion = ParseSettingReferenceType(settingsValues, "CompatibilityVersion", v => v as Version, new Version(0, 9, 1, 1)),
                UseNativeDates = ParseSettingNullableType(settingsValues, "UseNativeDates", false)
            };
        }

        private static T ParseSettingReferenceType<T>(Dictionary<string, object> settingsValues, string key, Func<object, T> convert, T defaultValue) where T : class
        {
            object val;
            if (settingsValues.TryGetValue(key, out val))
                return convert(val) ?? defaultValue;

            return defaultValue;
        }

        private static T ParseSettingNullableType<T>(Dictionary<string, object> settingsValues, string key, T defaultValue) where T : struct
        {
            object val;
            if (settingsValues.TryGetValue(key, out val))
            {
                var nullable = val as Nullable<T>;
                if (nullable == null || !nullable.HasValue)
                    return defaultValue;

                return nullable.Value;
            }

            return defaultValue;
        }

        private static T ParseConfigValueType<T>(Dictionary<string, object> settingsValues, string key, Func<object, T> convert, T defaultValue)
        {
            object val;
            if (settingsValues.TryGetValue(key, out val))
                return convert(val);

            return defaultValue;
        }
    }

    public class TypeContext
    {
        public Settings Settings { get; private set; }
        public TypeContext(Settings settings)
        {
            this.Settings = settings;
        }

        private static readonly string[] genericCollectionTypeStarts = new string[] {
            "System.Collections.Generic.List<",
            "System.Collections.Generic.IList<",
            "System.Collections.Generic.ICollection<"
        };

        private static readonly string nullableTypeStart = "System.Nullable<";

        /// <summary>
        /// Lookup table for "interface types", ie. non-builtin types (typically classes or unknown types). Keyed on the FullName of the type.
        /// </summary>
        private Dictionary<string, InterfaceType> interfaceTypes = new Dictionary<string, InterfaceType>();

        public void AddInterfaceType(string typeFullName, InterfaceType interfaceType)
        {
            interfaceTypes.Add(typeFullName, interfaceType);
        }

        public bool TryGetInterfaceType(string typeFullName, out InterfaceType interfaceType)
        {
            return interfaceTypes.TryGetValue(typeFullName, out interfaceType);
        }

        public bool ContainsInterfaceType(string typeFullName)
        {
            return interfaceTypes.ContainsKey(typeFullName);
        }

        public TypescriptType GetTypeScriptType(CodeTypeRef codeType)
        {
            switch (codeType.TypeKind)
            {
                case vsCMTypeRef.vsCMTypeRefChar:
                case vsCMTypeRef.vsCMTypeRefString:
                    return new StringType();

                case vsCMTypeRef.vsCMTypeRefBool:
                    return new BoolType();

                case vsCMTypeRef.vsCMTypeRefByte:
                case vsCMTypeRef.vsCMTypeRefDouble:
                case vsCMTypeRef.vsCMTypeRefInt:
                case vsCMTypeRef.vsCMTypeRefShort:
                case vsCMTypeRef.vsCMTypeRefFloat:
                case vsCMTypeRef.vsCMTypeRefLong:
                case vsCMTypeRef.vsCMTypeRefDecimal:
                    return new NumberType();

                default:
                    return TryResolveType(codeType);
            }
        }

        private TypescriptType TryResolveType(CodeTypeRef codeType)
        {
            if (codeType.TypeKind == vsCMTypeRef.vsCMTypeRefArray)
            {
                return new ArrayType()
                {
                    ElementType = GetTypeScriptType(codeType.ElementType)
                };
            }
            if(codeType.TypeKind == vsCMTypeRef.vsCMTypeRefOther && codeType.AsFullName.Split('.').Length==1)
            {//A generic type
                return new CustomType(codeType.AsFullName);
            }
            return GetTypeScriptType(codeType.AsFullName);
        }

        private ArrayType TryResolveEnumerableType(string typeFullName)
        {
            return new ArrayType
            {
                ElementType = GetTypeScriptType(typeFullName)
            };
        }

        public TypescriptType GetTypeScriptType(string typeFullName)
        {
            InterfaceType interfaceType;
            if (interfaceTypes.TryGetValue(typeFullName, out interfaceType))
                return interfaceType;

            if (IsGenericEnumerable(typeFullName))
            {
                return new ArrayType
                {
                    ElementType = GetTypeScriptType(UnwrapGenericType(typeFullName))
                };
            }
            else if (IsNullable(typeFullName))
            {
                return new NullableType
                {
                    WrappedType = GetTypeScriptType(UnwrapGenericType(typeFullName))
                };
            }

            switch (typeFullName)
            {
                case "System.Guid":
                    return new GuidType();

                case "System.Double":
                case "System.Int16":
                case "System.Int32":
                case "System.Int64":
                case "System.UInt16":
                case "System.UInt32":
                case "System.UInt64":
                case "System.Decimal":
                case "System.Byte":
                case "System.SByte":
                case "System.Single":
                    return new NumberType();
                case "System.Boolean":
                    return new BoolType(); 

                case "System.String":
                    return new StringType();

                case "System.DateTime":
                    if (Settings.UseNativeDates)
                        return new DateTimeType();
                    else
                        return new StringType();

                case "System.Object":
                    return new TypescriptType();
                default:
                    return new DebugType(typeFullName);
            }
        }

        private bool IsNullable(string typeFullName)
        {
            return typeFullName.StartsWith(nullableTypeStart);
        }

        public string UnwrapGenericType(string typeFullName)
        {
            int firstIndex = typeFullName.IndexOf('<');
            return typeFullName.Substring(firstIndex+1, typeFullName.Length - firstIndex- 2);
        }

        public bool IsGenericEnumerable(string typeFullName)
        {
            return genericCollectionTypeStarts.Any(t => typeFullName.StartsWith(t));
        }
    }
#>

from t4ts.

cskeppstedt avatar cskeppstedt commented on August 18, 2024

The project is in a much better shape to do something like this now (has tests, for one). It should be easier to implement generics if need be, I'm closing this issue in the meanwhile.

from t4ts.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.