Skip to content

Commit 5207d57

Browse files
committed
Implement most of the general override functionality. Stubborn equals method
1 parent 5d49b80 commit 5207d57

File tree

5 files changed

+380
-163
lines changed

5 files changed

+380
-163
lines changed

server/src/main/kotlin/org/javacs/kt/KotlinProtocolExtensionService.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import org.javacs.kt.util.AsyncExecutor
55
import org.javacs.kt.util.parseURI
66
import org.javacs.kt.resolve.resolveMain
77
import org.javacs.kt.position.offset
8-
import org.javacs.kt.listOverridableMembers
8+
import org.javacs.kt.overridemembers.listOverridableMembers
99
import java.util.concurrent.CompletableFuture
1010
import java.nio.file.Paths
1111

server/src/main/kotlin/org/javacs/kt/codeaction/quickfix/ImplementAbstractMembersQuickFix.kt

Lines changed: 8 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,14 @@ import org.javacs.kt.index.SymbolIndex
77
import org.javacs.kt.position.offset
88
import org.javacs.kt.position.position
99
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
1018
import org.jetbrains.kotlin.descriptors.ClassDescriptor
1119
import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor
1220
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
@@ -108,127 +116,3 @@ private fun getAbstractMembersStubs(file: CompiledFile, kotlinClass: KtClass) =
108116
null
109117
}
110118
}.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

Comments
 (0)