From c8b4e5ec7480ff90bcd6b252a8562283871927a8 Mon Sep 17 00:00:00 2001 From: Alefas Date: Wed, 1 Feb 2012 19:42:06 +0400 Subject: [PATCH] Introduce Variable feature (in pre-alpha quality). Problems: 1. Still problems with containers, due to new for me JetContainerNode. 2. Needs name suggester 3. Needs name validator 4. Needs type annotation adding 5. Needs changing to var and checking write access of usages 6. Needs final modifier adding 7. Some problems on class and file level. Should be fixed. Problems with inplace rename on this positions. Additionally possibly should be added proper work with expressions on class body level and namespace/file level. It parsed as error now. 8. Needs much more tests than just 6. --- .../jet/lang/parsing/JetParserDefinition.java | 6 +- .../jet/lang/psi/JetBlockExpression.java | 2 +- .../jet/lang/psi/JetBreakExpression.java | 2 +- .../jet/lang/psi/JetClassInitializer.java | 2 +- .../jet/lang/psi/JetClassObject.java | 2 +- .../jet/lang/psi/JetContinueExpression.java | 2 +- .../jetbrains/jet/lang/psi/JetFunction.java | 5 + .../JetLabelQualifiedInstanceExpression.java | 3 +- .../jet/lang/psi/JetLoopExpression.java | 2 +- .../jet/lang/psi/JetNamedDeclaration.java | 2 +- .../jetbrains/jet/lang/psi/JetProperty.java | 17 + .../jet/lang/psi/JetReturnExpression.java | 2 +- .../jet/lang/psi/JetSecondaryConstructor.java | 2 +- .../jet/lang/psi/JetSimpleNameExpression.java | 8 + .../jet/lang/psi/JetStatementExpression.java | 15 + .../jetbrains/jet/parsing/JetParsingTest.java | 3 +- idea/src/META-INF/plugin.xml | 1 + .../refactoring/JetIntroduceHandlerBase.java | 10 + .../plugin/refactoring/JetNameSuggester.java | 29 ++ .../refactoring/JetRefactoringBundle.java | 37 +++ .../JetRefactoringBundle.properties | 5 + .../JetRefactoringSupportProvider.java | 31 ++ .../refactoring/JetRefactoringUtil.java | 157 +++++++++ .../JetInplaceVariableIntroducer.java | 46 +++ .../JetIntroduceVariableHandler.java | 313 ++++++++++++++++++ .../introduceVariable/ClassBody.kt | 9 + .../introduceVariable/DoWhileAddBlock.kt | 14 + .../introduceVariable/FileLevel.kt | 5 + .../introduceVariable/FunctionAddBlock.kt | 6 + .../introduceVariable/IfCondition.kt | 11 + .../introduceVariable/IfElseAddBlock.kt | 12 + .../introduceVariable/IfThenAddBlock.kt | 11 + .../refactoring/introduceVariable/Simple.kt | 8 + .../introduceVariable/WhenAddBlock.kt | 14 + .../introduceVariable/WhileAddBlock.kt | 14 + .../JetIntroduceVariableTest.java | 86 +++++ 36 files changed, 881 insertions(+), 13 deletions(-) create mode 100644 compiler/frontend/src/org/jetbrains/jet/lang/psi/JetStatementExpression.java create mode 100644 idea/src/org/jetbrains/jet/plugin/refactoring/JetIntroduceHandlerBase.java create mode 100644 idea/src/org/jetbrains/jet/plugin/refactoring/JetNameSuggester.java create mode 100644 idea/src/org/jetbrains/jet/plugin/refactoring/JetRefactoringBundle.java create mode 100644 idea/src/org/jetbrains/jet/plugin/refactoring/JetRefactoringBundle.properties create mode 100644 idea/src/org/jetbrains/jet/plugin/refactoring/JetRefactoringSupportProvider.java create mode 100644 idea/src/org/jetbrains/jet/plugin/refactoring/JetRefactoringUtil.java create mode 100644 idea/src/org/jetbrains/jet/plugin/refactoring/introduceVariable/JetInplaceVariableIntroducer.java create mode 100644 idea/src/org/jetbrains/jet/plugin/refactoring/introduceVariable/JetIntroduceVariableHandler.java create mode 100644 idea/testData/refactoring/introduceVariable/ClassBody.kt create mode 100644 idea/testData/refactoring/introduceVariable/DoWhileAddBlock.kt create mode 100644 idea/testData/refactoring/introduceVariable/FileLevel.kt create mode 100644 idea/testData/refactoring/introduceVariable/FunctionAddBlock.kt create mode 100644 idea/testData/refactoring/introduceVariable/IfCondition.kt create mode 100644 idea/testData/refactoring/introduceVariable/IfElseAddBlock.kt create mode 100644 idea/testData/refactoring/introduceVariable/IfThenAddBlock.kt create mode 100644 idea/testData/refactoring/introduceVariable/Simple.kt create mode 100644 idea/testData/refactoring/introduceVariable/WhenAddBlock.kt create mode 100644 idea/testData/refactoring/introduceVariable/WhileAddBlock.kt create mode 100644 idea/tests/org/jetbrains/jet/plugin/refactoring/introduceVariable/JetIntroduceVariableTest.java diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/parsing/JetParserDefinition.java b/compiler/frontend/src/org/jetbrains/jet/lang/parsing/JetParserDefinition.java index c7c898e1180..823e6394eff 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/parsing/JetParserDefinition.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/parsing/JetParserDefinition.java @@ -25,8 +25,10 @@ import org.jetbrains.jet.lexer.JetTokens; public class JetParserDefinition implements ParserDefinition { public JetParserDefinition() { - if (!ApplicationManager.getApplication().isCommandLine()) { - } + //todo: ApplicationManager.getApplication() is null during JetParsingTest setting up + + /*if (!ApplicationManager.getApplication().isCommandLine()) { + }*/ } @NotNull diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetBlockExpression.java b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetBlockExpression.java index cf8074088aa..c59d75bb54e 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetBlockExpression.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetBlockExpression.java @@ -9,7 +9,7 @@ import java.util.List; /** * @author max */ -public class JetBlockExpression extends JetExpression { +public class JetBlockExpression extends JetExpression implements JetStatementExpression { public JetBlockExpression(@NotNull ASTNode node) { super(node); } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetBreakExpression.java b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetBreakExpression.java index cd2037985fb..8d05f373d85 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetBreakExpression.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetBreakExpression.java @@ -6,7 +6,7 @@ import org.jetbrains.annotations.NotNull; /** * @author max */ -public class JetBreakExpression extends JetLabelQualifiedExpression { +public class JetBreakExpression extends JetLabelQualifiedExpression implements JetStatementExpression { public JetBreakExpression(@NotNull ASTNode node) { super(node); } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetClassInitializer.java b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetClassInitializer.java index a2a93ee1f48..d2c97e6270c 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetClassInitializer.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetClassInitializer.java @@ -6,7 +6,7 @@ import org.jetbrains.annotations.NotNull; /** * @author max */ -public class JetClassInitializer extends JetDeclaration { +public class JetClassInitializer extends JetDeclaration implements JetStatementExpression { public JetClassInitializer(@NotNull ASTNode node) { super(node); } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetClassObject.java b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetClassObject.java index 4147cc3dd20..7b7b6717c5b 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetClassObject.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetClassObject.java @@ -8,7 +8,7 @@ import org.jetbrains.jet.JetNodeTypes; /** * @author max */ -public class JetClassObject extends JetDeclaration { +public class JetClassObject extends JetDeclaration implements JetStatementExpression { public JetClassObject(@NotNull ASTNode node) { super(node); } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetContinueExpression.java b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetContinueExpression.java index 7dec7aedd95..f6c124ba380 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetContinueExpression.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetContinueExpression.java @@ -6,7 +6,7 @@ import org.jetbrains.annotations.NotNull; /** * @author max */ -public class JetContinueExpression extends JetLabelQualifiedExpression { +public class JetContinueExpression extends JetLabelQualifiedExpression implements JetStatementExpression { public JetContinueExpression(@NotNull ASTNode node) { super(node); } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetFunction.java b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetFunction.java index 67b0b7c6b85..063085d7d8e 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetFunction.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetFunction.java @@ -81,4 +81,9 @@ abstract public class JetFunction extends JetTypeParameterListOwner public JetElement asElement() { return this; } + + public boolean isLocal() { + PsiElement parent = getParent(); + return !(parent instanceof JetFile || parent instanceof JetClassBody || parent instanceof JetNamespaceBody); + } } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetLabelQualifiedInstanceExpression.java b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetLabelQualifiedInstanceExpression.java index 297830432d7..6832da45771 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetLabelQualifiedInstanceExpression.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetLabelQualifiedInstanceExpression.java @@ -7,7 +7,8 @@ import org.jetbrains.jet.JetNodeTypes; /** * @author abreslav */ -public abstract class JetLabelQualifiedInstanceExpression extends JetLabelQualifiedExpression { +public abstract class JetLabelQualifiedInstanceExpression extends JetLabelQualifiedExpression + implements JetStatementExpression { public JetLabelQualifiedInstanceExpression(@NotNull ASTNode node) { super(node); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetLoopExpression.java b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetLoopExpression.java index 1bf26d49171..9322326bd30 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetLoopExpression.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetLoopExpression.java @@ -8,7 +8,7 @@ import org.jetbrains.jet.JetNodeTypes; /** * @author abreslav */ -public abstract class JetLoopExpression extends JetExpression { +public abstract class JetLoopExpression extends JetExpression implements JetStatementExpression { public JetLoopExpression(@NotNull ASTNode node) { super(node); } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetNamedDeclaration.java b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetNamedDeclaration.java index dc37a770c64..0179fab1e42 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetNamedDeclaration.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetNamedDeclaration.java @@ -11,7 +11,7 @@ import org.jetbrains.jet.lexer.JetTokens; /** * @author max */ -public abstract class JetNamedDeclaration extends JetDeclaration implements PsiNameIdentifierOwner { +public abstract class JetNamedDeclaration extends JetDeclaration implements PsiNameIdentifierOwner, JetStatementExpression { public JetNamedDeclaration(@NotNull ASTNode node) { super(node); } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetProperty.java b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetProperty.java index ccacaa8a4d4..012492e54c9 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetProperty.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetProperty.java @@ -2,6 +2,8 @@ package org.jetbrains.jet.lang.psi; import com.intellij.lang.ASTNode; import com.intellij.psi.PsiElement; +import com.intellij.psi.search.LocalSearchScope; +import com.intellij.psi.search.SearchScope; import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.TokenSet; import com.intellij.psi.util.PsiTreeUtil; @@ -39,6 +41,21 @@ public class JetProperty extends JetTypeParameterListOwner implements JetModifie return getNode().findChildByType(JetTokens.VAR_KEYWORD) != null; } + public boolean isLocal() { + PsiElement parent = getParent(); + return !(parent instanceof JetFile || parent instanceof JetClassBody || parent instanceof JetNamespaceBody); + } + + @NotNull + @Override + public SearchScope getUseScope() { + if (isLocal()) { + PsiElement block = PsiTreeUtil.getParentOfType(this, JetBlockExpression.class, JetClassInitializer.class); + if (block == null) return super.getUseScope(); + else return new LocalSearchScope(block); + } else return super.getUseScope(); + } + @Nullable public JetTypeReference getReceiverTypeRef() { ASTNode node = getNode().getFirstChildNode(); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetReturnExpression.java b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetReturnExpression.java index 396a0f22515..94177a29ea7 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetReturnExpression.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetReturnExpression.java @@ -7,7 +7,7 @@ import org.jetbrains.annotations.Nullable; /** * @author max */ -public class JetReturnExpression extends JetLabelQualifiedExpression { +public class JetReturnExpression extends JetLabelQualifiedExpression implements JetStatementExpression { public JetReturnExpression(@NotNull ASTNode node) { super(node); } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetSecondaryConstructor.java b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetSecondaryConstructor.java index 6d20610771c..accea61b0a2 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetSecondaryConstructor.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetSecondaryConstructor.java @@ -12,7 +12,7 @@ import java.util.List; /** * @author max */ -public class JetSecondaryConstructor extends JetDeclaration implements JetDeclarationWithBody { +public class JetSecondaryConstructor extends JetDeclaration implements JetDeclarationWithBody, JetStatementExpression { public JetSecondaryConstructor(@NotNull ASTNode node) { super(node); } diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetSimpleNameExpression.java b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetSimpleNameExpression.java index fc2d71a0772..133e8581b4c 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetSimpleNameExpression.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetSimpleNameExpression.java @@ -83,6 +83,14 @@ public class JetSimpleNameExpression extends JetReferenceExpression { return ReferenceProvidersRegistry.getReferencesFromProviders(this, PsiReferenceService.Hints.NO_HINTS); } + @Nullable + @Override + public PsiReference getReference() { + PsiReference[] references = getReferences(); + if (references.length == 1) return references[0]; + else return null; + } + @Override public void accept(@NotNull JetVisitorVoid visitor) { visitor.visitSimpleNameExpression(this); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetStatementExpression.java b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetStatementExpression.java new file mode 100644 index 00000000000..aaa44a9ebb0 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/jet/lang/psi/JetStatementExpression.java @@ -0,0 +1,15 @@ +package org.jetbrains.jet.lang.psi; + +/** + * User: Alefas + * Date: 31.01.12 + */ + +/** + * This is an interface to show that {@link JetExpression} is not + * actually an expression (in meaning that this expression can be placed after "val x = "). + * This is possibly redundant interface, all inheritors of this interface should be refactored that they are not + * {@link JetExpression}, after such refactoring, this interface can be removed. + */ +public interface JetStatementExpression { +} diff --git a/compiler/tests/org/jetbrains/jet/parsing/JetParsingTest.java b/compiler/tests/org/jetbrains/jet/parsing/JetParsingTest.java index 48433f12b24..85446a53257 100644 --- a/compiler/tests/org/jetbrains/jet/parsing/JetParsingTest.java +++ b/compiler/tests/org/jetbrains/jet/parsing/JetParsingTest.java @@ -70,7 +70,8 @@ public class JetParsingTest extends ParsingTestCase { Method[] methods = elem.getClass().getDeclaredMethods(); for (Method method : methods) { String methodName = method.getName(); - if (!methodName.startsWith("get") && !methodName.startsWith("find") || methodName.equals("getReference") || methodName.equals("getReferences")) continue; + if (!methodName.startsWith("get") && !methodName.startsWith("find") || methodName.equals("getReference") || + methodName.equals("getReferences") || methodName.equals("getUseScope")) continue; if (method.getParameterTypes().length > 0) continue; Class declaringClass = method.getDeclaringClass(); if (!declaringClass.getName().startsWith("org.jetbrains.jet")) continue; diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 476da74b585..e21de172e4b 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -57,6 +57,7 @@ + diff --git a/idea/src/org/jetbrains/jet/plugin/refactoring/JetIntroduceHandlerBase.java b/idea/src/org/jetbrains/jet/plugin/refactoring/JetIntroduceHandlerBase.java new file mode 100644 index 00000000000..ca3ffb8071b --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/refactoring/JetIntroduceHandlerBase.java @@ -0,0 +1,10 @@ +package org.jetbrains.jet.plugin.refactoring; + +import com.intellij.refactoring.RefactoringActionHandler; + +/** + * User: Alefas + * Date: 25.01.12 + */ +public abstract class JetIntroduceHandlerBase implements RefactoringActionHandler { +} diff --git a/idea/src/org/jetbrains/jet/plugin/refactoring/JetNameSuggester.java b/idea/src/org/jetbrains/jet/plugin/refactoring/JetNameSuggester.java new file mode 100644 index 00000000000..4b676dd0a49 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/refactoring/JetNameSuggester.java @@ -0,0 +1,29 @@ +package org.jetbrains.jet.plugin.refactoring; + +import org.jetbrains.jet.lang.psi.JetExpression; +import org.jetbrains.jet.lang.psi.JetFile; +import org.jetbrains.jet.lang.resolve.BindingContext; +import org.jetbrains.jet.lang.resolve.java.AnalyzerFacade; +import org.jetbrains.jet.lang.types.JetStandardLibrary; +import org.jetbrains.jet.lang.types.JetType; +import org.jetbrains.jet.lang.types.checker.JetTypeChecker; + +/** + * User: Alefas + * Date: 31.01.12 + */ +public class JetNameSuggester { + public static String[] suggestNames(JetExpression expression) { + String[] def = {"i"}; + BindingContext bindingContext = AnalyzerFacade.analyzeFileWithCache((JetFile) expression.getContainingFile(), + AnalyzerFacade.SINGLE_DECLARATION_PROVIDER); + JetType jetType = bindingContext.get(BindingContext.EXPRESSION_TYPE, expression); + if (jetType == null) return def; + + JetType booleanType = JetStandardLibrary.getJetStandardLibrary(expression.getProject()).getBooleanType(); + if (JetTypeChecker.INSTANCE.equalTypes(jetType, booleanType)) { + return new String[] {"b"}; + } + return new String[] {"i"}; //todo: + } +} diff --git a/idea/src/org/jetbrains/jet/plugin/refactoring/JetRefactoringBundle.java b/idea/src/org/jetbrains/jet/plugin/refactoring/JetRefactoringBundle.java new file mode 100644 index 00000000000..d004617799d --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/refactoring/JetRefactoringBundle.java @@ -0,0 +1,37 @@ +package org.jetbrains.jet.plugin.refactoring; + +import com.intellij.CommonBundle; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.PropertyKey; + +import java.lang.ref.Reference; +import java.lang.ref.SoftReference; +import java.util.ResourceBundle; + +/** + * User: Alefas + * Date: 25.01.12 + */ +public class JetRefactoringBundle { + private static Reference ourBundle; + + @NonNls + private static final String BUNDLE = "org.jetbrains.jet.plugin.refactoring.JetRefactoringBundle"; + + private JetRefactoringBundle() { + } + + public static String message(@NonNls @PropertyKey(resourceBundle = BUNDLE)String key, Object... params) { + return CommonBundle.message(getBundle(), key, params); + } + + private static ResourceBundle getBundle() { + ResourceBundle bundle = null; + if (ourBundle != null) bundle = ourBundle.get(); + if (bundle == null) { + bundle = ResourceBundle.getBundle(BUNDLE); + ourBundle = new SoftReference(bundle); + } + return bundle; + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/jet/plugin/refactoring/JetRefactoringBundle.properties b/idea/src/org/jetbrains/jet/plugin/refactoring/JetRefactoringBundle.properties new file mode 100644 index 00000000000..3f07febfbee --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/refactoring/JetRefactoringBundle.properties @@ -0,0 +1,5 @@ +cannot.refactor.not.expression=Cannot refactor not expression. +expressions.title=Expressions +introduce.variable=Introduce Variable +cannot.refactor.no.container=Cannot refactor without refactoring container. +cannot.refactor.no.expression=Cannot perform refactoring without an expression \ No newline at end of file diff --git a/idea/src/org/jetbrains/jet/plugin/refactoring/JetRefactoringSupportProvider.java b/idea/src/org/jetbrains/jet/plugin/refactoring/JetRefactoringSupportProvider.java new file mode 100644 index 00000000000..e1165b4c97b --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/refactoring/JetRefactoringSupportProvider.java @@ -0,0 +1,31 @@ +package org.jetbrains.jet.plugin.refactoring; + +import com.intellij.lang.refactoring.RefactoringSupportProvider; +import com.intellij.psi.PsiElement; +import com.intellij.refactoring.RefactoringActionHandler; +import org.jetbrains.jet.lang.psi.JetFunction; +import org.jetbrains.jet.lang.psi.JetProperty; +import org.jetbrains.jet.plugin.refactoring.introduceVariable.JetIntroduceVariableHandler; + +/** + * User: Alefas + * Date: 25.01.12 + */ +public class JetRefactoringSupportProvider extends RefactoringSupportProvider { + @Override + public RefactoringActionHandler getIntroduceVariableHandler() { + return new JetIntroduceVariableHandler(); + } + + @Override + public boolean isInplaceRenameAvailable(PsiElement element, PsiElement context) { + if (element instanceof JetProperty) { + JetProperty property = (JetProperty) element; + if (property.isLocal()) return true; + } else if (element instanceof JetFunction) { + JetFunction function = (JetFunction) element; + if (function.isLocal()) return true; + } + return false; + } +} diff --git a/idea/src/org/jetbrains/jet/plugin/refactoring/JetRefactoringUtil.java b/idea/src/org/jetbrains/jet/plugin/refactoring/JetRefactoringUtil.java new file mode 100644 index 00000000000..a6fdc87cd69 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/refactoring/JetRefactoringUtil.java @@ -0,0 +1,157 @@ +package org.jetbrains.jet.plugin.refactoring; + +import com.intellij.codeInsight.unwrap.ScopeHighlighter; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.ui.popup.JBPopupAdapter; +import com.intellij.openapi.ui.popup.JBPopupFactory; +import com.intellij.openapi.ui.popup.LightweightWindowEvent; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiWhiteSpace; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.ui.components.JBList; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.psi.*; + +import javax.swing.*; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import java.awt.*; +import java.util.ArrayList; + +/** + * User: Alefas + * Date: 25.01.12 + */ +public class JetRefactoringUtil { + + private JetRefactoringUtil() { + } + + public interface SelectExpressionCallback { + void run(@Nullable JetExpression expression); + } + + public static void selectExpression(@NotNull Editor editor, + @NotNull PsiFile file, + @NotNull SelectExpressionCallback callback) throws IntroduceRefactoringException { + if (editor.getSelectionModel().hasSelection()) { + int selectionStart = editor.getSelectionModel().getSelectionStart(); + int selectionEnd = editor.getSelectionModel().getSelectionEnd(); + String text = file.getText(); + while (selectionStart < selectionEnd && Character.isSpaceChar(text.charAt(selectionStart))) ++selectionStart; + while (selectionStart < selectionEnd && Character.isSpaceChar(text.charAt(selectionEnd - 1))) --selectionEnd; + callback.run(findExpression(editor, file, selectionStart, selectionEnd)); + } else { + int offset = editor.getCaretModel().getOffset(); + smartSelectExpression(editor, file, offset, callback); + } + } + + private static void smartSelectExpression(@NotNull Editor editor, @NotNull PsiFile file, int offset, + @NotNull final SelectExpressionCallback callback) + throws IntroduceRefactoringException { + if (offset < 0) throw new IntroduceRefactoringException(JetRefactoringBundle.message("cannot.refactor.not.expression")); + PsiElement element = file.findElementAt(offset); + if (element == null) throw new IntroduceRefactoringException(JetRefactoringBundle.message("cannot.refactor.not.expression")); + if (element instanceof PsiWhiteSpace) { + smartSelectExpression(editor, file, offset - 1, callback); + return; + } + ArrayList expressions = new ArrayList(); + while (element != null && !(element instanceof JetBlockExpression) && !(element instanceof JetNamedFunction) + && !(element instanceof JetClassBody) && !(element instanceof JetSecondaryConstructor)) { + if (element instanceof JetExpression && !(element instanceof JetStatementExpression)) { + expressions.add((JetExpression) element); + } + element = element.getParent(); + } + if (expressions.size() == 0) throw new IntroduceRefactoringException(JetRefactoringBundle.message("cannot.refactor.not.expression")); + + final DefaultListModel model = new DefaultListModel(); + for (JetExpression expression : expressions) { + model.addElement(expression); + } + + final ScopeHighlighter highlighter = new ScopeHighlighter(editor); + + final JList list = new JBList(model); + + list.setCellRenderer(new DefaultListCellRenderer() { + @Override + public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { + Component rendererComponent = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); + StringBuilder buffer = new StringBuilder(); + JetExpression element = (JetExpression) value; + if (element.isValid()) { + setText(getExpressionShortText(element)); + } + return rendererComponent; + } + }); + + list.addListSelectionListener(new ListSelectionListener() { + @Override + public void valueChanged(ListSelectionEvent e) { + highlighter.dropHighlight(); + int selectedIndex = list.getSelectedIndex(); + if (selectedIndex < 0) return; + JetExpression expression = (JetExpression) model.get(selectedIndex); + ArrayList toExtract = new ArrayList(); + toExtract.add(expression); + highlighter.highlight(expression, toExtract); + } + }); + + JBPopupFactory.getInstance().createListPopupBuilder(list). + setTitle(JetRefactoringBundle.message("expressions.title")).setMovable(false).setResizable(false). + setRequestFocus(true).setItemChoosenCallback(new Runnable() { + @Override + public void run() { + callback.run((JetExpression) list.getSelectedValue()); + } + }).addListener(new JBPopupAdapter() { + @Override + public void onClosed(LightweightWindowEvent event) { + highlighter.dropHighlight(); + } + }).createPopup().showInBestPositionFor(editor); + + } + + public static String getExpressionShortText(@NotNull JetExpression expression) { //todo: write appropriate implementation + String expressionText = expression.getText(); + if (expressionText.length() > 20) { + expressionText = expressionText.substring(0, 17) + "..."; + } + return expressionText; + } + + @Nullable + private static JetExpression findExpression(@NotNull Editor editor, @NotNull PsiFile file, + int startOffset, int endOffset) throws IntroduceRefactoringException{ + PsiElement element = PsiTreeUtil.findElementOfClassAtRange(file, startOffset, endOffset, JetExpression.class); + if (element == null || element.getTextRange().getStartOffset() != startOffset || + element.getTextRange().getEndOffset() != endOffset) { + //todo: if it's infix expression => add (), then commit document then return new created expression + throw new IntroduceRefactoringException(JetRefactoringBundle.message("cannot.refactor.not.expression")); + } else if (!(element instanceof JetExpression)) { + throw new IntroduceRefactoringException(JetRefactoringBundle.message("cannot.refactor.not.expression")); + } + return (JetExpression) element; + } + + public static class IntroduceRefactoringException extends Exception { + private String myMessage; + + public IntroduceRefactoringException(String message) { + myMessage = message; + } + + public String getMessage() { + return myMessage; + } + } + +} diff --git a/idea/src/org/jetbrains/jet/plugin/refactoring/introduceVariable/JetInplaceVariableIntroducer.java b/idea/src/org/jetbrains/jet/plugin/refactoring/introduceVariable/JetInplaceVariableIntroducer.java new file mode 100644 index 00000000000..c85824bf43f --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/refactoring/introduceVariable/JetInplaceVariableIntroducer.java @@ -0,0 +1,46 @@ +package org.jetbrains.jet.plugin.refactoring.introduceVariable; + +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiNamedElement; +import com.intellij.refactoring.introduce.inplace.InplaceVariableIntroducer; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.psi.JetExpression; +import org.jetbrains.jet.lang.psi.JetProperty; + +/** + * User: Alefas + * Date: 01.02.12 + */ +public class JetInplaceVariableIntroducer extends InplaceVariableIntroducer { + + private boolean myReplaceOccurrence; + private JetProperty myProperty; + + public JetInplaceVariableIntroducer(PsiNamedElement elementToRename, Editor editor, Project project, + String title, JetExpression[] occurrences, + @Nullable JetExpression expr, boolean replaceOccurrence, + JetProperty property) { + super(elementToRename, editor, project, title, occurrences, expr); + this.myReplaceOccurrence = replaceOccurrence; + myProperty = property; + } + + @Override + protected void moveOffsetAfter(boolean success) { + if (!myReplaceOccurrence || myExprMarker == null) { + myEditor.getCaretModel().moveToOffset(myProperty.getTextRange().getEndOffset()); + } else { + int startOffset = myExprMarker.getStartOffset(); + PsiFile file = myProperty.getContainingFile(); + PsiElement elementAt = file.findElementAt(startOffset); + if (elementAt != null) { + myEditor.getCaretModel().moveToOffset(elementAt.getTextRange().getEndOffset()); + } else { + myEditor.getCaretModel().moveToOffset(myExprMarker.getEndOffset()); + } + } + } +} diff --git a/idea/src/org/jetbrains/jet/plugin/refactoring/introduceVariable/JetIntroduceVariableHandler.java b/idea/src/org/jetbrains/jet/plugin/refactoring/introduceVariable/JetIntroduceVariableHandler.java new file mode 100644 index 00000000000..48f6d22fe02 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/refactoring/introduceVariable/JetIntroduceVariableHandler.java @@ -0,0 +1,313 @@ +package org.jetbrains.jet.plugin.refactoring.introduceVariable; + +import com.intellij.codeInsight.PsiEquivalenceUtil; +import com.intellij.openapi.actionSystem.DataContext; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.command.CommandProcessor; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Pass; +import com.intellij.openapi.util.Ref; +import com.intellij.psi.PsiDocumentManager; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.refactoring.HelpID; +import com.intellij.refactoring.introduce.inplace.OccurrencesChooser; +import com.intellij.refactoring.util.CommonRefactoringUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.psi.*; +import org.jetbrains.jet.plugin.refactoring.JetIntroduceHandlerBase; +import org.jetbrains.jet.plugin.refactoring.JetNameSuggester; +import org.jetbrains.jet.plugin.refactoring.JetRefactoringBundle; +import org.jetbrains.jet.plugin.refactoring.JetRefactoringUtil; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; + +/** + * User: Alefas + * Date: 25.01.12 + */ +public class JetIntroduceVariableHandler extends JetIntroduceHandlerBase { + + private static final String INTRODUCE_VARIABLE = JetRefactoringBundle.message("introduce.variable"); + + @Override + public void invoke(@NotNull final Project project, final Editor editor, final PsiFile file, DataContext dataContext) { + JetRefactoringUtil.SelectExpressionCallback callback = new JetRefactoringUtil.SelectExpressionCallback() { + @Override + public void run(@Nullable JetExpression expression) { + doRefactoring(project, editor, expression); + } + }; + try { + JetRefactoringUtil.selectExpression(editor, file, callback); + } catch (JetRefactoringUtil.IntroduceRefactoringException e) { + showErrorHint(project, editor, e.getMessage()); + } + } + + private static void doRefactoring(@NotNull final Project project, final Editor editor, @Nullable JetExpression _expression) { + if (_expression == null) { + showErrorHint(project, editor, JetRefactoringBundle.message("cannot.refactor.no.expression")); + return; + } + if (_expression.getParent() instanceof JetParenthesizedExpression) { + _expression = (JetExpression) _expression.getParent(); + } + final JetExpression expression = _expression; + final PsiElement container = getContainer(expression); + final PsiElement occurrenceContainer = getOccurrenceContainer(expression); + if (container == null) { + showErrorHint(project, editor, JetRefactoringBundle.message("cannot.refactor.no.container")); + return; + } + final boolean isInplaceAvailableOnDataContext = + editor.getSettings().isVariableInplaceRenameEnabled() && + !ApplicationManager.getApplication().isUnitTestMode(); + final ArrayList allOccurrences = findOccurrences(occurrenceContainer, expression); + Pass callback = new Pass() { + @Override + public void pass(OccurrencesChooser.ReplaceChoice replaceChoice) { + boolean replaceOccurrence = container != expression.getParent(); + final List allReplaces; + if (OccurrencesChooser.ReplaceChoice.ALL == replaceChoice) { + if (allOccurrences.size() > 1) replaceOccurrence = true; + allReplaces = allOccurrences; + } else { + allReplaces = Collections.singletonList(expression); + } + + String[] suggestedNames = JetNameSuggester.suggestNames(expression); //todo: add name validator + final LinkedHashSet suggestedNamesSet = new LinkedHashSet(); + Collections.addAll(suggestedNamesSet, suggestedNames); + PsiElement commonParent = PsiTreeUtil.findCommonParent(allReplaces); + PsiElement commonContainer = getContainer(commonParent); + final Ref propertyRef = new Ref(); + final ArrayList references = new ArrayList(); + final Ref reference = new Ref(); + final Runnable introduceRunnable = introduceVariable(project, expression, suggestedNames, allReplaces, commonContainer, + commonParent, replaceOccurrence, propertyRef, references, + reference); + final boolean finalReplaceOccurrence = replaceOccurrence; + CommandProcessor.getInstance().executeCommand(project, new Runnable() { + @Override + public void run() { + ApplicationManager.getApplication().runWriteAction(introduceRunnable); + JetProperty property = propertyRef.get(); + if (property != null) { + editor.getCaretModel().moveToOffset(property.getTextOffset()); + editor.getSelectionModel().removeSelection(); + if (isInplaceAvailableOnDataContext) { + JetInplaceVariableIntroducer variableIntroducer = + new JetInplaceVariableIntroducer(property, editor, project, INTRODUCE_VARIABLE, + references.toArray(new JetExpression[references.size()]), + reference.get(), finalReplaceOccurrence, + property); + PsiDocumentManager.getInstance(project). + doPostponedOperationsAndUnblockDocument(editor.getDocument()); + variableIntroducer.performInplaceRefactoring(suggestedNamesSet); + } + } + } + }, INTRODUCE_VARIABLE, null); + } + }; + if (isInplaceAvailableOnDataContext) { + OccurrencesChooser.simpleChooser(editor). + showChooser(expression, allOccurrences, callback); + } else { + callback.pass(OccurrencesChooser.ReplaceChoice.ALL); + } + } + + private static Runnable introduceVariable(final @NotNull Project project, final JetExpression expression, + final String[] suggestedNames, + final List allReplaces, final PsiElement commonContainer, + final PsiElement commonParent, final boolean replaceOccurrence, + final Ref propertyRef, + final ArrayList references, + final Ref reference) { + return new Runnable() { + @Override + public void run() { + String variableText = "val " + suggestedNames[0] + " = "; + if (expression instanceof JetParenthesizedExpression) { + JetParenthesizedExpression parenthesizedExpression = (JetParenthesizedExpression) expression; + JetExpression innerExpression = parenthesizedExpression.getExpression(); + if (innerExpression != null) variableText += innerExpression.getText(); + else variableText += expression.getText(); + } else variableText += expression.getText(); + JetProperty property = JetPsiFactory.createProperty(project, variableText); + if (property == null) return; + PsiElement anchor = commonParent; + if (anchor != commonContainer) { + while (anchor.getParent() != commonContainer) { + anchor = anchor.getParent(); + } + } else { + anchor = commonContainer.getFirstChild(); + int startOffset = commonContainer.getTextRange().getEndOffset(); + for (JetExpression expr : allReplaces) { + int offset = expr.getTextRange().getStartOffset(); + if (offset < startOffset) startOffset = offset; + } + while (anchor != null && !anchor.getTextRange().contains(startOffset)) { + anchor = anchor.getNextSibling(); + } + if (anchor == null) return; + } + boolean needBraces = !(commonContainer instanceof JetBlockExpression || + commonContainer instanceof JetClassBody || + commonContainer instanceof JetFile || + commonContainer instanceof JetClassInitializer); + if (!needBraces) { + property = (JetProperty) commonContainer.addBefore(property, anchor); + commonContainer.addBefore(JetPsiFactory.createWhiteSpace(project, "\n"), anchor); + } else { + JetExpression emptyBody = JetPsiFactory.createEmptyBody(project); + PsiElement firstChild = emptyBody.getFirstChild(); + emptyBody.addAfter(JetPsiFactory.createWhiteSpace(project, "\n"), firstChild); + property = (JetProperty) emptyBody.addAfter(property, firstChild); + emptyBody.addAfter(JetPsiFactory.createWhiteSpace(project, "\n"), firstChild); + anchor.replace(emptyBody); + } + for (JetExpression replace : allReplaces) { + if (replaceOccurrence) { + boolean isActualExpression = expression == replace; + JetExpression element = (JetExpression) replace.replace(JetPsiFactory.createExpression(project, suggestedNames[0])); + references.add(element); + if (isActualExpression) reference.set(element); + } else if (!needBraces) { + replace.delete(); + } + } + propertyRef.set(property); + } + }; + } + + private static ArrayList findOccurrences(PsiElement occurrenceContainer, @NotNull JetExpression expression) { + if (expression instanceof JetParenthesizedExpression) { + JetParenthesizedExpression parenthesizedExpression = (JetParenthesizedExpression) expression; + JetExpression innerExpression = parenthesizedExpression.getExpression(); + if (innerExpression != null) { + expression = innerExpression; + } + } + final JetExpression actualExpression = expression; + + final ArrayList result = new ArrayList(); + + JetVisitorVoid visitor = new JetVisitorVoid() { + @Override + public void visitJetElement(JetElement element) { + element.acceptChildren(this); + super.visitJetElement(element); + } + + @Override + public void visitExpression(JetExpression expression) { + if (PsiEquivalenceUtil.areElementsEquivalent(expression, actualExpression)) { + PsiElement parent = expression.getParent(); + if (parent instanceof JetParenthesizedExpression) { + result.add((JetParenthesizedExpression) parent); + } else { + result.add(expression); + } + } else { + super.visitExpression(expression); + } + } + }; + occurrenceContainer.accept(visitor); + return result; + } + + @Nullable + private static PsiElement getContainer(PsiElement place) { + if (place instanceof JetBlockExpression || place instanceof JetClassBody || place instanceof JetFile || + place instanceof JetClassInitializer) { + return place; + } + while (place != null) { + PsiElement parent = place.getParent(); + if (parent instanceof JetContainerNode) { + if (!(parent.getParent() instanceof JetIfExpression && + ((JetIfExpression) parent.getParent()).getCondition() == place)) { + return parent; + } + } if (parent instanceof JetBlockExpression || parent instanceof JetWhenEntry || + parent instanceof JetClassBody || parent instanceof JetFile || parent instanceof JetClassInitializer) { + return parent; + } else if (parent instanceof JetNamedFunction) { + JetNamedFunction function = (JetNamedFunction) parent; + if (function.getBodyExpression() == place) { + return parent; + } + } else if (parent instanceof JetSecondaryConstructor) { + JetSecondaryConstructor secondaryConstructor = (JetSecondaryConstructor) parent; + if (secondaryConstructor.getBodyExpression() == place) { + return parent; + } + } + place = parent; + } + return null; + } + + @Nullable + private static PsiElement getOccurrenceContainer(PsiElement place) { + PsiElement result = null; + while (place != null) { + PsiElement parent = place.getParent(); + if (parent instanceof JetContainerNode) { + if (!(place instanceof JetBlockExpression) && !(parent.getParent() instanceof JetIfExpression && + ((JetIfExpression) parent.getParent()).getCondition() == place)) { + result = parent; + } + } else if (parent instanceof JetClassBody || parent instanceof JetFile || parent instanceof JetClassInitializer) { + if (result == null) return parent; + else return result; + } else if (parent instanceof JetBlockExpression) { + result = parent; + } else if (parent instanceof JetWhenEntry ) { + if (!(place instanceof JetBlockExpression)) { + result = parent; + } + } else if (parent instanceof JetNamedFunction) { + JetNamedFunction function = (JetNamedFunction) parent; + if (function.getBodyExpression() == place) { + if (!(place instanceof JetBlockExpression)) { + result = parent; + } + } + } else if (parent instanceof JetSecondaryConstructor) { + JetSecondaryConstructor secondaryConstructor = (JetSecondaryConstructor) parent; + if (secondaryConstructor.getBodyExpression() == place) { + if (!(place instanceof JetBlockExpression)) { + result = parent; + } + } + } + place = parent; + } + return null; + } + + private static void showErrorHint(Project project, Editor editor, String message) { + if (ApplicationManager.getApplication().isUnitTestMode()) throw new RuntimeException(message); + CommonRefactoringUtil.showErrorHint(project, editor, message, + INTRODUCE_VARIABLE, + HelpID.INTRODUCE_VARIABLE); + } + + @Override + public void invoke(@NotNull Project project, @NotNull PsiElement[] elements, DataContext dataContext) { + //do nothing + } +} diff --git a/idea/testData/refactoring/introduceVariable/ClassBody.kt b/idea/testData/refactoring/introduceVariable/ClassBody.kt new file mode 100644 index 00000000000..a5bd81ba677 --- /dev/null +++ b/idea/testData/refactoring/introduceVariable/ClassBody.kt @@ -0,0 +1,9 @@ +open class A { + val x = 1 +} +/* +open class A { + val i = 1 + val x = i +} +*/ \ No newline at end of file diff --git a/idea/testData/refactoring/introduceVariable/DoWhileAddBlock.kt b/idea/testData/refactoring/introduceVariable/DoWhileAddBlock.kt new file mode 100644 index 00000000000..6a84f39c52a --- /dev/null +++ b/idea/testData/refactoring/introduceVariable/DoWhileAddBlock.kt @@ -0,0 +1,14 @@ +open class A { + { + do 1 while (true) + } +} +/* +open class A { + { + do { + val i = 1 + } while (true) + } +} +*/ \ No newline at end of file diff --git a/idea/testData/refactoring/introduceVariable/FileLevel.kt b/idea/testData/refactoring/introduceVariable/FileLevel.kt new file mode 100644 index 00000000000..9663d7878ad --- /dev/null +++ b/idea/testData/refactoring/introduceVariable/FileLevel.kt @@ -0,0 +1,5 @@ +fun x(x: Int = 1) {} +/* +val i = 1 +fun x(x: Int = i) {} +*/ \ No newline at end of file diff --git a/idea/testData/refactoring/introduceVariable/FunctionAddBlock.kt b/idea/testData/refactoring/introduceVariable/FunctionAddBlock.kt new file mode 100644 index 00000000000..8403106987d --- /dev/null +++ b/idea/testData/refactoring/introduceVariable/FunctionAddBlock.kt @@ -0,0 +1,6 @@ +fun x(): Int = 1 +/* +fun x(): Int { + val i = 1 +} +*/ \ No newline at end of file diff --git a/idea/testData/refactoring/introduceVariable/IfCondition.kt b/idea/testData/refactoring/introduceVariable/IfCondition.kt new file mode 100644 index 00000000000..f9eeed5f999 --- /dev/null +++ b/idea/testData/refactoring/introduceVariable/IfCondition.kt @@ -0,0 +1,11 @@ +fun a() { + if (true) 2 + else 1 +} +/* +fun a() { + val b = true + if (b) 2 + else 1 +} +*/ \ No newline at end of file diff --git a/idea/testData/refactoring/introduceVariable/IfElseAddBlock.kt b/idea/testData/refactoring/introduceVariable/IfElseAddBlock.kt new file mode 100644 index 00000000000..1c390070055 --- /dev/null +++ b/idea/testData/refactoring/introduceVariable/IfElseAddBlock.kt @@ -0,0 +1,12 @@ +fun a() { + if (true) 2 + else 1 +} +/* +fun a() { + if (true) 2 + else { + val i = 1 + } +} +*/ \ No newline at end of file diff --git a/idea/testData/refactoring/introduceVariable/IfThenAddBlock.kt b/idea/testData/refactoring/introduceVariable/IfThenAddBlock.kt new file mode 100644 index 00000000000..61e5cece18f --- /dev/null +++ b/idea/testData/refactoring/introduceVariable/IfThenAddBlock.kt @@ -0,0 +1,11 @@ +fun a() { + if (true) 1 + else 2 +} +/* +fun a() { + if (true) { + val i = 1 + } else 2 +} +*/ \ No newline at end of file diff --git a/idea/testData/refactoring/introduceVariable/Simple.kt b/idea/testData/refactoring/introduceVariable/Simple.kt new file mode 100644 index 00000000000..23c4d75fe91 --- /dev/null +++ b/idea/testData/refactoring/introduceVariable/Simple.kt @@ -0,0 +1,8 @@ +fun a() { + 1 +} +/* +fun a() { + val i = 1 +} +*/ \ No newline at end of file diff --git a/idea/testData/refactoring/introduceVariable/WhenAddBlock.kt b/idea/testData/refactoring/introduceVariable/WhenAddBlock.kt new file mode 100644 index 00000000000..a79b019167c --- /dev/null +++ b/idea/testData/refactoring/introduceVariable/WhenAddBlock.kt @@ -0,0 +1,14 @@ +fun a() { + when (1) { + is 1 -> 1 + } +} +/* +fun a() { + when (1) { + is 1 -> { + val i = 1 + } + } +} +*/ \ No newline at end of file diff --git a/idea/testData/refactoring/introduceVariable/WhileAddBlock.kt b/idea/testData/refactoring/introduceVariable/WhileAddBlock.kt new file mode 100644 index 00000000000..49b99c3bfd1 --- /dev/null +++ b/idea/testData/refactoring/introduceVariable/WhileAddBlock.kt @@ -0,0 +1,14 @@ +open class A { + { + while (true) 1 + } +} +/* +open class A { + { + while (true) { + val i = 1 + } + } +} +*/ \ No newline at end of file diff --git a/idea/tests/org/jetbrains/jet/plugin/refactoring/introduceVariable/JetIntroduceVariableTest.java b/idea/tests/org/jetbrains/jet/plugin/refactoring/introduceVariable/JetIntroduceVariableTest.java new file mode 100644 index 00000000000..168e4b221a7 --- /dev/null +++ b/idea/tests/org/jetbrains/jet/plugin/refactoring/introduceVariable/JetIntroduceVariableTest.java @@ -0,0 +1,86 @@ +package org.jetbrains.jet.plugin.refactoring.introduceVariable; + +import com.intellij.ide.DataManager; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.psi.PsiElement; +import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase; +import org.jetbrains.jet.lang.psi.JetFile; +import org.jetbrains.jet.lexer.JetTokens; +import org.jetbrains.jet.plugin.PluginTestCaseBase; + +/** + * User: Alefas + * Date: 31.01.12 + */ +public class JetIntroduceVariableTest extends LightCodeInsightFixtureTestCase { + public void testClassBody() { + doTest(); + } + + public void testDoWhileAddBlock() { + doTest(); + } + + /*public void testFileLevel() { + doTest(); + } + + public void testFunctionAddBlock() { + doTest(); + }*/ + + public void testIfCondition() { + doTest(); + } + + public void testIfElseAddBlock() { + doTest(); + } + + /*public void testIfThenAddBlock() { + doTest(); + }*/ + + public void testSimple() { + doTest(); + } + + /*public void testWhenAddBlock() { + doTest(); + }*/ + + public void testWhileAddBlock() { + doTest(); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + myFixture.setTestDataPath(PluginTestCaseBase.getTestDataPathBase() + "/refactoring/introduceVariable"); + } + + private void doTest() { + myFixture.configureByFile(getTestName(false) + ".kt"); + final JetFile file = (JetFile) myFixture.getFile(); + PsiElement lastChild = file.getLastChild(); + assert lastChild != null; + String expectedResultText = null; + if (lastChild.getNode().getElementType().equals(JetTokens.BLOCK_COMMENT)) { + String lastChildText = lastChild.getText(); + expectedResultText = lastChildText.substring(2, lastChildText.length() - 2).trim(); + } else if (lastChild.getNode().getElementType().equals(JetTokens.EOL_COMMENT)) { + expectedResultText = lastChild.getText().substring(2).trim(); + } + assert expectedResultText != null; + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + new JetIntroduceVariableHandler().invoke(getProject(), myFixture.getEditor(), file, + DataManager.getInstance().getDataContext(myFixture.getEditor(). + getComponent())); + } + }); + int endOffset = file.getLastChild().getTextRange().getStartOffset(); + assertEquals(expectedResultText, file.getText().substring(0, endOffset).trim()); + } +}