Merge remote-tracking branch 'origin/master'

This commit is contained in:
Ekaterina Tuzova
2013-08-15 13:48:38 +04:00
23 changed files with 175 additions and 53 deletions
@@ -10,7 +10,7 @@ import org.jetbrains.annotations.Nullable;
* @author dcheryasov
*/
public interface Callable extends PyTypedElement {
public interface Callable extends PyTypedElement, PyQualifiedNameOwner {
/**
* @return a list of parameters passed to this callable, possibly empty.
@@ -29,12 +29,4 @@ public interface Callable extends PyTypedElement {
*/
@Nullable
PyFunction asMethod();
/**
* Returns the qualified name of the function.
*
* @return the qualified name of the function, or null for a lambda expression.
*/
@Nullable
String getQualifiedName();
}
@@ -1,5 +1,7 @@
package com.jetbrains.python.psi;
import com.jetbrains.python.psi.types.PyType;
import com.jetbrains.python.psi.types.TypeEvalContext;
import com.jetbrains.python.toolbox.Maybe;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -51,4 +53,9 @@ public interface Property {
@NotNull
Maybe<Callable> getByDirection(@NotNull AccessDirection direction);
/**
* Get the return type of the property getter.
*/
@Nullable
PyType getType(@NotNull TypeEvalContext context);
}
@@ -21,7 +21,7 @@ import java.util.List;
* Represents a class declaration in source.
*/
public interface PyClass extends PsiNameIdentifierOwner, PyStatement, NameDefiner, PyDocStringOwner, StubBasedPsiElement<PyClassStub>,
ScopeOwner, PyDecoratable, PyTypedElement {
ScopeOwner, PyDecoratable, PyTypedElement, PyQualifiedNameOwner {
ArrayFactory<PyClass> ARRAY_FACTORY = new ArrayFactory<PyClass>() {
@NotNull
@Override
@@ -149,9 +149,6 @@ public interface PyClass extends PsiNameIdentifierOwner, PyStatement, NameDefine
boolean isSubclass(@NotNull String superClassQName);
@Nullable
String getQualifiedName();
/**
* Returns the list of names in the class' __slots__ attribute, or null if the class
* does not define such an attribute.
@@ -0,0 +1,19 @@
package com.jetbrains.python.psi;
import org.jetbrains.annotations.Nullable;
/**
* Base class for elements that have a qualified name (classes and functions).
*
* @author yole
*/
public interface PyQualifiedNameOwner extends PyElement {
/**
* Returns the qualified name of the element.
*
* @return the qualified name of the element, or null if the element doesn't have a name (for example, it is a lambda expression) or
* is contained inside an element that doesn't have a qualified name.
*/
@Nullable
String getQualifiedName();
}
@@ -3,7 +3,6 @@ package com.jetbrains.python.psi.types;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.StubBasedPsiElement;
import com.jetbrains.python.psi.PyTypedElement;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -158,8 +157,8 @@ public class TypeEvalContext {
}
}
public boolean maySwitchToAST(@NotNull StubBasedPsiElement element) {
return myAllowStubToAST || (element.getStub() == null && (myOrigin == null || myOrigin == element.getContainingFile()));
public boolean maySwitchToAST(@NotNull PsiElement element) {
return myAllowStubToAST || myOrigin == element.getContainingFile();
}
@Nullable
@@ -95,9 +95,6 @@ public class RestConfigurationEditor extends SettingsEditor<RestRunConfiguration
return myMainPanel;
}
protected void disposeEditor() {
}
public void setOpenInBrowserVisible(boolean visible) {
myOpenInBrowser.setVisible(visible);
}
@@ -680,6 +680,10 @@
<renamePsiElementProcessor implementation="com.jetbrains.django.refactoring.DjangoStaticPrefixRenameProcessor"/>
<usageTargetProvider implementation="com.jetbrains.django.refactoring.DjangoUsageTargetProvider"/>
<findUsagesHandlerFactory implementation="com.jetbrains.django.refactoring.DjangoFindUsagesHandlerFactory"/>
<referencesSearch implementation="com.jetbrains.django.refactoring.DjangoParameterReferenceSearcher"/>
<!-- App Engine -->
<facetType implementation="com.jetbrains.appengine.facet.AppEngineFacetType"/>
<framework.detector implementation="com.jetbrains.appengine.facet.AppEngineFrameworkDetector"/>
@@ -3,13 +3,10 @@ package com.jetbrains.python;
import com.intellij.navigation.GotoClassContributor;
import com.intellij.navigation.NavigationItem;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiFile;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.stubs.StubIndex;
import com.intellij.util.ArrayUtil;
import com.jetbrains.python.psi.PyClass;
import com.jetbrains.python.psi.PyFunction;
import com.jetbrains.python.psi.impl.PyPresentableElementImpl;
import com.jetbrains.python.psi.PyQualifiedNameOwner;
import com.jetbrains.python.psi.search.PyProjectScopeBuilder;
import com.jetbrains.python.psi.stubs.PyClassNameIndex;
import com.jetbrains.python.psi.stubs.PyFunctionNameIndex;
@@ -50,17 +47,8 @@ public class PyGotoSymbolContributor implements GotoClassContributor {
@Override
public String getQualifiedName(NavigationItem item) {
if (item instanceof PyClass) {
return ((PyClass) item).getQualifiedName();
}
if (item instanceof PyFunction) {
PyFunction function = (PyFunction) item;
final PyClass containingClass = function.getContainingClass();
if (containingClass != null) {
return containingClass.getQualifiedName() + "." + function.getName();
}
PsiFile file = function.getContainingFile();
return PyPresentableElementImpl.getPackageForFile(file) + "." + function.getName();
if (item instanceof PyQualifiedNameOwner) {
return ((PyQualifiedNameOwner) item).getQualifiedName();
}
return null;
}
@@ -42,20 +42,31 @@ public class PyClassNameCompletionContributor extends CompletionContributor {
if (parent instanceof PyReferenceExpression && ((PyReferenceExpression)parent).getQualifier() != null) {
return;
}
if (parent instanceof PyStringLiteralExpression) {
String prefix = parent.getText().substring(0, parameters.getOffset() - parent.getTextRange().getStartOffset());
if (prefix.contains(".")) {
return;
}
}
final FileViewProvider provider = element.getContainingFile().getViewProvider();
if (provider instanceof MultiplePsiFilesPerDocumentFileViewProvider) return;
if (PsiTreeUtil.getParentOfType(element, PyImportStatementBase.class) != null) {
return;
}
final PsiFile originalFile = parameters.getOriginalFile();
addVariantsFromIndex(result, originalFile, PyClassNameIndex.KEY, IMPORTING_INSERT_HANDLER, Conditions.<PyClass>alwaysTrue());
addVariantsFromIndex(result, originalFile, PyFunctionNameIndex.KEY, FUNCTION_INSERT_HANDLER, IS_TOPLEVEL);
addVariantsFromIndex(result, originalFile, PyVariableNameIndex.KEY, IMPORTING_INSERT_HANDLER, IS_TOPLEVEL);
addVariantsFromModules(result, originalFile);
addVariantsFromIndex(result, originalFile, PyClassNameIndex.KEY,
parent instanceof PyStringLiteralExpression ? STRING_LITERAL_INSERT_HANDLER : IMPORTING_INSERT_HANDLER,
Conditions.<PyClass>alwaysTrue());
addVariantsFromIndex(result, originalFile, PyFunctionNameIndex.KEY,
parent instanceof PyStringLiteralExpression ? STRING_LITERAL_INSERT_HANDLER : FUNCTION_INSERT_HANDLER, IS_TOPLEVEL);
addVariantsFromIndex(result, originalFile, PyVariableNameIndex.KEY,
parent instanceof PyStringLiteralExpression ? STRING_LITERAL_INSERT_HANDLER : IMPORTING_INSERT_HANDLER,
IS_TOPLEVEL);
addVariantsFromModules(result, originalFile, parent instanceof PyStringLiteralExpression);
}
}
private static void addVariantsFromModules(CompletionResultSet result, PsiFile targetFile) {
private static void addVariantsFromModules(CompletionResultSet result, PsiFile targetFile, boolean inStringLiteral) {
Collection<VirtualFile> files = FileTypeIndex.getFiles(PythonFileType.INSTANCE, PyProjectScopeBuilder.excludeSdkTestsScope(targetFile));
for (VirtualFile file : files) {
PsiFile pyFile = targetFile.getManager().findFile(file);
@@ -64,7 +75,7 @@ public class PyClassNameCompletionContributor extends CompletionContributor {
if (PythonReferenceImporter.isImportableModule(targetFile, importable)) {
LookupElementBuilder element = PyModuleType.buildFileLookupElement(importable, null);
if (element != null) {
result.addElement(element.withInsertHandler(IMPORTING_INSERT_HANDLER));
result.addElement(element.withInsertHandler(inStringLiteral ? STRING_LITERAL_INSERT_HANDLER : IMPORTING_INSERT_HANDLER));
}
}
}
@@ -113,6 +124,21 @@ public class PyClassNameCompletionContributor extends CompletionContributor {
}
};
private static final InsertHandler<LookupElement> STRING_LITERAL_INSERT_HANDLER = new InsertHandler<LookupElement>() {
@Override
public void handleInsert(InsertionContext context, LookupElement item) {
PsiElement element = item.getPsiElement();
if (element instanceof PyQualifiedNameOwner) {
String qName = ((PyQualifiedNameOwner) element).getQualifiedName();
String name = ((PyQualifiedNameOwner) element).getName();
if (qName != null && name != null) {
String qNamePrefix = qName.substring(0, qName.length()-name.length());
context.getDocument().insertString(context.getStartOffset(), qNamePrefix);
}
}
}
};
private static void addImportForLookupElement(final InsertionContext context, final LookupElement item, final int tailOffset) {
PsiDocumentManager manager = PsiDocumentManager.getInstance(context.getProject());
Document document = manager.getDocument(context.getFile());
@@ -66,7 +66,8 @@ public class PyBlockEvaluator {
if (currentValue instanceof Map) {
Object mapKey = prepareEvaluator().evaluate(indexExpression);
if (mapKey != null) {
((Map)currentValue).put(mapKey, prepareEvaluator().evaluate(node.getAssignedValue()));
Object value = myEvaluateCollectionItems ? prepareEvaluator().evaluate(node.getAssignedValue()) : node.getAssignedValue();
((Map)currentValue).put(mapKey, value);
}
}
}
@@ -691,6 +691,24 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> implement
mySite = site;
}
@NotNull
@Override
public Maybe<Callable> getGetter() {
return filterNonStubExpression(myGetter);
}
@NotNull
@Override
public Maybe<Callable> getSetter() {
return filterNonStubExpression(mySetter);
}
@NotNull
@Override
public Maybe<Callable> getDeleter() {
return filterNonStubExpression(myDeleter);
}
public String getName() {
return myName;
}
@@ -704,15 +722,29 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> implement
public Maybe<Callable> getByDirection(@NotNull AccessDirection direction) {
switch (direction) {
case READ:
return myGetter;
return getGetter();
case WRITE:
return mySetter;
return getSetter();
case DELETE:
return myDeleter;
return getDeleter();
}
throw new IllegalArgumentException("Unknown direction " + PyUtil.nvl(direction));
}
@Nullable
@Override
public PyType getType(@NotNull TypeEvalContext context) {
final Callable callable = myGetter.valueOrNull();
if (callable != null) {
// Ignore return types of non stub-based elements if we are not allowed to use AST
if (!(callable instanceof StubBasedPsiElement) && !context.maySwitchToAST(callable)) {
return null;
}
return callable.getReturnType(context, null);
}
return null;
}
@NotNull
@Override
protected Maybe<Callable> translate(@Nullable PyExpression expr) {
@@ -733,6 +765,17 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> implement
return NONE;
}
@NotNull
private static Maybe<Callable> filterNonStubExpression(@NotNull Maybe<Callable> maybeCallable) {
final Callable callable = maybeCallable.valueOrNull();
if (callable != null) {
if (!(callable instanceof StubBasedPsiElement)) {
return UNKNOWN_CALL;
}
}
return maybeCallable;
}
public String toString() {
return "property(" + myGetter + ", " + mySetter + ", " + myDeleter + ", " + myDoc + ")";
}
@@ -27,7 +27,6 @@ import com.jetbrains.python.psi.impl.references.PyReferenceImpl;
import com.jetbrains.python.psi.resolve.*;
import com.jetbrains.python.psi.types.*;
import com.jetbrains.python.refactoring.PyDefUseUtil;
import com.jetbrains.python.toolbox.Maybe;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -269,10 +268,13 @@ public class PyReferenceExpressionImpl extends PyElementImpl implements PyRefere
if (classType.isDefinition()) {
return Ref.<PyType>create(PyBuiltinCache.getInstance(pyClass).getObjectType(PyNames.PROPERTY));
}
final Maybe<Callable> accessor = property.getByDirection(AccessDirection.of(this));
final Callable callable = accessor.valueOrNull();
final PyType type = (callable != null) ? callable.getReturnType(context, this) : null;
return Ref.create(type);
if (AccessDirection.of(this) == AccessDirection.READ) {
final PyType type = property.getType(context);
if (type != null) {
return Ref.create(type);
}
}
return Ref.create();
}
}
else if (qualifierType instanceof PyUnionType) {
@@ -1,8 +1,10 @@
package com.jetbrains.python.psi.impl;
import com.intellij.icons.AllIcons;
import com.intellij.lang.ASTNode;
import com.intellij.lang.Language;
import com.intellij.lang.injection.InjectedLanguageManager;
import com.intellij.navigation.ItemPresentation;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.TextRange;
@@ -24,6 +26,7 @@ import org.intellij.lang.regexp.psi.RegExpGroup;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -304,6 +307,29 @@ public class PyStringLiteralExpressionImpl extends PyElementImpl implements PySt
return ReferenceProvidersRegistry.getReferencesFromProviders(this, PsiReferenceService.Hints.NO_HINTS);
}
@Override
public ItemPresentation getPresentation() {
return new ItemPresentation() {
@Nullable
@Override
public String getPresentableText() {
return getStringValue();
}
@Nullable
@Override
public String getLocationString() {
return "(" + PyPresentableElementImpl.getPackageForFile(getContainingFile()) + ")";
}
@Nullable
@Override
public Icon getIcon(boolean unused) {
return AllIcons.Nodes.Variable;
}
};
}
public PsiLanguageInjectionHost updateText(@NotNull String text) {
// TODO is this the correct implementation? most likely not
ASTNode valueNode = getNode().getFirstChildNode();
@@ -92,9 +92,6 @@ public class PyTestConfigurationEditor extends SettingsEditor<PyTestRunConfigura
return myRootPanel;
}
protected void disposeEditor() {
}
@Override
public JComponent getAnchor() {
return anchor;
@@ -0,0 +1,3 @@
class Shazam:
pass
@@ -0,0 +1 @@
x = 'mypackage.Shazam'
@@ -0,0 +1 @@
x = 'Shaz<caret>'
@@ -0,0 +1,3 @@
class Shazam:
pass
@@ -0,0 +1 @@
x = 'mypackage.<caret>'
+1 -1
View File
@@ -19,6 +19,6 @@ class A(object):
v1 = property(getter, setter)
v2 = property(fset=setter, fdel=deleter, fget=getter, doc="doc of v2")
v3 = property(lambda self: self._v, None, (deleter))
v3 = property(lambda self: 0, None, (deleter))
v4 = otherworldly # NOTE: not supported yet
v5 = property(v5getter, v5setter)
@@ -68,6 +68,13 @@ public class PyBlockEvaluatorTest extends PyTestCase {
assertEquals("c", map.get("b"));
}
public void testDictAssignNoEvaluate() {
PyBlockEvaluator eval = doEvaluate("a={}\na['b']='c'", true);
Map map = (Map) eval.getValue("a");
assertEquals(1, map.size());
assertTrue(map.get("b") instanceof PyStringLiteralExpression);
}
public void testDictUpdate() {
PyBlockEvaluator eval = doEvaluate("a={}\na.update({'b': 'c'})");
Map map = (Map) eval.getValue("a");
@@ -42,6 +42,9 @@ public class PyClassNameCompletionTest extends PyTestCase {
doTestWithoutFromImport();
}
public void testStringLiteral() { // PY-10526
doTest();
}
private void doTestWithoutFromImport() {
final PyCodeInsightSettings settings = PyCodeInsightSettings.getInstance();
boolean oldValue = settings.PREFER_FROM_IMPORT;
@@ -2,6 +2,8 @@ package com.jetbrains.python;
import com.jetbrains.python.fixtures.PyTestCase;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.types.PyType;
import com.jetbrains.python.psi.types.TypeEvalContext;
import com.jetbrains.python.toolbox.Maybe;
public class PyClassicPropertyTest extends PyTestCase {
@@ -78,6 +80,9 @@ public class PyClassicPropertyTest extends PyTestCase {
accessor = p.getGetter();
assertFalse(accessor.isDefined());
final PyType codeInsightType = p.getType(TypeEvalContext.codeInsightFallback());
assertNull(codeInsightType);
accessor = p.getSetter();
assertTrue(accessor.isDefined());
assertNull(accessor.value());