Skip to content

Commit

Permalink
detect assembly name
Browse files Browse the repository at this point in the history
  • Loading branch information
Charles DE VANDIERE committed Nov 20, 2019
1 parent b1b3361 commit 6632276
Show file tree
Hide file tree
Showing 7 changed files with 116 additions and 70 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

Tool to generate markdown from C# XML documentation, based on [MarkdownGenerator](https://github.com/neuecc/MarkdownGenerator) project.

[![Build Status](https://dev.azure.com/charlesdevandiere/charlesdevandiere/_apis/build/status/charlesdevandiere.xmldoc2md?branchName=master)](https://dev.azure.com/charlesdevandiere/charlesdevandiere/_build/latest?definitionId=2&branchName=master)

[![Nuget](https://img.shields.io/nuget/v/XMLDoc2Markdown.svg?color=blue&logo=nuget)](https://www.nuget.org/packages/XMLDoc2Markdown)

## How to use

### Install tool
Expand Down
28 changes: 28 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,31 @@
# XMLDoc2Markdown

Tool to generate markdown from C# XML documentation, based on [MarkdownGenerator](https://github.com/neuecc/MarkdownGenerator) project.

See generated sample documentation [here](sample).

## How to use

### Install tool

```shell
> dotnet tool install -g XMLDoc2Markdown
```

### Generate documentation

```shell
> xmldoc2md <DLL_SOURCE_PATH> <OUTPUT_DIRECTORY>
```

#### Example

```shell
> xmldoc2md Sample.dll docs
```

### Display command line help

```shell
> xmldoc2md -h
```
6 changes: 3 additions & 3 deletions docs/sample/Home.md → docs/sample/README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# References
# MyClassLib

## MyClassLib
## `MyClassLib`

- [`Class1`](MyClassLib/Class1)

## MyClassLib.SubNamespace
## `MyClassLib.SubNamespace`

- [`GenericClass<T>`](MyClassLib.SubNamespace/GenericClass{T})
- [`SubClass`](MyClassLib.SubNamespace/SubClass)
2 changes: 2 additions & 0 deletions generate-sample-doc.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
dotnet publish .\sample\MyClassLib\MyClassLib.csproj -c Release -o publish
dotnet run -p .\src\XMLDoc2Markdown\XMLDoc2Markdown.csproj .\publish\MyClassLib.dll .\docs\sample
56 changes: 0 additions & 56 deletions src/XMLDoc2Markdown/MarkdownGenerator.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml.Linq;

namespace XMLDoc2Markdown
{
Expand Down Expand Up @@ -215,57 +212,4 @@ public override string ToString()
return mb.ToString();
}
}


public static class MarkdownGenerator
{
public static MarkdownableType[] Load(string dllPath, string namespaceMatch)
{
var xmlPath = Path.Combine(Directory.GetParent(dllPath).FullName, Path.GetFileNameWithoutExtension(dllPath) + ".xml");

XmlDocumentComment[] comments = new XmlDocumentComment[0];
if (File.Exists(xmlPath))
{
comments = VSDocParser.ParseXmlComment(XDocument.Parse(File.ReadAllText(xmlPath)), namespaceMatch);
}
var commentsLookup = comments.ToLookup(x => x.ClassName);

var namespaceRegex =
!string.IsNullOrEmpty(namespaceMatch) ? new Regex(namespaceMatch) : null;

var markdownableTypes = new[] { Assembly.LoadFrom(dllPath) }
.SelectMany(x =>
{
try
{
return x.GetTypes();
}
catch (ReflectionTypeLoadException ex)
{
return ex.Types.Where(t => t != null);
}
catch
{
return Type.EmptyTypes;
}
})
.Where(x => x != null)
.Where(x => x.IsPublic && !typeof(Delegate).IsAssignableFrom(x) && !x.GetCustomAttributes<ObsoleteAttribute>().Any())
.Where(x => IsRequiredNamespace(x, namespaceRegex))
.Select(x => new MarkdownableType(x, commentsLookup))
.ToArray();


return markdownableTypes;
}

static bool IsRequiredNamespace(Type type, Regex regex)
{
if (regex == null)
{
return true;
}
return regex.IsMatch(type.Namespace != null ? type.Namespace : string.Empty);
}
}
}
22 changes: 11 additions & 11 deletions src/XMLDoc2Markdown/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,37 +43,37 @@ static void Main(string[] args)
string src = srcArg.Value;
string @out = outArg.Value;
string namespaceMatch = namespaceMatchOption.Value();
string homePageName = homePageNameOption.HasValue() ? homePageNameOption.Value() : "Home";
string homePageName = homePageNameOption.HasValue() ? homePageNameOption.Value() : "README";

var types = MarkdownGenerator.Load(src, namespaceMatch);
var xmlDocumentation = new XmlDocumentation(src, namespaceMatch);

if (!Directory.Exists(@out)) Directory.CreateDirectory(@out);

var homeBuilder = new MarkdownBuilder();
homeBuilder.Header(1, "References");
var indexBuilder = new MarkdownBuilder();
indexBuilder.Header(1, xmlDocumentation.AssemblyName);

foreach (var g in types.GroupBy(x => x.Namespace).OrderBy(x => x.Key))
foreach (var g in xmlDocumentation.Types.GroupBy(x => x.Namespace).OrderBy(x => x.Key))
{
string subDir = Path.Combine(@out, g.Key);
if (!Directory.Exists(subDir)) Directory.CreateDirectory(subDir);

homeBuilder.AppendLine();
homeBuilder.Header(2, g.Key);
homeBuilder.AppendLine();
indexBuilder.AppendLine();
indexBuilder.HeaderWithCode(2, g.Key);
indexBuilder.AppendLine();
foreach (var item in g.OrderBy(x => x.Name))
{
string typeName = item.BeautifyName.Replace("<", "{").Replace(">", "}").Replace(",", "").Replace(" ", "-");
var sb = new StringBuilder();

homeBuilder.ListLink(MarkdownBuilder.MarkdownCodeQuote(item.BeautifyName), g.Key + "/" + typeName);
indexBuilder.ListLink(MarkdownBuilder.MarkdownCodeQuote(item.BeautifyName), g.Key + "/" + typeName);

sb.Append(item.ToString());

File.WriteAllText(Path.Combine(@out, g.Key, $"{typeName}.md"), sb.ToString());
}
}

File.WriteAllText(Path.Combine(@out, $"{homePageName}.md"), homeBuilder.ToString());
File.WriteAllText(Path.Combine(@out, $"{homePageName}.md"), indexBuilder.ToString());

return 0;
});
Expand Down
68 changes: 68 additions & 0 deletions src/XMLDoc2Markdown/XmlDocumentation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Xml.Linq;

