Skip to content

Commit

Permalink
Generate nested tables for struct attributes
Browse files Browse the repository at this point in the history
  • Loading branch information
domi-b committed Nov 21, 2024
1 parent 2c76308 commit 70c5674
Show file tree
Hide file tree
Showing 2 changed files with 135 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,24 @@ MODEL TestModel (de) AT "http://models.geow.cloud" VERSION "1" =
END TestModel;
""";

private const string TestModelNestedStruct = """
INTERLIS 2.4;
MODEL TestModel (de) AT "http://models.geow.cloud" VERSION "1" =
TOPIC TestTopic =
CLASS TestClass =
attr1: MANDATORY TestStruct;
attr2: 10..20;
END TestClass;
STRUCTURE TestStruct =
attr1: TEXT*10;
attr2: MANDATORY (value1, value2);
END TestStruct;
END TestTopic;
END TestModel;
""";

[TestMethod]
public void TestInterlisFile()
{
Expand Down Expand Up @@ -127,7 +145,49 @@ public void TestInterlisFileEnumeration()
### TestClass
| Attributname | Kardinalität | Typ |
| --- | --- | --- |
| attr1 | 0..1 | (**topValue1**, **topValue2** (subValue1, subValue2, subValue3 (*subSubValue1*, *subSubValue2*)), **topValue3**) |
| attr1 | 0..1 | (<b>topValue1</b>, <b>topValue2</b> (subValue1, subValue2, subValue3 (<i>subSubValue1</i>, <i>subSubValue2</i>)), <b>topValue3</b>) |
""";

Assert.AreEqual(expected.ReplaceLineEndings(), documentation.ReplaceLineEndings());
}

