@@ -16,52 +16,114 @@ import SyntaxSupport
16
16
import Utils
17
17
18
18
extension LayoutNode {
19
- func generateInitializerDeclHeader( useDeprecatedChildName: Bool = false ) -> SyntaxNodeString {
19
+
20
+ func makeChildParamType( for child: Child , isOptional: Bool = false ) -> TypeSyntax {
21
+ var paramType : TypeSyntax
22
+
23
+ if !child. kind. isNodeChoicesEmpty {
24
+ paramType = " \( child. syntaxChoicesType) "
25
+ } else if child. hasBaseType {
26
+ paramType = " some \( child. syntaxNodeKind. protocolType) "
27
+ } else {
28
+ paramType = child. syntaxNodeKind. syntaxType
29
+ }
30
+
31
+ if isOptional {
32
+ if paramType. is ( SomeOrAnyTypeSyntax . self) {
33
+ paramType = " ( \( paramType) )? "
34
+ } else {
35
+ paramType = " \( paramType) ? "
36
+ }
37
+ }
38
+
39
+ return paramType
40
+ }
41
+
42
+ /// Generates a memberwise SyntaxNode initializer `SyntaxNodeString`.
43
+ ///
44
+ /// - parameters:
45
+ /// - rule: The ``NodeInitRule`` to use for generating the initializer. Applying a rule will make some children non-optional, and set default values for other children.
46
+ /// - useDeprecatedChildName: Whether to use the deprecated child name for the initializer parameter.
47
+ func generateInitializerDeclHeader( for rule: NodeInitRule ? = nil , useDeprecatedChildName: Bool = false ) -> SyntaxNodeString {
20
48
if children. isEmpty {
21
49
return " public init() "
22
50
}
23
51
24
- func createFunctionParameterSyntax( for child: Child ) -> FunctionParameterSyntax {
25
- var paramType : TypeSyntax
26
- if !child. kind. isNodeChoicesEmpty {
27
- paramType = " \( child. syntaxChoicesType) "
28
- } else if child. hasBaseType {
29
- paramType = " some \( child. syntaxNodeKind. protocolType) "
52
+ func childParameterName( for child: Child ) -> TokenSyntax {
53
+ let parameterName : TokenSyntax
54
+
55
+ if useDeprecatedChildName, let deprecatedVarName = child. deprecatedVarName {
56
+ parameterName = deprecatedVarName
30
57
} else {
31
- paramType = child. syntaxNodeKind . syntaxType
58
+ parameterName = child. varOrCaseName
32
59
}
60
+ return parameterName
61
+ }
33
62
34
- if child. isOptional {
35
- if paramType. is ( SomeOrAnyTypeSyntax . self) {
36
- paramType = " ( \( paramType) )? "
63
+ func ruleBasedChildIsOptional( for child: Child , with rule: NodeInitRule ? ) -> Bool ? {
64
+ if let rule = rule {
65
+ if rule. nonOptionalChildName == child. name {
66
+ return false
37
67
} else {
38
- paramType = " \( paramType ) ? "
68
+ return child . isOptional
39
69
}
70
+ } else {
71
+ return nil
40
72
}
73
+ }
41
74
42
- let parameterName : TokenSyntax
75
+ func ruleBasedChildDefaultValue( for child: Child , with rule: NodeInitRule ? ) -> InitializerClauseSyntax ? {
76
+ if let rule, let defaultValue = rule. childDefaultValues [ child. name] {
77
+ return InitializerClauseSyntax (
78
+ equal: . equalToken( leadingTrivia: . space, trailingTrivia: . space) ,
79
+ value: ExprSyntax ( " . \( defaultValue. spec. varOrCaseName) Token() " )
80
+ )
81
+ } else {
82
+ return nil
83
+ }
84
+ }
43
85
44
- if useDeprecatedChildName, let deprecatedVarName = child. deprecatedVarName {
45
- parameterName = deprecatedVarName
86
+ func ruleBasedShouldOverrideDefault( for child: Child , with rule: NodeInitRule ? ) -> Bool {
87
+ if let rule {
88
+ // If the rule provides a default for this child, override it and set the rule-based default.
89
+ if rule. childDefaultValues [ child. name] != nil {
90
+ return true
91
+ }
92
+
93
+ // For the non-optional rule-based parameter, strip the default value (override, but there will be no default)
94
+ return rule. nonOptionalChildName == child. name
46
95
} else {
47
- parameterName = child . varOrCaseName
96
+ return false
48
97
}
98
+ }
99
+
100
+ func createFunctionParameterSyntax( for child: Child , overrideOptional: Bool ? = nil , shouldOverrideDefault: Bool = false , overrideDefaultValue: InitializerClauseSyntax ? = nil ) -> FunctionParameterSyntax {
101
+
102
+ let parameterName = childParameterName ( for: child)
49
103
50
104
return FunctionParameterSyntax (
51
105
leadingTrivia: . newline,
52
106
firstName: child. isUnexpectedNodes ? . wildcardToken( trailingTrivia: . space) : parameterName,
53
107
secondName: child. isUnexpectedNodes ? parameterName : nil ,
54
108
colon: . colonToken( ) ,
55
- type: paramType ,
56
- defaultValue: child. defaultInitialization
109
+ type: makeChildParamType ( for : child , isOptional : overrideOptional ?? child . isOptional ) ,
110
+ defaultValue: shouldOverrideDefault ? overrideDefaultValue : child. defaultInitialization
57
111
)
58
112
}
59
113
114
+ // For convenience initializers, we don't need unexpected tokens in the arguments list
115
+ // because convenience initializers are meant to be used bo developers manually
116
+ // hence there should be no unexpected tokens
117
+ let childrenToIterate = rule != nil ? nonUnexpectedChildren : children
118
+
60
119
let params = FunctionParameterListSyntax {
61
120
FunctionParameterSyntax ( " leadingTrivia: Trivia? = nil " )
62
121
63
- for child in children {
64
- createFunctionParameterSyntax ( for: child)
122
+ for child in childrenToIterate {
123
+ createFunctionParameterSyntax ( for: child,
124
+ overrideOptional: ruleBasedChildIsOptional ( for: child, with: rule) ,
125
+ shouldOverrideDefault: ruleBasedShouldOverrideDefault ( for: child, with: rule) ,
126
+ overrideDefaultValue: ruleBasedChildDefaultValue ( for: child, with: rule) )
65
127
}
66
128
67
129
FunctionParameterSyntax ( " trailingTrivia: Trivia? = nil " )
@@ -75,6 +137,14 @@ extension LayoutNode {
75
137
"""
76
138
}
77
139
140
+ func generateRuleBasedDefaultValuesDocComment( for rule: NodeInitRule ) -> SwiftSyntax . Trivia {
141
+ var params = " "
142
+ for (childName, defaultValue) in rule. childDefaultValues {
143
+ params += " - ` \( childName) `: `TokenSyntax. \( defaultValue. spec. varOrCaseName) Token()` \n "
144
+ }
145
+ return docCommentTrivia ( from: params)
146
+ }
147
+
78
148
func generateInitializerDocComment( ) -> SwiftSyntax . Trivia {
79
149
func generateParamDocComment( for child: Child ) -> String ? {
80
150
if child. documentationAbstract. isEmpty {
0 commit comments