Skip to content

Commit 316fd49

Browse files
committed
Added documentation generator
1 parent 7eb4e71 commit 316fd49

8 files changed

Lines changed: 158 additions & 32 deletions

File tree

src/libArgument/libArgument.Tests/NewFeatureTests.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
using Codeaddicts.libArgument;
33
using Codeaddicts.libArgument.Attributes;
44
using NUnit.Framework;
5+
using System.Threading.Tasks;
6+
using System.Threading;
7+
using System.Windows.Forms;
58

69
namespace Codeaddicts.libArgument.Tests
710
{
@@ -10,23 +13,33 @@ public class NewFeatureTests
1013
{
1114
public class Options {
1215
[Argument]
16+
[Docs ("A message")]
1317
public string msg;
1418

1519
[Argument]
20+
[Docs ("A number")]
1621
public int num;
1722

1823
[Switch]
24+
[Docs ("A something")]
1925
public bool something;
2026
}
2127

2228
[Test]
2329
public void TestInferName () {
24-
var args = new string[] { "--msg", "Test", "--num", "1234", "--something" };
30+
var args = new [] { "--msg", "Test", "--num", "1234", "--something" };
2531
var options = ArgumentParser.Parse<Options> (args);
2632
StringAssert.AreEqualIgnoringCase ("Test", options.msg);
2733
Assert.AreEqual (1234, options.num);
2834
Assert.That (options.something);
2935
}
36+
37+
[Test]
38+
public void TestDocumentation () {
39+
var options = ArgumentParser.Parse<Options> (new string[] { });
40+
ArgumentParser.Help ();
41+
Assert.Pass ();
42+
}
3043
}
3144
}
3245

src/libArgument/libArgument.Tests/libArgument.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
<Reference Include="nunit.framework">
3232
<HintPath>..\packages\NUnit.2.6.4\lib\nunit.framework.dll</HintPath>
3333
</Reference>
34+
<Reference Include="System.Windows.Forms" />
3435
</ItemGroup>
3536
<ItemGroup>
3637
<Compile Include="GeneralTests.cs" />

src/libArgument/libArgument.userprefs

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,9 @@
11
<Properties StartupItem="libArgument.Tests\libArgument.Tests.csproj">
22
<MonoDevelop.Ide.Workspace ActiveConfiguration="Release" />
3-
<MonoDevelop.Ide.Workbench ActiveDocument="libArgument.Tests\NewFeatureTests.cs">
3+
<MonoDevelop.Ide.Workbench ActiveDocument="libArgument\ArgumentParser.cs">
44
<Files>
55
<File FileName="libArgument\ArgumentParser.cs" Line="7" Column="7" />
6-
<File FileName="libArgument.Tests\NewFeatureTests.cs" Line="36" Column="36" />
76
</Files>
8-
<Pads>
9-
<Pad Id="MonoDevelop.NUnit.TestPad">
10-
<State expanded="True" selected="True">
11-
<Node name="libArgument.Tests" expanded="True">
12-
<Node name="Codeaddicts" expanded="True">
13-
<Node name="libArgument" expanded="True">
14-
<Node name="Tests" expanded="True">
15-
<Node name="GeneralTests" expanded="True" />
16-
<Node name="NewFeatureTests" expanded="True" />
17-
</Node>
18-
</Node>
19-
</Node>
20-
</Node>
21-
</State>
22-
</Pad>
23-
</Pads>
247
</MonoDevelop.Ide.Workbench>
258
<MonoDevelop.Ide.DebuggingService.Breakpoints>
269
<BreakpointStore />

src/libArgument/libArgument/ArgumentParser.cs

Lines changed: 97 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
using System;
22
using System.Collections.Generic;
3-
using System.Globalization;
3+
using System.ComponentModel;
44
using System.Linq;
55
using System.Reflection;
6+
using System.Text;
67
using Codeaddicts.libArgument.Attributes;
7-
using System.ComponentModel;
8+
using Codeaddicts.libArgument.Documentation;
89

