@@ -7,6 +7,14 @@ import org.javacs.kt.index.SymbolIndex
7
7
import org.javacs.kt.position.offset
8
8
import org.javacs.kt.position.position
9
9
import org.javacs.kt.util.toPath
10
+ import org.javacs.kt.overridemembers.createFunctionStub
11
+ import org.javacs.kt.overridemembers.createVariableStub
12
+ import org.javacs.kt.overridemembers.getClassDescriptor
13
+ import org.javacs.kt.overridemembers.getDeclarationPadding
14
+ import org.javacs.kt.overridemembers.getNewMembersStartPosition
15
+ import org.javacs.kt.overridemembers.getSuperClassTypeProjections
16
+ import org.javacs.kt.overridemembers.hasNoBody
17
+ import org.javacs.kt.overridemembers.overridesDeclaration
10
18
import org.jetbrains.kotlin.descriptors.ClassDescriptor
11
19
import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor
12
20
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
@@ -32,8 +40,6 @@ import org.jetbrains.kotlin.types.KotlinType
32
40
import org.jetbrains.kotlin.types.TypeProjection
33
41
import org.jetbrains.kotlin.types.typeUtil.asTypeProjection
34
42
35
- private const val DEFAULT_TAB_SIZE = 4
36
-
37
43
class ImplementAbstractMembersQuickFix : QuickFix {
38
44
override fun compute (file : CompiledFile , index : SymbolIndex , range : Range , diagnostics : List <Diagnostic >): List <Either <Command , CodeAction >> {
39
45
val diagnostic = findDiagnosticMatch(diagnostics, range)
@@ -108,127 +114,3 @@ private fun getAbstractMembersStubs(file: CompiledFile, kotlinClass: KtClass) =
108
114
null
109
115
}
110
116
}.flatten()
111
-
112
- // interfaces are ClassDescriptors by default. When calling AbstractClass super methods, we get a ClassConstructorDescriptor
113
- private fun getClassDescriptor (descriptor : DeclarationDescriptor ? ): ClassDescriptor ? = if (descriptor is ClassDescriptor ) {
114
- descriptor
115
- } else if (descriptor is ClassConstructorDescriptor ) {
116
- descriptor.containingDeclaration
117
- } else {
118
- null
119
- }
120
-
121
- private fun getSuperClassTypeProjections (file : CompiledFile , superType : KtSuperTypeListEntry ): List <TypeProjection > = superType.typeReference?.typeElement?.children?.filter {
122
- it is KtTypeArgumentList
123
- }?.flatMap {
124
- (it as KtTypeArgumentList ).arguments
125
- }?.mapNotNull {
126
- (file.referenceExpressionAtPoint(it?.startOffset ? : 0 )?.second as ? ClassDescriptor )?.defaultType?.asTypeProjection()
127
- } ? : emptyList()
128
-
129
- // Checks if the class overrides the given declaration
130
- private fun overridesDeclaration (kotlinClass : KtClass , descriptor : FunctionDescriptor ): Boolean =
131
- kotlinClass.declarations.any {
132
- if (it.name == descriptor.name.asString() && it.hasModifier(KtTokens .OVERRIDE_KEYWORD )) {
133
- if (it is KtNamedFunction ) {
134
- parametersMatch(it, descriptor)
135
- } else {
136
- true
137
- }
138
- } else {
139
- false
140
- }
141
- }
142
-
143
- private fun overridesDeclaration (kotlinClass : KtClass , descriptor : PropertyDescriptor ): Boolean =
144
- kotlinClass.declarations.any {
145
- it.name == descriptor.name.asString() && it.hasModifier(KtTokens .OVERRIDE_KEYWORD )
146
- }
147
-
148
- // Checks if two functions have matching parameters
149
- private fun parametersMatch (function : KtNamedFunction , functionDescriptor : FunctionDescriptor ): Boolean {
150
- if (function.valueParameters.size == functionDescriptor.valueParameters.size) {
151
- for (index in 0 until function.valueParameters.size) {
152
- if (function.valueParameters[index].name != functionDescriptor.valueParameters[index].name.asString()) {
153
- return false
154
- } else if (function.valueParameters[index].typeReference?.typeName() != functionDescriptor.valueParameters[index].type.unwrappedType().toString()) {
155
- // Note: Since we treat Java overrides as non nullable by default, the above test will fail when the user has made the type nullable.
156
- // TODO: look into this
157
- return false
158
- }
159
- }
160
-
161
- if (function.typeParameters.size == functionDescriptor.typeParameters.size) {
162
- for (index in 0 until function.typeParameters.size) {
163
- if (function.typeParameters[index].variance != functionDescriptor.typeParameters[index].variance) {
164
- return false
165
- }
166
- }
167
- }
168
-
169
- return true
170
- }
171
-
172
- return false
173
- }
174
-
175
- private fun KtTypeReference.typeName (): String? = this .name ? : this .typeElement?.children?.filter {
176
- it is KtSimpleNameExpression
177
- }?.map {
178
- (it as KtSimpleNameExpression ).getReferencedName()
179
- }?.firstOrNull()
180
-
181
- private fun createFunctionStub (function : FunctionDescriptor ): String {
182
- val name = function.name
183
- val arguments = function.valueParameters.map { argument ->
184
- val argumentName = argument.name
185
- val argumentType = argument.type.unwrappedType()
186
-
187
- " $argumentName : $argumentType "
188
- }.joinToString(" , " )
189
- val returnType = function.returnType?.unwrappedType()?.toString()?.takeIf { " Unit" != it }
190
-
191
- return " override fun $name ($arguments )${returnType?.let { " : $it " } ? : " " } { }"
192
- }
193
-
194
- private fun createVariableStub (variable : PropertyDescriptor ): String {
195
- val variableType = variable.returnType?.unwrappedType()?.toString()?.takeIf { " Unit" != it }
196
- return " override val ${variable.name}${variableType?.let { " : $it " } ? : " " } = TODO(\" SET VALUE\" )"
197
- }
198
-
199
- // about types: regular Kotlin types are marked T or T?, but types from Java are (T..T?) because nullability cannot be decided.
200
- // Therefore we have to unpack in case we have the Java type. Fortunately, the Java types are not marked nullable, so we default to non nullable types. Let the user decide if they want nullable types instead. With this implementation Kotlin types also keeps their nullability
201
- private fun KotlinType.unwrappedType (): KotlinType = this .unwrap().makeNullableAsSpecified(this .isMarkedNullable)
202
-
203
- private fun getDeclarationPadding (file : CompiledFile , kotlinClass : KtClass ): String {
204
- // If the class is not empty, the amount of padding is the same as the one in the last declaration of the class
205
- val paddingSize = if (kotlinClass.declarations.isNotEmpty()) {
206
- val lastFunctionStartOffset = kotlinClass.declarations.last().startOffset
207
- position(file.content, lastFunctionStartOffset).character
208
- } else {
209
- // Otherwise, we just use a default tab size in addition to any existing padding
210
- // on the class itself (note that the class could be inside another class, for example)
211
- position(file.content, kotlinClass.startOffset).character + DEFAULT_TAB_SIZE
212
- }
213
-
214
- return " " .repeat(paddingSize)
215
- }
216
-
217
- private fun getNewMembersStartPosition (file : CompiledFile , kotlinClass : KtClass ): Position ? =
218
- // If the class is not empty, the new member will be put right after the last declaration
219
- if (kotlinClass.declarations.isNotEmpty()) {
220
- val lastFunctionEndOffset = kotlinClass.declarations.last().endOffset
221
- position(file.content, lastFunctionEndOffset)
222
- } else { // Otherwise, the member is put at the beginning of the class
223
- val body = kotlinClass.body
224
- if (body != null ) {
225
- position(file.content, body.startOffset + 1 )
226
- } else {
227
- // function has no body. We have to create one. New position is right after entire kotlin class text (with space)
228
- val newPosCorrectLine = position(file.content, kotlinClass.startOffset + 1 )
229
- newPosCorrectLine.character = (kotlinClass.text.length + 2 )
230
- newPosCorrectLine
231
- }
232
- }
233
-
234
- private fun KtClass.hasNoBody () = null == this .body
0 commit comments