Convert to Kotlin: KotlinRefactoringUtil2

This commit is contained in:
Alexey Sedunov
2016-08-31 13:28:20 +03:00
parent 1f8dba9c89
commit fc9b28884d
8 changed files with 380 additions and 468 deletions
@@ -44,7 +44,7 @@ public abstract class KotlinUnwrapRemoveBase extends AbstractUnwrapper<KotlinUnw
@Override
public String getDescription(PsiElement e) {
assert e instanceof KtElement;
return KotlinBundle.message(key, KotlinRefactoringUtil2.getExpressionShortText((KtElement) e));
return KotlinBundle.message(key, KotlinRefactoringUtil2.INSTANCE.getExpressionShortText((KtElement) e));
}
protected boolean canExtractExpression(@NotNull KtExpression expression, @NotNull KtElement parent) {
@@ -154,7 +154,7 @@ public class KotlinFindClassUsagesDialog extends FindClassUsagesDialog {
: klass.getUserData(ORIGINAL_CLASS);
if (originalClass != null) {
coloredComponent.append(KotlinRefactoringUtil2.formatClass(originalClass));
coloredComponent.append(KotlinRefactoringUtil2.INSTANCE.formatClass(originalClass));
}
}
@@ -50,7 +50,7 @@ public class KotlinFindFunctionUsagesDialog extends FindMethodUsagesDialog {
@Override
public void configureLabelComponent(@NotNull SimpleColoredComponent coloredComponent) {
coloredComponent.append(KotlinRefactoringUtil2.formatJavaOrLightMethod((PsiMethod) myPsiElement));
coloredComponent.append(KotlinRefactoringUtil2.INSTANCE.formatJavaOrLightMethod((PsiMethod) myPsiElement));
}
@Override
@@ -14,585 +14,497 @@
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.refactoring;
package org.jetbrains.kotlin.idea.refactoring
import com.intellij.codeInsight.unwrap.ScopeHighlighter;
import com.intellij.ide.IdeBundle;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.ui.popup.JBPopupAdapter;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.ui.popup.LightweightWindowEvent;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.search.searches.OverridingMethodsSearch;
import com.intellij.psi.util.PsiFormatUtil;
import com.intellij.psi.util.PsiFormatUtilBase;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.ui.components.JBList;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import kotlin.collections.CollectionsKt;
import kotlin.jvm.functions.Function1;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.asJava.LightClassUtilsKt;
import org.jetbrains.kotlin.asJava.elements.KtLightMethod;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.descriptors.CallableDescriptor;
import org.jetbrains.kotlin.descriptors.ClassDescriptor;
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor;
import org.jetbrains.kotlin.idea.KotlinBundle;
import org.jetbrains.kotlin.idea.caches.resolve.ResolutionUtils;
import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils;
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde;
import org.jetbrains.kotlin.idea.refactoring.introduce.IntroduceUtilKt;
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.psi.psiUtil.KtPsiUtilKt;
import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt;
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.lazy.BodyResolveMode;
import org.jetbrains.kotlin.types.KotlinType;
import com.intellij.codeInsight.unwrap.ScopeHighlighter
import com.intellij.ide.IdeBundle
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.ui.Messages
import com.intellij.openapi.ui.popup.JBPopupAdapter
import com.intellij.openapi.ui.popup.JBPopupFactory
import com.intellij.openapi.ui.popup.LightweightWindowEvent
import com.intellij.openapi.util.text.StringUtil
import com.intellij.psi.*
import com.intellij.psi.search.searches.OverridingMethodsSearch
import com.intellij.psi.util.PsiFormatUtil
import com.intellij.psi.util.PsiFormatUtilBase
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.ui.components.JBList
import com.intellij.util.containers.ContainerUtil
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
import org.jetbrains.kotlin.asJava.unwrapped
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
import org.jetbrains.kotlin.idea.KotlinBundle
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
import org.jetbrains.kotlin.idea.refactoring.introduce.findExpressionOrStringFragment
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getNextSiblingIgnoringWhitespaceAndComments
import org.jetbrains.kotlin.psi.psiUtil.getPrevSiblingIgnoringWhitespaceAndComments
import org.jetbrains.kotlin.psi.psiUtil.parameterIndex
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.lazy.BodyResolveMode
import java.awt.Component
import java.lang.RuntimeException
import java.util.*
import javax.swing.DefaultListCellRenderer
import javax.swing.DefaultListModel
import javax.swing.JList
import javax.swing.event.ListSelectionListener
import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import java.awt.*;
import java.util.*;
import java.util.List;
object KotlinRefactoringUtil2 {
public class KotlinRefactoringUtil2 {
private KotlinRefactoringUtil2() {
fun wrapOrSkip(s: String, inCode: Boolean): String {
return if (inCode) "<code>$s</code>" else s
}
@NotNull
public static String wrapOrSkip(@NotNull String s, boolean inCode) {
return inCode ? "<code>" + s + "</code>" : s;
fun formatClassDescriptor(classDescriptor: DeclarationDescriptor): String {
return IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.render(classDescriptor)
}
@NotNull
public static String formatClassDescriptor(@NotNull DeclarationDescriptor classDescriptor) {
return IdeDescriptorRenderers.SOURCE_CODE_SHORT_NAMES_IN_TYPES.render(classDescriptor);
}
fun formatPsiClass(
psiClass: PsiClass,
markAsJava: Boolean,
inCode: Boolean): String {
var description: String
@NotNull
public static String formatPsiClass(
@NotNull PsiClass psiClass,
boolean markAsJava,
boolean inCode
) {
String description;
String kind = psiClass.isInterface() ? "interface " : "class ";
val kind = if (psiClass.isInterface) "interface " else "class "
description = kind + PsiFormatUtil.formatClass(
psiClass,
PsiFormatUtilBase.SHOW_CONTAINING_CLASS
| PsiFormatUtilBase.SHOW_NAME
| PsiFormatUtilBase.SHOW_PARAMETERS
| PsiFormatUtilBase.SHOW_TYPE
);
description = wrapOrSkip(description, inCode);
PsiFormatUtilBase.SHOW_CONTAINING_CLASS or PsiFormatUtilBase.SHOW_NAME or PsiFormatUtilBase.SHOW_PARAMETERS or PsiFormatUtilBase.SHOW_TYPE)
description = wrapOrSkip(description, inCode)
return markAsJava ? "[Java] " + description : description;
return if (markAsJava) "[Java] " + description else description
}
@NotNull
public static List<? extends PsiElement> checkSuperMethods(
@NotNull KtDeclaration declaration,
@Nullable Collection<PsiElement> ignore,
@NotNull String actionStringKey
) {
BindingContext bindingContext = ResolutionUtils.analyze(declaration, BodyResolveMode.FULL);
fun checkSuperMethods(
declaration: KtDeclaration,
ignore: Collection<PsiElement>?,
actionStringKey: String): List<PsiElement> {
val bindingContext = declaration.analyze(BodyResolveMode.FULL)
CallableDescriptor declarationDescriptor =
(CallableDescriptor)bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, declaration);
val declarationDescriptor = bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, declaration) as CallableDescriptor?
if (declarationDescriptor == null || declarationDescriptor instanceof LocalVariableDescriptor) {
return Collections.singletonList(declaration);
if (declarationDescriptor == null || declarationDescriptor is LocalVariableDescriptor) {
return listOf(declaration)
}
Project project = declaration.getProject();
Map<PsiElement, CallableDescriptor> overriddenElementsToDescriptor = new HashMap<PsiElement, CallableDescriptor>();
for (CallableDescriptor overriddenDescriptor : DescriptorUtils.getAllOverriddenDescriptors(declarationDescriptor)) {
PsiElement overriddenDeclaration = DescriptorToSourceUtilsIde.INSTANCE.getAnyDeclaration(project, overriddenDescriptor);
if (PsiTreeUtil.instanceOf(overriddenDeclaration, KtNamedFunction.class, KtProperty.class, PsiMethod.class)) {
overriddenElementsToDescriptor.put(overriddenDeclaration, overriddenDescriptor);
val project = declaration.project
val overriddenElementsToDescriptor = HashMap<PsiElement, CallableDescriptor>()
for (overriddenDescriptor in DescriptorUtils.getAllOverriddenDescriptors(declarationDescriptor)) {
val overriddenDeclaration = DescriptorToSourceUtilsIde.getAnyDeclaration(project, overriddenDescriptor) ?: continue
if (PsiTreeUtil.instanceOf(overriddenDeclaration, KtNamedFunction::class.java, KtProperty::class.java, PsiMethod::class.java)) {
overriddenElementsToDescriptor.put(overriddenDeclaration, overriddenDescriptor)
}
}
if (ignore != null) {
overriddenElementsToDescriptor.keySet().removeAll(ignore);
overriddenElementsToDescriptor.keys.removeAll(ignore)
}
if (overriddenElementsToDescriptor.isEmpty()) return Collections.singletonList(declaration);
if (overriddenElementsToDescriptor.isEmpty()) return listOf(declaration)
List<String> superClasses = getClassDescriptions(overriddenElementsToDescriptor);
return askUserForMethodsToSearch(declaration, declarationDescriptor, overriddenElementsToDescriptor, superClasses, actionStringKey);
val superClasses = getClassDescriptions(overriddenElementsToDescriptor)
return askUserForMethodsToSearch(declaration, declarationDescriptor, overriddenElementsToDescriptor, superClasses, actionStringKey)
}
@NotNull
private static List<? extends PsiElement> askUserForMethodsToSearch(
@NotNull KtDeclaration declaration,
@NotNull CallableDescriptor declarationDescriptor,
@NotNull Map<PsiElement, CallableDescriptor> overriddenElementsToDescriptor,
@NotNull List<String> superClasses,
@NotNull String actionStringKey
) {
if (ApplicationManager.getApplication().isUnitTestMode()) {
return ContainerUtil.newArrayList(overriddenElementsToDescriptor.keySet());
private fun askUserForMethodsToSearch(
declaration: KtDeclaration,
declarationDescriptor: CallableDescriptor,
overriddenElementsToDescriptor: Map<PsiElement, CallableDescriptor>,
superClasses: List<String>,
actionStringKey: String): List<PsiElement> {
if (ApplicationManager.getApplication().isUnitTestMode) {
return ContainerUtil.newArrayList(overriddenElementsToDescriptor.keys)
}
String superClassesStr = "\n" + StringUtil.join(superClasses, "");
String message = KotlinBundle.message(
val superClassesStr = "\n" + StringUtil.join(superClasses, "")
val message = KotlinBundle.message(
"x.overrides.y.in.class.list",
DescriptorRenderer.COMPACT_WITH_SHORT_TYPES.render(declarationDescriptor),
superClassesStr,
KotlinBundle.message(actionStringKey)
);
KotlinBundle.message(actionStringKey))
int exitCode = Messages.showYesNoCancelDialog(declaration.getProject(), message, IdeBundle.message("title.warning"), Messages.getQuestionIcon());
switch (exitCode) {
case Messages.YES:
return ContainerUtil.newArrayList(overriddenElementsToDescriptor.keySet());
case Messages.NO:
return Collections.singletonList(declaration);
default:
return Collections.emptyList();
val exitCode = Messages.showYesNoCancelDialog(declaration.project, message, IdeBundle.message("title.warning"), Messages.getQuestionIcon())
when (exitCode) {
Messages.YES -> return ContainerUtil.newArrayList(overriddenElementsToDescriptor.keys)
Messages.NO -> return listOf(declaration)
else -> return emptyList()
}
}
@NotNull
private static List<String> getClassDescriptions(@NotNull Map<PsiElement, CallableDescriptor> overriddenElementsToDescriptor) {
return ContainerUtil.map(
overriddenElementsToDescriptor.entrySet(),
new Function<Map.Entry<PsiElement, CallableDescriptor>, String>() {
@Override
public String fun(Map.Entry<PsiElement, CallableDescriptor> entry) {
String description;
private fun getClassDescriptions(overriddenElementsToDescriptor: Map<PsiElement, CallableDescriptor>): List<String> {
return overriddenElementsToDescriptor.entries.map { entry ->
val description: String
PsiElement element = entry.getKey();
CallableDescriptor descriptor = entry.getValue();
if (element instanceof KtNamedFunction || element instanceof KtProperty) {
description = formatClassDescriptor(descriptor.getContainingDeclaration());
}
else {
assert element instanceof PsiMethod : "Invalid element: " + element.getText();
PsiClass psiClass = ((PsiMethod) element).getContainingClass();
assert psiClass != null : "Invalid element: " + element.getText();
description = formatPsiClass(psiClass, true, false);
}
return " " + description + "\n";
}
}
);
}
@NotNull
public static String formatClass(@NotNull DeclarationDescriptor classDescriptor, boolean inCode) {
PsiElement element = DescriptorToSourceUtils.descriptorToDeclaration(classDescriptor);
if (element instanceof PsiClass) {
return formatPsiClass((PsiClass) element, false, inCode);
}
return wrapOrSkip(formatClassDescriptor(classDescriptor), inCode);
}
@NotNull
public static String formatFunction(@NotNull DeclarationDescriptor functionDescriptor, boolean inCode) {
PsiElement element = DescriptorToSourceUtils.descriptorToDeclaration(functionDescriptor);
if (element instanceof PsiMethod) {
return formatPsiMethod((PsiMethod) element, false, inCode);
}
return wrapOrSkip(formatFunctionDescriptor(functionDescriptor), inCode);
}
@NotNull
private static String formatFunctionDescriptor(@NotNull DeclarationDescriptor functionDescriptor) {
return DescriptorRenderer.COMPACT.render(functionDescriptor);
}
@NotNull
public static String formatPsiMethod(
@NotNull PsiMethod psiMethod,
boolean showContainingClass,
boolean inCode) {
int options = PsiFormatUtilBase.SHOW_NAME | PsiFormatUtilBase.SHOW_PARAMETERS | PsiFormatUtilBase.SHOW_TYPE;
if (showContainingClass) {
//noinspection ConstantConditions
options |= PsiFormatUtilBase.SHOW_CONTAINING_CLASS;
}
String description = PsiFormatUtil.formatMethod(psiMethod, PsiSubstitutor.EMPTY, options, PsiFormatUtilBase.SHOW_TYPE);
description = wrapOrSkip(description, inCode);
return "[Java] " + description;
}
@NotNull
public static String formatJavaOrLightMethod(@NotNull PsiMethod method) {
PsiElement originalDeclaration = LightClassUtilsKt.getUnwrapped(method);
if (originalDeclaration instanceof KtDeclaration) {
KtDeclaration ktDeclaration = (KtDeclaration) originalDeclaration;
BindingContext bindingContext = ResolutionUtils.analyze(ktDeclaration, BodyResolveMode.FULL);
DeclarationDescriptor descriptor = bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, ktDeclaration);
if (descriptor != null) return formatFunctionDescriptor(descriptor);
}
return formatPsiMethod(method, false, false);
}
@NotNull
public static String formatClass(@NotNull KtClassOrObject classOrObject) {
BindingContext bindingContext = ResolutionUtils.analyze(classOrObject, BodyResolveMode.FULL);
DeclarationDescriptor descriptor = bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, classOrObject);
if (descriptor instanceof ClassDescriptor) return formatClassDescriptor(descriptor);
return "class " + classOrObject.getName();
}
@Nullable
public static Collection<? extends PsiElement> checkParametersInMethodHierarchy(@NotNull PsiParameter parameter) {
PsiMethod method = (PsiMethod)parameter.getDeclarationScope();
Set<PsiElement> parametersToDelete = collectParametersHierarchy(method, parameter);
if (parametersToDelete.size() > 1) {
if (ApplicationManager.getApplication().isUnitTestMode()) {
return parametersToDelete;
}
String message =
KotlinBundle.message("delete.param.in.method.hierarchy", formatJavaOrLightMethod(method));
int exitCode = Messages.showOkCancelDialog(
parameter.getProject(), message, IdeBundle.message("title.warning"), Messages.getQuestionIcon()
);
if (exitCode == Messages.OK) {
return parametersToDelete;
val element = entry.key
val descriptor = entry.value
if (element is KtNamedFunction || element is KtProperty) {
description = formatClassDescriptor(descriptor.getContainingDeclaration())
}
else {
return null;
assert(element is PsiMethod) { "Invalid element: " + element.getText() }
val psiClass = (element as PsiMethod).containingClass ?: error("Invalid element: " + element.getText())
description = formatPsiClass(psiClass, true, false)
}
" " + description + "\n"
}
}
fun formatClass(classDescriptor: DeclarationDescriptor, inCode: Boolean): String {
val element = DescriptorToSourceUtils.descriptorToDeclaration(classDescriptor)
if (element is PsiClass) {
return formatPsiClass((element as PsiClass?)!!, false, inCode)
}
return wrapOrSkip(formatClassDescriptor(classDescriptor), inCode)
}
fun formatFunction(functionDescriptor: DeclarationDescriptor, inCode: Boolean): String {
val element = DescriptorToSourceUtils.descriptorToDeclaration(functionDescriptor)
if (element is PsiMethod) {
return formatPsiMethod((element as PsiMethod?)!!, false, inCode)
}
return wrapOrSkip(formatFunctionDescriptor(functionDescriptor), inCode)
}
private fun formatFunctionDescriptor(functionDescriptor: DeclarationDescriptor): String {
return DescriptorRenderer.COMPACT.render(functionDescriptor)
}
fun formatPsiMethod(
psiMethod: PsiMethod,
showContainingClass: Boolean,
inCode: Boolean): String {
var options = PsiFormatUtilBase.SHOW_NAME or PsiFormatUtilBase.SHOW_PARAMETERS or PsiFormatUtilBase.SHOW_TYPE
if (showContainingClass) {
//noinspection ConstantConditions
options = options or PsiFormatUtilBase.SHOW_CONTAINING_CLASS
}
var description = PsiFormatUtil.formatMethod(psiMethod, PsiSubstitutor.EMPTY, options, PsiFormatUtilBase.SHOW_TYPE)
description = wrapOrSkip(description, inCode)
return "[Java] " + description
}
fun formatJavaOrLightMethod(method: PsiMethod): String {
val originalDeclaration = method.unwrapped
if (originalDeclaration is KtDeclaration) {
val bindingContext = originalDeclaration.analyze(BodyResolveMode.FULL)
val descriptor = bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, originalDeclaration)
if (descriptor != null) return formatFunctionDescriptor(descriptor)
}
return formatPsiMethod(method, false, false)
}
fun formatClass(classOrObject: KtClassOrObject): String {
val bindingContext = classOrObject.analyze(BodyResolveMode.FULL)
val descriptor = bindingContext.get(BindingContext.DECLARATION_TO_DESCRIPTOR, classOrObject)
if (descriptor is ClassDescriptor) return formatClassDescriptor(descriptor)
return "class " + classOrObject.name!!
}
fun checkParametersInMethodHierarchy(parameter: PsiParameter): Collection<PsiElement>? {
val method = parameter.declarationScope as PsiMethod
val parametersToDelete = collectParametersHierarchy(method, parameter)
if (parametersToDelete.size > 1) {
if (ApplicationManager.getApplication().isUnitTestMode) {
return parametersToDelete
}
val message = KotlinBundle.message("delete.param.in.method.hierarchy", formatJavaOrLightMethod(method))
val exitCode = Messages.showOkCancelDialog(
parameter.project, message, IdeBundle.message("title.warning"), Messages.getQuestionIcon())
if (exitCode == Messages.OK) {
return parametersToDelete
}
else {
return null
}
}
return parametersToDelete;
return parametersToDelete
}
// TODO: generalize breadth-first search
@NotNull
private static Set<PsiElement> collectParametersHierarchy(@NotNull PsiMethod method, @NotNull PsiParameter parameter) {
Deque<PsiMethod> queue = new ArrayDeque<PsiMethod>();
Set<PsiMethod> visited = new HashSet<PsiMethod>();
Set<PsiElement> parametersToDelete = new HashSet<PsiElement>();
private fun collectParametersHierarchy(method: PsiMethod, parameter: PsiParameter): Set<PsiElement> {
val queue = ArrayDeque<PsiMethod>()
val visited = HashSet<PsiMethod>()
val parametersToDelete = HashSet<PsiElement>()
queue.add(method);
queue.add(method)
while (!queue.isEmpty()) {
PsiMethod currentMethod = queue.poll();
val currentMethod = queue.poll()
visited.add(currentMethod);
addParameter(currentMethod, parametersToDelete, parameter);
visited.add(currentMethod)
addParameter(currentMethod, parametersToDelete, parameter)
for (PsiMethod superMethod : currentMethod.findSuperMethods(true)) {
for (superMethod in currentMethod.findSuperMethods(true)) {
if (!visited.contains(superMethod)) {
queue.offer(superMethod);
queue.offer(superMethod)
}
}
for (PsiMethod overrider : OverridingMethodsSearch.search(currentMethod)) {
for (overrider in OverridingMethodsSearch.search(currentMethod)) {
if (!visited.contains(overrider)) {
queue.offer(overrider);
queue.offer(overrider)
}
}
}
return parametersToDelete;
return parametersToDelete
}
private static void addParameter(@NotNull PsiMethod method, @NotNull Set<PsiElement> result, @NotNull PsiParameter parameter) {
int parameterIndex = KtPsiUtilKt.parameterIndex(LightClassUtilsKt.getUnwrapped(parameter));
private fun addParameter(method: PsiMethod, result: MutableSet<PsiElement>, parameter: PsiParameter) {
val parameterIndex = parameter.unwrapped!!.parameterIndex()
if (method instanceof KtLightMethod) {
KtDeclaration declaration = ((KtLightMethod) method).getKotlinOrigin();
if (declaration instanceof KtFunction) {
result.add(((KtFunction) declaration).getValueParameters().get(parameterIndex));
if (method is KtLightMethod) {
val declaration = method.kotlinOrigin
if (declaration is KtFunction) {
result.add(declaration.valueParameters[parameterIndex])
}
}
else {
result.add(method.getParameterList().getParameters()[parameterIndex]);
result.add(method.parameterList.parameters[parameterIndex])
}
}
public interface SelectElementCallback {
void run(@Nullable PsiElement element);
@Throws(IntroduceRefactoringException::class)
fun selectElement(
editor: Editor,
file: KtFile,
elementKinds: Collection<CodeInsightUtils.ElementKind>,
callback: (PsiElement?) -> Unit) {
selectElement(editor, file, true, elementKinds, callback)
}
public static void selectElement(
@NotNull Editor editor,
@NotNull KtFile file,
@NotNull Collection<CodeInsightUtils.ElementKind> elementKinds,
@NotNull SelectElementCallback callback
) throws IntroduceRefactoringException {
selectElement(editor, file, true, elementKinds, callback);
}
@Throws(IntroduceRefactoringException::class)
fun selectElement(editor: Editor,
file: KtFile,
failOnEmptySuggestion: Boolean,
elementKinds: Collection<CodeInsightUtils.ElementKind>,
callback: (PsiElement?) -> Unit) {
if (editor.selectionModel.hasSelection()) {
var selectionStart = editor.selectionModel.selectionStart
var selectionEnd = editor.selectionModel.selectionEnd
public static void selectElement(@NotNull Editor editor,
@NotNull KtFile file,
boolean failOnEmptySuggestion,
@NotNull Collection<CodeInsightUtils.ElementKind> elementKinds,
@NotNull SelectElementCallback callback
) throws IntroduceRefactoringException {
if (editor.getSelectionModel().hasSelection()) {
int selectionStart = editor.getSelectionModel().getSelectionStart();
int selectionEnd = editor.getSelectionModel().getSelectionEnd();
var firstElement: PsiElement = file.findElementAt(selectionStart)!!
var lastElement: PsiElement = file.findElementAt(selectionEnd - 1)!!
PsiElement firstElement = file.findElementAt(selectionStart);
PsiElement lastElement = file.findElementAt(selectionEnd - 1);
if (PsiTreeUtil.getParentOfType(firstElement, KtLiteralStringTemplateEntry.class, KtEscapeStringTemplateEntry.class) == null
&& PsiTreeUtil.getParentOfType(lastElement, KtLiteralStringTemplateEntry.class, KtEscapeStringTemplateEntry.class) == null) {
firstElement = PsiUtilsKt.getNextSiblingIgnoringWhitespaceAndComments(firstElement, true);
lastElement = PsiUtilsKt.getPrevSiblingIgnoringWhitespaceAndComments(lastElement, true);
selectionStart = firstElement.getTextRange().getStartOffset();
selectionEnd = lastElement.getTextRange().getEndOffset();
if (PsiTreeUtil.getParentOfType(firstElement, KtLiteralStringTemplateEntry::class.java, KtEscapeStringTemplateEntry::class.java) == null && PsiTreeUtil.getParentOfType(lastElement, KtLiteralStringTemplateEntry::class.java, KtEscapeStringTemplateEntry::class.java) == null) {
firstElement = firstElement.getNextSiblingIgnoringWhitespaceAndComments(true)!!
lastElement = lastElement.getPrevSiblingIgnoringWhitespaceAndComments(true)!!
selectionStart = firstElement.textRange.startOffset
selectionEnd = lastElement.textRange.endOffset
}
for (CodeInsightUtils.ElementKind elementKind : elementKinds) {
PsiElement element = findElement(file, selectionStart, selectionEnd, failOnEmptySuggestion, elementKind);
for (elementKind in elementKinds) {
val element = findElement(file, selectionStart, selectionEnd, failOnEmptySuggestion, elementKind)
if (element != null) {
callback.run(element);
return;
callback(element)
return
}
}
callback.run(null);
callback(null)
}
else {
int offset = editor.getCaretModel().getOffset();
smartSelectElement(editor, file, offset, failOnEmptySuggestion, elementKinds, callback);
val offset = editor.caretModel.offset
smartSelectElement(editor, file, offset, failOnEmptySuggestion, elementKinds, callback)
}
}
public static List<KtElement> getSmartSelectSuggestions(
@NotNull PsiFile file,
int offset,
@NotNull CodeInsightUtils.ElementKind elementKind
) throws IntroduceRefactoringException {
@Throws(IntroduceRefactoringException::class)
fun getSmartSelectSuggestions(
file: PsiFile,
offset: Int,
elementKind: CodeInsightUtils.ElementKind): List<KtElement> {
if (offset < 0) {
return new ArrayList<KtElement>();
return ArrayList()
}
PsiElement element = file.findElementAt(offset);
if (element == null) {
return new ArrayList<KtElement>();
}
if (element instanceof PsiWhiteSpace) {
return getSmartSelectSuggestions(file, offset - 1, elementKind);
var element: PsiElement? = file.findElementAt(offset) ?: return ArrayList()
if (element is PsiWhiteSpace) {
return getSmartSelectSuggestions(file, offset - 1, elementKind)
}
List<KtElement> elements = new ArrayList<KtElement>();
while (element != null && !(element instanceof KtBlockExpression && !(element.getParent() instanceof KtFunctionLiteral)) &&
!(element instanceof KtNamedFunction)
&& !(element instanceof KtClassBody)) {
boolean addElement = false;
boolean keepPrevious = true;
val elements = ArrayList<KtElement>()
while (element != null && !(element is KtBlockExpression && element.parent !is KtFunctionLiteral) &&
element !is KtNamedFunction
&& element !is KtClassBody) {
var addElement = false
var keepPrevious = true
if (element instanceof KtTypeElement) {
addElement = elementKind == CodeInsightUtils.ElementKind.TYPE_ELEMENT;
if (element is KtTypeElement) {
addElement = elementKind == CodeInsightUtils.ElementKind.TYPE_ELEMENT
if (!addElement) {
keepPrevious = false;
keepPrevious = false
}
}
else if (element instanceof KtExpression && !(element instanceof KtStatementExpression)) {
addElement = elementKind == CodeInsightUtils.ElementKind.EXPRESSION;
else if (element is KtExpression && element !is KtStatementExpression) {
addElement = elementKind == CodeInsightUtils.ElementKind.EXPRESSION
if (addElement) {
if (element instanceof KtParenthesizedExpression) {
addElement = false;
if (element is KtParenthesizedExpression) {
addElement = false
}
else if (KtPsiUtil.isLabelIdentifierExpression(element)) {
addElement = false;
addElement = false
}
else if (element.getParent() instanceof KtQualifiedExpression) {
KtQualifiedExpression qualifiedExpression = (KtQualifiedExpression) element.getParent();
if (qualifiedExpression.getReceiverExpression() != element) {
addElement = false;
else if (element.parent is KtQualifiedExpression) {
val qualifiedExpression = element.parent as KtQualifiedExpression
if (qualifiedExpression.receiverExpression !== element) {
addElement = false
}
}
else if (element.getParent() instanceof KtCallElement
|| element.getParent() instanceof KtThisExpression
|| PsiTreeUtil.getParentOfType(element, KtSuperExpression.class) != null) {
addElement = false;
else if (element.parent is KtCallElement
|| element.parent is KtThisExpression
|| PsiTreeUtil.getParentOfType(element, KtSuperExpression::class.java) != null) {
addElement = false
}
else if (element.getParent() instanceof KtOperationExpression) {
KtOperationExpression operationExpression = (KtOperationExpression) element.getParent();
if (operationExpression.getOperationReference() == element) {
addElement = false;
else if (element.parent is KtOperationExpression) {
val operationExpression = element.parent as KtOperationExpression
if (operationExpression.operationReference === element) {
addElement = false
}
}
if (addElement) {
KtExpression expression = (KtExpression)element;
BindingContext bindingContext = ResolutionUtils.analyze(expression, BodyResolveMode.FULL);
KotlinType expressionType = bindingContext.getType(expression);
val bindingContext = element.analyze(BodyResolveMode.FULL)
val expressionType = bindingContext.getType(element)
if (expressionType == null || KotlinBuiltIns.isUnit(expressionType)) {
addElement = false;
addElement = false
}
}
}
}
if (addElement) {
elements.add((KtElement) element);
elements.add(element as KtElement)
}
if (!keepPrevious) {
elements.clear();
elements.clear()
}
element = element.getParent();
element = element.parent
}
return elements;
return elements
}
private static void smartSelectElement(
@NotNull Editor editor,
@NotNull final PsiFile file,
final int offset,
boolean failOnEmptySuggestion,
@NotNull Collection<CodeInsightUtils.ElementKind> elementKinds,
@NotNull final SelectElementCallback callback
) throws IntroduceRefactoringException {
List<KtElement> elements = CollectionsKt.flatMap(
elementKinds,
new Function1<CodeInsightUtils.ElementKind, Iterable<? extends KtElement>>() {
@Override
public Iterable<? extends KtElement> invoke(CodeInsightUtils.ElementKind kind) {
return getSmartSelectSuggestions(file, offset, kind);
}
@Throws(IntroduceRefactoringException::class)
private fun smartSelectElement(
editor: Editor,
file: PsiFile,
offset: Int,
failOnEmptySuggestion: Boolean,
elementKinds: Collection<CodeInsightUtils.ElementKind>,
callback: (PsiElement?) -> Unit) {
val elements = elementKinds.flatMap { kind -> getSmartSelectSuggestions(file, offset, kind) }
if (elements.size == 0) {
if (failOnEmptySuggestion)
throw IntroduceRefactoringException(
KotlinRefactoringBundle.message("cannot.refactor.not.expression"))
callback(null)
return
}
if (elements.size == 1 || ApplicationManager.getApplication().isUnitTestMode) {
callback(elements[0])
return
}
val model = DefaultListModel<PsiElement>()
for (element in elements) {
model.addElement(element)
}
val highlighter = ScopeHighlighter(editor)
val list = JBList(model)
list.setCellRenderer(object : DefaultListCellRenderer() {
override fun getListCellRendererComponent(list: JList<*>, value: Any?, index: Int, isSelected: Boolean, cellHasFocus: Boolean): Component {
val rendererComponent = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus)
val element = value as KtElement?
if (element!!.isValid) {
text = getExpressionShortText(element)
}
);
if (elements.size() == 0) {
if (failOnEmptySuggestion) throw new IntroduceRefactoringException(
KotlinRefactoringBundle.message("cannot.refactor.not.expression"));
callback.run(null);
return;
}
if (elements.size() == 1 || ApplicationManager.getApplication().isUnitTestMode()) {
callback.run(elements.get(0));
return;
}
final DefaultListModel model = new DefaultListModel();
for (PsiElement element : elements) {
model.addElement(element);
}
final ScopeHighlighter highlighter = new ScopeHighlighter(editor);
final JList list = new JBList(model);
list.setCellRenderer(new DefaultListCellRenderer() {
@Override
public Component getListCellRendererComponent(@NotNull JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
Component rendererComponent = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
KtElement element = (KtElement) value;
if (element.isValid()) {
setText(getExpressionShortText(element));
}
return rendererComponent;
return rendererComponent
}
});
})
list.addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(@NotNull ListSelectionEvent e) {
highlighter.dropHighlight();
int selectedIndex = list.getSelectedIndex();
if (selectedIndex < 0) return;
KtElement expression = (KtElement) model.get(selectedIndex);
List<PsiElement> toExtract = new ArrayList<PsiElement>();
toExtract.add(expression);
highlighter.highlight(expression, toExtract);
}
});
list.addListSelectionListener(ListSelectionListener {
highlighter.dropHighlight()
val selectedIndex = list.getSelectedIndex()
if (selectedIndex < 0) return@ListSelectionListener
val toExtract = ArrayList<PsiElement>()
toExtract.add(model.get(selectedIndex))
highlighter.highlight(model.get(selectedIndex), toExtract)
})
String title = "Elements";
if (elementKinds.size() == 1) {
switch (elementKinds.iterator().next()) {
case EXPRESSION:
title = "Expressions";
break;
case TYPE_ELEMENT:
case TYPE_CONSTRUCTOR:
title = "Types";
break;
var title = "Elements"
if (elementKinds.size == 1) {
when (elementKinds.iterator().next()) {
CodeInsightUtils.ElementKind.EXPRESSION -> title = "Expressions"
CodeInsightUtils.ElementKind.TYPE_ELEMENT, CodeInsightUtils.ElementKind.TYPE_CONSTRUCTOR -> title = "Types"
}
}
JBPopupFactory.getInstance().createListPopupBuilder(list).
setTitle(title).setMovable(false).setResizable(false).
setRequestFocus(true).setItemChoosenCallback(new Runnable() {
@Override
public void run() {
callback.run((KtElement) list.getSelectedValue());
JBPopupFactory.getInstance().createListPopupBuilder(list).setTitle(title).setMovable(false).setResizable(false).setRequestFocus(true).setItemChoosenCallback { callback(list.getSelectedValue() as KtElement) }.addListener(object : JBPopupAdapter() {
override fun onClosed(event: LightweightWindowEvent?) {
highlighter.dropHighlight()
}
}).addListener(new JBPopupAdapter() {
@Override
public void onClosed(LightweightWindowEvent event) {
highlighter.dropHighlight();
}
}).createPopup().showInBestPositionFor(editor);
}).createPopup().showInBestPositionFor(editor)
}
public static String getExpressionShortText(@NotNull KtElement element) {
String text = ElementRenderingUtilsKt.renderTrimmed(element);
int firstNewLinePos = text.indexOf('\n');
String trimmedText = text.substring(0, firstNewLinePos != -1 ? firstNewLinePos : Math.min(100, text.length()));
if (trimmedText.length() != text.length()) trimmedText += " ...";
return trimmedText;
fun getExpressionShortText(element: KtElement): String {
val text = element.renderTrimmed()
val firstNewLinePos = text.indexOf('\n')
var trimmedText = text.substring(0, if (firstNewLinePos != -1) firstNewLinePos else Math.min(100, text.length))
if (trimmedText.length != text.length) trimmedText += " ..."
return trimmedText
}
@Nullable
private static PsiElement findElement(
@NotNull KtFile file,
int startOffset,
int endOffset,
boolean failOnNoExpression,
@NotNull CodeInsightUtils.ElementKind elementKind
) throws IntroduceRefactoringException {
PsiElement element = CodeInsightUtils.findElement(file, startOffset, endOffset, elementKind);
@Throws(IntroduceRefactoringException::class)
private fun findElement(
file: KtFile,
startOffset: Int,
endOffset: Int,
failOnNoExpression: Boolean,
elementKind: CodeInsightUtils.ElementKind): PsiElement? {
var element = CodeInsightUtils.findElement(file, startOffset, endOffset, elementKind)
if (element == null && elementKind == CodeInsightUtils.ElementKind.EXPRESSION) {
element = IntroduceUtilKt.findExpressionOrStringFragment(file, startOffset, endOffset);
element = findExpressionOrStringFragment(file, startOffset, endOffset)
}
if (element == null) {
//todo: if it's infix expression => add (), then commit document then return new created expression
if (failOnNoExpression) {
throw new IntroduceRefactoringException(KotlinRefactoringBundle.message("cannot.refactor.not.expression"));
throw IntroduceRefactoringException(KotlinRefactoringBundle.message("cannot.refactor.not.expression"))
}
return null;
return null
}
return element;
return element
}
public static class IntroduceRefactoringException extends RuntimeException {
private final String myMessage;
public IntroduceRefactoringException(String message) {
myMessage = message;
}
@Override
public String getMessage() {
return myMessage;
}
class IntroduceRefactoringException(private val myMessage: String) : RuntimeException() {
override val message: String?
get() = myMessage
}
}
@@ -110,7 +110,7 @@ class KotlinOverridingDialog extends DialogWrapper {
assert element instanceof PsiMethod
: "Method accepts only kotlin functions/properties and java methods, but '" + element.getText() + "' was found";
return KotlinRefactoringUtil2.formatPsiMethod((PsiMethod) element, true, false);
return KotlinRefactoringUtil2.INSTANCE.formatPsiMethod((PsiMethod) element, true, false);
}
@Override
@@ -18,8 +18,9 @@ package org.jetbrains.kotlin.idea;
import com.intellij.psi.PsiElement;
import com.intellij.testFramework.LightCodeInsightTestCase;
import kotlin.Unit;
import kotlin.jvm.functions.Function1;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils;
import org.jetbrains.kotlin.idea.refactoring.KotlinRefactoringUtil2;
import org.jetbrains.kotlin.psi.KtFile;
@@ -34,15 +35,16 @@ public abstract class AbstractExpressionSelectionTest extends LightCodeInsightTe
final String expectedExpression = KotlinTestUtils.getLastCommentInFile((KtFile) getFile());
try {
KotlinRefactoringUtil2.selectElement(
KotlinRefactoringUtil2.INSTANCE.selectElement(
getEditor(),
(KtFile) getFile(),
Collections.singletonList(CodeInsightUtils.ElementKind.EXPRESSION),
new KotlinRefactoringUtil2.SelectElementCallback() {
new Function1<PsiElement, Unit>() {
@Override
public void run(@Nullable PsiElement element) {
public Unit invoke(PsiElement element) {
assertNotNull("Selected expression mustn't be null", element);
assertEquals(expectedExpression, element.getText());
return Unit.INSTANCE;
}
}
);
@@ -34,12 +34,12 @@ public abstract class AbstractSmartSelectionTest extends LightCodeInsightTestCas
configureByFile(path);
String expectedResultText = KotlinTestUtils.getLastCommentInFile((KtFile) getFile());
List<KtElement> elements = KotlinRefactoringUtil2.getSmartSelectSuggestions(
List<KtElement> elements = KotlinRefactoringUtil2.INSTANCE.getSmartSelectSuggestions(
getFile(), getEditor().getCaretModel().getOffset(), CodeInsightUtils.ElementKind.EXPRESSION);
List<String> textualExpressions = new ArrayList<String>();
for (KtElement element : elements) {
textualExpressions.add(KotlinRefactoringUtil2.getExpressionShortText(element));
textualExpressions.add(KotlinRefactoringUtil2.INSTANCE.getExpressionShortText(element));
}
assertEquals(expectedResultText, StringUtil.join(textualExpressions, "\n"));
}
@@ -17,7 +17,6 @@
package org.jetbrains.kotlin.idea.refactoring.nameSuggester
import com.intellij.openapi.util.text.StringUtil
import com.intellij.psi.PsiElement
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils
@@ -30,6 +29,7 @@ import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.test.InTextDirectivesUtils
import org.jetbrains.kotlin.test.KotlinTestUtils
import java.lang.AssertionError
class KotlinNameSuggesterTest : LightCodeInsightFixtureTestCase() {
fun testArrayList() { doTest() }
@@ -90,19 +90,17 @@ class KotlinNameSuggesterTest : LightCodeInsightFixtureTestCase() {
if (withRuntime) {
ConfigLibraryUtil.configureKotlinRuntimeAndSdk(myModule, PluginTestCaseBase.mockJdk())
}
KotlinRefactoringUtil2.selectElement(myFixture.editor, file, listOf(CodeInsightUtils.ElementKind.EXPRESSION), object : KotlinRefactoringUtil2.SelectElementCallback {
override fun run(element: PsiElement?) {
val names = KotlinNameSuggester
.suggestNamesByExpressionAndType(element as KtExpression,
null,
element.analyze(BodyResolveMode.PARTIAL),
{ true },
"value")
.sorted()
val result = StringUtil.join(names, "\n").trim()
assertEquals(expectedResultText, result)
}
})
KotlinRefactoringUtil2.selectElement(myFixture.editor, file, listOf(CodeInsightUtils.ElementKind.EXPRESSION)) {
val names = KotlinNameSuggester
.suggestNamesByExpressionAndType(it as KtExpression,
null,
it.analyze(BodyResolveMode.PARTIAL),
{ true },
"value")
.sorted()
val result = StringUtil.join(names, "\n").trim()
assertEquals(expectedResultText, result)
}
}
catch (e: KotlinRefactoringUtil2.IntroduceRefactoringException) {
throw AssertionError("Failed to find expression: " + e.message)