Quick Fixes: Support cross-language "Create from Usage" with Kotlin target
This commit is contained in:
@@ -310,6 +310,8 @@ class KtPsiFactory @JvmOverloads constructor(private val project: Project, val m
|
|||||||
return createProperty(text + " val x").modifierList!!
|
return createProperty(text + " val x").modifierList!!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun createEmptyModifierList() = createModifierList(KtTokens.PRIVATE_KEYWORD).apply { firstChild.delete() }
|
||||||
|
|
||||||
fun createModifier(modifier: KtModifierKeywordToken): PsiElement {
|
fun createModifier(modifier: KtModifierKeywordToken): PsiElement {
|
||||||
return createModifierList(modifier.value).getModifier(modifier)!!
|
return createModifierList(modifier.value).getModifier(modifier)!!
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -143,20 +143,20 @@ private val MODIFIERS_TO_REPLACE = mapOf(
|
|||||||
ACTUAL_KEYWORD to listOf(HEADER_KEYWORD, EXPECT_KEYWORD, IMPL_KEYWORD)
|
ACTUAL_KEYWORD to listOf(HEADER_KEYWORD, EXPECT_KEYWORD, IMPL_KEYWORD)
|
||||||
)
|
)
|
||||||
|
|
||||||
private val MODIFIERS_ORDER = listOf(PUBLIC_KEYWORD, PROTECTED_KEYWORD, PRIVATE_KEYWORD, INTERNAL_KEYWORD,
|
val MODIFIERS_ORDER = listOf(PUBLIC_KEYWORD, PROTECTED_KEYWORD, PRIVATE_KEYWORD, INTERNAL_KEYWORD,
|
||||||
HEADER_KEYWORD, IMPL_KEYWORD, EXPECT_KEYWORD, ACTUAL_KEYWORD,
|
HEADER_KEYWORD, IMPL_KEYWORD, EXPECT_KEYWORD, ACTUAL_KEYWORD,
|
||||||
FINAL_KEYWORD, OPEN_KEYWORD, ABSTRACT_KEYWORD, SEALED_KEYWORD,
|
FINAL_KEYWORD, OPEN_KEYWORD, ABSTRACT_KEYWORD, SEALED_KEYWORD,
|
||||||
CONST_KEYWORD,
|
CONST_KEYWORD,
|
||||||
EXTERNAL_KEYWORD,
|
EXTERNAL_KEYWORD,
|
||||||
OVERRIDE_KEYWORD,
|
OVERRIDE_KEYWORD,
|
||||||
LATEINIT_KEYWORD,
|
LATEINIT_KEYWORD,
|
||||||
TAILREC_KEYWORD,
|
TAILREC_KEYWORD,
|
||||||
VARARG_KEYWORD,
|
VARARG_KEYWORD,
|
||||||
SUSPEND_KEYWORD,
|
SUSPEND_KEYWORD,
|
||||||
INNER_KEYWORD,
|
INNER_KEYWORD,
|
||||||
ENUM_KEYWORD, ANNOTATION_KEYWORD,
|
ENUM_KEYWORD, ANNOTATION_KEYWORD,
|
||||||
COMPANION_KEYWORD,
|
COMPANION_KEYWORD,
|
||||||
INLINE_KEYWORD,
|
INLINE_KEYWORD,
|
||||||
INFIX_KEYWORD,
|
INFIX_KEYWORD,
|
||||||
OPERATOR_KEYWORD,
|
OPERATOR_KEYWORD,
|
||||||
DATA_KEYWORD)
|
DATA_KEYWORD)
|
||||||
|
|||||||
@@ -411,18 +411,24 @@ fun KtLambdaArgument.getLambdaArgumentName(bindingContext: BindingContext): Name
|
|||||||
fun KtExpression.asAssignment(): KtBinaryExpression? =
|
fun KtExpression.asAssignment(): KtBinaryExpression? =
|
||||||
if (KtPsiUtil.isAssignment(this)) this as KtBinaryExpression else null
|
if (KtPsiUtil.isAssignment(this)) this as KtBinaryExpression else null
|
||||||
|
|
||||||
private fun KtDeclaration.modifierFromTokenSet(set: TokenSet): PsiElement? {
|
private fun KtModifierList.modifierFromTokenSet(set: TokenSet): PsiElement? {
|
||||||
val modifierList = modifierList ?: return null
|
|
||||||
return set.types
|
return set.types
|
||||||
.asSequence()
|
.asSequence()
|
||||||
.map { modifierList.getModifier(it as KtModifierKeywordToken) }
|
.map { getModifier(it as KtModifierKeywordToken) }
|
||||||
.firstOrNull { it != null }
|
.firstOrNull { it != null }
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun KtDeclaration.visibilityModifier() = modifierFromTokenSet(KtTokens.VISIBILITY_MODIFIERS)
|
private fun KtModifierListOwner.modifierFromTokenSet(set: TokenSet) = modifierList?.modifierFromTokenSet(set)
|
||||||
|
|
||||||
fun KtDeclaration.visibilityModifierType(): KtModifierKeywordToken?
|
fun KtModifierList.visibilityModifier() = modifierFromTokenSet(KtTokens.VISIBILITY_MODIFIERS)
|
||||||
|
|
||||||
|
fun KtModifierList.visibilityModifierType(): KtModifierKeywordToken?
|
||||||
|
= visibilityModifier()?.node?.elementType as KtModifierKeywordToken?
|
||||||
|
|
||||||
|
fun KtModifierListOwner.visibilityModifier() = modifierList?.modifierFromTokenSet(KtTokens.VISIBILITY_MODIFIERS)
|
||||||
|
|
||||||
|
fun KtModifierListOwner.visibilityModifierType(): KtModifierKeywordToken?
|
||||||
= visibilityModifier()?.node?.elementType as KtModifierKeywordToken?
|
= visibilityModifier()?.node?.elementType as KtModifierKeywordToken?
|
||||||
|
|
||||||
private val MODALITY_MODIFIERS = TokenSet.create(
|
private val MODALITY_MODIFIERS = TokenSet.create(
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import org.jetbrains.kotlin.name.Name
|
|||||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||||
import org.jetbrains.kotlin.resolve.constants.ErrorValue
|
import org.jetbrains.kotlin.resolve.constants.ErrorValue
|
||||||
|
|
||||||
private val JVM_STATIC_ANNOTATION_FQ_NAME = FqName("kotlin.jvm.JvmStatic")
|
val JVM_STATIC_ANNOTATION_FQ_NAME = FqName("kotlin.jvm.JvmStatic")
|
||||||
|
|
||||||
val JVM_FIELD_ANNOTATION_FQ_NAME = FqName("kotlin.jvm.JvmField")
|
val JVM_FIELD_ANNOTATION_FQ_NAME = FqName("kotlin.jvm.JvmField")
|
||||||
|
|
||||||
|
|||||||
@@ -31,10 +31,8 @@ import org.jetbrains.kotlin.lexer.KtModifierKeywordToken
|
|||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
|
import org.jetbrains.kotlin.psi.addRemoveModifier.MODIFIERS_ORDER
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getLambdaArgumentName
|
import org.jetbrains.kotlin.psi.psiUtil.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.hasBody
|
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.visibilityModifierType
|
|
||||||
import org.jetbrains.kotlin.psi.typeRefHelpers.setReceiverTypeReference
|
import org.jetbrains.kotlin.psi.typeRefHelpers.setReceiverTypeReference
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
import org.jetbrains.kotlin.resolve.OverridingUtil
|
import org.jetbrains.kotlin.resolve.OverridingUtil
|
||||||
@@ -42,6 +40,7 @@ import org.jetbrains.kotlin.resolve.calls.callUtil.getValueArgumentsInParenthese
|
|||||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
import org.jetbrains.kotlin.types.isError
|
import org.jetbrains.kotlin.types.isError
|
||||||
|
import org.jetbrains.kotlin.utils.SmartList
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
inline fun <reified T : PsiElement> PsiElement.replaced(newElement: T): T {
|
inline fun <reified T : PsiElement> PsiElement.replaced(newElement: T): T {
|
||||||
@@ -406,3 +405,27 @@ fun KtParameter.setDefaultValue(newDefaultValue: KtExpression): PsiElement? {
|
|||||||
val eq = equalsToken ?: add(psiFactory.createEQ())
|
val eq = equalsToken ?: add(psiFactory.createEQ())
|
||||||
return addAfter(newDefaultValue, eq) as KtExpression
|
return addAfter(newDefaultValue, eq) as KtExpression
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun KtModifierList.appendModifier(modifier: KtModifierKeywordToken) {
|
||||||
|
add(KtPsiFactory(this).createModifier(modifier))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun KtModifierList.normalize(): KtModifierList {
|
||||||
|
val psiFactory = KtPsiFactory(this)
|
||||||
|
return psiFactory.createEmptyModifierList().also { newList ->
|
||||||
|
val modifiers = SmartList<PsiElement>()
|
||||||
|
allChildren.forEach {
|
||||||
|
val elementType = it.node.elementType
|
||||||
|
when {
|
||||||
|
it is KtAnnotation || it is KtAnnotationEntry -> newList.add(it)
|
||||||
|
elementType is KtModifierKeywordToken -> {
|
||||||
|
if (elementType == KtTokens.DEFAULT_VISIBILITY_KEYWORD) return@forEach
|
||||||
|
if (elementType == KtTokens.FINALLY_KEYWORD && !hasModifier(KtTokens.OVERRIDE_KEYWORD)) return@forEach
|
||||||
|
modifiers.add(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
modifiers.sortBy { MODIFIERS_ORDER.indexOf(it.node.elementType) }
|
||||||
|
modifiers.forEach { newList.add(it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -782,6 +782,8 @@
|
|||||||
<declarationRangeHandler key="org.jetbrains.kotlin.psi.KtDeclarationWithBody"
|
<declarationRangeHandler key="org.jetbrains.kotlin.psi.KtDeclarationWithBody"
|
||||||
implementationClass="org.jetbrains.kotlin.idea.codeInsight.KotlinFunDeclarationRangeHandler"/>
|
implementationClass="org.jetbrains.kotlin.idea.codeInsight.KotlinFunDeclarationRangeHandler"/>
|
||||||
|
|
||||||
|
<lang.jvm.actions.jvmElementActionsFactory implementation="org.jetbrains.kotlin.idea.quickfix.crossLanguage.KotlinElementActionsFactory"/>
|
||||||
|
|
||||||
<intentionAction>
|
<intentionAction>
|
||||||
<className>org.jetbrains.kotlin.idea.intentions.FoldInitializerAndIfToElvisIntention</className>
|
<className>org.jetbrains.kotlin.idea.intentions.FoldInitializerAndIfToElvisIntention</className>
|
||||||
<category>Kotlin</category>
|
<category>Kotlin</category>
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2017 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.quickfix
|
||||||
|
|
||||||
|
import com.intellij.codeInsight.FileModificationService
|
||||||
|
import com.intellij.openapi.editor.Editor
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
|
import com.intellij.psi.PsiFile
|
||||||
|
|
||||||
|
abstract class KotlinCrossLanguageQuickFixAction<out T : PsiElement>(element: T) : QuickFixActionBase<T>(element) {
|
||||||
|
override val isCrossLanguageFix: Boolean
|
||||||
|
get() = true
|
||||||
|
|
||||||
|
override final fun invoke(project: Project, editor: Editor?, file: PsiFile) {
|
||||||
|
val element = element
|
||||||
|
if (element != null && FileModificationService.getInstance().prepareFileForWrite(element.containingFile)) {
|
||||||
|
invokeImpl(project, editor, file)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract fun invokeImpl(project: Project, editor: Editor?, file: PsiFile)
|
||||||
|
}
|
||||||
@@ -17,42 +17,18 @@
|
|||||||
package org.jetbrains.kotlin.idea.quickfix
|
package org.jetbrains.kotlin.idea.quickfix
|
||||||
|
|
||||||
import com.intellij.codeInsight.FileModificationService
|
import com.intellij.codeInsight.FileModificationService
|
||||||
import com.intellij.codeInsight.intention.IntentionAction
|
|
||||||
import com.intellij.openapi.application.ApplicationManager
|
|
||||||
import com.intellij.openapi.editor.Editor
|
import com.intellij.openapi.editor.Editor
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
import com.intellij.psi.PsiFile
|
import com.intellij.psi.PsiFile
|
||||||
import org.jetbrains.kotlin.psi.CREATEBYPATTERN_MAY_NOT_REFORMAT
|
|
||||||
import org.jetbrains.kotlin.psi.KtCodeFragment
|
|
||||||
import org.jetbrains.kotlin.psi.KtFile
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.createSmartPointer
|
|
||||||
|
|
||||||
abstract class KotlinQuickFixAction<out T : PsiElement>(element: T) : IntentionAction {
|
abstract class KotlinQuickFixAction<out T : PsiElement>(element: T) : QuickFixActionBase<T>(element) {
|
||||||
private val elementPointer = element.createSmartPointer()
|
protected open fun isAvailable(project: Project, editor: Editor?, file: KtFile) = true
|
||||||
|
|
||||||
protected val element: T?
|
override fun isAvailableImpl(project: Project, editor: Editor?, file: PsiFile): Boolean {
|
||||||
get() = elementPointer.element
|
val ktFile = file as? KtFile ?: return false
|
||||||
|
return isAvailable(project, editor, ktFile)
|
||||||
final override fun isAvailable(project: Project, editor: Editor?, file: PsiFile): Boolean {
|
|
||||||
if (ApplicationManager.getApplication().isUnitTestMode) {
|
|
||||||
CREATEBYPATTERN_MAY_NOT_REFORMAT = true
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
val element = element ?: return false
|
|
||||||
return element.isValid &&
|
|
||||||
!element.project.isDisposed &&
|
|
||||||
(file.manager.isInProject(file) || file is KtCodeFragment) &&
|
|
||||||
(file is KtFile) &&
|
|
||||||
isAvailable(project, editor, file)
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
CREATEBYPATTERN_MAY_NOT_REFORMAT = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected open fun isAvailable(project: Project, editor: Editor?, file: KtFile): Boolean {
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final override fun invoke(project: Project, editor: Editor?, file: PsiFile) {
|
final override fun invoke(project: Project, editor: Editor?, file: PsiFile) {
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2017 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.quickfix
|
||||||
|
|
||||||
|
import com.intellij.codeInsight.intention.IntentionAction
|
||||||
|
import com.intellij.openapi.application.ApplicationManager
|
||||||
|
import com.intellij.openapi.editor.Editor
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.psi.PsiElement
|
||||||
|
import com.intellij.psi.PsiFile
|
||||||
|
import org.jetbrains.kotlin.psi.CREATEBYPATTERN_MAY_NOT_REFORMAT
|
||||||
|
import org.jetbrains.kotlin.psi.KtCodeFragment
|
||||||
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.createSmartPointer
|
||||||
|
|
||||||
|
abstract class QuickFixActionBase<out T : PsiElement>(element: T) : IntentionAction {
|
||||||
|
private val elementPointer = element.createSmartPointer()
|
||||||
|
|
||||||
|
protected val element: T?
|
||||||
|
get() = elementPointer.element
|
||||||
|
|
||||||
|
open val isCrossLanguageFix: Boolean = false
|
||||||
|
|
||||||
|
protected open fun isAvailableImpl(project: Project, editor: Editor?, file: PsiFile) = true
|
||||||
|
|
||||||
|
final override fun isAvailable(project: Project, editor: Editor?, file: PsiFile): Boolean {
|
||||||
|
if (ApplicationManager.getApplication().isUnitTestMode) {
|
||||||
|
CREATEBYPATTERN_MAY_NOT_REFORMAT = true
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
val element = element ?: return false
|
||||||
|
return element.isValid &&
|
||||||
|
!element.project.isDisposed &&
|
||||||
|
(file.manager.isInProject(file) || file is KtCodeFragment) &&
|
||||||
|
(file is KtFile || isCrossLanguageFix) &&
|
||||||
|
isAvailableImpl(project, editor, file)
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
CREATEBYPATTERN_MAY_NOT_REFORMAT = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun startInWriteAction() = true
|
||||||
|
}
|
||||||
+65
-46
@@ -452,16 +452,34 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
|
|||||||
|
|
||||||
val psiFactory = KtPsiFactory(currentFile)
|
val psiFactory = KtPsiFactory(currentFile)
|
||||||
|
|
||||||
val modifiers =
|
val modifiers = buildString {
|
||||||
if (callableInfo.isAbstract) {
|
val modifierList = callableInfo.modifierList?.copied() ?: psiFactory.createEmptyModifierList()
|
||||||
if (containingElement is KtClass && containingElement.isInterface()) "" else "abstract "
|
val visibilityKeyword = modifierList.visibilityModifierType()
|
||||||
}
|
if (visibilityKeyword == null) {
|
||||||
else if (containingElement is KtClassOrObject
|
val defaultVisibility =
|
||||||
&& !(containingElement is KtClass && containingElement.isInterface())
|
if (callableInfo.isAbstract) ""
|
||||||
&& containingElement.isAncestor(config.originalElement)
|
else if (containingElement is KtClassOrObject
|
||||||
&& callableInfo.kind != CallableKind.CONSTRUCTOR) "private "
|
&& !(containingElement is KtClass && containingElement.isInterface())
|
||||||
else if (isExtension) "private "
|
&& containingElement.isAncestor(config.originalElement)
|
||||||
else ""
|
&& callableInfo.kind != CallableKind.CONSTRUCTOR) "private "
|
||||||
|
else if (isExtension) "private "
|
||||||
|
else ""
|
||||||
|
append(defaultVisibility)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Get rid of isAbstract
|
||||||
|
if (callableInfo.isAbstract
|
||||||
|
&& containingElement is KtClass
|
||||||
|
&& !containingElement.isInterface()
|
||||||
|
&& !modifierList.hasModifier(KtTokens.ABSTRACT_KEYWORD)) {
|
||||||
|
modifierList.appendModifier(KtTokens.ABSTRACT_KEYWORD)
|
||||||
|
}
|
||||||
|
|
||||||
|
val text = modifierList.normalize().text
|
||||||
|
if (text.isNotEmpty()) {
|
||||||
|
append("$text ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val isExpectClassMember by lazy {
|
val isExpectClassMember by lazy {
|
||||||
containingElement is KtClassOrObject && (containingElement.resolveToDescriptorIfAny() as? ClassDescriptor)?.isExpect ?: false
|
containingElement is KtClassOrObject && (containingElement.resolveToDescriptorIfAny() as? ClassDescriptor)?.isExpect ?: false
|
||||||
@@ -470,7 +488,7 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
|
|||||||
val declaration: KtNamedDeclaration = when (callableInfo.kind) {
|
val declaration: KtNamedDeclaration = when (callableInfo.kind) {
|
||||||
CallableKind.FUNCTION, CallableKind.CONSTRUCTOR -> {
|
CallableKind.FUNCTION, CallableKind.CONSTRUCTOR -> {
|
||||||
val body = when {
|
val body = when {
|
||||||
callableInfo.kind == CallableKind.CONSTRUCTOR -> ""
|
callableInfo is ConstructorInfo -> if (callableInfo.withBody) "{\n\n}" else ""
|
||||||
callableInfo.isAbstract -> ""
|
callableInfo.isAbstract -> ""
|
||||||
containingElement is KtClass && containingElement.hasModifier(KtTokens.EXTERNAL_KEYWORD) -> ""
|
containingElement is KtClass && containingElement.hasModifier(KtTokens.EXTERNAL_KEYWORD) -> ""
|
||||||
containingElement is KtObjectDeclaration && containingElement.hasModifier(KtTokens.EXTERNAL_KEYWORD) -> ""
|
containingElement is KtObjectDeclaration && containingElement.hasModifier(KtTokens.EXTERNAL_KEYWORD) -> ""
|
||||||
@@ -478,7 +496,7 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
|
|||||||
&& containingElement.parent.parent is KtClass
|
&& containingElement.parent.parent is KtClass
|
||||||
&& (containingElement.parent.parent as KtClass).hasModifier(KtTokens.EXTERNAL_KEYWORD) -> ""
|
&& (containingElement.parent.parent as KtClass).hasModifier(KtTokens.EXTERNAL_KEYWORD) -> ""
|
||||||
isExpectClassMember -> ""
|
isExpectClassMember -> ""
|
||||||
else -> "{}"
|
else -> "{\n\n}"
|
||||||
|
|
||||||
}
|
}
|
||||||
@Suppress("USELESS_CAST") // KT-10755
|
@Suppress("USELESS_CAST") // KT-10755
|
||||||
@@ -544,7 +562,11 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
|
|||||||
return assignmentToReplace.replace(declaration) as KtCallableDeclaration
|
return assignmentToReplace.replace(declaration) as KtCallableDeclaration
|
||||||
}
|
}
|
||||||
|
|
||||||
val declarationInPlace = placeDeclarationInContainer(declaration, containingElement, config.originalElement, jetFileToEdit)
|
val container = if (containingElement is KtClass && callableInfo.isForCompanion) {
|
||||||
|
containingElement.getOrCreateCompanionObject()
|
||||||
|
}
|
||||||
|
else containingElement
|
||||||
|
val declarationInPlace = placeDeclarationInContainer(declaration, container, config.originalElement, jetFileToEdit)
|
||||||
|
|
||||||
if (declarationInPlace is KtSecondaryConstructor) {
|
if (declarationInPlace is KtSecondaryConstructor) {
|
||||||
val containingClass = declarationInPlace.containingClassOrObject!!
|
val containingClass = declarationInPlace.containingClassOrObject!!
|
||||||
@@ -581,14 +603,11 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun setupTypeReferencesForShortening(declaration: KtNamedDeclaration,
|
private fun setupTypeReferencesForShortening(declaration: KtNamedDeclaration,
|
||||||
parameterTypeExpressions: List<TypeExpression>): List<KtElement> {
|
parameterTypeExpressions: List<TypeExpression>) {
|
||||||
val typeRefsToShorten = ArrayList<KtElement>()
|
|
||||||
|
|
||||||
if (config.isExtension) {
|
if (config.isExtension) {
|
||||||
val receiverTypeText = receiverTypeCandidate!!.theType.renderLong(typeParameterNameMap).first()
|
val receiverTypeText = receiverTypeCandidate!!.theType.renderLong(typeParameterNameMap).first()
|
||||||
val replacingTypeRef = KtPsiFactory(declaration).createType(receiverTypeText)
|
val replacingTypeRef = KtPsiFactory(declaration).createType(receiverTypeText)
|
||||||
val newTypeRef = (declaration as KtCallableDeclaration).setReceiverTypeReference(replacingTypeRef)!!
|
(declaration as KtCallableDeclaration).setReceiverTypeReference(replacingTypeRef)!!
|
||||||
typeRefsToShorten.add(newTypeRef)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val returnTypeRefs = declaration.getReturnTypeReferences()
|
val returnTypeRefs = declaration.getReturnTypeReferences()
|
||||||
@@ -599,7 +618,6 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
|
|||||||
if (returnType != null) {
|
if (returnType != null) {
|
||||||
// user selected a given type
|
// user selected a given type
|
||||||
replaceWithLongerName(returnTypeRefs, returnType)
|
replaceWithLongerName(returnTypeRefs, returnType)
|
||||||
typeRefsToShorten.addAll(declaration.getReturnTypeReferences())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -618,11 +636,6 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val expandedValueParameters = declaration.getValueParameters()
|
|
||||||
parameterIndicesToShorten.mapNotNullTo(typeRefsToShorten) { expandedValueParameters[it].typeReference }
|
|
||||||
|
|
||||||
return typeRefsToShorten
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun postprocessDeclaration(declaration: KtNamedDeclaration) {
|
private fun postprocessDeclaration(declaration: KtNamedDeclaration) {
|
||||||
@@ -636,15 +649,12 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun setupDeclarationBody(func: KtDeclarationWithBody) {
|
private fun setupDeclarationBody(func: KtDeclarationWithBody) {
|
||||||
|
if (func !is KtNamedFunction && func !is KtPropertyAccessor) return
|
||||||
|
if (skipReturnType && callableInfo is FunctionInfo && callableInfo.preferEmptyBody) return
|
||||||
val oldBody = func.bodyExpression ?: return
|
val oldBody = func.bodyExpression ?: return
|
||||||
val templateKind = when (func) {
|
|
||||||
is KtSecondaryConstructor -> TemplateKind.SECONDARY_CONSTRUCTOR
|
|
||||||
is KtNamedFunction, is KtPropertyAccessor -> TemplateKind.FUNCTION
|
|
||||||
else -> throw AssertionError("Unexpected declaration: " + func.getElementTextWithContext())
|
|
||||||
}
|
|
||||||
val bodyText = getFunctionBodyTextFromTemplate(
|
val bodyText = getFunctionBodyTextFromTemplate(
|
||||||
func.project,
|
func.project,
|
||||||
templateKind,
|
TemplateKind.FUNCTION,
|
||||||
if (callableInfo.name.isNotEmpty()) callableInfo.name else null,
|
if (callableInfo.name.isNotEmpty()) callableInfo.name else null,
|
||||||
if (skipReturnType) "Unit" else (func as? KtFunction)?.typeReference?.text ?: "",
|
if (skipReturnType) "Unit" else (func as? KtFunction)?.typeReference?.text ?: "",
|
||||||
receiverClassDescriptor?.importableFqName ?: receiverClassDescriptor?.name?.let { FqName.topLevel(it) }
|
receiverClassDescriptor?.importableFqName ?: receiverClassDescriptor?.name?.let { FqName.topLevel(it) }
|
||||||
@@ -751,13 +761,7 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
|
|||||||
|
|
||||||
// add parameter name to the template
|
// add parameter name to the template
|
||||||
val possibleNamesFromExpression = parameter.typeInfo.getPossibleNamesFromExpression(currentFileContext)
|
val possibleNamesFromExpression = parameter.typeInfo.getPossibleNamesFromExpression(currentFileContext)
|
||||||
val preferredName = parameter.preferredName
|
val possibleNames = arrayOf(*parameter.nameSuggestions.toTypedArray(), *possibleNamesFromExpression)
|
||||||
val possibleNames = if (preferredName != null) {
|
|
||||||
arrayOf(preferredName, *possibleNamesFromExpression)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
possibleNamesFromExpression
|
|
||||||
}
|
|
||||||
|
|
||||||
// figure out suggested names for each type option
|
// figure out suggested names for each type option
|
||||||
val parameterTypeToNamesMap = HashMap<String, Array<String>>()
|
val parameterTypeToNamesMap = HashMap<String, Array<String>>()
|
||||||
@@ -951,6 +955,14 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
|
|||||||
|
|
||||||
if (newDeclaration is KtProperty) {
|
if (newDeclaration is KtProperty) {
|
||||||
newDeclaration.getter?.let { setupDeclarationBody(it) }
|
newDeclaration.getter?.let { setupDeclarationBody(it) }
|
||||||
|
|
||||||
|
if (newDeclaration.getter == null
|
||||||
|
&& newDeclaration.initializer == null
|
||||||
|
&& callableInfo is PropertyInfo
|
||||||
|
&& callableInfo.withInitializer
|
||||||
|
&& !callableInfo.isLateinitPreferred) {
|
||||||
|
newDeclaration.initializer = KtPsiFactory(newDeclaration).createExpression("TODO(\"initialize me\")")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val callElement = config.originalElement as? KtCallElement
|
val callElement = config.originalElement as? KtCallElement
|
||||||
@@ -961,9 +973,9 @@ class CallableBuilder(val config: CallableBuilderConfiguration) {
|
|||||||
CodeStyleManager.getInstance(project).reformat(newDeclaration)
|
CodeStyleManager.getInstance(project).reformat(newDeclaration)
|
||||||
|
|
||||||
// change short type names to fully qualified ones (to be shortened below)
|
// change short type names to fully qualified ones (to be shortened below)
|
||||||
val typeRefsToShorten = setupTypeReferencesForShortening(newDeclaration, parameterTypeExpressions)
|
setupTypeReferencesForShortening(newDeclaration, parameterTypeExpressions)
|
||||||
if (!transformToJavaMemberIfApplicable(newDeclaration)) {
|
if (!transformToJavaMemberIfApplicable(newDeclaration)) {
|
||||||
elementsToShorten.addAll(typeRefsToShorten)
|
elementsToShorten.add(newDeclaration)
|
||||||
setupEditor(newDeclaration)
|
setupEditor(newDeclaration)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1090,17 +1102,24 @@ internal fun <D : KtNamedDeclaration> placeDeclarationInContainer(
|
|||||||
}
|
}
|
||||||
|
|
||||||
container is KtClassOrObject -> {
|
container is KtClassOrObject -> {
|
||||||
insertMember(null, container, declaration, container.declarations.lastOrNull())
|
var sibling: PsiElement? = container.declarations.lastOrNull { it::class == declaration::class }
|
||||||
|
if (sibling == null && declaration is KtProperty) {
|
||||||
|
sibling = container.getBody()?.lBrace
|
||||||
|
}
|
||||||
|
|
||||||
|
insertMember(null, container, declaration, sibling)
|
||||||
}
|
}
|
||||||
else -> throw AssertionError("Invalid containing element: ${container.text}")
|
else -> throw AssertionError("Invalid containing element: ${container.text}")
|
||||||
}
|
}
|
||||||
|
|
||||||
val parent = declarationInPlace.parent
|
if (declaration !is KtPrimaryConstructor) {
|
||||||
calcNecessaryEmptyLines(declarationInPlace, false).let {
|
val parent = declarationInPlace.parent
|
||||||
if (it > 0) parent.addBefore(psiFactory.createNewLine(it), declarationInPlace)
|
calcNecessaryEmptyLines(declarationInPlace, false).let {
|
||||||
}
|
if (it > 0) parent.addBefore(psiFactory.createNewLine(it), declarationInPlace)
|
||||||
calcNecessaryEmptyLines(declarationInPlace, true).let {
|
}
|
||||||
if (it > 0) parent.addAfter(psiFactory.createNewLine(it), declarationInPlace)
|
calcNecessaryEmptyLines(declarationInPlace, true).let {
|
||||||
|
if (it > 0) parent.addAfter(psiFactory.createNewLine(it), declarationInPlace)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return declarationInPlace
|
return declarationInPlace
|
||||||
}
|
}
|
||||||
|
|||||||
+44
-13
@@ -70,6 +70,10 @@ abstract class TypeInfo(val variance: Variance) {
|
|||||||
(builder.placement as CallablePlacement.WithReceiver).receiverTypeCandidate.theType.getPossibleSupertypes(variance, builder)
|
(builder.placement as CallablePlacement.WithReceiver).receiverTypeCandidate.theType.getPossibleSupertypes(variance, builder)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ByExplicitCandidateTypes(val types: List<KotlinType>) : TypeInfo(Variance.INVARIANT) {
|
||||||
|
override fun getPossibleTypes(builder: CallableBuilder) = types
|
||||||
|
}
|
||||||
|
|
||||||
abstract class DelegatingTypeInfo(val delegate: TypeInfo): TypeInfo(delegate.variance) {
|
abstract class DelegatingTypeInfo(val delegate: TypeInfo): TypeInfo(delegate.variance) {
|
||||||
override val substitutionsAllowed: Boolean = delegate.substitutionsAllowed
|
override val substitutionsAllowed: Boolean = delegate.substitutionsAllowed
|
||||||
override fun getPossibleNamesFromExpression(bindingContext: BindingContext) = delegate.getPossibleNamesFromExpression(bindingContext)
|
override fun getPossibleNamesFromExpression(bindingContext: BindingContext) = delegate.getPossibleNamesFromExpression(bindingContext)
|
||||||
@@ -155,8 +159,10 @@ fun TypeInfo.ofThis() = TypeInfo.OfThis(this)
|
|||||||
*/
|
*/
|
||||||
class ParameterInfo(
|
class ParameterInfo(
|
||||||
val typeInfo: TypeInfo,
|
val typeInfo: TypeInfo,
|
||||||
val preferredName: String? = null
|
val nameSuggestions: List<String>
|
||||||
)
|
) {
|
||||||
|
constructor(typeInfo: TypeInfo, preferredName: String? = null): this(typeInfo, listOfNotNull(preferredName))
|
||||||
|
}
|
||||||
|
|
||||||
enum class CallableKind {
|
enum class CallableKind {
|
||||||
FUNCTION,
|
FUNCTION,
|
||||||
@@ -171,7 +177,9 @@ abstract class CallableInfo (
|
|||||||
val returnTypeInfo: TypeInfo,
|
val returnTypeInfo: TypeInfo,
|
||||||
val possibleContainers: List<KtElement>,
|
val possibleContainers: List<KtElement>,
|
||||||
val typeParameterInfos: List<TypeInfo>,
|
val typeParameterInfos: List<TypeInfo>,
|
||||||
val isAbstract: Boolean = false
|
val isAbstract: Boolean = false,
|
||||||
|
val isForCompanion: Boolean = false,
|
||||||
|
val modifierList: KtModifierList? = null
|
||||||
) {
|
) {
|
||||||
abstract val kind: CallableKind
|
abstract val kind: CallableKind
|
||||||
abstract val parameterInfos: List<ParameterInfo>
|
abstract val parameterInfos: List<ParameterInfo>
|
||||||
@@ -189,8 +197,11 @@ class FunctionInfo(name: String,
|
|||||||
typeParameterInfos: List<TypeInfo> = Collections.emptyList(),
|
typeParameterInfos: List<TypeInfo> = Collections.emptyList(),
|
||||||
val isOperator: Boolean = false,
|
val isOperator: Boolean = false,
|
||||||
val isInfix: Boolean = false,
|
val isInfix: Boolean = false,
|
||||||
isAbstract: Boolean = false
|
isAbstract: Boolean = false,
|
||||||
) : CallableInfo(name, receiverTypeInfo, returnTypeInfo, possibleContainers, typeParameterInfos, isAbstract) {
|
isForCompanion: Boolean = false,
|
||||||
|
modifierList: KtModifierList? = null,
|
||||||
|
val preferEmptyBody: Boolean = false
|
||||||
|
) : CallableInfo(name, receiverTypeInfo, returnTypeInfo, possibleContainers, typeParameterInfos, isAbstract, isForCompanion, modifierList) {
|
||||||
override val kind: CallableKind get() = CallableKind.FUNCTION
|
override val kind: CallableKind get() = CallableKind.FUNCTION
|
||||||
|
|
||||||
override fun copy(receiverTypeInfo: TypeInfo, possibleContainers: List<KtElement>, isAbstract: Boolean) = FunctionInfo(
|
override fun copy(receiverTypeInfo: TypeInfo, possibleContainers: List<KtElement>, isAbstract: Boolean) = FunctionInfo(
|
||||||
@@ -206,8 +217,12 @@ class FunctionInfo(name: String,
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
class ClassWithPrimaryConstructorInfo(val classInfo: ClassInfo, expectedTypeInfo: TypeInfo): CallableInfo(
|
class ClassWithPrimaryConstructorInfo(
|
||||||
classInfo.name, TypeInfo.Empty, expectedTypeInfo.forceNotNull(), Collections.emptyList(), classInfo.typeArguments, false
|
val classInfo: ClassInfo,
|
||||||
|
expectedTypeInfo: TypeInfo,
|
||||||
|
modifierList: KtModifierList? = null
|
||||||
|
): CallableInfo(
|
||||||
|
classInfo.name, TypeInfo.Empty, expectedTypeInfo.forceNotNull(), Collections.emptyList(), classInfo.typeArguments, false, modifierList = modifierList
|
||||||
) {
|
) {
|
||||||
override val kind: CallableKind get() = CallableKind.CLASS_WITH_PRIMARY_CONSTRUCTOR
|
override val kind: CallableKind get() = CallableKind.CLASS_WITH_PRIMARY_CONSTRUCTOR
|
||||||
override val parameterInfos: List<ParameterInfo> get() = classInfo.parameterInfos
|
override val parameterInfos: List<ParameterInfo> get() = classInfo.parameterInfos
|
||||||
@@ -218,8 +233,10 @@ class ClassWithPrimaryConstructorInfo(val classInfo: ClassInfo, expectedTypeInfo
|
|||||||
class ConstructorInfo(
|
class ConstructorInfo(
|
||||||
override val parameterInfos: List<ParameterInfo>,
|
override val parameterInfos: List<ParameterInfo>,
|
||||||
val targetClass: PsiElement,
|
val targetClass: PsiElement,
|
||||||
val isPrimary: Boolean = false
|
val isPrimary: Boolean = false,
|
||||||
): CallableInfo("", TypeInfo.Empty, TypeInfo.Empty, Collections.emptyList(), Collections.emptyList(), false) {
|
modifierList: KtModifierList? = null,
|
||||||
|
val withBody: Boolean = false
|
||||||
|
): CallableInfo("", TypeInfo.Empty, TypeInfo.Empty, Collections.emptyList(), Collections.emptyList(), false, modifierList = modifierList) {
|
||||||
override val kind: CallableKind get() = CallableKind.CONSTRUCTOR
|
override val kind: CallableKind get() = CallableKind.CONSTRUCTOR
|
||||||
|
|
||||||
override fun copy(receiverTypeInfo: TypeInfo, possibleContainers: List<KtElement>, isAbstract: Boolean) = throw UnsupportedOperationException()
|
override fun copy(receiverTypeInfo: TypeInfo, possibleContainers: List<KtElement>, isAbstract: Boolean) = throw UnsupportedOperationException()
|
||||||
@@ -232,12 +249,23 @@ class PropertyInfo(name: String,
|
|||||||
possibleContainers: List<KtElement> = Collections.emptyList(),
|
possibleContainers: List<KtElement> = Collections.emptyList(),
|
||||||
typeParameterInfos: List<TypeInfo> = Collections.emptyList(),
|
typeParameterInfos: List<TypeInfo> = Collections.emptyList(),
|
||||||
isAbstract: Boolean = false,
|
isAbstract: Boolean = false,
|
||||||
val isLateinitPreferred: Boolean = false
|
val isLateinitPreferred: Boolean = false,
|
||||||
) : CallableInfo(name, receiverTypeInfo, returnTypeInfo, possibleContainers, typeParameterInfos, isAbstract) {
|
isForCompanion: Boolean = false,
|
||||||
|
modifierList: KtModifierList? = null,
|
||||||
|
val withInitializer: Boolean = false
|
||||||
|
) : CallableInfo(name, receiverTypeInfo, returnTypeInfo, possibleContainers, typeParameterInfos, isAbstract, isForCompanion, modifierList) {
|
||||||
override val kind: CallableKind get() = CallableKind.PROPERTY
|
override val kind: CallableKind get() = CallableKind.PROPERTY
|
||||||
override val parameterInfos: List<ParameterInfo> get() = Collections.emptyList()
|
override val parameterInfos: List<ParameterInfo> get() = Collections.emptyList()
|
||||||
|
|
||||||
override fun copy(receiverTypeInfo: TypeInfo, possibleContainers: List<KtElement>, isAbstract: Boolean) = PropertyInfo(
|
override fun copy(receiverTypeInfo: TypeInfo, possibleContainers: List<KtElement>, isAbstract: Boolean) =
|
||||||
|
copyProperty(receiverTypeInfo, possibleContainers, isAbstract)
|
||||||
|
|
||||||
|
fun copyProperty(
|
||||||
|
receiverTypeInfo: TypeInfo = this.receiverTypeInfo,
|
||||||
|
possibleContainers: List<KtElement> = this.possibleContainers,
|
||||||
|
isAbstract: Boolean = this.isAbstract,
|
||||||
|
isLateinitPreferred: Boolean = this.isLateinitPreferred
|
||||||
|
) = PropertyInfo(
|
||||||
name,
|
name,
|
||||||
receiverTypeInfo,
|
receiverTypeInfo,
|
||||||
returnTypeInfo,
|
returnTypeInfo,
|
||||||
@@ -245,6 +273,9 @@ class PropertyInfo(name: String,
|
|||||||
possibleContainers,
|
possibleContainers,
|
||||||
typeParameterInfos,
|
typeParameterInfos,
|
||||||
isAbstract,
|
isAbstract,
|
||||||
isLateinitPreferred
|
isLateinitPreferred,
|
||||||
|
isForCompanion,
|
||||||
|
modifierList,
|
||||||
|
withInitializer
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-4
@@ -29,11 +29,13 @@ fun setupEditorSelection(editor: Editor, declaration: KtNamedDeclaration) {
|
|||||||
val caretModel = editor.caretModel
|
val caretModel = editor.caretModel
|
||||||
val selectionModel = editor.selectionModel
|
val selectionModel = editor.selectionModel
|
||||||
|
|
||||||
if (declaration is KtSecondaryConstructor) {
|
val offset = when (declaration) {
|
||||||
caretModel.moveToOffset(declaration.getConstructorKeyword().endOffset)
|
is KtPrimaryConstructor -> declaration.getConstructorKeyword()?.endOffset ?: declaration.valueParameterList?.startOffset
|
||||||
|
is KtSecondaryConstructor -> declaration.getConstructorKeyword().endOffset
|
||||||
|
else -> declaration.nameIdentifier?.endOffset
|
||||||
}
|
}
|
||||||
else {
|
if (offset != null) {
|
||||||
caretModel.moveToOffset(declaration.nameIdentifier!!.endOffset)
|
caretModel.moveToOffset(offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun positionBetween(left: PsiElement, right: PsiElement) {
|
fun positionBetween(left: PsiElement, right: PsiElement) {
|
||||||
|
|||||||
+28
-7
@@ -17,14 +17,19 @@
|
|||||||
package org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable
|
package org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable
|
||||||
|
|
||||||
import com.intellij.codeInsight.intention.LowPriorityAction
|
import com.intellij.codeInsight.intention.LowPriorityAction
|
||||||
|
import com.intellij.codeInsight.navigation.NavigationUtil
|
||||||
|
import com.intellij.ide.util.EditorHelper
|
||||||
import com.intellij.openapi.editor.Editor
|
import com.intellij.openapi.editor.Editor
|
||||||
|
import com.intellij.openapi.fileEditor.FileEditorManager
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.psi.PsiClass
|
import com.intellij.psi.PsiClass
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
|
import com.intellij.psi.PsiFile
|
||||||
import org.jetbrains.kotlin.builtins.functions.FunctionClassDescriptor
|
import org.jetbrains.kotlin.builtins.functions.FunctionClassDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
|
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
|
||||||
|
import org.jetbrains.kotlin.idea.KotlinBundle
|
||||||
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
|
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
|
||||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.CreateFromUsageFixBase
|
import org.jetbrains.kotlin.idea.quickfix.KotlinCrossLanguageQuickFixAction
|
||||||
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.*
|
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.*
|
||||||
import org.jetbrains.kotlin.idea.refactoring.canRefactor
|
import org.jetbrains.kotlin.idea.refactoring.canRefactor
|
||||||
import org.jetbrains.kotlin.idea.refactoring.chooseContainerElementIfNecessary
|
import org.jetbrains.kotlin.idea.refactoring.chooseContainerElementIfNecessary
|
||||||
@@ -35,7 +40,7 @@ import org.jetbrains.kotlin.psi.*
|
|||||||
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
|
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class CreateCallableFromUsageFix<E : KtElement>(
|
open class CreateCallableFromUsageFix<E : KtElement>(
|
||||||
originalExpression: E,
|
originalExpression: E,
|
||||||
callableInfos: List<CallableInfo>
|
callableInfos: List<CallableInfo>
|
||||||
) : CreateCallableFromUsageFixBase<E>(originalExpression, callableInfos, false)
|
) : CreateCallableFromUsageFixBase<E>(originalExpression, callableInfos, false)
|
||||||
@@ -47,9 +52,9 @@ class CreateExtensionCallableFromUsageFix<E : KtElement>(
|
|||||||
|
|
||||||
abstract class CreateCallableFromUsageFixBase<E : KtElement>(
|
abstract class CreateCallableFromUsageFixBase<E : KtElement>(
|
||||||
originalExpression: E,
|
originalExpression: E,
|
||||||
private val callableInfos: List<CallableInfo>,
|
protected val callableInfos: List<CallableInfo>,
|
||||||
val isExtension: Boolean
|
val isExtension: Boolean
|
||||||
) : CreateFromUsageFixBase<E>(originalExpression) {
|
) : KotlinCrossLanguageQuickFixAction<E>(originalExpression) {
|
||||||
init {
|
init {
|
||||||
assert (callableInfos.isNotEmpty()) { "No CallableInfos: ${originalExpression.getElementTextWithContext()}" }
|
assert (callableInfos.isNotEmpty()) { "No CallableInfos: ${originalExpression.getElementTextWithContext()}" }
|
||||||
if (callableInfos.size > 1) {
|
if (callableInfos.size > 1) {
|
||||||
@@ -77,6 +82,8 @@ abstract class CreateCallableFromUsageFixBase<E : KtElement>(
|
|||||||
return if (isExtension || declaration.canRefactor()) declaration else null
|
return if (isExtension || declaration.canRefactor()) declaration else null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getFamilyName(): String = KotlinBundle.message("create.from.usage.family")
|
||||||
|
|
||||||
override fun getText(): String {
|
override fun getText(): String {
|
||||||
val element = element ?: return ""
|
val element = element ?: return ""
|
||||||
val receiverTypeInfo = callableInfos.first().receiverTypeInfo
|
val receiverTypeInfo = callableInfos.first().receiverTypeInfo
|
||||||
@@ -141,7 +148,7 @@ abstract class CreateCallableFromUsageFixBase<E : KtElement>(
|
|||||||
}.toString()
|
}.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun isAvailable(project: Project, editor: Editor?, file: KtFile): Boolean {
|
override fun isAvailableImpl(project: Project, editor: Editor?, file: PsiFile): Boolean {
|
||||||
val element = element ?: return false
|
val element = element ?: return false
|
||||||
|
|
||||||
val receiverInfo = callableInfos.first().receiverTypeInfo
|
val receiverInfo = callableInfos.first().receiverTypeInfo
|
||||||
@@ -173,12 +180,26 @@ abstract class CreateCallableFromUsageFixBase<E : KtElement>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun invoke(project: Project, editor: Editor?, file: KtFile) {
|
override fun invokeImpl(project: Project, editor: Editor?, file: PsiFile) {
|
||||||
|
if (editor == null) return
|
||||||
|
|
||||||
val element = element ?: return
|
val element = element ?: return
|
||||||
val callableInfo = callableInfos.first()
|
val callableInfo = callableInfos.first()
|
||||||
|
|
||||||
|
val fileForBuilder: KtFile
|
||||||
|
val editorForBuilder: Editor
|
||||||
|
if (file is KtFile) {
|
||||||
|
fileForBuilder = file
|
||||||
|
editorForBuilder = editor
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fileForBuilder = element.containingKtFile
|
||||||
|
EditorHelper.openInEditor(element)
|
||||||
|
editorForBuilder = FileEditorManager.getInstance(project).selectedTextEditor!!
|
||||||
|
}
|
||||||
|
|
||||||
val callableBuilder =
|
val callableBuilder =
|
||||||
CallableBuilderConfiguration(callableInfos, element as KtElement, file, editor!!, isExtension).createBuilder()
|
CallableBuilderConfiguration(callableInfos, element as KtElement, fileForBuilder, editorForBuilder, isExtension).createBuilder()
|
||||||
|
|
||||||
fun runBuilder(placement: CallablePlacement) {
|
fun runBuilder(placement: CallablePlacement) {
|
||||||
callableBuilder.placement = placement
|
callableBuilder.placement = placement
|
||||||
|
|||||||
+2
-1
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable
|
package org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable
|
||||||
|
|
||||||
|
import com.intellij.codeInsight.intention.IntentionAction
|
||||||
import com.intellij.psi.SmartPsiElementPointer
|
import com.intellij.psi.SmartPsiElementPointer
|
||||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||||
import org.jetbrains.kotlin.idea.quickfix.IntentionActionPriority
|
import org.jetbrains.kotlin.idea.quickfix.IntentionActionPriority
|
||||||
@@ -35,7 +36,7 @@ abstract class CreateCallableMemberFromUsageFactory<E : KtElement>(
|
|||||||
originalElementPointer: SmartPsiElementPointer<E>,
|
originalElementPointer: SmartPsiElementPointer<E>,
|
||||||
priority: IntentionActionPriority,
|
priority: IntentionActionPriority,
|
||||||
quickFixDataFactory: () -> List<CallableInfo>?,
|
quickFixDataFactory: () -> List<CallableInfo>?,
|
||||||
quickFixFactory: (E, List<CallableInfo>) -> CreateFromUsageFixBase<E>?
|
quickFixFactory: (E, List<CallableInfo>) -> IntentionAction?
|
||||||
): QuickFixWithDelegateFactory {
|
): QuickFixWithDelegateFactory {
|
||||||
return QuickFixWithDelegateFactory(priority) {
|
return QuickFixWithDelegateFactory(priority) {
|
||||||
val data = quickFixDataFactory().orEmpty()
|
val data = quickFixDataFactory().orEmpty()
|
||||||
|
|||||||
+390
@@ -0,0 +1,390 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2017 JetBrains s.r.o.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.quickfix.crossLanguage
|
||||||
|
|
||||||
|
import com.intellij.codeInsight.intention.IntentionAction
|
||||||
|
import com.intellij.codeInsight.intention.QuickFixFactory
|
||||||
|
import com.intellij.lang.jvm.*
|
||||||
|
import com.intellij.lang.jvm.actions.*
|
||||||
|
import com.intellij.openapi.project.Project
|
||||||
|
import com.intellij.psi.*
|
||||||
|
import com.intellij.psi.impl.source.tree.java.PsiReferenceExpressionImpl
|
||||||
|
import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade
|
||||||
|
import org.jetbrains.kotlin.asJava.classes.KtLightClassForSourceDeclaration
|
||||||
|
import org.jetbrains.kotlin.asJava.toLightMethods
|
||||||
|
import org.jetbrains.kotlin.asJava.unwrapped
|
||||||
|
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||||
|
import org.jetbrains.kotlin.descriptors.Modality
|
||||||
|
import org.jetbrains.kotlin.descriptors.SourceElement
|
||||||
|
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.impl.ClassDescriptorImpl
|
||||||
|
import org.jetbrains.kotlin.descriptors.impl.MutablePackageFragmentDescriptor
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
||||||
|
import org.jetbrains.kotlin.idea.core.appendModifier
|
||||||
|
import org.jetbrains.kotlin.idea.quickfix.AddModifierFix
|
||||||
|
import org.jetbrains.kotlin.idea.quickfix.RemoveModifierFix
|
||||||
|
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.*
|
||||||
|
import org.jetbrains.kotlin.idea.quickfix.createFromUsage.createCallable.CreateCallableFromUsageFix
|
||||||
|
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
|
||||||
|
import org.jetbrains.kotlin.idea.util.approximateFlexibleTypes
|
||||||
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
|
import org.jetbrains.kotlin.load.java.components.TypeUsage
|
||||||
|
import org.jetbrains.kotlin.load.java.lazy.JavaResolverComponents
|
||||||
|
import org.jetbrains.kotlin.load.java.lazy.LazyJavaResolverContext
|
||||||
|
import org.jetbrains.kotlin.load.java.lazy.TypeParameterResolver
|
||||||
|
import org.jetbrains.kotlin.load.java.lazy.child
|
||||||
|
import org.jetbrains.kotlin.load.java.lazy.descriptors.LazyJavaTypeParameterDescriptor
|
||||||
|
import org.jetbrains.kotlin.load.java.lazy.types.JavaTypeAttributes
|
||||||
|
import org.jetbrains.kotlin.load.java.lazy.types.JavaTypeResolver
|
||||||
|
import org.jetbrains.kotlin.load.java.structure.JavaTypeParameter
|
||||||
|
import org.jetbrains.kotlin.load.java.structure.impl.JavaTypeImpl
|
||||||
|
import org.jetbrains.kotlin.load.java.structure.impl.JavaTypeParameterImpl
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import org.jetbrains.kotlin.name.Name
|
||||||
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
import org.jetbrains.kotlin.resolve.annotations.JVM_FIELD_ANNOTATION_FQ_NAME
|
||||||
|
import org.jetbrains.kotlin.resolve.annotations.JVM_STATIC_ANNOTATION_FQ_NAME
|
||||||
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
|
import org.jetbrains.kotlin.types.Variance
|
||||||
|
import org.jetbrains.kotlin.types.typeUtil.supertypes
|
||||||
|
|
||||||
|
class KotlinElementActionsFactory : JvmElementActionsFactory() {
|
||||||
|
companion object {
|
||||||
|
val javaPsiModifiersMapping = mapOf(
|
||||||
|
JvmModifier.PRIVATE to KtTokens.PRIVATE_KEYWORD,
|
||||||
|
JvmModifier.PUBLIC to KtTokens.PUBLIC_KEYWORD,
|
||||||
|
JvmModifier.PROTECTED to KtTokens.PUBLIC_KEYWORD,
|
||||||
|
JvmModifier.ABSTRACT to KtTokens.ABSTRACT_KEYWORD
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private class FakeExpressionFromParameter(private val psiParam: PsiParameter) : PsiReferenceExpressionImpl() {
|
||||||
|
override fun getText(): String = psiParam.name!!
|
||||||
|
override fun getProject(): Project = psiParam.project
|
||||||
|
override fun getParent(): PsiElement = psiParam.parent
|
||||||
|
override fun getType(): PsiType? = psiParam.type
|
||||||
|
override fun isValid(): Boolean = true
|
||||||
|
override fun getContainingFile(): PsiFile = psiParam.containingFile
|
||||||
|
override fun getReferenceName(): String? = psiParam.name
|
||||||
|
override fun resolve(): PsiElement? = psiParam
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ModifierBuilder(
|
||||||
|
private val targetContainer: KtElement,
|
||||||
|
private val allowJvmStatic: Boolean = true
|
||||||
|
) {
|
||||||
|
private val psiFactory = KtPsiFactory(targetContainer.project)
|
||||||
|
|
||||||
|
val modifierList = psiFactory.createEmptyModifierList()
|
||||||
|
|
||||||
|
private fun JvmModifier.transformAndAppend(): Boolean {
|
||||||
|
javaPsiModifiersMapping[this]?.let {
|
||||||
|
modifierList.appendModifier(it)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
when (this) {
|
||||||
|
JvmModifier.STATIC -> {
|
||||||
|
if (allowJvmStatic && targetContainer is KtClassOrObject) {
|
||||||
|
addAnnotation(JVM_STATIC_ANNOTATION_FQ_NAME)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
JvmModifier.ABSTRACT -> modifierList.appendModifier(KtTokens.ABSTRACT_KEYWORD)
|
||||||
|
JvmModifier.FINAL -> modifierList.appendModifier(KtTokens.FINAL_KEYWORD)
|
||||||
|
else -> return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
var isValid = true
|
||||||
|
private set
|
||||||
|
|
||||||
|
fun addJvmModifier(modifier: JvmModifier) {
|
||||||
|
isValid = isValid && modifier.transformAndAppend()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addJvmModifiers(modifiers: Iterable<JvmModifier>) {
|
||||||
|
modifiers.forEach { addJvmModifier(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addAnnotation(fqName: FqName) {
|
||||||
|
if (!isValid) return
|
||||||
|
modifierList.add(psiFactory.createAnnotationEntry("@${fqName.asString()}"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CreatePropertyFix(
|
||||||
|
private val targetClass: JvmClass,
|
||||||
|
contextElement: KtElement,
|
||||||
|
propertyInfo: PropertyInfo
|
||||||
|
) : CreateCallableFromUsageFix<KtElement>(contextElement, listOf(propertyInfo)) {
|
||||||
|
override fun getFamilyName() = "Add property"
|
||||||
|
override fun getText(): String {
|
||||||
|
val info = callableInfos.first() as PropertyInfo
|
||||||
|
return buildString {
|
||||||
|
append("Add '")
|
||||||
|
if (info.isLateinitPreferred) {
|
||||||
|
append("lateinit ")
|
||||||
|
}
|
||||||
|
append(if (info.writable) "var" else "val")
|
||||||
|
append("' property '${info.name}' to '${targetClass.name}'")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun JvmClass.toKtClassOrFile(): KtElement? {
|
||||||
|
val psi = sourceElement
|
||||||
|
return when (psi) {
|
||||||
|
is KtClassOrObject -> psi
|
||||||
|
is KtLightClassForSourceDeclaration -> psi.kotlinOrigin
|
||||||
|
is KtLightClassForFacade -> psi.files.firstOrNull()
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private inline fun <reified T : KtElement> JvmElement.toKtElement() = sourceElement?.unwrapped as? T
|
||||||
|
|
||||||
|
private fun fakeParametersExpressions(parameters: List<JvmParameter>, project: Project): Array<PsiExpression>? =
|
||||||
|
when {
|
||||||
|
parameters.isEmpty() -> emptyArray()
|
||||||
|
else -> JavaPsiFacade
|
||||||
|
.getElementFactory(project)
|
||||||
|
.createParameterList(
|
||||||
|
parameters.map { it.name }.toTypedArray(),
|
||||||
|
parameters.map { it.type as? PsiType ?: return null }.toTypedArray()
|
||||||
|
)
|
||||||
|
.parameters
|
||||||
|
.map(::FakeExpressionFromParameter)
|
||||||
|
.toTypedArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun PsiType.collectTypeParameters(): List<PsiTypeParameter> {
|
||||||
|
val results = ArrayList<PsiTypeParameter>()
|
||||||
|
accept(
|
||||||
|
object : PsiTypeVisitor<Unit>() {
|
||||||
|
override fun visitArrayType(arrayType: PsiArrayType) {
|
||||||
|
arrayType.componentType.accept(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitClassType(classType: PsiClassType) {
|
||||||
|
(classType.resolve() as? PsiTypeParameter)?.let { results += it }
|
||||||
|
classType.parameters.forEach { it.accept(this) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitWildcardType(wildcardType: PsiWildcardType) {
|
||||||
|
wildcardType.bound?.accept(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun PsiType.resolveToKotlinType(resolutionFacade: ResolutionFacade): KotlinType? {
|
||||||
|
val typeParameters = collectTypeParameters()
|
||||||
|
val components = resolutionFacade.getFrontendService(JavaResolverComponents::class.java)
|
||||||
|
val rootContext = LazyJavaResolverContext(components, TypeParameterResolver.EMPTY) { null }
|
||||||
|
val dummyPackageDescriptor = MutablePackageFragmentDescriptor(resolutionFacade.moduleDescriptor, FqName("dummy"))
|
||||||
|
val dummyClassDescriptor = ClassDescriptorImpl(
|
||||||
|
dummyPackageDescriptor,
|
||||||
|
Name.identifier("Dummy"),
|
||||||
|
Modality.FINAL,
|
||||||
|
ClassKind.CLASS,
|
||||||
|
emptyList(),
|
||||||
|
SourceElement.NO_SOURCE,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
val typeParameterResolver = object : TypeParameterResolver {
|
||||||
|
override fun resolveTypeParameter(javaTypeParameter: JavaTypeParameter): TypeParameterDescriptor? {
|
||||||
|
val psiTypeParameter = (javaTypeParameter as JavaTypeParameterImpl).psi
|
||||||
|
val index = typeParameters.indexOf(psiTypeParameter)
|
||||||
|
if (index < 0) return null
|
||||||
|
return LazyJavaTypeParameterDescriptor(rootContext.child(this), javaTypeParameter, index, dummyClassDescriptor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val typeResolver = JavaTypeResolver(rootContext, typeParameterResolver)
|
||||||
|
val attributes = JavaTypeAttributes(TypeUsage.COMMON)
|
||||||
|
return typeResolver.transformJavaType(JavaTypeImpl.create(this), attributes).approximateFlexibleTypes(preferNotNull = true)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ExpectedTypes.toKotlinTypeInfo(resolutionFacade: ResolutionFacade): TypeInfo {
|
||||||
|
val candidateTypes = flatMapTo(LinkedHashSet<KotlinType>()) {
|
||||||
|
val ktType = (it.theType as? PsiType)?.resolveToKotlinType(resolutionFacade) ?: return@flatMapTo emptyList()
|
||||||
|
when (it.theKind) {
|
||||||
|
ExpectedType.Kind.EXACT, ExpectedType.Kind.SUBTYPE -> listOf(ktType)
|
||||||
|
ExpectedType.Kind.SUPERTYPE -> listOf(ktType) + ktType.supertypes()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (candidateTypes.isEmpty()) {
|
||||||
|
val nullableAnyType = resolutionFacade.moduleDescriptor.builtIns.nullableAnyType
|
||||||
|
return TypeInfo(nullableAnyType, Variance.INVARIANT)
|
||||||
|
}
|
||||||
|
return TypeInfo.ByExplicitCandidateTypes(candidateTypes.toList())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createChangeModifierActions(target: JvmModifiersOwner, request: MemberRequest.Modifier): List<IntentionAction> {
|
||||||
|
val kModifierOwner = target.toKtElement<KtModifierListOwner>() ?: return emptyList()
|
||||||
|
|
||||||
|
val modifier = request.modifier
|
||||||
|
val shouldPresent = request.shouldPresent
|
||||||
|
val (kToken, shouldPresentMapped) = if (JvmModifier.FINAL == modifier)
|
||||||
|
KtTokens.OPEN_KEYWORD to !shouldPresent
|
||||||
|
else
|
||||||
|
javaPsiModifiersMapping[modifier] to shouldPresent
|
||||||
|
if (kToken == null) return emptyList()
|
||||||
|
|
||||||
|
val action = if (shouldPresentMapped)
|
||||||
|
AddModifierFix.createIfApplicable(kModifierOwner, kToken)
|
||||||
|
else
|
||||||
|
RemoveModifierFix(kModifierOwner, kToken, false)
|
||||||
|
return listOfNotNull(action)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createAddConstructorActions(targetClass: JvmClass, request: MemberRequest.Constructor): List<IntentionAction> {
|
||||||
|
val targetKtClass = targetClass.toKtClassOrFile() as? KtClass ?: return emptyList()
|
||||||
|
|
||||||
|
if (request.typeParameters.isNotEmpty()) return emptyList()
|
||||||
|
|
||||||
|
val modifierBuilder = ModifierBuilder(targetKtClass).apply { addJvmModifiers(request.modifiers) }
|
||||||
|
if (!modifierBuilder.isValid) return emptyList()
|
||||||
|
|
||||||
|
val resolutionFacade = targetKtClass.getResolutionFacade()
|
||||||
|
val nullableAnyType = resolutionFacade.moduleDescriptor.builtIns.nullableAnyType
|
||||||
|
val parameterInfos = request.parameters.mapIndexed { index, param ->
|
||||||
|
val ktType = (param.type as? PsiType)?.resolveToKotlinType(resolutionFacade) ?: nullableAnyType
|
||||||
|
val name = param.name ?: "arg${index + 1}"
|
||||||
|
ParameterInfo(TypeInfo(ktType, Variance.IN_VARIANCE), listOf(name))
|
||||||
|
}
|
||||||
|
val needPrimary = !targetKtClass.hasExplicitPrimaryConstructor()
|
||||||
|
val constructorInfo = ConstructorInfo(
|
||||||
|
parameterInfos,
|
||||||
|
targetKtClass,
|
||||||
|
isPrimary = needPrimary,
|
||||||
|
modifierList = modifierBuilder.modifierList,
|
||||||
|
withBody = true
|
||||||
|
)
|
||||||
|
val addConstructorAction = object : CreateCallableFromUsageFix<KtElement>(targetKtClass, listOf(constructorInfo)) {
|
||||||
|
override fun getFamilyName() = "Add method"
|
||||||
|
override fun getText() = "Add ${if (needPrimary) "primary" else "secondary"} constructor to '${targetClass.name}'"
|
||||||
|
}
|
||||||
|
|
||||||
|
val changePrimaryConstructorAction = run {
|
||||||
|
val primaryConstructor = targetKtClass.primaryConstructor ?: return@run null
|
||||||
|
val lightMethod = primaryConstructor.toLightMethods().firstOrNull() ?: return@run null
|
||||||
|
val project = targetKtClass.project
|
||||||
|
val fakeParametersExpressions = fakeParametersExpressions(request.parameters, project) ?: return@run null
|
||||||
|
QuickFixFactory.getInstance()
|
||||||
|
.createChangeMethodSignatureFromUsageFix(
|
||||||
|
lightMethod,
|
||||||
|
fakeParametersExpressions,
|
||||||
|
PsiSubstitutor.EMPTY,
|
||||||
|
targetKtClass,
|
||||||
|
false,
|
||||||
|
2
|
||||||
|
).takeIf { it.isAvailable(project, null, targetKtClass.containingFile) }
|
||||||
|
}
|
||||||
|
|
||||||
|
return listOfNotNull(changePrimaryConstructorAction, addConstructorAction)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createAddPropertyActions(targetClass: JvmClass, request: MemberRequest.Property): List<IntentionAction> {
|
||||||
|
val targetContainer = targetClass.toKtClassOrFile() ?: return emptyList()
|
||||||
|
|
||||||
|
val modifierBuilder = ModifierBuilder(targetContainer).apply { addJvmModifier(request.visibilityModifier) }
|
||||||
|
if (!modifierBuilder.isValid) return emptyList()
|
||||||
|
|
||||||
|
val resolutionFacade = targetContainer.getResolutionFacade()
|
||||||
|
val nullableAnyType = resolutionFacade.moduleDescriptor.builtIns.nullableAnyType
|
||||||
|
val ktType = (request.propertyType as? PsiType)?.resolveToKotlinType(resolutionFacade) ?: nullableAnyType
|
||||||
|
val propertyInfo = PropertyInfo(
|
||||||
|
request.propertyName,
|
||||||
|
TypeInfo.Empty,
|
||||||
|
TypeInfo(ktType, Variance.INVARIANT),
|
||||||
|
request.setterRequired,
|
||||||
|
listOf(targetContainer),
|
||||||
|
modifierList = modifierBuilder.modifierList,
|
||||||
|
withInitializer = true
|
||||||
|
)
|
||||||
|
val propertyInfos = if (request.setterRequired) {
|
||||||
|
listOf(propertyInfo, propertyInfo.copyProperty(isLateinitPreferred = true))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
listOf(propertyInfo)
|
||||||
|
}
|
||||||
|
return propertyInfos.map { CreatePropertyFix(targetClass, targetContainer, it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createAddFieldActions(targetClass: JvmClass, request: CreateFieldRequest): List<IntentionAction> {
|
||||||
|
val targetContainer = targetClass.toKtClassOrFile() ?: return emptyList()
|
||||||
|
|
||||||
|
val modifierBuilder = ModifierBuilder(targetContainer, allowJvmStatic = false).apply {
|
||||||
|
addJvmModifiers(request.modifiers)
|
||||||
|
addAnnotation(JVM_FIELD_ANNOTATION_FQ_NAME)
|
||||||
|
}
|
||||||
|
if (!modifierBuilder.isValid) return emptyList()
|
||||||
|
|
||||||
|
val resolutionFacade = targetContainer.getResolutionFacade()
|
||||||
|
val typeInfo = request.fieldType.toKotlinTypeInfo(resolutionFacade)
|
||||||
|
val writable = JvmModifier.FINAL !in request.modifiers
|
||||||
|
val propertyInfo = PropertyInfo(
|
||||||
|
request.fieldName,
|
||||||
|
TypeInfo.Empty,
|
||||||
|
typeInfo,
|
||||||
|
writable,
|
||||||
|
listOf(targetContainer),
|
||||||
|
isForCompanion = JvmModifier.STATIC in request.modifiers,
|
||||||
|
modifierList = modifierBuilder.modifierList,
|
||||||
|
withInitializer = true
|
||||||
|
)
|
||||||
|
val propertyInfos = if (writable) {
|
||||||
|
listOf(propertyInfo, propertyInfo.copyProperty(isLateinitPreferred = true))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
listOf(propertyInfo)
|
||||||
|
}
|
||||||
|
return propertyInfos.map { CreatePropertyFix(targetClass, targetContainer, it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createAddMethodActions(targetClass: JvmClass, request: CreateMethodRequest): List<IntentionAction> {
|
||||||
|
val targetContainer = targetClass.toKtClassOrFile() ?: return emptyList()
|
||||||
|
|
||||||
|
val modifierBuilder = ModifierBuilder(targetContainer).apply { addJvmModifiers(request.modifiers) }
|
||||||
|
if (!modifierBuilder.isValid) return emptyList()
|
||||||
|
|
||||||
|
val resolutionFacade = targetContainer.getResolutionFacade()
|
||||||
|
val returnTypeInfo = request.returnType.toKotlinTypeInfo(resolutionFacade)
|
||||||
|
val parameterInfos = request.parameters.map { (suggestedNames, expectedTypes) ->
|
||||||
|
ParameterInfo(expectedTypes.toKotlinTypeInfo(resolutionFacade), suggestedNames.names.toList())
|
||||||
|
}
|
||||||
|
val functionInfo = FunctionInfo(
|
||||||
|
request.methodName,
|
||||||
|
TypeInfo.Empty,
|
||||||
|
returnTypeInfo,
|
||||||
|
listOf(targetContainer),
|
||||||
|
parameterInfos,
|
||||||
|
isAbstract = JvmModifier.ABSTRACT in request.modifiers,
|
||||||
|
isForCompanion = JvmModifier.STATIC in request.modifiers,
|
||||||
|
modifierList = modifierBuilder.modifierList,
|
||||||
|
preferEmptyBody = true
|
||||||
|
)
|
||||||
|
val action = object : CreateCallableFromUsageFix<KtElement>(targetContainer, listOf(functionInfo)) {
|
||||||
|
override fun getFamilyName() = "Add method"
|
||||||
|
override fun getText() = "Add method '${request.methodName}' to '${targetClass.name}'"
|
||||||
|
}
|
||||||
|
return listOf(action)
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+6
@@ -0,0 +1,6 @@
|
|||||||
|
class K {
|
||||||
|
fun foo(i: Int, s: String): Boolean {
|
||||||
|
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
// "Add method 'foo' to 'K'" "true"
|
||||||
|
class J {
|
||||||
|
void test(K k) {
|
||||||
|
boolean b = k.<caret>foo(1, "2");
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+3
@@ -0,0 +1,3 @@
|
|||||||
|
class K {
|
||||||
|
|
||||||
|
}
|
||||||
Vendored
+6
@@ -0,0 +1,6 @@
|
|||||||
|
// "Add method 'foo' to 'K'" "true"
|
||||||
|
class J {
|
||||||
|
void test(K k) {
|
||||||
|
boolean b = k.<caret>foo(1, "2");
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+9
@@ -0,0 +1,9 @@
|
|||||||
|
class K {
|
||||||
|
companion object {
|
||||||
|
@JvmStatic
|
||||||
|
fun foo(i: Int, s: String): Boolean {
|
||||||
|
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
// "Add method 'foo' to 'K'" "true"
|
||||||
|
// WITH_RUNTIME
|
||||||
|
class J {
|
||||||
|
void test() {
|
||||||
|
boolean b = K.<caret>foo(1, "2");
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+3
@@ -0,0 +1,3 @@
|
|||||||
|
class K {
|
||||||
|
|
||||||
|
}
|
||||||
Vendored
+7
@@ -0,0 +1,7 @@
|
|||||||
|
// "Add method 'foo' to 'K'" "true"
|
||||||
|
// WITH_RUNTIME
|
||||||
|
class J {
|
||||||
|
void test() {
|
||||||
|
boolean b = K.<caret>foo(1, "2");
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+7
@@ -0,0 +1,7 @@
|
|||||||
|
@file:JvmName("TestKt")
|
||||||
|
|
||||||
|
fun test() {}
|
||||||
|
|
||||||
|
fun foo(i: Int, s: String): Boolean {
|
||||||
|
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
|
||||||
|
}
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
// "Add method 'foo' to 'TestKt'" "true"
|
||||||
|
class J {
|
||||||
|
void test() {
|
||||||
|
boolean b = TestKt.<caret>foo(1, "2");
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+3
@@ -0,0 +1,3 @@
|
|||||||
|
@file:JvmName("TestKt")
|
||||||
|
|
||||||
|
fun test() {}
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
// "Add method 'foo' to 'TestKt'" "true"
|
||||||
|
class J {
|
||||||
|
void test() {
|
||||||
|
boolean b = TestKt.<caret>foo(1, "2");
|
||||||
|
}
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
class K {
|
||||||
|
@JvmField
|
||||||
|
lateinit var foo: String
|
||||||
|
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
// "Add 'lateinit var' property 'foo' to 'K'" "true"
|
||||||
|
// WITH_RUNTIME
|
||||||
|
class J {
|
||||||
|
void test(K k) {
|
||||||
|
String s = k.<caret>foo;
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
class K {
|
||||||
|
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
// "Add 'lateinit var' property 'foo' to 'K'" "true"
|
||||||
|
// WITH_RUNTIME
|
||||||
|
class J {
|
||||||
|
void test(K k) {
|
||||||
|
String s = k.<caret>foo;
|
||||||
|
}
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
class K {
|
||||||
|
@JvmField
|
||||||
|
var foo: String = TODO("initialize me")
|
||||||
|
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
// "Add 'var' property 'foo' to 'K'" "true"
|
||||||
|
// WITH_RUNTIME
|
||||||
|
class J {
|
||||||
|
void test(K k) {
|
||||||
|
String s = k.<caret>foo;
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
class K {
|
||||||
|
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
// "Add 'var' property 'foo' to 'K'" "true"
|
||||||
|
// WITH_RUNTIME
|
||||||
|
class J {
|
||||||
|
void test(K k) {
|
||||||
|
String s = k.<caret>foo;
|
||||||
|
}
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
class K {
|
||||||
|
companion object {
|
||||||
|
@JvmField
|
||||||
|
var foo: String = TODO("initialize me")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
// "Add 'var' property 'foo' to 'K'" "true"
|
||||||
|
// WITH_RUNTIME
|
||||||
|
class J {
|
||||||
|
void test() {
|
||||||
|
String s = K.<caret>foo;
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
class K {
|
||||||
|
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
// "Add 'var' property 'foo' to 'K'" "true"
|
||||||
|
// WITH_RUNTIME
|
||||||
|
class J {
|
||||||
|
void test() {
|
||||||
|
String s = K.<caret>foo;
|
||||||
|
}
|
||||||
|
}
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
@file:JvmName("TestKt")
|
||||||
|
|
||||||
|
fun test() {}
|
||||||
|
|
||||||
|
@JvmField
|
||||||
|
var foo: String = TODO("initialize me")
|
||||||
Vendored
+7
@@ -0,0 +1,7 @@
|
|||||||
|
// "Add 'var' property 'foo' to 'TestKt'" "true"
|
||||||
|
// WITH_RUNTIME
|
||||||
|
class J {
|
||||||
|
void test() {
|
||||||
|
String s = TestKt.<caret>foo;
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
@file:JvmName("TestKt")
|
||||||
|
|
||||||
|
fun test() {}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
// "Add 'var' property 'foo' to 'TestKt'" "true"
|
||||||
|
// WITH_RUNTIME
|
||||||
|
class J {
|
||||||
|
void test() {
|
||||||
|
String s = TestKt.<caret>foo;
|
||||||
|
}
|
||||||
|
}
|
||||||
+60
@@ -1339,6 +1339,33 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("idea/testData/quickfix/createFromUsage/createFunction/fromJava")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class FromJava extends AbstractQuickFixMultiFileTest {
|
||||||
|
public void testAllFilesPresentInFromJava() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/createFromUsage/createFunction/fromJava"), Pattern.compile("^(\\w+)\\.((before\\.Main\\.\\w+)|(test))$"), TargetBackend.ANY, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("classMember.before.Main.java")
|
||||||
|
public void testClassMember() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/fromJava/classMember.before.Main.java");
|
||||||
|
doTestWithExtraFile(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("companionMember.before.Main.java")
|
||||||
|
public void testCompanionMember() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/fromJava/companionMember.before.Main.java");
|
||||||
|
doTestWithExtraFile(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("topLevel.before.Main.java")
|
||||||
|
public void testTopLevel() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createFunction/fromJava/topLevel.before.Main.java");
|
||||||
|
doTestWithExtraFile(fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("idea/testData/quickfix/createFromUsage/createSecondaryConstructor")
|
@TestMetadata("idea/testData/quickfix/createFromUsage/createSecondaryConstructor")
|
||||||
@@ -1509,6 +1536,39 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes
|
|||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createVariable/property/varOnJavaType.before.Main.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createVariable/property/varOnJavaType.before.Main.kt");
|
||||||
doTestWithExtraFile(fileName);
|
doTestWithExtraFile(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("idea/testData/quickfix/createFromUsage/createVariable/property/fieldFromJava")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class FieldFromJava extends AbstractQuickFixMultiFileTest {
|
||||||
|
public void testAllFilesPresentInFieldFromJava() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/createFromUsage/createVariable/property/fieldFromJava"), Pattern.compile("^(\\w+)\\.((before\\.Main\\.\\w+)|(test))$"), TargetBackend.ANY, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("classMemberLateinitVar.before.Main.java")
|
||||||
|
public void testClassMemberLateinitVar() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createVariable/property/fieldFromJava/classMemberLateinitVar.before.Main.java");
|
||||||
|
doTestWithExtraFile(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("classMemberVar.before.Main.java")
|
||||||
|
public void testClassMemberVar() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createVariable/property/fieldFromJava/classMemberVar.before.Main.java");
|
||||||
|
doTestWithExtraFile(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("companionMemberVar.before.Main.java")
|
||||||
|
public void testCompanionMemberVar() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createVariable/property/fieldFromJava/companionMemberVar.before.Main.java");
|
||||||
|
doTestWithExtraFile(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("topLevelVar.before.Main.java")
|
||||||
|
public void testTopLevelVar() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/createFromUsage/createVariable/property/fieldFromJava/topLevelVar.before.Main.java");
|
||||||
|
doTestWithExtraFile(fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3467,6 +3467,15 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("idea/testData/quickfix/createFromUsage/createFunction/fromJava")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class FromJava extends AbstractQuickFixTest {
|
||||||
|
public void testAllFilesPresentInFromJava() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/createFromUsage/createFunction/fromJava"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("idea/testData/quickfix/createFromUsage/createFunction/delegateAccessors")
|
@TestMetadata("idea/testData/quickfix/createFromUsage/createFunction/delegateAccessors")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
@@ -72,6 +72,8 @@ open class KotlinUClass private constructor(
|
|||||||
|
|
||||||
override val psi = unwrap<UClass, PsiClass>(psi)
|
override val psi = unwrap<UClass, PsiClass>(psi)
|
||||||
|
|
||||||
|
override fun getSourceElement() = sourcePsi ?: this
|
||||||
|
|
||||||
override fun getOriginalElement(): PsiElement? = super.getOriginalElement()
|
override fun getOriginalElement(): PsiElement? = super.getOriginalElement()
|
||||||
|
|
||||||
override fun getNameIdentifier(): PsiIdentifier? = UastLightIdentifier(psi, ktClass)
|
override fun getNameIdentifier(): PsiIdentifier? = UastLightIdentifier(psi, ktClass)
|
||||||
|
|||||||
@@ -42,6 +42,8 @@ open class KotlinUMethod(
|
|||||||
|
|
||||||
override val sourcePsi = psi.kotlinOrigin
|
override val sourcePsi = psi.kotlinOrigin
|
||||||
|
|
||||||
|
override fun getSourceElement() = sourcePsi ?: this
|
||||||
|
|
||||||
override val uastDefaultValue by lz {
|
override val uastDefaultValue by lz {
|
||||||
val annotationParameter = psi.kotlinOrigin as? KtParameter ?: return@lz null
|
val annotationParameter = psi.kotlinOrigin as? KtParameter ?: return@lz null
|
||||||
val defaultValue = annotationParameter.defaultValue ?: return@lz null
|
val defaultValue = annotationParameter.defaultValue ?: return@lz null
|
||||||
|
|||||||
@@ -269,6 +269,7 @@ open class KotlinUField(
|
|||||||
override val sourcePsi: KtElement?,
|
override val sourcePsi: KtElement?,
|
||||||
givenParent: UElement?
|
givenParent: UElement?
|
||||||
) : AbstractKotlinUVariable(givenParent), UField, PsiField by psi {
|
) : AbstractKotlinUVariable(givenParent), UField, PsiField by psi {
|
||||||
|
override fun getSourceElement() = sourcePsi ?: this
|
||||||
|
|
||||||
override val javaPsi = unwrap<UField, PsiField>(psi)
|
override val javaPsi = unwrap<UField, PsiField>(psi)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user