mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-13 21:55:01 +07:00
show variants from __init__.py in completion of relative imports; remove duplicate logic for calculating completion variants for a directory; simplify ResolveImportUtil.getPointInImport() (PY-2816)
This commit is contained in:
@@ -13,7 +13,7 @@ import java.util.Collections;
|
||||
public class PyStdlibModuleMembersProvider extends PyModuleMembersProvider {
|
||||
@Override
|
||||
protected Collection<PyDynamicMember> getMembersByQName(PyFile module, String qName, ResolveImportUtil.PointInImport point) {
|
||||
if (qName.equals("os") && point.role == ResolveImportUtil.PointInImport.ROLE.AS_MODULE) {
|
||||
if (qName.equals("os") && point == ResolveImportUtil.PointInImport.AS_MODULE) {
|
||||
return Collections.singletonList(new PyDynamicMember("path"));
|
||||
}
|
||||
return Collections.emptyList();
|
||||
|
||||
@@ -8,8 +8,6 @@ import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.util.Condition;
|
||||
import com.intellij.openapi.util.Iconable;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
@@ -20,9 +18,9 @@ import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.PyTokenTypes;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.resolve.*;
|
||||
import com.jetbrains.python.psi.types.PyModuleType;
|
||||
import com.jetbrains.python.psi.types.PyType;
|
||||
import com.jetbrains.python.psi.types.TypeEvalContext;
|
||||
import com.jetbrains.python.sdk.PythonSdkType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -93,19 +91,7 @@ public class PyImportReferenceImpl extends PyReferenceImpl {
|
||||
if (type != null) {
|
||||
Object[] variants = getTypeCompletionVariants(myElement, type);
|
||||
if (!alreadyHasImportKeyword()) {
|
||||
for (int i=0; i < variants.length; i+=1) {
|
||||
Object item = variants[i];
|
||||
if (item instanceof LookupElementBuilder) {
|
||||
variants[i] = ((LookupElementBuilder)item).setInsertHandler(ImportKeywordHandler.INSTANCE);
|
||||
}
|
||||
else if (item instanceof PsiNamedElement) {
|
||||
final PsiNamedElement element = (PsiNamedElement)item;
|
||||
variants[i] = LookupElementBuilder
|
||||
.create(element.getName()) // it can't really have null name
|
||||
.setIcon(element.getIcon(0))
|
||||
.setInsertHandler(ImportKeywordHandler.INSTANCE);
|
||||
}
|
||||
}
|
||||
replaceInsertHandler(variants, ImportKeywordHandler.INSTANCE);
|
||||
}
|
||||
return variants;
|
||||
}
|
||||
@@ -119,6 +105,22 @@ public class PyImportReferenceImpl extends PyReferenceImpl {
|
||||
}
|
||||
}
|
||||
|
||||
private static void replaceInsertHandler(Object[] variants, final InsertHandler<LookupElement> insertHandler) {
|
||||
for (int i=0; i < variants.length; i+=1) {
|
||||
Object item = variants[i];
|
||||
if (item instanceof LookupElementBuilder) {
|
||||
variants[i] = ((LookupElementBuilder)item).setInsertHandler(insertHandler);
|
||||
}
|
||||
else if (item instanceof PsiNamedElement) {
|
||||
final PsiNamedElement element = (PsiNamedElement)item;
|
||||
variants[i] = LookupElementBuilder
|
||||
.create(element.getName()) // it can't really have null name
|
||||
.setIcon(element.getIcon(0))
|
||||
.setInsertHandler(insertHandler);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean alreadyHasImportKeyword() {
|
||||
ASTNode node = myElement.getNode();
|
||||
while (node != null) {
|
||||
@@ -180,7 +182,7 @@ public class PyImportReferenceImpl extends PyReferenceImpl {
|
||||
else { // null source, must be a "from ... import"
|
||||
relative_level = from_import.getRelativeLevel();
|
||||
if (relative_level > 0) {
|
||||
PsiDirectory relative_dir = ResolveImportUtil.stepBackFrom(myCurrentFile, relative_level);
|
||||
PsiDirectory relative_dir = ResolveImportUtil.stepBackFrom(myCurrentFile, relative_level-1);
|
||||
if (relative_dir != null) {
|
||||
addImportedNames(from_import.getImportElements());
|
||||
fillFromDir(relative_dir, null);
|
||||
@@ -246,51 +248,21 @@ public class PyImportReferenceImpl extends PyReferenceImpl {
|
||||
}
|
||||
|
||||
// adds variants found under given dir
|
||||
private void fillFromDir(PsiDirectory target_dir, @Nullable InsertHandler<LookupElement> handler) {
|
||||
private void fillFromDir(PsiDirectory target_dir, @Nullable InsertHandler<LookupElement> insertHandler) {
|
||||
if (target_dir != null) {
|
||||
for (PsiElement dir_item : target_dir.getChildren()) {
|
||||
if (dir_item != myCurrentFile) {
|
||||
if (dir_item instanceof PsiDirectory) {
|
||||
final PsiDirectory dir = (PsiDirectory)dir_item;
|
||||
if (dir.findFile(PyNames.INIT_DOT_PY) != null) {
|
||||
final String name = dir.getName();
|
||||
if (PyNames.isIdentifier(name)) {
|
||||
myObjects.add(LookupElementBuilder
|
||||
.create(name)
|
||||
.setTypeText(getPresentablePath(dir.getParent()))
|
||||
.setIcon(dir.getIcon(Iconable.ICON_FLAG_CLOSED)));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (dir_item instanceof PsiFile) { // plain file
|
||||
String filename = ((PsiFile)dir_item).getName();
|
||||
if (!PyNames.INIT_DOT_PY.equals(filename) && filename.endsWith(PyNames.DOT_PY)) {
|
||||
final String name = filename.substring(0, filename.length() - PyNames.DOT_PY.length());
|
||||
if (PyNames.isIdentifier(name)) {
|
||||
final PsiDirectory dir = ((PsiFile)dir_item).getContainingDirectory();
|
||||
myObjects.add(LookupElementBuilder
|
||||
.create(name)
|
||||
.setTypeText(getPresentablePath(dir))
|
||||
.setInsertHandler(handler)
|
||||
.setIcon(dir_item.getIcon(0)));
|
||||
}
|
||||
}
|
||||
}
|
||||
PsiFile initPy = target_dir.findFile(PyNames.INIT_DOT_PY);
|
||||
if (initPy instanceof PyFile) {
|
||||
PyModuleType moduleType = new PyModuleType((PyFile)initPy);
|
||||
ProcessingContext context = new ProcessingContext();
|
||||
context.put(PyType.CTX_NAMES, myNamesAlready);
|
||||
Object[] completionVariants = moduleType.getCompletionVariants("", (PyExpression)getElement(), context);
|
||||
if (insertHandler != null) {
|
||||
replaceInsertHandler(completionVariants, insertHandler);
|
||||
}
|
||||
myObjects.addAll(Arrays.asList(completionVariants));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String getPresentablePath(PsiDirectory directory) {
|
||||
if (directory == null) {
|
||||
return "";
|
||||
}
|
||||
final String path = directory.getVirtualFile().getPath();
|
||||
if (path.contains(PythonSdkType.SKELETON_DIR_NAME)) {
|
||||
return "<built-in>";
|
||||
}
|
||||
return FileUtil.toSystemDependentName(path);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -27,9 +27,6 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static com.jetbrains.python.psi.resolve.ResolveImportUtil.PointInImport.ROLE.AS_MODULE;
|
||||
import static com.jetbrains.python.psi.resolve.ResolveImportUtil.PointInImport.ROLE.AS_NAME;
|
||||
|
||||
/**
|
||||
* @author dcheryasov
|
||||
*/
|
||||
@@ -937,41 +934,23 @@ public class ResolveImportUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Points to an import statement and role as found by {@link #getPointInImport(PsiReference)}.
|
||||
* Immutable.
|
||||
*/
|
||||
public static class PointInImport {
|
||||
public final PyFromImportStatement fromImportStatement;
|
||||
public final PyImportStatement importStatement;
|
||||
public final ROLE role;
|
||||
public static enum PointInImport {
|
||||
/**
|
||||
* The reference is not inside an import statement.
|
||||
*/
|
||||
NONE,
|
||||
|
||||
PointInImport(PyFromImportStatement fromImportStatement, PyImportStatement importStatement, ROLE role) {
|
||||
this.fromImportStatement = fromImportStatement;
|
||||
this.importStatement = importStatement;
|
||||
this.role = role;
|
||||
}
|
||||
/**
|
||||
* The reference is inside import and refers to a module
|
||||
*/
|
||||
AS_MODULE,
|
||||
|
||||
public static enum ROLE {
|
||||
/**
|
||||
* The reference is not inside an import statement.
|
||||
*/
|
||||
NONE,
|
||||
|
||||
/**
|
||||
* The reference is inside import and refers to a module
|
||||
*/
|
||||
AS_MODULE,
|
||||
|
||||
/**
|
||||
* The reference is inside import and refers to a name imported from a module
|
||||
*/
|
||||
AS_NAME
|
||||
}
|
||||
/**
|
||||
* The reference is inside import and refers to a name imported from a module
|
||||
*/
|
||||
AS_NAME
|
||||
}
|
||||
|
||||
public static final PointInImport NOT_IN_IMPORT = new PointInImport(null, null, PointInImport.ROLE.NONE);
|
||||
|
||||
/**
|
||||
* @param element what we test (identifier, reference, import element, etc)
|
||||
* @return the how the element relates to an enclosing import statement, if any
|
||||
@@ -982,23 +961,17 @@ public class ResolveImportUtil {
|
||||
PyImportElement.class, PyFromImportStatement.class
|
||||
);
|
||||
if (parent instanceof PyFromImportStatement) {
|
||||
return new PointInImport((PyFromImportStatement)parent, null, AS_MODULE); // from foo ...
|
||||
return PointInImport.AS_MODULE; // from foo ...
|
||||
}
|
||||
if (parent instanceof PyImportElement) {
|
||||
PsiElement statement = parent.getParent();
|
||||
if (statement instanceof PyImportStatement) {
|
||||
return new PointInImport(null, (PyImportStatement)statement, AS_MODULE); // import foo,...
|
||||
return PointInImport.AS_MODULE; // import foo,...
|
||||
}
|
||||
else if (statement instanceof PyFromImportStatement) {
|
||||
PyFromImportStatement importer = (PyFromImportStatement)statement; // from ??? import foo
|
||||
if (importer.getImportSource() == null && importer.getRelativeLevel() > 0) {
|
||||
return new PointInImport(importer, null, AS_MODULE); // from . import foo,...
|
||||
}
|
||||
else {
|
||||
return new PointInImport(importer, null, AS_NAME);
|
||||
} // from bar import foo,...
|
||||
return PointInImport.AS_NAME;
|
||||
}
|
||||
}
|
||||
return NOT_IN_IMPORT;
|
||||
return PointInImport.NONE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ public abstract class PyModuleMembersProvider {
|
||||
|
||||
@Nullable
|
||||
public PsiElement resolveMember(PyFile module, String name) {
|
||||
for (PyDynamicMember o : getMembers(module, ResolveImportUtil.NOT_IN_IMPORT)) {
|
||||
for (PyDynamicMember o : getMembers(module, ResolveImportUtil.PointInImport.NONE)) {
|
||||
if (o.getName().equals(name)) {
|
||||
return o.resolve(module);
|
||||
}
|
||||
|
||||
@@ -25,9 +25,6 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static com.jetbrains.python.psi.resolve.ResolveImportUtil.PointInImport.ROLE.*;
|
||||
// .impl looks impure
|
||||
|
||||
/**
|
||||
* @author yole
|
||||
*/
|
||||
@@ -113,9 +110,9 @@ public class PyModuleType implements PyType { // Modules don't descend from obje
|
||||
}
|
||||
}
|
||||
|
||||
if (point.role == NONE || point.role == AS_NAME) { // when not imported from, add regular attributes
|
||||
if (point == ResolveImportUtil.PointInImport.NONE || point == ResolveImportUtil.PointInImport.AS_NAME) { // when not imported from, add regular attributes
|
||||
final VariantsProcessor processor = new VariantsProcessor(location);
|
||||
processor.setPlainNamesOnly(point.role == AS_NAME); // no parens after imported function names
|
||||
processor.setPlainNamesOnly(point == ResolveImportUtil.PointInImport.AS_NAME); // no parens after imported function names
|
||||
myModule.processDeclarations(processor, ResolveState.initial(), null, location);
|
||||
if (names_already != null) {
|
||||
for (LookupElement le : processor.getResultList()) {
|
||||
@@ -130,7 +127,7 @@ public class PyModuleType implements PyType { // Modules don't descend from obje
|
||||
result.addAll(processor.getResultList());
|
||||
}
|
||||
}
|
||||
if (point.role == AS_MODULE || point.role == AS_NAME) { // when imported from somehow, add submodules
|
||||
if (point == ResolveImportUtil.PointInImport.AS_MODULE || point == ResolveImportUtil.PointInImport.AS_NAME) { // when imported from somehow, add submodules
|
||||
for (PsiFileSystemItem pfsi : getSubmodulesList()) {
|
||||
if (pfsi == location.getContainingFile().getOriginalFile()) continue;
|
||||
String s = pfsi.getName();
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
def xyzzy(): pass
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
from . import xyzzy
|
||||
@@ -0,0 +1 @@
|
||||
from . import x<caret>
|
||||
@@ -282,4 +282,11 @@ public class PythonCompletionTest extends PyLightFixtureTestCase {
|
||||
myFixture.completeBasic();
|
||||
myFixture.checkResultByFile("completion/relativeImport/pkg/main.after.py");
|
||||
}
|
||||
|
||||
public void testRelativeImportNameFromInitPy() { // PY-2816
|
||||
myFixture.copyDirectoryToProject("completion/relativeImport", "relativeImport");
|
||||
myFixture.configureByFile("relativeImport/pkg/name.py");
|
||||
myFixture.completeBasic();
|
||||
myFixture.checkResultByFile("completion/relativeImport/pkg/name.after.py");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user