diff --git a/python/src/com/jetbrains/python/codeInsight/completion/PyKeywordCompletionContributor.java b/python/src/com/jetbrains/python/codeInsight/completion/PyKeywordCompletionContributor.java index 9ebf49353700..bb18d89d2302 100644 --- a/python/src/com/jetbrains/python/codeInsight/completion/PyKeywordCompletionContributor.java +++ b/python/src/com/jetbrains/python/codeInsight/completion/PyKeywordCompletionContributor.java @@ -310,7 +310,7 @@ public class PyKeywordCompletionContributor extends CompletionContributor { private static final PsiElementPattern.Capture IN_EXCEPT_BODY = psiElement().inside(psiElement(PyStatementList.class).inside(psiElement(PyExceptPart.class))); - private static final PsiElementPattern.Capture AFTER_IF = afterStatement(psiElement(PyIfStatement.class)); + private static final PsiElementPattern.Capture AFTER_IF = afterStatement(psiElement(PyIfStatement.class).withLastChild(psiElement(PyIfPart.class))); private static final PsiElementPattern.Capture AFTER_TRY = afterStatement(psiElement(PyTryExceptStatement.class)); private static final PsiElementPattern.Capture AFTER_LOOP_NO_ELSE = @@ -355,14 +355,11 @@ public class PyKeywordCompletionContributor extends CompletionContributor { } } - private static void putKeyword( - @NotNull @NonNls String keyword, - InsertHandler handler, - TailType tail, - CompletionResultSet result) { - final PythonLookupElement lookup_elt = new PythonLookupElement(keyword, true, null); - lookup_elt.setHandler(handler); - result.addElement(TailTypeDecorator.withTail(lookup_elt, tail)); + private static void putKeyword(@NotNull @NonNls String keyword, InsertHandler handler, TailType tail, + CompletionResultSet result) { + final PythonLookupElement lookupElement = new PythonLookupElement(keyword, true, null); + lookupElement.setHandler(handler); + result.addElement(TailTypeDecorator.withTail(lookupElement, tail)); } private void addPreColonStatements() { @@ -472,8 +469,7 @@ public class PyKeywordCompletionContributor extends CompletionContributor { CompletionType.BASIC, psiElement() .withLanguage(PythonLanguage.getInstance()) .and(FIRST_ON_LINE) - .andOr(IN_IF_BODY, AFTER_IF) // NOTE: does allow 'elif' after 'else', may be useful for easier reordering of branches - //.andNot(RIGHT_AFTER_COLON) + .andOr(IN_IF_BODY, AFTER_IF) .andNot(AFTER_QUALIFIER).andNot(IN_STRING_LITERAL) , new PyKeywordCompletionProvider(TailType.NONE, UnindentingInsertHandler.INSTANCE, "elif")); diff --git a/python/testData/completion/continue.after.py b/python/testData/keywordCompletion/continue.after.py similarity index 100% rename from python/testData/completion/continue.after.py rename to python/testData/keywordCompletion/continue.after.py diff --git a/python/testData/completion/continue.py b/python/testData/keywordCompletion/continue.py similarity index 100% rename from python/testData/completion/continue.py rename to python/testData/keywordCompletion/continue.py diff --git a/python/testData/completion/elif.after.py b/python/testData/keywordCompletion/elif.after.py similarity index 100% rename from python/testData/completion/elif.after.py rename to python/testData/keywordCompletion/elif.after.py diff --git a/python/testData/completion/elif.py b/python/testData/keywordCompletion/elif.py similarity index 100% rename from python/testData/completion/elif.py rename to python/testData/keywordCompletion/elif.py diff --git a/python/testData/completion/elifNotIndented.after.py b/python/testData/keywordCompletion/elifNotIndented.after.py similarity index 100% rename from python/testData/completion/elifNotIndented.after.py rename to python/testData/keywordCompletion/elifNotIndented.after.py diff --git a/python/testData/completion/elifNotIndented.py b/python/testData/keywordCompletion/elifNotIndented.py similarity index 100% rename from python/testData/completion/elifNotIndented.py rename to python/testData/keywordCompletion/elifNotIndented.py diff --git a/python/testData/completion/else.after.py b/python/testData/keywordCompletion/else.after.py similarity index 100% rename from python/testData/completion/else.after.py rename to python/testData/keywordCompletion/else.after.py diff --git a/python/testData/completion/else.py b/python/testData/keywordCompletion/else.py similarity index 100% rename from python/testData/completion/else.py rename to python/testData/keywordCompletion/else.py diff --git a/python/testData/completion/elseInCondExpr.after.py b/python/testData/keywordCompletion/elseInCondExpr.after.py similarity index 100% rename from python/testData/completion/elseInCondExpr.after.py rename to python/testData/keywordCompletion/elseInCondExpr.after.py diff --git a/python/testData/completion/elseInCondExpr.py b/python/testData/keywordCompletion/elseInCondExpr.py similarity index 100% rename from python/testData/completion/elseInCondExpr.py rename to python/testData/keywordCompletion/elseInCondExpr.py diff --git a/python/testData/completion/elseInTryNotIndented.after.py b/python/testData/keywordCompletion/elseInTryNotIndented.after.py similarity index 100% rename from python/testData/completion/elseInTryNotIndented.after.py rename to python/testData/keywordCompletion/elseInTryNotIndented.after.py diff --git a/python/testData/completion/elseInTryNotIndented.py b/python/testData/keywordCompletion/elseInTryNotIndented.py similarity index 100% rename from python/testData/completion/elseInTryNotIndented.py rename to python/testData/keywordCompletion/elseInTryNotIndented.py diff --git a/python/testData/completion/elseNotIndented.after.py b/python/testData/keywordCompletion/elseNotIndented.after.py similarity index 100% rename from python/testData/completion/elseNotIndented.after.py rename to python/testData/keywordCompletion/elseNotIndented.after.py diff --git a/python/testData/completion/elseNotIndented.py b/python/testData/keywordCompletion/elseNotIndented.py similarity index 100% rename from python/testData/completion/elseNotIndented.py rename to python/testData/keywordCompletion/elseNotIndented.py diff --git a/python/testData/completion/except.after.py b/python/testData/keywordCompletion/except.after.py similarity index 100% rename from python/testData/completion/except.after.py rename to python/testData/keywordCompletion/except.after.py diff --git a/python/testData/completion/except.py b/python/testData/keywordCompletion/except.py similarity index 100% rename from python/testData/completion/except.py rename to python/testData/keywordCompletion/except.py diff --git a/python/testData/completion/exceptNotIndented.after.py b/python/testData/keywordCompletion/exceptNotIndented.after.py similarity index 100% rename from python/testData/completion/exceptNotIndented.after.py rename to python/testData/keywordCompletion/exceptNotIndented.after.py diff --git a/python/testData/completion/exceptNotIndented.py b/python/testData/keywordCompletion/exceptNotIndented.py similarity index 100% rename from python/testData/completion/exceptNotIndented.py rename to python/testData/keywordCompletion/exceptNotIndented.py diff --git a/python/testData/completion/finallyInExcept.after.py b/python/testData/keywordCompletion/finallyInExcept.after.py similarity index 100% rename from python/testData/completion/finallyInExcept.after.py rename to python/testData/keywordCompletion/finallyInExcept.after.py diff --git a/python/testData/completion/finallyInExcept.py b/python/testData/keywordCompletion/finallyInExcept.py similarity index 100% rename from python/testData/completion/finallyInExcept.py rename to python/testData/keywordCompletion/finallyInExcept.py diff --git a/python/testData/completion/fromDotImport.after.py b/python/testData/keywordCompletion/fromDotImport.after.py similarity index 100% rename from python/testData/completion/fromDotImport.after.py rename to python/testData/keywordCompletion/fromDotImport.after.py diff --git a/python/testData/completion/fromDotImport.py b/python/testData/keywordCompletion/fromDotImport.py similarity index 100% rename from python/testData/completion/fromDotImport.py rename to python/testData/keywordCompletion/fromDotImport.py diff --git a/python/testData/completion/importKeyword.after.py b/python/testData/keywordCompletion/importKeyword.after.py similarity index 100% rename from python/testData/completion/importKeyword.after.py rename to python/testData/keywordCompletion/importKeyword.after.py diff --git a/python/testData/completion/importKeyword.py b/python/testData/keywordCompletion/importKeyword.py similarity index 100% rename from python/testData/completion/importKeyword.py rename to python/testData/keywordCompletion/importKeyword.py diff --git a/python/testData/completion/keywordAfterComment.after.py b/python/testData/keywordCompletion/keywordAfterComment.after.py similarity index 100% rename from python/testData/completion/keywordAfterComment.after.py rename to python/testData/keywordCompletion/keywordAfterComment.after.py diff --git a/python/testData/completion/keywordAfterComment.py b/python/testData/keywordCompletion/keywordAfterComment.py similarity index 100% rename from python/testData/completion/keywordAfterComment.py rename to python/testData/keywordCompletion/keywordAfterComment.py diff --git a/python/testData/completion/lambdaInExpression.after.py b/python/testData/keywordCompletion/lambdaInExpression.after.py similarity index 100% rename from python/testData/completion/lambdaInExpression.after.py rename to python/testData/keywordCompletion/lambdaInExpression.after.py diff --git a/python/testData/completion/lambdaInExpression.py b/python/testData/keywordCompletion/lambdaInExpression.py similarity index 100% rename from python/testData/completion/lambdaInExpression.py rename to python/testData/keywordCompletion/lambdaInExpression.py diff --git a/python/testData/completion/noContinueInFinally.py b/python/testData/keywordCompletion/noContinueInFinally.py similarity index 100% rename from python/testData/completion/noContinueInFinally.py rename to python/testData/keywordCompletion/noContinueInFinally.py diff --git a/python/testData/keywordCompletion/noElifBeforeElse.py b/python/testData/keywordCompletion/noElifBeforeElse.py new file mode 100644 index 000000000000..b5b02b324d05 --- /dev/null +++ b/python/testData/keywordCompletion/noElifBeforeElse.py @@ -0,0 +1,5 @@ +if True: + a = 1 +else: + pass + \ No newline at end of file diff --git a/python/testData/completion/noneInArgList.after.py b/python/testData/keywordCompletion/noneInArgList.after.py similarity index 100% rename from python/testData/completion/noneInArgList.after.py rename to python/testData/keywordCompletion/noneInArgList.after.py diff --git a/python/testData/completion/noneInArgList.py b/python/testData/keywordCompletion/noneInArgList.py similarity index 100% rename from python/testData/completion/noneInArgList.py rename to python/testData/keywordCompletion/noneInArgList.py diff --git a/python/testData/completion/nonlocal.after.py b/python/testData/keywordCompletion/nonlocal.after.py similarity index 100% rename from python/testData/completion/nonlocal.after.py rename to python/testData/keywordCompletion/nonlocal.after.py diff --git a/python/testData/completion/nonlocal.py b/python/testData/keywordCompletion/nonlocal.py similarity index 100% rename from python/testData/completion/nonlocal.py rename to python/testData/keywordCompletion/nonlocal.py diff --git a/python/testData/completion/yield.after.py b/python/testData/keywordCompletion/yield.after.py similarity index 100% rename from python/testData/completion/yield.after.py rename to python/testData/keywordCompletion/yield.after.py diff --git a/python/testData/completion/yield.py b/python/testData/keywordCompletion/yield.py similarity index 100% rename from python/testData/completion/yield.py rename to python/testData/keywordCompletion/yield.py diff --git a/python/testSrc/com/jetbrains/python/PythonCompletionTest.java b/python/testSrc/com/jetbrains/python/PythonCompletionTest.java index 88d6779540b0..c00e632a21eb 100644 --- a/python/testSrc/com/jetbrains/python/PythonCompletionTest.java +++ b/python/testSrc/com/jetbrains/python/PythonCompletionTest.java @@ -83,10 +83,6 @@ public class PythonCompletionTest extends PyTestCase { doTest(); } - public void testKeywordAfterComment() { // PY-697 - doTest(); - } - public void testClassPrivate() { doTest(); } @@ -228,14 +224,6 @@ public class PythonCompletionTest extends PyTestCase { doTest(); } - public void testEmptyFile() { // PY-1845 - myFixture.configureByText(PythonFileType.INSTANCE, ""); - myFixture.completeBasic(); - final List elements = myFixture.getLookupElementStrings(); - assertNotNull(elements); - assertTrue(elements.contains("import")); - } - public void testImportItself() { // PY-1895 myFixture.copyDirectoryToProject("completion/importItself/package1", "package1"); myFixture.configureFromTempProjectFile("package1/submodule1.py"); @@ -275,14 +263,6 @@ public class PythonCompletionTest extends PyTestCase { doTest(); } - public void testNonlocal() { // PY-2289 - doTest3K(); - } - - public void testYield() { - doTest(); - } - private void doTest3K() { PythonLanguageLevelPusher.setForcedLanguageLevel(myFixture.getProject(), LanguageLevel.PYTHON30); try { @@ -297,55 +277,6 @@ public class PythonCompletionTest extends PyTestCase { doTest(); } - public void testElse() { - doTest(); - } - - public void testElseNotIndented() { - doTest(); - } - - public void testElseInTryNotIndented() { - doTest(); - } - - public void testElif() { - doTest(); - } - - public void testElifNotIndented() { - doTest(); - } - - public void testExcept() { - doTest(); - } - - public void testExceptNotIndented() { - doTest(); - } - - public void testFinallyInExcept() { - doTest(); - } - - public void testContinue() { - doTest(); - } - - public void testNoContinueInFinally() { - final String testName = "completion/" + getTestName(true); - myFixture.configureByFile(testName + ".py"); - myFixture.completeBasic(); - final List lookupElementStrings = myFixture.getLookupElementStrings(); - assertNotNull(lookupElementStrings); - assertFalse(lookupElementStrings.contains("continue")); - } - - public void testElseInCondExpr() { // PY-2397 - doTest(); - } - public void testLocalVarInDictKey() { // PY-2558 doTest(); } @@ -358,10 +289,6 @@ public class PythonCompletionTest extends PyTestCase { doTest(); } - public void testFromDotImport() { // PY-2772 - doTest3K(); - } - public void testNoIdentifiersInImport() { doTest(); } @@ -400,10 +327,6 @@ public class PythonCompletionTest extends PyTestCase { doMultiFileTest(); } - public void testLambdaInExpression() { // PY-3150 - doTest(); - } - public void testVeryPrivate() { // PY-3246 doTest(); } @@ -467,10 +390,6 @@ public class PythonCompletionTest extends PyTestCase { myFixture.checkResultByFile("completion/identifiersInPlainDocstring.after.py"); } - public void testNoneInArgList() { // PY-3464 - doTest3K(); - } - public void testPep328Completion() { // PY-3409 myFixture.copyDirectoryToProject("completion/pep328", "pep328"); myFixture.configureByFile("pep328/package/subpackage1/moduleX.py"); @@ -528,11 +447,6 @@ public class PythonCompletionTest extends PyTestCase { doTest(); } - // PY-5144 - public void testImportKeyword() { - doTest(); - } - public void testAssignedNearby() { doTest(); } @@ -624,24 +538,6 @@ public class PythonCompletionTest extends PyTestCase { doTest(); } - public void testAsInWith() { // PY-3701 - setLanguageLevel(LanguageLevel.PYTHON27); - assertTrue(doTestByText("with open(foo) ").contains("as")); - } - - public void testAsInExcept() { // PY-1846 - setLanguageLevel(LanguageLevel.PYTHON27); - assertTrue(doTestByText("try:\n" + - " pass\n" + - "except IOError ").contains("as")); - } - - public void testElseInFor() { // PY-6755 - assertTrue(doTestByText("for item in range(10):\n" + - " pass\n" + - "el").contains("else")); - } - public void testArgs() { // PY-7208 doTestByText("def foo(*)"); myFixture.checkResult("def foo(*args)"); diff --git a/python/testSrc/com/jetbrains/python/PythonKeywordCompletionTest.java b/python/testSrc/com/jetbrains/python/PythonKeywordCompletionTest.java new file mode 100644 index 000000000000..6e9f45459291 --- /dev/null +++ b/python/testSrc/com/jetbrains/python/PythonKeywordCompletionTest.java @@ -0,0 +1,163 @@ +/* + * 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; + +import com.intellij.codeInsight.completion.impl.CamelHumpMatcher; +import com.jetbrains.python.fixtures.PyTestCase; +import com.jetbrains.python.psi.LanguageLevel; +import com.jetbrains.python.psi.impl.PythonLanguageLevelPusher; + +import java.util.List; + +public class PythonKeywordCompletionTest extends PyTestCase { + + private void doTest3K() { + PythonLanguageLevelPusher.setForcedLanguageLevel(myFixture.getProject(), LanguageLevel.PYTHON30); + try { + doTest(); + } + finally { + PythonLanguageLevelPusher.setForcedLanguageLevel(myFixture.getProject(), null); + } + } + + private void doTest() { + CamelHumpMatcher.forceStartMatching(getTestRootDisposable()); + final String testName = "keywordCompletion/" + getTestName(true); + myFixture.configureByFile(testName + ".py"); + myFixture.completeBasic(); + myFixture.checkResultByFile(testName + ".after.py"); + } + + private List doTestByText(String text) { + myFixture.configureByText(PythonFileType.INSTANCE, text); + myFixture.completeBasic(); + return myFixture.getLookupElementStrings(); + } + + public void testKeywordAfterComment() { // PY-697 + doTest(); + } + + public void testEmptyFile() { // PY-1845 + myFixture.configureByText(PythonFileType.INSTANCE, ""); + myFixture.completeBasic(); + final List elements = myFixture.getLookupElementStrings(); + assertNotNull(elements); + assertTrue(elements.contains("import")); + } + + public void testNonlocal() { // PY-2289 + doTest3K(); + } + + public void testYield() { + doTest(); + } + + public void testElse() { + doTest(); + } + + public void testElseNotIndented() { + doTest(); + } + + public void testElseInTryNotIndented() { + doTest(); + } + + public void testElif() { + doTest(); + } + + public void testElifNotIndented() { + doTest(); + } + + public void testExcept() { + doTest(); + } + + public void testExceptNotIndented() { + doTest(); + } + + public void testFinallyInExcept() { + doTest(); + } + + public void testContinue() { + doTest(); + } + + public void testNoContinueInFinally() { + final String testName = "keywordCompletion/" + getTestName(true); + myFixture.configureByFile(testName + ".py"); + myFixture.completeBasic(); + final List lookupElementStrings = myFixture.getLookupElementStrings(); + assertNotNull(lookupElementStrings); + assertDoesntContain(lookupElementStrings, "continue"); + } + + public void testNoElifBeforeElse() { + final String testName = "keywordCompletion/" + getTestName(true); + myFixture.configureByFile(testName + ".py"); + myFixture.completeBasic(); + final List lookupElementStrings = myFixture.getLookupElementStrings(); + assertNotNull(lookupElementStrings); + assertDoesntContain(lookupElementStrings, "elif"); + } + + public void testElseInCondExpr() { // PY-2397 + doTest(); + } + + public void testFromDotImport() { // PY-2772 + doTest3K(); + } + + public void testLambdaInExpression() { // PY-3150 + doTest(); + } + + public void testNoneInArgList() { // PY-3464 + doTest3K(); + } + + // PY-5144 + public void testImportKeyword() { + doTest(); + } + + public void testAsInWith() { // PY-3701 + setLanguageLevel(LanguageLevel.PYTHON27); + assertTrue(doTestByText("with open(foo) ").contains("as")); + } + + public void testAsInExcept() { // PY-1846 + setLanguageLevel(LanguageLevel.PYTHON27); + assertTrue(doTestByText("try:\n" + + " pass\n" + + "except IOError ").contains("as")); + } + + public void testElseInFor() { // PY-6755 + assertTrue(doTestByText("for item in range(10):\n" + + " pass\n" + + "el").contains("else")); + } +} diff --git a/python/testSrc/com/jetbrains/python/fixtures/PyTestCase.java b/python/testSrc/com/jetbrains/python/fixtures/PyTestCase.java index 4d227f6dbcfe..09c74b76c4d4 100644 --- a/python/testSrc/com/jetbrains/python/fixtures/PyTestCase.java +++ b/python/testSrc/com/jetbrains/python/fixtures/PyTestCase.java @@ -52,7 +52,7 @@ import java.io.File; /** * @author yole */ -@TestDataPath("$CONTENT_ROOT/../../testData/") +@TestDataPath("$CONTENT_ROOT/../testData/") public abstract class PyTestCase extends UsefulTestCase { public static String PYTHON_2_MOCK_SDK = "2.7"; public static String PYTHON_3_MOCK_SDK = "3.2";