Split module idea into idea, idea-core and idea-completion
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="kotlin-runtime" level="project" />
|
||||
<orderEntry type="library" name="idea-full" level="project" />
|
||||
<orderEntry type="module" module-name="idea-analysis" />
|
||||
<orderEntry type="module" module-name="descriptors" />
|
||||
<orderEntry type="module" module-name="frontend" />
|
||||
<orderEntry type="module" module-name="util" />
|
||||
<orderEntry type="module" module-name="light-classes" />
|
||||
<orderEntry type="module" module-name="frontend.java" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.core
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.di.InjectorForMacros
|
||||
import org.jetbrains.kotlin.idea.util.FuzzyType
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.JetPsiFactory
|
||||
import org.jetbrains.kotlin.resolve.BindingTraceContext
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
|
||||
import org.jetbrains.kotlin.resolve.scopes.JetScope
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
|
||||
import org.jetbrains.kotlin.types.JetType
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.types.expressions.ExpressionTypingContext
|
||||
import java.util.HashMap
|
||||
|
||||
public class IterableTypesDetector(
|
||||
private val project: Project,
|
||||
private val moduleDescriptor: ModuleDescriptor,
|
||||
private val scope: JetScope,
|
||||
private val loopVarType: JetType? = null
|
||||
) {
|
||||
|
||||
private val injector = InjectorForMacros(project, moduleDescriptor)
|
||||
private val cache = HashMap<FuzzyType, Boolean>()
|
||||
private val iteratorName = Name.identifier("iterator")
|
||||
|
||||
private val typesWithExtensionIterator: Collection<JetType> = scope.getFunctions(iteratorName)
|
||||
.map { it.getExtensionReceiverParameter() }
|
||||
.filterNotNull()
|
||||
.map { it.getType() }
|
||||
|
||||
public fun isIterable(type: FuzzyType): Boolean {
|
||||
return cache.getOrPut(type, { isIterableNoCache(type) })
|
||||
}
|
||||
|
||||
private fun isIterableNoCache(type: FuzzyType): Boolean {
|
||||
// optimization
|
||||
if (!canBeIterable(type)) return false
|
||||
|
||||
val expression = JetPsiFactory(project).createExpression("fake")
|
||||
val expressionReceiver = ExpressionReceiver(expression, type.type)
|
||||
val context = ExpressionTypingContext.newContext(injector.getExpressionTypingServices(), BindingTraceContext(), scope, DataFlowInfo.EMPTY, TypeUtils.NO_EXPECTED_TYPE)
|
||||
val elementType = injector.getExpressionTypingComponents().getForLoopConventionsChecker().checkIterableConvention(expressionReceiver, context)
|
||||
if (elementType == null) return false
|
||||
return loopVarType == null || FuzzyType(elementType, type.freeParameters).checkIsSubtypeOf(loopVarType) != null
|
||||
}
|
||||
|
||||
private fun canBeIterable(type: FuzzyType): Boolean {
|
||||
return type.type.getMemberScope().getFunctions(iteratorName).isNotEmpty() || typesWithExtensionIterator.any { type.checkIsSubtypeOf(it) != null }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.core
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.psi.stubs.StringStubIndexExtension
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.ResolutionFacade
|
||||
import org.jetbrains.kotlin.idea.codeInsight.ReferenceVariantsHelper
|
||||
import org.jetbrains.kotlin.idea.stubindex.*
|
||||
import org.jetbrains.kotlin.idea.util.CallType
|
||||
import org.jetbrains.kotlin.idea.util.getImplicitReceiversWithInstance
|
||||
import org.jetbrains.kotlin.idea.util.substituteExtensionIfCallable
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.*
|
||||
import org.jetbrains.kotlin.resolve.QualifiedExpressionResolver.LookupMode
|
||||
import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfo
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.SmartCastUtils
|
||||
import org.jetbrains.kotlin.resolve.lazy.ResolveSessionUtils
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
|
||||
import org.jetbrains.kotlin.types.JetType
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.singletonOrEmptyList
|
||||
import java.util.HashSet
|
||||
import java.util.LinkedHashSet
|
||||
|
||||
public class KotlinIndicesHelper(
|
||||
private val project: Project,
|
||||
private val resolutionFacade: ResolutionFacade,
|
||||
private val bindingContext: BindingContext,
|
||||
private val scope: GlobalSearchScope,
|
||||
private val moduleDescriptor: ModuleDescriptor,
|
||||
private val visibilityFilter: (DeclarationDescriptor) -> Boolean
|
||||
) {
|
||||
public fun getTopLevelCallablesByName(name: String): Collection<CallableDescriptor> {
|
||||
val declarations = HashSet<JetNamedDeclaration>()
|
||||
declarations.addTopLevelNonExtensionCallablesByName(JetFunctionShortNameIndex.getInstance(), name)
|
||||
declarations.addTopLevelNonExtensionCallablesByName(JetPropertyShortNameIndex.getInstance(), name)
|
||||
return declarations.flatMap {
|
||||
if (it.getContainingJetFile().isCompiled()) { //TODO: it's temporary while resolveToDescriptor does not work for compiled declarations
|
||||
analyzeImportReference(it.getFqName()!!).filterIsInstance<CallableDescriptor>()
|
||||
}
|
||||
else {
|
||||
(resolutionFacade.resolveToDescriptor(it) as? CallableDescriptor).singletonOrEmptyList()
|
||||
}
|
||||
}.filter { it.getExtensionReceiverParameter() == null && visibilityFilter(it) }
|
||||
}
|
||||
|
||||
private fun MutableSet<JetNamedDeclaration>.addTopLevelNonExtensionCallablesByName(
|
||||
index: StringStubIndexExtension<out JetCallableDeclaration>,
|
||||
name: String
|
||||
) {
|
||||
index.get(name, project, scope).filterTo(this) { it.getParent() is JetFile && it.getReceiverTypeReference() == null }
|
||||
}
|
||||
|
||||
public fun getTopLevelCallables(nameFilter: (String) -> Boolean): Collection<CallableDescriptor> {
|
||||
return (JetTopLevelFunctionFqnNameIndex.getInstance().getAllKeys(project).sequence() +
|
||||
JetTopLevelPropertyFqnNameIndex.getInstance().getAllKeys(project).sequence())
|
||||
.map { FqName(it) }
|
||||
.filter { nameFilter(it.shortName().asString()) }
|
||||
.toSet()
|
||||
.flatMap { findTopLevelCallables(it).filter(visibilityFilter) }
|
||||
}
|
||||
|
||||
public fun getCallableTopLevelExtensions(nameFilter: (String) -> Boolean, expression: JetSimpleNameExpression): Collection<CallableDescriptor> {
|
||||
val receiverValues = receiverValues(expression)
|
||||
if (receiverValues.isEmpty()) return emptyList()
|
||||
|
||||
val dataFlowInfo = bindingContext.getDataFlowInfo(expression)
|
||||
val containingDeclaration = bindingContext[BindingContext.RESOLUTION_SCOPE, expression]?.getContainingDeclaration() ?: return emptyList()
|
||||
|
||||
val receiverTypeNames = possibleReceiverTypeNames(receiverValues.map { it.first }, containingDeclaration, dataFlowInfo)
|
||||
|
||||
val index = JetTopLevelExtensionsByReceiverTypeIndex.INSTANCE
|
||||
|
||||
val declarations = index.getAllKeys(project)
|
||||
.sequence()
|
||||
.filter {
|
||||
JetTopLevelExtensionsByReceiverTypeIndex.receiverTypeNameFromKey(it) in receiverTypeNames
|
||||
&& nameFilter(JetTopLevelExtensionsByReceiverTypeIndex.callableNameFromKey(it))
|
||||
}
|
||||
.flatMap { index.get(it, project, scope).sequence() }
|
||||
|
||||
return findSuitableExtensions(declarations, receiverValues, dataFlowInfo, bindingContext)
|
||||
}
|
||||
|
||||
private fun possibleReceiverTypeNames(receiverValues: Collection<ReceiverValue>, containingDeclaration: DeclarationDescriptor, dataFlowInfo: DataFlowInfo): Set<String> {
|
||||
val result = HashSet<String>()
|
||||
for (receiverValue in receiverValues) {
|
||||
for (type in SmartCastUtils.getSmartCastVariants(receiverValue, bindingContext, containingDeclaration, dataFlowInfo)) {
|
||||
result.addTypeNames(type)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun MutableCollection<String>.addTypeNames(type: JetType) {
|
||||
val constructor = type.getConstructor()
|
||||
addIfNotNull(constructor.getDeclarationDescriptor()?.getName()?.asString())
|
||||
constructor.getSupertypes().forEach { addTypeNames(it) }
|
||||
}
|
||||
|
||||
private fun receiverValues(expression: JetSimpleNameExpression): Collection<Pair<ReceiverValue, CallType>> {
|
||||
val receiverPair = ReferenceVariantsHelper.getExplicitReceiverData(expression)
|
||||
if (receiverPair != null) {
|
||||
val (receiverExpression, callType) = receiverPair
|
||||
|
||||
val expressionType = bindingContext[BindingContext.EXPRESSION_TYPE, receiverExpression]
|
||||
if (expressionType == null || expressionType.isError()) return emptyList()
|
||||
|
||||
val receiverValue = ExpressionReceiver(receiverExpression, expressionType)
|
||||
|
||||
return listOf(receiverValue to callType)
|
||||
}
|
||||
else {
|
||||
val resolutionScope = bindingContext[BindingContext.RESOLUTION_SCOPE, expression] ?: return emptyList()
|
||||
return resolutionScope.getImplicitReceiversWithInstance().map { it.getValue() to CallType.NORMAL }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that function or property with the given qualified name can be resolved in given scope and called on given receiver
|
||||
*/
|
||||
private fun findSuitableExtensions(
|
||||
declarations: Sequence<JetCallableDeclaration>,
|
||||
receiverValues: Collection<Pair<ReceiverValue, CallType>>,
|
||||
dataFlowInfo: DataFlowInfo,
|
||||
bindingContext: BindingContext
|
||||
): Collection<CallableDescriptor> {
|
||||
val result = LinkedHashSet<CallableDescriptor>()
|
||||
|
||||
fun processDescriptor(descriptor: CallableDescriptor) {
|
||||
if (visibilityFilter(descriptor)) {
|
||||
for ((receiverValue, callType) in receiverValues) {
|
||||
result.addAll(descriptor.substituteExtensionIfCallable(receiverValue, callType, bindingContext, dataFlowInfo))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (declaration in declarations) {
|
||||
if (declaration.getContainingJetFile().isCompiled()) {
|
||||
//TODO: it's temporary while resolveToDescriptor does not work for compiled declarations
|
||||
for (descriptor in analyzeImportReference(declaration.getFqName()!!)) {
|
||||
if (descriptor is CallableDescriptor && descriptor.getExtensionReceiverParameter() != null) {
|
||||
processDescriptor(descriptor)
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
processDescriptor(resolutionFacade.resolveToDescriptor(declaration) as CallableDescriptor)
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
public fun getClassDescriptors(nameFilter: (String) -> Boolean, kindFilter: (ClassKind) -> Boolean): Collection<ClassDescriptor> {
|
||||
return JetFullClassNameIndex.getInstance().getAllKeys(project).sequence()
|
||||
.map { FqName(it) }
|
||||
.filter { nameFilter(it.shortName().asString()) }
|
||||
.toList()
|
||||
.flatMap { getClassDescriptorsByFQName(it, kindFilter) }
|
||||
}
|
||||
|
||||
private fun getClassDescriptorsByFQName(classFQName: FqName, kindFilter: (ClassKind) -> Boolean): Collection<ClassDescriptor> {
|
||||
val declarations = JetFullClassNameIndex.getInstance()[classFQName.asString(), project, scope]
|
||||
|
||||
if (declarations.isEmpty()) {
|
||||
// This fqn is absent in caches, dead or not in scope
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
// Note: Can't search with psi element as analyzer could be built over temp files
|
||||
return ResolveSessionUtils.getClassOrObjectDescriptorsByFqName(moduleDescriptor, classFQName) { kindFilter(it.getKind()) }
|
||||
.filter(visibilityFilter)
|
||||
}
|
||||
|
||||
private fun findTopLevelCallables(fqName: FqName): Collection<CallableDescriptor> {
|
||||
return analyzeImportReference(fqName)
|
||||
.filterIsInstance<CallableDescriptor>()
|
||||
.filter { it.getExtensionReceiverParameter() == null }
|
||||
}
|
||||
|
||||
private fun analyzeImportReference(fqName: FqName): Collection<DeclarationDescriptor> {
|
||||
val importDirective = JetPsiFactory(project).createImportDirective(ImportPath(fqName, false))
|
||||
val scope = JetModuleUtil.getSubpackagesOfRootScope(moduleDescriptor)
|
||||
return QualifiedExpressionResolver().processImportReference(importDirective, scope, scope, BindingTraceContext(), LookupMode.EVERYTHING).getAllDescriptors()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.core
|
||||
|
||||
import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfo
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.psi.JetExpression
|
||||
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
||||
import org.jetbrains.kotlin.types.JetType
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValue
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ThisReceiver
|
||||
import com.google.common.collect.SetMultimap
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.Nullability
|
||||
import java.util.Collections
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory
|
||||
import java.util.HashMap
|
||||
import java.util.HashSet
|
||||
import com.intellij.openapi.util.Pair
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.idea.util.makeNotNullable
|
||||
import org.jetbrains.kotlin.resolve.calls.context.ResolutionContext
|
||||
import org.jetbrains.kotlin.utils.singletonOrEmptyList
|
||||
|
||||
class SmartCastCalculator(val bindingContext: BindingContext, val containingDeclaration: DeclarationDescriptor) {
|
||||
public fun calculate(position: JetExpression, receiver: JetExpression?): (VariableDescriptor) -> Collection<JetType> {
|
||||
val dataFlowInfo = bindingContext.getDataFlowInfo(position)
|
||||
val (variableToTypes, notNullVariables) = processDataFlowInfo(dataFlowInfo, receiver)
|
||||
|
||||
fun typesOf(descriptor: VariableDescriptor): Collection<JetType> {
|
||||
var type = descriptor.getReturnType() ?: return listOf()
|
||||
if (notNullVariables.contains(descriptor)) {
|
||||
type = type.makeNotNullable()
|
||||
}
|
||||
|
||||
val smartCastTypes = variableToTypes[descriptor]
|
||||
if (smartCastTypes == null || smartCastTypes.isEmpty()) return type.singletonOrEmptyList()
|
||||
return smartCastTypes + type.singletonOrEmptyList()
|
||||
}
|
||||
|
||||
return ::typesOf
|
||||
}
|
||||
|
||||
private data class ProcessDataFlowInfoResult(
|
||||
val variableToTypes: Map<VariableDescriptor, Collection<JetType>> = Collections.emptyMap(),
|
||||
val notNullVariables: Set<VariableDescriptor> = Collections.emptySet()
|
||||
)
|
||||
|
||||
private fun processDataFlowInfo(dataFlowInfo: DataFlowInfo, receiver: JetExpression?): ProcessDataFlowInfoResult {
|
||||
if (dataFlowInfo == DataFlowInfo.EMPTY) return ProcessDataFlowInfoResult()
|
||||
|
||||
val dataFlowValueToVariable: (DataFlowValue) -> VariableDescriptor?
|
||||
if (receiver != null) {
|
||||
val receiverType = bindingContext[BindingContext.EXPRESSION_TYPE, receiver] ?: return ProcessDataFlowInfoResult()
|
||||
val receiverId = DataFlowValueFactory.createDataFlowValue(receiver, receiverType, bindingContext, containingDeclaration).getId()
|
||||
dataFlowValueToVariable = { value ->
|
||||
val id = value.getId()
|
||||
if (id is Pair<*, *> && id.first == receiverId) id.second as? VariableDescriptor else null
|
||||
}
|
||||
}
|
||||
else {
|
||||
dataFlowValueToVariable = { value ->
|
||||
val id = value.getId()
|
||||
when {
|
||||
id is VariableDescriptor -> id
|
||||
id is Pair<*, *> && id.first is ThisReceiver -> id.second as? VariableDescriptor
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val variableToType = HashMap<VariableDescriptor, Collection<JetType>>()
|
||||
val typeInfo: SetMultimap<DataFlowValue, JetType> = dataFlowInfo.getCompleteTypeInfo()
|
||||
for ((dataFlowValue, types) in typeInfo.asMap().entrySet()) {
|
||||
val variable = dataFlowValueToVariable.invoke(dataFlowValue)
|
||||
if (variable != null) {
|
||||
variableToType[variable] = types
|
||||
}
|
||||
}
|
||||
|
||||
val nullabilityInfo: Map<DataFlowValue, Nullability> = dataFlowInfo.getCompleteNullabilityInfo()
|
||||
val notNullVariables = nullabilityInfo
|
||||
.filter { it.getValue() == Nullability.NOT_NULL }
|
||||
.map { dataFlowValueToVariable(it.getKey()) }
|
||||
.filterNotNullTo(HashSet<VariableDescriptor>())
|
||||
|
||||
return ProcessDataFlowInfoResult(variableToType, notNullVariables)
|
||||
}
|
||||
}
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.core.codeInsight;
|
||||
|
||||
import com.intellij.codeInsight.generation.ClassMemberWithElement;
|
||||
import com.intellij.codeInsight.generation.MemberChooserObject;
|
||||
import com.intellij.codeInsight.generation.MemberChooserObjectBase;
|
||||
import com.intellij.openapi.util.Iconable;
|
||||
import com.intellij.psi.PsiClass;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiMember;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
|
||||
import org.jetbrains.kotlin.idea.JetDescriptorIconProvider;
|
||||
import org.jetbrains.kotlin.psi.JetClass;
|
||||
import org.jetbrains.kotlin.psi.JetDeclaration;
|
||||
import org.jetbrains.kotlin.psi.JetNamedDeclaration;
|
||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
public class DescriptorClassMember extends MemberChooserObjectBase implements ClassMemberWithElement {
|
||||
|
||||
public static final String NO_PARENT_FOR = "No parent for ";
|
||||
@NotNull
|
||||
private final DeclarationDescriptor myDescriptor;
|
||||
@NotNull
|
||||
private final PsiElement myPsiElement;
|
||||
|
||||
public DescriptorClassMember(@NotNull PsiElement element, @NotNull DeclarationDescriptor descriptor) {
|
||||
super(DescriptorRenderer.STARTS_FROM_NAME.render(descriptor), getIcon(element, descriptor));
|
||||
myPsiElement = element;
|
||||
myDescriptor = descriptor;
|
||||
}
|
||||
|
||||
private static Icon getIcon(PsiElement element, DeclarationDescriptor declarationDescriptor) {
|
||||
if (element.isValid()) {
|
||||
boolean isClass = element instanceof PsiClass || element instanceof JetClass;
|
||||
int flags = isClass ? 0 : Iconable.ICON_FLAG_VISIBILITY;
|
||||
if (element instanceof JetDeclaration) { // kotlin declaration
|
||||
// visibility and abstraction better detect by a descriptor
|
||||
return JetDescriptorIconProvider.getIcon(declarationDescriptor, element, flags);
|
||||
}
|
||||
else {
|
||||
// it is better to show java icons for java code
|
||||
return element.getIcon(flags);
|
||||
}
|
||||
}
|
||||
|
||||
return JetDescriptorIconProvider.getIcon(declarationDescriptor, element, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemberChooserObject getParentNodeDelegate() {
|
||||
DeclarationDescriptor parent = myDescriptor.getContainingDeclaration();
|
||||
PsiElement declaration;
|
||||
if (myPsiElement instanceof JetDeclaration) {
|
||||
// kotlin
|
||||
declaration = PsiTreeUtil.getStubOrPsiParentOfType(myPsiElement, JetNamedDeclaration.class);
|
||||
}
|
||||
else {
|
||||
// java or bytecode
|
||||
declaration = ((PsiMember) myPsiElement).getContainingClass();
|
||||
}
|
||||
assert parent != null : NO_PARENT_FOR + myDescriptor;
|
||||
assert declaration != null : NO_PARENT_FOR + myPsiElement;
|
||||
return new DescriptorClassMember(declaration, parent);
|
||||
}
|
||||
|
||||
public DeclarationDescriptor getDescriptor() {
|
||||
return myDescriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
DescriptorClassMember that = (DescriptorClassMember) o;
|
||||
|
||||
if (!myDescriptor.equals(that.myDescriptor)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return myDescriptor.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PsiElement getElement() {
|
||||
return myPsiElement;
|
||||
}
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.core.codeInsight;
|
||||
|
||||
import com.intellij.codeInsight.intention.IntentionAction;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor;
|
||||
import org.jetbrains.kotlin.idea.JetBundle;
|
||||
import org.jetbrains.kotlin.resolve.OverrideResolver;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public class ImplementMethodsHandler extends OverrideImplementMethodsHandler implements IntentionAction {
|
||||
@Override
|
||||
protected Set<CallableMemberDescriptor> collectMethodsToGenerate(@NotNull ClassDescriptor descriptor) {
|
||||
return OverrideResolver.getMissingImplementations(descriptor);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getChooserTitle() {
|
||||
return "Implement Members";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getNoMethodsFoundHint() {
|
||||
return "No methods to implement have been found";
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getText() {
|
||||
return JetBundle.message("implement.members");
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getFamilyName() {
|
||||
return JetBundle.message("implement.members");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
|
||||
return isValidFor(editor, file);
|
||||
}
|
||||
}
|
||||
+352
@@ -0,0 +1,352 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.core.codeInsight;
|
||||
|
||||
import com.intellij.codeInsight.hint.HintManager;
|
||||
import com.intellij.ide.util.MemberChooser;
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.lang.LanguageCodeInsightActionHandler;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.DialogWrapper;
|
||||
import com.intellij.openapi.util.Condition;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
|
||||
import org.jetbrains.kotlin.descriptors.*;
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.ResolvePackage;
|
||||
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde;
|
||||
import org.jetbrains.kotlin.idea.quickfix.QuickfixPackage;
|
||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers;
|
||||
import org.jetbrains.kotlin.idea.util.ShortenReferences;
|
||||
import org.jetbrains.kotlin.psi.*;
|
||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer;
|
||||
import org.jetbrains.kotlin.renderer.DescriptorRendererBuilder;
|
||||
import org.jetbrains.kotlin.renderer.NameShortness;
|
||||
import org.jetbrains.kotlin.types.JetType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.jetbrains.kotlin.psi.PsiPackage.JetPsiFactory;
|
||||
|
||||
public abstract class OverrideImplementMethodsHandler implements LanguageCodeInsightActionHandler {
|
||||
|
||||
private static final DescriptorRenderer OVERRIDE_RENDERER = new DescriptorRendererBuilder()
|
||||
.setRenderDefaultValues(false)
|
||||
.setModifiers(DescriptorRenderer.Modifier.OVERRIDE)
|
||||
.setWithDefinedIn(false)
|
||||
.setNameShortness(NameShortness.SOURCE_CODE_QUALIFIED)
|
||||
.setOverrideRenderingPolicy(DescriptorRenderer.OverrideRenderingPolicy.RENDER_OVERRIDE)
|
||||
.setUnitReturnType(false)
|
||||
.setTypeNormalizer(IdeDescriptorRenderers.APPROXIMATE_FLEXIBLE_TYPES)
|
||||
.build();
|
||||
|
||||
private static final Logger LOG = Logger.getInstance(OverrideImplementMethodsHandler.class.getCanonicalName());
|
||||
|
||||
public static List<DescriptorClassMember> membersFromDescriptors(
|
||||
JetFile file, Iterable<CallableMemberDescriptor> missingImplementations
|
||||
) {
|
||||
List<DescriptorClassMember> members = new ArrayList<DescriptorClassMember>();
|
||||
for (CallableMemberDescriptor memberDescriptor : missingImplementations) {
|
||||
PsiElement declaration = DescriptorToSourceUtilsIde.INSTANCE$.getAnyDeclaration(file.getProject(), memberDescriptor);
|
||||
if (declaration == null) {
|
||||
LOG.error("Can not find declaration for descriptor " + memberDescriptor);
|
||||
}
|
||||
else {
|
||||
DescriptorClassMember member = new DescriptorClassMember(declaration, memberDescriptor);
|
||||
members.add(member);
|
||||
}
|
||||
}
|
||||
return members;
|
||||
}
|
||||
|
||||
public static void generateMethods(
|
||||
@NotNull final Editor editor,
|
||||
@NotNull final JetClassOrObject classOrObject,
|
||||
@NotNull final List<DescriptorClassMember> selectedElements
|
||||
) {
|
||||
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
JetClassBody body = classOrObject.getBody();
|
||||
if (body == null) {
|
||||
JetPsiFactory psiFactory = JetPsiFactory(classOrObject);
|
||||
classOrObject.add(psiFactory.createWhiteSpace());
|
||||
body = (JetClassBody) classOrObject.add(psiFactory.createEmptyClassBody());
|
||||
}
|
||||
|
||||
PsiElement afterAnchor = findInsertAfterAnchor(editor, body);
|
||||
|
||||
if (afterAnchor == null) return;
|
||||
|
||||
PsiElement firstGenerated = null;
|
||||
|
||||
List<JetElement> elementsToCompact = new ArrayList<JetElement>();
|
||||
for (JetElement element : generateOverridingMembers(selectedElements, classOrObject)) {
|
||||
PsiElement added = body.addAfter(element, afterAnchor);
|
||||
|
||||
if (firstGenerated == null) {
|
||||
firstGenerated = added;
|
||||
}
|
||||
|
||||
afterAnchor = added;
|
||||
elementsToCompact.add((JetElement) added);
|
||||
}
|
||||
|
||||
ShortenReferences.DEFAULT.process(elementsToCompact);
|
||||
|
||||
if (firstGenerated == null) return;
|
||||
|
||||
Project project = classOrObject.getProject();
|
||||
SmartPsiElementPointer<PsiElement> pointer = SmartPointerManager.getInstance(project).createSmartPsiElementPointer(firstGenerated);
|
||||
|
||||
PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(editor.getDocument());
|
||||
|
||||
PsiElement element = pointer.getElement();
|
||||
if (element != null) {
|
||||
QuickfixPackage.moveCaretIntoGeneratedElement(editor, element);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiElement findInsertAfterAnchor(Editor editor, final JetClassBody body) {
|
||||
PsiElement afterAnchor = body.getLBrace();
|
||||
if (afterAnchor == null) return null;
|
||||
|
||||
int offset = editor.getCaretModel().getOffset();
|
||||
PsiElement offsetCursorElement = PsiTreeUtil.findFirstParent(
|
||||
body.getContainingFile().findElementAt(offset),
|
||||
new Condition<PsiElement>() {
|
||||
@Override
|
||||
public boolean value(PsiElement element) {
|
||||
return element.getParent() == body;
|
||||
}
|
||||
});
|
||||
|
||||
if (offsetCursorElement instanceof PsiWhiteSpace) {
|
||||
return removeAfterOffset(offset, (PsiWhiteSpace) offsetCursorElement);
|
||||
}
|
||||
|
||||
if (offsetCursorElement != null && offsetCursorElement != body.getRBrace()) {
|
||||
return offsetCursorElement;
|
||||
}
|
||||
|
||||
return afterAnchor;
|
||||
}
|
||||
|
||||
private static PsiElement removeAfterOffset(int offset, PsiWhiteSpace whiteSpace) {
|
||||
ASTNode spaceNode = whiteSpace.getNode();
|
||||
if (spaceNode.getTextRange().contains(offset)) {
|
||||
String beforeWhiteSpaceText = spaceNode.getText().substring(0, offset - spaceNode.getStartOffset());
|
||||
if (!StringUtil.containsLineBreak(beforeWhiteSpaceText)) {
|
||||
// Prevent insertion on same line
|
||||
beforeWhiteSpaceText += "\n";
|
||||
}
|
||||
|
||||
JetPsiFactory factory = JetPsiFactory(whiteSpace.getProject());
|
||||
|
||||
PsiElement insertAfter = whiteSpace.getPrevSibling();
|
||||
whiteSpace.delete();
|
||||
|
||||
PsiElement beforeSpace = factory.createWhiteSpace(beforeWhiteSpaceText);
|
||||
insertAfter.getParent().addAfter(beforeSpace, insertAfter);
|
||||
|
||||
return insertAfter.getNextSibling();
|
||||
}
|
||||
|
||||
return whiteSpace;
|
||||
}
|
||||
|
||||
private static List<JetElement> generateOverridingMembers(List<DescriptorClassMember> selectedElements, JetClassOrObject classOrObject) {
|
||||
List<JetElement> overridingMembers = new ArrayList<JetElement>();
|
||||
for (DescriptorClassMember selectedElement : selectedElements) {
|
||||
DeclarationDescriptor descriptor = selectedElement.getDescriptor();
|
||||
if (descriptor instanceof SimpleFunctionDescriptor) {
|
||||
overridingMembers.add(overrideFunction(classOrObject, (SimpleFunctionDescriptor) descriptor));
|
||||
}
|
||||
else if (descriptor instanceof PropertyDescriptor) {
|
||||
overridingMembers.add(overrideProperty(classOrObject, (PropertyDescriptor) descriptor));
|
||||
}
|
||||
}
|
||||
return overridingMembers;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static JetElement overrideProperty(@NotNull JetClassOrObject classOrObject, @NotNull PropertyDescriptor descriptor) {
|
||||
PropertyDescriptor newDescriptor = (PropertyDescriptor) descriptor.copy(
|
||||
descriptor.getContainingDeclaration(),
|
||||
Modality.OPEN,
|
||||
descriptor.getVisibility(),
|
||||
descriptor.getKind(),
|
||||
/* copyOverrides = */ true);
|
||||
newDescriptor.addOverriddenDescriptor(descriptor);
|
||||
|
||||
StringBuilder body = new StringBuilder();
|
||||
body.append("\nget()");
|
||||
body.append(" = ");
|
||||
body.append(generateUnsupportedOrSuperCall(classOrObject, descriptor));
|
||||
if (descriptor.isVar()) {
|
||||
body.append("\nset(value) {}");
|
||||
}
|
||||
return JetPsiFactory(classOrObject.getProject()).createProperty(OVERRIDE_RENDERER.render(newDescriptor) + body);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static JetNamedFunction overrideFunction(@NotNull JetClassOrObject classOrObject, @NotNull FunctionDescriptor descriptor) {
|
||||
FunctionDescriptor newDescriptor = descriptor.copy(
|
||||
descriptor.getContainingDeclaration(),
|
||||
Modality.OPEN,
|
||||
descriptor.getVisibility(),
|
||||
descriptor.getKind(),
|
||||
/* copyOverrides = */ true);
|
||||
newDescriptor.addOverriddenDescriptor(descriptor);
|
||||
|
||||
|
||||
JetType returnType = descriptor.getReturnType();
|
||||
KotlinBuiltIns builtIns = KotlinBuiltIns.getInstance();
|
||||
|
||||
boolean returnsNotUnit = returnType != null && !builtIns.getUnitType().equals(returnType);
|
||||
boolean isAbstract = descriptor.getModality() == Modality.ABSTRACT;
|
||||
|
||||
String delegation = generateUnsupportedOrSuperCall(classOrObject, descriptor);
|
||||
|
||||
String body = "{" + (returnsNotUnit && !isAbstract ? "return " : "") + delegation + "}";
|
||||
|
||||
return JetPsiFactory(classOrObject.getProject()).createFunction(OVERRIDE_RENDERER.render(newDescriptor) + body);
|
||||
}
|
||||
|
||||
private static String generateUnsupportedOrSuperCall(@NotNull JetClassOrObject classOrObject, @NotNull CallableMemberDescriptor descriptor) {
|
||||
boolean isAbstract = descriptor.getModality() == Modality.ABSTRACT;
|
||||
if (isAbstract) {
|
||||
return "throw UnsupportedOperationException()";
|
||||
}
|
||||
else {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("super");
|
||||
if (classOrObject.getDelegationSpecifiers().size() > 1) {
|
||||
builder.append("<").append(descriptor.getContainingDeclaration().getName()).append(">");
|
||||
}
|
||||
builder.append(".").append(descriptor.getName());
|
||||
|
||||
if (descriptor instanceof FunctionDescriptor) {
|
||||
builder.append("(");
|
||||
boolean first = true;
|
||||
for (ValueParameterDescriptor parameterDescriptor : descriptor.getValueParameters()) {
|
||||
if (!first) {
|
||||
builder.append(", ");
|
||||
}
|
||||
first = false;
|
||||
builder.append(parameterDescriptor.getName());
|
||||
}
|
||||
builder.append(")");
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Set<CallableMemberDescriptor> collectMethodsToGenerate(@NotNull JetClassOrObject classOrObject) {
|
||||
DeclarationDescriptor descriptor = ResolvePackage.resolveToDescriptor(classOrObject);
|
||||
if (descriptor instanceof ClassDescriptor) {
|
||||
return collectMethodsToGenerate((ClassDescriptor) descriptor);
|
||||
}
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
protected abstract Set<CallableMemberDescriptor> collectMethodsToGenerate(@NotNull ClassDescriptor descriptor);
|
||||
|
||||
private MemberChooser<DescriptorClassMember> showOverrideImplementChooser(
|
||||
Project project,
|
||||
DescriptorClassMember[] members
|
||||
) {
|
||||
MemberChooser<DescriptorClassMember> chooser = new MemberChooser<DescriptorClassMember>(members, true, true, project);
|
||||
chooser.setTitle(getChooserTitle());
|
||||
chooser.show();
|
||||
if (chooser.getExitCode() != DialogWrapper.OK_EXIT_CODE) return null;
|
||||
return chooser;
|
||||
}
|
||||
|
||||
protected abstract String getChooserTitle();
|
||||
|
||||
@Override
|
||||
public boolean isValidFor(Editor editor, PsiFile file) {
|
||||
if (!(file instanceof JetFile)) {
|
||||
return false;
|
||||
}
|
||||
PsiElement elementAtCaret = file.findElementAt(editor.getCaretModel().getOffset());
|
||||
JetClassOrObject classOrObject = PsiTreeUtil.getParentOfType(elementAtCaret, JetClassOrObject.class);
|
||||
return classOrObject != null;
|
||||
}
|
||||
|
||||
protected abstract String getNoMethodsFoundHint();
|
||||
|
||||
public void invoke(@NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file, boolean implementAll) {
|
||||
PsiElement elementAtCaret = file.findElementAt(editor.getCaretModel().getOffset());
|
||||
JetClassOrObject classOrObject = PsiTreeUtil.getParentOfType(elementAtCaret, JetClassOrObject.class);
|
||||
|
||||
assert classOrObject != null;
|
||||
|
||||
Set<CallableMemberDescriptor> missingImplementations = collectMethodsToGenerate(classOrObject);
|
||||
if (missingImplementations.isEmpty() && !implementAll) {
|
||||
HintManager.getInstance().showErrorHint(editor, getNoMethodsFoundHint());
|
||||
return;
|
||||
}
|
||||
List<DescriptorClassMember> members = membersFromDescriptors((JetFile) file, missingImplementations);
|
||||
|
||||
List<DescriptorClassMember> selectedElements;
|
||||
if (implementAll) {
|
||||
selectedElements = members;
|
||||
}
|
||||
else {
|
||||
MemberChooser<DescriptorClassMember> chooser = showOverrideImplementChooser(
|
||||
project,
|
||||
members.toArray(new DescriptorClassMember[members.size()]));
|
||||
|
||||
if (chooser == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
selectedElements = chooser.getSelectedElements();
|
||||
if (selectedElements == null || selectedElements.isEmpty()) return;
|
||||
}
|
||||
|
||||
PsiDocumentManager.getInstance(project).commitAllDocuments();
|
||||
|
||||
generateMethods(editor, classOrObject, selectedElements);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invoke(@NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) {
|
||||
invoke(project, editor, file, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean startInWriteAction() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.core
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithVisibility
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.psi.JetSimpleNameExpression
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getReceiverExpression
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
|
||||
import org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils
|
||||
|
||||
fun DeclarationDescriptorWithVisibility.isVisible(
|
||||
from: DeclarationDescriptor,
|
||||
bindingContext: BindingContext? = null,
|
||||
element: JetSimpleNameExpression? = null
|
||||
): Boolean {
|
||||
if (Visibilities.isVisible(ReceiverValue.IRRELEVANT_RECEIVER, this, from)) return true
|
||||
if (bindingContext == null || element == null) return false
|
||||
|
||||
val receiver = element.getReceiverExpression()
|
||||
val type = receiver?.let { bindingContext.get(BindingContext.EXPRESSION_TYPE, it) }
|
||||
val explicitReceiver = type?.let { ExpressionReceiver(receiver, it) }
|
||||
|
||||
if (explicitReceiver != null) {
|
||||
val normalizeReceiver = ExpressionTypingUtils.normalizeReceiverValueForVisibility(explicitReceiver, bindingContext)
|
||||
return Visibilities.isVisible(normalizeReceiver, this, from)
|
||||
}
|
||||
|
||||
val jetScope = bindingContext[BindingContext.RESOLUTION_SCOPE, element]
|
||||
val implicitReceivers = jetScope?.getImplicitReceiversHierarchy()
|
||||
if (implicitReceivers != null) {
|
||||
for (implicitReceiver in implicitReceivers) {
|
||||
val normalizeReceiver = ExpressionTypingUtils.normalizeReceiverValueForVisibility(implicitReceiver.getValue(), bindingContext)
|
||||
if (Visibilities.isVisible(normalizeReceiver, this, from)) return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.core.formatter;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.codeStyle.CodeStyleSettings;
|
||||
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
|
||||
import com.intellij.psi.codeStyle.CustomCodeStyleSettings;
|
||||
|
||||
public class JetCodeStyleSettings extends CustomCodeStyleSettings {
|
||||
|
||||
public boolean SPACE_AROUND_RANGE = false;
|
||||
|
||||
public boolean SPACE_BEFORE_TYPE_COLON = false;
|
||||
public boolean SPACE_AFTER_TYPE_COLON = true;
|
||||
|
||||
public boolean SPACE_BEFORE_EXTEND_COLON = true;
|
||||
public boolean SPACE_AFTER_EXTEND_COLON = true;
|
||||
|
||||
public boolean INSERT_WHITESPACES_IN_SIMPLE_ONE_LINE_METHOD = true;
|
||||
public boolean ALIGN_IN_COLUMNS_CASE_BRANCH = false;
|
||||
|
||||
public boolean SPACE_AROUND_FUNCTION_TYPE_ARROW = true;
|
||||
|
||||
public boolean SPACE_AROUND_WHEN_ARROW = true;
|
||||
public boolean SPACE_BEFORE_LAMBDA_ARROW = true;
|
||||
|
||||
public boolean LBRACE_ON_NEXT_LINE = false;
|
||||
|
||||
public int NAME_COUNT_TO_USE_STAR_IMPORT = ApplicationManager.getApplication().isUnitTestMode() ? Integer.MAX_VALUE : 5;
|
||||
public boolean IMPORT_PACKAGES = true;
|
||||
|
||||
public static JetCodeStyleSettings getInstance(Project project) {
|
||||
return CodeStyleSettingsManager.getSettings(project).getCustomSettings(JetCodeStyleSettings.class);
|
||||
}
|
||||
|
||||
public JetCodeStyleSettings(CodeStyleSettings container) {
|
||||
super("JetCodeStyleSettings", container);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.core
|
||||
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.ResolutionFacade
|
||||
import com.intellij.psi.PsiClass
|
||||
import org.jetbrains.kotlin.psi.JetClassOrObject
|
||||
import org.jetbrains.kotlin.asJava.KotlinLightClass
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.KotlinLightClassForDecompiledDeclaration
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.JavaResolveExtension
|
||||
import org.jetbrains.kotlin.load.java.structure.impl.JavaClassImpl
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
|
||||
|
||||
public fun ResolutionFacade.psiClassToDescriptor(
|
||||
psiClass: PsiClass,
|
||||
declarationTranslator: (JetClassOrObject) -> JetClassOrObject? = { it }
|
||||
): ClassifierDescriptor? {
|
||||
return if (psiClass is KotlinLightClass && psiClass !is KotlinLightClassForDecompiledDeclaration) {
|
||||
val origin = psiClass.getOrigin ()?: return null
|
||||
val declaration = declarationTranslator(origin) ?: return null
|
||||
resolveToDescriptor(declaration)
|
||||
} else {
|
||||
get(JavaResolveExtension)(psiClass).first.resolveClass(JavaClassImpl(psiClass))
|
||||
} as? ClassifierDescriptor
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.core
|
||||
|
||||
import com.intellij.psi.filters.position.PositionElementFilter
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
|
||||
public class FirstChildInParentFilter(val level: Int = 1) : PositionElementFilter() {
|
||||
override fun isAcceptable(element: Any?, context: PsiElement?): Boolean {
|
||||
if (element !is PsiElement) return false
|
||||
|
||||
var parent: PsiElement? = element
|
||||
for (i in 1..level) {
|
||||
if (parent == null) break
|
||||
parent = parent?.getContext()
|
||||
}
|
||||
|
||||
return (parent != null) && PsiTreeUtil.isAncestor(parent?.getFirstChild(), element, true)
|
||||
}
|
||||
|
||||
|
||||
override fun toString(): String {
|
||||
return "firstChildInParent($level)"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.core.quickfix;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import com.intellij.extapi.psi.ASTDelegatePsiElement;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.PsiWhiteSpace;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.ReadOnly;
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor;
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic;
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.ResolvePackage;
|
||||
import org.jetbrains.kotlin.idea.references.BuiltInsReferenceResolver;
|
||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers;
|
||||
import org.jetbrains.kotlin.name.FqName;
|
||||
import org.jetbrains.kotlin.psi.*;
|
||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer;
|
||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils;
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils;
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilPackage;
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
|
||||
import org.jetbrains.kotlin.types.DeferredType;
|
||||
import org.jetbrains.kotlin.types.JetType;
|
||||
import org.jetbrains.kotlin.types.checker.JetTypeChecker;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public class QuickFixUtil {
|
||||
private QuickFixUtil() {
|
||||
}
|
||||
|
||||
public static boolean removePossiblyWhiteSpace(ASTDelegatePsiElement element, PsiElement possiblyWhiteSpace) {
|
||||
if (possiblyWhiteSpace instanceof PsiWhiteSpace) {
|
||||
element.deleteChildInternal(possiblyWhiteSpace.getNode());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static <T extends PsiElement> T getParentElementOfType(Diagnostic diagnostic, Class<T> aClass) {
|
||||
return PsiTreeUtil.getParentOfType(diagnostic.getPsiElement(), aClass, false);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static JetType getDeclarationReturnType(JetNamedDeclaration declaration) {
|
||||
PsiFile file = declaration.getContainingFile();
|
||||
if (!(file instanceof JetFile)) return null;
|
||||
BindingContext bindingContext = ResolvePackage.analyzeFully((JetFile) file);
|
||||
DeclarationDescriptor descriptor = bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, declaration);
|
||||
if (!(descriptor instanceof CallableDescriptor)) return null;
|
||||
JetType type = ((CallableDescriptor) descriptor).getReturnType();
|
||||
if (type instanceof DeferredType) {
|
||||
type = ((DeferredType) type).getDelegate();
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static JetType findLowerBoundOfOverriddenCallablesReturnTypes(BindingContext context, JetDeclaration callable) {
|
||||
DeclarationDescriptor descriptor = context.get(BindingContext.DECLARATION_TO_DESCRIPTOR, callable);
|
||||
if (!(descriptor instanceof CallableDescriptor)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
JetType matchingReturnType = null;
|
||||
for (CallableDescriptor overriddenDescriptor : ((CallableDescriptor) descriptor).getOverriddenDescriptors()) {
|
||||
JetType overriddenReturnType = overriddenDescriptor.getReturnType();
|
||||
if (overriddenReturnType == null) {
|
||||
return null;
|
||||
}
|
||||
if (matchingReturnType == null || JetTypeChecker.DEFAULT.isSubtypeOf(overriddenReturnType, matchingReturnType)) {
|
||||
matchingReturnType = overriddenReturnType;
|
||||
}
|
||||
else if (!JetTypeChecker.DEFAULT.isSubtypeOf(matchingReturnType, overriddenReturnType)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return matchingReturnType;
|
||||
}
|
||||
|
||||
public static boolean canModifyElement(@NotNull PsiElement element) {
|
||||
return element.isWritable() && !BuiltInsReferenceResolver.isFromBuiltIns(element);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static PsiElement safeGetDeclaration(@Nullable CallableDescriptor descriptor) {
|
||||
//do not create fix if descriptor has more than one overridden declaration
|
||||
if (descriptor == null || descriptor.getOverriddenDescriptors().size() > 1) return null;
|
||||
return DescriptorToSourceUtils.descriptorToDeclaration(descriptor);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static JetParameter getParameterDeclarationForValueArgument(
|
||||
@NotNull ResolvedCall<?> resolvedCall,
|
||||
@Nullable ValueArgument valueArgument
|
||||
) {
|
||||
PsiElement declaration = safeGetDeclaration(CallUtilPackage.getParameterForArgument(resolvedCall, valueArgument));
|
||||
return declaration instanceof JetParameter ? (JetParameter) declaration : null;
|
||||
}
|
||||
|
||||
private static boolean equalOrLastInThenOrElse(JetExpression thenOrElse, JetExpression expression) {
|
||||
if (thenOrElse == expression) return true;
|
||||
return thenOrElse instanceof JetBlockExpression && expression.getParent() == thenOrElse &&
|
||||
PsiTreeUtil.getNextSiblingOfType(expression, JetExpression.class) == null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static JetIfExpression getParentIfForBranch(@Nullable JetExpression expression) {
|
||||
JetIfExpression ifExpression = PsiTreeUtil.getParentOfType(expression, JetIfExpression.class, true);
|
||||
if (ifExpression == null) return null;
|
||||
if (equalOrLastInThenOrElse(ifExpression.getThen(), expression)
|
||||
|| equalOrLastInThenOrElse(ifExpression.getElse(), expression)) {
|
||||
return ifExpression;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean canEvaluateTo(JetExpression parent, JetExpression child) {
|
||||
if (parent == null || child == null) {
|
||||
return false;
|
||||
}
|
||||
while (parent != child) {
|
||||
if (child.getParent() instanceof JetParenthesizedExpression) {
|
||||
child = (JetExpression) child.getParent();
|
||||
continue;
|
||||
}
|
||||
child = getParentIfForBranch(child);
|
||||
if (child == null) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean canFunctionOrGetterReturnExpression(@NotNull JetDeclaration functionOrGetter, @NotNull JetExpression expression) {
|
||||
if (functionOrGetter instanceof JetFunctionLiteral) {
|
||||
JetBlockExpression functionLiteralBody = ((JetFunctionLiteral) functionOrGetter).getBodyExpression();
|
||||
PsiElement returnedElement = functionLiteralBody == null ? null : functionLiteralBody.getLastChild();
|
||||
return returnedElement instanceof JetExpression && canEvaluateTo((JetExpression) returnedElement, expression);
|
||||
}
|
||||
else {
|
||||
if (functionOrGetter instanceof JetWithExpressionInitializer && canEvaluateTo(((JetWithExpressionInitializer) functionOrGetter).getInitializer(), expression)) {
|
||||
return true;
|
||||
}
|
||||
JetReturnExpression returnExpression = PsiTreeUtil.getParentOfType(expression, JetReturnExpression.class);
|
||||
return returnExpression != null && canEvaluateTo(returnExpression.getReturnedExpression(), expression);
|
||||
}
|
||||
}
|
||||
|
||||
@ReadOnly
|
||||
@NotNull
|
||||
public static Set<String> getUsedParameters(
|
||||
@NotNull JetCallElement callElement,
|
||||
@Nullable JetValueArgument ignoreArgument,
|
||||
@NotNull CallableDescriptor callableDescriptor
|
||||
) {
|
||||
Set<String> usedParameters = Sets.newHashSet();
|
||||
boolean isPositionalArgument = true;
|
||||
int idx = 0;
|
||||
for (ValueArgument argument : callElement.getValueArguments()) {
|
||||
if (argument.isNamed()) {
|
||||
JetValueArgumentName name = argument.getArgumentName();
|
||||
assert name != null : "Named argument's name cannot be null";
|
||||
if (argument != ignoreArgument) {
|
||||
usedParameters.add(name.getText());
|
||||
}
|
||||
isPositionalArgument = false;
|
||||
}
|
||||
else if (isPositionalArgument) {
|
||||
if (callableDescriptor.getValueParameters().size() > idx) {
|
||||
ValueParameterDescriptor parameter = callableDescriptor.getValueParameters().get(idx);
|
||||
if (argument != ignoreArgument) {
|
||||
usedParameters.add(parameter.getName().asString());
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return usedParameters;
|
||||
}
|
||||
|
||||
public static String renderTypeWithFqNameOnClash(JetType type, String nameToCheckAgainst) {
|
||||
FqName typeFqName = DescriptorUtils.getFqNameSafe(DescriptorUtils.getClassDescriptorForType(type));
|
||||
FqName fqNameToCheckAgainst = new FqName(nameToCheckAgainst);
|
||||
DescriptorRenderer renderer = typeFqName.shortName().equals(fqNameToCheckAgainst.shortName())
|
||||
? IdeDescriptorRenderers.SOURCE_CODE
|
||||
: IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES;
|
||||
return renderer.renderType(type);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,257 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.core.refactoring;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor;
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.ResolvePackage;
|
||||
import org.jetbrains.kotlin.lexer.JetLexer;
|
||||
import org.jetbrains.kotlin.lexer.JetTokens;
|
||||
import org.jetbrains.kotlin.name.Name;
|
||||
import org.jetbrains.kotlin.psi.*;
|
||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode;
|
||||
import org.jetbrains.kotlin.types.ErrorUtils;
|
||||
import org.jetbrains.kotlin.types.JetType;
|
||||
import org.jetbrains.kotlin.types.TypeUtils;
|
||||
import org.jetbrains.kotlin.types.checker.JetTypeChecker;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class JetNameSuggester {
|
||||
private JetNameSuggester() {
|
||||
}
|
||||
|
||||
private static void addName(ArrayList<String> result, @Nullable String name, JetNameValidator validator) {
|
||||
if ("class".equals(name)) name = "clazz";
|
||||
if (!isIdentifier(name)) return;
|
||||
String newName = validator.validateName(name);
|
||||
if (newName == null) return;
|
||||
result.add(newName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Name suggestion types:
|
||||
* 1. According to type:
|
||||
* 1a. Primitive types to some short name
|
||||
* 1b. Class types according to class name camel humps: (AbCd => {abCd, cd})
|
||||
* 1c. Arrays => arrayOfInnerType
|
||||
* 2. Reference expressions according to reference name camel humps
|
||||
* 3. Method call expression according to method callee expression
|
||||
* @param expression to suggest name for variable
|
||||
* @param validator to check scope for such names
|
||||
* @param defaultName
|
||||
* @return possible names
|
||||
*/
|
||||
public static @NotNull String[] suggestNames(@NotNull JetExpression expression, @NotNull JetNameValidator validator, @Nullable String defaultName) {
|
||||
ArrayList<String> result = new ArrayList<String>();
|
||||
|
||||
BindingContext bindingContext = ResolvePackage.analyze(expression, BodyResolveMode.FULL);
|
||||
JetType jetType = bindingContext.get(BindingContext.EXPRESSION_TYPE, expression);
|
||||
if (jetType != null) {
|
||||
addNamesForType(result, jetType, validator);
|
||||
}
|
||||
addNamesForExpression(result, expression, validator);
|
||||
|
||||
if (result.isEmpty()) addName(result, defaultName, validator);
|
||||
return ArrayUtil.toStringArray(result);
|
||||
}
|
||||
|
||||
public static @NotNull String[] suggestNames(@NotNull JetType type, @NotNull JetNameValidator validator, @Nullable String defaultName) {
|
||||
ArrayList<String> result = new ArrayList<String>();
|
||||
addNamesForType(result, type, validator);
|
||||
if (result.isEmpty()) addName(result, defaultName, validator);
|
||||
return ArrayUtil.toStringArray(result);
|
||||
}
|
||||
|
||||
public static @NotNull String[] suggestNamesForType(@NotNull JetType jetType, @NotNull JetNameValidator validator) {
|
||||
ArrayList<String> result = new ArrayList<String>();
|
||||
addNamesForType(result, jetType, validator);
|
||||
return ArrayUtil.toStringArray(result);
|
||||
}
|
||||
|
||||
public static @NotNull String[] suggestNamesForExpression(@NotNull JetExpression expression, @NotNull JetNameValidator validator) {
|
||||
ArrayList<String> result = new ArrayList<String>();
|
||||
addNamesForExpression(result, expression, validator);
|
||||
return ArrayUtil.toStringArray(result);
|
||||
}
|
||||
|
||||
private static final String[] COMMON_TYPE_PARAMETER_NAMES = {"T", "U", "V", "W", "X", "Y", "Z"};
|
||||
|
||||
public static @NotNull String[] suggestNamesForTypeParameters(int count, @NotNull JetNameValidator validator) {
|
||||
ArrayList<String> result = new ArrayList<String>();
|
||||
for (int i = 0; i < count; i++) {
|
||||
result.add(validator.validateNameWithVariants(COMMON_TYPE_PARAMETER_NAMES));
|
||||
}
|
||||
return ArrayUtil.toStringArray(result);
|
||||
}
|
||||
|
||||
private static void addNamesForType(ArrayList<String> result, JetType jetType, JetNameValidator validator) {
|
||||
KotlinBuiltIns builtIns = KotlinBuiltIns.getInstance();
|
||||
JetTypeChecker typeChecker = JetTypeChecker.DEFAULT;
|
||||
jetType = TypeUtils.makeNotNullable(jetType); // wipe out '?'
|
||||
if (ErrorUtils.containsErrorType(jetType)) return;
|
||||
if (typeChecker.equalTypes(builtIns.getBooleanType(), jetType)) {
|
||||
addName(result, "b", validator);
|
||||
}
|
||||
else if (typeChecker.equalTypes(builtIns.getIntType(), jetType)) {
|
||||
addName(result, "i", validator);
|
||||
}
|
||||
else if (typeChecker.equalTypes(builtIns.getByteType(), jetType)) {
|
||||
addName(result, "byte", validator);
|
||||
}
|
||||
else if (typeChecker.equalTypes(builtIns.getLongType(), jetType)) {
|
||||
addName(result, "l", validator);
|
||||
}
|
||||
else if (typeChecker.equalTypes(builtIns.getFloatType(), jetType)) {
|
||||
addName(result, "fl", validator);
|
||||
}
|
||||
else if (typeChecker.equalTypes(builtIns.getDoubleType(), jetType)) {
|
||||
addName(result, "d", validator);
|
||||
}
|
||||
else if (typeChecker.equalTypes(builtIns.getShortType(), jetType)) {
|
||||
addName(result, "sh", validator);
|
||||
}
|
||||
else if (typeChecker.equalTypes(builtIns.getCharType(), jetType)) {
|
||||
addName(result, "c", validator);
|
||||
}
|
||||
else if (typeChecker.equalTypes(builtIns.getStringType(), jetType)) {
|
||||
addName(result, "s", validator);
|
||||
}
|
||||
else if (KotlinBuiltIns.isArray(jetType) || KotlinBuiltIns.isPrimitiveArray(jetType)) {
|
||||
JetType elementType = KotlinBuiltIns.getInstance().getArrayElementType(jetType);
|
||||
if (typeChecker.equalTypes(builtIns.getBooleanType(), elementType)) {
|
||||
addName(result, "booleans", validator);
|
||||
}
|
||||
else if (typeChecker.equalTypes(builtIns.getIntType(), elementType)) {
|
||||
addName(result, "ints", validator);
|
||||
}
|
||||
else if (typeChecker.equalTypes(builtIns.getByteType(), elementType)) {
|
||||
addName(result, "bytes", validator);
|
||||
}
|
||||
else if (typeChecker.equalTypes(builtIns.getLongType(), elementType)) {
|
||||
addName(result, "longs", validator);
|
||||
}
|
||||
else if (typeChecker.equalTypes(builtIns.getFloatType(), elementType)) {
|
||||
addName(result, "floats", validator);
|
||||
}
|
||||
else if (typeChecker.equalTypes(builtIns.getDoubleType(), elementType)) {
|
||||
addName(result, "doubles", validator);
|
||||
}
|
||||
else if (typeChecker.equalTypes(builtIns.getShortType(), elementType)) {
|
||||
addName(result, "shorts", validator);
|
||||
}
|
||||
else if (typeChecker.equalTypes(builtIns.getCharType(), elementType)) {
|
||||
addName(result, "chars", validator);
|
||||
}
|
||||
else if (typeChecker.equalTypes(builtIns.getStringType(), elementType)) {
|
||||
addName(result, "strings", validator);
|
||||
}
|
||||
else {
|
||||
ClassDescriptor classDescriptor = TypeUtils.getClassDescriptor(elementType);
|
||||
if (classDescriptor != null) {
|
||||
Name className = classDescriptor.getName();
|
||||
addName(result, "arrayOf" + StringUtil.capitalize(className.asString()) + "s", validator);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
addForClassType(result, jetType, validator);
|
||||
}
|
||||
}
|
||||
|
||||
private static void addForClassType(ArrayList<String> result, JetType jetType, JetNameValidator validator) {
|
||||
ClassifierDescriptor descriptor = jetType.getConstructor().getDeclarationDescriptor();
|
||||
if (descriptor != null) {
|
||||
Name className = descriptor.getName();
|
||||
if (!className.isSpecial()) {
|
||||
addCamelNames(result, className.asString(), validator);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void addCamelNames(ArrayList<String> result, String name, JetNameValidator validator) {
|
||||
if (name == "") return;
|
||||
String s = deleteNonLetterFromString(name);
|
||||
if (s.startsWith("get") || s.startsWith("set")) s = s.substring(3);
|
||||
else if (s.startsWith("is")) s = s.substring(2);
|
||||
for (int i = 0; i < s.length(); ++i) {
|
||||
if (i == 0) {
|
||||
addName(result, StringUtil.decapitalize(s), validator);
|
||||
}
|
||||
else if (s.charAt(i) >= 'A' && s.charAt(i) <= 'Z') {
|
||||
addName(result, StringUtil.decapitalize(s.substring(i)), validator);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String deleteNonLetterFromString(String s) {
|
||||
Pattern pattern = Pattern.compile("[^a-zA-Z]");
|
||||
Matcher matcher = pattern.matcher(s);
|
||||
return matcher.replaceAll("");
|
||||
}
|
||||
|
||||
private static void addNamesForExpression(final ArrayList<String> result, JetExpression expression, final JetNameValidator validator) {
|
||||
expression.accept(new JetVisitorVoid() {
|
||||
@Override
|
||||
public void visitQualifiedExpression(@NotNull JetQualifiedExpression expression) {
|
||||
JetExpression selectorExpression = expression.getSelectorExpression();
|
||||
addNamesForExpression(result, selectorExpression, validator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitSimpleNameExpression(@NotNull JetSimpleNameExpression expression) {
|
||||
String referenceName = expression.getReferencedName();
|
||||
if (referenceName.equals(referenceName.toUpperCase())) {
|
||||
addName(result, referenceName, validator);
|
||||
}
|
||||
else {
|
||||
addCamelNames(result, referenceName, validator);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitCallExpression(@NotNull JetCallExpression expression) {
|
||||
addNamesForExpression(result, expression.getCalleeExpression(), validator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPostfixExpression(@NotNull JetPostfixExpression expression) {
|
||||
addNamesForExpression(result, expression.getBaseExpression(), validator);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static boolean isIdentifier(@Nullable String name) {
|
||||
ApplicationManager.getApplication().assertReadAccessAllowed();
|
||||
if (name == null || name.isEmpty()) return false;
|
||||
|
||||
JetLexer lexer = new JetLexer();
|
||||
lexer.start(name, 0, name.length());
|
||||
if (lexer.getTokenType() != JetTokens.IDENTIFIER) return false;
|
||||
lexer.advance();
|
||||
return lexer.getTokenType() == null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.core.refactoring
|
||||
|
||||
import java.util.HashSet
|
||||
import java.util.Collections
|
||||
|
||||
public abstract class JetNameValidator {
|
||||
/**
|
||||
* Validates name, and slightly improves it by adding number to name in case of conflicts
|
||||
* @param name to check it in scope
|
||||
* @return name or nameI, where I is number
|
||||
*/
|
||||
public open fun validateName(name: String): String {
|
||||
if (validateInner(name)) return name
|
||||
var i = 1
|
||||
while (!validateInner(name + i)) {
|
||||
++i
|
||||
}
|
||||
|
||||
return name + i
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates name using set of variants which are tried in succession (and extended with suffixes if necessary)
|
||||
* For example, when given sequence of a, b, c possible names are tried out in the following order: a, b, c, a1, b1, c1, a2, b2, c2, ...
|
||||
* @param names to check it in scope
|
||||
* @return name or nameI, where name is one of variants and I is a number
|
||||
*/
|
||||
public fun validateNameWithVariants(vararg names: String): String {
|
||||
var i = 0
|
||||
while (true) {
|
||||
for (name in names) {
|
||||
val candidate = if (i > 0) name + i else name
|
||||
if (validateInner(candidate)) return candidate
|
||||
}
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract fun validateInner(name: String): Boolean
|
||||
}
|
||||
|
||||
public object EmptyValidator : JetNameValidator() {
|
||||
override fun validateInner(name: String): Boolean = true
|
||||
}
|
||||
|
||||
public open class CollectingValidator(
|
||||
existingNames: Collection<String> = Collections.emptySet(),
|
||||
val filter: (String) -> Boolean = { true }
|
||||
): JetNameValidator() {
|
||||
private val suggestedSet = HashSet(existingNames)
|
||||
|
||||
override fun validateInner(name: String): Boolean {
|
||||
if (name !in suggestedSet && filter(name)) {
|
||||
suggestedSet.add(name)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: To be used from Java
|
||||
public class SimpleCollectingValidator : CollectingValidator()
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.core.refactoring
|
||||
|
||||
import com.intellij.psi.PsiElementFactory
|
||||
import com.intellij.psi.PsiJavaCodeReferenceElement
|
||||
import com.intellij.psi.PsiReferenceList
|
||||
import com.intellij.psi.PsiReferenceList.Role
|
||||
|
||||
public fun PsiElementFactory.createReferenceListWithRole(
|
||||
references: Array<PsiJavaCodeReferenceElement>,
|
||||
role: Role
|
||||
): PsiReferenceList? {
|
||||
val refsText = references.map { it.getCanonicalText() }
|
||||
val refListText = if (refsText.isNotEmpty()) refsText.joinToString() else return null
|
||||
return when (role) {
|
||||
Role.THROWS_LIST -> createMethodFromText("void foo() throws $refListText {}", null).getThrowsList()
|
||||
Role.EXTENDS_LIST -> createClassFromText("class Foo extends $refListText {}", null).getInnerClasses()[0].getExtendsList()
|
||||
Role.IMPLEMENTS_LIST -> createClassFromText("class Foo implements $refListText {}", null).getInnerClasses()[0].getImplementsList()
|
||||
Role.EXTENDS_BOUNDS_LIST -> createTypeParameterFromText("T extends $refListText", null).getExtendsList()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,600 @@
|
||||
/*
|
||||
* Copyright 2010-2015 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.core.refactoring
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.psi.PsiDirectory
|
||||
import com.intellij.openapi.roots.JavaProjectRootsUtil
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import com.intellij.refactoring.util.ConflictsUtil
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getPackage
|
||||
import com.intellij.psi.PsiFileFactory
|
||||
import org.jetbrains.kotlin.idea.JetFileType
|
||||
import com.intellij.openapi.project.Project
|
||||
import java.io.File
|
||||
import com.intellij.openapi.vfs.LocalFileSystem
|
||||
import com.intellij.psi.PsiManager
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import java.util.ArrayList
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.refactoring.BaseRefactoringProcessor.ConflictsInTestsException
|
||||
import com.intellij.refactoring.ui.ConflictsDialog
|
||||
import com.intellij.util.containers.MultiMap
|
||||
import org.jetbrains.kotlin.psi.codeFragmentUtil.suppressDiagnosticsInDebugMode
|
||||
import com.intellij.ide.util.PsiElementListCellRenderer
|
||||
import com.intellij.openapi.ui.popup.JBPopup
|
||||
import com.intellij.openapi.ui.popup.PopupChooserBuilder
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.codeInsight.unwrap.RangeSplitter
|
||||
import com.intellij.codeInsight.unwrap.UnwrapHandler
|
||||
import com.intellij.openapi.editor.markup.TextAttributes
|
||||
import com.intellij.openapi.editor.markup.HighlighterTargetArea
|
||||
import com.intellij.openapi.editor.markup.RangeHighlighter
|
||||
import java.util.Collections
|
||||
import com.intellij.openapi.editor.colors.EditorColors
|
||||
import com.intellij.openapi.editor.colors.EditorColorsManager
|
||||
import com.intellij.ui.components.JBList
|
||||
import com.intellij.openapi.ui.popup.JBPopupAdapter
|
||||
import com.intellij.openapi.ui.popup.LightweightWindowEvent
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.psi.psiUtil.isAncestor
|
||||
import com.intellij.psi.PsiNamedElement
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import javax.swing.Icon
|
||||
import org.jetbrains.kotlin.idea.util.string.collapseSpaces
|
||||
import org.jetbrains.kotlin.asJava.KotlinLightMethod
|
||||
import com.intellij.psi.PsiMethod
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||
import com.intellij.psi.PsiPackage
|
||||
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
|
||||
import org.jetbrains.kotlin.psi.psiUtil.parents
|
||||
import com.intellij.refactoring.util.RefactoringUIUtil
|
||||
import com.intellij.psi.PsiMember
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.getJavaMemberDescriptor
|
||||
import com.intellij.psi.PsiElementFactory
|
||||
import com.intellij.psi.PsiModifier
|
||||
import com.intellij.psi.PsiTypeParameterList
|
||||
import com.intellij.refactoring.changeSignature.ChangeSignatureUtil
|
||||
import com.intellij.psi.PsiModifierList
|
||||
import org.jetbrains.kotlin.asJava.LightClassUtil
|
||||
import com.intellij.psi.PsiField
|
||||
import com.intellij.util.VisibilityUtil
|
||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor.Kind
|
||||
import org.jetbrains.kotlin.resolve.OverridingUtil
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import com.intellij.psi.PsiReferenceList
|
||||
import com.intellij.psi.PsiTypeParameter
|
||||
import com.intellij.psi.PsiTypeParameterListOwner
|
||||
import com.intellij.psi.PsiNameIdentifierOwner
|
||||
import com.intellij.psi.PsiJavaCodeReferenceElement
|
||||
import com.intellij.lang.java.JavaLanguage
|
||||
import com.intellij.psi.PsiClass
|
||||
import com.intellij.codeInsight.daemon.impl.quickfix.CreateFromUsageUtils
|
||||
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
|
||||
|
||||
fun <T: Any> PsiElement.getAndRemoveCopyableUserData(key: Key<T>): T? {
|
||||
val data = getCopyableUserData(key)
|
||||
putCopyableUserData(key, null)
|
||||
return data
|
||||
}
|
||||
|
||||
fun getOrCreateKotlinFile(fileName: String, targetDir: PsiDirectory): JetFile? =
|
||||
(targetDir.findFile(fileName) ?: createKotlinFile(fileName, targetDir)) as? JetFile
|
||||
|
||||
fun createKotlinFile(fileName: String, targetDir: PsiDirectory): JetFile {
|
||||
val packageName = targetDir.getPackage()?.getQualifiedName()
|
||||
|
||||
targetDir.checkCreateFile(fileName)
|
||||
val file = PsiFileFactory.getInstance(targetDir.getProject()).createFileFromText(
|
||||
fileName, JetFileType.INSTANCE, if (packageName != null && packageName.isNotEmpty()) "package $packageName \n\n" else ""
|
||||
)
|
||||
|
||||
return targetDir.add(file) as JetFile
|
||||
}
|
||||
|
||||
public fun File.toVirtualFile(): VirtualFile? = LocalFileSystem.getInstance().findFileByIoFile(this)
|
||||
|
||||
public fun File.toPsiFile(project: Project): PsiFile? {
|
||||
return toVirtualFile()?.let { vfile -> PsiManager.getInstance(project).findFile(vfile) }
|
||||
}
|
||||
|
||||
public fun File.toPsiDirectory(project: Project): PsiDirectory? {
|
||||
return toVirtualFile()?.let { vfile -> PsiManager.getInstance(project).findDirectory(vfile) }
|
||||
}
|
||||
|
||||
public fun PsiElement.getUsageContext(): PsiElement {
|
||||
return when (this) {
|
||||
is JetElement -> PsiTreeUtil.getParentOfType(this, javaClass<JetNamedDeclaration>(), javaClass<JetFile>())!!
|
||||
else -> ConflictsUtil.getContainer(this)
|
||||
}
|
||||
}
|
||||
|
||||
public fun PsiElement.isInJavaSourceRoot(): Boolean =
|
||||
!JavaProjectRootsUtil.isOutsideJavaSourceRoot(getContainingFile())
|
||||
|
||||
public inline fun JetFile.createTempCopy(textTransform: (String) -> String): JetFile {
|
||||
val tmpFile = JetPsiFactory(this).createAnalyzableFile(getName(), textTransform(getText() ?: ""), this)
|
||||
tmpFile.setOriginalFile(this)
|
||||
tmpFile.suppressDiagnosticsInDebugMode = suppressDiagnosticsInDebugMode
|
||||
return tmpFile
|
||||
}
|
||||
|
||||
public fun PsiElement.getAllExtractionContainers(strict: Boolean = true): List<JetElement> {
|
||||
val containers = ArrayList<JetElement>()
|
||||
|
||||
var element: PsiElement? = if (strict) getParent() else this
|
||||
while (element != null) {
|
||||
when (element) {
|
||||
is JetBlockExpression, is JetClassBody, is JetFile -> containers.add(element as JetElement)
|
||||
}
|
||||
|
||||
element = element!!.getParent()
|
||||
}
|
||||
|
||||
return containers
|
||||
}
|
||||
|
||||
public fun PsiElement.getExtractionContainers(strict: Boolean = true, includeAll: Boolean = false): List<JetElement> {
|
||||
fun getEnclosingDeclaration(element: PsiElement, strict: Boolean): PsiElement? {
|
||||
return element.parents(!strict)
|
||||
.filter {
|
||||
(it is JetDeclarationWithBody && it !is JetFunctionLiteral)
|
||||
|| it is JetClassInitializer
|
||||
|| it is JetClassBody
|
||||
|| it is JetFile
|
||||
}
|
||||
.firstOrNull()
|
||||
}
|
||||
|
||||
if (includeAll) return getAllExtractionContainers(strict)
|
||||
|
||||
val enclosingDeclaration = getEnclosingDeclaration(this, strict)?.let {
|
||||
if (it is JetDeclarationWithBody || it is JetClassInitializer) getEnclosingDeclaration(it, true) else it
|
||||
}
|
||||
|
||||
return when (enclosingDeclaration) {
|
||||
is JetFile -> Collections.singletonList(enclosingDeclaration)
|
||||
is JetClassBody -> getAllExtractionContainers(strict).filterIsInstance<JetClassBody>()
|
||||
else -> {
|
||||
val targetContainer = when (enclosingDeclaration) {
|
||||
is JetDeclarationWithBody -> enclosingDeclaration.getBodyExpression()
|
||||
is JetClassInitializer -> enclosingDeclaration.getBody()
|
||||
else -> null
|
||||
}
|
||||
if (targetContainer is JetBlockExpression) Collections.singletonList(targetContainer) else Collections.emptyList()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public fun Project.checkConflictsInteractively(conflicts: MultiMap<PsiElement, String>, onAccept: () -> Unit) {
|
||||
if (!conflicts.isEmpty()) {
|
||||
if (ApplicationManager.getApplication()!!.isUnitTestMode()) throw ConflictsInTestsException(conflicts.values())
|
||||
|
||||
val dialog = ConflictsDialog(this, conflicts, onAccept)
|
||||
dialog.show()
|
||||
if (!dialog.isOK()) return
|
||||
}
|
||||
|
||||
onAccept()
|
||||
}
|
||||
|
||||
public fun reportDeclarationConflict(
|
||||
conflicts: MultiMap<PsiElement, String>,
|
||||
declaration: PsiElement,
|
||||
message: (renderedDeclaration: String) -> String
|
||||
) {
|
||||
conflicts.putValue(declaration, message(RefactoringUIUtil.getDescription(declaration, true).capitalize()))
|
||||
}
|
||||
|
||||
public fun <T, E: PsiElement> getPsiElementPopup(
|
||||
editor: Editor,
|
||||
elements: List<T>,
|
||||
renderer: PsiElementListCellRenderer<E>,
|
||||
title: String?,
|
||||
highlightSelection: Boolean,
|
||||
toPsi: (T) -> E,
|
||||
processor: (T) -> Boolean): JBPopup {
|
||||
val highlighter = if (highlightSelection) SelectionAwareScopeHighlighter(editor) else null
|
||||
|
||||
val list = JBList(elements.map(toPsi))
|
||||
list.setCellRenderer(renderer)
|
||||
list.addListSelectionListener { e ->
|
||||
highlighter?.dropHighlight()
|
||||
val index = list.getSelectedIndex()
|
||||
if (index >= 0) {
|
||||
highlighter?.highlight(list.getModel()!!.getElementAt(index) as PsiElement)
|
||||
}
|
||||
}
|
||||
|
||||
return with(PopupChooserBuilder(list)) {
|
||||
title?.let { setTitle(it) }
|
||||
renderer.installSpeedSearch(this, true)
|
||||
setItemChoosenCallback {
|
||||
val index = list.getSelectedIndex()
|
||||
if (index >= 0) {
|
||||
processor(elements[index])
|
||||
}
|
||||
}
|
||||
addListener(object: JBPopupAdapter() {
|
||||
override fun onClosed(event: LightweightWindowEvent?) {
|
||||
highlighter?.dropHighlight();
|
||||
}
|
||||
})
|
||||
|
||||
createPopup()
|
||||
}
|
||||
}
|
||||
|
||||
public class SelectionAwareScopeHighlighter(val editor: Editor) {
|
||||
private val highlighters = ArrayList<RangeHighlighter>()
|
||||
|
||||
private fun addHighlighter(r: TextRange, attr: TextAttributes) {
|
||||
highlighters.add(
|
||||
editor.getMarkupModel().addRangeHighlighter(
|
||||
r.getStartOffset(),
|
||||
r.getEndOffset(),
|
||||
UnwrapHandler.HIGHLIGHTER_LEVEL,
|
||||
attr,
|
||||
HighlighterTargetArea.EXACT_RANGE
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
public fun highlight(wholeAffected: PsiElement) {
|
||||
dropHighlight()
|
||||
|
||||
val attributes = EditorColorsManager.getInstance().getGlobalScheme().getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES)!!
|
||||
val selectedRange = with(editor.getSelectionModel()) { TextRange(getSelectionStart(), getSelectionEnd()) }
|
||||
for (r in RangeSplitter.split(wholeAffected.getTextRange()!!, Collections.singletonList(selectedRange))) {
|
||||
addHighlighter(r, attributes)
|
||||
}
|
||||
}
|
||||
|
||||
public fun dropHighlight() {
|
||||
highlighters.forEach { it.dispose() }
|
||||
highlighters.clear()
|
||||
}
|
||||
}
|
||||
|
||||
fun PsiElement.getLineCount(): Int {
|
||||
val doc = getContainingFile()?.let { file -> PsiDocumentManager.getInstance(getProject()).getDocument(file) }
|
||||
if (doc != null) {
|
||||
val spaceRange = getTextRange() ?: TextRange.EMPTY_RANGE
|
||||
|
||||
val startLine = doc.getLineNumber(spaceRange.getStartOffset())
|
||||
val endLine = doc.getLineNumber(spaceRange.getEndOffset())
|
||||
|
||||
return endLine - startLine
|
||||
}
|
||||
|
||||
return (getText() ?: "").count { it == '\n' } + 1
|
||||
}
|
||||
|
||||
fun PsiElement.isMultiLine(): Boolean = getLineCount() > 1
|
||||
|
||||
public fun JetElement.getContextForContainingDeclarationBody(): BindingContext? {
|
||||
val enclosingDeclaration = getStrictParentOfType<JetDeclaration>()
|
||||
val bodyElement = when (enclosingDeclaration) {
|
||||
is JetDeclarationWithBody -> enclosingDeclaration.getBodyExpression()
|
||||
is JetWithExpressionInitializer -> enclosingDeclaration.getInitializer()
|
||||
is JetMultiDeclaration -> enclosingDeclaration.getInitializer()
|
||||
is JetParameter -> enclosingDeclaration.getDefaultValue()
|
||||
is JetClassInitializer -> enclosingDeclaration.getBody()
|
||||
is JetClass -> {
|
||||
val delegationSpecifierList = enclosingDeclaration.getDelegationSpecifierList()
|
||||
if (delegationSpecifierList.isAncestor(this)) this else null
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
return bodyElement?.let { it.analyze() }
|
||||
}
|
||||
|
||||
public fun chooseContainerElement<T>(
|
||||
containers: List<T>,
|
||||
editor: Editor,
|
||||
title: String,
|
||||
highlightSelection: Boolean,
|
||||
toPsi: (T) -> PsiElement,
|
||||
onSelect: (T) -> Unit) {
|
||||
return getPsiElementPopup(
|
||||
editor,
|
||||
containers,
|
||||
object : PsiElementListCellRenderer<PsiElement>() {
|
||||
private fun PsiElement.renderName(): String {
|
||||
if (this is JetPropertyAccessor) {
|
||||
return (getParent() as JetProperty).renderName() + if (isGetter()) ".get" else ".set"
|
||||
}
|
||||
if (this is JetObjectDeclaration && this.isCompanion()) {
|
||||
return "Companion object of ${getStrictParentOfType<JetClassOrObject>()?.renderName() ?: "<anonymous>"}"
|
||||
}
|
||||
return (this as? PsiNamedElement)?.getName() ?: "<anonymous>"
|
||||
}
|
||||
|
||||
private fun PsiElement.renderDeclaration(): String? {
|
||||
val descriptor = when {
|
||||
this is JetFile -> getName()
|
||||
this is JetElement -> analyze()[BindingContext.DECLARATION_TO_DESCRIPTOR, this]
|
||||
this is PsiMember -> getJavaMemberDescriptor()
|
||||
else -> null
|
||||
} ?: return null
|
||||
val name = renderName()
|
||||
val params = (descriptor as? FunctionDescriptor)?.let { descriptor ->
|
||||
descriptor.getValueParameters()
|
||||
.map { DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(it.getType()) }
|
||||
.joinToString(", ", "(", ")")
|
||||
} ?: ""
|
||||
return "$name$params"
|
||||
}
|
||||
|
||||
private fun PsiElement.renderText(): String {
|
||||
return StringUtil.shortenTextWithEllipsis(getText()!!.collapseSpaces(), 53, 0)
|
||||
}
|
||||
|
||||
private fun PsiElement.getRepresentativeElement(): PsiElement {
|
||||
return when (this) {
|
||||
is JetBlockExpression -> (getParent() as? JetDeclarationWithBody) ?: this
|
||||
is JetClassBody -> getParent() as JetClassOrObject
|
||||
else -> this
|
||||
}
|
||||
}
|
||||
|
||||
override fun getElementText(element: PsiElement): String? {
|
||||
val representativeElement = element.getRepresentativeElement()
|
||||
return representativeElement.renderDeclaration() ?: representativeElement.renderText()
|
||||
}
|
||||
|
||||
override fun getContainerText(element: PsiElement, name: String?): String? = null
|
||||
|
||||
override fun getIconFlags(): Int = 0
|
||||
|
||||
override fun getIcon(element: PsiElement): Icon? =
|
||||
super.getIcon(element.getRepresentativeElement())
|
||||
},
|
||||
title,
|
||||
highlightSelection,
|
||||
toPsi,
|
||||
{
|
||||
onSelect(it)
|
||||
true
|
||||
}
|
||||
).showInBestPositionFor(editor)
|
||||
}
|
||||
|
||||
public fun chooseContainerElementIfNecessary<T>(
|
||||
containers: List<T>,
|
||||
editor: Editor,
|
||||
title: String,
|
||||
highlightSelection: Boolean,
|
||||
toPsi: (T) -> PsiElement,
|
||||
onSelect: (T) -> Unit
|
||||
) {
|
||||
when {
|
||||
containers.isEmpty() -> return
|
||||
containers.size() == 1 || ApplicationManager.getApplication()!!.isUnitTestMode() -> onSelect(containers.first())
|
||||
else -> chooseContainerElement(containers, editor, title, highlightSelection, toPsi, onSelect)
|
||||
}
|
||||
}
|
||||
|
||||
public fun PsiElement.isTrueJavaMethod(): Boolean = this is PsiMethod && this !is KotlinLightMethod
|
||||
|
||||
fun compareDescriptors(project: Project, d1: DeclarationDescriptor?, d2: DeclarationDescriptor?): Boolean {
|
||||
if (d1 == d2) return true
|
||||
if (d1 == null || d2 == null) return false
|
||||
if (DescriptorToSourceUtilsIde.getAllDeclarations(project, d1) == DescriptorToSourceUtilsIde.getAllDeclarations(project, d2)) return true
|
||||
return DescriptorRenderer.FQ_NAMES_IN_TYPES.render(d1) == DescriptorRenderer.FQ_NAMES_IN_TYPES.render(d2)
|
||||
}
|
||||
|
||||
public fun comparePossiblyOverridingDescriptors(project: Project,
|
||||
currentDescriptor: DeclarationDescriptor?,
|
||||
originalDescriptor: DeclarationDescriptor?): Boolean {
|
||||
if (compareDescriptors(project, currentDescriptor, originalDescriptor)) return true
|
||||
if (originalDescriptor is CallableDescriptor) {
|
||||
if (!OverridingUtil.traverseOverridenDescriptors(originalDescriptor) { !compareDescriptors(project, currentDescriptor, it) }) return true
|
||||
if (originalDescriptor !is CallableMemberDescriptor || currentDescriptor !is CallableMemberDescriptor) return false
|
||||
val kind = originalDescriptor.getKind()
|
||||
if (kind != Kind.FAKE_OVERRIDE && kind != Kind.DELEGATION) return false
|
||||
if (currentDescriptor.getKind() != kind) return false
|
||||
|
||||
val originalOverriddenDescriptors = originalDescriptor.getOverriddenDescriptors()
|
||||
val currentOverriddenDescriptors = currentDescriptor.getOverriddenDescriptors()
|
||||
if (originalOverriddenDescriptors.size() != currentOverriddenDescriptors.size()) return false
|
||||
return (currentOverriddenDescriptors zip originalOverriddenDescriptors ).all {
|
||||
comparePossiblyOverridingDescriptors(project, it.first, it.second)
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
public fun PsiElement.canRefactor(): Boolean {
|
||||
return when {
|
||||
this is PsiPackage ->
|
||||
getDirectories().any { it.canRefactor() }
|
||||
this is JetElement,
|
||||
this is PsiMember && getLanguage() == JavaLanguage.INSTANCE,
|
||||
this is PsiDirectory ->
|
||||
isWritable() && ProjectRootsUtil.isInProjectSource(this)
|
||||
else ->
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
private fun copyModifierListItems(from: PsiModifierList, to: PsiModifierList, withPsiModifiers: Boolean = true) {
|
||||
if (withPsiModifiers) {
|
||||
for (modifier in PsiModifier.MODIFIERS) {
|
||||
if (from.hasExplicitModifier(modifier)) {
|
||||
to.setModifierProperty(modifier, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
for (annotation in from.getAnnotations()) {
|
||||
to.addAnnotation(annotation.getQualifiedName())
|
||||
}
|
||||
}
|
||||
|
||||
private fun copyTypeParameters<T: PsiTypeParameterListOwner>(
|
||||
from: T,
|
||||
to: T,
|
||||
inserter: (T, PsiTypeParameterList) -> Unit
|
||||
) where T : PsiNameIdentifierOwner {
|
||||
val factory = PsiElementFactory.SERVICE.getInstance((from : PsiElement).getProject())
|
||||
val templateTypeParams = from.getTypeParameterList()?.getTypeParameters() ?: PsiTypeParameter.EMPTY_ARRAY
|
||||
if (templateTypeParams.isNotEmpty()) {
|
||||
inserter(to, factory.createTypeParameterList())
|
||||
val targetTypeParamList = to.getTypeParameterList()
|
||||
val newTypeParams = templateTypeParams.map {
|
||||
factory.createTypeParameter(it.getName(), it.getExtendsList().getReferencedTypes())
|
||||
}
|
||||
ChangeSignatureUtil.synchronizeList(
|
||||
targetTypeParamList,
|
||||
newTypeParams,
|
||||
{ it.getTypeParameters().toList() },
|
||||
BooleanArray(newTypeParams.size())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
public fun createJavaMethod(function: JetFunction, targetClass: PsiClass): PsiMethod {
|
||||
val template = LightClassUtil.getLightClassMethod(function)
|
||||
?: throw AssertionError("Can't generate light method: ${JetPsiUtil.getElementTextWithContext(function)}")
|
||||
return createJavaMethod(template, targetClass)
|
||||
}
|
||||
|
||||
public fun createJavaMethod(template: PsiMethod, targetClass: PsiClass): PsiMethod {
|
||||
val factory = PsiElementFactory.SERVICE.getInstance(template.getProject())
|
||||
val methodToAdd = if (template.isConstructor()) {
|
||||
factory.createConstructor(template.getName())
|
||||
}
|
||||
else {
|
||||
factory.createMethod(template.getName(), template.getReturnType())
|
||||
}
|
||||
val method = targetClass.add(methodToAdd) as PsiMethod
|
||||
|
||||
copyModifierListItems(template.getModifierList(), method.getModifierList())
|
||||
|
||||
copyTypeParameters(template, method) { (method, typeParameterList) ->
|
||||
method.addAfter(typeParameterList, method.getModifierList())
|
||||
}
|
||||
|
||||
val targetParamList = method.getParameterList()
|
||||
val newParams = template.getParameterList().getParameters().map {
|
||||
val param = factory.createParameter(it.getName(), it.getType())
|
||||
copyModifierListItems(it.getModifierList(), param.getModifierList())
|
||||
param
|
||||
}
|
||||
ChangeSignatureUtil.synchronizeList(
|
||||
targetParamList,
|
||||
newParams,
|
||||
{ it.getParameters().toList() },
|
||||
BooleanArray(newParams.size())
|
||||
)
|
||||
|
||||
if (template.getModifierList().hasModifierProperty(PsiModifier.ABSTRACT) || targetClass.isInterface()) {
|
||||
method.getBody().delete()
|
||||
}
|
||||
else if (!template.isConstructor()) {
|
||||
CreateFromUsageUtils.setupMethodBody(method)
|
||||
}
|
||||
|
||||
return method
|
||||
}
|
||||
|
||||
fun createJavaField(property: JetProperty, targetClass: PsiClass): PsiField {
|
||||
val template = LightClassUtil.getLightClassPropertyMethods(property).getGetter()
|
||||
?: throw AssertionError("Can't generate light method: ${JetPsiUtil.getElementTextWithContext(property)}")
|
||||
|
||||
val factory = PsiElementFactory.SERVICE.getInstance(template.getProject())
|
||||
val field = targetClass.add(factory.createField(property.getName(), template.getReturnType())) as PsiField
|
||||
|
||||
with(field.getModifierList()) {
|
||||
val templateModifiers = template.getModifierList()
|
||||
setModifierProperty(VisibilityUtil.getVisibilityModifier(templateModifiers), true)
|
||||
if (!property.isVar() || targetClass.isInterface()) {
|
||||
setModifierProperty(PsiModifier.FINAL, true)
|
||||
}
|
||||
copyModifierListItems(templateModifiers, this, false)
|
||||
}
|
||||
|
||||
return field
|
||||
}
|
||||
|
||||
fun createJavaClass(klass: JetClass, targetClass: PsiClass): PsiMember {
|
||||
val kind = (klass.resolveToDescriptor() as ClassDescriptor).getKind()
|
||||
|
||||
val factory = PsiElementFactory.SERVICE.getInstance(klass.getProject())
|
||||
val javaClassToAdd = when (kind) {
|
||||
ClassKind.CLASS -> factory.createClass(klass.getName())
|
||||
ClassKind.TRAIT -> factory.createInterface(klass.getName())
|
||||
ClassKind.ANNOTATION_CLASS -> factory.createAnnotationType(klass.getName())
|
||||
ClassKind.ENUM_CLASS -> factory.createEnum(klass.getName())
|
||||
else -> throw AssertionError("Unexpected class kind: ${JetPsiUtil.getElementTextWithContext(klass)}")
|
||||
}
|
||||
val javaClass = targetClass.add(javaClassToAdd) as PsiClass
|
||||
|
||||
val template = LightClassUtil.getPsiClass(klass)
|
||||
?: throw AssertionError("Can't generate light class: ${JetPsiUtil.getElementTextWithContext(klass)}")
|
||||
|
||||
copyModifierListItems(template.getModifierList(), javaClass.getModifierList())
|
||||
if (template.isInterface()) {
|
||||
javaClass.getModifierList().setModifierProperty(PsiModifier.ABSTRACT, false)
|
||||
}
|
||||
|
||||
copyTypeParameters(template, javaClass) { (klass, typeParameterList) ->
|
||||
klass.addAfter(typeParameterList, klass.getNameIdentifier())
|
||||
}
|
||||
|
||||
val extendsList = factory.createReferenceListWithRole(
|
||||
template.getExtendsList()?.getReferenceElements() ?: PsiJavaCodeReferenceElement.EMPTY_ARRAY,
|
||||
PsiReferenceList.Role.EXTENDS_LIST
|
||||
)
|
||||
extendsList?.let { javaClass.getExtendsList()?.replace(it) }
|
||||
|
||||
val implementsList = factory.createReferenceListWithRole(
|
||||
template.getImplementsList()?.getReferenceElements() ?: PsiJavaCodeReferenceElement.EMPTY_ARRAY,
|
||||
PsiReferenceList.Role.IMPLEMENTS_LIST
|
||||
)
|
||||
implementsList?.let { javaClass.getImplementsList()?.replace(it) }
|
||||
|
||||
for (method in template.getMethods()) {
|
||||
val hasParams = method.getParameterList().getParametersCount() > 0
|
||||
val needSuperCall = !template.isEnum() &&
|
||||
(template.getSuperClass()?.getConstructors() ?: PsiMethod.EMPTY_ARRAY).all {
|
||||
it.getParameterList().getParametersCount() > 0
|
||||
}
|
||||
if (method.isConstructor() && !(hasParams || needSuperCall)) continue
|
||||
with(createJavaMethod(method, javaClass)) {
|
||||
if (isConstructor() && needSuperCall) {
|
||||
getBody().add(factory.createStatementFromText("super();", this))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return javaClass
|
||||
}
|
||||
Reference in New Issue
Block a user