diff --git a/plugins/org.python.pydev.core/src/org/python/pydev/core/docstrings/AssistDocString.java b/plugins/org.python.pydev.core/src/org/python/pydev/core/docstrings/AssistDocString.java index d02e3cee21..68a5289d36 100644 --- a/plugins/org.python.pydev.core/src/org/python/pydev/core/docstrings/AssistDocString.java +++ b/plugins/org.python.pydev.core/src/org/python/pydev/core/docstrings/AssistDocString.java @@ -152,6 +152,7 @@ public boolean isValid(PySelection ps, String sel, IPyEdit edit, int offset) { public static String updatedDocstring(String baseDocstring, List params, String delimiter, String indent, String docstringStyle) { + final String initialIndent = indent; String docStringStartEnd; if (baseDocstring.startsWith("\"\"\"") && baseDocstring.endsWith("\"\"\"")) { docStringStartEnd = "\"\"\""; @@ -188,11 +189,21 @@ public static String updatedDocstring(String baseDocstring, List params, .compile("\\s*" + Pattern.quote(docstringStyle) + "(\\w)+(\\b)"); // Google docstring .compile("\\s*(\\w)*:"); - Pattern googlePattern = Pattern.compile("\\s*(\\w)*:"); + Pattern googlePattern = Pattern.compile("\\s*(\\w+)*:"); Map paramInfos = new HashMap<>(); List splitted = StringUtils.splitInLines(baseDocstring, false); + String lastIndent = ""; + if (splitted.size() == 0) { + splitted.add(""); + } else { + String last = splitted.get(splitted.size() - 1); + if (last.strip().length() == 0) { + lastIndent = last; + } + } + String firstLine = splitted.get(0).trim(); if (firstLine.length() > 0) { // First line must have only the delimiter. @@ -242,14 +253,54 @@ public static String updatedDocstring(String baseDocstring, List params, } } - // Now, actually go on and insert the new strings. - FastStringBuffer buf = new FastStringBuffer(); + final FastStringBuffer buf = new FastStringBuffer(); + final boolean isGoogleStyle = docstringStyle.equals(Character.toString('G')); int paramsSize = params.size(); + if (isGoogleStyle) { + String useIndent = null; + for (int paramI = 0; paramI < paramsSize; paramI++) { + String paramName = params.get(paramI); + if (!PySelection.isIdentifier(paramName)) { + continue; + } + ParamInfo foundInfo = paramInfos.get(paramName); + if (foundInfo != null && foundInfo.paramLine >= 0) { + String lineContents = splitted.get(foundInfo.paramLine); + useIndent = PySelection.getIndentationFromLine(lineContents); + break; + } + } + + if (useIndent == null) { + // If not found, try to get based on `Args:` + ParamInfo foundInfo = paramInfos.get("Args"); + if (foundInfo == null) { + foundInfo = paramInfos.get("Returns"); + } + if (foundInfo != null && foundInfo.paramLine >= 0) { + String lineContents = splitted.get(foundInfo.paramLine); + useIndent = PySelection.getIndentationFromLine(lineContents) + " "; + } + } + + if (useIndent != null) { + indent = useIndent; + } else { + buf.clear(); + // If there are no matches, we need to put the `Args:` in the end and use the new indent based on it. + splitted.add(buf.append(indent).append("Args:").toString()); + indent = indent + " "; + } + + } + + // Now, actually go on and insert the new strings. for (int paramI = 0; paramI < paramsSize; paramI++) { String paramName = params.get(paramI); if (!PySelection.isIdentifier(paramName)) { continue; } + buf.clear(); ParamInfo existingInfo = paramInfos.get(paramName); boolean hasParam = existingInfo != null && existingInfo.paramLine != -1; @@ -271,9 +322,9 @@ public static String updatedDocstring(String baseDocstring, List params, int addIndex = getAddIndex(paramName, params, paramI, paramInfos, otherMatches, splitted); // neither is present, so, add both at a given location (if needed). - if (docstringStyle.equals(Character.toString('G'))) { + if (isGoogleStyle) { splitted.add(addIndex, - buf.append(indent).append(indent).append(paramName).append(":").toString()); + buf.append(indent).append(paramName).append(":").toString()); } else { splitted.add(addIndex, buf.append(indent).append(docstringStyle).append("param ") .append(paramName).append(":").toString()); @@ -300,8 +351,8 @@ public static String updatedDocstring(String baseDocstring, List params, // Add only the type after the existing param (if it has to be added) if (addTypeForParam) { int addIndex = existingInfo.paramLine + 1; - if (docstringStyle.equals(Character.toString('G'))) { - splitted.add(addIndex, buf.append(indent).append(paramName).append(":").toString()); + if (isGoogleStyle) { + // splitted.add(addIndex, buf.append(indent).append(paramName).append(":").toString()); } else { splitted.add(addIndex, buf.append(indent).append(docstringStyle).append("type ") .append(paramName).append(":").toString()); @@ -321,12 +372,13 @@ public static String updatedDocstring(String baseDocstring, List params, } } } - String lastLine = splitted.get(splitted.size() - 1).trim(); - if (lastLine.length() > 0) { - // Last line must have only the indentation (compute after all new tags are added). - splitted.add(indent); + buf.append(docStringStartEnd); + buf.append(StringUtils.join(delimiter, splitted)); + String lastLine = splitted.get(splitted.size() - 1); + if (!lastIndent.equals(lastLine)) { + buf.append(delimiter).append(lastIndent); } - buf.append(docStringStartEnd).append(StringUtils.join(delimiter, splitted)).append(docStringStartEnd); + buf.append(docStringStartEnd); return buf.toString(); } diff --git a/plugins/org.python.pydev.core/src/org/python/pydev/core/docstrings/DocstringPreferences.java b/plugins/org.python.pydev.core/src/org/python/pydev/core/docstrings/DocstringPreferences.java index a24acc189e..6d148faea8 100644 --- a/plugins/org.python.pydev.core/src/org/python/pydev/core/docstrings/DocstringPreferences.java +++ b/plugins/org.python.pydev.core/src/org/python/pydev/core/docstrings/DocstringPreferences.java @@ -90,6 +90,10 @@ public static boolean getTypeTagShouldBeGenerated(String parameterName) { IEclipsePreferences preferences = PydevPrefs.getEclipsePreferences(); String preference = preferences.get(TYPETAG_GENERATION, DEFAULT_TYPETAG_GENERATION).toLowerCase().strip(); + if (DOCSTRING_STYLE_GOOGLE.equals(getPreferredDocstringStyle())) { + // For google there's no separate tag. + return false; + } if (preference.equals(TYPETAG_GENERATION_NEVER.toLowerCase())) { return false; } else if (preference.equals(TYPETAG_GENERATION_ALWAYS.toLowerCase())) { diff --git a/plugins/org.python.pydev/tests/org/python/pydev/editor/correctionassist/AssistDocStringTest.java b/plugins/org.python.pydev/tests/org/python/pydev/editor/correctionassist/AssistDocStringTest.java index c1ec36c6a6..b82cd90c8c 100644 --- a/plugins/org.python.pydev/tests/org/python/pydev/editor/correctionassist/AssistDocStringTest.java +++ b/plugins/org.python.pydev/tests/org/python/pydev/editor/correctionassist/AssistDocStringTest.java @@ -299,7 +299,7 @@ public void testUpdateDocstring() { + " test\n" + " :param a:\n" + " :type a:\n" - + " '''", AssistDocString.updatedDocstring("'''test'''", Arrays.asList("a"), "\n", " ", ":")); + + "'''", AssistDocString.updatedDocstring("'''test'''", Arrays.asList("a"), "\n", " ", ":")); } public void testUpdateDocstring2() { @@ -307,7 +307,7 @@ public void testUpdateDocstring2() { + " :param a:\n" + " :type a:\n" + " :param test:\n" - + " '''", + + "'''", AssistDocString.updatedDocstring("''':param test:'''", Arrays.asList("a"), "\n", " ", ":")); } @@ -316,7 +316,7 @@ public void testUpdateDocstring3() { + " :param a:\n" + " :type a:\n" + " :param test:\n" - + " '''", + + "'''", AssistDocString.updatedDocstring("'''\n" + " :param test:\n" + "'''", Arrays.asList("a"), "\n", " ", ":")); @@ -329,7 +329,7 @@ public void testUpdateDocstring3a() { + " :param b:\n" + " :type b:\n" + " :param test:\n" - + " '''", + + "'''", AssistDocString.updatedDocstring("'''\n" + " :param test:\n" + "'''", Arrays.asList("a", "b"), "\n", " ", ":")); @@ -344,7 +344,7 @@ public void testUpdateDocstring3b() { + " :param c:\n" + " :type c:\n" + " :param test:\n" - + " '''", + + "'''", AssistDocString.updatedDocstring("'''\n" + " :param test:\n" + "'''", Arrays.asList("a", "b", "c"), "\n", " ", ":")); @@ -359,7 +359,7 @@ public void testUpdateDocstring3c() { + " :param c:\n" + " :type c:\n" + " :param test:\n" - + " '''", + + "'''", AssistDocString.updatedDocstring("'''\n" + " :param b:\n" + " :param test:\n" @@ -371,7 +371,7 @@ public void testUpdateDocstring4() { + " :param test:\n" + " :param a:\n" + " :type a:\n" - + " '''", + + "'''", AssistDocString.updatedDocstring("'''\n" + " :param test:\n" + " :param a:\n" @@ -383,7 +383,7 @@ public void testUpdateDocstring5() { + " :param a:\n" + " :type a:\n" + " :param test:\n" - + " '''", + + "'''", AssistDocString.updatedDocstring("'''\n" + " :param a:\n" + " :param test:\n" @@ -397,7 +397,7 @@ public void testUpdateDocstring6() { + " :param b: var b\n" + " :type b:\n" + " :param test:\n" - + " '''", + + "'''", AssistDocString.updatedDocstring("'''\n" + " :param a: var a\n" + " :param b: var b\n" @@ -412,7 +412,7 @@ public void testUpdateDocstring7() { + " :param b:\n" + " :type b:\n" + " :param test:\n" - + " '''", + + "'''", AssistDocString.updatedDocstring("'''\n" + " :param b:\n" + " :param test:\n" @@ -426,7 +426,7 @@ public void testUpdateDocstring8() { + " :param b:\n" + " :type b:\n" + " :param test:\n" - + " '''", + + "'''", AssistDocString.updatedDocstring("'''\n" + " :type a:\n" + " :type b:\n" @@ -443,7 +443,7 @@ public void testUpdateDocstringSphinx() { + " :param str a: var a\n" + " :param b: var b\n" + " :param test:\n" - + " '''", + + "'''", AssistDocString.updatedDocstring("'''\n" + " :param str a: var a\n" + " :param b: var b\n" @@ -461,7 +461,7 @@ public void testUpdateDocstringSphinx2() { + " :param b: var b\n" + " :type b:\n" + " :param test:\n" - + " '''", + + "'''", AssistDocString.updatedDocstring("'''\n" + " :param str a: var a\n" + " :param b: var b\n" @@ -470,15 +470,53 @@ public void testUpdateDocstringSphinx2() { } public void testUpdateDocstringGoogle() { + DocstringPreferences.GENERATE_TYPE_DOCSTRING_ON_TESTS = false; + String initialAndExpected = "'''\n" + + "Args:\n" + + " a:\n" + + " b:\n" + + " c:\n" + + "'''"; + assertEquals(initialAndExpected, + AssistDocString.updatedDocstring(initialAndExpected, Collections.emptyList(), "\n", " ", "G")); + } + + public void testUpdateDocstringGoogle2() { + DocstringPreferences.GENERATE_TYPE_DOCSTRING_ON_TESTS = false; assertEquals("'''\n" + + "Args:\n" + " a:\n" + " b:\n" + " c:\n" - + " '''", + + "'''", AssistDocString.updatedDocstring("'''\n" + + "Args:\n" + " a:\n" - + " b:\n" - + " c:\n" - + "'''", Collections.emptyList(), "\n", " ", "G")); + + "'''", Arrays.asList("a", "b", "c"), "\n", " ", "G")); + } + + public void testUpdateDocstringGoogle3() { + DocstringPreferences.GENERATE_TYPE_DOCSTRING_ON_TESTS = false; + assertEquals("'''\n" + + " Args:\n" + + " a:\n" + + " b:\n" + + " c:\n" + + " '''", + AssistDocString.updatedDocstring("'''\n" + + " Args:\n" + + " a:\n" + + " '''", Arrays.asList("a", "b", "c"), "\n", " ", "G")); + } + + public void testUpdateDocstringGoogle4() { + DocstringPreferences.GENERATE_TYPE_DOCSTRING_ON_TESTS = false; + assertEquals("'''\n" + + " \n" + + " Args:\n" + + " a:\n" + + " '''", + AssistDocString.updatedDocstring("'''\n" + + " '''", Arrays.asList("a"), "\n", " ", "G")); } }