[TestMethod]
public void TestInterlisFileNestedStruct()
{
var reader = new InterlisReader();
var interlisFile = reader.ReadFile(new StringReader(TestModelNestedStruct));

var visitor = new MarkdownDocumentationVisitor();
visitor.VisitInterlisFile(interlisFile);
var documentation = visitor.GetDocumentation();

const string structInlineTable =
"<table>" +
"<thead>" +
"<tr><th>Attributname</th><th>Kardinalität</th><th>Typ</th></tr>" +
"</thead>" +
"<tbody>" +
"<tr><td>attr1</td><td>0..1</td><td>Text [10]</td></tr>" +
"<tr><td>attr2</td><td>1</td><td>(<b>value1</b>, <b>value2</b>)</td></tr>" +
"</tbody>" +
"</table>";

var expected = $"""
# TestModel
## TestTopic
### TestClass
| Attributname | Kardinalität | Typ |
| --- | --- | --- |
| attr1 | 1 | TestStruct<br/>{structInlineTable} |
| attr2 | 0..1 | 10..20 |
### TestStruct
| Attributname | Kardinalität | Typ |
| --- | --- | --- |
| attr1 | 0..1 | Text [10] |
| attr2 | 1 | (<b>value1</b>, <b>value2</b>) |
""";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ namespace Geowerkstatt.Interlis.LanguageServer.Visitors;
public class MarkdownDocumentationVisitor : Interlis24AstBaseVisitor<object>
{
private readonly StringBuilder documentation = new StringBuilder();
private bool useHtml;

/// <summary>
/// Generates markdown documentation for the given model.
Expand Down Expand Up @@ -39,14 +40,30 @@ public class MarkdownDocumentationVisitor : Interlis24AstBaseVisitor<object>
/// <param name="modelDef">The INTERLIS class.</param>
public override object? VisitClassDef([NotNull] ClassDef classDef)
{
documentation.AppendLine($"### {classDef.Name}");
documentation.AppendLine("| Attributname | Kardinalität | Typ |");
documentation.AppendLine("| --- | --- | --- |");
var result = base.VisitClassDef(classDef);
VisitRelatedAssociations(classDef);
documentation.AppendLine();

return result;
void VisitTableBody()
{
base.VisitClassDef(classDef);
VisitRelatedAssociations(classDef);
}

if (useHtml)
{
documentation.Append("<table>");
documentation.Append("<thead><tr><th>Attributname</th><th>Kardinalität</th><th>Typ</th></tr></thead>");
documentation.Append("<tbody>");
VisitTableBody();
documentation.Append("</tbody></table>");
}
else
{
documentation.AppendLine($"### {classDef.Name}");
documentation.AppendLine("| Attributname | Kardinalität | Typ |");
documentation.AppendLine("| --- | --- | --- |");
VisitTableBody();
documentation.AppendLine();
}

return null;
}

private void VisitRelatedAssociations(ClassDef classDef)
Expand Down Expand Up @@ -83,9 +100,20 @@ private void VisitRelatedAssociation(ClassDef classDef, AttributeDef left, Attri
public override object? VisitAttributeDef([NotNull] AttributeDef attributeDef)
{
var cardinality = CalculateCardinality(attributeDef.TypeDef.Cardinality);
var type = GetTypeName(attributeDef.TypeDef);

documentation.AppendLine($"| {attributeDef.Name} | {cardinality} | {type} |");
if (useHtml)
{
documentation.Append($"<tr><td>{attributeDef.Name}</td><td>{cardinality}</td><td>");
VisitTypeName(attributeDef.TypeDef);
documentation.Append("</td></tr>");
}
else
{
documentation.Append($"| {attributeDef.Name} | {cardinality} | ");
VisitTypeName(attributeDef.TypeDef);
documentation.AppendLine(" |");
}

return base.VisitAttributeDef(attributeDef);
}

Expand All @@ -107,9 +135,15 @@ private static string CalculateCardinality(Cardinality? cardinality)
return "";
}

private static string? GetTypeName(TypeDef? type)
private void VisitTypeName(TypeDef? type)
{
return type switch
if (type is ReferenceType referenceType)
{
VisitReferenceType(referenceType);
return;
}

var typeName = type switch
{
TextType textType => textType.Length == null ? "Text" : $"Text [{textType.Length}]",
NumericType numericType => numericType.Min != null && numericType.Max != null ? $"{numericType.Min}..{numericType.Max}" : "Numerisch",
Expand All @@ -121,28 +155,47 @@ private static string CalculateCardinality(Cardinality? cardinality)
_ => "Blackbox",
},
EnumerationType enumerationType => FormatEnumerationValues(enumerationType.Values),
ReferenceType referenceType => referenceType.Target.Value?.Path.Last(),
TypeRef typeRef => typeRef.Extends?.Path.Last(),
RoleType roleType => string.Join(", ", roleType.Targets.Select(target => target.Value?.Path.Last()).Where(target => target is not null)),
_ => type?.ToString(),
};
documentation.Append(typeName);
}

private static string FormatEnumerationValues(EnumerationValuesList enumerationValues, int depth = 0)
{
const string bold = "**";
const string italic = "*";

var formatting = depth switch
var (formatStart, formatEnd) = depth switch
{
0 => bold,
1 => "",
_ => italic,
0 => ("<b>", "</b>"),
1 => ("", ""),
_ => ("<i>", "</i>"),
};
var formattedValues = enumerationValues.Select(v => $"{formatting}{v.Name}{formatting}{(v.SubValues.Count == 0 ? "" : " " + FormatEnumerationValues(v.SubValues, depth + 1))}");
var formattedValues = enumerationValues.Select(v => $"{formatStart}{v.Name}{formatEnd}{(v.SubValues.Count == 0 ? "" : " " + FormatEnumerationValues(v.SubValues, depth + 1))}");
return $"({string.Join(", ", formattedValues)})";
}

/// <summary>
/// Appends the name of the referenced type to the documentation.
/// If the referenced type is a structure, its attributes and associations are also documented using an HTML table.
/// </summary>
/// <param name="referenceType">The referenced type.</param>
private void VisitReferenceType(ReferenceType referenceType)
{
var reference = referenceType.Target.Value;
var typeName = reference?.Path.Last();
documentation.Append(typeName);

if (reference?.Target is ClassDef classDef && classDef.IsStructure)
{
documentation.Append("<br/>");

var didUseHtml = useHtml;
useHtml = true;
VisitClassDef(classDef);
useHtml = didUseHtml;
}
}

/// <summary>
/// Returns the generated markdown documentation of the visited elements.
/// </summary>
Expand Down

0 comments on commit 70c5674

Please sign in to comment.