Merge remote-tracking branch 'origin/master'

This commit is contained in:
Ekaterina Tuzova
2013-06-18 12:29:39 +04:00
29 changed files with 363 additions and 274 deletions
@@ -3,6 +3,12 @@ package com.jetbrains.python.psi;
import org.jetbrains.annotations.Nullable;
public interface PyDocStringOwner extends PyElement {
@Nullable
String getDocStringValue();
@Nullable
StructuredDocString getStructuredDocString();
@Nullable
PyStringLiteralExpression getDocStringExpression();
}
@@ -48,9 +48,6 @@ extends
@Nullable
PyType getReturnTypeFromDocString();
@Nullable
String getDocStringValue();
/**
* If the function raises a DeprecationWarning or a PendingDeprecationWarning, returns the explanation text provided for the warning..
*
@@ -0,0 +1,69 @@
package com.jetbrains.python.psi;
import com.jetbrains.python.toolbox.Substring;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
/**
* @author vlan
*/
public interface StructuredDocString {
String getDescription();
String getSummary();
@Nullable
Substring getTagValue(String... tagNames);
@Nullable
Substring getTagValue(String tagName, @NotNull String argName);
@Nullable
Substring getTagValue(String[] tagNames, @NotNull String argName);
List<Substring> getTagArguments(String... tagNames);
List<Substring> getParameterSubstrings();
@Nullable
Substring getParamByNameAndKind(@NotNull String name, String kind);
List<String> getParameters();
List<String> getKeywordArguments();
@Nullable
String getReturnType();
@Nullable
String getReturnDescription();
@Nullable
String getParamType(@Nullable String paramName);
@Nullable
String getParamDescription(@Nullable String paramName);
@Nullable
String getKeywordArgumentDescription(@Nullable String paramName);
List<String> getRaisedExceptions();
@Nullable
String getRaisedExceptionDescription(@Nullable String exceptionName);
@Nullable
String getAttributeDescription();
List<String> getAdditionalTags();
List<Substring> getKeywordArgumentSubstrings();
@Nullable
Substring getReturnTypeSubstring();
@Nullable
Substring getParamTypeSubstring(@Nullable String paramName);
}
@@ -1,4 +1,4 @@
package com.jetbrains.python.documentation;
package com.jetbrains.python.toolbox;
import com.intellij.openapi.util.TextRange;
import org.jetbrains.annotations.NotNull;
@@ -8,15 +8,13 @@ import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author vlan
*/
/**
* Substring with explicit offsets within its parent string.
* <p>
* Regular java.lang.String objects share a single char buffer for results of substring(), trim(), etc., but the offset and count
* fields of Strings are unfortunately private.
*
* @author vlan
*/
public class Substring implements CharSequence {
private static final Pattern RE_NL = Pattern.compile("(\\r?\\n)");
@@ -1,33 +0,0 @@
package com.jetbrains.python;
import com.intellij.psi.PsiElement;
import com.jetbrains.python.psi.PyElement;
import com.jetbrains.python.psi.PyExpressionStatement;
import com.jetbrains.python.psi.PyStringLiteralExpression;
import com.jetbrains.python.psi.PyUtil;
import org.jetbrains.annotations.Nullable;
// TODO: find a better place for this.
/**
* Utility class for finding an expression which would fit as a doc string.
* User: dcheryasov
* Date: Jun 7, 2009 5:06:12 AM
*/
public class PythonDocStringFinder {
private PythonDocStringFinder() {}
/**
* Looks for a doc string under given parent.
* @param parent where to look. For classes and functions, this would be PyStatementList, for modules, PyFile.
* @return the defining expression, or null.
*/
@Nullable
public static PyStringLiteralExpression find(PyElement parent) {
if (parent != null) {
PsiElement seeker = PyUtil.getFirstNonCommentAfter(parent.getFirstChild());
if (seeker instanceof PyExpressionStatement) seeker = PyUtil.getFirstNonCommentAfter(seeker.getFirstChild());
if (seeker instanceof PyStringLiteralExpression) return (PyStringLiteralExpression)seeker;
}
return null;
}
}
@@ -8,8 +8,8 @@ import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.util.IncorrectOperationException;
import com.jetbrains.python.PythonDocStringFinder;
import com.jetbrains.python.codeInsight.PyCodeInsightSettings;
import com.jetbrains.python.documentation.DocStringUtil;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.impl.PyQualifiedName;
import com.jetbrains.python.psi.resolve.QualifiedNameFinder;
@@ -63,7 +63,7 @@ public class AddImportHelper {
// maybe we arrived at the doc comment stmt; skip over it, too
else if (!skippedOverImports && !skippedOverDoc && file instanceof PyFile) {
PsiElement doc_elt =
PythonDocStringFinder.find((PyElement)file); // this gives the literal; its parent is the expr seeker may have encountered
DocStringUtil.findDocStringExpression((PyElement)file); // this gives the literal; its parent is the expr seeker may have encountered
if (doc_elt != null && doc_elt.getParent() == feeler) {
feeler = feeler.getNextSibling();
seeker = feeler; // skip over doc even if there's nothing below it
@@ -12,7 +12,6 @@ import com.jetbrains.python.PyBundle;
import com.jetbrains.python.debugger.PySignature;
import com.jetbrains.python.debugger.PySignatureCacheManager;
import com.jetbrains.python.documentation.PyDocstringGenerator;
import com.jetbrains.python.documentation.StructuredDocString;
import com.jetbrains.python.psi.*;
import org.jetbrains.annotations.NotNull;
@@ -83,23 +82,15 @@ public class SpecifyTypeInDocstringIntention extends TypeIntention {
protected boolean isParamTypeDefined(PyParameter parameter) {
PyFunction pyFunction = PsiTreeUtil.getParentOfType(parameter, PyFunction.class);
if (pyFunction != null && parameter != null) {
final String docstring = pyFunction.getDocStringValue();
if (docstring != null) {
StructuredDocString structuredDocString = StructuredDocString.parse(docstring);
return structuredDocString != null && structuredDocString.getParamType(StringUtil.notNullize(parameter.getName())) != null;
}
return false;
final StructuredDocString structuredDocString = pyFunction.getStructuredDocString();
return structuredDocString != null && structuredDocString.getParamType(StringUtil.notNullize(parameter.getName())) != null;
}
return false;
}
@Override
protected boolean isReturnTypeDefined(@NotNull PyFunction function) {
final String docstring = function.getDocStringValue();
if (docstring != null) {
StructuredDocString structuredDocString = StructuredDocString.parse(docstring);
return structuredDocString != null && structuredDocString.getReturnType( ) != null;
}
return false;
final StructuredDocString structuredDocString = function.getStructuredDocString();
return structuredDocString != null && structuredDocString.getReturnType() != null;
}
}
@@ -7,7 +7,8 @@ import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.PythonHelpersLocator;
import com.jetbrains.python.documentation.StructuredDocString;
import com.jetbrains.python.documentation.DocStringUtil;
import com.jetbrains.python.psi.StructuredDocString;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.impl.PyBuiltinCache;
import com.jetbrains.python.psi.impl.PyQualifiedName;
@@ -280,7 +281,7 @@ public class PyStdlibTypeProvider extends PyTypeProviderBase {
if (docString == null && level.isPy3K()) { //if we couldn't find for Py3K will search in Python2 db
return getStructuredDocString(qualifiedName, LanguageLevel.PYTHON27);
}
return StructuredDocString.parse(docString);
return DocStringUtil.parse(docString);
}
@Nullable
@@ -10,9 +10,11 @@ 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;
import com.jetbrains.python.psi.impl.PyStringLiteralExpressionImpl;
import com.jetbrains.python.psi.types.PyType;
import com.jetbrains.python.psi.types.PyTypeParser;
import com.jetbrains.python.toolbox.Substring;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -40,17 +42,17 @@ public class DocStringReferenceProvider extends PsiReferenceProvider {
final List<PsiReference> result = new ArrayList<PsiReference>();
final int offset = ranges.get(0).getStartOffset();
// XXX: It does not work with multielement docstrings
StructuredDocString docString = StructuredDocString.parse(text);
StructuredDocString docString = DocStringUtil.parse(text);
if (docString != null) {
result.addAll(referencesFromNames(element, offset, docString,
docString.getTagArguments(StructuredDocString.PARAM_TAGS), StructuredDocString.PARAMETER));
docString.getTagArguments(StructuredDocStringBase.PARAM_TAGS), StructuredDocStringBase.PARAMETER));
result.addAll(referencesFromNames(element, offset, docString,
docString.getTagArguments(StructuredDocString.PARAM_TYPE_TAGS), StructuredDocString.PARAMETER_TYPE));
docString.getTagArguments(StructuredDocStringBase.PARAM_TYPE_TAGS), StructuredDocStringBase.PARAMETER_TYPE));
result.addAll(referencesFromNames(element, offset, docString,
docString.getKeywordArgumentSubstrings(), StructuredDocString.KEYWORD));
docString.getKeywordArgumentSubstrings(), StructuredDocStringBase.KEYWORD));
result.addAll(referencesFromNames(element, offset, docString,
docString.getTagArguments(StructuredDocString.VARIABLE_TAGS), StructuredDocString.VARIABLE));
docString.getTagArguments(StructuredDocStringBase.VARIABLE_TAGS), StructuredDocStringBase.VARIABLE));
result.addAll(returnTypes(element, docString, offset));
}
return result.toArray(new PsiReference[result.size()]);
@@ -81,7 +83,7 @@ public class DocStringReferenceProvider extends PsiReferenceProvider {
if (PyNames.isIdentifier(s)) {
result.add(new DocStringParameterReference(element, name.getTextRange().shiftRight(offset), refType));
}
if (refType.equals(StructuredDocString.PARAMETER_TYPE)) {
if (refType.equals(StructuredDocStringBase.PARAMETER_TYPE)) {
final Substring type = docString.getParamTypeSubstring(s);
if (type != null) {
result.addAll(parseTypeReferences(element, type, offset));
@@ -1,5 +1,18 @@
package com.jetbrains.python.documentation;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.psi.PsiComment;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiWhiteSpace;
import com.intellij.psi.util.PsiTreeUtil;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.impl.PyPsiUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* User: catherine
*/
@@ -12,4 +25,128 @@ public class DocStringUtil {
.replaceFirst("^((:py)?:class:`[~!]?|[A-Z]\\{)", "")
.replaceFirst("(`|\\})?\\.?$", "");
}
@Nullable
public static String getDocStringValue(@NotNull PyDocStringOwner owner) {
return PyPsiUtils.strValue(owner.getDocStringExpression());
}
@Nullable
public static StructuredDocString parse(@Nullable String text) {
if (text == null) {
return null;
}
if (isSphinxDocString(text)) {
return new SphinxDocString(text);
}
return new EpydocString(text);
}
public static boolean isSphinxDocString(@NotNull String text) {
return text.contains(":param ") || text.contains(":rtype") || text.contains(":type");
}
public static boolean isEpydocDocString(@NotNull String text) {
return text.contains("@param ") || text.contains("@rtype") || text.contains("@type");
}
/**
* Looks for a doc string under given parent.
* @param parent where to look. For classes and functions, this would be PyStatementList, for modules, PyFile.
* @return the defining expression, or null.
*/
@Nullable
public static PyStringLiteralExpression findDocStringExpression(@Nullable PyElement parent) {
if (parent != null) {
PsiElement seeker = PyUtil.getFirstNonCommentAfter(parent.getFirstChild());
if (seeker instanceof PyExpressionStatement) seeker = PyUtil.getFirstNonCommentAfter(seeker.getFirstChild());
if (seeker instanceof PyStringLiteralExpression) return (PyStringLiteralExpression)seeker;
}
return null;
}
public static StructuredDocString getStructuredDocString(PyDocStringOwner owner) {
return parse(owner.getDocStringValue());
}
public static boolean isDocStringExpression(@Nullable PyExpression expression) {
final PyDocStringOwner docStringOwner = PsiTreeUtil.getParentOfType(expression, PyDocStringOwner.class);
if (docStringOwner != null) {
if (docStringOwner.getDocStringExpression() == expression) {
return true;
}
}
if (expression instanceof PyStringLiteralExpression) {
return isVariableDocString((PyStringLiteralExpression)expression);
}
return false;
}
@Nullable
public static PyStringLiteralExpression getAttributeDocString(@NotNull PyTargetExpression attr) {
if (attr.getParent() instanceof PyAssignmentStatement) {
final PyAssignmentStatement assignment = (PyAssignmentStatement)attr.getParent();
PsiElement nextSibling = assignment.getNextSibling();
while (nextSibling != null && (nextSibling instanceof PsiWhiteSpace || nextSibling instanceof PsiComment)) {
nextSibling = nextSibling.getNextSibling();
}
if (nextSibling instanceof PyExpressionStatement) {
final PyExpression expression = ((PyExpressionStatement)nextSibling).getExpression();
if (expression instanceof PyStringLiteralExpression) {
return (PyStringLiteralExpression)expression;
}
}
}
return null;
}
@Nullable
public static String getAttributeDocComment(@NotNull PyTargetExpression attr) {
if (attr.getParent() instanceof PyAssignmentStatement) {
final PyAssignmentStatement assignment = (PyAssignmentStatement)attr.getParent();
PsiElement prevSibling = assignment.getPrevSibling();
while (prevSibling != null && (prevSibling instanceof PsiWhiteSpace)) {
prevSibling = prevSibling.getPrevSibling();
}
if (prevSibling instanceof PsiComment && prevSibling.getText().startsWith("#:")) {
return prevSibling.getText().substring(2);
}
}
return null;
}
public static boolean isVariableDocString(@NotNull PyStringLiteralExpression expr) {
final Module module = ModuleUtilCore.findModuleForPsiElement(expr);
if (module == null) return false;
final PyDocumentationSettings settings = PyDocumentationSettings.getInstance(module);
if (settings.isEpydocFormat(expr.getContainingFile()) || settings.isReSTFormat(expr.getContainingFile())) {
final PsiElement parent = expr.getParent();
if (!(parent instanceof PyExpressionStatement)) {
return false;
}
PsiElement prevElement = parent.getPrevSibling();
while (prevElement instanceof PsiWhiteSpace || prevElement instanceof PsiComment) {
prevElement = prevElement.getPrevSibling();
}
if (prevElement instanceof PyAssignmentStatement) {
final PyAssignmentStatement assignmentStatement = (PyAssignmentStatement)prevElement;
final ScopeOwner scope = PsiTreeUtil.getParentOfType(prevElement, ScopeOwner.class);
if (scope instanceof PyClass || scope instanceof PyFile) {
return true;
}
if (scope instanceof PyFunction) {
PyFunction function = (PyFunction) scope;
if (!PyNames.INIT.equals(function.getName())) {
return false;
}
for (PyExpression target : assignmentStatement.getTargets()) {
if (PyUtil.isInstanceAttribute(target)) {
return true;
}
}
}
}
}
return false;
}
}
@@ -2,6 +2,7 @@ package com.jetbrains.python.documentation;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.xml.util.XmlTagUtilBase;
import com.jetbrains.python.toolbox.Substring;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -10,7 +11,7 @@ import java.util.*;
/**
* @author yole
*/
public class EpydocString extends StructuredDocString {
public class EpydocString extends StructuredDocStringBase {
public static String[] RTYPE_TAGS = new String[] { "rtype", "returntype" };
public static String[] KEYWORD_ARGUMENT_TAGS = new String[] { "keyword", "kwarg", "kwparam" };
@@ -1,54 +0,0 @@
package com.jetbrains.python.documentation;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.psi.PsiComment;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiWhiteSpace;
import com.intellij.psi.util.PsiTreeUtil;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
import com.jetbrains.python.psi.*;
/**
* @author yole
*/
public class EpydocUtil {
private EpydocUtil() {
}
public static boolean isVariableDocString(PyStringLiteralExpression expr) {
final Module module = ModuleUtilCore.findModuleForPsiElement(expr);
if (module == null) return false;
final PyDocumentationSettings settings = PyDocumentationSettings.getInstance(module);
if (settings.isEpydocFormat(expr.getContainingFile()) || settings.isReSTFormat(expr.getContainingFile())) {
final PsiElement parent = expr.getParent();
if (!(parent instanceof PyExpressionStatement)) {
return false;
}
PsiElement prevElement = parent.getPrevSibling();
while (prevElement instanceof PsiWhiteSpace || prevElement instanceof PsiComment) {
prevElement = prevElement.getPrevSibling();
}
if (prevElement instanceof PyAssignmentStatement) {
final PyAssignmentStatement assignmentStatement = (PyAssignmentStatement)prevElement;
final ScopeOwner scope = PsiTreeUtil.getParentOfType(prevElement, ScopeOwner.class);
if (scope instanceof PyClass || scope instanceof PyFile) {
return true;
}
if (scope instanceof PyFunction) {
PyFunction function = (PyFunction) scope;
if (!PyNames.INIT.equals(function.getName())) {
return false;
}
for (PyExpression target : assignmentStatement.getTargets()) {
if (PyUtil.isInstanceAttribute(target)) {
return true;
}
}
}
}
}
return false;
}
}
@@ -31,6 +31,7 @@ import com.jetbrains.python.debugger.PySignature;
import com.jetbrains.python.debugger.PySignatureCacheManager;
import com.jetbrains.python.debugger.PySignatureUtil;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.toolbox.Substring;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -149,7 +150,7 @@ public class PyDocstringGenerator {
public Pair<String, Integer> addParamToDocstring() {
String text = getDocstringText();
StructuredDocString structuredDocString = StructuredDocString.parse(text);
StructuredDocString structuredDocString = DocStringUtil.parse(text);
Collection<DocstringParam> paramsToAdd = getParamsToAdd(structuredDocString, myParams);
@@ -172,7 +173,7 @@ public class PyDocstringGenerator {
private Collection<DocstringParam> collectParametersToAdd() {
String text = getDocstringText();
StructuredDocString structuredDocString = StructuredDocString.parse(text); //TODO: do we need to cache it?
StructuredDocString structuredDocString = DocStringUtil.parse(text); //TODO: do we need to cache it?
return getParamsToAdd(structuredDocString, myParams);
}
@@ -30,6 +30,7 @@ import com.jetbrains.python.psi.types.*;
import com.jetbrains.python.sdk.PythonSdkType;
import com.jetbrains.python.toolbox.ChainIterable;
import com.jetbrains.python.toolbox.Maybe;
import com.jetbrains.python.toolbox.Substring;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -365,7 +366,7 @@ class PyDocumentationBuilder {
String[] lines = removeCommonIndentation(docstring);
String preparedDocstring = StringUtil.join(lines, "\n");
if (documentationSettings.isEpydocFormat(element.getContainingFile()) ||
StructuredDocString.isEpydocDocstring(preparedDocstring)) {
DocStringUtil.isEpydocDocString(preparedDocstring)) {
final EpydocString epydocString = new EpydocString(preparedDocstring);
String formatted = epydocString.getDescription();
@@ -375,7 +376,7 @@ class PyDocumentationBuilder {
return;
}
else if (documentationSettings.isReSTFormat(element.getContainingFile()) ||
StructuredDocString.isSphinxDocstring(preparedDocstring)) {
DocStringUtil.isSphinxDocString(preparedDocstring)) {
String formatted = null;
Sdk pythonSdk = PythonSdkType.findPython2Sdk(module);
if (pythonSdk != null) {
@@ -451,7 +452,7 @@ class PyDocumentationBuilder {
}
private static Pair<String, String> getTypeAndDescr(String docString, @NotNull PyNamedParameter followed) {
StructuredDocString structuredDocString = StructuredDocString.parse(docString);
StructuredDocString structuredDocString = DocStringUtil.parse(docString);
String type = null;
String desc = null;
if (structuredDocString != null) {
@@ -473,7 +474,7 @@ class PyDocumentationBuilder {
.addItem(" of class ").addWith(PythonDocumentationProvider.LinkMyClass, $().addWith(TagCode, $(cls.getName()))).addItem(BR)
;
final String docString = PyPsiUtils.strValue(PyUtil.getAttributeDocString((PyTargetExpression)myElement));
final String docString = PyPsiUtils.strValue(DocStringUtil.getAttributeDocString((PyTargetExpression)myElement));
if (docString != null) {
addFormattedDocString(myElement, docString, myBody, myEpilog);
}
@@ -73,7 +73,7 @@ public class PythonDocumentationProvider extends AbstractDocumentationProvider i
String summary = "";
final PyStringLiteralExpression docStringExpression = func.getDocStringExpression();
if (docStringExpression != null) {
final StructuredDocString docString = StructuredDocString.parse(docStringExpression.getStringValue());
final StructuredDocString docString = DocStringUtil.parse(docStringExpression.getStringValue());
if (docString != null) {
summary = docString.getSummary();
}
@@ -92,7 +92,7 @@ public class PythonDocumentationProvider extends AbstractDocumentationProvider i
}
}
if (docStringExpression != null) {
final StructuredDocString docString = StructuredDocString.parse(docStringExpression.getStringValue());
final StructuredDocString docString = DocStringUtil.parse(docStringExpression.getStringValue());
if (docString != null) {
summary = docString.getSummary();
}
@@ -1,5 +1,6 @@
package com.jetbrains.python.documentation;
import com.jetbrains.python.toolbox.Substring;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -9,7 +10,7 @@ import java.util.List;
/**
* @author yole
*/
public class SphinxDocString extends StructuredDocString {
public class SphinxDocString extends StructuredDocStringBase {
public static String[] KEYWORD_ARGUMENT_TAGS = new String[] { "keyword", "key" };
public static String[] ALL_TAGS = new String[] { ":param", ":parameter", ":arg", ":argument", ":keyword", ":key",
":type", ":raise", ":raises", ":var", ":cvar", ":ivar",
@@ -3,6 +3,8 @@ package com.jetbrains.python.documentation;
import com.google.common.collect.Maps;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
import com.jetbrains.python.psi.StructuredDocString;
import com.jetbrains.python.toolbox.Substring;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -16,7 +18,7 @@ import java.util.regex.Pattern;
/**
* @author yole
*/
public abstract class StructuredDocString {
public abstract class StructuredDocStringBase implements StructuredDocString {
protected final String myDescription;
protected final Map<String, Substring> mySimpleTagValues = Maps.newHashMap();
@@ -38,26 +40,7 @@ public abstract class StructuredDocString {
public static String KEYWORD = "keyword";
public static String VARIABLE = "variable";
@Nullable
public static StructuredDocString parse(String text) {
if (text == null) {
return null;
}
if (isSphinxDocstring(text)) {
return new SphinxDocString(text);
}
return new EpydocString(text);
}
public static boolean isSphinxDocstring(String text) {
return text.contains(":param ") || text.contains(":rtype") || text.contains(":type");
}
public static boolean isEpydocDocstring(String text) {
return text.contains("@param ") || text.contains("@rtype") || text.contains("@type");
}
protected StructuredDocString(@NotNull String docStringText, String tagPrefix) {
protected StructuredDocStringBase(@NotNull String docStringText, String tagPrefix) {
final Substring docString = new Substring(docStringText);
final List<Substring> lines = docString.splitLines();
final int nlines = lines.size();
@@ -76,10 +59,12 @@ public abstract class StructuredDocString {
myDescription = builder.toString();
}
@Override
public String getDescription() {
return myDescription;
}
@Override
public String getSummary() {
final List<String> strings = StringUtil.split(StringUtil.trimLeading(myDescription), "\n", true, false);
if (strings.size() > 1) {
@@ -166,6 +151,7 @@ public abstract class StructuredDocString {
return result;
}
@Override
@Nullable
public Substring getTagValue(String... tagNames) {
for (String tagName : tagNames) {
@@ -177,12 +163,14 @@ public abstract class StructuredDocString {
return null;
}
@Override
@Nullable
public Substring getTagValue(String tagName, @NotNull String argName) {
final Map<Substring, Substring> argValues = myArgTagValues.get(tagName);
return argValues != null ? argValues.get(new Substring(argName)) : null;
}
@Override
@Nullable
public Substring getTagValue(String[] tagNames, @NotNull String argName) {
for (String tagName : tagNames) {
@@ -194,6 +182,7 @@ public abstract class StructuredDocString {
return null;
}
@Override
public List<Substring> getTagArguments(String... tagNames) {
for (String tagName : tagNames) {
final Map<Substring, Substring> map = myArgTagValues.get(tagName);
@@ -204,6 +193,7 @@ public abstract class StructuredDocString {
return Collections.emptyList();
}
@Override
public List<Substring> getParameterSubstrings() {
final List<Substring> results = new ArrayList<Substring>();
results.addAll(getTagArguments(PARAM_TAGS));
@@ -211,6 +201,7 @@ public abstract class StructuredDocString {
return results;
}
@Override
@Nullable
public Substring getParamByNameAndKind(@NotNull String name, String kind) {
for (Substring s: getTagArguments(kind)) {
@@ -220,39 +211,4 @@ public abstract class StructuredDocString {
}
return null;
}
public abstract List<String> getParameters();
public abstract List<String> getKeywordArguments();
@Nullable
public abstract String getReturnType();
@Nullable
public abstract String getReturnDescription();
@Nullable
public abstract String getParamType(@Nullable String paramName);
@Nullable
public abstract String getParamDescription(@Nullable String paramName);
@Nullable
public abstract String getKeywordArgumentDescription(@Nullable String paramName);
public abstract List<String> getRaisedExceptions();
@Nullable
public abstract String getRaisedExceptionDescription(@Nullable String exceptionName);
@Nullable
public abstract String getAttributeDescription();
public abstract List<String> getAdditionalTags();
public abstract List<Substring> getKeywordArgumentSubstrings();
@Nullable
public abstract Substring getReturnTypeSubstring();
@Nullable
public abstract Substring getParamTypeSubstring(@Nullable String paramName);
}
@@ -21,6 +21,7 @@ import com.jetbrains.python.documentation.*;
import com.jetbrains.python.inspections.quickfix.DocstringQuickFix;
import com.jetbrains.python.inspections.quickfix.PySuppressInspectionFix;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.toolbox.Substring;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -117,7 +118,7 @@ public class PyDocstringInspection extends PyInspection {
return false;
}
StructuredDocString docString = StructuredDocString.parse(text);
StructuredDocString docString = DocStringUtil.parse(text);
if (docString == null) {
return false;
@@ -8,8 +8,9 @@ import com.jetbrains.python.PyBundle;
import com.jetbrains.python.debugger.PySignature;
import com.jetbrains.python.debugger.PySignatureCacheManager;
import com.jetbrains.python.debugger.PySignatureUtil;
import com.jetbrains.python.documentation.StructuredDocString;
import com.jetbrains.python.documentation.Substring;
import com.jetbrains.python.documentation.DocStringUtil;
import com.jetbrains.python.psi.StructuredDocString;
import com.jetbrains.python.toolbox.Substring;
import com.jetbrains.python.psi.PyElementGenerator;
import com.jetbrains.python.psi.PyFunction;
import com.jetbrains.python.psi.PyStringLiteralExpression;
@@ -72,7 +73,7 @@ public class PyDocstringTypesInspection extends PyInspection {
return;
}
StructuredDocString docString = StructuredDocString.parse(text);
StructuredDocString docString = DocStringUtil.parse(text);
if (docString == null) {
return;
}
@@ -8,6 +8,7 @@ import com.intellij.psi.PsiWhiteSpace;
import com.intellij.psi.ResolveResult;
import com.intellij.psi.util.PsiTreeUtil;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.documentation.DocStringUtil;
import com.jetbrains.python.inspections.quickfix.StatementEffectDocstringQuickFix;
import com.jetbrains.python.inspections.quickfix.StatementEffectFunctionCallQuickFix;
import com.jetbrains.python.inspections.quickfix.StatementEffectIntroduceVariableQuickFix;
@@ -81,7 +82,7 @@ public class PyStatementEffectInspection extends PyInspection {
prevSibling = prevSibling.getPrevSibling();
if (prevSibling instanceof PyAssignmentStatement) {
for (PyExpression target : ((PyAssignmentStatement)prevSibling).getTargets()) {
if (PyUtil.getAttributeDocString((PyTargetExpression)target) == expression) {
if (DocStringUtil.getAttributeDocString((PyTargetExpression)target) == expression) {
return true;
}
}
@@ -107,7 +108,7 @@ public class PyStatementEffectInspection extends PyInspection {
}
if (expression instanceof PyStringLiteralExpression) {
if (PyUtil.isDocString(expression)) return true;
if (DocStringUtil.isDocStringExpression(expression)) return true;
}
else if (expression instanceof PyListCompExpression) {
if (hasEffect(((PyListCompExpression)expression).getResultExpression())) {
@@ -42,7 +42,6 @@ import com.jetbrains.python.PyTokenTypes;
import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil;
import com.jetbrains.python.codeInsight.stdlib.PyNamedTupleType;
import com.jetbrains.python.documentation.EpydocUtil;
import com.jetbrains.python.psi.impl.PyBuiltinCache;
import com.jetbrains.python.psi.impl.PyPsiUtils;
import com.jetbrains.python.psi.resolve.PyResolveContext;
@@ -459,50 +458,6 @@ public class PyUtil {
return element instanceof PyTargetExpression && ScopeUtil.getScopeOwner(element) instanceof PyClass;
}
public static boolean isDocString(PyExpression expression) {
final PyDocStringOwner docStringOwner = PsiTreeUtil.getParentOfType(expression, PyDocStringOwner.class);
if (docStringOwner != null) {
if (docStringOwner.getDocStringExpression() == expression) {
return true;
}
}
if (EpydocUtil.isVariableDocString((PyStringLiteralExpression)expression)) return true;
return false;
}
@Nullable
public static PyStringLiteralExpression getAttributeDocString(PyTargetExpression attr) {
if (attr.getParent() instanceof PyAssignmentStatement) {
final PyAssignmentStatement assignment = (PyAssignmentStatement)attr.getParent();
PsiElement nextSibling = assignment.getNextSibling();
while (nextSibling != null && (nextSibling instanceof PsiWhiteSpace || nextSibling instanceof PsiComment)) {
nextSibling = nextSibling.getNextSibling();
}
if (nextSibling instanceof PyExpressionStatement) {
final PyExpression expression = ((PyExpressionStatement)nextSibling).getExpression();
if (expression instanceof PyStringLiteralExpression) {
return (PyStringLiteralExpression)expression;
}
}
}
return null;
}
@Nullable
public static String getAttributeDocComment(PyTargetExpression attr) {
if (attr.getParent() instanceof PyAssignmentStatement) {
final PyAssignmentStatement assignment = (PyAssignmentStatement)attr.getParent();
PsiElement prevSibling = assignment.getPrevSibling();
while (prevSibling != null && (prevSibling instanceof PsiWhiteSpace)) {
prevSibling = prevSibling.getPrevSibling();
}
if (prevSibling instanceof PsiComment && prevSibling.getText().startsWith("#:")) {
return prevSibling.getText().substring(2);
}
}
return null;
}
public static boolean isIfNameEqualsMain(PyIfStatement ifStatement) {
final PyExpression condition = ifStatement.getIfPart().getCondition();
return isNameEqualsMain(condition);
@@ -19,6 +19,7 @@ import com.jetbrains.python.*;
import com.jetbrains.python.codeInsight.controlflow.ControlFlowCache;
import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil;
import com.jetbrains.python.documentation.DocStringUtil;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.resolve.PyResolveContext;
import com.jetbrains.python.psi.resolve.PyResolveUtil;
@@ -1003,7 +1004,7 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> implement
}
public PyStringLiteralExpression getDocStringExpression() {
return PythonDocStringFinder.find(getStatementList());
return DocStringUtil.findDocStringExpression(getStatementList());
}
@Override
@@ -1012,7 +1013,13 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> implement
if (stub != null) {
return stub.getDocString();
}
return PyPsiUtils.strValue(getDocStringExpression());
return DocStringUtil.getDocStringValue(this);
}
@Nullable
@Override
public StructuredDocString getStructuredDocString() {
return DocStringUtil.getStructuredDocString(this);
}
public String toString() {
@@ -19,6 +19,7 @@ import com.intellij.util.containers.MultiMap;
import com.intellij.util.indexing.IndexingDataKeys;
import com.jetbrains.python.*;
import com.jetbrains.python.codeInsight.controlflow.ControlFlowCache;
import com.jetbrains.python.documentation.DocStringUtil;
import com.jetbrains.python.inspections.PythonVisitorFilter;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.resolve.RatedResolveResult;
@@ -719,8 +720,22 @@ public class PyFileImpl extends PsiFileBase implements PyFile, PyExpression {
return myType;
}
@Nullable
@Override
public String getDocStringValue() {
return DocStringUtil.getDocStringValue(this);
}
@Nullable
@Override
public StructuredDocString getStructuredDocString() {
return DocStringUtil.getStructuredDocString(this);
}
@Nullable
@Override
public PyStringLiteralExpression getDocStringExpression() {
return PythonDocStringFinder.find(this);
return DocStringUtil.findDocStringExpression(this);
}
public void subtreeChanged() {
@@ -12,17 +12,18 @@ import com.intellij.psi.search.LocalSearchScope;
import com.intellij.psi.search.SearchScope;
import com.intellij.psi.stubs.IStubElementType;
import com.intellij.psi.stubs.StubElement;
import com.intellij.psi.util.CachedValueProvider;
import com.intellij.psi.util.CachedValuesManager;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.PlatformIcons;
import com.jetbrains.python.PyElementTypes;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.PyTokenTypes;
import com.jetbrains.python.PythonDocStringFinder;
import com.jetbrains.python.codeInsight.controlflow.ControlFlowCache;
import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil;
import com.jetbrains.python.documentation.StructuredDocString;
import com.jetbrains.python.documentation.DocStringUtil;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.resolve.QualifiedNameFinder;
import com.jetbrains.python.psi.stubs.PyClassStub;
@@ -58,6 +59,17 @@ public class PyFunctionImpl extends PyPresentableElementImpl<PyFunctionStub> imp
super(stub, nodeType);
}
private class CachedStructuredDocStringProvider implements CachedValueProvider<StructuredDocString> {
@Nullable
@Override
public Result<StructuredDocString> compute() {
final PyFunctionImpl f = PyFunctionImpl.this;
return Result.create(DocStringUtil.getStructuredDocString(f), f);
}
}
private CachedStructuredDocStringProvider myCachedStructuredDocStringProvider = new CachedStructuredDocStringProvider();
@Nullable
@Override
public String getName() {
@@ -305,8 +317,7 @@ public class PyFunctionImpl extends PyPresentableElementImpl<PyFunctionStub> imp
@Nullable
@Override
public PyType getReturnTypeFromDocString() {
final String value = getDocStringValue();
final String typeName = value != null ? extractReturnType(value) : null;
final String typeName = extractReturnType();
return typeName != null ? PyTypeParser.getTypeByName(this, typeName) : null;
}
@@ -364,8 +375,13 @@ public class PyFunctionImpl extends PyPresentableElementImpl<PyFunctionStub> imp
if (stub != null) {
return stub.getDocString();
}
final PyStringLiteralExpression docStringExpression = getDocStringExpression();
return PyPsiUtils.strValue(docStringExpression);
return DocStringUtil.getDocStringValue(this);
}
@Nullable
@Override
public StructuredDocString getStructuredDocString() {
return CachedValuesManager.getManager(getProject()).getCachedValue(this, myCachedStructuredDocStringProvider);
}
private boolean isGeneratedStub() {
@@ -383,7 +399,11 @@ public class PyFunctionImpl extends PyPresentableElementImpl<PyFunctionStub> imp
}
@Nullable
private static String extractReturnType(String docString) {
private String extractReturnType() {
final String docString = getDocStringValue();
if (docString == null) {
return null;
}
final List<String> lines = StringUtil.split(docString, "\n");
while (lines.size() > 0 && lines.get(0).trim().length() == 0) {
lines.remove(0);
@@ -395,9 +415,8 @@ public class PyFunctionImpl extends PyPresentableElementImpl<PyFunctionStub> imp
return firstLine.substring(pos + 2).trim();
}
}
StructuredDocString epydocString = StructuredDocString.parse(docString);
return epydocString != null ? epydocString.getReturnType() : null;
final StructuredDocString structuredDocString = getStructuredDocString();
return structuredDocString != null ? structuredDocString.getReturnType() : null;
}
private static class ReturnVisitor extends PyRecursiveElementVisitor {
@@ -456,7 +475,7 @@ public class PyFunctionImpl extends PyPresentableElementImpl<PyFunctionStub> imp
public PyStringLiteralExpression getDocStringExpression() {
final PyStatementList stmtList = getStatementList();
return stmtList != null ? PythonDocStringFinder.find(stmtList) : null;
return stmtList != null ? DocStringUtil.findDocStringExpression(stmtList) : null;
}
protected String getElementLocation() {
@@ -17,7 +17,6 @@ import com.jetbrains.python.PyNames;
import com.jetbrains.python.PyTokenTypes;
import com.jetbrains.python.PythonDialectsTokenSetProvider;
import com.jetbrains.python.codeInsight.stdlib.PyStdlibTypeProvider;
import com.jetbrains.python.documentation.StructuredDocString;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.resolve.PyResolveContext;
import com.jetbrains.python.psi.stubs.PyNamedParameterStub;
@@ -206,15 +205,15 @@ public class PyNamedParameterImpl extends PyPresentableElementImpl<PyNamedParame
}
}
String docString = func.getDocStringValue();
StructuredDocString docString = func.getStructuredDocString();
if (PyNames.INIT.equals(func.getName()) && docString == null) {
PyClass pyClass = func.getContainingClass();
if (pyClass != null)
docString = pyClass.getDocStringValue();
if (pyClass != null) {
docString = pyClass.getStructuredDocString();
}
}
StructuredDocString epydocString = StructuredDocString.parse(docString);
if (epydocString != null) {
String typeName = epydocString.getParamType(getName());
if (docString != null) {
String typeName = docString.getParamType(getName());
if (typeName != null) {
return PyTypeParser.getTypeByName(this, typeName);
}
@@ -24,7 +24,8 @@ import com.jetbrains.python.codeInsight.controlflow.ControlFlowCache;
import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
import com.jetbrains.python.codeInsight.dataflow.scope.Scope;
import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil;
import com.jetbrains.python.documentation.StructuredDocString;
import com.jetbrains.python.documentation.DocStringUtil;
import com.jetbrains.python.psi.StructuredDocString;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.impl.references.PyQualifiedReference;
import com.jetbrains.python.psi.impl.references.PyTargetReference;
@@ -247,9 +248,9 @@ public class PyTargetExpressionImpl extends PyPresentableElementImpl<PyTargetExp
@Nullable
public static PyType getTypeFromDocString(PyTargetExpressionImpl targetExpression) {
final String docString = PyPsiUtils.strValue(PyUtil.getAttributeDocString(targetExpression));
final String docString = PyPsiUtils.strValue(DocStringUtil.getAttributeDocString(targetExpression));
if (docString != null) {
StructuredDocString structuredDocString = StructuredDocString.parse(docString);
StructuredDocString structuredDocString = DocStringUtil.parse(docString);
if (structuredDocString != null) {
String typeName = structuredDocString.getParamType(null);
if (typeName == null) {
@@ -265,9 +266,9 @@ public class PyTargetExpressionImpl extends PyPresentableElementImpl<PyTargetExp
@Nullable
public static PyType getTypeFromComment(PyTargetExpressionImpl targetExpression) {
String docComment = PyUtil.getAttributeDocComment(targetExpression);
String docComment = DocStringUtil.getAttributeDocComment(targetExpression);
if (docComment != null) {
StructuredDocString structuredDocString = StructuredDocString.parse(docComment);
StructuredDocString structuredDocString = DocStringUtil.parse(docComment);
if (structuredDocString != null) {
String typeName = structuredDocString.getParamType(null);
if (typeName == null) {
@@ -4,7 +4,6 @@ import com.intellij.lang.annotation.Annotation;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.util.TextRange;
import com.jetbrains.python.PythonDocStringFinder;
import com.jetbrains.python.documentation.*;
import com.jetbrains.python.highlighting.PyHighlighter;
import com.jetbrains.python.psi.*;
@@ -16,17 +15,17 @@ public class DocStringAnnotator extends PyAnnotator {
@Override
public void visitPyFile(final PyFile node) {
annotateDocStringStmt(PythonDocStringFinder.find(node));
annotateDocStringStmt(DocStringUtil.findDocStringExpression(node));
}
@Override
public void visitPyFunction(final PyFunction node) {
annotateDocStringStmt(PythonDocStringFinder.find(node.getStatementList()));
annotateDocStringStmt(DocStringUtil.findDocStringExpression(node.getStatementList()));
}
@Override
public void visitPyClass(final PyClass node) {
annotateDocStringStmt(PythonDocStringFinder.find(node.getStatementList()));
annotateDocStringStmt(DocStringUtil.findDocStringExpression(node.getStatementList()));
}
@Override
@@ -45,7 +44,7 @@ public class DocStringAnnotator extends PyAnnotator {
@Override
public void visitPyExpressionStatement(PyExpressionStatement node) {
if (node.getExpression() instanceof PyStringLiteralExpression &&
EpydocUtil.isVariableDocString((PyStringLiteralExpression)node.getExpression())) {
DocStringUtil.isVariableDocString((PyStringLiteralExpression)node.getExpression())) {
annotateDocStringStmt((PyStringLiteralExpression)node.getExpression());
}
}
@@ -53,7 +53,7 @@ import java.util.regex.Pattern;
/**
* @author yole
*/
public class Pep8ExternalAnnotator extends ExternalAnnotator<Pep8ExternalAnnotator.State, Pep8ExternalAnnotator.State> {
public class Pep8ExternalAnnotator extends ExternalAnnotator<Pep8ExternalAnnotator.State, Pep8ExternalAnnotator.Results> {
private static final Logger LOG = Logger.getInstance(Pep8ExternalAnnotator.class);
public static class Problem {
@@ -76,7 +76,6 @@ public class Pep8ExternalAnnotator extends ExternalAnnotator<Pep8ExternalAnnotat
private final HighlightDisplayLevel level;
private final List<String> ignoredErrors;
private final int margin;
public final List<Problem> problems = new ArrayList<Problem>();
public State(String interpreterPath, String fileText, HighlightDisplayLevel level,
List<String> ignoredErrors, int margin) {
@@ -88,6 +87,15 @@ public class Pep8ExternalAnnotator extends ExternalAnnotator<Pep8ExternalAnnotat
}
}
public static class Results {
public final List<Problem> problems = new ArrayList<Problem>();
private final HighlightDisplayLevel level;
public Results(HighlightDisplayLevel level) {
this.level = level;
}
}
private boolean myReportedMissingInterpreter;
@Nullable
@@ -136,7 +144,7 @@ public class Pep8ExternalAnnotator extends ExternalAnnotator<Pep8ExternalAnnotat
@Nullable
@Override
public State doAnnotate(State collectedInfo) {
public Results doAnnotate(State collectedInfo) {
if (collectedInfo == null) return null;
final String pep8Path = PythonHelpersLocator.getHelperPath("pep8.py");
ArrayList<String> options = new ArrayList<String>();
@@ -151,23 +159,29 @@ public class Pep8ExternalAnnotator extends ExternalAnnotator<Pep8ExternalAnnotat
null,
5000,
collectedInfo.fileText.getBytes());
Results results = new Results(collectedInfo.level);
if (output.getStderrLines().isEmpty()) {
for (String line : output.getStdoutLines()) {
final Problem problem = parseProblem(line);
if (problem != null) {
collectedInfo.problems.add(problem);
results.problems.add(problem);
}
}
}
else if (((ApplicationInfoImpl) ApplicationInfo.getInstance()).isEAP()) {
LOG.info("Error running pep8.py: " + output.getStderr());
}
return collectedInfo;
return results;
}
@Override
public void apply(@NotNull PsiFile file, State annotationResult, @NotNull AnnotationHolder holder) {
if (annotationResult == null || !file.isValid()) return;
public void apply(@NotNull PsiFile file, Results annotationResult, @NotNull AnnotationHolder holder) {
if (annotationResult == null) return;
if (!file.isValid()) {
LOG.info("Trying to apply diagnostics to invalid PsiFile, skipped");
return;
}
final String text = file.getText();
Project project = file.getProject();
final Document document = PsiDocumentManager.getInstance(project).getDocument(file);
@@ -260,6 +274,9 @@ public class Pep8ExternalAnnotator extends ExternalAnnotator<Pep8ExternalAnnotat
int column = Integer.parseInt(m.group(2));
return new Problem(line, column, m.group(3), m.group(4));
}
if (((ApplicationInfoImpl) ApplicationInfo.getInstance()).isEAP()) {
LOG.info("Failed to parse problem line from pep8.py: " + s);
}
return null;
}
@@ -2,7 +2,7 @@ package com.jetbrains.python;
import com.intellij.testFramework.UsefulTestCase;
import com.jetbrains.python.documentation.EpydocString;
import com.jetbrains.python.documentation.Substring;
import com.jetbrains.python.toolbox.Substring;
import java.util.List;