Skip to content

Commit cbeabf0

Browse files
✨ feat: support descriptive texts (header, title, legend, caption, footer) (#62)
* ✨ feat: support descriptive texts (header, title, legend, caption, footer) (closes #61) closes #61 * ♻️ refactor: scheme for better auto-completion
1 parent 08ba6cd commit cbeabf0

File tree

9 files changed

+252
-2
lines changed

9 files changed

+252
-2
lines changed

Configuration/Examples/Rich/.swiftplantuml.yml

+8
Original file line numberDiff line numberDiff line change
@@ -67,3 +67,11 @@ stereotypes:
6767
spot:
6868
character: P
6969
color: AntiqueWhite
70+
texts:
71+
header: Simple header example
72+
title: |
73+
<u>Formatted</u> title example
74+
on <i>several</i> lines and using <font color=red>html</font>
75+
legend: Some boxed text
76+
caption: Second to last
77+
footer: The end

Configuration/Schema/json-schema-swiftplantuml.json

+42
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,9 @@
105105
"color": "AntiqueWhite"
106106
}
107107
}
108+
},
109+
"texts": {
110+
"title": "Diagram Title"
108111
}
109112
}],
110113
"definitions": {
@@ -1023,6 +1026,45 @@
10231026
}
10241027
},
10251028
"additionalProperties": true
1029+
},
1030+
"texts": {
1031+
"$id": "#/properties/texts",
1032+
"type": "object",
1033+
"title": "The texts schema",
1034+
"description": "Descriptive texts (a.k.a common commands) you can add around your diagram",
1035+
"default": {},
1036+
"examples": [{
1037+
"title": "Diagram Title"
1038+
}],
1039+
"required": [],
1040+
"properties": {
1041+
"header": {
1042+
"type": "string",
1043+
"title": "The header Schema",
1044+
"description": "Add a header to the top right above the diagram"
1045+
},
1046+
"title": {
1047+
"type": "string",
1048+
"title": "The title Schema",
1049+
"description": "Add a title above the diagram"
1050+
},
1051+
"legend": {
1052+
"type": "string",
1053+
"title": "The legend Schema",
1054+
"description": "Add a legend (boxed text) under the diagram but above the caption"
1055+
},
1056+
"caption": {
1057+
"type": "string",
1058+
"title": "The caption Schema",
1059+
"description": "Add a caption under the diagram"
1060+
},
1061+
"footer": {
1062+
"type": "string",
1063+
"title": "The footer Schema",
1064+
"description": "Add a footer at the bottom below the diagram"
1065+
}
1066+
},
1067+
"additionalProperties": true
10261068
}
10271069
},
10281070
"additionalProperties": true

README.md

+4
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,10 @@ elements:
227227
showNestedTypes: false
228228
showExtensions: merged
229229
theme: plain # see https://plantuml.com/theme
230+
texts:
231+
title: |
232+
<u>Formatted</u> title example
233+
on <i>several</i> lines and using <font color=red>html</font>
230234
skinparamCommands: # see https://plantuml.com/skinparam
231235
- skinparam classBackgroundColor PaleGreen
232236
- skinparam classArrowColor SeaGreen

Sources/SwiftPlantUMLFramework/Configuration/Configuration.swift

