mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
show module names in second completion (PY-7066)
This commit is contained in:
+25
-6
@@ -9,18 +9,22 @@ import com.intellij.openapi.command.WriteCommandAction;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Condition;
|
||||
import com.intellij.openapi.util.Conditions;
|
||||
import com.intellij.openapi.util.Iconable;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.search.FileTypeIndex;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.stubs.StubIndex;
|
||||
import com.intellij.psi.stubs.StubIndexKey;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.jetbrains.python.PythonFileType;
|
||||
import com.jetbrains.python.codeInsight.imports.AddImportHelper;
|
||||
import com.jetbrains.python.codeInsight.imports.PythonReferenceImporter;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.impl.PyPsiUtils;
|
||||
import com.jetbrains.python.psi.search.PyProjectScopeBuilder;
|
||||
import com.jetbrains.python.psi.stubs.PyClassNameIndex;
|
||||
import com.jetbrains.python.psi.stubs.PyFunctionNameIndex;
|
||||
import com.jetbrains.python.psi.types.PyModuleType;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
@@ -40,9 +44,25 @@ public class PyClassNameCompletionContributor extends CompletionContributor {
|
||||
if (PsiTreeUtil.getParentOfType(element, PyImportStatementBase.class) != null) {
|
||||
return;
|
||||
}
|
||||
addVariantsFromIndex(result, parameters.getOriginalFile(), PyClassNameIndex.KEY, CLASS_INSERT_HANDLER,
|
||||
addVariantsFromIndex(result, parameters.getOriginalFile(), PyClassNameIndex.KEY, IMPORTING_INSERT_HANDLER,
|
||||
Conditions.<PyClass>alwaysTrue());
|
||||
addVariantsFromIndex(result, parameters.getOriginalFile(), PyFunctionNameIndex.KEY, FUNCTION_INSERT_HANDLER, TOPLEVEL_FUNCTION);
|
||||
addVariantsFromModules(result, parameters.getOriginalFile());
|
||||
}
|
||||
}
|
||||
|
||||
private static void addVariantsFromModules(CompletionResultSet result, PsiFile targetFile) {
|
||||
Collection<VirtualFile> files = FileTypeIndex.getFiles(PythonFileType.INSTANCE, PyProjectScopeBuilder.excludeSdkTestsScope(targetFile));
|
||||
for (VirtualFile file : files) {
|
||||
PsiFile pyFile = targetFile.getManager().findFile(file);
|
||||
if (pyFile == null) continue;
|
||||
PsiFileSystemItem importable = (PsiFileSystemItem) PyUtil.turnInitIntoDir(pyFile);
|
||||
if (PythonReferenceImporter.isImportableModule(targetFile, importable)) {
|
||||
LookupElementBuilder element = PyModuleType.buildFileLookupElement(importable, null);
|
||||
if (element != null) {
|
||||
result.addElement(element.withInsertHandler(IMPORTING_INSERT_HANDLER));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,8 +85,7 @@ public class PyClassNameCompletionContributor extends CompletionContributor {
|
||||
for (final String elementName : CompletionUtil.sortMatching(resultSet.getPrefixMatcher(), keys)) {
|
||||
for (T element : StubIndex.getInstance().get(key, elementName, project, scope)) {
|
||||
if (condition.value(element)) {
|
||||
resultSet.addElement(LookupElementBuilder.create(element)
|
||||
.withIcon(element.getIcon(Iconable.ICON_FLAG_CLOSED))
|
||||
resultSet.addElement(LookupElementBuilder.createWithIcon(element)
|
||||
.withTailText(" " + ((NavigationItem)element).getPresentation().getLocationString(), true)
|
||||
.withInsertHandler(insertHandler));
|
||||
}
|
||||
@@ -74,9 +93,9 @@ public class PyClassNameCompletionContributor extends CompletionContributor {
|
||||
}
|
||||
}
|
||||
|
||||
private static final InsertHandler<LookupElement> CLASS_INSERT_HANDLER = new InsertHandler<LookupElement>() {
|
||||
private static final InsertHandler<LookupElement> IMPORTING_INSERT_HANDLER = new InsertHandler<LookupElement>() {
|
||||
public void handleInsert(final InsertionContext context, final LookupElement item) {
|
||||
addImportForLookupElement(context, item, context.getTailOffset() - 1);
|
||||
addImportForLookupElement(context, item, context.getTailOffset() - 1);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ import com.intellij.openapi.editor.colors.EditorColorsManager;
|
||||
import com.intellij.openapi.editor.colors.EditorColorsScheme;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.ProjectFileIndex;
|
||||
import com.intellij.openapi.roots.ProjectRootManager;
|
||||
import com.intellij.openapi.ui.popup.PopupChooserBuilder;
|
||||
import com.intellij.openapi.util.AsyncResult;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
@@ -124,7 +123,7 @@ public class ImportFromExistingAction implements QuestionAction {
|
||||
final Project project = myTarget.getProject();
|
||||
final PyElementGenerator gen = PyElementGenerator.getInstance(project);
|
||||
AddImportHelper.ImportPriority priority = AddImportHelper.getImportPriority(myTarget, item.getFile());
|
||||
if (isRoot(project, item.getFile())) {
|
||||
if (isRoot(item.getFile())) {
|
||||
AddImportHelper.addImportStatement(myTarget.getContainingFile(), myName, null, priority);
|
||||
}
|
||||
else {
|
||||
@@ -170,11 +169,11 @@ public class ImportFromExistingAction implements QuestionAction {
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isRoot(Project project, PsiFileSystemItem directory) {
|
||||
public static boolean isRoot(PsiFileSystemItem directory) {
|
||||
if (directory == null) return true;
|
||||
VirtualFile vFile = directory.getVirtualFile();
|
||||
if (vFile == null) return true;
|
||||
ProjectFileIndex fileIndex = ProjectRootManager.getInstance(project).getFileIndex();
|
||||
ProjectFileIndex fileIndex = ProjectFileIndex.SERVICE.getInstance(directory.getProject());
|
||||
return Comparing.equal(fileIndex.getClassRootForFile(vFile), vFile) ||
|
||||
Comparing.equal(fileIndex.getContentRootForFile(vFile), vFile) ||
|
||||
Comparing.equal(fileIndex.getSourceRootForFile(vFile), vFile);
|
||||
|
||||
@@ -157,7 +157,7 @@ public class PythonReferenceImporter implements ReferenceImporter {
|
||||
if (isIndexableTopLevel(symbol)) { // we only want top-level symbols
|
||||
PsiFileSystemItem srcfile = symbol instanceof PsiFileSystemItem ? ((PsiFileSystemItem)symbol).getParent() : symbol.getContainingFile();
|
||||
if (srcfile != null && srcfile != existing_import_file && srcfile != node.getContainingFile() &&
|
||||
(ImportFromExistingAction.isRoot(project, srcfile) || PyNames.isIdentifier(FileUtil.getNameWithoutExtension(srcfile.getName()))) &&
|
||||
(ImportFromExistingAction.isRoot(srcfile) || PyNames.isIdentifier(FileUtil.getNameWithoutExtension(srcfile.getName()))) &&
|
||||
!isShadowedModule(srcfile)) {
|
||||
PyQualifiedName import_path = ResolveImportUtil.findCanonicalImportPath(srcfile, node);
|
||||
if (import_path != null && !seen_file_names.contains(import_path.toString())) {
|
||||
@@ -216,11 +216,7 @@ public class PythonReferenceImporter implements ReferenceImporter {
|
||||
List<PsiElement> result = new ArrayList<PsiElement>();
|
||||
PsiFile[] files = FilenameIndex.getFilesByName(project, reftext + ".py", scope);
|
||||
for (PsiFile file : files) {
|
||||
PsiDirectory parent = file.getParent();
|
||||
if (parent != null && file != targetFile &&
|
||||
(parent.findFile(PyNames.INIT_DOT_PY) != null ||
|
||||
ImportFromExistingAction.isRoot(project, parent) ||
|
||||
parent == targetFile.getParent())) {
|
||||
if (isImportableModule(targetFile, file)) {
|
||||
result.add(file);
|
||||
}
|
||||
}
|
||||
@@ -235,6 +231,14 @@ public class PythonReferenceImporter implements ReferenceImporter {
|
||||
return result;
|
||||
}
|
||||
|
||||
public static boolean isImportableModule(PsiFile targetFile, PsiFileSystemItem file) {
|
||||
PsiDirectory parent = (PsiDirectory)file.getParent();
|
||||
return parent != null && file != targetFile &&
|
||||
(parent.findFile(PyNames.INIT_DOT_PY) != null ||
|
||||
ImportFromExistingAction.isRoot(parent) ||
|
||||
parent == targetFile.getParent());
|
||||
}
|
||||
|
||||
private static boolean isIndexableTopLevel(PsiElement symbol) {
|
||||
if (symbol instanceof PsiFileSystemItem) {
|
||||
return true;
|
||||
|
||||
@@ -633,8 +633,8 @@ public class PyUtil {
|
||||
|
||||
@Nullable
|
||||
public static PsiElement turnInitIntoDir(PsiElement target) {
|
||||
if (target instanceof PyFile && PyNames.INIT_DOT_PY.equals(((PyFile)target).getName())) {
|
||||
return ((PyFile)target).getContainingDirectory();
|
||||
if (target instanceof PyFile && isPackage((PsiFile) target)) {
|
||||
return ((PsiFile)target).getContainingDirectory();
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
@@ -273,7 +273,7 @@ public class PyModuleType implements PyType { // Modules don't descend from obje
|
||||
return result.toArray();
|
||||
}
|
||||
|
||||
private void addImportedSubmodules(PyExpression location, Set<String> names_already, List<Object> result) {
|
||||
private void addImportedSubmodules(PyExpression location, Set<String> exiatingNames, List<Object> result) {
|
||||
PsiFile file = location.getContainingFile();
|
||||
if (file instanceof PyFile) {
|
||||
PyFile pyFile = (PyFile)file;
|
||||
@@ -286,10 +286,10 @@ public class PyModuleType implements PyType { // Modules don't descend from obje
|
||||
}
|
||||
LookupElement element = null;
|
||||
if (target instanceof PsiFileSystemItem) {
|
||||
element = buildFileLookupElement(location, names_already, (PsiFileSystemItem) target);
|
||||
element = buildFileLookupElement((PsiFileSystemItem) target, exiatingNames);
|
||||
}
|
||||
else if (target instanceof PsiNamedElement) {
|
||||
element = LookupElementBuilder.create((PsiNamedElement)target).withIcon(target.getIcon(0));
|
||||
element = LookupElementBuilder.createWithIcon((PsiNamedElement)target);
|
||||
}
|
||||
if (element != null) {
|
||||
result.add(element);
|
||||
@@ -304,26 +304,23 @@ public class PyModuleType implements PyType { // Modules don't descend from obje
|
||||
Set<String> names_already) {
|
||||
List<LookupElement> result = new ArrayList<LookupElement>();
|
||||
for (PsiFileSystemItem item : getSubmodulesList(directory)) {
|
||||
LookupElement lookupElement = buildFileLookupElement(location, names_already, item);
|
||||
if (lookupElement != null) {
|
||||
result.add(lookupElement);
|
||||
if (item != location.getContainingFile().getOriginalFile()) {
|
||||
LookupElement lookupElement = buildFileLookupElement(item, names_already);
|
||||
if (lookupElement != null) {
|
||||
result.add(lookupElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static LookupElement buildFileLookupElement(PsiElement location,
|
||||
Set<String> names_already,
|
||||
PsiFileSystemItem item) {
|
||||
if (item == location.getContainingFile().getOriginalFile()) return null;
|
||||
String s = item.getName();
|
||||
int pos = s.lastIndexOf('.'); // it may not contain a dot, except in extension; cut it off.
|
||||
if (pos > 0) s = s.substring(0, pos);
|
||||
public static LookupElementBuilder buildFileLookupElement(PsiFileSystemItem item, @Nullable Set<String> existingNames) {
|
||||
String s = FileUtil.getNameWithoutExtension(item.getName());
|
||||
if (!PyNames.isIdentifier(s)) return null;
|
||||
if (names_already != null) {
|
||||
if (names_already.contains(s)) return null;
|
||||
else names_already.add(s);
|
||||
if (existingNames != null) {
|
||||
if (existingNames.contains(s)) return null;
|
||||
else existingNames.add(s);
|
||||
}
|
||||
return LookupElementBuilder.create(item, s)
|
||||
.withTypeText(getPresentablePath((PsiDirectory)item.getParent()))
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
import exceptions
|
||||
|
||||
exceptions
|
||||
@@ -0,0 +1 @@
|
||||
exc<caret>
|
||||
@@ -33,11 +33,18 @@ public class PyClassNameCompletionTest extends PyTestCase {
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testModule() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
private void doTest() {
|
||||
final String path = "/completion/className/" + getTestName(true);
|
||||
myFixture.copyDirectoryToProject(path, "");
|
||||
myFixture.configureFromTempProjectFile(getTestName(true) + ".py");
|
||||
myFixture.complete(CompletionType.BASIC, 2);
|
||||
myFixture.checkResultByFile(path + "/" + getTestName(true) + ".after.py");
|
||||
if (myFixture.getLookupElements() != null) {
|
||||
myFixture.finishLookup();
|
||||
}
|
||||
myFixture.checkResultByFile(path + "/" + getTestName(true) + ".after.py", true);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user