mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
PY-16761 Prepend vararg parameters with * in generated Google code style docstrings
This commit is contained in:
+13
-16
@@ -23,6 +23,7 @@ import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.PsiReference;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import com.jetbrains.python.PyBundle;
|
||||
import com.jetbrains.python.debugger.PySignature;
|
||||
import com.jetbrains.python.debugger.PySignatureCacheManager;
|
||||
@@ -31,6 +32,7 @@ import com.jetbrains.python.documentation.PyDocstringGenerator;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.toolbox.Substring;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* User: ktisha
|
||||
@@ -57,7 +59,6 @@ public class SpecifyTypeInDocstringIntention extends TypeIntention {
|
||||
|
||||
final PsiElement resolved = reference != null ? reference.resolve() : null;
|
||||
PyParameter parameter = getParameter(problemElement, resolved);
|
||||
String kind = parameter != null ? "type" : "rtype";
|
||||
|
||||
final PyCallable callable;
|
||||
if (parameter != null) {
|
||||
@@ -67,29 +68,25 @@ public class SpecifyTypeInDocstringIntention extends TypeIntention {
|
||||
callable = getCallable(elementAt);
|
||||
}
|
||||
if (callable instanceof PyFunction) {
|
||||
generateDocstring(kind, (PyFunction)callable, problemElement);
|
||||
generateDocstring(parameter, (PyFunction)callable);
|
||||
}
|
||||
}
|
||||
|
||||
private static void generateDocstring(String kind, PyFunction pyFunction, PyExpression problemElement) {
|
||||
private static void generateDocstring(@Nullable PyParameter param, PyFunction pyFunction) {
|
||||
if (!DocStringUtil.ensureNotPlainDocstringFormat(pyFunction)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final boolean isReturn = "rtype".equals(kind);
|
||||
|
||||
final PyDocstringGenerator docstringGenerator = PyDocstringGenerator.forDocStringOwner(pyFunction);
|
||||
final PySignature signature = PySignatureCacheManager.getInstance(pyFunction.getProject()).findSignature(pyFunction);
|
||||
final String name = isReturn ? "" : StringUtil.notNullize(problemElement.getName());
|
||||
final String type;
|
||||
if (signature != null) {
|
||||
type = StringUtil.notNullize(signature.getArgTypeQualifiedName(name), "object");
|
||||
}
|
||||
else {
|
||||
type = "object";
|
||||
}
|
||||
if (!isReturn) {
|
||||
docstringGenerator.withParamTypedByName(name, type);
|
||||
String type = "object";
|
||||
if (param != null) {
|
||||
final String paramName = StringUtil.notNullize(param.getName());
|
||||
final PySignature signature = PySignatureCacheManager.getInstance(pyFunction.getProject()).findSignature(pyFunction);
|
||||
if (signature != null) {
|
||||
type = ObjectUtils.chooseNotNull(signature.getArgTypeQualifiedName(paramName), type);
|
||||
}
|
||||
final String docParamName = DocStringUtil.getPreferredParameterName(docstringGenerator.getDocStringFormat(), param);
|
||||
docstringGenerator.withParamTypedByName(docParamName, type);
|
||||
}
|
||||
else {
|
||||
docstringGenerator.withReturnValue(type);
|
||||
|
||||
@@ -31,6 +31,8 @@ import com.jetbrains.python.psi.types.TypeEvalContext;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import static com.jetbrains.python.psi.PyUtil.as;
|
||||
|
||||
/**
|
||||
* User: ktisha
|
||||
*
|
||||
@@ -98,10 +100,11 @@ public abstract class TypeIntention implements IntentionAction {
|
||||
|
||||
@Nullable
|
||||
protected static PyParameter getParameter(PyExpression problemElement, PsiElement resolved) {
|
||||
PyParameter parameter = problemElement instanceof PyParameter? (PyParameter)problemElement : null;
|
||||
if (resolved instanceof PyParameter)
|
||||
PyParameter parameter = as(problemElement, PyParameter.class);
|
||||
if (resolved instanceof PyParameter) {
|
||||
parameter = (PyParameter)resolved;
|
||||
return parameter;
|
||||
}
|
||||
return parameter == null || parameter.isSelf() ? null : parameter;
|
||||
}
|
||||
|
||||
private boolean isAvailableForReturn(@NotNull final PsiElement elementAt) {
|
||||
|
||||
@@ -306,4 +306,12 @@ public class DocStringUtil {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static String getPreferredParameterName(@NotNull DocStringFormat format, @NotNull PyParameter parameter) {
|
||||
if (format == DocStringFormat.GOOGLE && parameter.getAsNamed() != null) {
|
||||
return parameter.getAsNamed().getRepr(false);
|
||||
}
|
||||
return StringUtil.notNullize(parameter.getName());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ package com.jetbrains.python.documentation;
|
||||
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.toolbox.Substring;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -99,7 +98,7 @@ public class GoogleCodeStyleDocString extends SectionBasedDocString {
|
||||
type = name;
|
||||
name = null;
|
||||
}
|
||||
if (name != null ? !PyNames.isIdentifierString(name.toString()) : type.isEmpty()) {
|
||||
if (name != null ? !isValidFieldName(name) : type.isEmpty()) {
|
||||
return Pair.create(null, lineNum);
|
||||
}
|
||||
description = colonSeparatedParts.get(1);
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
package com.jetbrains.python.documentation;
|
||||
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.toolbox.Substring;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -83,7 +82,7 @@ public class NumpyDocString extends SectionBasedDocString {
|
||||
type = name;
|
||||
name = null;
|
||||
}
|
||||
if (name != null? !PyNames.isIdentifierString(name.toString()) : type.isEmpty()) {
|
||||
if (name != null? !isValidFieldName(name) : type.isEmpty()) {
|
||||
return Pair.create(null, lineNum);
|
||||
}
|
||||
final Pair<List<Substring>, Integer> parsedDescription = parseIndentedBlock(lineNum + 1, getLineIndentSize(lineNum));
|
||||
|
||||
@@ -170,12 +170,15 @@ public class PyDocstringGenerator {
|
||||
public PyDocstringGenerator withInferredParameters(boolean alwaysAddReturn) {
|
||||
if (myDocStringOwner instanceof PyFunction) {
|
||||
for (PyParameter param : ((PyFunction)myDocStringOwner).getParameterList().getParameters()) {
|
||||
if (param.getAsNamed() == null) {
|
||||
continue;
|
||||
}
|
||||
final String paramName = param.getName();
|
||||
final StructuredDocString docString = getStructuredDocString();
|
||||
if (StringUtil.isEmpty(paramName) || param.isSelf() || docString != null && docString.getParameters().contains(paramName)) {
|
||||
continue;
|
||||
}
|
||||
withParam(paramName);
|
||||
withParam(DocStringUtil.getPreferredParameterName(getDocStringFormat(), param));
|
||||
}
|
||||
final RaiseVisitor visitor = new RaiseVisitor();
|
||||
final PyStatementList statementList = ((PyFunction)myDocStringOwner).getStatementList();
|
||||
|
||||
@@ -29,6 +29,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
@@ -38,6 +39,7 @@ import java.util.regex.Pattern;
|
||||
* @see <a href="http://sphinxcontrib-napoleon.readthedocs.org/en/latest/index.html">Napoleon</a>
|
||||
*/
|
||||
public abstract class SectionBasedDocString extends DocStringLineParser implements StructuredDocString {
|
||||
protected static final Pattern FIELD_NAME = Pattern.compile("\\*{1,2}\\s*(\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*)");
|
||||
|
||||
/**
|
||||
* Frequently used section types
|
||||
@@ -281,10 +283,10 @@ public abstract class SectionBasedDocString extends DocStringLineParser implemen
|
||||
@NotNull
|
||||
@Override
|
||||
public List<String> getParameters() {
|
||||
return ContainerUtil.map(getParameterSubstrings(), new Function<Substring, String>() {
|
||||
return ContainerUtil.map(getParameterFields(), new Function<SectionField, String>() {
|
||||
@Override
|
||||
public String fun(Substring substring) {
|
||||
return substring.toString();
|
||||
public String fun(SectionField field) {
|
||||
return field.getName();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -521,6 +523,19 @@ public abstract class SectionBasedDocString extends DocStringLineParser implemen
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected static String extractFieldName(@NotNull CharSequence s) {
|
||||
final Matcher matcher = FIELD_NAME.matcher(s);
|
||||
if (matcher.matches()) {
|
||||
return matcher.group(1);
|
||||
}
|
||||
return s.toString();
|
||||
}
|
||||
|
||||
public static boolean isValidFieldName(@NotNull CharSequence s) {
|
||||
return FIELD_NAME.matcher(s).matches();
|
||||
}
|
||||
|
||||
public static class Section {
|
||||
private final Substring myTitle;
|
||||
private final List<SectionField> myFields;
|
||||
@@ -585,7 +600,7 @@ public abstract class SectionBasedDocString extends DocStringLineParser implemen
|
||||
|
||||
@NotNull
|
||||
public String getName() {
|
||||
return myName == null ? "" : myName.toString();
|
||||
return myName == null ? "" : extractFieldName(myName.toString());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
@@ -15,10 +15,8 @@
|
||||
*/
|
||||
package com.jetbrains.python.inspections;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.intellij.codeInspection.LocalInspectionToolSession;
|
||||
import com.intellij.codeInspection.ProblemsHolder;
|
||||
import com.intellij.lang.ASTNode;
|
||||
@@ -28,8 +26,9 @@ import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiElementVisitor;
|
||||
import com.jetbrains.python.PyBundle;
|
||||
import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.documentation.DocStringFormat;
|
||||
import com.jetbrains.python.documentation.DocStringUtil;
|
||||
import com.jetbrains.python.documentation.PlainDocString;
|
||||
import com.jetbrains.python.inspections.quickfix.DocstringQuickFix;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.testing.PythonUnitTestUtil;
|
||||
@@ -38,7 +37,9 @@ import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author Alexey.Ivanov
|
||||
@@ -142,34 +143,24 @@ public class PyDocstringInspection extends PyInspection {
|
||||
|
||||
StructuredDocString docString = DocStringUtil.parse(text, node);
|
||||
|
||||
if (docString == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
List<Substring> docstringParams = docString.getParameterSubstrings();
|
||||
|
||||
if (docstringParams == null) {
|
||||
if (docString instanceof PlainDocString) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pyDocStringOwner instanceof PyFunction) {
|
||||
PyDecoratorList decoratorList = ((PyFunction)pyDocStringOwner).getDecoratorList();
|
||||
boolean isClassMethod = false;
|
||||
if (decoratorList != null) {
|
||||
isClassMethod = decoratorList.findDecorator(PyNames.CLASSMETHOD) != null;
|
||||
}
|
||||
PyParameter[] realParams = ((PyFunction)pyDocStringOwner).getParameterList().getParameters();
|
||||
|
||||
List<PyParameter> missingParams = getMissingParams(realParams, docstringParams, isClassMethod);
|
||||
List<PyParameter> missingParams = getMissingParams(docString, realParams);
|
||||
boolean registered = false;
|
||||
if (!missingParams.isEmpty()) {
|
||||
for (PyParameter param : missingParams) {
|
||||
registerProblem(param, "Missing parameter " + param.getName() + " in docstring",
|
||||
new DocstringQuickFix(param.getName(), null));
|
||||
final DocStringFormat format = DocStringUtil.getConfiguredDocStringFormat(pyDocStringOwner);
|
||||
final String docParamName = DocStringUtil.getPreferredParameterName(format, param);
|
||||
registerProblem(param, "Missing parameter " + param.getName() + " in docstring", new DocstringQuickFix(docParamName, null));
|
||||
}
|
||||
registered = true;
|
||||
}
|
||||
List<Substring> unexpectedParams = getUnexpectedParams(docstringParams, realParams, node);
|
||||
List<Substring> unexpectedParams = getUnexpectedParams(docString, realParams);
|
||||
if (!unexpectedParams.isEmpty()) {
|
||||
for (Substring param : unexpectedParams) {
|
||||
ProblemsHolder holder = getHolder();
|
||||
@@ -187,13 +178,11 @@ public class PyDocstringInspection extends PyInspection {
|
||||
return false;
|
||||
}
|
||||
|
||||
private static List<Substring> getUnexpectedParams(List<Substring> docstringParams,
|
||||
PyParameter[] realParams,
|
||||
PyStringLiteralExpression node) {
|
||||
private static List<Substring> getUnexpectedParams(StructuredDocString docString, PyParameter[] realParams) {
|
||||
Map<String, Substring> unexpected = Maps.newHashMap();
|
||||
|
||||
for (Substring s : docstringParams) {
|
||||
unexpected.put(s.getValue(), s);
|
||||
for (Substring s : docString.getParameterSubstrings()) {
|
||||
unexpected.put(StringUtil.trimLeading(s.getValue(), '*'), s);
|
||||
}
|
||||
|
||||
for (PyParameter p : realParams) {
|
||||
@@ -204,26 +193,18 @@ public class PyDocstringInspection extends PyInspection {
|
||||
return Lists.newArrayList(unexpected.values());
|
||||
}
|
||||
|
||||
private static List<PyParameter> getMissingParams(PyParameter[] realParams, List<Substring> docstringParams, boolean isClassMethod) {
|
||||
private static List<PyParameter> getMissingParams(StructuredDocString docString, PyParameter[] realParams) {
|
||||
List<PyParameter> missing = new ArrayList<PyParameter>();
|
||||
Set<String> params = Sets.newHashSet(Lists.transform(docstringParams, new Function<Substring, String>() {
|
||||
@Override
|
||||
public String apply(Substring input) {
|
||||
return input.getValue();
|
||||
}
|
||||
}));
|
||||
boolean hasMissing = false;
|
||||
for (PyParameter p : realParams) {
|
||||
String paramText = p.getText();
|
||||
if ((!isClassMethod && !paramText.equals(PyNames.CANONICAL_SELF)) && !paramText.equals("*") ||
|
||||
(isClassMethod && !paramText.equals("cls"))) {
|
||||
if (!params.contains(p.getName())) {
|
||||
hasMissing = true;
|
||||
missing.add(p);
|
||||
}
|
||||
if (p.isSelf() || p instanceof PySingleStarParameter || p instanceof PyTupleParameter) {
|
||||
continue;
|
||||
}
|
||||
//noinspection ConstantConditions
|
||||
if (docString.getParameters().contains(p.getName())) {
|
||||
missing.add(p);
|
||||
}
|
||||
}
|
||||
return hasMissing ? missing : Collections.<PyParameter>emptyList();
|
||||
return missing;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,6 @@ import org.jetbrains.annotations.Nullable;
|
||||
public class DocstringQuickFix implements LocalQuickFix {
|
||||
String myMissingText;
|
||||
String myUnexpected;
|
||||
String myPrefix;
|
||||
|
||||
public DocstringQuickFix(String missing, String unexpected) {
|
||||
myMissingText = missing;
|
||||
|
||||
+2
-1
@@ -33,6 +33,7 @@ import com.intellij.util.containers.HashSet;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.PythonLanguage;
|
||||
import com.jetbrains.python.documentation.DocStringUtil;
|
||||
import com.jetbrains.python.documentation.PyDocstringGenerator;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.search.PyOverridingMethodsSearch;
|
||||
@@ -339,7 +340,7 @@ public class PyChangeSignatureUsageProcessor implements ChangeSignatureUsageProc
|
||||
for (PyParameter p : function.getParameterList().getParameters()) {
|
||||
final String paramName = p.getName();
|
||||
if (!names.contains(paramName) && paramName != null) {
|
||||
generator.withoutParam(paramName);
|
||||
generator.withoutParam(DocStringUtil.getPreferredParameterName(generator.getDocStringFormat(), p));
|
||||
}
|
||||
}
|
||||
generator.buildAndInsert();
|
||||
|
||||
Reference in New Issue
Block a user