mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Merge branch 'master' of git.labs.intellij.net:idea/community
This commit is contained in:
+3
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2013 JetBrains s.r.o.
|
||||
* Copyright 2000-2014 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.
|
||||
@@ -17,6 +17,7 @@ package com.intellij.codeInsight.template.postfix.settings;
|
||||
|
||||
import com.intellij.application.options.editor.EditorOptionsProvider;
|
||||
import com.intellij.codeInsight.CodeInsightBundle;
|
||||
import com.intellij.codeInsight.template.impl.LiveTemplateCompletionContributor;
|
||||
import com.intellij.codeInsight.template.impl.TemplateSettings;
|
||||
import com.intellij.codeInsight.template.postfix.templates.PostfixTemplate;
|
||||
import com.intellij.openapi.options.Configurable;
|
||||
@@ -155,6 +156,7 @@ public class PostfixTemplatesConfigurable implements SearchableConfigurable, Edi
|
||||
|
||||
private void updateComponents() {
|
||||
boolean pluginEnabled = myPostfixTemplatesEnabled.isSelected();
|
||||
myCompletionEnabledCheckbox.setVisible(!LiveTemplateCompletionContributor.shouldShowAllTemplates());
|
||||
myCompletionEnabledCheckbox.setEnabled(pluginEnabled);
|
||||
myShortcutComboBox.setEnabled(pluginEnabled);
|
||||
if (myTemplatesListPanel != null) {
|
||||
|
||||
+18
-3
@@ -1,3 +1,18 @@
|
||||
/*
|
||||
* Copyright 2000-2014 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 com.intellij.codeInsight.template.postfix.templates;
|
||||
|
||||
import com.intellij.codeInsight.guess.GuessManager;
|
||||
@@ -28,17 +43,17 @@ public class InstanceofExpressionPostfixTemplate extends PostfixTemplate {
|
||||
|
||||
@Override
|
||||
public boolean isApplicable(@NotNull PsiElement context, @NotNull Document copyDocument, int newOffset) {
|
||||
return getTopmostExpression(context) != null;
|
||||
return PostfixTemplatesUtils.isNotPrimitiveTypeExpression(getTopmostExpression(context));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void expand(@NotNull PsiElement context, @NotNull Editor editor) {
|
||||
PsiExpression expression = getTopmostExpression(context);
|
||||
if (expression == null) return;
|
||||
if (!PostfixTemplatesUtils.isNotPrimitiveTypeExpression(expression)) return;
|
||||
surroundExpression(context.getProject(), editor, expression);
|
||||
}
|
||||
|
||||
private static void surroundExpression(Project project, Editor editor, PsiExpression expr) throws IncorrectOperationException {
|
||||
private static void surroundExpression(@NotNull Project project, @NotNull Editor editor, @NotNull PsiExpression expr) throws IncorrectOperationException {
|
||||
assert expr.isValid();
|
||||
PsiType[] types = GuessManager.getInstance(project).guessTypeToCast(expr);
|
||||
final boolean parenthesesNeeded = expr instanceof PsiPolyadicExpression ||
|
||||
|
||||
+23
-6
@@ -1,3 +1,18 @@
|
||||
/*
|
||||
* Copyright 2000-2014 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 com.intellij.codeInsight.template.postfix.templates;
|
||||
|
||||
import com.intellij.codeInsight.CodeInsightServicesUtil;
|
||||
@@ -10,6 +25,13 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@Aliases("!")
|
||||
public class NotExpressionPostfixTemplate extends ExpressionPostfixTemplateWithChooser {
|
||||
private static final Condition<PsiExpression> BOOLEAN_TYPE_CONDITION = new Condition<PsiExpression>() {
|
||||
@Override
|
||||
public boolean value(PsiExpression expression) {
|
||||
return PostfixTemplatesUtils.isBoolean(expression.getType());
|
||||
}
|
||||
};
|
||||
|
||||
public NotExpressionPostfixTemplate() {
|
||||
super("not", "Negates boolean expression", "!expr");
|
||||
}
|
||||
@@ -22,11 +44,6 @@ public class NotExpressionPostfixTemplate extends ExpressionPostfixTemplateWithC
|
||||
@NotNull
|
||||
@Override
|
||||
protected Condition<PsiExpression> getTypeCondition() {
|
||||
return new Condition<PsiExpression>() {
|
||||
@Override
|
||||
public boolean value(PsiExpression expression) {
|
||||
return PostfixTemplatesUtils.isBoolean(expression.getType());
|
||||
}
|
||||
};
|
||||
return BOOLEAN_TYPE_CONDITION;
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -39,13 +39,13 @@ public abstract class NullCheckPostfixTemplate extends PostfixTemplate {
|
||||
|
||||
@Override
|
||||
public boolean isApplicable(@NotNull PsiElement context, @NotNull Document copyDocument, int newOffset) {
|
||||
return getTopmostExpression(context) != null;
|
||||
return PostfixTemplatesUtils.isNotPrimitiveTypeExpression(getTopmostExpression(context));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void expand(@NotNull PsiElement context, @NotNull Editor editor) {
|
||||
PsiExpression expr = getTopmostExpression(context);
|
||||
if (expr == null) return;
|
||||
if (!PostfixTemplatesUtils.isNotPrimitiveTypeExpression(expr)) return;
|
||||
|
||||
Project project = expr.getProject();
|
||||
PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory();
|
||||
|
||||
+3
-1
@@ -19,6 +19,7 @@ import com.intellij.codeInsight.template.postfix.util.PostfixTemplatesUtils;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiExpression;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ThrowExceptionPostfixTemplate extends PostfixTemplate {
|
||||
@@ -28,7 +29,8 @@ public class ThrowExceptionPostfixTemplate extends PostfixTemplate {
|
||||
|
||||
@Override
|
||||
public boolean isApplicable(@NotNull PsiElement context, @NotNull Document copyDocument, int newOffset) {
|
||||
return getTopmostExpression(context) != null;
|
||||
PsiExpression expression = getTopmostExpression(context);
|
||||
return expression != null && PostfixTemplatesUtils.isThrowable(expression.getType());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+11
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2013 JetBrains s.r.o.
|
||||
* Copyright 2000-2014 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.
|
||||
@@ -46,11 +46,21 @@ public abstract class PostfixTemplatesUtils {
|
||||
editor.getCaretModel().moveToOffset(replace.getTextRange().getEndOffset());
|
||||
}
|
||||
|
||||
@Contract("null -> false")
|
||||
public static boolean isNotPrimitiveTypeExpression(@Nullable PsiExpression expression) {
|
||||
return expression != null && !(expression.getType() instanceof PsiPrimitiveType);
|
||||
}
|
||||
|
||||
@Contract("null -> false")
|
||||
public static boolean isIterable(@Nullable PsiType type) {
|
||||
return type != null && InheritanceUtil.isInheritor(type, CommonClassNames.JAVA_LANG_ITERABLE);
|
||||
}
|
||||
|
||||
@Contract("null -> false")
|
||||
public static boolean isThrowable(@Nullable PsiType type) {
|
||||
return type != null && InheritanceUtil.isInheritor(type, CommonClassNames.JAVA_LANG_THROWABLE);
|
||||
}
|
||||
|
||||
@Contract("null -> false")
|
||||
public static boolean isArray(@Nullable PsiType type) {
|
||||
return type != null && type instanceof PsiArrayType;
|
||||
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
public class Foo {
|
||||
void m(int o) {
|
||||
o.instanceof<caret>
|
||||
}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
public class Foo {
|
||||
void m(int o) {
|
||||
o.instanceof <caret>
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
public class Foo {
|
||||
void m(int o) {
|
||||
o.notnull<caret>
|
||||
}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
public class Foo {
|
||||
void m(int o) {
|
||||
o.notnull <caret>
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
public class Foo {
|
||||
void m(int o) {
|
||||
o.null<caret>
|
||||
}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
public class Foo {
|
||||
void m(int o) {
|
||||
o.null <caret>
|
||||
}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
public class Foo {
|
||||
void m(Object o) {
|
||||
o.throw<caret>
|
||||
}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
public class Foo {
|
||||
void m(Object o) {
|
||||
throw o;<caret>
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
import java.lang.RuntimeException;
|
||||
|
||||
public class Foo {
|
||||
void m(Object o) {
|
||||
o.throw<caret>
|
||||
void m() {
|
||||
new RuntimeException("error").throw<caret>
|
||||
}
|
||||
}
|
||||
+4
-2
@@ -1,5 +1,7 @@
|
||||
import java.lang.RuntimeException;
|
||||
|
||||
public class Foo {
|
||||
void m(Object o) {
|
||||
throw o;<caret>
|
||||
void m() {
|
||||
throw new RuntimeException("error");
|
||||
}
|
||||
}
|
||||
+16
@@ -1,3 +1,18 @@
|
||||
/*
|
||||
* Copyright 2000-2014 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 com.intellij.codeInsight.template.postfix.templates;
|
||||
|
||||
import com.intellij.codeInsight.template.impl.TemplateManagerImpl;
|
||||
@@ -7,6 +22,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
public class InstanceofPostfixTemplateTest extends PostfixTemplateTestCase {
|
||||
public void testSingleExpression() { doTest(); }
|
||||
public void testAlias() { doTest(); }
|
||||
public void testPrimitive() { doTest(); }
|
||||
|
||||
public void testSingleExpressionTemplate() {
|
||||
TemplateManagerImpl.setTemplateTesting(getProject(), getTestRootDisposable());
|
||||
|
||||
+16
@@ -1,3 +1,18 @@
|
||||
/*
|
||||
* Copyright 2000-2014 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 com.intellij.codeInsight.template.postfix.templates;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -11,6 +26,7 @@ public class NotNullPostfixTemplateTest extends PostfixTemplateTestCase {
|
||||
protected String getSuffix() { return "notnull"; }
|
||||
|
||||
public void testSimple() { doTest(); }
|
||||
public void testPrimitive() { doTest(); }
|
||||
public void testNn() { doTest(); }
|
||||
public void testSecondStatement() { doTest(); }
|
||||
}
|
||||
+16
@@ -1,3 +1,18 @@
|
||||
/*
|
||||
* Copyright 2000-2014 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 com.intellij.codeInsight.template.postfix.templates;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -11,5 +26,6 @@ public class NullPostfixTemplateTest extends PostfixTemplateTestCase {
|
||||
protected String getSuffix() { return "null"; }
|
||||
|
||||
public void testSimple() { doTest(); }
|
||||
public void testPrimitive() { doTest(); }
|
||||
public void testSecondStatement() { doTest(); }
|
||||
}
|
||||
+2
-2
@@ -26,8 +26,8 @@ import java.util.Set;
|
||||
*/
|
||||
public abstract class DependentMembersCollectorBase<T extends PsiElement, C> {
|
||||
protected final HashSet<T> myCollection = new HashSet<T>();
|
||||
private final C myClass;
|
||||
private final C mySuperClass;
|
||||
protected final C myClass;
|
||||
protected final C mySuperClass;
|
||||
|
||||
public DependentMembersCollectorBase(C clazz, C superClass) {
|
||||
myClass = clazz;
|
||||
|
||||
+30
-23
@@ -3,7 +3,7 @@
|
||||
<grid id="27dc6" binding="myPanel" layout-manager="GridLayoutManager" row-count="1" 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"/>
|
||||
<xy x="20" y="20" width="631" height="400"/>
|
||||
</constraints>
|
||||
<properties/>
|
||||
<border type="none"/>
|
||||
@@ -28,8 +28,7 @@
|
||||
<border type="none"/>
|
||||
<children/>
|
||||
</grid>
|
||||
<grid id="177e3" layout-manager="GridLayoutManager" row-count="2" column-count="3" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||
<margin top="0" left="0" bottom="0" right="0"/>
|
||||
<grid id="177e3" layout-manager="GridBagLayout">
|
||||
<constraints>
|
||||
<splitpane position="left"/>
|
||||
</constraints>
|
||||
@@ -38,7 +37,8 @@
|
||||
<children>
|
||||
<scrollpane id="86224" class="com.intellij.ui.components.JBScrollPane">
|
||||
<constraints>
|
||||
<grid row="0" column="0" row-span="1" col-span="3" vsize-policy="3" hsize-policy="7" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
|
||||
<grid row="0" column="0" row-span="1" col-span="6" vsize-policy="3" hsize-policy="7" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
|
||||
<gridbag weightx="1.0" weighty="1.0"/>
|
||||
</constraints>
|
||||
<properties/>
|
||||
<border type="none"/>
|
||||
@@ -49,27 +49,34 @@
|
||||
</component>
|
||||
</children>
|
||||
</scrollpane>
|
||||
<component id="c4746" class="javax.swing.JButton" binding="mySelectAllButton" default-binding="true">
|
||||
<grid id="6e08e" layout-manager="GridBagLayout">
|
||||
<constraints>
|
||||
<grid row="1" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
|
||||
<grid row="1" column="0" row-span="1" col-span="6" vsize-policy="3" hsize-policy="3" anchor="4" fill="2" indent="0" use-parent-layout="false"/>
|
||||
<gridbag weightx="0.0" weighty="0.0"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<text resource-bundle="messages/RefactoringBundle" key="select.all.button"/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="1217e" class="javax.swing.JButton" binding="myUnselectAllButton" default-binding="true">
|
||||
<constraints>
|
||||
<grid row="1" column="2" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<text resource-bundle="messages/RefactoringBundle" key="unselect.all.button"/>
|
||||
</properties>
|
||||
</component>
|
||||
<hspacer id="eacfb">
|
||||
<constraints>
|
||||
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="1" hsize-policy="6" anchor="0" fill="1" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
</hspacer>
|
||||
<properties/>
|
||||
<border type="none"/>
|
||||
<children>
|
||||
<component id="1217e" class="javax.swing.JButton" binding="myUnselectAllButton" default-binding="true">
|
||||
<constraints>
|
||||
<grid row="0" column="1" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="4" fill="0" indent="0" use-parent-layout="false"/>
|
||||
<gridbag top="0" left="5" bottom="0" right="0" weightx="0.0" weighty="0.0"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<text resource-bundle="messages/RefactoringBundle" key="unselect.all.button"/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="c4746" class="javax.swing.JButton" binding="mySelectAllButton" default-binding="true">
|
||||
<constraints>
|
||||
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="4" fill="0" indent="0" use-parent-layout="false"/>
|
||||
<gridbag weightx="0.0" weighty="0.0"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<text resource-bundle="messages/RefactoringBundle" key="select.all.button"/>
|
||||
</properties>
|
||||
</component>
|
||||
</children>
|
||||
</grid>
|
||||
</children>
|
||||
</grid>
|
||||
</children>
|
||||
|
||||
+13
-1
@@ -126,6 +126,14 @@ public abstract class AbstractMemberSelectionTable<T extends PsiElement, M exten
|
||||
myTableModel.fireTableDataChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Redraws table
|
||||
*/
|
||||
public void redraw() {
|
||||
myTableModel.redraw(getSelectedMemberInfos());
|
||||
myTableModel.fireTableDataChanged();
|
||||
}
|
||||
|
||||
public void setMemberInfos(Collection<M> memberInfos) {
|
||||
myMemberInfos = new ArrayList<M>(memberInfos);
|
||||
fireMemberInfoChange(memberInfos);
|
||||
@@ -313,9 +321,13 @@ public abstract class AbstractMemberSelectionTable<T extends PsiElement, M exten
|
||||
}
|
||||
|
||||
Collection<M> changed = Collections.singletonList(myTable.myMemberInfos.get(rowIndex));
|
||||
redraw(changed);
|
||||
// fireTableRowsUpdated(rowIndex, rowIndex);
|
||||
}
|
||||
|
||||
public void redraw(Collection<M> changed) {
|
||||
myTable.fireMemberInfoChange(changed);
|
||||
fireTableDataChanged();
|
||||
// fireTableRowsUpdated(rowIndex, rowIndex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -19,6 +19,7 @@ import com.intellij.openapi.actionSystem.DataContext;
|
||||
import com.intellij.openapi.editor.Caret;
|
||||
import com.intellij.openapi.editor.CaretAction;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
@@ -114,7 +115,7 @@ public abstract class EditorActionHandler {
|
||||
* @param editor the editor in which the action is invoked.
|
||||
* @param dataContext the data context for the action.
|
||||
*/
|
||||
public void executeInCaretContext(final Editor editor, Caret caret, final DataContext dataContext) {
|
||||
public void executeInCaretContext(@NotNull final Editor editor, @Nullable Caret caret, final DataContext dataContext) {
|
||||
if (caret == null && runForAllCarets()) {
|
||||
editor.getCaretModel().runForEachCaret(new CaretAction() {
|
||||
@Override
|
||||
|
||||
@@ -11,4 +11,5 @@ Pythonid 3.1
|
||||
Karma 134.686 134.31
|
||||
org.intellij.scala 0.32.512
|
||||
org.jetbrains.kannotator 0.2.420
|
||||
SBT 1.0.0 1.1.0 1.2.0 1.3.0 1.3.1 1.4.0 1.5.0
|
||||
SBT 1.0.0 1.1.0 1.2.0 1.3.0 1.3.1 1.4.0 1.5.0
|
||||
"JSTestDriver Plugin" 134.686 134.31 134.307 134.1039
|
||||
@@ -29,13 +29,20 @@ public class SemVer {
|
||||
private final int myMajor;
|
||||
private final int myMinor;
|
||||
private final int myPatch;
|
||||
private final String myRawVersion;
|
||||
|
||||
public SemVer(int major, int minor, int patch) {
|
||||
public SemVer(@NotNull String rawVersion, int major, int minor, int patch) {
|
||||
myRawVersion = rawVersion;
|
||||
myMajor = major;
|
||||
myMinor = minor;
|
||||
myPatch = patch;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getRawVersion() {
|
||||
return myRawVersion;
|
||||
}
|
||||
|
||||
public int getMajor() {
|
||||
return myMajor;
|
||||
}
|
||||
@@ -85,7 +92,7 @@ public class SemVer {
|
||||
}
|
||||
Integer patch = toInteger(patchStr);
|
||||
if (major != null && minor != null && patch != null) {
|
||||
return new SemVer(major, minor, patch);
|
||||
return new SemVer(text, major, minor, patch);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2013 JetBrains s.r.o.
|
||||
* Copyright 2000-2014 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.
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
package com.intellij.util.text;
|
||||
|
||||
import junit.framework.Assert;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
@@ -24,15 +24,17 @@ import org.junit.Test;
|
||||
public class SemVerTest {
|
||||
@Test
|
||||
public void testParsing() throws Exception {
|
||||
SemVer semVer = SemVer.parseFromText("0.9.2");
|
||||
String version = "0.9.2";
|
||||
SemVer semVer = SemVer.parseFromText(version);
|
||||
Assert.assertNotNull(semVer);
|
||||
Assert.assertEquals(new SemVer(0, 9, 2), semVer);
|
||||
Assert.assertEquals(new SemVer(version, 0, 9, 2), semVer);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExtendedVersion() throws Exception {
|
||||
SemVer semVer = SemVer.parseFromText("0.9.2-dart");
|
||||
String version = "0.9.2-dart";
|
||||
SemVer semVer = SemVer.parseFromText(version);
|
||||
Assert.assertNotNull(semVer);
|
||||
Assert.assertEquals(new SemVer(0, 9, 2), semVer);
|
||||
Assert.assertEquals(new SemVer(version, 0, 9, 2), semVer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -516,6 +516,9 @@ INSP.NAME.assignment.to.loop.or.with.parameter.display.name=Assignment to 'for'
|
||||
INSP.NAME.assignment.to.loop.or.with.parameter.display.message=Variable ''{0}'' already declared in ''for'' loop or ''with'' statement above
|
||||
|
||||
# Refactoring
|
||||
refactoring.will.not.be.accessible=Member, you are trying to move depends on ''{0}'' which will not be accessible after this refactoring
|
||||
|
||||
|
||||
# introduce
|
||||
refactoring.introduce.name.error=Incorrect name
|
||||
refactoring.introduce.selection.error=Cannot perform refactoring using selected element(s)
|
||||
@@ -559,7 +562,7 @@ refactoring.extract.method.error.name.clash=Method name clashes with already exi
|
||||
refactoring.extract.method.error.cannot.perform.refactoring.with.local=Cannot perform refactoring from expression with local variables modifications and return instructions inside code fragment
|
||||
|
||||
# extract superclass
|
||||
refactoring.extract.super.target.path.outside.roots=Target directory is outside the project.<br>Must be within content roots
|
||||
refactoring.extract.super.target.path.outside.roots=Target directory is outside the project. Must be within content roots
|
||||
refactoring.extract.super.name.0.must.be.ident=Name ''{0}'' is invalid. Must be a valid Python identifier
|
||||
refactoring.extract.super.class.no.members.allowed=None of members could be extracted
|
||||
|
||||
|
||||
@@ -80,11 +80,11 @@ public final class PyClassRefactoringUtil {
|
||||
if (methods.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
for (PsiElement e : methods) {
|
||||
for (final PsiElement e : methods) {
|
||||
rememberNamedReferences(e);
|
||||
}
|
||||
final PyFunction[] elements = methods.toArray(new PyFunction[methods.size()]);
|
||||
return addMethods(superClass, elements);
|
||||
return addMethods(superClass, true, elements);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -92,28 +92,31 @@ public final class PyClassRefactoringUtil {
|
||||
*
|
||||
* @param destination where to add methods
|
||||
* @param methods methods
|
||||
* @return newly added methods
|
||||
* @param skipIfExist do not add anything if method already exists
|
||||
* @return newly added methods or existing one (if skipIfExists is true and method already exists)
|
||||
*/
|
||||
@NotNull
|
||||
public static List<PyFunction> addMethods(@NotNull final PyClass destination, @NotNull final PyFunction... methods) {
|
||||
public static List<PyFunction> addMethods(@NotNull final PyClass destination, final boolean skipIfExist, @NotNull final PyFunction... methods) {
|
||||
|
||||
final PyStatementList destStatementList = destination.getStatementList();
|
||||
final List<PyFunction> newlyCreatedMethods = new ArrayList<PyFunction>(methods.length);
|
||||
final List<PyFunction> result = new ArrayList<PyFunction>(methods.length);
|
||||
|
||||
for (final PyFunction method : methods) {
|
||||
|
||||
if (destination.findMethodByName(method.getName(), false) != null) {
|
||||
continue; //We skip adding if class already has this method. I am not sure if this behaviour is correct, but it was here, so I left if for backward compatibility
|
||||
final PyFunction existingMethod = destination.findMethodByName(method.getName(), false);
|
||||
if ((existingMethod != null) && skipIfExist) {
|
||||
result.add(existingMethod);
|
||||
continue; //We skip adding if class already has this method.
|
||||
}
|
||||
|
||||
|
||||
final PyFunction newMethod = insertMethodInProperPlace(destStatementList, method);
|
||||
newlyCreatedMethods.add(newMethod);
|
||||
result.add(newMethod);
|
||||
restoreNamedReferences(newMethod);
|
||||
}
|
||||
|
||||
PyPsiUtils.removeRedundantPass(destStatementList);
|
||||
return newlyCreatedMethods;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+3
-22
@@ -17,7 +17,7 @@ package com.jetbrains.python.refactoring.classes;
|
||||
|
||||
import com.intellij.refactoring.classMembers.DependentMembersCollectorBase;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.resolve.PyResolveContext;
|
||||
import com.jetbrains.python.refactoring.classes.membersManager.MembersManager;
|
||||
|
||||
/**
|
||||
* @author Dennis.Ushakov
|
||||
@@ -28,26 +28,7 @@ public class PyDependentMembersCollector extends DependentMembersCollectorBase<P
|
||||
}
|
||||
|
||||
@Override
|
||||
public void collect(PyElement member) {
|
||||
//TODO: Move to MembersManager as well
|
||||
final PyRecursiveElementVisitor visitor = new PyRecursiveElementVisitor() {
|
||||
@Override
|
||||
public void visitPyCallExpression(PyCallExpression node) {
|
||||
final Callable markedFunction = node.resolveCalleeFunction(PyResolveContext.noImplicits());
|
||||
final PyFunction function = markedFunction != null ? markedFunction.asMethod() : null;
|
||||
if (!existsInSuperClass(function)) {
|
||||
myCollection.add(function);
|
||||
}
|
||||
}
|
||||
};
|
||||
member.accept(visitor);
|
||||
}
|
||||
|
||||
private boolean existsInSuperClass(PyFunction classMember) {
|
||||
if (getSuperClass() == null) return false;
|
||||
final String name = classMember != null ? classMember.getName() : null;
|
||||
if (name == null) return false;
|
||||
final PyFunction methodBySignature = (getSuperClass()).findMethodByName(name, true);
|
||||
return methodBySignature != null;
|
||||
public void collect(final PyElement member) {
|
||||
myCollection.addAll(MembersManager.getAllDependencies(myClass, member, getSuperClass()));
|
||||
}
|
||||
}
|
||||
|
||||
+9
-14
@@ -7,9 +7,7 @@ import com.intellij.openapi.roots.ProjectRootManager;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.refactoring.RefactoringBundle;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
import com.jetbrains.python.PyBundle;
|
||||
import com.jetbrains.python.PythonLanguage;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
@@ -24,6 +22,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* @author Ilya.Kazakevich
|
||||
@@ -32,20 +31,10 @@ class PyExtractSuperclassPresenterImpl extends MembersBasedPresenterNoPreviewImp
|
||||
implements PyExtractSuperclassPresenter {
|
||||
private final NamesValidator myNamesValidator = LanguageNamesValidation.INSTANCE.forLanguage(PythonLanguage.getInstance());
|
||||
|
||||
|
||||
@NotNull
|
||||
private static final MultiMap<PsiElement, String> EMPTY_MAP = MultiMap.create();
|
||||
|
||||
PyExtractSuperclassPresenterImpl(@NotNull final PyExtractSuperclassView view,
|
||||
@NotNull final PyClass classUnderRefactoring,
|
||||
@NotNull final PyMemberInfoStorage infoStorage) {
|
||||
super(view, classUnderRefactoring, infoStorage);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected MultiMap<PsiElement, String> getConflicts() {
|
||||
return EMPTY_MAP; //There are no conflicts for extracting
|
||||
super(view, classUnderRefactoring, infoStorage, new PyExtractSuperclassInfoModel(classUnderRefactoring));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -79,7 +68,7 @@ class PyExtractSuperclassPresenterImpl extends MembersBasedPresenterNoPreviewImp
|
||||
final Collection<PyMemberInfo<PyElement>> pyMemberInfos =
|
||||
PyUtil.filterOutObject(myStorage.getClassMemberInfos(myClassUnderRefactoring));
|
||||
myView.configure(
|
||||
new PyExtractSuperclassInitializationInfo(new PyExtractSuperclassInfoModel(myClassUnderRefactoring), pyMemberInfos, defaultFilePath,
|
||||
new PyExtractSuperclassInitializationInfo(myModel, pyMemberInfos, defaultFilePath,
|
||||
roots));
|
||||
myView.initAndShow();
|
||||
|
||||
@@ -96,4 +85,10 @@ class PyExtractSuperclassPresenterImpl extends MembersBasedPresenterNoPreviewImp
|
||||
PyExtractSuperclassHelper
|
||||
.extractSuperclass(myClassUnderRefactoring, myView.getSelectedMemberInfos(), myView.getSuperClassName(), myView.getModuleFile());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected Iterable<? extends PyClass> getDestClassesToCheckConflicts() {
|
||||
return Collections.emptyList(); // No conflict can take place in newly created classes
|
||||
}
|
||||
}
|
||||
|
||||
+7
-6
@@ -1,9 +1,6 @@
|
||||
package com.jetbrains.python.refactoring.classes.membersManager;
|
||||
|
||||
import com.jetbrains.python.psi.PyAssignmentStatement;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.psi.PyElement;
|
||||
import com.jetbrains.python.psi.PyTargetExpression;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.refactoring.classes.PyClassRefactoringUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@@ -22,11 +19,15 @@ class ClassFieldsManager extends FieldsManager {
|
||||
super(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasConflict(@NotNull final PyTargetExpression member, @NotNull final PyClass aClass) {
|
||||
return NamePredicate.hasElementWithSameName(member, aClass.getClassAttributes());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<PyElement> moveAssignments(@NotNull final PyClass from,
|
||||
@NotNull final Collection<PyAssignmentStatement> statements,
|
||||
@NotNull final PyClass... to) {
|
||||
@NotNull final Collection<PyAssignmentStatement> statements,
|
||||
@NotNull final PyClass... to) {
|
||||
//TODO: Copy/paste with InstanceFieldsManager. Move to parent?
|
||||
final List<PyElement> result = new ArrayList<PyElement>();
|
||||
for (final PyClass destClass : to) {
|
||||
|
||||
+43
-5
@@ -5,15 +5,14 @@ import com.google.common.collect.Collections2;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
import com.jetbrains.NotNullPredicate;
|
||||
import com.jetbrains.python.psi.PyAssignmentStatement;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.psi.PyElement;
|
||||
import com.jetbrains.python.psi.PyTargetExpression;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -34,6 +33,21 @@ abstract class FieldsManager extends MembersManager<PyTargetExpression> {
|
||||
myStatic = isStatic;
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected Collection<PyElement> getDependencies(@NotNull final MultiMap<PyClass, PyElement> usedElements) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected MultiMap<PyClass, PyElement> getDependencies(@NotNull final PyElement member) {
|
||||
final MultiMap<PyClass, PyElement> result = new MultiMap<PyClass, PyElement>();
|
||||
member.accept(new MyPyRecursiveElementVisitor(result));
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<? extends PyElement> getElementsToStoreReferences(@NotNull final Collection<PyTargetExpression> elements) {
|
||||
// We need to save references from assignments
|
||||
@@ -50,7 +64,8 @@ abstract class FieldsManager extends MembersManager<PyTargetExpression> {
|
||||
protected Collection<PyElement> moveMembers(@NotNull final PyClass from,
|
||||
@NotNull final Collection<PyMemberInfo<PyTargetExpression>> members,
|
||||
@NotNull final PyClass... to) {
|
||||
return moveAssignments(from, Collections2.filter(Collections2.transform(fetchElements(members), ASSIGNMENT_TRANSFORM), NotNullPredicate.INSTANCE),
|
||||
return moveAssignments(from, Collections2
|
||||
.filter(Collections2.transform(fetchElements(members), ASSIGNMENT_TRANSFORM), NotNullPredicate.INSTANCE),
|
||||
to);
|
||||
}
|
||||
|
||||
@@ -116,4 +131,27 @@ abstract class FieldsManager extends MembersManager<PyTargetExpression> {
|
||||
return PsiTreeUtil.getParentOfType(input, PyAssignmentStatement.class);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches field declarations
|
||||
*/
|
||||
private static class MyPyRecursiveElementVisitor extends PyRecursiveElementVisitor {
|
||||
@NotNull
|
||||
private final MultiMap<PyClass, PyElement> myResult;
|
||||
|
||||
private MyPyRecursiveElementVisitor(@NotNull final MultiMap<PyClass, PyElement> result) {
|
||||
myResult = result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPyReferenceExpression(final PyReferenceExpression node) {
|
||||
final PsiElement declaration = node.getReference().resolve();
|
||||
if (declaration instanceof PyElement) {
|
||||
final PyClass parent = PsiTreeUtil.getParentOfType(declaration, PyClass.class);
|
||||
if (parent != null) {
|
||||
myResult.putValue(parent, (PyElement)declaration);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
-1
@@ -8,6 +8,7 @@ import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.impl.PyFunctionBuilder;
|
||||
import com.jetbrains.python.refactoring.classes.PyClassRefactoringUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
@@ -24,6 +25,10 @@ class InstanceFieldsManager extends FieldsManager {
|
||||
super(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasConflict(@NotNull final PyTargetExpression member, @NotNull final PyClass aClass) {
|
||||
return NamePredicate.hasElementWithSameName(member, aClass.getInstanceAttributes());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collection<PyElement> moveAssignments(@NotNull final PyClass from,
|
||||
@@ -77,7 +82,7 @@ class InstanceFieldsManager extends FieldsManager {
|
||||
final PyFunctionBuilder functionBuilder = new PyFunctionBuilder(PyNames.INIT);
|
||||
functionBuilder.parameter(PyNames.CANONICAL_SELF); //TODO: Take param from codestyle?
|
||||
final PyFunction function = functionBuilder.buildFunction(to.getProject(), LanguageLevel.forElement(to));
|
||||
return PyClassRefactoringUtil.addMethods(to, function).get(0);
|
||||
return PyClassRefactoringUtil.addMethods(to, true, function).get(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
package com.jetbrains.python.refactoring.classes.membersManager;
|
||||
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.refactoring.RefactoringBundle;
|
||||
import com.intellij.refactoring.ui.ConflictsDialog;
|
||||
import com.intellij.refactoring.util.RefactoringUIUtil;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
import com.jetbrains.python.PyBundle;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Displays error messages about fact that destination class already contains some member infos
|
||||
* or members under refactoring would not be available at the new place.
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public class MembersConflictDialog extends ConflictsDialog {
|
||||
/**
|
||||
* @param project project under refactoring
|
||||
* @param duplicatesConflict duplicates conflicts : that means destination class has the same member.
|
||||
* If member "foo" already exists in class "bar": pass [bar] -] [foo].
|
||||
* @param dependenciesConflicts dependency conflict: list of elements used by member under refactoring and would not be available
|
||||
* at new destination. If user wants to move method, that uses field "bar" which would not be available at new class,
|
||||
* pass [bar] field
|
||||
*/
|
||||
public MembersConflictDialog(
|
||||
@NotNull final Project project,
|
||||
@NotNull final MultiMap<PyClass, PyMemberInfo<?>> duplicatesConflict,
|
||||
@NotNull final Collection<PyMemberInfo<?>> dependenciesConflicts) {
|
||||
super(project, convertDescription(duplicatesConflict, dependenciesConflicts), null, true, false);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static MultiMap<PsiElement, String> convertDescription(
|
||||
@NotNull final MultiMap<PyClass, PyMemberInfo<?>> duplicateConflictDescriptions,
|
||||
@NotNull final Collection<PyMemberInfo<?>> dependenciesConflicts) {
|
||||
final MultiMap<PsiElement, String> result = new MultiMap<PsiElement, String>();
|
||||
for (final PyClass aClass : duplicateConflictDescriptions.keySet()) {
|
||||
for (final PyMemberInfo<?> pyMemberInfo : duplicateConflictDescriptions.get(aClass)) {
|
||||
final String message = RefactoringBundle.message("0.already.contains.a.1",
|
||||
RefactoringUIUtil.getDescription(aClass, false),
|
||||
RefactoringUIUtil.getDescription(pyMemberInfo.getMember(), false));
|
||||
result.putValue(aClass, message);
|
||||
}
|
||||
}
|
||||
|
||||
for (final PyMemberInfo<?> memberUnderConflict : dependenciesConflicts) {
|
||||
result.putValue(memberUnderConflict.getMember(), PyBundle.message(
|
||||
"refactoring.will.not.be.accessible",
|
||||
RefactoringUIUtil.getDescription(memberUnderConflict.getMember(), false)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+92
-10
@@ -23,13 +23,14 @@ import com.google.common.collect.Collections2;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiNamedElement;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
import com.jetbrains.NotNullPredicate;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.psi.PyElement;
|
||||
import com.jetbrains.python.refactoring.classes.PyClassRefactoringUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@@ -74,6 +75,7 @@ public abstract class MembersManager<T extends PyElement> implements Function<T,
|
||||
/**
|
||||
* Transforms elements, manager says it could move to appropriate {@link com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo}.
|
||||
* Types are checked at runtime.
|
||||
*
|
||||
* @param pyClass class whose members we want to move
|
||||
* @param manager manager that should check class and report list of memebers
|
||||
* @return member infos
|
||||
@@ -81,9 +83,10 @@ public abstract class MembersManager<T extends PyElement> implements Function<T,
|
||||
//TODO: Move to TypeSafeMovingStrategy
|
||||
@NotNull
|
||||
@SuppressWarnings({"unchecked", "rawtypes"}) //We check type at runtime
|
||||
private static Collection<PyMemberInfo<PyElement>> transformSafely(@NotNull final PyClass pyClass, @NotNull final MembersManager<?> manager) {
|
||||
private static Collection<PyMemberInfo<PyElement>> transformSafely(@NotNull final PyClass pyClass,
|
||||
@NotNull final MembersManager<?> manager) {
|
||||
final List<PyElement> membersCouldBeMoved = manager.getMembersCouldBeMoved(pyClass);
|
||||
manager.checkElementTypes((Collection)membersCouldBeMoved);
|
||||
manager.checkElementTypes((Iterable)membersCouldBeMoved);
|
||||
return (Collection<PyMemberInfo<PyElement>>)Collections2.transform(membersCouldBeMoved, (Function)manager);
|
||||
}
|
||||
|
||||
@@ -114,13 +117,12 @@ public abstract class MembersManager<T extends PyElement> implements Function<T,
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Checks that all elements has allowed type for manager
|
||||
*
|
||||
* @param members elements to check against manager
|
||||
* @param elements elements to check against manager
|
||||
*/
|
||||
void checkElementTypes(@NotNull final Collection<T> elements) {
|
||||
void checkElementTypes(@NotNull final Iterable<T> elements) {
|
||||
for (final PyElement pyElement : elements) {
|
||||
Preconditions.checkArgument(myExpectedClass.isAssignableFrom(pyElement.getClass()),
|
||||
String.format("Manager %s expected %s but got %s", this, myExpectedClass, pyElement));
|
||||
@@ -159,10 +161,11 @@ public abstract class MembersManager<T extends PyElement> implements Function<T,
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds member in class. It is here only for backward compatibility with some tests.
|
||||
* Finds member in class.
|
||||
* @param pyClass class to find member in
|
||||
* @param pyElement element to find
|
||||
* @return member info with element
|
||||
*/
|
||||
//TODO: mark deprecated?
|
||||
@TestOnly
|
||||
@NotNull
|
||||
public static PyMemberInfo<PyElement> findMember(@NotNull final PyClass pyClass, @NotNull final PyElement pyElement) {
|
||||
final PyMemberInfo<PyElement> result = findMember(pyClass, new FindByElement(pyElement));
|
||||
@@ -245,8 +248,9 @@ public abstract class MembersManager<T extends PyElement> implements Function<T,
|
||||
|
||||
/**
|
||||
* Fetches elements from member info.
|
||||
*
|
||||
* @param memberInfos member info to fetch elements from
|
||||
* @param <T> type of element
|
||||
* @param <T> type of element
|
||||
* @return list of elements
|
||||
*/
|
||||
@NotNull
|
||||
@@ -254,6 +258,84 @@ public abstract class MembersManager<T extends PyElement> implements Function<T,
|
||||
return Collections2.transform(memberInfos, new PyMemberExtractor<T>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if moving certain member to certain class may lead to conflict (actually that means
|
||||
* that class already has this member)
|
||||
*
|
||||
* @param member member to check
|
||||
* @param aClass class where this member wanna be moved
|
||||
* @return true if conflict exists.
|
||||
*/
|
||||
public abstract boolean hasConflict(@NotNull T member, @NotNull PyClass aClass);
|
||||
|
||||
/**
|
||||
* Returns all elements this member depends on.
|
||||
*
|
||||
* @param classWhereMemberDeclared class where member declared
|
||||
* @param member member itself
|
||||
* @param destinationClass where this member would be moved (or null if new class is unknown)
|
||||
* @return collection of elements this member depends on excluding those, would be available in destination class
|
||||
*/
|
||||
@NotNull
|
||||
public static Collection<? extends PyElement> getAllDependencies(
|
||||
@NotNull final PyClass classWhereMemberDeclared,
|
||||
@NotNull final PyElement member,
|
||||
@Nullable final PyClass destinationClass) {
|
||||
final PyMemberInfo<PyElement> memberInfo = findMember(classWhereMemberDeclared, member);
|
||||
|
||||
|
||||
final Collection<? extends PyElement> elementsToCheckDependency =
|
||||
memberInfo.getMembersManager().getElementsToStoreReferences(Collections.singleton(member));
|
||||
|
||||
final MultiMap<PyClass, PyElement> dependencies = new MultiMap<PyClass, PyElement>();
|
||||
|
||||
final Collection<PyElement> result = new HashSet<PyElement>();
|
||||
for (final MembersManager<? extends PyElement> manager : MANAGERS) {
|
||||
for (final PyElement elementToCheckDependency : elementsToCheckDependency) {
|
||||
dependencies.putAllValues(manager.getDependencies(elementToCheckDependency));
|
||||
}
|
||||
}
|
||||
|
||||
if (destinationClass != null) {
|
||||
final Iterator<PyClass> classesIterator = dependencies.keySet().iterator();
|
||||
while (classesIterator.hasNext()) {
|
||||
final PyClass memberClass = classesIterator.next();
|
||||
if (memberClass.equals(destinationClass) ||
|
||||
ArrayUtil.contains(memberClass, destinationClass.getSuperClasses())) { // IF still would be available
|
||||
classesIterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (final MembersManager<? extends PyElement> manager : MANAGERS) {
|
||||
result.addAll(manager.getDependencies(dependencies));
|
||||
}
|
||||
result.addAll(dependencies.values());
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch dependencies this element depends on.
|
||||
* Manager should return them in format "class, where member declared" -- "member itself".
|
||||
* For example: if parameter is function, and this function uses field "foo" declared in class "bar", then manager (responsible for fields)
|
||||
* returns "bar" -] reference to "foo"
|
||||
*
|
||||
* @param member member to check dependencies for
|
||||
* @return dependencies
|
||||
*/
|
||||
@NotNull
|
||||
protected abstract MultiMap<PyClass, PyElement> getDependencies(@NotNull PyElement member);
|
||||
|
||||
/**
|
||||
* Get dependencies by members and classes they declared in (obtained from {@link #getDependencies(com.jetbrains.python.psi.PyElement)})
|
||||
* For example manager, responsible for "extends SomeClass" members may return list of classes
|
||||
*
|
||||
* @param usedElements class-to-element dependencies
|
||||
* @return dependencies
|
||||
*/
|
||||
@NotNull
|
||||
protected abstract Collection<PyElement> getDependencies(@NotNull MultiMap<PyClass, PyElement> usedElements);
|
||||
|
||||
private static class PyMemberExtractor<T extends PyElement> implements Function<PyMemberInfo<T>, T> {
|
||||
@SuppressWarnings("NullableProblems") //IDEA-120100
|
||||
@Override
|
||||
|
||||
+55
-1
@@ -3,7 +3,10 @@ package com.jetbrains.python.refactoring.classes.membersManager;
|
||||
import com.google.common.collect.Collections2;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.PsiReference;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
import com.jetbrains.NotNullPredicate;
|
||||
import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.codeInsight.imports.AddImportHelper;
|
||||
@@ -37,6 +40,26 @@ class MethodsManager extends MembersManager<PyFunction> {
|
||||
super(PyFunction.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasConflict(@NotNull final PyFunction member, @NotNull final PyClass aClass) {
|
||||
return NamePredicate.hasElementWithSameName(member, Arrays.asList(aClass.getMethods()));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected Collection<PyElement> getDependencies(@NotNull final MultiMap<PyClass, PyElement> usedElements) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected MultiMap<PyClass, PyElement> getDependencies(@NotNull final PyElement member) {
|
||||
final MultiMap<PyClass, PyElement> result = new MultiMap<PyClass, PyElement>();
|
||||
member.accept(new MyPyRecursiveElementVisitor(result));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected List<PyElement> getMembersCouldBeMoved(@NotNull final PyClass pyClass) {
|
||||
@@ -69,7 +92,7 @@ class MethodsManager extends MembersManager<PyFunction> {
|
||||
final PyFunctionBuilder functionBuilder = PyFunctionBuilder.copySignature(function, DECORATORS_MAY_BE_COPIED_TO_ABSTRACT);
|
||||
functionBuilder.decorate(PyNames.ABSTRACTMETHOD);
|
||||
final LanguageLevel level = LanguageLevel.forElement(destClass);
|
||||
PyClassRefactoringUtil.addMethods(destClass, functionBuilder.buildFunction(destClass.getProject(), level));
|
||||
PyClassRefactoringUtil.addMethods(destClass, false, functionBuilder.buildFunction(destClass.getProject(), level));
|
||||
classesToAddMetaAbc.add(destClass);
|
||||
}
|
||||
}
|
||||
@@ -227,4 +250,35 @@ class MethodsManager extends MembersManager<PyFunction> {
|
||||
return input.isToAbstract() == myAllowAbstractOnly;
|
||||
}
|
||||
}
|
||||
|
||||
private static class MyPyRecursiveElementVisitor extends PyRecursiveElementVisitor {
|
||||
@NotNull
|
||||
private final MultiMap<PyClass, PyElement> myResult;
|
||||
|
||||
private MyPyRecursiveElementVisitor(@NotNull final MultiMap<PyClass, PyElement> result) {
|
||||
myResult = result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPyCallExpression(final PyCallExpression node) {
|
||||
// TODO: refactor, messy code
|
||||
final PyExpression callee = node.getCallee();
|
||||
if (callee != null) {
|
||||
final PsiReference calleeRef = callee.getReference();
|
||||
if (calleeRef != null) {
|
||||
final PsiElement calleeDeclaration = calleeRef.resolve();
|
||||
if (calleeDeclaration instanceof PyFunction) {
|
||||
final PyFunction calleeFunction = (PyFunction)calleeDeclaration;
|
||||
final PyClass clazz = calleeFunction.getContainingClass();
|
||||
if (clazz != null) {
|
||||
if (PyUtil.isInit(calleeFunction)) {
|
||||
return; // Init call should not be marked as dependency
|
||||
}
|
||||
myResult.putValue(clazz, calleeFunction);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.jetbrains.python.refactoring.classes.membersManager;
|
||||
|
||||
import com.google.common.base.Optional;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.intellij.navigation.NavigationItem;
|
||||
import com.jetbrains.NotNullPredicate;
|
||||
import com.jetbrains.python.psi.PyElement;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
|
||||
/**
|
||||
* Finds elements by name
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
class NamePredicate extends NotNullPredicate<PyElement> {
|
||||
@NotNull
|
||||
private final String myName;
|
||||
|
||||
|
||||
NamePredicate(@NotNull final String name) {
|
||||
myName = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean applyNotNull(@NotNull final PyElement input) {
|
||||
return myName.equals(input.getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if collection has {@link com.jetbrains.python.psi.PyElement} with name equals to name of provided element.
|
||||
* If element has no name -- returns false any way.
|
||||
* @param needle element to take name from
|
||||
* @param stock collection elements to search between
|
||||
* @return true if stock contains element with name equal to needle's name
|
||||
*/
|
||||
static boolean hasElementWithSameName(@NotNull final NavigationItem needle, @NotNull final Iterable<? extends PyElement> stock) {
|
||||
final String name = needle.getName();
|
||||
if (name != null) {
|
||||
final Optional<? extends PyElement> optional = Iterables.tryFind(stock, new NamePredicate(name));
|
||||
return optional.isPresent();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+11
-1
@@ -16,6 +16,7 @@
|
||||
package com.jetbrains.python.refactoring.classes.membersManager;
|
||||
|
||||
import com.intellij.refactoring.classMembers.MemberInfoBase;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.psi.PyElement;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -29,7 +30,7 @@ public class PyMemberInfo<T extends PyElement> extends MemberInfoBase<T> {
|
||||
private final boolean myCouldBeAbstract;
|
||||
|
||||
/**
|
||||
* TODO: Doc new param
|
||||
* @param couldBeAbstract if element could be marked as abstract (like abstract method)
|
||||
* @param member element itself
|
||||
* @param isStatic is it static or not?
|
||||
* @param displayName element display name
|
||||
@@ -72,4 +73,13 @@ public class PyMemberInfo<T extends PyElement> extends MemberInfoBase<T> {
|
||||
public int hashCode() {
|
||||
return getMember().hashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if moving this member to some class may create conflict.
|
||||
* @param destinationClass destination class to check
|
||||
* @return true if conflict.
|
||||
*/
|
||||
public boolean hasConflict(@NotNull final PyClass destinationClass) {
|
||||
return myMembersManager.hasConflict(myMember, destinationClass);
|
||||
}
|
||||
}
|
||||
|
||||
+40
-7
@@ -3,6 +3,7 @@ package com.jetbrains.python.refactoring.classes.membersManager;
|
||||
import com.google.common.collect.Collections2;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.intellij.refactoring.RefactoringBundle;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
import com.jetbrains.NotNullPredicate;
|
||||
import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.psi.*;
|
||||
@@ -10,10 +11,7 @@ import com.jetbrains.python.refactoring.classes.PyClassRefactoringUtil;
|
||||
import com.jetbrains.python.refactoring.classes.ui.PyClassCellRenderer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Plugin that moves superclasses from one class to another
|
||||
@@ -29,6 +27,23 @@ class SuperClassesManager extends MembersManager<PyClass> {
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected Collection<PyElement> getDependencies(@NotNull final MultiMap<PyClass, PyElement> usedElements) {
|
||||
return Lists.<PyElement>newArrayList(usedElements.keySet());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected MultiMap<PyClass, PyElement> getDependencies(@NotNull PyElement member) {
|
||||
return MultiMap.emptyInstance();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasConflict(@NotNull final PyClass member, @NotNull final PyClass aClass) {
|
||||
final List<PyExpression> expressionList = getExpressionsBySuperClass(aClass, Collections.singleton(member));
|
||||
return !expressionList.isEmpty();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected List<PyElement> getMembersCouldBeMoved(@NotNull final PyClass pyClass) {
|
||||
@@ -44,19 +59,37 @@ class SuperClassesManager extends MembersManager<PyClass> {
|
||||
PyClassRefactoringUtil.addSuperclasses(from.getProject(), destClass, elements.toArray(new PyClass[members.size()]));
|
||||
}
|
||||
|
||||
final List<PyExpression> expressionsToDelete = getExpressionsBySuperClass(from, elements);
|
||||
for (final PyExpression expressionToDelete : expressionsToDelete) {
|
||||
expressionToDelete.delete();
|
||||
}
|
||||
|
||||
return Collections.emptyList(); //Hack: we know that "superclass expression" can't have reference
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns superclass expressions that are resolved to one or more classes from collection
|
||||
* @param from class to get superclass expressions from
|
||||
* @param classes classes to check superclasses against
|
||||
* @return collection of expressions that are resolved to one or more class from classes param
|
||||
*/
|
||||
@NotNull
|
||||
private static List<PyExpression> getExpressionsBySuperClass(@NotNull final PyClass from, @NotNull final Collection<PyClass> classes) {
|
||||
final List<PyExpression> expressionsToDelete = new ArrayList<PyExpression>(classes.size());
|
||||
|
||||
for (final PyExpression expression : from.getSuperClassExpressions()) {
|
||||
// Remove all superclass expressions that point to class from memberinfo
|
||||
if (!(expression instanceof PyQualifiedExpression)) {
|
||||
continue;
|
||||
}
|
||||
final PyReferenceExpression reference = (PyReferenceExpression)expression;
|
||||
for (final PyClass element : elements) {
|
||||
for (final PyClass element : classes) {
|
||||
if (reference.getReference().isReferenceTo(element)) {
|
||||
expression.delete();
|
||||
expressionsToDelete.add(expression);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Collections.emptyList(); //Hack: we know that "superclass expression" can't have reference
|
||||
return expressionsToDelete;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
+44
-5
@@ -1,12 +1,17 @@
|
||||
package com.jetbrains.python.refactoring.classes.membersManager.vp;
|
||||
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.refactoring.RefactoringBundle;
|
||||
import com.intellij.refactoring.classMembers.MemberInfoModel;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.psi.PyElement;
|
||||
import com.jetbrains.python.refactoring.classes.PyMemberInfoStorage;
|
||||
import com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* All presenters that use members inherits this class.
|
||||
* <strong>Warning</strong>: Do not inherit it directly.
|
||||
@@ -23,26 +28,40 @@ abstract class MembersBasedPresenterImpl<T extends MembersBasedView<?>> implemen
|
||||
protected final PyClass myClassUnderRefactoring;
|
||||
@NotNull
|
||||
protected final PyMemberInfoStorage myStorage;
|
||||
/**
|
||||
* Member model
|
||||
*/
|
||||
@NotNull
|
||||
protected final MemberInfoModel<PyElement, PyMemberInfo<PyElement>> myModel;
|
||||
|
||||
/**
|
||||
* @param view View for presenter
|
||||
* @param classUnderRefactoring class to be refactored
|
||||
* @param infoStorage info storage
|
||||
* @param model Member model (to be used for dependencies checking)
|
||||
*/
|
||||
MembersBasedPresenterImpl(@NotNull final T view,
|
||||
@NotNull final PyClass classUnderRefactoring,
|
||||
@NotNull final PyMemberInfoStorage infoStorage) {
|
||||
@NotNull final PyMemberInfoStorage infoStorage,
|
||||
@NotNull final MemberInfoModel<PyElement, PyMemberInfo<PyElement>> model) {
|
||||
myView = view;
|
||||
myClassUnderRefactoring = classUnderRefactoring;
|
||||
myStorage = infoStorage;
|
||||
myModel = model;
|
||||
}
|
||||
|
||||
//TODO: Mark Async ?
|
||||
@Override
|
||||
public void okClicked() {
|
||||
final MultiMap<PyClass, PyMemberInfo<?>> conflicts = getConflicts();
|
||||
final Collection<PyMemberInfo<?>> dependencyConflicts = new ArrayList<PyMemberInfo<?>>();
|
||||
for (final PyMemberInfo<PyElement> memberInfo : myStorage.getClassMemberInfos(myClassUnderRefactoring)) {
|
||||
if (myModel.checkForProblems(memberInfo) != MemberInfoModel.OK) {
|
||||
dependencyConflicts.add(memberInfo);
|
||||
}
|
||||
}
|
||||
|
||||
final MultiMap<PsiElement, String> conflicts = getConflicts();
|
||||
if (conflicts.isEmpty() || myView.showConflictsDialog(conflicts)) {
|
||||
if ((conflicts.isEmpty() && dependencyConflicts.isEmpty()) || myView.showConflictsDialog(conflicts, dependencyConflicts)) {
|
||||
try {
|
||||
validateView();
|
||||
doRefactor();
|
||||
@@ -75,9 +94,29 @@ abstract class MembersBasedPresenterImpl<T extends MembersBasedView<?>> implemen
|
||||
abstract void doRefactor();
|
||||
|
||||
/**
|
||||
* Checks if one of destination classes already has members that should be moved, so conflict would take place.
|
||||
*
|
||||
* @return map of conflicts (if any)
|
||||
* @see #getDestClassesToCheckConflicts()
|
||||
*/
|
||||
@NotNull
|
||||
protected abstract MultiMap<PsiElement, String> getConflicts();
|
||||
protected final MultiMap<PyClass, PyMemberInfo<?>> getConflicts() {
|
||||
final MultiMap<PyClass, PyMemberInfo<?>> result = new MultiMap<PyClass, PyMemberInfo<?>>();
|
||||
final Collection<PyMemberInfo<PyElement>> memberInfos = myView.getSelectedMemberInfos();
|
||||
for (final PyClass destinationClass : getDestClassesToCheckConflicts()) {
|
||||
for (final PyMemberInfo<PyElement> pyMemberInfo : memberInfos) {
|
||||
if (pyMemberInfo.hasConflict(destinationClass)) {
|
||||
result.putValue(destinationClass, pyMemberInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return classes where this refactoring will move members. To be used to check for conflicts (if one of target classes already has members)
|
||||
* @see #getConflicts()
|
||||
*/
|
||||
@NotNull
|
||||
protected abstract Iterable<? extends PyClass> getDestClassesToCheckConflicts();
|
||||
}
|
||||
|
||||
+18
-11
@@ -2,28 +2,33 @@ package com.jetbrains.python.refactoring.classes.membersManager.vp;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.command.CommandProcessor;
|
||||
import com.intellij.refactoring.classMembers.MemberInfoModel;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.psi.PyElement;
|
||||
import com.jetbrains.python.refactoring.classes.PyMemberInfoStorage;
|
||||
import com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Presenter that has not preview. Children should implement {@link #refactorNoPreview()}.
|
||||
* To "preview" button would be displayed
|
||||
*
|
||||
* @param <T> view for this presenter
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
|
||||
public abstract class MembersBasedPresenterNoPreviewImpl<T extends MembersBasedView<?>> extends MembersBasedPresenterImpl<T> {
|
||||
/**
|
||||
*
|
||||
* @param view view for this presenter
|
||||
* @param view view for this presenter
|
||||
* @param classUnderRefactoring class to refactor
|
||||
* @param infoStorage info storage
|
||||
* @param infoStorage info storage
|
||||
* @param model Member model (to be used for dependencies checking)
|
||||
*/
|
||||
protected MembersBasedPresenterNoPreviewImpl(@NotNull final T view,
|
||||
@NotNull final PyClass classUnderRefactoring,
|
||||
@NotNull final PyMemberInfoStorage infoStorage) {
|
||||
super(view, classUnderRefactoring, infoStorage);
|
||||
@NotNull final PyMemberInfoStorage infoStorage,
|
||||
@NotNull final MemberInfoModel<PyElement, PyMemberInfo<PyElement>> model) {
|
||||
super(view, classUnderRefactoring, infoStorage, model);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -36,12 +41,7 @@ public abstract class MembersBasedPresenterNoPreviewImpl<T extends MembersBasedV
|
||||
CommandProcessor.getInstance().executeCommand(myClassUnderRefactoring.getProject(), new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
refactorNoPreview();
|
||||
}
|
||||
});
|
||||
ApplicationManager.getApplication().runWriteAction(new MyRunnableRefactoring());
|
||||
}
|
||||
}, getCommandName(), null);
|
||||
myView.close();
|
||||
@@ -57,4 +57,11 @@ public abstract class MembersBasedPresenterNoPreviewImpl<T extends MembersBasedV
|
||||
* Do refactor with out of preview. Implement this method to do refactoring.
|
||||
*/
|
||||
protected abstract void refactorNoPreview();
|
||||
|
||||
private class MyRunnableRefactoring implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
refactorNoPreview();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+7
-2
@@ -1,8 +1,11 @@
|
||||
package com.jetbrains.python.refactoring.classes.membersManager.vp;
|
||||
|
||||
import com.intellij.refactoring.BaseRefactoringProcessor;
|
||||
import com.intellij.refactoring.classMembers.MemberInfoModel;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.psi.PyElement;
|
||||
import com.jetbrains.python.refactoring.classes.PyMemberInfoStorage;
|
||||
import com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
@@ -20,11 +23,13 @@ public abstract class MembersBasedPresenterWithPreviewImpl<T extends MembersBase
|
||||
* @param view view for this presenter
|
||||
* @param classUnderRefactoring class to refactor
|
||||
* @param infoStorage info storage
|
||||
* @param model Member model (to be used for dependencies checking)
|
||||
*/
|
||||
protected MembersBasedPresenterWithPreviewImpl(@NotNull final T view,
|
||||
@NotNull final PyClass classUnderRefactoring,
|
||||
@NotNull final PyMemberInfoStorage infoStorage) {
|
||||
super(view, classUnderRefactoring, infoStorage);
|
||||
@NotNull final PyMemberInfoStorage infoStorage,
|
||||
@NotNull final MemberInfoModel<PyElement, PyMemberInfo<PyElement>> model) {
|
||||
super(view, classUnderRefactoring, infoStorage, model);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+9
-3
@@ -1,8 +1,8 @@
|
||||
package com.jetbrains.python.refactoring.classes.membersManager.vp;
|
||||
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.refactoring.BaseRefactoringProcessor;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.psi.PyElement;
|
||||
import com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -22,10 +22,16 @@ public interface MembersBasedView<C extends MembersViewInitializationInfo> {
|
||||
/**
|
||||
* Display conflict dialogs.
|
||||
*
|
||||
* @param conflicts conflicts.
|
||||
* @param duplicatesConflict duplicates conflicts : that means destination class has the same member.
|
||||
* If member "foo" already exists in class "bar": pass [bar] -] [foo].
|
||||
* @param dependenciesConflicts dependency conflict: list of elements used by member under refactoring and would not be available
|
||||
* at new destination. If user wants to move method, that uses field "bar" which would not be available at new class,
|
||||
* pass [bar] field
|
||||
* @return true if user's choice is "continue". False if "cancel"
|
||||
*/
|
||||
boolean showConflictsDialog(@NotNull MultiMap<PsiElement, String> conflicts);
|
||||
boolean showConflictsDialog(
|
||||
@NotNull MultiMap<PyClass, PyMemberInfo<?>> duplicatesConflict,
|
||||
@NotNull Collection<PyMemberInfo<?>> dependenciesConflicts);
|
||||
|
||||
/**
|
||||
* Displays error message
|
||||
|
||||
+11
-6
@@ -2,13 +2,15 @@ package com.jetbrains.python.refactoring.classes.membersManager.vp;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.DialogWrapper;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.refactoring.BaseRefactoringProcessor;
|
||||
import com.intellij.refactoring.ui.ConflictsDialog;
|
||||
import com.intellij.refactoring.ui.RefactoringDialog;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.psi.PyElement;
|
||||
import com.jetbrains.python.refactoring.classes.membersManager.MembersConflictDialog;
|
||||
import com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo;
|
||||
import com.jetbrains.python.refactoring.classes.ui.PyMemberSelectionPanel;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -72,10 +74,12 @@ public abstract class MembersBasedViewSwingImpl<P extends MembersBasedPresenter,
|
||||
//TODO: Take this from presenter to prevent inconsistence: now it is possible to create view that supports abstract backed by presenter that does not. And vice versa.
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean showConflictsDialog(@NotNull final MultiMap<PsiElement, String> conflicts) {
|
||||
Preconditions.checkArgument(!conflicts.isEmpty(), "Can't show dialog for empty conflicts");
|
||||
final ConflictsDialog conflictsDialog = new ConflictsDialog(myProject, conflicts);
|
||||
public boolean showConflictsDialog(@NotNull final MultiMap<PyClass, PyMemberInfo<?>> duplicatesConflict,
|
||||
@NotNull final Collection<PyMemberInfo<?>> dependenciesConflicts) {
|
||||
Preconditions.checkArgument(!(duplicatesConflict.isEmpty() && dependenciesConflicts.isEmpty()), "Can't show dialog for empty conflicts");
|
||||
final DialogWrapper conflictsDialog = new MembersConflictDialog(myProject, duplicatesConflict, dependenciesConflicts);
|
||||
conflictsDialog.show();
|
||||
return conflictsDialog.isOK();
|
||||
}
|
||||
@@ -133,6 +137,7 @@ public abstract class MembersBasedViewSwingImpl<P extends MembersBasedPresenter,
|
||||
public void initAndShow() {
|
||||
Preconditions.checkArgument(myConfigured, "Not configured, run 'configure' first!");
|
||||
init();
|
||||
myPyMemberSelectionPanel.redraw(); // To display errors for checked member
|
||||
show();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2013 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 com.jetbrains.python.refactoring.classes.pullUp;
|
||||
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.refactoring.RefactoringBundle;
|
||||
import com.intellij.refactoring.util.RefactoringUIUtil;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.psi.PyElement;
|
||||
import com.jetbrains.python.psi.PyFunction;
|
||||
import com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* @author Dennis.Ushakov
|
||||
*/
|
||||
final class PyPullUpConflictsUtil {
|
||||
private PyPullUpConflictsUtil() {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
static MultiMap<PsiElement, String> checkConflicts(final Collection<PyMemberInfo<PyElement>> infos, @NotNull final PyClass superClass) {
|
||||
final MultiMap<PsiElement, String> conflictsList = new MultiMap<PsiElement, String>();
|
||||
for (PyMemberInfo<PyElement> info : infos) {
|
||||
PsiElement member = info.getMember();
|
||||
boolean isConflict = false;
|
||||
//TODO: Delegate to MemeberManagers here
|
||||
if (member instanceof PyFunction) {
|
||||
final String name = ((PyFunction)member).getName();
|
||||
if (name == null) continue;
|
||||
final PyFunction superClassMethod = superClass.findMethodByName(name, false);
|
||||
isConflict = superClassMethod != null;
|
||||
} else if (member instanceof PyClass) {
|
||||
final PyClass clazz = (PyClass)member;
|
||||
for (PyClass aClass : superClass.getSuperClasses()) {
|
||||
if (aClass == clazz) {
|
||||
conflictsList.putValue(superClass,
|
||||
RefactoringUIUtil.getDescription(superClass, false) + " already extends " + RefactoringUIUtil.getDescription(clazz, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isConflict) {
|
||||
final String message = RefactoringBundle.message("0.already.contains.a.1",
|
||||
RefactoringUIUtil.getDescription(superClass, false),
|
||||
RefactoringUIUtil.getDescription(member, false));
|
||||
conflictsList.putValue(superClass, message);
|
||||
}
|
||||
}
|
||||
|
||||
return conflictsList;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.jetbrains.python.refactoring.classes.pullUp;
|
||||
|
||||
import com.intellij.refactoring.classMembers.AbstractUsesDependencyMemberInfoModel;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.psi.PyElement;
|
||||
import com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Dependencies model for PyPullUp refactoring
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
class PyPullUpInfoModel extends AbstractUsesDependencyMemberInfoModel<PyElement, PyClass, PyMemberInfo<PyElement>> {
|
||||
@NotNull
|
||||
private final PyPullUpView myView;
|
||||
|
||||
|
||||
PyPullUpInfoModel(@NotNull final PyClass classUnderRefactoring,
|
||||
@NotNull final PyPullUpView view) {
|
||||
super(classUnderRefactoring, null, false);
|
||||
myView = view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAbstractEnabled(final PyMemberInfo<PyElement> member) {
|
||||
return member.isCouldBeAbstract() && isMemberEnabled(member); // TODO: copy paste with other models, get rid of
|
||||
}
|
||||
|
||||
@Override
|
||||
public int checkForProblems(@NotNull final PyMemberInfo<PyElement> member) {
|
||||
return member.isChecked() ? OK : super.checkForProblems(member);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected int doCheck(@NotNull final PyMemberInfo<PyElement> memberInfo, final int problem) {
|
||||
return problem;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMemberEnabled(final PyMemberInfo<PyElement> member) {
|
||||
final PyClass currentSuperClass = myView.getSelectedParent();
|
||||
if (member.getMember() instanceof PyClass) {
|
||||
//TODO: Delegate to Memebers Managers
|
||||
final PyClass memberClass = (PyClass)member.getMember();
|
||||
if (memberClass.isSubclass(currentSuperClass) || currentSuperClass.isSubclass(memberClass)) {
|
||||
return false; //Class is already parent of superclass
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+5
-52
@@ -17,11 +17,8 @@ package com.jetbrains.python.refactoring.classes.pullUp;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.refactoring.BaseRefactoringProcessor;
|
||||
import com.intellij.refactoring.classMembers.AbstractUsesDependencyMemberInfoModel;
|
||||
import com.intellij.refactoring.util.CommonRefactoringUtil;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.psi.PyElement;
|
||||
import com.jetbrains.python.psi.PyUtil;
|
||||
@@ -48,7 +45,7 @@ class PyPullUpPresenterImpl extends MembersBasedPresenterWithPreviewImpl<PyPullU
|
||||
* @param clazz class to refactor
|
||||
*/
|
||||
PyPullUpPresenterImpl(@NotNull final PyPullUpView view, @NotNull final PyMemberInfoStorage infoStorage, @NotNull final PyClass clazz) {
|
||||
super(view, clazz, infoStorage);
|
||||
super(view, clazz, infoStorage, new PyPullUpInfoModel(clazz, view));
|
||||
myParents = PyAncestorsUtils.getAncestorsUnderUserControl(clazz);
|
||||
Preconditions.checkArgument(!myParents.isEmpty(), "No parents found");
|
||||
}
|
||||
@@ -57,7 +54,7 @@ class PyPullUpPresenterImpl extends MembersBasedPresenterWithPreviewImpl<PyPullU
|
||||
@Override
|
||||
public void launch() {
|
||||
myView.configure(
|
||||
new PyPullUpViewInitializationInfo(new PyPullUpInfoModel(), myStorage.getClassMemberInfos(myClassUnderRefactoring), myParents));
|
||||
new PyPullUpViewInitializationInfo(myModel, myStorage.getClassMemberInfos(myClassUnderRefactoring), myParents));
|
||||
myView.initAndShow();
|
||||
}
|
||||
|
||||
@@ -93,54 +90,10 @@ class PyPullUpPresenterImpl extends MembersBasedPresenterWithPreviewImpl<PyPullU
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public MultiMap<PsiElement, String> getConflicts() {
|
||||
final Collection<PyMemberInfo<PyElement>> infos = myView.getSelectedMemberInfos();
|
||||
final PyClass superClass = myView.getSelectedParent();
|
||||
return PyPullUpConflictsUtil.checkConflicts(infos, superClass);
|
||||
}
|
||||
|
||||
private class PyPullUpInfoModel extends AbstractUsesDependencyMemberInfoModel<PyElement, PyClass, PyMemberInfo<PyElement>> {
|
||||
|
||||
PyPullUpInfoModel() {
|
||||
super(myClassUnderRefactoring, null, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAbstractEnabled(final PyMemberInfo<PyElement> member) {
|
||||
return member.isCouldBeAbstract() && isMemberEnabled(member); // TODO: copy paste with other models, get rid of
|
||||
}
|
||||
|
||||
@Override
|
||||
public int checkForProblems(@NotNull final PyMemberInfo<PyElement> member) {
|
||||
return member.isChecked() ? OK : super.checkForProblems(member);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected int doCheck(@NotNull final PyMemberInfo<PyElement> memberInfo, final int problem) {
|
||||
if (problem == ERROR && memberInfo.isStatic()) {
|
||||
return WARNING;
|
||||
}
|
||||
return problem;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMemberEnabled(final PyMemberInfo<PyElement> member) {
|
||||
final PyClass currentSuperClass = myView.getSelectedParent();
|
||||
if (member.getMember() instanceof PyClass) {
|
||||
//TODO: Delegate to Memebers Managers
|
||||
final PyClass memberClass = (PyClass)member.getMember();
|
||||
if (memberClass.isSubclass(currentSuperClass) || currentSuperClass.isSubclass(memberClass)) {
|
||||
return false; //Class is already parent of superclass
|
||||
}
|
||||
}
|
||||
if (!PyPullUpConflictsUtil.checkConflicts(Collections.singletonList(member), myView.getSelectedParent()).isEmpty()) {
|
||||
return false; //Member has conflict
|
||||
}
|
||||
return (!myStorage.getDuplicatedMemberInfos(currentSuperClass).contains(member)) && member.getMember() != currentSuperClass;
|
||||
}
|
||||
@Override
|
||||
protected Iterable<? extends PyClass> getDestClassesToCheckConflicts() {
|
||||
return Collections.singletonList(myView.getSelectedParent());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
-115
@@ -1,115 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2013 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 com.jetbrains.python.refactoring.classes.pushDown;
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.refactoring.RefactoringBundle;
|
||||
import com.intellij.refactoring.util.RefactoringUIUtil;
|
||||
import com.intellij.util.Function;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.resolve.PyResolveContext;
|
||||
import com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Dennis.Ushakov
|
||||
*/
|
||||
class PyPushDownConflicts {
|
||||
private static final Logger LOG = Logger.getInstance(PyPushDownProcessor.class.getName());
|
||||
|
||||
private final PyClass myClass;
|
||||
private final Collection<PyMemberInfo<PyElement>> myMembers;
|
||||
private final MultiMap<PsiElement, String> myConflicts;
|
||||
|
||||
public PyPushDownConflicts(final PyClass clazz, final Collection<PyMemberInfo<PyElement>> members) {
|
||||
myClass = clazz;
|
||||
myMembers = members;
|
||||
myConflicts = new MultiMap<PsiElement, String>();
|
||||
}
|
||||
|
||||
MultiMap<PsiElement, String> getConflicts() {
|
||||
return myConflicts;
|
||||
}
|
||||
|
||||
void checkTargetClassConflicts(PyClass clazz) {
|
||||
checkPlacementConflicts(clazz);
|
||||
}
|
||||
|
||||
private void checkPlacementConflicts(PyClass clazz) {
|
||||
for (PyMemberInfo<PyElement> member : myMembers) {
|
||||
final PyElement element = member.getMember();
|
||||
if (element instanceof PyFunction) {
|
||||
for (PyFunction function : clazz.getMethods()) {
|
||||
if (Comparing.strEqual(function.getName(), element.getName())) {
|
||||
final String message = RefactoringBundle.message("0.is.already.overridden.in.1",
|
||||
RefactoringUIUtil.getDescription(element, false),
|
||||
RefactoringUIUtil.getDescription(clazz, false));
|
||||
myConflicts.putValue(element, message);
|
||||
}
|
||||
}
|
||||
} else if (element instanceof PyClass) {
|
||||
} else LOG.error("unmatched member class " + clazz.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
public void checkSourceClassConflicts() {
|
||||
final List<PyElement> elements = ContainerUtil.map(myMembers, new Function<PyMemberInfo<PyElement>, PyElement>() {
|
||||
public PyElement fun(PyMemberInfo<PyElement> pyMemberInfo) {
|
||||
return pyMemberInfo.getMember();
|
||||
}
|
||||
});
|
||||
for (PyFunction pyFunction : myClass.getMethods()) {
|
||||
final UsedMembersCollector collector = new UsedMembersCollector(elements);
|
||||
pyFunction.accept(collector);
|
||||
final List<PyElement> conflicts = collector.getCollection();
|
||||
|
||||
for (PyElement conflict : conflicts) {
|
||||
final String message = RefactoringBundle.message("0.uses.1.which.is.pushed.down",
|
||||
RefactoringUIUtil.getDescription(pyFunction, false),
|
||||
RefactoringUIUtil.getDescription(conflict, false));
|
||||
myConflicts.putValue(pyFunction, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class UsedMembersCollector extends PyRecursiveElementVisitor {
|
||||
private final List<PyElement> myCollection = new ArrayList<PyElement>();
|
||||
private final Collection<PyElement> myMovedMembers;
|
||||
|
||||
private UsedMembersCollector(Collection<PyElement> movedMembers) {
|
||||
myMovedMembers = movedMembers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPyCallExpression(PyCallExpression node) {
|
||||
final Callable function = node.resolveCalleeFunction(PyResolveContext.noImplicits());
|
||||
if (function != null && myMovedMembers.contains(function)) {
|
||||
myCollection.add(function);
|
||||
}
|
||||
}
|
||||
|
||||
public List<PyElement> getCollection() {
|
||||
return myCollection;
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
-8
@@ -1,10 +1,8 @@
|
||||
package com.jetbrains.python.refactoring.classes.pushDown;
|
||||
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.refactoring.BaseRefactoringProcessor;
|
||||
import com.intellij.refactoring.classMembers.UsedByDependencyMemberInfoModel;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.psi.PyElement;
|
||||
import com.jetbrains.python.psi.PyUtil;
|
||||
@@ -25,7 +23,7 @@ public class PyPushDownPresenterImpl extends MembersBasedPresenterWithPreviewImp
|
||||
@NotNull final PyPushDownView view,
|
||||
@NotNull final PyClass classUnderRefactoring,
|
||||
@NotNull final PyMemberInfoStorage infoStorage) {
|
||||
super(view, classUnderRefactoring, infoStorage);
|
||||
super(view, classUnderRefactoring, infoStorage, new UsedByDependencyMemberInfoModel<PyElement, PyClass, PyMemberInfo<PyElement>>(classUnderRefactoring));
|
||||
myProject = project;
|
||||
}
|
||||
|
||||
@@ -37,16 +35,14 @@ public class PyPushDownPresenterImpl extends MembersBasedPresenterWithPreviewImp
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected MultiMap<PsiElement, String> getConflicts() {
|
||||
return new PyPushDownConflicts(myClassUnderRefactoring, myStorage.getClassMemberInfos(myClassUnderRefactoring)).getConflicts();
|
||||
protected Iterable<? extends PyClass> getDestClassesToCheckConflicts() {
|
||||
return PyPushDownProcessor.getInheritors(myClassUnderRefactoring);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void launch() {
|
||||
UsedByDependencyMemberInfoModel<PyElement, PyClass, PyMemberInfo<PyElement>> model =
|
||||
new UsedByDependencyMemberInfoModel<PyElement, PyClass, PyMemberInfo<PyElement>>(myClassUnderRefactoring);
|
||||
myView
|
||||
.configure(new MembersViewInitializationInfo(model, PyUtil.filterOutObject(myStorage.getClassMemberInfos(myClassUnderRefactoring))));
|
||||
.configure(new MembersViewInitializationInfo(myModel, PyUtil.filterOutObject(myStorage.getClassMemberInfos(myClassUnderRefactoring))));
|
||||
myView.initAndShow();
|
||||
}
|
||||
}
|
||||
|
||||
+10
-1
@@ -44,10 +44,19 @@ public class PyPushDownProcessor extends PyMembersRefactoringBaseProcessor {
|
||||
|
||||
@NotNull
|
||||
private static PyClass[] getChildren(@NotNull final PyClass from) {
|
||||
Collection<PyClass> all = PyClassInheritorsSearch.search(from, false).findAll();
|
||||
final Collection<PyClass> all = getInheritors(from);
|
||||
return all.toArray(new PyClass[all.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param from class to check for inheritors
|
||||
* @return inheritors of class
|
||||
*/
|
||||
@NotNull
|
||||
static Collection<PyClass> getInheritors(@NotNull final PyClass from) {
|
||||
return PyClassInheritorsSearch.search(from, false).findAll();
|
||||
}
|
||||
|
||||
|
||||
public String getProcessedElementsHeader() {
|
||||
return HEADER;
|
||||
|
||||
@@ -104,6 +104,6 @@ public class PyMemberSelectionPanel extends JPanel {
|
||||
* Redraws table. Call it when some new data is available.
|
||||
*/
|
||||
public void redraw() {
|
||||
myTable.fireExternalDataChange();
|
||||
myTable.redraw();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,4 +59,24 @@ class Bar(object):
|
||||
|
||||
class Foo(Bar):
|
||||
def __init__(self):
|
||||
self.foo = 12
|
||||
self.foo = 12
|
||||
|
||||
|
||||
class ParentWithConflicts(Bar):
|
||||
CLASS_FIELD = 42
|
||||
def __init__(self):
|
||||
self.instance_field = 12
|
||||
|
||||
def my_func(self):
|
||||
pass
|
||||
|
||||
|
||||
class ChildWithConflicts(ParentWithConflicts, Bar): # Bar -> conflict
|
||||
CLASS_FIELD = 42 # Conflict
|
||||
GOOD_FIELD = 32
|
||||
def __init__(self):
|
||||
self.instance_field = 12 # Conflict
|
||||
self.good_instance_field = "egg"
|
||||
|
||||
def my_func(self): # Conflict
|
||||
pass
|
||||
@@ -0,0 +1,39 @@
|
||||
class EmptyParent:pass
|
||||
|
||||
class SomeParent:
|
||||
PARENT_CLASS_FIELD = 42
|
||||
|
||||
def __init__(self):
|
||||
self.parent_instance_field = "egg"
|
||||
|
||||
def parent_func(self):
|
||||
pass
|
||||
|
||||
|
||||
class ChildWithDependencies(SomeParent, EmptyParent):
|
||||
CLASS_FIELD_FOO = 42
|
||||
CLASS_FIELD_DEPENDS_ON_CLASS_FIELD_FOO = CLASS_FIELD_FOO
|
||||
CLASS_FIELD_DEPENDS_ON_PARENT_FIELD = SomeParent.PARENT_CLASS_FIELD
|
||||
|
||||
def __init__(self):
|
||||
SomeParent.__init__(self)
|
||||
self.instance_field_bar = 42
|
||||
self.depends_on_instance_field_bar = self.instance_field_bar
|
||||
self.depends_on_class_field_foo = ChildWithDependencies.CLASS_FIELD_FOO
|
||||
|
||||
def normal_method(self):
|
||||
pass
|
||||
|
||||
def method_depends_on_parent_method(self):
|
||||
self.parent_func()
|
||||
pass
|
||||
|
||||
def method_depends_on_parent_field(self):
|
||||
i = self.parent_instance_field
|
||||
pass
|
||||
|
||||
def method_depends_on_normal_method(self):
|
||||
self.normal_method()
|
||||
|
||||
def method_depends_on_instance_field_bar(self):
|
||||
eggs = self.instance_field_bar
|
||||
@@ -41,6 +41,7 @@ import com.jetbrains.python.PythonHelpersLocator;
|
||||
import com.jetbrains.python.PythonMockSdk;
|
||||
import com.jetbrains.python.PythonTestUtil;
|
||||
import com.jetbrains.python.psi.LanguageLevel;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.psi.PyFile;
|
||||
import com.jetbrains.python.psi.impl.PyFileImpl;
|
||||
import com.jetbrains.python.psi.impl.PythonLanguageLevelPusher;
|
||||
@@ -130,6 +131,15 @@ public abstract class PyTestCase extends UsefulTestCase {
|
||||
assertNull(PARSED_ERROR_MSG, ((PyFileImpl)file).getTreeElement());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name
|
||||
* @return class by its name from file
|
||||
*/
|
||||
@NotNull
|
||||
protected PyClass getClassByName(@NotNull final String name) {
|
||||
return myFixture.findElementByText("class " + name, PyClass.class);
|
||||
}
|
||||
|
||||
protected static class PyLightProjectDescriptor implements LightProjectDescriptor {
|
||||
private final String myPythonVersion;
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.jetbrains.python.refactoring.classes;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.jetbrains.python.psi.PyElement;
|
||||
import com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Transforms {@link com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo} to its display names
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public class NameTransformer implements Function<PyMemberInfo<? extends PyElement>, String> {
|
||||
/**
|
||||
* To be used instead of creation
|
||||
*/
|
||||
public static final NameTransformer INSTANCE = new NameTransformer();
|
||||
|
||||
private NameTransformer() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String apply(@NotNull final PyMemberInfo<? extends PyElement> input) {
|
||||
return input.getDisplayName();
|
||||
}
|
||||
}
|
||||
+23
-9
@@ -6,6 +6,7 @@ import com.jetbrains.python.fixtures.PyTestCase;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.psi.PyElement;
|
||||
import com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo;
|
||||
import com.jetbrains.python.refactoring.classes.membersManager.vp.MembersBasedPresenter;
|
||||
import com.jetbrains.python.refactoring.classes.membersManager.vp.MembersBasedView;
|
||||
import com.jetbrains.python.refactoring.classes.membersManager.vp.MembersViewInitializationInfo;
|
||||
import org.easymock.Capture;
|
||||
@@ -83,6 +84,13 @@ public abstract class PyRefactoringPresenterTestCase<C extends MembersViewInitia
|
||||
myMocksControl = new MocksControl(MocksControl.MockType.NICE);
|
||||
myView = myMocksControl.createMock(myViewClass);
|
||||
|
||||
configureMockCapture();
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures view to capture config info
|
||||
*/
|
||||
private void configureMockCapture() {
|
||||
myViewConfigCapture = new Capture<C>();
|
||||
|
||||
myView.configure(EasyMock.capture(myViewConfigCapture));
|
||||
@@ -90,15 +98,6 @@ public abstract class PyRefactoringPresenterTestCase<C extends MembersViewInitia
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param name
|
||||
* @return class by its name from file
|
||||
*/
|
||||
@NotNull
|
||||
protected PyClass getClassByName(@NotNull final String name) {
|
||||
return myFixture.findElementByText("class " + name, PyClass.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return collection of members displayed by presenter
|
||||
*/
|
||||
@@ -117,4 +116,19 @@ public abstract class PyRefactoringPresenterTestCase<C extends MembersViewInitia
|
||||
return input.getName();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns member infos presenter wants to display
|
||||
* @param presenter presenter to check
|
||||
* @return collection of member infos
|
||||
*/
|
||||
@NotNull
|
||||
protected Collection<PyMemberInfo<PyElement>> getMemberInfos(@NotNull MembersBasedPresenter presenter) {
|
||||
myMocksControl.replay();
|
||||
presenter.launch();
|
||||
final Collection<PyMemberInfo<PyElement>> result = myViewConfigCapture.getValue().getMemberInfos();
|
||||
myMocksControl.reset();
|
||||
configureMockCapture();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-3
@@ -53,7 +53,7 @@ public class PyExtractSuperclassPresenterTest
|
||||
*
|
||||
* @param py3k if py 3?
|
||||
*/
|
||||
private void ensureStaticNoObject( final boolean py3k) {
|
||||
private void ensureStaticNoObject(final boolean py3k) {
|
||||
final Collection<PyPresenterTestMemberEntry> members = launchAndGetMembers("StaticOnly");
|
||||
|
||||
final Matcher<Iterable<? extends PyPresenterTestMemberEntry>> matcher =
|
||||
@@ -77,7 +77,6 @@ public class PyExtractSuperclassPresenterTest
|
||||
sut.okClicked();
|
||||
|
||||
Assert.assertTrue("No error displayed empty list of selected members", errorMessageCapture.hasCaptured());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -101,7 +100,6 @@ public class PyExtractSuperclassPresenterTest
|
||||
sut.okClicked();
|
||||
|
||||
Assert.assertTrue("No error displayed for invalid class name", errorMessageCapture.hasCaptured());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+135
@@ -0,0 +1,135 @@
|
||||
package com.jetbrains.python.refactoring.classes.pullUp;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.intellij.refactoring.classMembers.MemberInfoModel;
|
||||
import com.jetbrains.NotNullPredicate;
|
||||
import com.jetbrains.python.fixtures.PyTestCase;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.psi.PyElement;
|
||||
import com.jetbrains.python.refactoring.classes.PyMemberInfoStorage;
|
||||
import com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo;
|
||||
import org.easymock.internal.MocksControl;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.junit.Assert;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Tests dependencies management for PyPullUp refactoring
|
||||
*
|
||||
* @author Ilya.Kazakevich
|
||||
*/
|
||||
public class PyPullUpInfoModelTest extends PyTestCase {
|
||||
private PyPullUpInfoModel mySut;
|
||||
private List<PyMemberInfo<PyElement>> myMemberInfos;
|
||||
|
||||
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
myFixture.configureByFile("/refactoring/pullup/pyPullUpInfoModel.py");
|
||||
final PyClass childClass = getClassByName("ChildWithDependencies");
|
||||
final PyClass parentClass = getClassByName("SomeParent");
|
||||
mySut = new PyPullUpInfoModel(childClass, new MocksControl(MocksControl.MockType.NICE).createMock(PyPullUpView.class));
|
||||
mySut.setSuperClass(parentClass);
|
||||
myMemberInfos = new PyMemberInfoStorage(childClass).getClassMemberInfos(childClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks class field depends on class field
|
||||
*/
|
||||
public void testClassMemberDependencies() throws Exception {
|
||||
checkMembers("CLASS_FIELD_DEPENDS_ON_CLASS_FIELD_FOO");
|
||||
Assert.assertThat("Class member dependencies failed", getErrorMemberNames(), Matchers.containsInAnyOrder("CLASS_FIELD_FOO"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks instance field depends on class field
|
||||
*/
|
||||
public void testInstanceMemberDependencies() throws Exception {
|
||||
checkMembers("self.depends_on_class_field_foo");
|
||||
Assert.assertThat("Instance member dependencies failed", getErrorMemberNames(), Matchers.containsInAnyOrder("CLASS_FIELD_FOO"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks method depends on another class field
|
||||
*/
|
||||
public void testMethodMemberDependencies() throws Exception {
|
||||
checkMembers("method_depends_on_normal_method(self)");
|
||||
Assert.assertThat("Method dependencies failed", getErrorMemberNames(), Matchers.containsInAnyOrder("normal_method(self)"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks method depends on method
|
||||
*/
|
||||
public void testMethodOnInstanceMemberDependencies() throws Exception {
|
||||
checkMembers("method_depends_on_instance_field_bar(self)");
|
||||
Assert.assertThat("Instance on member dependencies failed", getErrorMemberNames(), Matchers.containsInAnyOrder("self.instance_field_bar"));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* All dependencies are met: new (destination) class has all of them
|
||||
*/
|
||||
public void testParentDependenciesOk() throws Exception {
|
||||
checkMembers("CLASS_FIELD_DEPENDS_ON_PARENT_FIELD",
|
||||
"method_depends_on_parent_method(self)",
|
||||
"method_depends_on_parent_field(self)");
|
||||
Assert.assertThat("Dependence check false positive: parent has all required members", getErrorMemberNames(), Matchers.empty());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* New (destination) class has no members, required by member under refactoring.
|
||||
* Error should be displayed.
|
||||
*/
|
||||
public void testNoParentDependenciesOk() throws Exception {
|
||||
mySut.setSuperClass(getClassByName("EmptyParent"));
|
||||
checkMembers("CLASS_FIELD_DEPENDS_ON_PARENT_FIELD",
|
||||
"method_depends_on_parent_method(self)",
|
||||
"method_depends_on_parent_field(self)");
|
||||
Assert.assertThat("Dependence check false positive: parent has all required members", getErrorMemberNames(), Matchers.contains("extends SomeParent"));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return names of error members (displayed with red)
|
||||
*/
|
||||
@NotNull
|
||||
private List<String> getErrorMemberNames() {
|
||||
final List<String> result = new ArrayList<String>();
|
||||
for (final PyMemberInfo<PyElement> info : myMemberInfos) {
|
||||
if (mySut.checkForProblems(info) != MemberInfoModel.OK) {
|
||||
result.add(info.getDisplayName());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks members to be moved (sets checkbox on them)
|
||||
* @param memberNames names of members to check
|
||||
*/
|
||||
private void checkMembers(@NotNull final String... memberNames) {
|
||||
for (final String memberName : memberNames) {
|
||||
Iterables.find(myMemberInfos, new NamePredicate(memberName)).setChecked(true);
|
||||
}
|
||||
mySut.memberInfoChanged(myMemberInfos);
|
||||
}
|
||||
|
||||
private static class NamePredicate extends NotNullPredicate<PyMemberInfo<?>> {
|
||||
@NotNull
|
||||
private final String myNameToSearch;
|
||||
|
||||
private NamePredicate(@NotNull final String nameToSearch) {
|
||||
myNameToSearch = nameToSearch;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean applyNotNull(@NotNull final PyMemberInfo<?> input) {
|
||||
return input.getDisplayName().equals(myNameToSearch);
|
||||
}
|
||||
}
|
||||
}
|
||||
+35
-1
@@ -1,11 +1,16 @@
|
||||
package com.jetbrains.python.refactoring.classes.pullUp;
|
||||
|
||||
import com.google.common.collect.Collections2;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
import com.jetbrains.python.psi.LanguageLevel;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.psi.PyElement;
|
||||
import com.jetbrains.python.refactoring.classes.NameTransformer;
|
||||
import com.jetbrains.python.refactoring.classes.PyMemberInfoStorage;
|
||||
import com.jetbrains.python.refactoring.classes.PyPresenterTestMemberEntry;
|
||||
import com.jetbrains.python.refactoring.classes.PyRefactoringPresenterTestCase;
|
||||
import com.jetbrains.python.refactoring.classes.membersManager.PyMemberInfo;
|
||||
import org.easymock.Capture;
|
||||
import org.easymock.EasyMock;
|
||||
import org.hamcrest.Matcher;
|
||||
import org.hamcrest.Matchers;
|
||||
@@ -49,6 +54,35 @@ public class PyPullUpPresenterTest extends PyRefactoringPresenterTestCase<PyPull
|
||||
ensureNoMembers("NoParentsAllowed");
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that presenter displays conflicts if destination class already has that members
|
||||
*/
|
||||
public void testConflicts() throws Exception {
|
||||
final PyPullUpPresenterImpl sut = configureByClass("ChildWithConflicts");
|
||||
final Collection<PyMemberInfo<PyElement>> infos = getMemberInfos(sut);
|
||||
|
||||
final Capture<MultiMap<PyClass, PyMemberInfo<?>>> conflictCapture = new Capture<MultiMap<PyClass, PyMemberInfo<?>>>();
|
||||
EasyMock.expect(myView.showConflictsDialog(EasyMock.capture(conflictCapture), EasyMock.<Collection<PyMemberInfo<?>>>anyObject())).andReturn(false).anyTimes();
|
||||
EasyMock.expect(myView.getSelectedMemberInfos()).andReturn(infos).anyTimes();
|
||||
final PyClass parent = getClassByName("ParentWithConflicts");
|
||||
EasyMock.expect(myView.getSelectedParent()).andReturn(parent).anyTimes();
|
||||
myMocksControl.replay();
|
||||
sut.okClicked();
|
||||
|
||||
final MultiMap<PyClass, PyMemberInfo<?>> conflictMap = conflictCapture.getValue();
|
||||
Assert.assertTrue("No conflicts found, while it should", conflictMap.containsKey(parent));
|
||||
final Collection<String> conflictedMemberNames = Collections2.transform(conflictMap.get(parent), NameTransformer.INSTANCE);
|
||||
Assert.assertThat("Failed to find right conflicts", conflictedMemberNames, Matchers.containsInAnyOrder(
|
||||
"extends Bar",
|
||||
"CLASS_FIELD",
|
||||
"self.instance_field",
|
||||
"my_func(self)",
|
||||
"__init__(self)"
|
||||
));
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that refactoring does not work for classes with out of members
|
||||
*/
|
||||
@@ -104,7 +138,7 @@ public class PyPullUpPresenterTest extends PyRefactoringPresenterTestCase<PyPull
|
||||
new PyPresenterTestMemberEntry("CLASS_FIELD", true, true, false),
|
||||
new PyPresenterTestMemberEntry("__init__(self)", true, false, false),
|
||||
new PyPresenterTestMemberEntry("extends SubParent1", false, false, false),
|
||||
new PyPresenterTestMemberEntry("foo(self)", false, false, false),
|
||||
new PyPresenterTestMemberEntry("foo(self)", true, false, true),
|
||||
new PyPresenterTestMemberEntry("bar(self)", true, false, true),
|
||||
new PyPresenterTestMemberEntry("static_1(cls)", true, true, py3K),
|
||||
new PyPresenterTestMemberEntry("static_2()", true, true, py3K),
|
||||
|
||||
Reference in New Issue
Block a user