diff --git a/python/src/com/jetbrains/python/codeInsight/completion/PyDocstringCompletionContributor.java b/python/src/com/jetbrains/python/codeInsight/completion/PyDocstringCompletionContributor.java index 9bbbe561f331..594c64754b7c 100644 --- a/python/src/com/jetbrains/python/codeInsight/completion/PyDocstringCompletionContributor.java +++ b/python/src/com/jetbrains/python/codeInsight/completion/PyDocstringCompletionContributor.java @@ -22,9 +22,13 @@ import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleUtilCore; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiReference; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.ProcessingContext; +import com.jetbrains.python.documentation.docstrings.DocStringParameterReference; +import com.jetbrains.python.documentation.docstrings.DocStringTypeReference; import com.jetbrains.python.psi.PyDocStringOwner; +import com.jetbrains.python.psi.PyNamedParameter; import com.jetbrains.python.psi.PyStringLiteralExpression; import com.jetbrains.python.refactoring.PyRefactoringUtil; import org.jetbrains.annotations.NotNull; @@ -47,31 +51,41 @@ public class PyDocstringCompletionContributor extends CompletionContributor { private static class IdentifierCompletionProvider extends CompletionProvider { - private IdentifierCompletionProvider() { - } - @Override protected void addCompletions(@NotNull CompletionParameters parameters, ProcessingContext context, @NotNull CompletionResultSet result) { - if (parameters.isAutoPopup()) return; final PsiElement element = parameters.getOriginalPosition(); if (element == null) return; final PsiFile file = element.getContainingFile(); // Parameter references are filled with DocStringParameterReference#getVariants - if (file.findReferenceAt(parameters.getOffset()) != null) return; - final PyDocStringOwner docStringOwner = PsiTreeUtil.getParentOfType(element, PyDocStringOwner.class); - final Module module = ModuleUtilCore.findModuleForPsiElement(element); - if (module != null) { - result = result.withPrefixMatcher(getPrefix(parameters.getOffset(), file)); - final Collection identifiers = PyRefactoringUtil.collectUsedNames(docStringOwner); - for (String identifier : identifiers) - result.addElement(LookupElementBuilder.create(identifier).withAutoCompletionPolicy(AutoCompletionPolicy.NEVER_AUTOCOMPLETE)); + final PsiReference reference = file.findReferenceAt(parameters.getOffset()); + if (reference == null) { + if (parameters.isAutoPopup()) return; + final PyDocStringOwner docStringOwner = PsiTreeUtil.getParentOfType(element, PyDocStringOwner.class); + final Module module = ModuleUtilCore.findModuleForPsiElement(element); + if (module != null) { + result = result.withPrefixMatcher(getPrefix(parameters.getOffset(), file)); + final Collection identifiers = PyRefactoringUtil.collectUsedNames(docStringOwner); + for (String identifier : identifiers) { + result.addElement(LookupElementBuilder.create(identifier).withAutoCompletionPolicy(AutoCompletionPolicy.NEVER_AUTOCOMPLETE)); + } - - final Collection fileIdentifiers = PyRefactoringUtil.collectUsedNames(parameters.getOriginalFile()); - for (String identifier : fileIdentifiers) - result.addElement(LookupElementBuilder.create(identifier).withAutoCompletionPolicy(AutoCompletionPolicy.NEVER_AUTOCOMPLETE)); + final Collection fileIdentifiers = PyRefactoringUtil.collectUsedNames(parameters.getOriginalFile()); + for (String identifier : fileIdentifiers) { + result.addElement(LookupElementBuilder.create(identifier).withAutoCompletionPolicy(AutoCompletionPolicy.NEVER_AUTOCOMPLETE)); + } + } + } + else if (reference instanceof DocStringParameterReference) { + for (PyNamedParameter param : ((DocStringParameterReference)reference).collectParameterVariants()) { + result.addElement(LookupElementBuilder.createWithIcon(param)); + } + } + else if (reference instanceof DocStringTypeReference) { + for (Object variant : ((DocStringTypeReference)reference).collectTypeVariants()) { + result.addElement(LookupElementBuilder.create(variant)); + } } } } @@ -90,7 +104,13 @@ public class PyDocstringCompletionContributor extends CompletionContributor { } @Override - public boolean invokeAutoPopup(@NotNull PsiElement position, char typeChar) { - return false; + public void beforeCompletion(@NotNull CompletionInitializationContext context) { + // With standard dummy identifier inserted, docstring might become malformed + // e.g. "@param param" -> "@param paraIntellijIdeaRulezzz m" + // and param is no longer parameter, but type reference now + final PsiReference ref = context.getFile().findReferenceAt(context.getCaret().getOffset()); + if (ref instanceof DocStringParameterReference || ref instanceof DocStringTypeReference) { + context.setDummyIdentifier(CompletionInitializationContext.DUMMY_IDENTIFIER_TRIMMED); + } } } diff --git a/python/src/com/jetbrains/python/documentation/docstrings/DocStringParameterReference.java b/python/src/com/jetbrains/python/documentation/docstrings/DocStringParameterReference.java index c16df248b0d8..42395ef5e111 100644 --- a/python/src/com/jetbrains/python/documentation/docstrings/DocStringParameterReference.java +++ b/python/src/com/jetbrains/python/documentation/docstrings/DocStringParameterReference.java @@ -34,6 +34,7 @@ import com.jetbrains.python.psi.types.TypeEvalContext; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Collections; import java.util.List; import java.util.Set; @@ -121,9 +122,15 @@ public class DocStringParameterReference extends PsiReferenceBase collectParameterVariants() { PyDocStringOwner owner = PsiTreeUtil.getParentOfType(getElement(), PyDocStringOwner.class); if (owner instanceof PyFunction) { - List result = Lists.newArrayList(); + List result = Lists.newArrayList(); final List namedParameters = ParamHelper.collectNamedParameters(((PyFunction)owner).getParameterList()); Set usedParameters = new HashSet(); PyStringLiteralExpression expression = PsiTreeUtil.getParentOfType(getElement(), PyStringLiteralExpression.class, false); @@ -139,9 +146,9 @@ public class DocStringParameterReference extends PsiReferenceBase collectTypeVariants() { final PsiFile file = myElement.getContainingFile(); - final ArrayList variants = Lists.newArrayList("str", "int", "basestring", "bool", "buffer", "bytearray", "complex", "dict", - "tuple", "enumerate", "file", "float", "frozenset", "list", "long", "set", "object"); + final ArrayList + variants = Lists.newArrayList("str", "int", "basestring", "bool", "buffer", "bytearray", "complex", "dict", + "tuple", "enumerate", "file", "float", "frozenset", "list", "long", "set", "object"); if (file instanceof PyFile) { variants.addAll(((PyFile)file).getTopLevelClasses()); final List fromImports = ((PyFile)file).getFromImports(); @@ -139,7 +147,6 @@ public class DocStringTypeReference extends PsiPolyVariantReferenceBaseam1: description + """ \ No newline at end of file diff --git a/python/testData/completion/overrideParamNameInRestDocstring.py b/python/testData/completion/overrideParamNameInRestDocstring.py new file mode 100644 index 000000000000..660fea531c0d --- /dev/null +++ b/python/testData/completion/overrideParamNameInRestDocstring.py @@ -0,0 +1,4 @@ +def f(param1, param2): + """ + :param param1: description + """ \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PythonCompletionTest.java b/python/testSrc/com/jetbrains/python/PythonCompletionTest.java index bd867033dd94..ef19da7f5ccb 100644 --- a/python/testSrc/com/jetbrains/python/PythonCompletionTest.java +++ b/python/testSrc/com/jetbrains/python/PythonCompletionTest.java @@ -438,6 +438,30 @@ public class PythonCompletionTest extends PyTestCase { }); } + // PY-16870 + public void testOverrideParamNameInGoogleDocstring() { + runWithDocStringFormat(DocStringFormat.GOOGLE, new Runnable() { + @Override + public void run() { + final List variants = doTestByFile(); + assertNotNull(variants); + assertSameElements(variants, "param2"); + } + }); + } + + // PY-16870 + public void testOverrideParamNameInRestDocstring() { + runWithDocStringFormat(DocStringFormat.REST, new Runnable() { + @Override + public void run() { + final List variants = doTestByFile(); + assertNotNull(variants); + assertSameElements(variants, "param2"); + } + }); + } + public void testPep328Completion() { // PY-3409 myFixture.copyDirectoryToProject("pep328", "pep328"); myFixture.configureByFile("pep328/package/subpackage1/moduleX.py");