Move: make minor refactorings and add GUI form for MoveKotlinMethodDialog
This commit is contained in:
committed by
Igor Yakovlev
parent
5515465760
commit
ed67517fb9
+88
@@ -0,0 +1,88 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="org.jetbrains.kotlin.idea.refactoring.move.moveMethod.MoveKotlinMethodDialog">
|
||||||
|
<grid id="27dc6" binding="mainPanel" layout-manager="GridLayoutManager" row-count="3" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||||
|
<margin top="0" left="0" bottom="0" right="0"/>
|
||||||
|
<constraints>
|
||||||
|
<xy x="20" y="20" width="500" height="400"/>
|
||||||
|
</constraints>
|
||||||
|
<properties/>
|
||||||
|
<border type="none"/>
|
||||||
|
<children>
|
||||||
|
<grid id="ea7b1" layout-manager="GridLayoutManager" row-count="3" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||||
|
<margin top="0" left="0" bottom="0" right="0"/>
|
||||||
|
<constraints>
|
||||||
|
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
|
||||||
|
</constraints>
|
||||||
|
<properties/>
|
||||||
|
<border type="none"/>
|
||||||
|
<children>
|
||||||
|
<component id="2ea99" class="javax.swing.JRadioButton" binding="toObjectRadioButton">
|
||||||
|
<constraints>
|
||||||
|
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="9" fill="0" indent="0" use-parent-layout="false"/>
|
||||||
|
</constraints>
|
||||||
|
<properties>
|
||||||
|
<text resource-bundle="messages/KotlinBundle" key="label.text.to.object"/>
|
||||||
|
</properties>
|
||||||
|
</component>
|
||||||
|
<grid id="eacad" binding="targetObjectChooserPanel" layout-manager="BorderLayout" hgap="0" vgap="0">
|
||||||
|
<constraints>
|
||||||
|
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="1" fill="1" indent="0" use-parent-layout="false"/>
|
||||||
|
</constraints>
|
||||||
|
<properties/>
|
||||||
|
<border type="none"/>
|
||||||
|
<children/>
|
||||||
|
</grid>
|
||||||
|
<grid id="87410" binding="parametersPanel" layout-manager="BorderLayout" hgap="0" vgap="0">
|
||||||
|
<constraints>
|
||||||
|
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="1" fill="1" indent="0" use-parent-layout="false"/>
|
||||||
|
</constraints>
|
||||||
|
<properties/>
|
||||||
|
<border type="none"/>
|
||||||
|
<children/>
|
||||||
|
</grid>
|
||||||
|
</children>
|
||||||
|
</grid>
|
||||||
|
<grid id="f056" layout-manager="GridLayoutManager" row-count="2" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||||
|
<margin top="0" left="0" bottom="0" right="0"/>
|
||||||
|
<constraints>
|
||||||
|
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
|
||||||
|
</constraints>
|
||||||
|
<properties/>
|
||||||
|
<border type="none"/>
|
||||||
|
<children>
|
||||||
|
<component id="59af5" class="javax.swing.JRadioButton" binding="toClassRadioButton">
|
||||||
|
<constraints>
|
||||||
|
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="9" fill="0" indent="0" use-parent-layout="false"/>
|
||||||
|
</constraints>
|
||||||
|
<properties>
|
||||||
|
<text resource-bundle="messages/KotlinBundle" key="label.text.to.class"/>
|
||||||
|
</properties>
|
||||||
|
</component>
|
||||||
|
<grid id="9bb47" binding="targetVariableListPanel" layout-manager="BorderLayout" hgap="0" vgap="0">
|
||||||
|
<constraints>
|
||||||
|
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="2" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
|
||||||
|
</constraints>
|
||||||
|
<properties/>
|
||||||
|
<border type="none"/>
|
||||||
|
<children/>
|
||||||
|
</grid>
|
||||||
|
</children>
|
||||||
|
</grid>
|
||||||
|
<component id="2229f" class="javax.swing.JCheckBox" binding="openInEditorCheckBox">
|
||||||
|
<constraints>
|
||||||
|
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||||
|
</constraints>
|
||||||
|
<properties>
|
||||||
|
<selected value="true"/>
|
||||||
|
<text resource-bundle="messages/KotlinBundle" key="open.moved.method.in.editor"/>
|
||||||
|
</properties>
|
||||||
|
</component>
|
||||||
|
</children>
|
||||||
|
</grid>
|
||||||
|
<buttonGroups>
|
||||||
|
<group name="group1">
|
||||||
|
<member id="2ea99"/>
|
||||||
|
<member id="59af5"/>
|
||||||
|
</group>
|
||||||
|
</buttonGroups>
|
||||||
|
</form>
|
||||||
+299
@@ -0,0 +1,299 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.refactoring.move.moveMethod;
|
||||||
|
|
||||||
|
import com.intellij.ide.util.TreeClassChooser;
|
||||||
|
import com.intellij.ide.util.TreeJavaClassChooserDialog;
|
||||||
|
import com.intellij.openapi.ui.VerticalFlowLayout;
|
||||||
|
import com.intellij.psi.PsiClass;
|
||||||
|
import com.intellij.psi.PsiElement;
|
||||||
|
import com.intellij.psi.search.GlobalSearchScope;
|
||||||
|
import com.intellij.refactoring.ui.RefactoringDialog;
|
||||||
|
import com.intellij.ui.EditorTextField;
|
||||||
|
import com.intellij.ui.ScrollPaneFactory;
|
||||||
|
import com.intellij.ui.TitledSeparator;
|
||||||
|
import com.intellij.ui.components.JBList;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.jetbrains.kotlin.asJava.LightClassUtilsKt;
|
||||||
|
import org.jetbrains.kotlin.asJava.classes.KtLightClassForSourceDeclaration;
|
||||||
|
import org.jetbrains.kotlin.idea.KotlinBundle;
|
||||||
|
import org.jetbrains.kotlin.idea.KotlinFileType;
|
||||||
|
import org.jetbrains.kotlin.idea.KotlinIconProvider;
|
||||||
|
import org.jetbrains.kotlin.idea.completion.CompletionUtilsKt;
|
||||||
|
import org.jetbrains.kotlin.idea.core.KotlinNameSuggester;
|
||||||
|
import org.jetbrains.kotlin.idea.core.NewDeclarationNameValidator;
|
||||||
|
import org.jetbrains.kotlin.idea.core.completion.DeclarationLookupObject;
|
||||||
|
import org.jetbrains.kotlin.idea.core.completion.PackageLookupObject;
|
||||||
|
import org.jetbrains.kotlin.idea.projectView.KtClassOrObjectTreeNode;
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.KotlinRefactoringUtilKt;
|
||||||
|
import org.jetbrains.kotlin.idea.refactoring.ui.KotlinTypeReferenceEditorComboWithBrowseButton;
|
||||||
|
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers;
|
||||||
|
import org.jetbrains.kotlin.psi.*;
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.KtPsiUtilKt;
|
||||||
|
import org.jetbrains.kotlin.renderer.DescriptorRenderer;
|
||||||
|
import org.jetbrains.kotlin.types.KotlinType;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import javax.swing.tree.DefaultMutableTreeNode;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import static org.jetbrains.kotlin.name.Name.isValidIdentifier;
|
||||||
|
|
||||||
|
public class MoveKotlinMethodDialog extends RefactoringDialog {
|
||||||
|
private static final String RECENTS_KEY = MoveKotlinMethodDialog.class.getName() + ".RECENTS_KEY";
|
||||||
|
private final KtNamedFunction method;
|
||||||
|
private final Map<KtNamedDeclaration, KtClass> variableToClassMap;
|
||||||
|
private final KtNamedDeclaration[] variables;
|
||||||
|
private final Map<KtClass, Set<KtNamedDeclaration>> thisClassesToMembers;
|
||||||
|
private final KtClassOrObject targetContainer;
|
||||||
|
private final Map<KtClass, EditorTextField> oldClassParameterNameFields;
|
||||||
|
private KtNamedDeclaration selectedTarget;
|
||||||
|
private JPanel mainPanel;
|
||||||
|
private JRadioButton toClassRadioButton;
|
||||||
|
private JRadioButton toObjectRadioButton;
|
||||||
|
private JPanel targetObjectChooserPanel;
|
||||||
|
private KotlinTypeReferenceEditorComboWithBrowseButton targetObjectChooser;
|
||||||
|
private JList<KtNamedDeclaration> targetVariableList;
|
||||||
|
private JPanel targetVariableListPanel;
|
||||||
|
private JPanel parametersPanel;
|
||||||
|
private JCheckBox openInEditorCheckBox;
|
||||||
|
|
||||||
|
public MoveKotlinMethodDialog(
|
||||||
|
@NotNull KtNamedFunction method,
|
||||||
|
@NotNull Map<KtNamedDeclaration, KtClass> variableToClassMap,
|
||||||
|
@Nullable KtClassOrObject targetContainer
|
||||||
|
) {
|
||||||
|
super(method.getProject(), true);
|
||||||
|
|
||||||
|
this.method = method;
|
||||||
|
this.variableToClassMap = variableToClassMap;
|
||||||
|
this.targetContainer = targetContainer;
|
||||||
|
this.thisClassesToMembers = MoveKotlinMethodProcessorKt.getThisClassesToMembers(method);
|
||||||
|
this.variables = variableToClassMap.keySet().toArray(new KtNamedDeclaration[0]);
|
||||||
|
this.oldClassParameterNameFields = new HashMap<>();
|
||||||
|
|
||||||
|
init();
|
||||||
|
setTitle(KotlinBundle.message("title.move.method"));
|
||||||
|
initTargetObjectChooser();
|
||||||
|
initTargetVariableList();
|
||||||
|
initParametersPanel();
|
||||||
|
initButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doAction() {
|
||||||
|
if (toClassRadioButton.isSelected()) selectedTarget = targetVariableList.getSelectedValue();
|
||||||
|
if (toObjectRadioButton.isSelected() && selectedTarget == null) {
|
||||||
|
setErrorText(KotlinBundle.message("text.no.destination.object.specified"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<KtClass, String> oldClassParameterNames = new LinkedHashMap<>();
|
||||||
|
for (Map.Entry<KtClass, EditorTextField> entry : oldClassParameterNameFields.entrySet()) {
|
||||||
|
EditorTextField field = entry.getValue();
|
||||||
|
if (!isValidIdentifier(field.getText())) {
|
||||||
|
setErrorText(KotlinBundle.message("parameter.name.is.invalid", field.getText()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (field.isEnabled()) {
|
||||||
|
oldClassParameterNames.put(entry.getKey(), field.getText());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MoveKotlinMethodProcessor processor =
|
||||||
|
new MoveKotlinMethodProcessor(method, selectedTarget, oldClassParameterNames, openInEditorCheckBox.isSelected());
|
||||||
|
invokeRefactoring(processor);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
protected JComponent createCenterPanel() {
|
||||||
|
return mainPanel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initButtons() {
|
||||||
|
toClassRadioButton.addActionListener(e -> enableTargetChooser());
|
||||||
|
toObjectRadioButton.addActionListener(e -> enableTargetChooser());
|
||||||
|
|
||||||
|
if (variables.length != 0 && !(targetContainer instanceof KtObjectDeclaration)) {
|
||||||
|
toClassRadioButton.setSelected(true);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
toObjectRadioButton.setSelected(true);
|
||||||
|
if (variables.length == 0) {
|
||||||
|
toClassRadioButton.setEnabled(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enableTextFields();
|
||||||
|
enableTargetChooser();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initTargetVariableList() {
|
||||||
|
AbstractListModel<KtNamedDeclaration> listModel = new AbstractListModel<KtNamedDeclaration>() {
|
||||||
|
@Override
|
||||||
|
public int getSize() {
|
||||||
|
return variables.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KtNamedDeclaration getElementAt(int index) {
|
||||||
|
return variables[index];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
targetVariableList = new JBList<>(listModel);
|
||||||
|
DefaultListCellRenderer listCellRenderer = new DefaultListCellRenderer() {
|
||||||
|
private final DescriptorRenderer renderer = IdeDescriptorRenderers.SOURCE_CODE_TYPES_WITH_SHORT_NAMES;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Component getListCellRendererComponent(
|
||||||
|
JList<?> list,
|
||||||
|
Object value,
|
||||||
|
int index,
|
||||||
|
boolean isSelected,
|
||||||
|
boolean cellHasFocus
|
||||||
|
) {
|
||||||
|
super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
|
||||||
|
if (value instanceof KtNamedDeclaration) {
|
||||||
|
KtNamedDeclaration variable = (KtNamedDeclaration) value;
|
||||||
|
setIcon(KotlinIconProvider.Companion.getBaseIcon(variable));
|
||||||
|
setText(variable.getName());
|
||||||
|
KotlinType type = MoveKotlinMethodProcessorKt.type(variable);
|
||||||
|
if (type != null) {
|
||||||
|
setText(getText() + ": " + renderer.renderType(type));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
targetVariableList.setCellRenderer(listCellRenderer);
|
||||||
|
targetVariableList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||||
|
|
||||||
|
int defaultVariableIndex = -1;
|
||||||
|
for (int i = 0; i < variables.length; i++) {
|
||||||
|
if (variableToClassMap.get(variables[i]) == targetContainer) {
|
||||||
|
defaultVariableIndex = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
targetVariableList.setSelectedIndex(defaultVariableIndex != -1 ? defaultVariableIndex : 0);
|
||||||
|
targetVariableList.getSelectionModel().addListSelectionListener(e -> enableTextFields());
|
||||||
|
|
||||||
|
JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(targetVariableList);
|
||||||
|
targetVariableListPanel.add(scrollPane);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initTargetObjectChooser() {
|
||||||
|
targetObjectChooser = new KotlinTypeReferenceEditorComboWithBrowseButton(
|
||||||
|
e -> {
|
||||||
|
TreeClassChooser chooser = createTreeClassChooser();
|
||||||
|
chooser.selectDirectory(method.getContainingFile().getContainingDirectory());
|
||||||
|
chooser.showDialog();
|
||||||
|
PsiClass psiClass = chooser.getSelected();
|
||||||
|
if (psiClass instanceof KtLightClassForSourceDeclaration) {
|
||||||
|
selectedTarget = ((KtLightClassForSourceDeclaration) psiClass).getKotlinOrigin();
|
||||||
|
targetObjectChooser.setText(Objects.requireNonNull(psiClass.getQualifiedName()));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(targetContainer == null || targetContainer.getFqName() == null) ? null : targetContainer.getFqName().asString(),
|
||||||
|
targetContainer == null ? Objects.requireNonNull(KtPsiUtilKt.getContainingClassOrObject(method)) : targetContainer,
|
||||||
|
RECENTS_KEY
|
||||||
|
);
|
||||||
|
KtTypeCodeFragment codeFragment = targetObjectChooser.getCodeFragment();
|
||||||
|
if (codeFragment != null) {
|
||||||
|
CompletionUtilsKt.setExtraCompletionFilter(
|
||||||
|
codeFragment,
|
||||||
|
lookupElement -> {
|
||||||
|
Object lookupObject = lookupElement.getObject();
|
||||||
|
if (!(lookupObject instanceof DeclarationLookupObject)) return false;
|
||||||
|
PsiElement psiElement = ((DeclarationLookupObject) lookupObject).getPsiElement();
|
||||||
|
if (lookupObject instanceof PackageLookupObject) return true;
|
||||||
|
return (psiElement instanceof KtObjectDeclaration) && KotlinRefactoringUtilKt.canRefactor(psiElement);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
targetObjectChooserPanel.add(targetObjectChooser);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void enableTextFields() {
|
||||||
|
for (EditorTextField textField : oldClassParameterNameFields.values()) {
|
||||||
|
textField.setEnabled(true);
|
||||||
|
}
|
||||||
|
if (toClassRadioButton.isSelected()) {
|
||||||
|
KtNamedDeclaration variable = variables[targetVariableList.getSelectedIndex()];
|
||||||
|
KtClassOrObject containingClass = KtPsiUtilKt.getContainingClassOrObject(variable);
|
||||||
|
if (!(containingClass instanceof KtClass)) return;
|
||||||
|
if (!(variable instanceof KtParameter) || ((KtParameter) variable).hasValOrVar()) {
|
||||||
|
Set<KtNamedDeclaration> members = thisClassesToMembers.get(containingClass);
|
||||||
|
if (members != null && members.size() == 1 && members.contains(variable)) {
|
||||||
|
EditorTextField field = oldClassParameterNameFields.get(containingClass);
|
||||||
|
if (field != null) field.setEnabled(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void enableTargetChooser() {
|
||||||
|
if (toClassRadioButton.isSelected()) {
|
||||||
|
targetVariableList.setEnabled(true);
|
||||||
|
targetObjectChooser.setEnabled(false);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
targetVariableList.setEnabled(false);
|
||||||
|
targetObjectChooser.setEnabled(true);
|
||||||
|
}
|
||||||
|
enableTextFields();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initParametersPanel() {
|
||||||
|
if (thisClassesToMembers.isEmpty()) return;
|
||||||
|
parametersPanel.setLayout(new VerticalFlowLayout(VerticalFlowLayout.TOP, 0, 0, true, true));
|
||||||
|
NewDeclarationNameValidator validator =
|
||||||
|
new NewDeclarationNameValidator((PsiElement) method, null, NewDeclarationNameValidator.Target.VARIABLES, new ArrayList<>());
|
||||||
|
|
||||||
|
for (KtClass ktClass : thisClassesToMembers.keySet()) {
|
||||||
|
KotlinType type = MoveKotlinMethodProcessorKt.defaultType(ktClass);
|
||||||
|
if (type == null) continue;
|
||||||
|
String text = KotlinBundle.message("text.select.a.name.for.this.parameter", ktClass.getName());
|
||||||
|
parametersPanel.add(new TitledSeparator(text, null));
|
||||||
|
List<String> suggestedNames = KotlinNameSuggester.INSTANCE.suggestNamesByType(type, validator, null);
|
||||||
|
String suggestedName = suggestedNames.isEmpty() ? "parameter" : suggestedNames.get(0);
|
||||||
|
EditorTextField field = new EditorTextField(suggestedName, myProject, KotlinFileType.INSTANCE);
|
||||||
|
oldClassParameterNameFields.put(ktClass, field);
|
||||||
|
parametersPanel.add(field);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private TreeJavaClassChooserDialog createTreeClassChooser() {
|
||||||
|
return new TreeJavaClassChooserDialog(
|
||||||
|
KotlinBundle.message("title.choose.destination.object"),
|
||||||
|
myProject,
|
||||||
|
GlobalSearchScope.projectScope(myProject),
|
||||||
|
aClass -> {
|
||||||
|
if (!(aClass instanceof KtLightClassForSourceDeclaration)) return false;
|
||||||
|
KtClassOrObject ktClassOrObject = ((KtLightClassForSourceDeclaration) aClass).getKotlinOrigin();
|
||||||
|
return ktClassOrObject instanceof KtObjectDeclaration && !((KtObjectDeclaration) ktClassOrObject).isObjectLiteral();
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
true
|
||||||
|
) {
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
protected PsiClass getSelectedFromTreeUserObject(DefaultMutableTreeNode node) {
|
||||||
|
PsiClass psiClass = super.getSelectedFromTreeUserObject(node);
|
||||||
|
if (psiClass != null) return psiClass;
|
||||||
|
Object userObject = node.getUserObject();
|
||||||
|
if (!(userObject instanceof KtClassOrObjectTreeNode)) return null;
|
||||||
|
return LightClassUtilsKt.toLightClass(((KtClassOrObjectTreeNode) userObject).getValue());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
-290
@@ -1,290 +0,0 @@
|
|||||||
package org.jetbrains.kotlin.idea.refactoring.move.moveMethod
|
|
||||||
|
|
||||||
import com.intellij.codeInsight.lookup.LookupElement
|
|
||||||
import com.intellij.ide.util.TreeClassChooser
|
|
||||||
import com.intellij.ide.util.TreeJavaClassChooserDialog
|
|
||||||
import com.intellij.openapi.ui.VerticalFlowLayout
|
|
||||||
import com.intellij.psi.PsiClass
|
|
||||||
import com.intellij.psi.search.GlobalSearchScope
|
|
||||||
import com.intellij.refactoring.ui.RefactoringDialog
|
|
||||||
import com.intellij.ui.EditorTextField
|
|
||||||
import com.intellij.ui.ScrollPaneFactory
|
|
||||||
import com.intellij.ui.TitledSeparator
|
|
||||||
import com.intellij.ui.components.JBList
|
|
||||||
import com.intellij.util.ui.JBUI
|
|
||||||
import com.jetbrains.rd.util.firstOrNull
|
|
||||||
import org.jetbrains.kotlin.asJava.classes.KtLightClassForSourceDeclaration
|
|
||||||
import org.jetbrains.kotlin.asJava.toLightClass
|
|
||||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
|
||||||
import org.jetbrains.kotlin.idea.KotlinBundle
|
|
||||||
import org.jetbrains.kotlin.idea.KotlinFileType
|
|
||||||
import org.jetbrains.kotlin.idea.KotlinIconProvider.Companion.getBaseIcon
|
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
|
|
||||||
import org.jetbrains.kotlin.idea.completion.extraCompletionFilter
|
|
||||||
import org.jetbrains.kotlin.idea.core.KotlinNameSuggester.suggestNamesByType
|
|
||||||
import org.jetbrains.kotlin.idea.core.NewDeclarationNameValidator
|
|
||||||
import org.jetbrains.kotlin.idea.core.completion.DeclarationLookupObject
|
|
||||||
import org.jetbrains.kotlin.idea.core.completion.PackageLookupObject
|
|
||||||
import org.jetbrains.kotlin.idea.projectView.KtClassOrObjectTreeNode
|
|
||||||
import org.jetbrains.kotlin.idea.refactoring.canRefactor
|
|
||||||
import org.jetbrains.kotlin.idea.refactoring.ui.KotlinTypeReferenceEditorComboWithBrowseButton
|
|
||||||
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
|
||||||
import org.jetbrains.kotlin.name.Name.isValidIdentifier
|
|
||||||
import org.jetbrains.kotlin.psi.*
|
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
|
|
||||||
import org.jetbrains.kotlin.renderer.ClassifierNamePolicy
|
|
||||||
import java.awt.Component
|
|
||||||
import java.awt.Dimension
|
|
||||||
import java.awt.GridBagConstraints
|
|
||||||
import java.awt.GridBagLayout
|
|
||||||
import java.awt.event.ActionListener
|
|
||||||
import javax.swing.*
|
|
||||||
import javax.swing.tree.DefaultMutableTreeNode
|
|
||||||
|
|
||||||
|
|
||||||
class MoveKotlinMethodDialog(
|
|
||||||
private val method: KtNamedFunction,
|
|
||||||
private val variableToClassMap: Map<KtNamedDeclaration, KtClass>,
|
|
||||||
private val targetContainer: KtClassOrObject?
|
|
||||||
) : RefactoringDialog(method.project, true) {
|
|
||||||
private val variables = variableToClassMap.keys.toList()
|
|
||||||
private val targetVariableList = createTargetVariableChooser()
|
|
||||||
private val thisClassesToMembers = getThisClassesToMembers(method)
|
|
||||||
private val oldClassParameterNameFields = LinkedHashMap<KtClass, EditorTextField>()
|
|
||||||
private val targetObjectChooser = createTargetObjectChooser()
|
|
||||||
private val toClassRadioButton = JRadioButton(KotlinBundle.message("label.text.to.class"))
|
|
||||||
private val toObjectRadioButton = JRadioButton(KotlinBundle.message("label.text.to.object"))
|
|
||||||
private var selectedTarget: KtNamedDeclaration? = null
|
|
||||||
private val openInEditorCheckBox = JCheckBox(KotlinBundle.message("open.moved.method.in.editor"), true)
|
|
||||||
|
|
||||||
init {
|
|
||||||
super.init()
|
|
||||||
title = KotlinBundle.message("title.move.method")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun doAction() {
|
|
||||||
if (toClassRadioButton.isSelected) selectedTarget = targetVariableList.selectedValue
|
|
||||||
|
|
||||||
if (toObjectRadioButton.isSelected && selectedTarget == null) {
|
|
||||||
setErrorText(KotlinBundle.message("text.no.destination.object.specified"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val oldClassParameterNames = LinkedHashMap<KtClass, String>()
|
|
||||||
for ((ktClass, field) in oldClassParameterNameFields) {
|
|
||||||
if (!isValidIdentifier(field.text)) {
|
|
||||||
setErrorText(KotlinBundle.message("parameter.name.is.invalid", field.text))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (field.isEnabled) {
|
|
||||||
oldClassParameterNames[ktClass] = field.text
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val processor = MoveKotlinMethodProcessor(method, selectedTarget!!, oldClassParameterNames, openInEditorCheckBox.isSelected)
|
|
||||||
invokeRefactoring(processor)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun createCenterPanel(): JComponent? {
|
|
||||||
val mainPanel = JPanel(GridBagLayout())
|
|
||||||
val buttonGroup = ButtonGroup()
|
|
||||||
|
|
||||||
toClassRadioButton.addActionListener { enableTargetChooser() }
|
|
||||||
toObjectRadioButton.addActionListener { enableTargetChooser() }
|
|
||||||
buttonGroup.add(toClassRadioButton)
|
|
||||||
buttonGroup.add(toObjectRadioButton)
|
|
||||||
|
|
||||||
targetVariableList.addListSelectionListener { enableTextFields() }
|
|
||||||
val scrollPane = ScrollPaneFactory.createScrollPane(targetVariableList)
|
|
||||||
mainPanel.add(
|
|
||||||
scrollPane, GridBagConstraints(
|
|
||||||
1, 0, 1, 1, 1.0, 1.0,
|
|
||||||
GridBagConstraints.WEST, GridBagConstraints.BOTH, JBUI.emptyInsets(), 0, 0
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
mainPanel.add(
|
|
||||||
targetObjectChooser, GridBagConstraints(
|
|
||||||
1, GridBagConstraints.RELATIVE, 1, 1, 1.0, 1.0,
|
|
||||||
GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, JBUI.emptyInsets(), 0, 0
|
|
||||||
)
|
|
||||||
)
|
|
||||||
mainPanel.add(
|
|
||||||
toClassRadioButton, GridBagConstraints(
|
|
||||||
0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 1.0,
|
|
||||||
GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, JBUI.emptyInsets(), 0, 0
|
|
||||||
)
|
|
||||||
)
|
|
||||||
mainPanel.add(
|
|
||||||
toObjectRadioButton, GridBagConstraints(
|
|
||||||
0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 1.0,
|
|
||||||
GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, JBUI.emptyInsets(), 0, 0
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
val parametersPanel: JPanel? = createParametersPanel()
|
|
||||||
if (parametersPanel != null) {
|
|
||||||
mainPanel.add(
|
|
||||||
parametersPanel, GridBagConstraints(
|
|
||||||
0, GridBagConstraints.RELATIVE, 2, 1, 1.0, 0.0,
|
|
||||||
GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, JBUI.emptyInsets(), 0, 0
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
mainPanel.add(
|
|
||||||
openInEditorCheckBox, GridBagConstraints(
|
|
||||||
0, GridBagConstraints.RELATIVE, 2, 1, 0.0, 0.0,
|
|
||||||
GridBagConstraints.WEST, GridBagConstraints.NONE, JBUI.emptyInsets(), 0, 0
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
if (variables.isNotEmpty() && targetContainer !is KtObjectDeclaration) {
|
|
||||||
toClassRadioButton.isSelected = true
|
|
||||||
} else {
|
|
||||||
toObjectRadioButton.isSelected = true
|
|
||||||
if (variables.isEmpty()) {
|
|
||||||
toClassRadioButton.isEnabled = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enableTextFields()
|
|
||||||
enableTargetChooser()
|
|
||||||
updateOnChanged(targetVariableList)
|
|
||||||
return mainPanel
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createTargetVariableChooser(): JList<KtNamedDeclaration> {
|
|
||||||
val listModel = object : AbstractListModel<KtNamedDeclaration>() {
|
|
||||||
override fun getElementAt(index: Int): KtNamedDeclaration = variables[index]
|
|
||||||
override fun getSize(): Int = variables.size
|
|
||||||
}
|
|
||||||
val list = JBList(listModel)
|
|
||||||
val listCellRenderer = object : DefaultListCellRenderer() {
|
|
||||||
private val renderer = IdeDescriptorRenderers.SOURCE_CODE_TYPES.withOptions {
|
|
||||||
classifierNamePolicy = ClassifierNamePolicy.SHORT
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getListCellRendererComponent(
|
|
||||||
list: JList<*>?, value: Any?, index: Int, isSelected: Boolean, cellHasFocus: Boolean
|
|
||||||
): Component {
|
|
||||||
super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus)
|
|
||||||
if (value is KtNamedDeclaration) {
|
|
||||||
icon = value.getBaseIcon()
|
|
||||||
text = value.nameAsSafeName.identifier
|
|
||||||
(value.resolveToDescriptorIfAny() as? CallableDescriptor)?.returnType?.let { type ->
|
|
||||||
text = "$text: ${renderer.renderType(type)}"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
list.cellRenderer = listCellRenderer
|
|
||||||
list.selectionMode = ListSelectionModel.SINGLE_SELECTION
|
|
||||||
val defaultVariableIndex = variables.indexOf(variableToClassMap.filter { it.value == targetContainer }.firstOrNull()?.key)
|
|
||||||
list.selectedIndex = if (defaultVariableIndex != -1) defaultVariableIndex else 0
|
|
||||||
list.selectionModel.addListSelectionListener { updateOnChanged(list) }
|
|
||||||
return list
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createTargetObjectChooser(): KotlinTypeReferenceEditorComboWithBrowseButton {
|
|
||||||
val targetObjectChooser = KotlinTypeReferenceEditorComboWithBrowseButton(
|
|
||||||
ActionListener {
|
|
||||||
val chooser: TreeClassChooser = object : TreeJavaClassChooserDialog(
|
|
||||||
KotlinBundle.message("title.choose.destination.object"),
|
|
||||||
project,
|
|
||||||
GlobalSearchScope.projectScope(project),
|
|
||||||
{ psiClass ->
|
|
||||||
if (psiClass !is KtLightClassForSourceDeclaration) {
|
|
||||||
false
|
|
||||||
} else {
|
|
||||||
val ktClassOrObject = psiClass.kotlinOrigin
|
|
||||||
ktClassOrObject is KtObjectDeclaration && !ktClassOrObject.isObjectLiteral()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
true
|
|
||||||
) {
|
|
||||||
override fun getSelectedFromTreeUserObject(node: DefaultMutableTreeNode): PsiClass? {
|
|
||||||
val psiClass = super.getSelectedFromTreeUserObject(node)
|
|
||||||
if (psiClass != null) return psiClass
|
|
||||||
val userObject = node.userObject
|
|
||||||
return if (userObject !is KtClassOrObjectTreeNode) null else userObject.value.toLightClass()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
chooser.selectDirectory(method.containingFile.containingDirectory)
|
|
||||||
chooser.showDialog()
|
|
||||||
val psiClass = chooser.selected
|
|
||||||
if (psiClass is KtLightClassForSourceDeclaration) {
|
|
||||||
selectedTarget = psiClass.kotlinOrigin
|
|
||||||
targetObjectChooser.text = psiClass.kotlinOrigin.fqName.toString()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
(targetContainer ?: method.containingClassOrObject as? KtObjectDeclaration)?.fqName?.asString(),
|
|
||||||
targetContainer ?: method.containingClassOrObject!!,
|
|
||||||
RECENTS_KEY
|
|
||||||
)
|
|
||||||
targetObjectChooser.codeFragment?.let { codeFragment ->
|
|
||||||
codeFragment.extraCompletionFilter = { lookupElement: LookupElement ->
|
|
||||||
val lookupObject = lookupElement.getObject() as? DeclarationLookupObject
|
|
||||||
val psiElement = lookupObject?.psiElement
|
|
||||||
lookupObject is PackageLookupObject || psiElement is KtObjectDeclaration && psiElement.canRefactor()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return targetObjectChooser
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun updateOnChanged(list: JList<*>) {
|
|
||||||
okAction.isEnabled = !list.selectionModel.isSelectionEmpty
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun enableTextFields() {
|
|
||||||
for (textField in oldClassParameterNameFields.values) {
|
|
||||||
textField.isEnabled = true
|
|
||||||
}
|
|
||||||
if (toClassRadioButton.isSelected) {
|
|
||||||
val variable = variables[targetVariableList.selectedIndex]
|
|
||||||
val containingClass = variable.containingClassOrObject as? KtClass ?: return
|
|
||||||
if (variable !is KtParameter || variable.hasValOrVar()) {
|
|
||||||
if (thisClassesToMembers[containingClass]?.size == 1
|
|
||||||
&& thisClassesToMembers[containingClass]?.contains(variable) == true
|
|
||||||
) {
|
|
||||||
oldClassParameterNameFields[containingClass]?.isEnabled = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun enableTargetChooser() {
|
|
||||||
if (toClassRadioButton.isSelected) {
|
|
||||||
targetVariableList.isEnabled = true
|
|
||||||
targetObjectChooser.isEnabled = false
|
|
||||||
} else {
|
|
||||||
targetVariableList.isEnabled = false
|
|
||||||
targetObjectChooser.isEnabled = true
|
|
||||||
}
|
|
||||||
enableTextFields()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createParametersPanel(): JPanel? {
|
|
||||||
if (thisClassesToMembers.isEmpty()) return null
|
|
||||||
val panel = JPanel(VerticalFlowLayout(VerticalFlowLayout.TOP, 0, 0, true, true))
|
|
||||||
val validator = NewDeclarationNameValidator(method, null, NewDeclarationNameValidator.Target.VARIABLES)
|
|
||||||
for (ktClass in thisClassesToMembers.keys) {
|
|
||||||
val text = KotlinBundle.message("text.select.a.name.for.this.parameter", ktClass.nameAsSafeName.identifier)
|
|
||||||
panel.add(TitledSeparator(text, null))
|
|
||||||
val suggestedName = suggestNamesByType(ktClass.resolveToDescriptorIfAny()!!.defaultType, validator).firstOrNull() ?: "parameter"
|
|
||||||
val field = EditorTextField(suggestedName, project, KotlinFileType.INSTANCE)
|
|
||||||
field.minimumSize = Dimension(field.preferredSize)
|
|
||||||
oldClassParameterNameFields[ktClass] = field
|
|
||||||
panel.add(field)
|
|
||||||
}
|
|
||||||
panel.border = BorderFactory.createEmptyBorder(5, 0, 5, 0)
|
|
||||||
return panel
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val RECENTS_KEY = "${MoveKotlinMethodDialog::class.java.name}.RECENTS_KEY"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+25
-19
@@ -9,9 +9,7 @@ import com.intellij.psi.PsiReference
|
|||||||
import com.intellij.refactoring.move.MoveCallback
|
import com.intellij.refactoring.move.MoveCallback
|
||||||
import com.intellij.refactoring.move.MoveHandlerDelegate
|
import com.intellij.refactoring.move.MoveHandlerDelegate
|
||||||
import com.intellij.refactoring.util.CommonRefactoringUtil
|
import com.intellij.refactoring.util.CommonRefactoringUtil
|
||||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
|
||||||
import org.jetbrains.kotlin.idea.KotlinBundle
|
import org.jetbrains.kotlin.idea.KotlinBundle
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
|
|
||||||
import org.jetbrains.kotlin.idea.references.mainReference
|
import org.jetbrains.kotlin.idea.references.mainReference
|
||||||
import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
|
import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
@@ -22,27 +20,38 @@ import org.jetbrains.kotlin.psi.psiUtil.isObjectLiteral
|
|||||||
|
|
||||||
class MoveKotlinMethodHandler : MoveHandlerDelegate() {
|
class MoveKotlinMethodHandler : MoveHandlerDelegate() {
|
||||||
private fun showErrorHint(project: Project, dataContext: DataContext?, message: String) {
|
private fun showErrorHint(project: Project, dataContext: DataContext?, message: String) {
|
||||||
val editor = if (dataContext == null) null else CommonDataKeys.EDITOR.getData(dataContext)
|
val editor = dataContext?.let { CommonDataKeys.EDITOR.getData(it) }
|
||||||
CommonRefactoringUtil.showErrorHint(project, editor, message, KotlinBundle.message("text.move.method"), null)
|
CommonRefactoringUtil.showErrorHint(project, editor, message, KotlinBundle.message("text.move.method"), null)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun invokeMoveMethodRefactoring(
|
private fun invokeMoveMethodRefactoring(
|
||||||
project: Project, method: KtNamedFunction, targetContainer: KtClassOrObject?, dataContext: DataContext?
|
project: Project,
|
||||||
|
method: KtNamedFunction,
|
||||||
|
targetContainer: KtClassOrObject?,
|
||||||
|
dataContext: DataContext?
|
||||||
) {
|
) {
|
||||||
var message: String? = null
|
|
||||||
if (method.containingClassOrObject == null) return
|
if (method.containingClassOrObject == null) return
|
||||||
if (!method.manager.isInProject(method)) {
|
|
||||||
message = KotlinBundle.message("text.move.method.is.not.supported.for.non.project.methods")
|
val errorMessageKey = when {
|
||||||
} else if (method.mentionsTypeParameters()) {
|
!method.manager.isInProject(method) ->
|
||||||
message = KotlinBundle.message("text.move.method.is.not.supported.for.generic.classes")
|
"text.move.method.is.not.supported.for.non.project.methods"
|
||||||
} else if (method.hasModifier(KtTokens.OVERRIDE_KEYWORD) || method.hasModifier(KtTokens.OPEN_KEYWORD)) {
|
method.mentionsTypeParameters() ->
|
||||||
message = KotlinBundle.message("text.move.method.is.not.supported.when.method.is.a.part.of.inheritance.hierarchy")
|
"text.move.method.is.not.supported.for.generic.classes"
|
||||||
|
method.hasModifier(KtTokens.OVERRIDE_KEYWORD) || method.hasModifier(KtTokens.OPEN_KEYWORD) ->
|
||||||
|
"text.move.method.is.not.supported.when.method.is.a.part.of.inheritance.hierarchy"
|
||||||
|
else -> null
|
||||||
}
|
}
|
||||||
message?.let {
|
|
||||||
showErrorHint(project, dataContext, message)
|
if (errorMessageKey != null) {
|
||||||
|
showErrorHint(project, dataContext, KotlinBundle.message(errorMessageKey))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
MoveKotlinMethodDialog(method, collectSuitableVariables(method), targetContainer).show()
|
|
||||||
|
MoveKotlinMethodDialog(
|
||||||
|
method,
|
||||||
|
collectSuitableVariables(method),
|
||||||
|
targetContainer
|
||||||
|
).show()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun collectSuitableVariables(method: KtNamedFunction): Map<KtNamedDeclaration, KtClass> {
|
private fun collectSuitableVariables(method: KtNamedFunction): Map<KtNamedDeclaration, KtClass> {
|
||||||
@@ -55,7 +64,7 @@ class MoveKotlinMethodHandler : MoveHandlerDelegate() {
|
|||||||
|
|
||||||
val variableToClassMap = LinkedHashMap<KtNamedDeclaration, KtClass>()
|
val variableToClassMap = LinkedHashMap<KtNamedDeclaration, KtClass>()
|
||||||
for (variable in allVariables) {
|
for (variable in allVariables) {
|
||||||
(variable.resolveToDescriptorIfAny() as? CallableDescriptor)?.returnType?.let { type ->
|
variable.type()?.let { type ->
|
||||||
if (type.arguments.isEmpty()) {
|
if (type.arguments.isEmpty()) {
|
||||||
val ktClass = type.constructor.declarationDescriptor?.findPsi() as? KtClass
|
val ktClass = type.constructor.declarationDescriptor?.findPsi() as? KtClass
|
||||||
if (ktClass != null && method.manager.isInProject(ktClass)) {
|
if (ktClass != null && method.manager.isInProject(ktClass)) {
|
||||||
@@ -108,10 +117,7 @@ class MoveKotlinMethodHandler : MoveHandlerDelegate() {
|
|||||||
typeParameters.addAll(ktClassOrObject.typeParameters)
|
typeParameters.addAll(ktClassOrObject.typeParameters)
|
||||||
ktClassOrObject = if (ktClassOrObject.hasModifier(KtTokens.INNER_KEYWORD)) ktClassOrObject.containingClassOrObject else null
|
ktClassOrObject = if (ktClassOrObject.hasModifier(KtTokens.INNER_KEYWORD)) ktClassOrObject.containingClassOrObject else null
|
||||||
}
|
}
|
||||||
collectDescendantsOfType<KtUserType>().forEach { userType ->
|
return collectDescendantsOfType<KtUserType>().any { userType -> userType.referenceExpression?.mainReference?.resolve() in typeParameters }
|
||||||
if (userType.referenceExpression?.mainReference?.resolve() in typeParameters) return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getActionName(elements: Array<out PsiElement>): String = "${KotlinBundle.message("text.move.method")}.."
|
override fun getActionName(elements: Array<out PsiElement>): String = "${KotlinBundle.message("text.move.method")}.."
|
||||||
|
|||||||
+7
-3
@@ -283,12 +283,16 @@ class MoveKotlinMethodProcessor(
|
|||||||
|
|
||||||
internal fun getThisClassesToMembers(method: KtNamedFunction) = traverseOuterInstanceReferences(method)
|
internal fun getThisClassesToMembers(method: KtNamedFunction) = traverseOuterInstanceReferences(method)
|
||||||
|
|
||||||
|
internal fun KtNamedDeclaration.type() = (resolveToDescriptorIfAny() as? CallableDescriptor)?.returnType
|
||||||
|
|
||||||
|
internal fun KtClass.defaultType() = resolveToDescriptorIfAny()?.defaultType
|
||||||
|
|
||||||
private fun traverseOuterInstanceReferences(
|
private fun traverseOuterInstanceReferences(
|
||||||
method: KtNamedFunction,
|
method: KtNamedFunction,
|
||||||
body: (SourceInstanceReferenceUsageInfo) -> Unit = {}
|
body: (SourceInstanceReferenceUsageInfo) -> Unit = {}
|
||||||
): LinkedHashMap<KtClass, MutableSet<KtNamedDeclaration>> {
|
): Map<KtClass, MutableSet<KtNamedDeclaration>> {
|
||||||
val context = method.analyzeWithContent()
|
val context = method.analyzeWithContent()
|
||||||
val containingClassOrObject = method.containingClassOrObject ?: return LinkedHashMap()
|
val containingClassOrObject = method.containingClassOrObject ?: return emptyMap()
|
||||||
val descriptor = containingClassOrObject.unsafeResolveToDescriptor()
|
val descriptor = containingClassOrObject.unsafeResolveToDescriptor()
|
||||||
|
|
||||||
fun getClassOrObjectAndMemberReferencedBy(reference: KtExpression): Pair<DeclarationDescriptor?, CallableDescriptor?> {
|
fun getClassOrObjectAndMemberReferencedBy(reference: KtExpression): Pair<DeclarationDescriptor?, CallableDescriptor?> {
|
||||||
@@ -311,7 +315,7 @@ private fun traverseOuterInstanceReferences(
|
|||||||
return classOrObjectDescriptor to memberDescriptor
|
return classOrObjectDescriptor to memberDescriptor
|
||||||
}
|
}
|
||||||
|
|
||||||
val thisClassesToMembers = LinkedHashMap<KtClass, MutableSet<KtNamedDeclaration>>()
|
val thisClassesToMembers = mutableMapOf<KtClass, MutableSet<KtNamedDeclaration>>()
|
||||||
method.bodyExpression?.forEachDescendantOfType<KtExpression> { reference ->
|
method.bodyExpression?.forEachDescendantOfType<KtExpression> { reference ->
|
||||||
val (classOrObjectDescriptor, memberDescriptor) = getClassOrObjectAndMemberReferencedBy(reference)
|
val (classOrObjectDescriptor, memberDescriptor) = getClassOrObjectAndMemberReferencedBy(reference)
|
||||||
(classOrObjectDescriptor?.findPsi() as? KtClassOrObject)?.let { resolvedClassOrObject ->
|
(classOrObjectDescriptor?.findPsi() as? KtClassOrObject)?.let { resolvedClassOrObject ->
|
||||||
|
|||||||
Reference in New Issue
Block a user