diff --git a/python/psi-api/src/com/jetbrains/python/nameResolver/FQNamesProvider.java b/python/psi-api/src/com/jetbrains/python/nameResolver/FQNamesProvider.java index 8b6a4580b0b4..7d47d9000d08 100644 --- a/python/psi-api/src/com/jetbrains/python/nameResolver/FQNamesProvider.java +++ b/python/psi-api/src/com/jetbrains/python/nameResolver/FQNamesProvider.java @@ -15,10 +15,20 @@ */ package com.jetbrains.python.nameResolver; +import com.intellij.navigation.NavigationItem; +import com.intellij.psi.util.QualifiedName; +import com.intellij.util.ArrayUtil; +import com.jetbrains.python.psi.PyQualifiedNameOwner; import org.jetbrains.annotations.NotNull; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + /** * Some enum value that represents one or more fully qualified names for some function + * * @author Ilya.Kazakevich */ public interface FQNamesProvider { @@ -28,8 +38,73 @@ public interface FQNamesProvider { @NotNull String[] getNames(); + @NotNull + default QualifiedName[] getQualifiedNames() { + return Arrays.stream(getNames()).map(QualifiedName::fromDottedString).toArray(QualifiedName[]::new); + } + + + @NotNull + default String getFirstName() { + return getNames()[0]; + } + + default boolean isShortNameMatches(@NotNull final NavigationItem item) { + final String name = item.getName(); + if (name == null) { + return false; + } + return getShortNames().contains(name); + } + + /** + * @return all names in unqualified ("after last dot") format + */ + @NotNull + default List getShortNames() { + return Arrays.stream(getQualifiedNames()).map(QualifiedName::getLastComponent).filter(o -> o != null).collect(Collectors.toList()); + } + /** * @return is name of class (true) or function (false) */ boolean isClass(); + + /** + * @return if element should be checked by full name conformity by {@link #isNameMatches(PyQualifiedNameOwner)} + * or only name and package should be checked + * @see #isNameMatches(PyQualifiedNameOwner) + */ + default boolean alwaysCheckQualifiedName() { + return true; + } + + /** + * Checks if element name matches. {@link #alwaysCheckQualifiedName()} controls if full name should be checked, or only last and first + * parts (name and package) are enough. It may be used for cases when physical FQN is not documented. + */ + default boolean isNameMatches(@NotNull final PyQualifiedNameOwner qualifiedNameOwner) { + final String qualifiedName = qualifiedNameOwner.getQualifiedName(); + if (qualifiedName == null) { + return false; + } + + // Only check qualified name + if (alwaysCheckQualifiedName()) { + return ArrayUtil.contains(qualifiedName, getNames()); + } + + // Relaxed check: package and name + final QualifiedName elementQualifiedName = QualifiedName.fromDottedString(qualifiedName); + final Stream nameStream = Arrays.stream(getQualifiedNames()); + return nameStream.anyMatch((name) -> { + final String first = name.getFirstComponent(); + final String last = name.getLastComponent(); + return first != null + && last != null + && first.equals(elementQualifiedName.getFirstComponent()) + && last.equals(elementQualifiedName.getLastComponent()); + } + ); + } } diff --git a/python/psi-api/src/com/jetbrains/python/psi/PyElementGenerator.java b/python/psi-api/src/com/jetbrains/python/psi/PyElementGenerator.java index 3fbeb3add142..2282f36d0368 100644 --- a/python/psi-api/src/com/jetbrains/python/psi/PyElementGenerator.java +++ b/python/psi-api/src/com/jetbrains/python/psi/PyElementGenerator.java @@ -50,7 +50,7 @@ public abstract class PyElementGenerator { public abstract PyStringLiteralExpression createStringLiteralFromString(@NotNull String unescaped); - public abstract PyStringLiteralExpression createStringLiteral(@NotNull PyStringLiteralExpression oldElement, @NotNull String unescaped); + public abstract PyStringLiteralExpression createStringLiteral(@NotNull StringLiteralExpression oldElement, @NotNull String unescaped); public abstract PyListLiteralExpression createListLiteral(); diff --git a/python/src/com/jetbrains/python/PyStringLiteralReference.java b/python/src/com/jetbrains/python/PyStringLiteralReference.java index 524197817f0f..c9bc6f02dd66 100644 --- a/python/src/com/jetbrains/python/PyStringLiteralReference.java +++ b/python/src/com/jetbrains/python/PyStringLiteralReference.java @@ -18,7 +18,6 @@ package com.jetbrains.python; import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiElement; import com.jetbrains.python.psi.PyElementGenerator; -import com.jetbrains.python.psi.PyStringLiteralUtil; import com.jetbrains.python.psi.StringLiteralExpression; import org.jetbrains.annotations.NotNull; @@ -39,12 +38,12 @@ public abstract class PyStringLiteralReference extends BaseReference { @SuppressWarnings("RefusedBequest") // 1 instead of 1 in range and "-1" at the end because we do not need quotes @Override public final TextRange getRangeInElement() { - return PyStringLiteralUtil.getTextRange(myElement); + return myStringLiteral.getStringValueTextRange(); } @Override public PsiElement handleElementRename(@NotNull final String newElementName) { - final PsiElement newString = PyElementGenerator.getInstance(myElement.getProject()).createStringLiteralFromString(newElementName); + final PsiElement newString = PyElementGenerator.getInstance(myElement.getProject()).createStringLiteral(myStringLiteral, newElementName); myStringLiteral.replace(newString); return newString; } diff --git a/python/src/com/jetbrains/python/psi/PyUtil.java b/python/src/com/jetbrains/python/psi/PyUtil.java index 5893ec303e4d..1aecf0552ef8 100644 --- a/python/src/com/jetbrains/python/psi/PyUtil.java +++ b/python/src/com/jetbrains/python/psi/PyUtil.java @@ -1708,7 +1708,10 @@ public class PyUtil { * @param expectedPackage package like "django" * @param expectedName expected name (i.e. AppConfig) * @return true if element in package + * @deprecated use {@link com.jetbrains.python.nameResolver.FQNamesProvider#isNameMatches(PyQualifiedNameOwner)} + * Remove in 2018 */ + @Deprecated public static boolean isSymbolInPackage(@NotNull final PyQualifiedNameOwner symbol, @NotNull final String expectedPackage, @NotNull final String expectedName) { diff --git a/python/src/com/jetbrains/python/psi/impl/PyElementGeneratorImpl.java b/python/src/com/jetbrains/python/psi/impl/PyElementGeneratorImpl.java index e1602e665767..899c28476149 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyElementGeneratorImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyElementGeneratorImpl.java @@ -108,7 +108,8 @@ public class PyElementGeneratorImpl extends PyElementGenerator { return createStringLiteralFromString(null, unescaped, true); } - public PyStringLiteralExpression createStringLiteral(@NotNull PyStringLiteralExpression oldElement, @NotNull String unescaped) { + @Override + public PyStringLiteralExpression createStringLiteral(@NotNull StringLiteralExpression oldElement, @NotNull String unescaped) { Pair quotes = PyStringLiteralUtil.getQuotes(oldElement.getText()); if (quotes != null) { return createStringLiteralAlreadyEscaped(quotes.first + unescaped + quotes.second);