Skip to content

Instantly share code, notes, and snippets.

@dperish
Forked from lontivero/gist:593fc51f1208555112e0
Last active May 30, 2017 03:16
Show Gist options
  • Save dperish/a12b774816791a5a25bb to your computer and use it in GitHub Desktop.
Save dperish/a12b774816791a5a25bb to your computer and use it in GitHub Desktop.
/* XmlToMD.csx
*
* Converts .Net generated XML comments into a markdown file.
* Forked from: https://gist.github.com/lontivero/593fc51f1208555112e0
*
* Usage: scriptcs xmltomd.csx -- [XML_INPUT_FILE] <[MD_OUTPUT_FILE]>
*
* The second argument is optional, and will default to the same path
* & file name prefix as the input file.
*/
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Xml;
using System.Xml.Linq;
if (Env.ScriptArgs.Count > 0) {
var xml = File.ReadAllText(Env.ScriptArgs[0]);
var doc = XDocument.Parse(xml);
var md = ToMarkDown(doc.Root);
File.WriteAllText(
(Env.ScriptArgs.Count == 2)
? Env.ScriptArgs[1].ToLowerInvariant().Replace(".xml", ".md")
: Env.ScriptArgs[1],
md);
}
else {
Console.WriteLine(
"Usage: scriptcs xmltomd.csx -- [XML_INPUT_FILE] <[MD_OUTPUT_FILE]> \r\n\r\n" +
"To generate the XML files from comments, check the 'XML " +
"Documentation File' checkbox under the Build tab of your " +
"project's Properties page."
);
}
static string ToMarkDown(XNode e)
{
var templates = new Dictionary<string, string>
{
{"doc", "## {0} ##\n\n{1}\n\n"},
{"type", "# {0}\n\n{1}\n\n---\n"},
{"field", "##### {0}\n\n{1}\n\n---\n"},
{"property", "##### {0}\n\n{1}\n\n---\n"},
{"method", "##### {0}\n\n{1}\n\n---\n"},
{"event", "##### {0}\n\n{1}\n\n---\n"},
{"summary", "{0}\n\n"},
{"remarks", "\n\n>{0}\n\n"},
{"example", "_C# code_\n\n```c#\n{0}\n```\n\n"},
{"seePage", "[[{1}|{0}]]"},
{"seeAnchor", "[{1}]({0})"},
{"param", "|Name | Description |\n|-----|------|\n|{0}: |{1}|\n" },
{"exception", "[[{0}|{0}]]: {1}\n\n" },
{"returns", "Returns: {0}\n\n"},
{"none", ""}
};
var d = new Func<string, XElement, string[]>((att, node) => new[]
{
node.Attribute(att).Value,
ToMarkDown(node.Nodes())
});
var methods = new Dictionary<string, Func<XElement, IEnumerable<string>>>
{
{"doc", x=> new[]{
x.Element("assembly").Element("name").Value,
ToMarkDown(x.Element("members").Elements("member"))
}},
{"type", x=>d("name", x)},
{"field", x=> d("name", x)},
{"property", x=> d("name", x)},
{"method",x=>d("name", x)},
{"event", x=>d("name", x)},
{"summary", x=> new[]{ ToMarkDown(x.Nodes()) }},
{"remarks", x => new[]{ToMarkDown(x.Nodes())}},
{"example", x => new[]{ToCodeBlock(x.Value)}},
{"seePage", x=> d("cref", x) },
{"seeAnchor", x=> { var xx = d("cref", x); xx[0] = xx[0].ToLower(); return xx; }},
{"param", x => d("name", x) },
{"exception", x => d("cref", x) },
{"returns", x => new[]{ToMarkDown(x.Nodes())}},
{"none", x => new string[0]}
};
string name;
if(e.NodeType== XmlNodeType.Element)
{
var el = (XElement) e;
name = el.Name.LocalName;
if (name == "member")
{
switch (el.Attribute("name").Value[0])
{
case 'F': name = "field"; break;
case 'P': name = "property"; break;
case 'T': name = "type"; break;
case 'E': name = "event"; break;
case 'M': name = "method"; break;
default: name = "none"; break;
}
}
if(name == "see")
{
var anchor = el.Attribute("cref").Value.StartsWith("!:#");
name = anchor ? "seeAnchor" : "seePage";
}
var vals = methods[name](el).ToArray();
string str="";
switch (vals.Length)
{
case 1: str= string.Format(templates[name], vals[0]);break;
case 2: str= string.Format(templates[name], vals[0],vals[1]);break;
case 3: str= string.Format(templates[name], vals[0],vals[1],vals[2]);break;
case 4: str= string.Format(templates[name], vals[0], vals[1], vals[2], vals[3]);break;
}
return str;
}
if(e.NodeType==XmlNodeType.Text)
return Regex.Replace( ((XText)e).Value.Replace('\n', ' '), @"\s+", " ");
return "";
}
internal static string ToMarkDown(IEnumerable<XNode> es)
{
return es.Aggregate("", (current, x) => current + ToMarkDown(x));
}
static string ToCodeBlock(string s)
{
var lines = s.Split(new char[] {'\n'}, StringSplitOptions.RemoveEmptyEntries);
var blank = lines[0].TakeWhile(x => x == ' ').Count() - 4;
return string.Join("\n",lines.Select(x => new string(x.SkipWhile((y, i) => i < blank).ToArray())));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment