red-gate / XmlDoc2CmdletDoc

Create cmdlet XML help files from XML doc comments
Other
63 stars 24 forks source link

Feature/#37 handle array element types #38

Closed hsimah closed 6 years ago

hsimah commented 7 years ago

for #37 and #33 + some upgrades to C#7 and latest NUnit.

ChrisLambrou commented 6 years ago

I think this may have to be approached from two separate points of view.

  1. In the comment readers, when retrieving the XML Doc comment for a type, we should convert things such as T[] and IEnumerable<T> to their inner element type, T.
  2. Where types are displayed in other elements, we should convert them to a more readable representation. For example:
    <dev:type>
        <maml:name>System.Collections.Generic.IEnumerable`1[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]</maml:name>
        <maml:uri />
    </dev:type>

    should become:

    <dev:type>
        <maml:name>IEnumerable&lt;int&gt;</maml:name>
        <maml:uri />
    </dev:type>
hsimah commented 6 years ago

Looking at Microsoft cmdlets, they do report parameter types as the T[] and my code simply gives T. I agree we should be telling the end user that the parameter accepts a collection of inputs.

-I am a little busy at work this week, but if I get time I will play around with trying to output a better type.- Personally I would prefer T[] over IEnumerable<T> as it's more powershell-like. I am happy to do what you feel is best, though considering it is your tool.

Shall we move discussion to a new issue or continue in #33 ?

I came up with this:

    /// <summary>
    /// The type of the parameter.
    /// </summary>
    public Type ParameterType
    {
        get
        {
            switch (MemberInfo.MemberType)
            {
                case MemberTypes.Property:
                    return ((PropertyInfo)MemberInfo).PropertyType;
                case MemberTypes.Field:
                    return ((FieldInfo)MemberInfo).FieldType;
                default:
                    throw new NotSupportedException("Unsupported type: " + MemberInfo);
            }
        }
    }

    /// <summary>
    /// The element type of the parameter
    /// </summary>
    /// <remarks>Will return null if the parameter type is not an array</remarks>
    public Type ParameterElementType
    {
        get
        {
            switch (MemberInfo.MemberType)
            {
                case MemberTypes.Property:
                    var propertyType = ((PropertyInfo)MemberInfo).PropertyType;
                    return propertyType.GetElementType();
                case MemberTypes.Field:
                    var fieldType = ((FieldInfo)MemberInfo).FieldType;
                    return fieldType.GetElementType();
                default:
                    throw new NotSupportedException("Unsupported type: " + MemberInfo);
            }
        }
    }

And

        /// <summary>
        /// The list of enumerated value names. Returns an empty sequence if there are no enumerated values
        /// (normally because the parameter type is not an Enum type).
        /// </summary>
        public IEnumerable<string> EnumValues
        {
            get
            {
                if (MemberInfo.MemberType == MemberTypes.Property)
                {
                    var type = ParameterType;

                    if (type.IsArray)
                    {
                        type = ParameterElementType;
                    }

                    if (type.IsEnum)
                    {
                        return type
                            .GetFields(BindingFlags.Public | BindingFlags.Static)
                            .Select(field => field.Name);
                    }
                }
                return Enumerable.Empty<string>();
            }
        }

This of course only works for array parameters. I only ever use those as I based my binary cmdlets on the Microsoft ones, for better or worse. This will result in the following markup:

For string[] parameter:

    <command:parameterValue required="true">string[]</command:parameterValue>
    <dev:type>
        <maml:name>System.String[]</maml:name>
        <maml:uri />
    </dev:type>

For MyType[] parameter:

<command:parameterValue required="true">MyType[]</command:parameterValue>
<dev:type>
    <maml:name>Cmdlets.MyType[]</maml:name>
    <maml:uri />
</dev:type>
<dev:defaultValue>Enum1</dev:defaultValue>
<command:parameterValueGroup>
    <command:parameterValue required="false" variableLength="false">Enum1</command:parameterValue>
    <command:parameterValue required="false" variableLength="false">Enum2</command:parameterValue>
    <command:parameterValue required="false" variableLength="false">Enum3</command:parameterValue>
</command:parameterValueGroup>

There will be other pleases to do this sort of check, but as an idea I think this would work.