From 4a73b1fa66a5b1b61af405840366a09d0dfbfbf3 Mon Sep 17 00:00:00 2001 From: Mikhail Golubev Date: Mon, 14 Sep 2015 22:05:08 +0300 Subject: [PATCH] PY-16870 Complete parameter in Google docstring when there is no colon after them yet --- .../PyDocstringCompletionContributor.java | 1 + .../DocStringReferenceProvider.java | 5 +--- ...ingSectionHeaderCompletionContributor.java | 3 ++ .../docstrings/GoogleCodeStyleDocString.java | 29 ++++++++++--------- .../paramNameInGoogleDocstring.after.py | 5 ++++ .../completion/paramNameInGoogleDocstring.py | 5 ++++ .../docstrings/googleNoColonAfterParameter.py | 6 ++++ .../afterGoogleReturnSectionBeforeRaises.py | 2 +- .../afterParamSectionBeforeKeywords.py | 2 +- .../python/PySectionBasedDocStringTest.java | 16 ++++++++++ .../python/PythonCompletionTest.java | 12 ++++++++ 11 files changed, 67 insertions(+), 19 deletions(-) create mode 100644 python/testData/completion/paramNameInGoogleDocstring.after.py create mode 100644 python/testData/completion/paramNameInGoogleDocstring.py create mode 100644 python/testData/docstrings/googleNoColonAfterParameter.py diff --git a/python/src/com/jetbrains/python/codeInsight/completion/PyDocstringCompletionContributor.java b/python/src/com/jetbrains/python/codeInsight/completion/PyDocstringCompletionContributor.java index ad116e1e0c95..9bbbe561f331 100644 --- a/python/src/com/jetbrains/python/codeInsight/completion/PyDocstringCompletionContributor.java +++ b/python/src/com/jetbrains/python/codeInsight/completion/PyDocstringCompletionContributor.java @@ -58,6 +58,7 @@ public class PyDocstringCompletionContributor extends CompletionContributor { 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); diff --git a/python/src/com/jetbrains/python/documentation/docstrings/DocStringReferenceProvider.java b/python/src/com/jetbrains/python/documentation/docstrings/DocStringReferenceProvider.java index 0baa707157c5..d6cb3876da28 100644 --- a/python/src/com/jetbrains/python/documentation/docstrings/DocStringReferenceProvider.java +++ b/python/src/com/jetbrains/python/documentation/docstrings/DocStringReferenceProvider.java @@ -19,10 +19,8 @@ import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiReference; import com.intellij.psi.PsiReferenceProvider; -import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.ProcessingContext; import com.jetbrains.python.PyNames; -import com.jetbrains.python.psi.PyDocStringOwner; import com.jetbrains.python.psi.PyImportElement; import com.jetbrains.python.psi.PyStringLiteralExpression; import com.jetbrains.python.psi.StructuredDocString; @@ -44,8 +42,7 @@ public class DocStringReferenceProvider extends PsiReferenceProvider { @NotNull @Override public PsiReference[] getReferencesByElement(@NotNull final PsiElement element, @NotNull ProcessingContext context) { - final PyDocStringOwner docStringOwner = PsiTreeUtil.getParentOfType(element, PyDocStringOwner.class); - if (docStringOwner != null && element == docStringOwner.getDocStringExpression()) { + if (element == DocStringUtil.getParentDefinitionDocString(element)) { final PyStringLiteralExpression expr = (PyStringLiteralExpression)element; final List ranges = expr.getStringValueTextRanges(); diff --git a/python/src/com/jetbrains/python/documentation/docstrings/DocStringSectionHeaderCompletionContributor.java b/python/src/com/jetbrains/python/documentation/docstrings/DocStringSectionHeaderCompletionContributor.java index 3450a022f95e..86405b29158b 100644 --- a/python/src/com/jetbrains/python/documentation/docstrings/DocStringSectionHeaderCompletionContributor.java +++ b/python/src/com/jetbrains/python/documentation/docstrings/DocStringSectionHeaderCompletionContributor.java @@ -42,6 +42,9 @@ public class DocStringSectionHeaderCompletionContributor extends CompletionContr final PsiElement stringNode = parameters.getOriginalPosition(); assert stringNode != null; final int offset = parameters.getOffset(); + if (file.findReferenceAt(offset) != null) { + return; + } final DocStringFormat format = DocStringUtil.getConfiguredDocStringFormat(file); if (!(format == DocStringFormat.GOOGLE || format == DocStringFormat.NUMPY)) { return; diff --git a/python/src/com/jetbrains/python/documentation/docstrings/GoogleCodeStyleDocString.java b/python/src/com/jetbrains/python/documentation/docstrings/GoogleCodeStyleDocString.java index b6e88f9e5610..f335aa877501 100644 --- a/python/src/com/jetbrains/python/documentation/docstrings/GoogleCodeStyleDocString.java +++ b/python/src/com/jetbrains/python/documentation/docstrings/GoogleCodeStyleDocString.java @@ -78,12 +78,11 @@ public class GoogleCodeStyleDocString extends SectionBasedDocString { boolean mayHaveType, boolean preferType) { final Substring line = getLine(lineNum); - Substring name, type = null, description; + Substring name, type = null; + // Napoleon requires that each parameter line contains a colon - we don't because + // we need to parse and complete parameter names before colon is typed final List colonSeparatedParts = splitByFirstColon(line); assert colonSeparatedParts.size() <= 2; - if (colonSeparatedParts.size() < 2) { - return Pair.create(null, lineNum); - } final Substring textBeforeColon = colonSeparatedParts.get(0); name = textBeforeColon.trim(); if (mayHaveType) { @@ -104,16 +103,20 @@ public class GoogleCodeStyleDocString extends SectionBasedDocString { if (name != null ? !PyNames.isIdentifierString(name.toString()) : type.isEmpty()) { return Pair.create(null, lineNum); } - description = colonSeparatedParts.get(1); - // parse line with indentation at least one space greater than indentation of the field - final Pair, Integer> pair = parseIndentedBlock(lineNum + 1, getLineIndentSize(lineNum)); - final List nestedBlock = pair.getFirst(); - if (!nestedBlock.isEmpty()) { - //noinspection ConstantConditions - description = description.union(ContainerUtil.getLastItem(nestedBlock)); + final Pair, Integer> pair; + if (colonSeparatedParts.size() == 2) { + Substring description = colonSeparatedParts.get(1); + // parse line with indentation at least one space greater than indentation of the field + pair = parseIndentedBlock(lineNum + 1, getLineIndentSize(lineNum)); + final List nestedBlock = pair.getFirst(); + if (!nestedBlock.isEmpty()) { + //noinspection ConstantConditions + description = description.union(ContainerUtil.getLastItem(nestedBlock)); + } + description = description.trim(); + return Pair.create(new SectionField(name, type, description), pair.getSecond()); } - description = description.trim(); - return Pair.create(new SectionField(name, type, description), pair.getSecond()); + return Pair.create(new SectionField(name, type, null), lineNum + 1); } diff --git a/python/testData/completion/paramNameInGoogleDocstring.after.py b/python/testData/completion/paramNameInGoogleDocstring.after.py new file mode 100644 index 000000000000..78441689cfcc --- /dev/null +++ b/python/testData/completion/paramNameInGoogleDocstring.after.py @@ -0,0 +1,5 @@ +def f(param1, param2): + """ + Args: + par + """ diff --git a/python/testData/completion/paramNameInGoogleDocstring.py b/python/testData/completion/paramNameInGoogleDocstring.py new file mode 100644 index 000000000000..78441689cfcc --- /dev/null +++ b/python/testData/completion/paramNameInGoogleDocstring.py @@ -0,0 +1,5 @@ +def f(param1, param2): + """ + Args: + par + """ diff --git a/python/testData/docstrings/googleNoColonAfterParameter.py b/python/testData/docstrings/googleNoColonAfterParameter.py new file mode 100644 index 000000000000..ac76b2a5fe92 --- /dev/null +++ b/python/testData/docstrings/googleNoColonAfterParameter.py @@ -0,0 +1,6 @@ +def f(x, y): + """ + Args: + x + y (int) + """ \ No newline at end of file diff --git a/python/testData/intentions/afterGoogleReturnSectionBeforeRaises.py b/python/testData/intentions/afterGoogleReturnSectionBeforeRaises.py index 9b5cca2f7265..b0a73d8f3201 100644 --- a/python/testData/intentions/afterGoogleReturnSectionBeforeRaises.py +++ b/python/testData/intentions/afterGoogleReturnSectionBeforeRaises.py @@ -1,7 +1,7 @@ def f(): """ Returns: - object: + object: Raises: RuntimeException diff --git a/python/testData/intentions/afterParamSectionBeforeKeywords.py b/python/testData/intentions/afterParamSectionBeforeKeywords.py index c3f6a3efe925..0f5ae32b54bf 100644 --- a/python/testData/intentions/afterParamSectionBeforeKeywords.py +++ b/python/testData/intentions/afterParamSectionBeforeKeywords.py @@ -2,7 +2,7 @@ def f(x): """ Args: - x: + x: Keyword arguments: diff --git a/python/testSrc/com/jetbrains/python/PySectionBasedDocStringTest.java b/python/testSrc/com/jetbrains/python/PySectionBasedDocStringTest.java index c5f41e5db095..96234043b78f 100644 --- a/python/testSrc/com/jetbrains/python/PySectionBasedDocStringTest.java +++ b/python/testSrc/com/jetbrains/python/PySectionBasedDocStringTest.java @@ -328,6 +328,22 @@ public class PySectionBasedDocStringTest extends PyTestCase { assertEquals("args", paramSection.getFields().get(1).getName()); } + public void testGoogleNoColonAfterParameter() { + final GoogleCodeStyleDocString docString = findAndParseGoogleStyleDocString(); + assertSize(1, docString.getSections()); + final Section paramSection = docString.getSections().get(0); + assertSize(2, paramSection.getFields()); + final SectionField x = paramSection.getFields().get(0); + assertEquals("x", x.getName()); + assertEmpty(x.getType()); + assertEmpty(x.getDescription()); + + final SectionField y = paramSection.getFields().get(1); + assertEquals("y", y.getName()); + assertEquals("int", y.getType()); + assertEmpty(y.getDescription()); + } + @Override protected String getTestDataPath() { return super.getTestDataPath() + "/docstrings"; diff --git a/python/testSrc/com/jetbrains/python/PythonCompletionTest.java b/python/testSrc/com/jetbrains/python/PythonCompletionTest.java index a87ea1a72119..bd867033dd94 100644 --- a/python/testSrc/com/jetbrains/python/PythonCompletionTest.java +++ b/python/testSrc/com/jetbrains/python/PythonCompletionTest.java @@ -426,6 +426,18 @@ public class PythonCompletionTest extends PyTestCase { }); } + // PY-16870 + public void testParamNameInGoogleDocstring() { + runWithDocStringFormat(DocStringFormat.GOOGLE, new Runnable() { + @Override + public void run() { + final List variants = doTestByFile(); + assertNotNull(variants); + assertSameElements(variants, "param1", "param2"); + } + }); + } + public void testPep328Completion() { // PY-3409 myFixture.copyDirectoryToProject("pep328", "pep328"); myFixture.configureByFile("pep328/package/subpackage1/moduleX.py");