+8-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public enum AccessLevel: String, Codable {
1818
/// Configuration options to influence the generation and visual representation of the class diagram
1919
public struct Configuration: Codable {
2020
/// memberwise initializer
21-
public init(files: FileOptions = FileOptions(), elements: ElementOptions = ElementOptions(), hideShowCommands: [String]? = ["hide empty members"], skinparamCommands: [String]? = ["skinparam shadowing false"], includeRemoteURL: String? = nil, theme: Theme? = nil, relationships: RelationshipOptions = RelationshipOptions(), stereotypes: Stereotypes = Stereotypes.default, relationshipExclude _: [String]? = nil) {
21+
public init(files: FileOptions = FileOptions(), elements: ElementOptions = ElementOptions(), hideShowCommands: [String]? = ["hide empty members"], skinparamCommands: [String]? = ["skinparam shadowing false"], includeRemoteURL: String? = nil, theme: Theme? = nil, relationships: RelationshipOptions = RelationshipOptions(), stereotypes: Stereotypes = Stereotypes.default, relationshipExclude _: [String]? = nil, texts: PageTexts? = nil) {
2222
self.files = files
2323
self.elements = elements
2424
self.hideShowCommands = hideShowCommands
@@ -27,6 +27,7 @@ public struct Configuration: Codable {
2727
self.theme = theme
2828
self.relationships = relationships
2929
self.stereotypes = stereotypes
30+
self.texts = texts
3031
}
3132

3233
public init(from decoder: Decoder) throws {
@@ -55,6 +56,9 @@ public struct Configuration: Codable {
5556
if let stereotypes = try container.decodeIfPresent(Stereotypes.self, forKey: .stereotypes) {
5657
self.stereotypes = stereotypes
5758
}
59+
if let texts = try container.decodeIfPresent(PageTexts.self, forKey: .texts) {
60+
self.texts = texts
61+
}
5862
}
5963

6064
/// default configuration used if no configuration file was found
@@ -83,6 +87,9 @@ public struct Configuration: Codable {
8387
/// sterotypes (spotted character with background color and optional name) to be shown for an entity type
8488
public private(set) var stereotypes = Stereotypes(classStereotype: Stereotype.class, structStereotype: Stereotype.struct, extensionStereotype: Stereotype.extension, enumStereotype: Stereotype.enum, protocolStereotype: Stereotype.protocol)
8589

90+
// Descriptive texts ("common commands") you can add around your diagram
91+
public var texts: PageTexts?
92+
8693
internal var shallExtensionsBeMerged: Bool {
8794
elements.showExtensions.safelyUnwrap == .merged
8895
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import Foundation
2+
/// Descriptive texts ("common commands") you can add around your diagram
3+
public struct PageTexts: Codable {
4+
internal init(header: String? = nil, title: String? = nil, legend: String? = nil, caption: String? = nil, footer: String? = nil) {
5+
self.header = header
6+
self.title = title
7+
self.legend = legend
8+
self.caption = caption
9+
self.footer = footer
10+
}
11+
12+
/// add a header to the top right above the diagram
13+
public var header: String?
14+
/// add a title above the diagram
15+
public var title: String?
16+
/// add a legend (boxed text) under the diagram but above the caption
17+
public var legend: String?
18+
/// add a caption under the diagram
19+
public var caption: String?
20+
/// add a footer at the bottom below the diagram
21+
public var footer: String?
22+
}
23+
24+
extension PageTexts {
25+
func plantuml() -> String? {
26+
if header == nil,
27+
title == nil,
28+
legend == nil,
29+
caption == nil,
30+
footer == nil
31+
{
32+
return nil
33+
}
34+
var text = ""
35+
if let header = header {
36+
text.appendAsNewLine("header")
37+
text.appendAsNewLine(header)
38+
text.appendAsNewLine("end header")
39+
}
40+
if let title = title {
41+
text.appendAsNewLine("title")
42+
text.appendAsNewLine(title)
43+
text.appendAsNewLine("end title")
44+
}
45+
if let legend = legend {
46+
text.appendAsNewLine("legend")
47+
text.appendAsNewLine(legend)
48+
text.appendAsNewLine("end legend")
49+
}
50+
if let caption = caption {
51+
text.appendAsNewLine("caption")
52+
text.appendAsNewLine(caption)
53+
text.appendAsNewLine("end caption")
54+
}
55+
if let footer = footer {
56+
text.appendAsNewLine("footer")
57+
text.appendAsNewLine(footer)
58+
text.appendAsNewLine("end footer")
59+
}
60+
return text
61+
}
62+
}

Sources/SwiftPlantUMLFramework/PlantUMLScript.swift

+4
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ public struct PlantUMLScript {
2424
}
2525
text.appendAsNewLine(defaultStyling)
2626

27+
if let texts = configuration.texts?.plantuml() {
28+
text.appendAsNewLine(texts)
29+
}
30+
2731
let newLine = "\n"
2832
var mainContent = newLine
2933

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
@testable import SwiftPlantUMLFramework
2+
import XCTest
3+
4+
final class PageTextsTests: XCTestCase {
5+
func testNone() throws {
6+
let texts = PageTexts()
7+
XCTAssertNil(texts.plantuml())
8+
}
9+
10+
func testHeader() throws {
11+
let headerText = """
12+
<u>Simple</u> header example
13+
on <i>several</i> lines and using <font color=red>html</font>
14+
"""
15+
16+
let texts = PageTexts(header: headerText)
17+
let result = texts.plantuml()
18+
19+
let expected = """
20+
21+
header
22+
\(headerText)
23+
end header
24+
"""
25+
XCTAssertEqual(result, expected)
26+
}
27+
28+
func testTitle() throws {
29+
let titleText = """
30+
<u>Simple</u> title example
31+
on <i>several</i> lines and using <font color=red>html</font>
32+
"""
33+
34+
let texts = PageTexts(title: titleText)
35+
let result = texts.plantuml()
36+
37+
let expected = """
38+
39+
title
40+
\(titleText)
41+
end title
42+
"""
43+
XCTAssertEqual(result, expected)
44+
}
45+
46+
func testLegend() throws {
47+
let legendText = """
48+
<u>Simple</u> legend example
49+
on <i>several</i> lines and using <font color=red>html</font>
50+
"""
51+
52+
let texts = PageTexts(legend: legendText)
53+
let result = texts.plantuml()
54+
55+
let expected = """
56+
57+
legend
58+
\(legendText)
59+
end legend
60+
"""
61+
XCTAssertEqual(result, expected)
62+
}
63+
64+
func testCaption() throws {
65+
let captionText = """
66+
<u>Simple</u> caption example
67+
on <i>several</i> lines and using <font color=red>html</font>
68+
"""
69+
70+
let texts = PageTexts(caption: captionText)
71+
let result = texts.plantuml()
72+
73+
let expected = """
74+
75+
caption
76+
\(captionText)
77+
end caption
78+
"""
79+
XCTAssertEqual(result, expected)
80+
}
81+
82+
func testFooter() throws {
83+
let footerText = """
84+
<u>Simple</u> footer example
85+
on <i>several</i> lines and using <font color=red>html</font>
86+
"""
87+
88+
let texts = PageTexts(footer: footerText)
89+
let result = texts.plantuml()
90+
91+
let expected = """
92+
93+
footer
94+
\(footerText)
95+
end footer
96+
"""
97+
XCTAssertEqual(result, expected)
98+
}
99+
100+
func testAll() throws {
101+
let texts = PageTexts(header: "1", title: "2", legend: "3", caption: "4", footer: "5")
102+
let result = try XCTUnwrap(texts.plantuml())
103+
XCTAssertTrue(result.contains("1"))
104+
XCTAssertTrue(result.contains("2"))
105+
XCTAssertTrue(result.contains("3"))
106+
XCTAssertTrue(result.contains("4"))
107+
XCTAssertTrue(result.contains("5"))
108+
}
109+
}

Tests/SwiftPlantUMLFrameworkTests/PlantUMLScriptTests.swift

+9
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,15 @@ final class PlantUMLScriptTests: XCTestCase {
8888
XCTAssertEqual(script.text.noSpacesAndNoLineBreaks, expected.noSpacesAndNoLineBreaks)
8989
}
9090

91+
func testIncludingPageTexts() {
92+
guard let items = try! SyntaxStructure.create(from: getTestFile())?.substructure else { return XCTFail("cannot read test data") }
93+
let headerText = "Header123"
94+
var config = Configuration.default
95+
config.texts = PageTexts(header: headerText)
96+
let script = PlantUMLScript(items: items, configuration: config)
97+
XCTAssertTrue(script.text.contains(headerText))
98+
}
99+
91100
func getTestFile(named: String = "basics") throws -> URL {
92101
// https://stackoverflow.com/questions/47177036/use-resources-in-unit-tests-with-swift-package-manager
93102
let path = Bundle.module.path(forResource: named, ofType: "txt", inDirectory: "TestData") ?? "nonesense"

Tests/SwiftPlantUMLFrameworkTests/TestData/ProjectMock/customConfigValid.yml

+6-1
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,9 @@ stereotypes:
6262
spot:
6363
character: P
6464
color: AntiqueWhite
65-
65+
texts:
66+
header: headerText
67+
title: titleText
68+
legend: legendText
69+
caption: captionText
70+
footer: footerText

0 commit comments

Comments
 (0)