void BuildTable <T>(MarkdownBuilder mb, string label, T[] array, IEnumerable <XmlDocumentComment> docs, Func <T, string> type, Func <T, string> name, Func <T, string> finalName) { if (array.Any()) { mb.AppendLine(label); mb.AppendLine(); string[] head = new[] { "Type", "Name", "Summary" }; IEnumerable <T> seq = array; IEnumerable <string[]> data = seq.Select(item2 => { string summary = docs.FirstOrDefault(x => x.MemberName == name(item2))?.Summary ?? ""; return(new string[] { MarkdownBuilder.MarkdownCodeQuote(type(item2)), finalName(item2), summary }); }); mb.Table(head, data); mb.AppendLine(); } }
void BuildTable <T>(MarkdownBuilder mb, string label, T[] array, IEnumerable <XmlDocumentComment> docs, Func <T, string> type, Func <T, string> name, Func <T, string> finalName) { if (array.Any()) { mb.AppendLine(label); mb.AppendLine(); string[] head = (this.type.IsEnum) ? new[] { "Value", "Name", "Summary" } : new[] { "Type", "Name", "Summary" }; IEnumerable <T> seq = array; if (!this.type.IsEnum) { seq = array.OrderBy(x => name(x)); } var data = seq.Select(item2 => { var candidates = docs.Where(x => x.MemberName == name(item2) || x.MemberName.StartsWith(name(item2) + "`")).ToArray(); if (candidates.Count() >= 2 && item2 is MethodBase) { // Attempt to identify method overloads var methodParameterNames = (item2 as MethodBase).GetParameters().Select(p => p.Name); var overloadCandidates = candidates.Where(x => x.Parameters.Keys.SequenceEqual(methodParameterNames)).ToArray(); if (overloadCandidates.Any()) { candidates = overloadCandidates; } } var summary = candidates.FirstOrDefault()?.Summary ?? ""; return(new[] { MarkdownBuilder.MarkdownCodeQuote(type(item2)), finalName(item2), summary }); }); mb.Table(head, data); mb.AppendLine(); } }
private void BuildTable <T> (MarkdownBuilder mb, string label, T[] array, IEnumerable <XmlDocumentComment> docs, Func <T, string> type, Func <T, string> name, Func <T, string> finalName) { if (array.Any()) { mb.Header(4, label); mb.AppendLine(); var head = Type.IsEnum ? new[] { "Value", "Name", "Summary" } : new[] { "Type", "Name", "Summary" }; IEnumerable <T> seq = array; if (!Type.IsEnum) { seq = array.OrderBy(x => name(x)); } var data = seq.Select(item2 => { var summary = docs.FirstOrDefault(x => x.MemberName == name(item2) || x.MemberName.StartsWith(name(item2) + "`"))?.Summary ?? ""; return(new[] { type(item2), finalName(item2), summary }); }); mb.Table(head, data); mb.AppendLine(); } }
public string BuildMethodsPage() { var mb = new MarkdownBuilder(); mb.Header(2, Beautifier.BeautifyType(type, false) + " Methods"); mb.AppendLine(); mb.AppendLine($"Namespace: {type.Namespace}"); mb.AppendLine(); mb.AppendLine($"Assembly: {type.Assembly.ManifestModule.Name}"); mb.AppendLine(); var summary = commentLookup[type.FullName].FirstOrDefault(x => x.MemberType == MemberType.Type)?.Summary ?? string.Empty; if (summary != string.Empty) { mb.AppendLine(summary); } mb.AppendLine(); BuildTable(mb, "Methods", GetMethods(), commentLookup[type.FullName], x => Beautifier.BeautifyType(x.ReturnType), x => x.Name, x => Beautifier.ToMarkdownMethodInfoWithoutParamNames(x)); return(mb.ToString()); }
// 0 = DLL source path, 1 = destination root private static void Main(string[] args) { // put DLL & XML on same directory. string target = null; string dest = "docs"; string namespaceMatch = string.Empty; if (args.Length == 0) { Console.WriteLine("DLL file location is a required argument"); Environment.Exit(1); } if (args.Length == 1) { target = args[0]; } else if (args.Length == 2) { target = args[0]; dest = args[1]; } else if (args.Length == 3) { target = args[0]; dest = args[1]; namespaceMatch = args[2]; } MarkdownableType[] types = MarkdownGenerator.Load(target, namespaceMatch); // Home Markdown Builder MarkdownBuilder homeBuilder = new MarkdownBuilder(); homeBuilder.Header(1, "References"); homeBuilder.AppendLine(); foreach (IGrouping <string, MarkdownableType> g in types.GroupBy(x => x.Namespace).OrderBy(x => x.Key)) { if (!Directory.Exists(dest)) { Directory.CreateDirectory(dest); } homeBuilder.HeaderWithLink(2, g.Key, g.Key); homeBuilder.AppendLine(); StringBuilder sb = new StringBuilder(); foreach (MarkdownableType item in g.OrderBy(x => x.Name)) { homeBuilder.ListLink(MarkdownBuilder.MarkdownCodeQuote(item.BeautifyName), g.Key + "#" + item.BeautifyName.Replace("<", "").Replace(">", "").Replace(",", "").Replace(" ", "-").ToLower()); sb.Append(item.ToString()); } File.WriteAllText(Path.Combine(dest, g.Key + ".md"), sb.ToString()); homeBuilder.AppendLine(); } // Gen Home File.WriteAllText(Path.Combine(dest, "Home.md"), homeBuilder.ToString()); }
private string GeneralToString() { var mb = new MarkdownBuilder(); mb.Header(2, Beautifier.BeautifyType(Type, false)); mb.AppendLine(); var summary = commentLookup[Type.FullName].FirstOrDefault(x => x.MemberType == MemberType.Type)?.Summary; if (!string.IsNullOrWhiteSpace(summary)) { mb.Header(4, "Summary"); mb.AppendLine(summary); } var remarks = commentLookup[Type.FullName].FirstOrDefault(x => x.MemberType == MemberType.Type)?.Remarks; if (!string.IsNullOrWhiteSpace(remarks)) { mb.Header(4, "Remarks"); mb.AppendLine(remarks); } { // Signature code. var stat = (Type.IsAbstract && Type.IsSealed) ? "static " : ""; var abst = (Type.IsAbstract && !Type.IsInterface && !Type.IsSealed) ? "abstract " : ""; var classOrStructOrEnumOrInterface = Type.IsInterface ? "interface" : Type.IsEnum ? "enum" : Type.IsValueType ? "struct" : "class"; var sb = new StringBuilder(); sb.AppendLine($"public {stat}{abst}{classOrStructOrEnumOrInterface} {Beautifier.BeautifyType(Type, true)}"); var impl = string.Join(", ", new[] { Type.BaseType }.Concat(Type.GetInterfaces()).Where(x => x != null && x != typeof(object) && x != typeof(ValueType)).Select(x => Beautifier.BeautifyType(x))); if (impl != "") { sb.AppendLine(" : " + impl); } mb.Code("csharp", sb.ToString()); mb.AppendLine(); } if (Type.IsEnum) { var enums = Enum.GetNames(Type) .Select(x => new { Name = x, Value = (int)Enum.Parse(Type, x) }) .OrderBy(x => x.Value).ToArray(); BuildTable(mb, "Enum", enums, commentLookup[Type.FullName], x => x.Value.ToString(), x => x.Name, x => x.Name); } else { BuildTable(mb, "Fields", GetFields(), commentLookup[Type.FullName], x => Beautifier.BeautifyType(x.FieldType), x => x.Name, x => x.Name); BuildTable(mb, "Properties", GetProperties(), commentLookup[Type.FullName], x => Beautifier.BeautifyType(x.PropertyType), x => x.Name, x => x.Name); BuildTable(mb, "Events", GetEvents(), commentLookup[Type.FullName], x => Beautifier.BeautifyType(x.EventHandlerType), x => x.Name, x => x.Name); BuildTable(mb, "Methods", GetMethods(), commentLookup[Type.FullName], x => Beautifier.BeautifyType(x.ReturnType), x => x.Name, x => Beautifier.ToMarkdownMethodInfo(x)); BuildTable(mb, "Static Fields", GetStaticFields(), commentLookup[Type.FullName], x => Beautifier.BeautifyType(x.FieldType), x => x.Name, x => x.Name); BuildTable(mb, "Static Properties", GetStaticProperties(), commentLookup[Type.FullName], x => Beautifier.BeautifyType(x.PropertyType), x => x.Name, x => x.Name); BuildTable(mb, "Static Methods", GetStaticMethods(), commentLookup[Type.FullName], x => Beautifier.BeautifyType(x.ReturnType), x => x.Name, x => Beautifier.ToMarkdownMethodInfo(x)); BuildTable(mb, "Static Events", GetStaticEvents(), commentLookup[Type.FullName], x => Beautifier.BeautifyType(x.EventHandlerType), x => x.Name, x => x.Name); } return(mb.ToString()); }
private string CommandToString() { var mb = new MarkdownBuilder(); if (HasCommandAlias) { mb.Header(2, CommandAlias); } else { var typeName = Beautifier.BeautifyType(Type, false); typeName = char.ToLowerInvariant(typeName[0]) + (typeName.Length > 1 ? typeName.Substring(1) : string.Empty); mb.Header(2, typeName); } mb.AppendLine(); var summary = commentLookup[Type.FullName].FirstOrDefault(x => x.MemberType == MemberType.Type)?.Summary; if (!string.IsNullOrWhiteSpace(summary)) { mb.Header(4, "Summary"); mb.AppendLine(summary); mb.AppendLine(); } var remarks = commentLookup[Type.FullName].FirstOrDefault(x => x.MemberType == MemberType.Type)?.Remarks; if (!string.IsNullOrWhiteSpace(remarks)) { mb.Header(4, "Remarks"); mb.AppendLine(remarks); mb.AppendLine(); } // Params ---------------------------------------- var parameters = GetParameters().Where(f => f.Name != "Wait" && f.Name != "ConditionalExpression"); if (parameters.Count() > 0) { mb.Header(4, "Parameters"); mb.Append("\n<div class=\"config-table\">\n\n"); mb.Append("ID | Type | Description\n"); mb.Append("--- | --- | ---\n"); foreach (var parameter in parameters) { var paramAlias = parameter.CustomAttributes.FirstOrDefault(a => a.AttributeType.Name == paramAliasAttrName)?.ConstructorArguments[0].Value as string; var paramId = paramAlias ?? parameter.Name; var isNameless = paramId == string.Empty; var isOptional = !parameter.CustomAttributes.Any(a => a.AttributeType.Name == requiredParamAttrName); if (isNameless) { paramId = parameter.Name; } else { paramId = char.ToLowerInvariant(paramId[0]) + (paramId.Length > 1 ? paramId.Substring(1) : string.Empty); } if (isNameless || !isOptional) { var style = (isNameless ? "command-param-nameless " : string.Empty) + (!isOptional ? "command-param-required" : string.Empty); style = style.Trim(); var title = (isNameless ? "Nameless parameter: value should be provided after the command identifer without specifying parameter ID " : string.Empty) + (!isOptional ? " Required parameter: parameter should always be specified" : string.Empty); title = title.Trim(); paramId = $"<span class=\"{style}\" title=\"{title}\">{paramId}</span>"; } var typeName = Beautifier.BeautifyType(parameter.FieldType); var doc = default(XmlDocumentComment); var baseType = Type; while (baseType != null && doc is null) { var key = baseType.FullName != null && baseType.FullName.Contains("[") ? baseType.FullName.GetBefore("[") : baseType.FullName; doc = commentLookup[key].FirstOrDefault(x => x.MemberName == parameter.Name || x.MemberName.StartsWith(parameter.Name + "`")); baseType = baseType.BaseType; } var descr = doc?.Summary ?? string.Empty; mb.Append($"{paramId} | {typeName} | {descr}\n"); } mb.Append("\n</div>\n\n"); } // ----------------------------------------------- var example = commentLookup[Type.FullName].FirstOrDefault(x => x.MemberType == MemberType.Type)?.Example; if (!string.IsNullOrWhiteSpace(example)) { mb.Header(4, "Example"); mb.Code(example); mb.AppendLine(); } return(mb.ToString()); }
public override string ToString() { var mb = new MarkdownBuilder(); var typeCategory = type.IsClass ? " Class" : type.IsInterface ? " Interface" : string.Empty; mb.HeaderWithCode(2, Beautifier.BeautifyType(type, false) + typeCategory); mb.AppendLine(); mb.AppendLine($"Namespace: {type.Namespace}"); mb.AppendLine(); mb.AppendLine($"Assembly: {type.Assembly.ManifestModule.Name}"); mb.AppendLine(); var summary = commentLookup[type.FullName].FirstOrDefault(x => x.MemberType == MemberType.Type)?.Summary ?? ""; if (summary != "") { mb.AppendLine(summary); mb.AppendLine(); } var sb = new StringBuilder(); var isStatic = type.IsAbstract && type.IsSealed; var @sealed = !type.IsAbstract && type.IsSealed ? "sealed " : ""; var stat = isStatic ? "static " : ""; var abst = (type.IsAbstract && !type.IsInterface && !type.IsSealed) ? "abstract " : ""; var classOrStructOrEnumOrInterface = type.IsInterface ? "interface" : type.IsEnum ? "enum" : type.IsValueType ? "struct" : "class"; sb.AppendLine($"public {stat}{@sealed}{abst}{classOrStructOrEnumOrInterface} {Beautifier.BeautifyType(type, true)}"); var impl = string.Join(", ", new[] { type.BaseType }.Concat(type.GetInterfaces()).Where(x => x != null && x != typeof(object) && x != typeof(ValueType)).Select(x => Beautifier.BeautifyType(x))); if (impl != "") { sb.AppendLine(" : " + impl); } mb.Code("csharp", sb.ToString()); var typeParameters = commentLookup[type.FullName].FirstOrDefault(x => x.MemberType == MemberType.Type)?.TypeParameters; if (typeParameters.Count > 0) { mb.Header(3, "Type Parameters"); mb.AppendLine(); mb.Table(new[] { "Name", "Summary" }, typeParameters.Select(x => new[] { x.Key, x.Value })); } mb.AppendLine(); if (type.IsEnum) { var underlyingEnumType = Enum.GetUnderlyingType(type); var enums = Enum.GetNames(type) .Select(x => new { Name = x, Value = (Convert.ChangeType(Enum.Parse(type, x), underlyingEnumType)) }) .OrderBy(x => x.Value) .ToArray(); BuildTable(mb, "Enum", enums, commentLookup[type.FullName], x => x.Value.ToString(), x => x.Name, x => x.Name); } else { BuildTable(mb, "Constructors", GetConstructors(), commentLookup[type.FullName], x => Beautifier.BeautifyType(x.DeclaringType), x => "#ctor", x => Beautifier.ToMarkdownConstructorInfo(x)); BuildTable(mb, "Fields", GetFields(), commentLookup[type.FullName], x => Beautifier.BeautifyType(x.FieldType), x => x.Name, x => x.Name); BuildTable(mb, "Properties", GetProperties(), commentLookup[type.FullName], x => Beautifier.BeautifyType(x.PropertyType), x => x.Name, x => x.Name); BuildTable(mb, "Events", GetEvents(), commentLookup[type.FullName], x => Beautifier.BeautifyType(x.EventHandlerType), x => x.Name, x => x.Name); BuildTable(mb, "Methods", GetMethods(), commentLookup[type.FullName], x => Beautifier.BeautifyType(x.ReturnType), x => x.Name, x => Beautifier.ToMarkdownMethodInfoWithoutParamNames(x)); BuildTable(mb, "Static Fields", GetStaticFields(), commentLookup[type.FullName], x => Beautifier.BeautifyType(x.FieldType), x => x.Name, x => x.Name); BuildTable(mb, "Static Properties", GetStaticProperties(), commentLookup[type.FullName], x => Beautifier.BeautifyType(x.PropertyType), x => x.Name, x => x.Name); BuildTable(mb, "Static Methods", GetStaticMethods(), commentLookup[type.FullName], x => Beautifier.BeautifyType(x.ReturnType), x => x.Name, x => Beautifier.ToMarkdownMethodInfo(x)); BuildTable(mb, "Static Events", GetStaticEvents(), commentLookup[type.FullName], x => Beautifier.BeautifyType(x.EventHandlerType), x => x.Name, x => x.Name); } return(mb.ToString()); }
public string BuildMethodPage(MethodInfo methodInfo) { var mb = new MarkdownBuilder(); mb.HeaderWithCode(2, $"{Beautifier.BeautifyType(type)}.{Beautifier.ToMarkdownMethodInfoWithoutParamNames(methodInfo, true)} Method"); mb.AppendLine(); mb.AppendLine($"Namespace: {type.Namespace}"); mb.AppendLine(); mb.AppendLine($"Assembly: {type.Assembly.ManifestModule.Name}"); mb.AppendLine(); var comment = commentLookup[type.FullName].FirstOrDefault(x => x.MemberName == methodInfo.Name || x.MemberName.StartsWith(methodInfo.Name + "`")); var summary = comment?.Summary ?? string.Empty; if (summary != string.Empty) { mb.AppendLine(summary); mb.AppendLine(); } var sb = new StringBuilder(); var stat = methodInfo.IsStatic ? "static " : ""; var abst = methodInfo.IsAbstract ? "abstract " : ""; var returnType = Beautifier.BeautifyType(methodInfo.ReturnType); sb.Append($"public {stat}{abst}{returnType} {Beautifier.ToMarkdownMethodInfo(methodInfo, true)}"); mb.Code("csharp", sb.ToString()); mb.AppendLine(); if (comment.TypeParameters.Count > 0) { mb.Header(3, "Type Parameters"); mb.AppendLine(); foreach (var tp in comment.TypeParameters) { mb.CodeQuote(tp.Key); mb.AppendLine(); mb.AppendLine(); mb.AppendLine(tp.Value); } mb.AppendLine(); } if (comment.Parameters.Count > 0) { mb.Header(3, "Parameters"); mb.AppendLine(); foreach (var parameter in comment.Parameters) { mb.CodeQuote(parameter.Key); mb.AppendLine(); mb.AppendLine(); mb.AppendLine(parameter.Value); } mb.AppendLine(); } if (comment.Returns.Length > 0 && !comment.Returns.Equals("void")) { mb.Header(3, "Returns"); mb.AppendLine(); mb.CodeQuote(returnType); mb.AppendLine(); mb.AppendLine(); mb.AppendLine(comment.Returns); } return(mb.ToString()); }
// 0 = dll src path, 1 = dest root static void Main(string[] args) { // put dll & xml on same diretory. var target = "UniRx.dll"; // :) string dest = "md"; string namespaceMatch = string.Empty; if (args.Length == 1) { target = args[0]; } else if (args.Length == 2) { target = args[0]; dest = args[1]; } else if (args.Length == 3) { target = args[0]; dest = args[1]; namespaceMatch = args[2]; } var types = new List <MarkdownableType>(); if (target.EndsWith(".dll")) { types.AddRange(MarkdownGenerator.Load(target, namespaceMatch).Where(x => x.Name.EndsWith("Extensions"))); } else { //assume directory: var assemblies = Directory.GetFiles(target, namespaceMatch); foreach (var assembly in assemblies) { if (!assembly.EndsWith(".Tests.dll")) { types.AddRange(MarkdownGenerator.Load(assembly, "").Where(x => x.Name.EndsWith("Extensions"))); } } } // Home Markdown Builder var homeBuilder = new MarkdownBuilder(); homeBuilder.Header(1, "Extension Method Library"); homeBuilder.AppendLine(); foreach (var g in types.GroupBy(x => x.Namespace).OrderBy(x => x.Key)) { if (!Directory.Exists(dest)) { Directory.CreateDirectory(dest); } homeBuilder.HeaderWithLink(2, g.Key, g.Key); homeBuilder.AppendLine(); var sb = new StringBuilder(); foreach (var item in g.OrderBy(x => x.Name)) { homeBuilder.ListLink(MarkdownBuilder.MarkdownCodeQuote(item.BeautifyName), g.Key + "#" + item.BeautifyName.Replace("<", "").Replace(">", "").Replace(",", "").Replace(" ", "-").ToLower()); sb.Append(item.ToString()); } File.WriteAllText(Path.Combine(dest, g.Key + ".md"), sb.ToString()); homeBuilder.AppendLine(); } // Gen Home File.WriteAllText(Path.Combine(dest, "Home.md"), homeBuilder.ToString()); }