From aa4e4623d4be0fe21d434ad3202302322128b472 Mon Sep 17 00:00:00 2001 From: Nikolay Krasko Date: Thu, 19 Apr 2012 16:05:39 +0400 Subject: [PATCH] KT-1630 Do not select automatically completion proposal in definition of lambda parameters #KT-1630 fixed --- .../lang/parsing/JetExpressionParsing.java | 139 +++++++++--------- .../testData/psi/FunctionLiterals_ERR.jet | 4 + .../testData/psi/FunctionLiterals_ERR.txt | 67 ++++++++- idea/src/META-INF/plugin.xml | 1 + .../UnfocusedPossibleFunctionParameter.java | 76 ++++++++++ .../InBeginningOfFunctionLiteral.kt | 4 + .../InBeginningOfFunctionLiteral.kt.after | 4 + .../InBeginningOfFunctionLiteralInBrackets.kt | 4 + ...inningOfFunctionLiteralInBrackets.kt.after | 4 + .../confidence/InBlockOfFunctionLiteral.kt | 4 + .../InBlockOfFunctionLiteral.kt.after | 4 + .../confidence/JetConfidenceTest.java | 19 +++ 12 files changed, 257 insertions(+), 73 deletions(-) create mode 100644 idea/src/org/jetbrains/jet/plugin/completion/confidence/UnfocusedPossibleFunctionParameter.java create mode 100644 idea/testData/completion/confidence/InBeginningOfFunctionLiteral.kt create mode 100644 idea/testData/completion/confidence/InBeginningOfFunctionLiteral.kt.after create mode 100644 idea/testData/completion/confidence/InBeginningOfFunctionLiteralInBrackets.kt create mode 100644 idea/testData/completion/confidence/InBeginningOfFunctionLiteralInBrackets.kt.after create mode 100644 idea/testData/completion/confidence/InBlockOfFunctionLiteral.kt create mode 100644 idea/testData/completion/confidence/InBlockOfFunctionLiteral.kt.after diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/parsing/JetExpressionParsing.java b/compiler/frontend/src/org/jetbrains/jet/lang/parsing/JetExpressionParsing.java index 22263d12863..50eb3075b84 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/parsing/JetExpressionParsing.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/parsing/JetExpressionParsing.java @@ -1128,27 +1128,20 @@ public class JetExpressionParsing extends AbstractJetParsing { // {(a, b) -> ...} { - PsiBuilder.Marker rollbackMarker = mark(); + boolean preferParamsToExpressions = isConfirmedParametersByComma(); + PsiBuilder.Marker rollbackMarker = mark(); parseFunctionLiteralParametersAndType(); - paramsFound = rollbackOrDropAt(rollbackMarker, ARROW); + paramsFound = preferParamsToExpressions ? + rollbackOrDrop(rollbackMarker, ARROW, "An -> is expected", RBRACE) : + rollbackOrDropAt(rollbackMarker, ARROW); } if (!paramsFound) { // If not found, try a typeRef DOT and then LPAR .. RPAR ARROW // {((A) -> B).(x) -> ... } - PsiBuilder.Marker rollbackMarker = mark(); - int lastDot = matchTokenStreamPredicate(new LastBefore(new At(DOT), new AtSet(ARROW, RPAR))); - if (lastDot >= 0) { - createTruncatedBuilder(lastDot).parseTypeRef(); - if (at(DOT)) { - advance(); // DOT - parseFunctionLiteralParametersAndType(); - } - } - - paramsFound = rollbackOrDropAt(rollbackMarker, ARROW); + paramsFound = parseFunctionTypeDotParametersAndType(); } } else { @@ -1157,69 +1150,21 @@ public class JetExpressionParsing extends AbstractJetParsing { // {a -> ...} // {a, b -> ...} PsiBuilder.Marker rollbackMarker = mark(); + boolean preferParamsToExpressions = (lookahead(1) == COMMA); parseFunctionLiteralShorthandParameterList(); parseOptionalFunctionLiteralType(); - paramsFound = rollbackOrDropAt(rollbackMarker, ARROW); + + paramsFound = preferParamsToExpressions ? + rollbackOrDrop(rollbackMarker, ARROW, "An -> is expected", RBRACE) : + rollbackOrDropAt(rollbackMarker, ARROW); } if (!paramsFound && atSet(JetParsing.TYPE_REF_FIRST)) { // Try to parse a type DOT valueParameterList ARROW // {A.(b) -> ...} - PsiBuilder.Marker rollbackMarker = mark(); - int lastDot = matchTokenStreamPredicate(new LastBefore(new At(DOT), new AtSet(ARROW, RBRACE))); - if (lastDot >= 0) { // There is a receiver type - createTruncatedBuilder(lastDot).parseTypeRef(); - } - - if (at(DOT)) { - advance(); // DOT - parseFunctionLiteralParametersAndType(); - paramsFound = rollbackOrDropAt(rollbackMarker, ARROW); - } - else { - rollbackMarker.rollbackTo(); - } + paramsFound = parseFunctionTypeDotParametersAndType(); } -// int doubleArrowPos = matchTokenStreamPredicate(new FirstBefore(new At(ARROW), new At(RBRACE)) { -// @Override -// public boolean isTopLevel(int openAngleBrackets, int openBrackets, int openBraces, int openParentheses) { -// return openBraces == 0; -// } -// }); -// -// boolean doubleArrowPresent = doubleArrowPos >= 0; -// if (doubleArrowPresent) { -// boolean dontExpectParameters = false; -// -// int lastDot = matchTokenStreamPredicate(new LastBefore(new At(DOT), new AtOffset(doubleArrowPos))); -// if (lastDot >= 0) { // There is a receiver type -// createTruncatedBuilder(lastDot).parseTypeRef(); -// -// expect(DOT, "Expecting '.'"); -// -// if (!at(LPAR)) { -// int firstLParPos = matchTokenStreamPredicate(new FirstBefore(new At(LPAR), new AtOffset(doubleArrowPos))); -// -// if (firstLParPos >= 0) { -// errorUntilOffset("Expecting '('", firstLParPos); -// } else { -// errorUntilOffset("To specify a receiver type, use the full notation: {ReceiverType.(parameters) [: ReturnType] -> ...}", -// doubleArrowPos); -// dontExpectParameters = true; -// } -// } -// -// } -// -// if (at(LPAR)) { -// parseFunctionLiteralParametersAndType(); -// } -// else if (!dontExpectParameters) { -// parseFunctionLiteralShorthandParameterList(); -// } -// -// expectNoAdvance(ARROW, "Expecting '->'"); -// } } + if (!paramsFound) { if (preferBlock) { literal.drop(); @@ -1231,6 +1176,7 @@ public class JetExpressionParsing extends AbstractJetParsing { return; } } + PsiBuilder.Marker body = mark(); parseStatements(); body.done(BLOCK); @@ -1252,6 +1198,25 @@ public class JetExpressionParsing extends AbstractJetParsing { return false; } + private boolean rollbackOrDrop(PsiBuilder.Marker rollbackMarker, + JetToken expected, String expectMessage, + IElementType validForDrop) { + if (at(expected)) { + advance(); // dropAt + rollbackMarker.drop(); + return true; + } + else if (at(validForDrop)) { + rollbackMarker.drop(); + expect(expected, expectMessage); + return true; + } + + rollbackMarker.rollbackTo(); + return false; + } + + /* * SimpleName{,} */ @@ -1289,9 +1254,45 @@ public class JetExpressionParsing extends AbstractJetParsing { parameterList.done(VALUE_PARAMETER_LIST); } + // Check that position is followed by top level comma. It can't be expression and we want it be + // parsed as parameters in function literal + private boolean isConfirmedParametersByComma() { + assert _at(LPAR); + PsiBuilder.Marker lparMarker = mark(); + advance(); // LPAR + int comma = matchTokenStreamPredicate(new FirstBefore(new At(COMMA), new AtSet(ARROW, RPAR))); + lparMarker.rollbackTo(); + return comma > 0; + } + + private boolean parseFunctionTypeDotParametersAndType() { + PsiBuilder.Marker rollbackMarker = mark(); + + // True when it's confirmed that body of literal can't be simple expressions and we prefer to parse + // it to function params if possible. + boolean preferParamsToExpressions = false; + + int lastDot = matchTokenStreamPredicate(new LastBefore(new At(DOT), new AtSet(ARROW, RPAR))); + if (lastDot >= 0) { + createTruncatedBuilder(lastDot).parseTypeRef(); + if (at(DOT)) { + advance(); // DOT + + if (at(LPAR)) { + preferParamsToExpressions = isConfirmedParametersByComma(); + } + + parseFunctionLiteralParametersAndType(); + } + } + + return preferParamsToExpressions ? + rollbackOrDrop(rollbackMarker, ARROW, "An -> is expected", RBRACE) : + rollbackOrDropAt(rollbackMarker, ARROW); + } + private void parseFunctionLiteralParametersAndType() { parseFunctionLiteralParameterList(); - parseOptionalFunctionLiteralType(); } diff --git a/compiler/testData/psi/FunctionLiterals_ERR.jet b/compiler/testData/psi/FunctionLiterals_ERR.jet index d275d6711a6..e3d82032282 100644 --- a/compiler/testData/psi/FunctionLiterals_ERR.jet +++ b/compiler/testData/psi/FunctionLiterals_ERR.jet @@ -15,4 +15,8 @@ fun foo() { {a : b -> f} {T.a : b -> f} + + {(a, b) } + {T.(a, b) } + {a, } } \ No newline at end of file diff --git a/compiler/testData/psi/FunctionLiterals_ERR.txt b/compiler/testData/psi/FunctionLiterals_ERR.txt index baa260581ae..3f4b7b3bb28 100644 --- a/compiler/testData/psi/FunctionLiterals_ERR.txt +++ b/compiler/testData/psi/FunctionLiterals_ERR.txt @@ -326,7 +326,66 @@ JetFile: FunctionLiterals_ERR.jet REFERENCE_EXPRESSION PsiElement(IDENTIFIER)('f') PsiElement(RBRACE)('}') - PsiWhiteSpace('\n') - PsiElement(RBRACE)('}') - PsiErrorElement:Expecting '}' - \ No newline at end of file + PsiWhiteSpace('\n\n ') + FUNCTION_LITERAL_EXPRESSION + FUNCTION_LITERAL + PsiElement(LBRACE)('{') + VALUE_PARAMETER_LIST + PsiElement(LPAR)('(') + VALUE_PARAMETER + PsiElement(IDENTIFIER)('a') + PsiElement(COMMA)(',') + PsiWhiteSpace(' ') + VALUE_PARAMETER + PsiElement(IDENTIFIER)('b') + PsiElement(RPAR)(')') + PsiErrorElement:An -> is expected + + PsiWhiteSpace(' ') + BLOCK + + PsiElement(RBRACE)('}') + PsiWhiteSpace('\n ') + FUNCTION_LITERAL_EXPRESSION + FUNCTION_LITERAL + PsiElement(LBRACE)('{') + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('T') + PsiElement(DOT)('.') + VALUE_PARAMETER_LIST + PsiElement(LPAR)('(') + VALUE_PARAMETER + PsiElement(IDENTIFIER)('a') + PsiElement(COMMA)(',') + PsiWhiteSpace(' ') + VALUE_PARAMETER + PsiElement(IDENTIFIER)('b') + PsiElement(RPAR)(')') + PsiErrorElement:An -> is expected + + PsiWhiteSpace(' ') + BLOCK + + PsiElement(RBRACE)('}') + PsiWhiteSpace('\n ') + FUNCTION_LITERAL_EXPRESSION + FUNCTION_LITERAL + PsiElement(LBRACE)('{') + VALUE_PARAMETER_LIST + VALUE_PARAMETER + PsiElement(IDENTIFIER)('a') + PsiElement(COMMA)(',') + PsiWhiteSpace(' ') + VALUE_PARAMETER + PsiErrorElement:Expecting parameter name + PsiElement(RBRACE)('}') + PsiErrorElement:Expecting '->' or ',' + + PsiWhiteSpace('\n') + BLOCK + + PsiElement(RBRACE)('}') + PsiErrorElement:Expecting '}' + \ No newline at end of file diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 00f43bf7d8d..78f27b2f6f7 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -109,6 +109,7 @@ + diff --git a/idea/src/org/jetbrains/jet/plugin/completion/confidence/UnfocusedPossibleFunctionParameter.java b/idea/src/org/jetbrains/jet/plugin/completion/confidence/UnfocusedPossibleFunctionParameter.java new file mode 100644 index 00000000000..7cc77460e6c --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/completion/confidence/UnfocusedPossibleFunctionParameter.java @@ -0,0 +1,76 @@ +/* + * Copyright 2010-2012 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 org.jetbrains.jet.plugin.completion.confidence; + +import com.intellij.codeInsight.completion.CompletionConfidence; +import com.intellij.codeInsight.completion.CompletionParameters; +import com.intellij.psi.PsiElement; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.ThreeState; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.psi.*; + +/** + * @author Nikolay Krasko + */ +public class UnfocusedPossibleFunctionParameter extends CompletionConfidence { + @NotNull + @Override + public ThreeState shouldFocusLookup(@NotNull CompletionParameters parameters) { + // 1. Do not automatically insert completion for first reference expression in block inside + // function literal if it has no parameters yet. + + // 2. The same but for the case when first expression is additionally surrounded with brackets + + PsiElement position = parameters.getPosition(); + JetFunctionLiteralExpression functionLiteral = PsiTreeUtil.getParentOfType( + position, JetFunctionLiteralExpression.class); + + if (functionLiteral != null) { + PsiElement expectedReference = position.getParent(); + if (expectedReference instanceof JetSimpleNameExpression) { + if (PsiTreeUtil.findChildOfType(functionLiteral, JetParameterList.class) == null) { + { + // 1. + PsiElement expectedBlock = expectedReference.getParent(); + if (expectedBlock instanceof JetBlockExpression) { + if (expectedReference.getPrevSibling() == null) { + return ThreeState.NO; + } + } + } + + { + // 2. + PsiElement expectedParenthesized = expectedReference.getParent(); + if (expectedParenthesized instanceof JetParenthesizedExpression) { + PsiElement expectedBlock = expectedParenthesized.getParent(); + if (expectedBlock instanceof JetBlockExpression) { + if (expectedParenthesized.getPrevSibling() == null) { + return ThreeState.NO; + } + } + } + } + } + } + } + + return ThreeState.UNSURE; + } +} + diff --git a/idea/testData/completion/confidence/InBeginningOfFunctionLiteral.kt b/idea/testData/completion/confidence/InBeginningOfFunctionLiteral.kt new file mode 100644 index 00000000000..03898c0f8b1 --- /dev/null +++ b/idea/testData/completion/confidence/InBeginningOfFunctionLiteral.kt @@ -0,0 +1,4 @@ +fun main(args : Array) { + val mmm = 12 + val t = { } +} \ No newline at end of file diff --git a/idea/testData/completion/confidence/InBeginningOfFunctionLiteral.kt.after b/idea/testData/completion/confidence/InBeginningOfFunctionLiteral.kt.after new file mode 100644 index 00000000000..c1079d37611 --- /dev/null +++ b/idea/testData/completion/confidence/InBeginningOfFunctionLiteral.kt.after @@ -0,0 +1,4 @@ +fun main(args : Array) { + val mmm = 12 + val t = { mm } +} \ No newline at end of file diff --git a/idea/testData/completion/confidence/InBeginningOfFunctionLiteralInBrackets.kt b/idea/testData/completion/confidence/InBeginningOfFunctionLiteralInBrackets.kt new file mode 100644 index 00000000000..0fcec1593b2 --- /dev/null +++ b/idea/testData/completion/confidence/InBeginningOfFunctionLiteralInBrackets.kt @@ -0,0 +1,4 @@ +fun main(args : Array) { + val mmm = 12 + val t = { () } +} \ No newline at end of file diff --git a/idea/testData/completion/confidence/InBeginningOfFunctionLiteralInBrackets.kt.after b/idea/testData/completion/confidence/InBeginningOfFunctionLiteralInBrackets.kt.after new file mode 100644 index 00000000000..34d71fc9fe5 --- /dev/null +++ b/idea/testData/completion/confidence/InBeginningOfFunctionLiteralInBrackets.kt.after @@ -0,0 +1,4 @@ +fun main(args : Array) { + val mmm = 12 + val t = { (mm ) } +} \ No newline at end of file diff --git a/idea/testData/completion/confidence/InBlockOfFunctionLiteral.kt b/idea/testData/completion/confidence/InBlockOfFunctionLiteral.kt new file mode 100644 index 00000000000..f438d237441 --- /dev/null +++ b/idea/testData/completion/confidence/InBlockOfFunctionLiteral.kt @@ -0,0 +1,4 @@ +fun main(args : Array) { + val mmm = 12 + val t = { i -> } +} \ No newline at end of file diff --git a/idea/testData/completion/confidence/InBlockOfFunctionLiteral.kt.after b/idea/testData/completion/confidence/InBlockOfFunctionLiteral.kt.after new file mode 100644 index 00000000000..b541c7e8f9f --- /dev/null +++ b/idea/testData/completion/confidence/InBlockOfFunctionLiteral.kt.after @@ -0,0 +1,4 @@ +fun main(args : Array) { + val mmm = 12 + val t = { i -> mmm } +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/jet/completion/confidence/JetConfidenceTest.java b/idea/tests/org/jetbrains/jet/completion/confidence/JetConfidenceTest.java index ec19983e491..9c6cd87ab9a 100644 --- a/idea/tests/org/jetbrains/jet/completion/confidence/JetConfidenceTest.java +++ b/idea/tests/org/jetbrains/jet/completion/confidence/JetConfidenceTest.java @@ -16,6 +16,8 @@ package org.jetbrains.jet.completion.confidence; +import com.intellij.codeInsight.completion.CodeCompletionHandlerBase; +import com.intellij.codeInsight.completion.CompletionType; import com.intellij.codeInsight.completion.LightCompletionTestCase; import com.intellij.openapi.projectRoots.JavaSdk; import com.intellij.openapi.projectRoots.Sdk; @@ -37,6 +39,18 @@ public class JetConfidenceTest extends LightCompletionTestCase { doTest("pub"); } + public void testInBeginningOfFunctionLiteral() { + doTest("mm"); + } + + public void testInBeginningOfFunctionLiteralInBrackets() { + doTest("mm"); + } + + public void testInBlockOfFunctionLiteral() { + doTest("mm"); + } + protected void doTest(String completionActivateType) { configureByFile(getBeforeFileName()); type(completionActivateType + " "); @@ -60,4 +74,9 @@ public class JetConfidenceTest extends LightCompletionTestCase { protected Sdk getProjectJDK() { return JavaSdk.getInstance().createJdk("JDK", SystemUtils.getJavaHome().getAbsolutePath()); } + + @Override + protected void complete() { + new CodeCompletionHandlerBase(CompletionType.BASIC, false, true, true).invokeCompletion(getProject(), getEditor(), 0, false); + } } \ No newline at end of file