PY-9795, PY-4717 References in section-based docstrings

This commit is contained in:
Mikhail Golubev
2015-09-02 14:35:17 +03:00
parent 973dca8315
commit 8619d3d4a5
5 changed files with 96 additions and 54 deletions
@@ -41,13 +41,15 @@ import java.util.Set;
* @author yole
*/
public class DocStringParameterReference extends PsiReferenceBase<PyStringLiteralExpression> implements PsiReferenceEx {
private final TagBasedDocString.ReferenceType myType;
private final ReferenceType myType;
public DocStringParameterReference(PyStringLiteralExpression element, TextRange range, TagBasedDocString.ReferenceType refType) {
public DocStringParameterReference(PyStringLiteralExpression element, TextRange range, ReferenceType refType) {
super(element, range);
myType = refType;
}
public enum ReferenceType {PARAMETER, PARAMETER_TYPE, KEYWORD, VARIABLE, CLASS_VARIABLE, INSTANCE_VARIABLE}
@Override
public PsiElement resolve() {
PyDocStringOwner owner = PsiTreeUtil.getParentOfType(getElement(), PyDocStringOwner.class);
@@ -58,20 +60,20 @@ public class DocStringParameterReference extends PsiReferenceBase<PyStringLitera
final PyFunction init = ((PyClass)owner).findMethodByName(PyNames.INIT, false);
if (init != null) {
PsiElement element = resolveParameter(init);
if (element == null && (myType.equals(TagBasedDocString.ReferenceType.CLASS_VARIABLE) ||
myType.equals(TagBasedDocString.ReferenceType.PARAMETER_TYPE)))
if (element == null && (myType.equals(ReferenceType.CLASS_VARIABLE) ||
myType.equals(ReferenceType.PARAMETER_TYPE)))
element = resolveClassVariable((PyClass)owner);
if (element == null && (myType.equals(TagBasedDocString.ReferenceType.INSTANCE_VARIABLE) ||
myType.equals(TagBasedDocString.ReferenceType.PARAMETER_TYPE)))
if (element == null && (myType.equals(ReferenceType.INSTANCE_VARIABLE) ||
myType.equals(ReferenceType.PARAMETER_TYPE)))
element = resolveInstanceVariable((PyClass)owner);
return element;
}
else {
if (myType.equals(TagBasedDocString.ReferenceType.CLASS_VARIABLE) ||
myType.equals(TagBasedDocString.ReferenceType.PARAMETER_TYPE))
if (myType.equals(ReferenceType.CLASS_VARIABLE) ||
myType.equals(ReferenceType.PARAMETER_TYPE))
return resolveClassVariable((PyClass)owner);
if (myType.equals(TagBasedDocString.ReferenceType.INSTANCE_VARIABLE) ||
myType.equals(TagBasedDocString.ReferenceType.PARAMETER_TYPE))
if (myType.equals(ReferenceType.INSTANCE_VARIABLE) ||
myType.equals(ReferenceType.PARAMETER_TYPE))
return resolveInstanceVariable((PyClass)owner);
}
}
@@ -141,8 +143,8 @@ public class DocStringParameterReference extends PsiReferenceBase<PyStringLitera
}
return ArrayUtil.EMPTY_OBJECT_ARRAY;
}
public TagBasedDocString.ReferenceType getType() {
public ReferenceType getType() {
return myType;
}
@@ -22,6 +22,8 @@ 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.documentation.DocStringParameterReference.ReferenceType;
import com.jetbrains.python.documentation.SectionBasedDocString.SectionField;
import com.jetbrains.python.psi.PyDocStringOwner;
import com.jetbrains.python.psi.PyImportElement;
import com.jetbrains.python.psi.PyStringLiteralExpression;
@@ -62,24 +64,31 @@ public class DocStringReferenceProvider extends PsiReferenceProvider {
final TagBasedDocString taggedDocString = (TagBasedDocString)docString;
result.addAll(referencesFromNames(expr, offset, docString,
taggedDocString.getTagArguments(TagBasedDocString.PARAM_TAGS),
TagBasedDocString.ReferenceType.PARAMETER));
ReferenceType.PARAMETER));
result.addAll(referencesFromNames(expr, offset, docString,
taggedDocString.getTagArguments(TagBasedDocString.PARAM_TYPE_TAGS),
TagBasedDocString.ReferenceType.PARAMETER_TYPE));
ReferenceType.PARAMETER_TYPE));
result.addAll(referencesFromNames(expr, offset, docString,
docString.getKeywordArgumentSubstrings(), TagBasedDocString.ReferenceType.KEYWORD));
docString.getKeywordArgumentSubstrings(), ReferenceType.KEYWORD));
result.addAll(referencesFromNames(expr, offset, docString,
taggedDocString.getTagArguments("var"),
TagBasedDocString.ReferenceType.VARIABLE));
ReferenceType.VARIABLE));
result.addAll(referencesFromNames(expr, offset, docString,
taggedDocString.getTagArguments("cvar"),
TagBasedDocString.ReferenceType.CLASS_VARIABLE));
ReferenceType.CLASS_VARIABLE));
result.addAll(referencesFromNames(expr, offset, docString,
taggedDocString.getTagArguments("ivar"),
TagBasedDocString.ReferenceType.INSTANCE_VARIABLE));
ReferenceType.INSTANCE_VARIABLE));
result.addAll(returnTypes(element, docString, offset));
}
else if (docString instanceof SectionBasedDocString) {
final SectionBasedDocString sectioned = (SectionBasedDocString)docString;
result.addAll(referencesFromFields(expr, offset, sectioned.getParameterFields(), ReferenceType.PARAMETER));
result.addAll(referencesFromFields(expr, offset, sectioned.getKeywordArgumentFields(), ReferenceType.KEYWORD));
result.addAll(referencesFromFields(expr, offset, sectioned.getAttributeFields(), ReferenceType.INSTANCE_VARIABLE));
result.addAll(referencesFromFields(expr, offset, sectioned.getReturnFields(), null));
}
return result.toArray(new PsiReference[result.size()]);
}
}
@@ -97,11 +106,11 @@ public class DocStringReferenceProvider extends PsiReferenceProvider {
}
return result;
}
private static List<PsiReference> referencesFromNames(PyStringLiteralExpression element,
private static List<PsiReference> referencesFromNames(@NotNull PyStringLiteralExpression element,
int offset,
StructuredDocString docString,
List<Substring> paramNames,
TagBasedDocString.ReferenceType refType) {
@NotNull StructuredDocString docString,
@NotNull List<Substring> paramNames,
@NotNull ReferenceType refType) {
List<PsiReference> result = new ArrayList<PsiReference>();
for (Substring name : paramNames) {
final String s = name.toString();
@@ -109,7 +118,7 @@ public class DocStringReferenceProvider extends PsiReferenceProvider {
final TextRange range = name.getTextRange().shiftRight(offset);
result.add(new DocStringParameterReference(element, range, refType));
}
if (refType.equals(TagBasedDocString.ReferenceType.PARAMETER_TYPE)) {
if (refType.equals(ReferenceType.PARAMETER_TYPE)) {
final Substring type = docString.getParamTypeSubstring(s);
if (type != null) {
result.addAll(parseTypeReferences(element, type, offset));
@@ -119,7 +128,28 @@ public class DocStringReferenceProvider extends PsiReferenceProvider {
return result;
}
private static List<PsiReference> parseTypeReferences(PsiElement anchor, Substring s, int offset) {
@NotNull
private static List<PsiReference> referencesFromFields(@NotNull PyStringLiteralExpression element,
int offset,
@NotNull List<SectionField> fields,
@Nullable ReferenceType nameRefType) {
final List<PsiReference> result = new ArrayList<PsiReference>();
for (SectionField field : fields) {
final Substring nameSub = field.getNameAsSubstring();
if (nameRefType != null && nameSub != null && !nameSub.isEmpty()) {
final TextRange range = nameSub.getTextRange().shiftRight(offset);
result.add(new DocStringParameterReference(element, range, nameRefType));
}
final Substring typeSub = field.getTypeAsSubstring();
if (typeSub != null && !typeSub.isEmpty()) {
result.addAll(parseTypeReferences(element, typeSub, offset));
}
}
return result;
}
@NotNull
private static List<PsiReference> parseTypeReferences(@NotNull PsiElement anchor, @NotNull Substring s, int offset) {
final List<PsiReference> result = new ArrayList<PsiReference>();
final PyTypeParser.ParseResult parseResult = PyTypeParser.parse(anchor, s.toString());
final Map<TextRange, ? extends PyType> types = parseResult.getTypes();
@@ -15,7 +15,6 @@
*/
package com.jetbrains.python.documentation;
import com.google.common.base.Preconditions;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
@@ -29,7 +28,6 @@ import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiWhiteSpace;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.ArrayUtil;
import com.jetbrains.python.PyTokenTypes;
import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.impl.PyPsiUtils;
@@ -80,7 +78,7 @@ public class DocStringUtil {
@NotNull
public static StructuredDocString parseDocString(@NotNull DocStringFormat format, @NotNull ASTNode node) {
Preconditions.checkArgument(node.getElementType() == PyTokenTypes.DOCSTRING);
//Preconditions.checkArgument(node.getElementType() == PyTokenTypes.DOCSTRING);
return parseDocString(format, node.getText());
}
@@ -400,14 +400,10 @@ public abstract class SectionBasedDocString extends DocStringLineParser implemen
}
@NotNull
private List<SectionField> getParameterFields() {
public List<SectionField> getParameterFields() {
final List<SectionField> result = new ArrayList<SectionField>();
for (Section section : mySections) {
if (section.getNormalizedTitle().equals(PARAMETERS_SECTION)) {
for (SectionField field : section.getFields()) {
result.add(field);
}
}
for (Section section : getSectionsWithNormalizedTitle(PARAMETERS_SECTION)) {
result.addAll(section.getFields());
}
return result;
}
@@ -445,14 +441,10 @@ public abstract class SectionBasedDocString extends DocStringLineParser implemen
}
@NotNull
private List<SectionField> getKeywordArgumentFields() {
public List<SectionField> getKeywordArgumentFields() {
final List<SectionField> result = new ArrayList<SectionField>();
for (Section section : mySections) {
if (section.getNormalizedTitle().equals(KEYWORD_ARGUMENTS_SECTION)) {
for (SectionField field : section.getFields()) {
result.add(field);
}
}
for (Section section : getSectionsWithNormalizedTitle(KEYWORD_ARGUMENTS_SECTION)) {
result.addAll(section.getFields());
}
return result;
}
@@ -488,14 +480,19 @@ public abstract class SectionBasedDocString extends DocStringLineParser implemen
return field != null ? field.getDescription() : null;
}
@NotNull
public List<SectionField> getReturnFields() {
final List<SectionField> result = new ArrayList<SectionField>();
for (Section section : getSectionsWithNormalizedTitle(RETURNS_SECTION)) {
result.addAll(section.getFields());
}
return result;
}
@Nullable
private SectionField getFirstReturnField() {
for (Section section : mySections) {
if (section.getNormalizedTitle().equals(RETURNS_SECTION) && !section.getFields().isEmpty()) {
return section.getFields().get(0);
}
}
return null;
return ContainerUtil.getFirstItem(getReturnFields());
}
@Override
@@ -521,12 +518,10 @@ public abstract class SectionBasedDocString extends DocStringLineParser implemen
}
@NotNull
private List<SectionField> getExceptionFields() {
public List<SectionField> getExceptionFields() {
final List<SectionField> result = new ArrayList<SectionField>();
for (Section section : mySections) {
if (section.getNormalizedTitle().equals(RAISES_SECTION)) {
result.addAll(section.getFields());
}
for (Section section : getSectionsWithNormalizedTitle(RAISES_SECTION)) {
result.addAll(section.getFields());
}
return result;
}
@@ -541,6 +536,25 @@ public abstract class SectionBasedDocString extends DocStringLineParser implemen
});
}
@NotNull
public List<SectionField> getAttributeFields() {
final List<SectionField> result = new ArrayList<SectionField>();
for (Section section : getSectionsWithNormalizedTitle(ATTRIBUTES_SECTION)) {
result.addAll(section.getFields());
}
return result;
}
@NotNull
private List<Section> getSectionsWithNormalizedTitle(@NotNull final String title) {
return ContainerUtil.mapNotNull(mySections, new Function<Section, Section>() {
@Override
public Section fun(Section section) {
return section.getNormalizedTitle().equals(title) ? section : null;
}
});
}
@Nullable
@Override
public String getAttributeDescription() {
@@ -52,8 +52,6 @@ public abstract class TagBasedDocString extends DocStringLineParser implements S
@NotNull
private final String myTagPrefix;
public enum ReferenceType {PARAMETER, PARAMETER_TYPE, KEYWORD, VARIABLE, CLASS_VARIABLE, INSTANCE_VARIABLE}
public static String TYPE = "type";
protected TagBasedDocString(@NotNull Substring docStringText, @NotNull String tagPrefix) {