diff --git a/idea/src/org/jetbrains/jet/plugin/refactoring/JetRefactoringUtil.java b/idea/src/org/jetbrains/jet/plugin/refactoring/JetRefactoringUtil.java index 380a33c545c..109974dc939 100644 --- a/idea/src/org/jetbrains/jet/plugin/refactoring/JetRefactoringUtil.java +++ b/idea/src/org/jetbrains/jet/plugin/refactoring/JetRefactoringUtil.java @@ -21,10 +21,10 @@ 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.*; import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.PsiUtilBase; +import com.intellij.psi.util.PsiUtilCore; import com.intellij.ui.components.JBList; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -85,7 +85,8 @@ public class JetRefactoringUtil { return; } ArrayList expressions = new ArrayList(); - while (element != null && !(element instanceof JetBlockExpression) && !(element instanceof JetNamedFunction) + while (element != null && !(element instanceof JetBlockExpression && !(element.getParent() instanceof JetFunctionLiteral)) && + !(element instanceof JetNamedFunction) && !(element instanceof JetClassBody) && !(element instanceof JetSecondaryConstructor)) { if (element instanceof JetExpression && !(element instanceof JetStatementExpression)) { boolean addExpression = true; @@ -132,7 +133,6 @@ public class JetRefactoringUtil { @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)); @@ -179,7 +179,7 @@ public class JetRefactoringUtil { } @Nullable - private static JetExpression findExpression(@NotNull Editor editor, @NotNull PsiFile file, + public 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 || @@ -202,6 +202,66 @@ public class JetRefactoringUtil { return (JetExpression) element; } + @NotNull + public static PsiElement[] findStatements(@NotNull PsiFile file, int startOffset, int endOffset) { + PsiElement element1 = file.findElementAt(startOffset); + PsiElement element2 = file.findElementAt(endOffset - 1); + if (element1 instanceof PsiWhiteSpace) { + startOffset = element1.getTextRange().getEndOffset(); + element1 = file.findElementAt(startOffset); + } + if (element2 instanceof PsiWhiteSpace) { + endOffset = element2.getTextRange().getStartOffset(); + element2 = file.findElementAt(endOffset - 1); + } + if (element1 == null || element2 == null) return PsiElement.EMPTY_ARRAY; + + PsiElement parent = PsiTreeUtil.findCommonParent(element1, element2); + if (parent == null) return PsiElement.EMPTY_ARRAY; + while (true) { + if (parent instanceof JetBlockExpression) break; + if (parent == null || parent instanceof JetFile) return PsiElement.EMPTY_ARRAY; + parent = parent.getParent(); + } + + if (!parent.equals(element1)) { + while (!parent.equals(element1.getParent())) { + element1 = element1.getParent(); + } + } + if (startOffset != element1.getTextRange().getStartOffset()) return PsiElement.EMPTY_ARRAY; + + if (!parent.equals(element2)) { + while (!parent.equals(element2.getParent())) { + element2 = element2.getParent(); + } + } + if (endOffset != element2.getTextRange().getEndOffset()) return PsiElement.EMPTY_ARRAY; + + PsiElement[] children = parent.getChildren(); + ArrayList array = new ArrayList(); + boolean flag = false; + for (PsiElement child : children) { + if (child.equals(element1)) { + flag = true; + } + if (flag && !(child instanceof PsiWhiteSpace)) { + array.add(child); + } + if (child.equals(element2)) { + break; + } + } + + for (PsiElement element : array) { + if (!(element instanceof JetExpression || element instanceof PsiWhiteSpace || element instanceof PsiComment)) { + return PsiElement.EMPTY_ARRAY; + } + } + + return PsiUtilCore.toPsiElementArray(array); + } + public static class IntroduceRefactoringException extends Exception { private String myMessage; diff --git a/idea/src/org/jetbrains/jet/plugin/refactoring/introduceVariable/JetIntroduceVariableHandler.java b/idea/src/org/jetbrains/jet/plugin/refactoring/introduceVariable/JetIntroduceVariableHandler.java index cddd0bb8568..73868932c9d 100644 --- a/idea/src/org/jetbrains/jet/plugin/refactoring/introduceVariable/JetIntroduceVariableHandler.java +++ b/idea/src/org/jetbrains/jet/plugin/refactoring/introduceVariable/JetIntroduceVariableHandler.java @@ -16,6 +16,7 @@ package org.jetbrains.jet.plugin.refactoring.introduceVariable; +import com.google.common.base.Predicate; import com.intellij.codeInsight.PsiEquivalenceUtil; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.application.ApplicationManager; @@ -34,13 +35,24 @@ 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.di.InjectorForMacros; +import org.jetbrains.jet.di.InjectorForTopDownAnalyzerForJvm; +import org.jetbrains.jet.lang.cfg.pseudocode.JetControlFlowDataTraceFactory; import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; +import org.jetbrains.jet.lang.descriptors.ModuleDescriptor; import org.jetbrains.jet.lang.psi.*; -import org.jetbrains.jet.lang.resolve.BindingContext; +import org.jetbrains.jet.lang.resolve.*; +import org.jetbrains.jet.lang.resolve.calls.autocasts.DataFlowInfo; import org.jetbrains.jet.lang.resolve.java.AnalyzerFacadeForJVM; +import org.jetbrains.jet.lang.resolve.java.CompilerDependencies; +import org.jetbrains.jet.lang.resolve.java.CompilerSpecialMode; +import org.jetbrains.jet.lang.resolve.name.Name; +import org.jetbrains.jet.lang.resolve.scopes.JetScope; import org.jetbrains.jet.lang.types.JetType; import org.jetbrains.jet.lang.types.NamespaceType; +import org.jetbrains.jet.lang.types.TypeUtils; import org.jetbrains.jet.lang.types.checker.JetTypeChecker; +import org.jetbrains.jet.lang.types.expressions.ExpressionTypingServices; import org.jetbrains.jet.lang.types.lang.JetStandardLibrary; import org.jetbrains.jet.lexer.JetTokens; import org.jetbrains.jet.plugin.codeInsight.ReferenceToClassesShortening; @@ -48,6 +60,7 @@ import org.jetbrains.jet.plugin.project.AnalyzeSingleFileUtil; import org.jetbrains.jet.plugin.refactoring.*; import org.jetbrains.jet.resolve.DescriptorRenderer; +import java.io.File; import java.util.*; /** @@ -97,7 +110,6 @@ public class JetIntroduceVariableHandler extends JetIntroduceHandlerBase { return; } else if (expression.getParent() instanceof JetCallElement) { if (expression instanceof JetFunctionLiteralExpression) { - noTypeInference = true; needParentheses = true; } else { showErrorHint(project, editor, JetRefactoringBundle.message("cannot.refactor.no.expression")); @@ -113,6 +125,23 @@ public class JetIntroduceVariableHandler extends JetIntroduceHandlerBase { } BindingContext bindingContext = AnalyzeSingleFileUtil.getContextForSingleFile((JetFile)expression.getContainingFile()); final JetType expressionType = bindingContext.get(BindingContext.EXPRESSION_TYPE, expression); //can be null or error type + JetScope scope = bindingContext.get(BindingContext.RESOLUTION_SCOPE, expression); + if (scope != null) { + DataFlowInfo dataFlowInfo = bindingContext.get(BindingContext.NON_DEFAULT_EXPRESSION_DATA_FLOW, expression); + if (dataFlowInfo == null) { + dataFlowInfo = DataFlowInfo.EMPTY; + } + + ObservableBindingTrace bindingTrace = new ObservableBindingTrace(new BindingTraceContext()); + InjectorForMacros injector = new InjectorForMacros(project); + JetType typeNoExpectedType = injector.getExpressionTypingServices().getType(scope, expression, + TypeUtils.NO_EXPECTED_TYPE, dataFlowInfo, + bindingTrace); + if (expressionType != null && typeNoExpectedType != null && !JetTypeChecker.INSTANCE.equalTypes(expressionType, + typeNoExpectedType)) { + noTypeInference = true; + } + } if (expressionType instanceof NamespaceType) { showErrorHint(project, editor, JetRefactoringBundle.message("cannot.refactor.namespace.expression")); return; diff --git a/idea/testData/refactoring/introduceVariable/FunctionLiteral.kt b/idea/testData/refactoring/introduceVariable/FunctionLiteral.kt index eec79ae40b0..d8e687f7f8f 100644 --- a/idea/testData/refactoring/introduceVariable/FunctionLiteral.kt +++ b/idea/testData/refactoring/introduceVariable/FunctionLiteral.kt @@ -3,7 +3,7 @@ fun foo(c : Collection){ } /* fun foo(c : Collection){ - val function: () -> Boolean = {it; false} + val function = {it; false} c.filter(function) } */ \ No newline at end of file diff --git a/idea/testData/refactoring/introduceVariable/FunctionLiteralFromExpected.kt b/idea/testData/refactoring/introduceVariable/FunctionLiteralFromExpected.kt new file mode 100644 index 00000000000..7b7b9066273 --- /dev/null +++ b/idea/testData/refactoring/introduceVariable/FunctionLiteralFromExpected.kt @@ -0,0 +1,17 @@ +class A { + fun foo() = 1 +} +fun apply(x: (A) -> Int) = 2 +fun test() { + apply{it.foo()} +} +/* +class A { + fun foo() = 1 +} +fun apply(x: (A) -> Int) = 2 +fun test() { + val function: (A) -> Int = {it.foo()} + apply(function) +} +*/ \ 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 index e75e211460c..965e4af6110 100644 --- a/idea/tests/org/jetbrains/jet/plugin/refactoring/introduceVariable/JetIntroduceVariableTest.java +++ b/idea/tests/org/jetbrains/jet/plugin/refactoring/introduceVariable/JetIntroduceVariableTest.java @@ -57,6 +57,10 @@ public class JetIntroduceVariableTest extends LightCodeInsightFixtureTestCase { doTest(); } + public void testFunctionLiteralFromExpected() { + doTest(); + } + public void testIfCondition() { doTest(); }