code cleanup (names, nullability) + extracted function for building documentation for properties + removed duplicated PyUtil.isPythonIdentifier

This commit is contained in:
Ekaterina Tuzova
2015-09-06 17:51:46 +03:00
parent 7027085f86
commit f4c155e073
5 changed files with 97 additions and 96 deletions
@@ -19,7 +19,7 @@ import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.codeInsight.template.*;
import com.intellij.openapi.util.text.StringUtil;
import com.jetbrains.python.psi.PyUtil;
import com.jetbrains.python.PyNames;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
@@ -62,7 +62,7 @@ public class CollectionElementNameMacro extends Macro {
}
}
final String result = smartUnPluralize(param);
return result != null && PyUtil.isPythonIdentifier(result) ? new TextResult(result) : null;
return result != null && PyNames.isIdentifier(result) ? new TextResult(result) : null;
}
private static String smartUnPluralize(String param) {
@@ -73,32 +73,25 @@ class PyDocumentationBuilder {
myResult = wrapInTag("html", wrapInTag("body", myResult));
}
public String build() {
final ChainIterable<String> reassignCat = new ChainIterable<String>(); // sequence for reassignment info, etc
PsiElement followed = resolveToDocStringOwner(reassignCat);
// check if we got a property ref.
// if so, element is an accessor, and originalElement if an identifier
// TODO: use messages from resources!
private boolean buildForProperty(PsiElement followed, PsiElement outerElement) {
PyClass cls;
PsiElement outer = null;
boolean isProperty = false;
String accessorKind = "None";
final TypeEvalContext context = TypeEvalContext.userInitiated(myElement.getProject(), myElement.getContainingFile());
if (myOriginalElement != null) {
String elementName = myOriginalElement.getText();
if (PyUtil.isPythonIdentifier(elementName)) {
outer = myOriginalElement.getParent();
if (outer instanceof PyQualifiedExpression) {
PyExpression qualifier = ((PyQualifiedExpression)outer).getQualifier();
if (PyNames.isIdentifier(elementName)) {
if (outerElement instanceof PyQualifiedExpression) {
final PyExpression qualifier = ((PyQualifiedExpression)outerElement).getQualifier();
if (qualifier != null) {
PyType type = context.getType(qualifier);
final PyType type = context.getType(qualifier);
if (type instanceof PyClassType) {
cls = ((PyClassType)type).getPyClass();
Property property = cls.findProperty(elementName, true, null);
if (property != null) {
isProperty = true;
final AccessDirection dir = AccessDirection.of((PyElement)outer);
final AccessDirection dir = AccessDirection.of((PyElement)outerElement);
Maybe<PyCallable> accessor = property.getByDirection(dir);
myProlog
.addItem("property ").addWith(TagBold, $().addWith(TagCode, $(elementName)))
@@ -123,6 +116,7 @@ class PyDocumentationBuilder {
}
myBody.addItem(BR);
if (accessor.isDefined() && accessor.value() == null) followed = null;
String accessorKind;
if (dir == AccessDirection.READ) {
accessorKind = "Getter";
}
@@ -133,19 +127,44 @@ class PyDocumentationBuilder {
accessorKind = "Deleter";
}
if (followed != null) myEpilog.addWith(TagSmall, $(BR, BR, accessorKind, " of property")).addItem(BR);
if (!(followed instanceof PyDocStringOwner)) {
String accessorMessage;
if (followed != null) {
accessorMessage = "Declaration: ";
}
else {
accessorMessage = accessorKind + " is not defined.";
}
myBody.addWith(TagItalic, $(accessorMessage)).addItem(BR);
if (followed != null) myBody.addItem(combUp(PyUtil.getReadableRepr(followed, false)));
}
}
}
}
}
}
}
return isProperty;
}
public String build() {
final ChainIterable<String> reassignmentChain = new ChainIterable<String>();
final TypeEvalContext context = TypeEvalContext.userInitiated(myElement.getProject(), myElement.getContainingFile());
PsiElement outerElement = myOriginalElement != null ? myOriginalElement.getParent() : null;
PsiElement followed = resolveToDocStringOwner(reassignmentChain);
final boolean isProperty = buildForProperty(followed, outerElement);
if (myProlog.isEmpty() && !isProperty && !isAttribute()) {
myProlog.add(reassignCat);
myProlog.add(reassignmentChain);
}
// now followed may contain a doc string
if (followed instanceof PyDocStringOwner) {
PyClass cls;
String docString = null;
PyStringLiteralExpression docStringExpression = ((PyDocStringOwner)followed).getDocStringExpression();
if (docStringExpression != null) docString = docStringExpression.getStringValue();
@@ -181,26 +200,14 @@ class PyDocumentationBuilder {
addFormattedDocString(myElement, docString, myBody, myEpilog);
}
}
else if (isProperty) {
// if it was a normal accessor, ti would be a function, handled by previous branch
String accessorMessage;
if (followed != null) {
accessorMessage = "Declaration: ";
}
else {
accessorMessage = accessorKind + " is not defined.";
}
myBody.addWith(TagItalic, $(accessorMessage)).addItem(BR);
if (followed != null) myBody.addItem(combUp(PyUtil.getReadableRepr(followed, false)));
}
else if (isAttribute()) {
addAttributeDoc();
}
else if (followed instanceof PyNamedParameter) {
myBody.addItem(combUp("Parameter " + PyUtil.getReadableRepr(followed, false)));
boolean typeFromDocstringAdded = addTypeAndDescriptionFromDocstring((PyNamedParameter)followed);
if (outer instanceof PyExpression) {
PyType type = context.getType((PyExpression)outer);
if (outerElement instanceof PyExpression) {
PyType type = context.getType((PyExpression)outerElement);
if (type != null) {
String typeString = null;
if (type instanceof PyDynamicallyEvaluatedType) {
@@ -210,8 +217,8 @@ class PyDocumentationBuilder {
}
}
else {
if (outer.getReference() != null) {
PsiElement target = outer.getReference().resolve();
if (outerElement.getReference() != null) {
PsiElement target = outerElement.getReference().resolve();
if (target instanceof PyTargetExpression) {
final String targetName = ((PyTargetExpression)target).getName();
@@ -232,9 +239,9 @@ class PyDocumentationBuilder {
}
}
}
else if (followed != null && outer instanceof PyReferenceExpression) {
else if (followed != null && outerElement instanceof PyReferenceExpression) {
myBody.addItem(combUp("\nInferred type: "));
PythonDocumentationProvider.describeExpressionTypeWithLinks(myBody, (PyReferenceExpression)outer, context);
PythonDocumentationProvider.describeExpressionTypeWithLinks(myBody, (PyReferenceExpression)outerElement, context);
}
if (myBody.isEmpty() && myEpilog.isEmpty()) {
return null; // got nothing substantial to say!
@@ -249,12 +256,11 @@ class PyDocumentationBuilder {
}
@Nullable
private PsiElement resolveToDocStringOwner(ChainIterable<String> prolog_cat) {
private PsiElement resolveToDocStringOwner(ChainIterable<String> reassignmentChain) {
// here the ^Q target is already resolved; the resolved element may point to intermediate assignments
if (myElement instanceof PyTargetExpression) {
final String target_name = myElement.getText();
//prolog_cat.add(TagSmall.apply($("Assigned to ", element.getText(), BR)));
prolog_cat.addWith(TagSmall, $(PyBundle.message("QDOC.assigned.to.$0", target_name)).addItem(BR));
final String targetName = myElement.getText();
reassignmentChain.addWith(TagSmall, $(PyBundle.message("QDOC.assigned.to.$0", targetName)).addItem(BR));
final PyExpression assignedValue = ((PyTargetExpression)myElement).findAssignedValue();
if (assignedValue instanceof PyReferenceExpression) {
final PsiElement resolved = resolveWithoutImplicits((PyReferenceExpression)assignedValue);
@@ -265,20 +271,18 @@ class PyDocumentationBuilder {
return assignedValue;
}
if (myElement instanceof PyReferenceExpression) {
//prolog_cat.add(TagSmall.apply($("Assigned to ", element.getText(), BR)));
prolog_cat.addWith(TagSmall, $(PyBundle.message("QDOC.assigned.to.$0", myElement.getText())).addItem(BR));
reassignmentChain.addWith(TagSmall, $(PyBundle.message("QDOC.assigned.to.$0", myElement.getText())).addItem(BR));
return resolveWithoutImplicits((PyReferenceExpression)myElement);
}
// it may be a call to a standard wrapper
if (myElement instanceof PyCallExpression) {
final PyCallExpression call = (PyCallExpression)myElement;
Pair<String, PyFunction> wrap_info = PyCallExpressionHelper.interpretAsModifierWrappingCall(call, myOriginalElement);
if (wrap_info != null) {
String wrapper_name = wrap_info.getFirst();
PyFunction wrapped_func = wrap_info.getSecond();
//prolog_cat.addWith(TagSmall, $("Wrapped in ").addWith(TagCode, $(wrapper_name)).add(BR));
prolog_cat.addWith(TagSmall, $(PyBundle.message("QDOC.wrapped.in.$0", wrapper_name)).addItem(BR));
return wrapped_func;
Pair<String, PyFunction> wrapInfo = PyCallExpressionHelper.interpretAsModifierWrappingCall(call, myOriginalElement);
if (wrapInfo != null) {
String wrapperName = wrapInfo.getFirst();
PyFunction wrappedFunction = wrapInfo.getSecond();
reassignmentChain.addWith(TagSmall, $(PyBundle.message("QDOC.wrapped.in.$0", wrapperName)).addItem(BR));
return wrappedFunction;
}
}
return myElement;
@@ -1082,10 +1082,6 @@ public class PyUtil {
return name.length() > 4 && name.startsWith("__") && name.endsWith("__");
}
public static boolean isPythonIdentifier(@NotNull String name) {
return PyNames.isIdentifier(name);
}
/**
* Constructs new lookup element for completion of keyword argument with equals sign appended.
*
@@ -202,7 +202,7 @@ public class KeywordArgumentCompletionUtil {
if (Comparing.equal(myKwArgs.getName(), operandName) &&
argument instanceof PyStringLiteralExpression) {
String name = ((PyStringLiteralExpression)argument).getStringValue();
if (PyUtil.isPythonIdentifier(name)) {
if (PyNames.isIdentifier(name)) {
myRet.add(PyUtil.createNamedParameterLookup(name, argument.getProject()));
}
}
@@ -16,7 +16,7 @@
package com.jetbrains.python;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import com.intellij.testFramework.TestDataFile;
@@ -27,7 +27,6 @@ import com.jetbrains.python.fixtures.LightMarkedTestCase;
import com.jetbrains.python.fixtures.PyTestCase;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.impl.PythonLanguageLevelPusher;
import junit.framework.Assert;
import java.io.IOException;
import java.util.Map;
@@ -57,24 +56,24 @@ public class PyQuickDocTest extends LightMarkedTestCase {
}
private void checkByHTML(String text) {
Assert.assertNotNull(text);
assertNotNull(text);
checkByHTML(text, "/quickdoc/" + getTestName(false) + ".html");
}
private void checkByHTML(String text, @TestDataFile String filePath) {
final String fullPath = getTestDataPath() + filePath;
final VirtualFile vFile = PyTestCase.getVirtualFileByName(fullPath);
Assert.assertNotNull("file " + fullPath + " not found", vFile);
final VirtualFile virtualFile = PyTestCase.getVirtualFileByName(fullPath);
assertNotNull("file " + fullPath + " not found", virtualFile);
String loadedText;
try {
loadedText = VfsUtil.loadText(vFile);
loadedText = VfsUtilCore.loadText(virtualFile);
}
catch (IOException e) {
throw new RuntimeException(e);
}
String fileText = StringUtil.convertLineSeparators(loadedText, "\n");
Assert.assertEquals(fileText.trim(), text.trim());
assertEquals(fileText.trim(), text.trim());
}
@Override
@@ -84,34 +83,35 @@ public class PyQuickDocTest extends LightMarkedTestCase {
private void checkRefDocPair() {
Map<String, PsiElement> marks = loadTest();
Assert.assertEquals(2, marks.size());
final PsiElement original_elt = marks.get("<the_doc>");
PsiElement doc_elt = original_elt.getParent(); // ident -> expr
Assert.assertTrue(doc_elt instanceof PyStringLiteralExpression);
String doc_text = ((PyStringLiteralExpression)doc_elt).getStringValue();
Assert.assertNotNull(doc_text);
assertEquals(2, marks.size());
final PsiElement originalElement = marks.get("<the_doc>");
PsiElement docElement = originalElement.getParent(); // ident -> expr
assertTrue(docElement instanceof PyStringLiteralExpression);
String stringValue = ((PyStringLiteralExpression)docElement).getStringValue();
assertNotNull(stringValue);
PsiElement ref_elt = marks.get("<the_ref>").getParent(); // ident -> expr
final PyDocStringOwner doc_owner = (PyDocStringOwner)((PyReferenceExpression)ref_elt).getReference().resolve();
Assert.assertEquals(doc_elt, doc_owner.getDocStringExpression());
PsiElement referenceElement = marks.get("<the_ref>").getParent(); // ident -> expr
final PyDocStringOwner docOwner = (PyDocStringOwner)((PyReferenceExpression)referenceElement).getReference().resolve();
assertNotNull(docOwner);
assertEquals(docElement, docOwner.getDocStringExpression());
checkByHTML(myProvider.generateDoc(doc_owner, original_elt));
checkByHTML(myProvider.generateDoc(docOwner, originalElement));
}
private void checkHTMLOnly() {
Map<String, PsiElement> marks = loadTest();
final PsiElement original_elt = marks.get("<the_ref>");
PsiElement ref_elt = original_elt.getParent(); // ident -> expr
final PsiElement doc_owner = ((PyReferenceExpression)ref_elt).getReference().resolve();
checkByHTML(myProvider.generateDoc(doc_owner, original_elt));
final PsiElement originalElement = marks.get("<the_ref>");
PsiElement referenceElement = originalElement.getParent(); // ident -> expr
final PsiElement docOwner = ((PyReferenceExpression)referenceElement).getReference().resolve();
checkByHTML(myProvider.generateDoc(docOwner, originalElement));
}
private void checkHover() {
Map<String, PsiElement> marks = loadTest();
final PsiElement original_elt = marks.get("<the_ref>");
PsiElement ref_elt = original_elt.getParent(); // ident -> expr
final PsiElement docOwner = ((PyReferenceExpression)ref_elt).getReference().resolve();
checkByHTML(myProvider.getQuickNavigateInfo(docOwner, ref_elt));
final PsiElement originalElement = marks.get("<the_ref>");
PsiElement referenceElement = originalElement.getParent(); // ident -> expr
final PsiElement docOwner = ((PyReferenceExpression)referenceElement).getReference().resolve();
checkByHTML(myProvider.getQuickNavigateInfo(docOwner, referenceElement));
}
public void testDirectFunc() {
@@ -157,17 +157,18 @@ public class PyQuickDocTest extends LightMarkedTestCase {
public void testInheritedMethod() {
Map<String, PsiElement> marks = loadTest();
Assert.assertEquals(2, marks.size());
PsiElement doc_elt = marks.get("<the_doc>").getParent(); // ident -> expr
Assert.assertTrue(doc_elt instanceof PyStringLiteralExpression);
String doc_text = ((PyStringLiteralExpression)doc_elt).getStringValue();
Assert.assertNotNull(doc_text);
assertEquals(2, marks.size());
PsiElement docElement = marks.get("<the_doc>").getParent(); // ident -> expr
assertTrue(docElement instanceof PyStringLiteralExpression);
String docText = ((PyStringLiteralExpression)docElement).getStringValue();
assertNotNull(docText);
PsiElement ref_elt = marks.get("<the_ref>").getParent(); // ident -> expr
final PyDocStringOwner doc_owner = (PyDocStringOwner)((PyReferenceExpression)ref_elt).getReference().resolve();
Assert.assertNull(doc_owner.getDocStringExpression()); // no direct doc!
final PyDocStringOwner docOwner = (PyDocStringOwner)((PyReferenceExpression)ref_elt).getReference().resolve();
assertNotNull(docOwner);
assertNull(docOwner.getDocStringExpression()); // no direct doc!
checkByHTML(myProvider.generateDoc(doc_owner, null));
checkByHTML(myProvider.generateDoc(docOwner, null));
}
public void testPropNewGetter() {
@@ -177,10 +178,10 @@ public class PyQuickDocTest extends LightMarkedTestCase {
public void testPropNewSetter() {
PythonLanguageLevelPusher.setForcedLanguageLevel(myFixture.getProject(), LanguageLevel.PYTHON26);
Map<String, PsiElement> marks = loadTest();
PsiElement ref_elt = marks.get("<the_ref>");
PsiElement referenceElement = marks.get("<the_ref>");
try {
final PyDocStringOwner doc_owner = (PyDocStringOwner)((PyTargetExpression)(ref_elt.getParent())).getReference().resolve();
checkByHTML(myProvider.generateDoc(doc_owner, ref_elt));
final PyDocStringOwner docStringOwner = (PyDocStringOwner)((PyTargetExpression)(referenceElement.getParent())).getReference().resolve();
checkByHTML(myProvider.generateDoc(docStringOwner, referenceElement));
}
finally {
PythonLanguageLevelPusher.setForcedLanguageLevel(myFixture.getProject(), null);
@@ -190,10 +191,10 @@ public class PyQuickDocTest extends LightMarkedTestCase {
public void testPropNewDeleter() {
PythonLanguageLevelPusher.setForcedLanguageLevel(myFixture.getProject(), LanguageLevel.PYTHON26);
Map<String, PsiElement> marks = loadTest();
PsiElement ref_elt = marks.get("<the_ref>");
PsiElement referenceElement = marks.get("<the_ref>");
try {
final PyDocStringOwner doc_owner = (PyDocStringOwner)((PyReferenceExpression)(ref_elt.getParent())).getReference().resolve();
checkByHTML(myProvider.generateDoc(doc_owner, ref_elt));
final PyDocStringOwner docStringOwner = (PyDocStringOwner)((PyReferenceExpression)(referenceElement.getParent())).getReference().resolve();
checkByHTML(myProvider.generateDoc(docStringOwner, referenceElement));
}
finally {
PythonLanguageLevelPusher.setForcedLanguageLevel(myFixture.getProject(), null);
@@ -207,9 +208,9 @@ public class PyQuickDocTest extends LightMarkedTestCase {
public void testPropOldSetter() {
Map<String, PsiElement> marks = loadTest();
PsiElement ref_elt = marks.get("<the_ref>");
final PyDocStringOwner doc_owner = (PyDocStringOwner)((PyTargetExpression)(ref_elt.getParent())).getReference().resolve();
checkByHTML(myProvider.generateDoc(doc_owner, ref_elt));
PsiElement referenceElement = marks.get("<the_ref>");
final PyDocStringOwner docStringOwner = (PyDocStringOwner)((PyTargetExpression)(referenceElement.getParent())).getReference().resolve();
checkByHTML(myProvider.generateDoc(docStringOwner, referenceElement));
}
public void testPropOldDeleter() {