910
namespace Codeaddicts.libArgument
1011
{
@@ -13,60 +14,97 @@ namespace Codeaddicts.libArgument
1314
/// </summary>
1415
public static class ArgumentParser
1516
{
17+
static List<DocNode> documentation;
18+
1619
/// <summary>
1720
/// Parses the specified arguments.
1821
/// </summary>
1922
/// <param name="args">Arguments.</param>
2023
/// <typeparam name="T">The class that contains the options.</typeparam>
2124
public static T Parse<T> (string[] args) where T : class, new()
2225
{
26+
// Create the documentation cache for this class
27+
if (documentation == null)
28+
documentation = new List<DocNode> ();
29+
else
30+
documentation.Clear ();
31+
2332
// Instantiate the class that contains the options
2433
T options = new T ();
2534

26-
// Create a new List<string> and fill it with the arguments
35+
// Create a new List<string> from the supplied arguments
2736
var list = new List<string> (args);
2837

2938
// Get all public fields from the class instance
3039
var fields = typeof (T).GetFields (BindingFlags.Instance | BindingFlags.Public);
3140

3241
// Iterate over all fields
33-
foreach (var item in fields)
42+
foreach (var item in fields) {
3443

3544
// Parse the current field
45+
ParseDoc<T> (options, item.Name);
3646
ParseField<T> (options, list, item.Name);
3747

48+
}
49+
3850
// Return the generated class instance
3951
return options;
4052
}
4153

4254
/// <summary>
4355
/// Generates and prints the argument doc.
4456
/// </summary>
45-
/// <typeparam name="T">The 1st type parameter.</typeparam>
46-
static void PrintHelp<T> () where T : class, new()
57+
/// <typeparam name="T">The class that contains the options.</typeparam>
58+
public static void Help ()
4759
{
48-
// Not ready yet.
60+
documentation.Sort ((n1, n2) => {
61+
if (!string.IsNullOrEmpty (n1.ShortName) && !string.IsNullOrEmpty (n2.ShortName))
62+
return n1.ShortName.CompareTo (n2.ShortName);
63+
if (string.IsNullOrEmpty (n1.FullName) && string.IsNullOrEmpty (n2.ShortName))
64+
return n1.ShortName.CompareTo (n2.FullName);
65+
if (!string.IsNullOrEmpty (n1.FullName) && !string.IsNullOrEmpty (n2.FullName))
66+
return n1.FullName.CompareTo (n2.FullName);
67+
if (string.IsNullOrEmpty (n1.ShortName) && string.IsNullOrEmpty (n2.FullName))
68+
return n1.FullName.CompareTo (n2.ShortName);
69+
return n1.FieldName.CompareTo (n2.FieldName);
70+
});
71+
var accum = new StringBuilder ();
72+
foreach (var node in documentation) {
73+
if (!string.IsNullOrEmpty (node.ShortName))
74+
accum.AppendFormat ("{0}", node.ShortName.PadRight (Console.WindowWidth / 10) + "| ");
75+
if (!string.IsNullOrEmpty (node.ShortName) && !string.IsNullOrEmpty (node.FullName))
76+
accum.AppendFormat ("\n{0}", node.FullName.PadRight (Console.WindowWidth / 10) + "| ");
77+
else if (string.IsNullOrEmpty (node.ShortName) && !string.IsNullOrEmpty (node.FullName))
78+
accum.Append (node.FullName.PadRight (Console.WindowWidth / 10) + "| ");
79+
else if (string.IsNullOrEmpty (node.ShortName) && string.IsNullOrEmpty (node.FullName))
80+
accum.Append (node.FieldName.PadRight (Console.WindowWidth / 10) + "| ");
81+
if (!string.IsNullOrEmpty (node.Description))
82+
accum.AppendLine (node.Description);
83+
else
84+
accum.AppendLine ("<No Description>");
85+
}
86+
Console.WriteLine (accum);
4987
}
5088

5189
/// <summary>
5290
/// Parses the field.
5391
/// </summary>
5492
/// <param name="options">Options.</param>
5593
/// <param name="args">Arguments.</param>
56-
/// <param name="name">Name.</param>
94+
/// <param name="fieldname">Name.</param>
5795
/// <typeparam name="T">The 1st type parameter.</typeparam>
58-
static void ParseField<T> (T options, List<string> args, string name) where T : class, new()
96+
static void ParseField<T> (T options, List<string> args, string fieldname) where T : class, new()
5997
{
6098
// Get field
61-
var field = typeof(T).GetField (name);
99+
var field = typeof(T).GetField (fieldname);
62100

63101
// Get attributes
64102
var attributes = field.GetCustomAttributes (true);
65103

66104
// Iterate over the attributes
67105
foreach (var attrib in attributes) {
68106

69-
// Check if the current attribute is a Switch attribute
107+
// Check if the current attribute is of type Switch
70108
var @switch = attrib as Switch;
71109
if (@switch != null) {
72110

@@ -135,6 +173,53 @@ public static class ArgumentParser
135173
return;
136174
}
137175
}
176+
177+
static void ParseDoc<T> (T options, string fieldname) where T : class, new()
178+
{
179+
// Get field
180+
var field = typeof(T).GetField (fieldname);
181+
182+
// Get or create a documentation node for this field
183+
var node = documentation.Exists (_node => _node.FieldName == fieldname)
184+
? documentation.First (_node => _node.FieldName == fieldname)
185+
: new DocNode (fieldname);
186+
187+
// Get attributes
188+
var attributes = field.GetCustomAttributes (true);
189+
190+
// Iterate over all attributes
191+
foreach (var attrib in attributes) {
192+
193+
// Check if the current attribute is of type Argument or Switch
194+
var xarg = attrib as Argument;
195+
var xswitch = attrib as Switch;
196+
if (xswitch != null) {
197+
if (xswitch.infername)
198+
xswitch = xswitch.InferName (field.Name);
199+
node.ShortName = xswitch.FriendlyShort;
200+
node.FullName = xswitch.FriendlyFull;
201+
} else if (xarg != null) {
202+
if (xarg.infername)
203+
xarg = xarg.InferName (field.Name);
204+
node.ShortName = xarg.FriendlyShort;
205+
node.FullName = xarg.FriendlyFull;
206+
}
207+
208+
// Check if the current attribute is of type Docs
209+
var doc = attrib as Docs;
210+
211+
// If not, skip this attribute
212+
if (doc == null)
213+
continue;
214+
215+
// Return the documentation for this field
216+
node.Description = doc.description;
217+
}
218+
219+
if (documentation.Exists (_node => _node.FieldName == fieldname)) {
220+
documentation.Remove (documentation.First (_node => _node.FieldName == fieldname));
221+
}
222+
documentation.Add (node);
223+
}
138224
}
139225
}
140-
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using System;
2+
3+
namespace Codeaddicts.libArgument.Attributes
4+
{
5+
[AttributeUsage (AttributeTargets.Field, AllowMultiple = false)]
6+
public class Docs : Attribute
7+
{
8+
public readonly string description;
9+
10+
public Docs (string description)
11+
{
12+
this.description = description;
13+
}
14+
}
15+
}
16+

src/libArgument/libArgument/Attributes/Switch.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public Switch (string fullname) : base (fullname) {
1414
public Switch (string shortname, string fullname) : base (shortname, fullname) {
1515
}
1616

17-
public Switch InferName (string name) {
17+
public new Switch InferName (string name) {
1818
return new Switch (name);
1919
}
2020
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using System;
2+
3+
namespace Codeaddicts.libArgument.Documentation
4+
{
5+
6+
public class DocNode
7+
{
8+
public readonly string FieldName;
9+
public string FullName;
10+
public string ShortName;
11+
public string Description;
12+
13+
public DocNode (
14+
string fieldName,
15+
string fullName = "",
16+
string shortName = "",
17+
string description = "") {
18+
FieldName = fieldName;
19+
FullName = fullName;
20+
ShortName = shortName;
21+
Description = description;
22+
}
23+
}
24+
}
25+

src/libArgument/libArgument/libArgument.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,13 @@
3535
<Compile Include="ArgumentParser.cs" />
3636
<Compile Include="Attributes\Switch.cs" />
3737
<Compile Include="Attributes\Argument.cs" />
38+
<Compile Include="Attributes\Docs.cs" />
39+
<Compile Include="Documentation\DocNode.cs" />
3840
</ItemGroup>
3941
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
4042
<ItemGroup>
4143
<Folder Include="Attributes\" />
44+
<Folder Include="Documentation\" />
4245
</ItemGroup>
4346
<ProjectExtensions>
4447
<MonoDevelop>

0 commit comments

Comments
 (0)