namespace XMLDoc2Markdown
{
public class XmlDocumentation
{
public string AssemblyName { get; private set; }
public IEnumerable<MarkdownableType> Types { get; private set; }

public XmlDocumentation(string dllPath, string namespaceMatch)
{
Assembly assembly = Assembly.LoadFrom(dllPath);

this.AssemblyName = assembly.GetName().Name;

var xmlPath = Path.Combine(Directory.GetParent(dllPath).FullName, Path.GetFileNameWithoutExtension(dllPath) + ".xml");

XmlDocumentComment[] comments = new XmlDocumentComment[0];
if (File.Exists(xmlPath))
{
comments = VSDocParser.ParseXmlComment(XDocument.Parse(File.ReadAllText(xmlPath)), namespaceMatch);
}
var commentsLookup = comments.ToLookup(x => x.ClassName);

var namespaceRegex =
!string.IsNullOrEmpty(namespaceMatch) ? new Regex(namespaceMatch) : null;

var markdownableTypes = new[] { assembly }
.SelectMany(x =>
{
try
{
return x.GetTypes();
}
catch (ReflectionTypeLoadException ex)
{
return ex.Types.Where(t => t != null);
}
catch
{
return Type.EmptyTypes;
}
})
.Where(x => x != null)
.Where(x => x.IsPublic && !typeof(Delegate).IsAssignableFrom(x) && !x.GetCustomAttributes<ObsoleteAttribute>().Any())
.Where(x => IsRequiredNamespace(x, namespaceRegex))
.Select(x => new MarkdownableType(x, commentsLookup))
.ToArray();

this.Types = markdownableTypes;
}

static bool IsRequiredNamespace(Type type, Regex regex)
{
if (regex == null)
{
return true;
}
return regex.IsMatch(type.Namespace != null ? type.Namespace : string.Empty);
}
}
}

0 comments on commit 6632276

Please sign in to comment.