Split module idea into idea, idea-core and idea-completion

This commit is contained in:
Valentin Kipyatkov
2015-04-04 22:11:18 +03:00
parent 203c8ea125
commit 412ab8f8b8
150 changed files with 418 additions and 300 deletions
+19
View File
@@ -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)
}
}
@@ -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;
}
}
@@ -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);
}
}
@@ -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()
@@ -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
}