diff --git a/PlantUMLKeyboard/Sources/PlantUMLKeyboard/PlantUMLKeyboard+Choice.swift b/PlantUMLKeyboard/Sources/PlantUMLKeyboard/PlantUMLKeyboard+Choice.swift index 04e9dd9..74b2442 100644 --- a/PlantUMLKeyboard/Sources/PlantUMLKeyboard/PlantUMLKeyboard+Choice.swift +++ b/PlantUMLKeyboard/Sources/PlantUMLKeyboard/PlantUMLKeyboard+Choice.swift @@ -14,7 +14,7 @@ struct ChoiceKeyButton: View { @Environment(\.colorScheme) var colorScheme // @State private var showingSheet = false - @State private var selection: String? + @State private var selection: Symbol? var symbol:Symbol var onPressSymbol: (Symbol) -> Void @@ -34,7 +34,15 @@ struct ChoiceKeyButton: View { var body: some View { Button { // showingSheet.toggle() - presentViewOnRootController( ChoiceView( symbol: symbol, selection: $selection ) ) + if let choices = symbol.additionalValues?.map({ + if let ref = try? Symbol.matchRef(in: $0 ), let choice = Symbol.references?.first(where: { s in s.id == ref }) { + return choice + } + return Symbol( id: symbol.id, value: String(format: symbol.value, $0 ) ) + }) { + presentViewOnRootController( ChoiceView( title: symbol.id, choices: choices, selection: $selection ) ) + + } } label: { Label(symbol.id, systemImage: "list.bullet") @@ -45,12 +53,8 @@ struct ChoiceKeyButton: View { .onChange(of: selection) { _ in if let selection { - - let value = String(format: symbol.value, selection ) - - let symbol = Symbol( id: symbol.id, value: value ) - onPressSymbol( symbol ) + onPressSymbol( selection ) } } @@ -63,12 +67,9 @@ struct ChoiceKeyButton: View { struct ChoiceView: View { @Environment(\.dismiss) var dismiss - var symbol: Symbol - @Binding var selection: String? - - private var items: [String] { - symbol.additionalValues ?? [] - } + var title: String + var choices: [Symbol] + @Binding var selection: Symbol? private func Navigation( content: () -> some View ) -> some View { if #available(iOS 16.0, *) { @@ -82,20 +83,20 @@ struct ChoiceView: View { var body: some View { Navigation { - List(items, id: \.self, selection: $selection) { name in - Text(name) + List(choices, id: \.self, selection: $selection) { + Text($0.id) } .onChange(of: selection) { _ in dismiss() } - .navigationTitle( symbol.id ) + .navigationTitle( title ) .toolbar { ToolbarItem( placement: .navigationBarTrailing ) { Button { dismiss() } label: { - Label(symbol.id, systemImage: "xmark") + Label(title, systemImage: "xmark") .labelStyle( .iconOnly ) } } diff --git a/PlantUMLKeyboard/Sources/PlantUMLKeyboard/PlantUMLSymbol+Data.swift b/PlantUMLKeyboard/Sources/PlantUMLKeyboard/PlantUMLSymbol+Data.swift index 589840b..c455ea8 100644 --- a/PlantUMLKeyboard/Sources/PlantUMLKeyboard/PlantUMLSymbol+Data.swift +++ b/PlantUMLKeyboard/Sources/PlantUMLKeyboard/PlantUMLSymbol+Data.swift @@ -8,6 +8,40 @@ import Foundation import UIKit import PlantUMLFramework + + +// +// LOAD JSON DATA +// +let plantUMLSymbols: Array = { + + guard let path = Bundle.module.path(forResource: "plantuml_keyboard_data", ofType: "json") else { + return [] + } + + let decoder = JSONDecoder() + + do { + let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe) + + let result = try decoder.decode(Array.self, from: data) + + Symbol.references = result.first { $0.name == "references" } + + return result.filter { $0.name != "references" } + + } catch { + logger.error( "\(error.localizedDescription)") + return [] + } + +}() + +// ///////////////////////////////////////////////////////////////// +// @resultBuilder based implementation +// ///////////////////////////////////////////////////////////////// + + // // MARK: COMMON DIAGRAMS // @@ -37,10 +71,10 @@ fileprivate func common_symbols() -> [[Symbol]] { } + // // MARK: SEQUENCE DIAGRAMS // - @Symbol.LineBuilder fileprivate func sequence_symbols() -> [[Symbol]] { @@ -99,8 +133,6 @@ fileprivate func sequence_symbols() -> [[Symbol]] { // // MARK: DEPLOYMENT DIAGRAMS // - - @Symbol.LineBuilder fileprivate func deployment_symbols() -> [[Symbol]] { @@ -162,31 +194,8 @@ fileprivate func deployment_symbols() -> [[Symbol]] { let plantUMLSymbols_static = [ - + PlantUMLSymbolGroup( name: "general", rows: common_symbols() ), PlantUMLSymbolGroup( name: "sequence", rows: sequence_symbols() ), PlantUMLSymbolGroup( name: "deployment", rows: deployment_symbols() ), ] - - -let plantUMLSymbols: Array = { - - guard let path = Bundle.module.path(forResource: "plantuml_keyboard_data", ofType: "json") else { - return [] - } - - let decoder = JSONDecoder() - - do { - let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe) - - let result = try decoder.decode(Array.self, from: data) - - return result - - } catch { - logger.error( "\(error.localizedDescription)") - return [] - } - -}() diff --git a/PlantUMLKeyboard/Sources/PlantUMLKeyboard/PlantUMLSymbol.swift b/PlantUMLKeyboard/Sources/PlantUMLKeyboard/PlantUMLSymbol.swift index 7975fd7..7dd28a2 100644 --- a/PlantUMLKeyboard/Sources/PlantUMLKeyboard/PlantUMLSymbol.swift +++ b/PlantUMLKeyboard/Sources/PlantUMLKeyboard/PlantUMLSymbol.swift @@ -7,61 +7,94 @@ import UIKit import LineEditor +import PlantUMLFramework -public struct Symbol : Decodable, Identifiable, CustomStringConvertible, LineEditorKeyboardSymbol { - +public struct Symbol : Decodable, Identifiable, Equatable, Hashable, CustomStringConvertible, LineEditorKeyboardSymbol { + enum CodingKeys: String, CodingKey { - case id - case value - case additionalValues = "additional" - case type - } - public var description: String { value } - - public var id:String - private var _value:String? - public private(set) var additionalValues:[String]? - public var type = "string" - - public var value: String { - get { _value ?? id } - } - -// var additionalValues: [String]? { -// get { _additionalValues } -// } + case id + case value + case additionalValues = "additional" + case type + } + + public var id:String + private var _value:String? + public private(set) var additionalValues:[String]? + public var type = "string" + + public var value: String { get { _value ?? id } } + public var description: String { id } - public init( id:String, value:String? = nil, additionalValues: [String]? = nil) { - self.id = id - self._value = value - self.additionalValues = additionalValues - } + static var references: PlantUMLSymbolGroup? + public init( id:String, value:String? = nil, additionalValues: [String]? = nil ) { + self.id = id + self._value = value + self.additionalValues = additionalValues + } + public init(from decoder: Decoder) throws { - + let container = try decoder.container(keyedBy: CodingKeys.self ) self.id = try container.decode(String.self, forKey: CodingKeys.id) self._value = try container.decodeIfPresent(String.self, forKey: CodingKeys.value) self.additionalValues = try container.decodeIfPresent([String].self, forKey: CodingKeys.additionalValues) + if let type = try container.decodeIfPresent(String.self, forKey: CodingKeys.type) { self.type = type } - + } + // [Regular Expression Capture Groups in Swift](https://www.advancedswift.com/regex-capture-groups/) + public static func matchRef( in value: String ) throws -> String? { + + let ref_exp = try NSRegularExpression(pattern: #"^#ref\(\s*(?[\w\d ]+[\w\d]+)\s*\)"#, options: [] ) + + let range = NSRange(value.startIndex.. Bool ) -> Symbol? { + + for row in rows.indices { + + if let result = rows[row].first(where: predicate ) { + return result + } + } + + return nil + } } - - +//// MARK: EQUATABLE COMPLIANCE +//extension Symbol { +// +// public static func ==(lhs: Symbol, rhs: Symbol) -> Bool { +// return lhs.id == rhs.id +// } +//} +// +//// MARK: HASHABLE COMPLIANCE +//extension Symbol { +// public func hash(into hasher: inout Hasher) { +// hasher.combine(id) +// } +//} diff --git a/PlantUMLKeyboard/Sources/PlantUMLKeyboard/plantuml_keyboard_data.json b/PlantUMLKeyboard/Sources/PlantUMLKeyboard/plantuml_keyboard_data.json index c2df245..7fbef63 100644 --- a/PlantUMLKeyboard/Sources/PlantUMLKeyboard/plantuml_keyboard_data.json +++ b/PlantUMLKeyboard/Sources/PlantUMLKeyboard/plantuml_keyboard_data.json @@ -1,5 +1,15 @@ [ +{ + "name": "references", + "rows": [ + [ + { "id": "note left", "value": "note left /' of p1 '/", "additional": ["this note is displayed left", "end note"]}, + { "id": "note right", "value": "note right /' of p1 '/", "additional": ["this note is displayed right", "end note"]}, + { "id": "note over", "value": "note over p1 /', p2 '/", "additional": ["this note is displayed over participant1", "end note"]} + ] + ] +}, { "name": "general", "rows": [ @@ -77,10 +87,11 @@ { "id": "group", "value": "group My own label", "additional": ["end"] }, { "id": "loop", "value": "loop N times", "additional": ["end"] }, { "id": "alt", "value": "alt successful case", "additional": [ "else some kind of failure", "end"] }, - { "id": "note left", "value": "note left /' of p1 '/", "additional": ["this note is displayed left", "end note"]}, - { "id": "note right", "value": "note right /' of p1 '/", "additional": ["this note is displayed right", "end note"]}, - { "id": "note over", "value": "note over p1 /', p2 '/", "additional": ["this note is displayed over participant1", "end note"]} - + { "id": "note", "value": "note %@", "type": "choice", "additional": [ + "#ref(note left)", + "#ref(note right)", + "#ref(note over)" + ]} ] ] }, diff --git a/PlantUMLKeyboard/Tests/PlantUMLKeyboardTests/PlantUMLKeyboardTests.swift b/PlantUMLKeyboard/Tests/PlantUMLKeyboardTests/PlantUMLKeyboardTests.swift index 938a370..3c44419 100644 --- a/PlantUMLKeyboard/Tests/PlantUMLKeyboardTests/PlantUMLKeyboardTests.swift +++ b/PlantUMLKeyboard/Tests/PlantUMLKeyboardTests/PlantUMLKeyboardTests.swift @@ -42,7 +42,44 @@ final class PlantUMLKeyboardTests: XCTestCase { XCTAssertEqual( symbols[0][11].value, "queue as q1" ) - print( symbols ) +// print( symbols ) + + } + + + func testRef() throws { + + do { + + let ref_1 = try Symbol.matchRef( in: "test" ) + XCTAssertNil(ref_1) + + let ref_2 = try Symbol.matchRef( in: "#ref(note left)" ) + XCTAssertNotNil(ref_2) + XCTAssertEqual( ref_2 , "note left" ) + + let ref_3 = try Symbol.matchRef( in: "#ref( note left)" ) + XCTAssertNotNil(ref_3) + XCTAssertEqual( ref_3 , "note left" ) + + let ref_4 = try Symbol.matchRef( in: "#ref(note left )" ) + XCTAssertNotNil(ref_4) + XCTAssertEqual( ref_4 , "note left" ) + + let ref_5 = try Symbol.matchRef( in: "#ref( note left )" ) + XCTAssertNotNil(ref_5) + XCTAssertEqual( ref_5 , "note left" ) + + let ref_6 = try Symbol.matchRef( in: "#ref( mixed by erry )" ) + XCTAssertNotNil(ref_6) + XCTAssertEqual( ref_6 , "mixed by erry" ) + + } catch { + XCTFail( "error evaluating isReference regular expression: \(error)") + } + + +// print( symbols ) } }