*/
package com.jetbrains.python.codeInsight;
+import com.google.common.base.Preconditions;
import com.intellij.extapi.psi.ASTWrapperPsiElement;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.util.Key;
import com.jetbrains.python.psi.PyFunction;
import com.jetbrains.python.psi.PyPsiFacade;
import com.jetbrains.python.psi.PyTypedElement;
+import com.jetbrains.python.psi.types.PyClassType;
import com.jetbrains.python.psi.types.PyType;
import com.jetbrains.python.psi.types.TypeEvalContext;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
/**
+ * Note: if you use {@link #myTypeName} to override real field, be sure to use
+ * {@link com.jetbrains.python.psi.types.PyOverridingClassMembersProvider}
+ *
* @author Dennis.Ushakov
*/
public class PyCustomMember extends UserDataHolderBase {
* Force resolving to {@link MyInstanceElement} even if element is function
*/
private boolean myAlwaysResolveToCustomElement;
+ private Icon myIcon = AllIcons.Nodes.Method;
+ private PyCustomMemberTypeInfo<?> myCustomTypeInfo;
public PyCustomMember(@NotNull final String name, @Nullable final String type, final boolean resolveToInstance) {
myName = name;
if (myTarget != null) {
return myTarget.getIcon(0);
}
- return AllIcons.Nodes.Method;
+ return myIcon;
}
@Nullable
return ((MyInstanceElement)element).getThis().equals(this);
}
+ /**
+ * @param icon icon to use (will be used method icon otherwise)
+ */
+ public PyCustomMember withIcon(@NotNull final Icon icon) {
+ myIcon = icon;
+ return this;
+ }
+
+ /**
+ * Adds custom info to type if class has {@link #myTypeName} set.
+ * Info could be later obtained by key.
+ *
+ * @param customInfo custom info to add
+ */
+ public PyCustomMember withCustomTypeInfo(@NotNull final PyCustomMemberTypeInfo<?> customInfo) {
+ Preconditions.checkState(myTypeName != null, "Cant add custom type info if no type provided");
+ myCustomTypeInfo = customInfo;
+ return this;
+ }
+
private class MyInstanceElement extends ASTWrapperPsiElement implements PyTypedElement {
private final PyClass myClass;
private final PsiElement myContext;
return myTypeCallback.fun(myContext);
}
else if (myClass != null) {
- return PyPsiFacade.getInstance(getProject()).createClassType(myClass, !myResolveToInstance);
+ final PyClassType type = PyPsiFacade.getInstance(getProject()).createClassType(myClass, !myResolveToInstance);
+ if (myCustomTypeInfo != null) {
+ myCustomTypeInfo.fill(type);
+ }
+ return type;
}
return null;
}
--- /dev/null
+/*
+ * Copyright 2000-2016 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.jetbrains.python.codeInsight;
+
+import com.intellij.openapi.util.Key;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.UserDataHolder;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Info to add to type of custom member.
+ *
+ * @author Ilya.Kazakevich
+ */
+public class PyCustomMemberTypeInfo<K> {
+ @NotNull
+ private final Map<Key<K>, K> myCustomInfo = new HashMap<Key<K>, K>();
+
+ public PyCustomMemberTypeInfo(@NotNull final Key<K> key, @NotNull final K value) {
+ this(Collections.singleton(Pair.create(key, value)));
+ }
+
+ public PyCustomMemberTypeInfo(@NotNull final Iterable<Pair<Key<K>, K>> customInfo) {
+ for (final Pair<Key<K>, K> pair : customInfo) {
+ myCustomInfo.put(pair.first, pair.second);
+ }
+ }
+
+ public PyCustomMemberTypeInfo(@NotNull final Map<Key<K>, K> customInfo) {
+ myCustomInfo.putAll(customInfo);
+ }
+
+ void fill(@NotNull final UserDataHolder typeToFill) {
+ for (final Map.Entry<Key<K>, K> entry : myCustomInfo.entrySet()) {
+ typeToFill.putUserData(entry.getKey(), entry.getValue());
+ }
+ }
+}
import com.intellij.util.Function;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.resolve.PyResolveContext;
+import com.jetbrains.python.psi.types.TypeEvalContext;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
* Same as {@link #isName(PyElement, FQNamesProvider...)} for call expr, but first checks name.
* Aliases not supported, but much lighter that way
*
- * @param call expr
- * @param function names to check
+ * @param call expr
+ * @param function names to check
* @return true if callee is correct
*/
public static boolean isCalleeShortCut(@NotNull final PyCallExpression call,
/**
* Checks if some string contains last component one of name
- * @param text test to check
+ *
+ * @param text test to check
* @param names
*/
public static boolean isContainsName(@NotNull final String text, @NotNull final FQNamesProvider names) {
}
return false;
}
+
/**
* Checks if some file contains last component one of name
- * @param file file to check
+ *
+ * @param file file to check
* @param names
*/
public static boolean isContainsName(@NotNull final PsiFile file, @NotNull final FQNamesProvider names) {
return isContainsName(file.getText(), names);
}
+ /**
+ * Check if class has parent with some name
+ * @param child class to check
+ */
+ public static boolean isSubclass(@NotNull final PyClass child,
+ @NotNull final FQNamesProvider parentName,
+ @NotNull final TypeEvalContext context) {
+ for (final String nameToCheck : parentName.getNames()) {
+ if (child.isSubclass(nameToCheck, context)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* Looks for call of some function
*/
}
@Nullable
- private static PyType getIterationType(@Nullable PyType iterableType, @Nullable PyExpression source, @NotNull PsiElement anchor,
+ public static PyType getIterationType(@Nullable PyType iterableType, @Nullable PyExpression source, @NotNull PsiElement anchor,
@NotNull TypeEvalContext context) {
if (iterableType instanceof PyTupleType) {
final PyTupleType tupleType = (PyTupleType)iterableType;
}
@Nullable
- private static PyType getContextSensitiveType(@NotNull PyFunction function, @NotNull TypeEvalContext context,
+ public static PyType getContextSensitiveType(@NotNull PyFunction function, @NotNull TypeEvalContext context,
@Nullable PyExpression source) {
return function.getCallType(source, Collections.<PyExpression, PyNamedParameter>emptyMap(), context);
}
myElementTypes = elementTypes;
}
+
+ @Nullable
+ @Override
+ public PyType getReturnType(@NotNull final TypeEvalContext context) {
+ if (isDefinition()) {
+ return new PyCollectionTypeImpl(getPyClass(), false, myElementTypes);
+ }
+ return null;
+ }
+
@NotNull
@Override
public List<PyType> getElementTypes(@NotNull TypeEvalContext context) {
return new PyCollectionTypeImpl(pyClass, isDefinition, elementTypes);
}
+ @Override
+ public PyClassType toInstance() {
+ return myIsDefinition ? new PyCollectionTypeImpl(myClass, false, myElementTypes) : this;
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) return true;
}
return result;
}
-}
+}
\ No newline at end of file
import com.google.common.base.Joiner;
import com.intellij.codeInsight.intention.IntentionAction;
+import com.intellij.codeInsight.lookup.LookupElement;
+import com.intellij.codeInsight.lookup.LookupEx;
import com.intellij.codeInspection.LocalQuickFix;
import com.intellij.codeInspection.ex.QuickFixWrapper;
import com.intellij.execution.actions.ConfigurationContext;
//noinspection ConstantConditions
return getCommonCodeStyleSettings().getIndentOptions();
}
+
+ /**
+ * When you have more than one completion variant, you may use this method providing variant to choose.
+ * It only works for one caret (multiple carets not supported) and since it puts tab after completion, be sure to limit
+ * line somehow (i.e. with comment).
+ * <br/>
+ * Example: "user.n[caret]." There are "name" and "nose" fields.
+ * By calling this function with "nose" you will end with "user.nose ".
+ */
+ protected final void completeCaretWithMultipleVariants(@NotNull final String... desiredVariants) {
+ final LookupElement[] lookupElements = myFixture.completeBasic();
+ final LookupEx lookup = myFixture.getLookup();
+ if (lookupElements != null && lookupElements.length > 1) {
+ // More than one element returned, check directly because completion can't work in this case
+ for (final LookupElement element : lookupElements) {
+ final String suggestedString = element.getLookupString();
+ if (Arrays.asList(desiredVariants).contains(suggestedString)) {
+ myFixture.getLookup().setCurrentItem(element);
+ lookup.setCurrentItem(element);
+ myFixture.completeBasicAllCarets('\t');
+ return;
+ }
+ }
+ }
+ }
}