Merge branch 'goto-module'

This commit is contained in:
Andrey Vlasovskikh
2014-02-21 15:17:20 +04:00
10 changed files with 261 additions and 9 deletions
+1
View File
@@ -89,6 +89,7 @@
<stubIndex implementation="com.jetbrains.python.psi.stubs.PySuperClassIndex"/>
<stubIndex implementation="com.jetbrains.python.psi.stubs.PyVariableNameIndex"/>
<stubIndex implementation="com.jetbrains.python.psi.stubs.PyInstanceAttributeIndex"/>
<fileBasedIndex implementation="com.jetbrains.python.psi.stubs.PyModuleNameIndex"/>
<declarationRangeHandler key="com.jetbrains.python.psi.PyClass"
implementationClass="com.jetbrains.python.codeInsight.PyDeclarationRangeHandler"/>
@@ -19,11 +19,14 @@ import com.intellij.navigation.ChooseByNameContributor;
import com.intellij.navigation.NavigationItem;
import com.intellij.openapi.project.Project;
import com.intellij.util.ArrayUtil;
import com.jetbrains.python.psi.PyClass;
import com.intellij.util.containers.HashSet;
import com.jetbrains.python.psi.stubs.PyClassNameIndex;
import com.jetbrains.python.psi.stubs.PyModuleNameIndex;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
/**
* @author yole
@@ -31,13 +34,18 @@ import java.util.Collection;
public class PyGotoClassContributor implements ChooseByNameContributor {
@NotNull
public String[] getNames(final Project project, final boolean includeNonProjectItems) {
final Collection<String> classNames = PyClassNameIndex.allKeys(project);
return ArrayUtil.toStringArray(classNames);
Set<String> results = new HashSet<String>();
results.addAll(PyClassNameIndex.allKeys(project));
results.addAll(PyModuleNameIndex.getAllKeys(project));
return ArrayUtil.toStringArray(results);
}
@NotNull
public NavigationItem[] getItemsByName(final String name, final String pattern, final Project project, final boolean includeNonProjectItems) {
final Collection<PyClass> classes = PyClassNameIndex.find(name, project, includeNonProjectItems);
return classes.toArray(new NavigationItem[classes.size()]);
public NavigationItem[] getItemsByName(final String name, final String pattern, final Project project,
final boolean includeNonProjectItems) {
final List<NavigationItem> results = new ArrayList<NavigationItem>();
results.addAll(PyClassNameIndex.find(name, project, includeNonProjectItems));
results.addAll(PyModuleNameIndex.find(name, project, includeNonProjectItems));
return results.toArray(new NavigationItem[results.size()]);
}
}
@@ -25,6 +25,7 @@ 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;
import com.jetbrains.python.psi.stubs.PyModuleNameIndex;
import com.jetbrains.python.psi.stubs.PyVariableNameIndex;
import org.jetbrains.annotations.NotNull;
@@ -41,6 +42,7 @@ public class PyGotoSymbolContributor implements GotoClassContributor {
public String[] getNames(final Project project, final boolean includeNonProjectItems) {
Set<String> symbols = new HashSet<String>();
symbols.addAll(PyClassNameIndex.allKeys(project));
symbols.addAll(PyModuleNameIndex.getAllKeys(project));
symbols.addAll(StubIndex.getInstance().getAllKeys(PyFunctionNameIndex.KEY, project));
symbols.addAll(StubIndex.getInstance().getAllKeys(PyVariableNameIndex.KEY, project));
return ArrayUtil.toStringArray(symbols);
@@ -54,6 +56,7 @@ public class PyGotoSymbolContributor implements GotoClassContributor {
List<NavigationItem> symbols = new ArrayList<NavigationItem>();
symbols.addAll(PyClassNameIndex.find(name, project, scope));
symbols.addAll(PyModuleNameIndex.find(name, project, includeNonProjectItems));
symbols.addAll(PyFunctionNameIndex.find(name, project, scope));
symbols.addAll(PyVariableNameIndex.find(name, project, scope));
@@ -16,15 +16,19 @@
package com.jetbrains.python.psi.impl;
import com.intellij.extapi.psi.PsiFileBase;
import com.intellij.icons.AllIcons;
import com.intellij.lang.ASTNode;
import com.intellij.lang.Language;
import com.intellij.navigation.ItemPresentation;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.roots.ProjectFileIndex;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.scope.PsiScopeProcessor;
import com.intellij.psi.stubs.StubElement;
import com.intellij.psi.templateLanguages.TemplateLanguageFileViewProvider;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.PsiModificationTracker;
import com.intellij.psi.util.QualifiedName;
@@ -33,11 +37,15 @@ import com.intellij.util.IncorrectOperationException;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.MultiMap;
import com.intellij.util.indexing.IndexingDataKeys;
import com.jetbrains.python.*;
import com.jetbrains.python.PyElementTypes;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.PythonFileType;
import com.jetbrains.python.PythonLanguage;
import com.jetbrains.python.codeInsight.controlflow.ControlFlowCache;
import com.jetbrains.python.documentation.DocStringUtil;
import com.jetbrains.python.inspections.PythonVisitorFilter;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.resolve.QualifiedNameFinder;
import com.jetbrains.python.psi.resolve.RatedResolveResult;
import com.jetbrains.python.psi.resolve.ResolveImportUtil;
import com.jetbrains.python.psi.resolve.VariantsProcessor;
@@ -49,6 +57,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.io.File;
import java.util.*;
public class PyFileImpl extends PsiFileBase implements PyFile, PyExpression {
@@ -770,4 +779,78 @@ public class PyFileImpl extends PsiFileBase implements PyFile, PyExpression {
}
return elementType == PyElementTypes.IMPORT_STATEMENT || elementType == PyElementTypes.FROM_IMPORT_STATEMENT;
}
@Override
public ItemPresentation getPresentation() {
return new ItemPresentation() {
@Override
public String getPresentableText() {
return getModuleName(PyFileImpl.this);
}
@Override
public String getLocationString() {
final String name = getLocationName();
return name != null ? "(" + name + ")" : null;
}
@Override
public Icon getIcon(final boolean open) {
if (PyUtil.isPackage(PyFileImpl.this)) {
return AllIcons.Modules.SourceFolder;
}
return PyFileImpl.this.getIcon(0);
}
@NotNull
private String getModuleName(@NotNull PyFile file) {
if (PyUtil.isPackage(file)) {
final PsiDirectory dir = file.getContainingDirectory();
if (dir != null) {
return dir.getName();
}
}
return FileUtil.getNameWithoutExtension(file.getName());
}
@Nullable
private String getLocationName() {
final QualifiedName name = QualifiedNameFinder.findShortestImportableQName(PyFileImpl.this);
if (name != null) {
final QualifiedName prefix = name.removeTail(1);
if (prefix.getComponentCount() > 0) {
return prefix.toString();
}
}
final String relativePath = getRelativeContainerPath();
if (relativePath != null) {
return relativePath;
}
final PsiDirectory psiDirectory = getParent();
if (psiDirectory != null) {
return psiDirectory.getVirtualFile().getPresentableUrl();
}
return null;
}
@Nullable
private String getRelativeContainerPath() {
final PsiDirectory psiDirectory = getParent();
if (psiDirectory != null) {
final VirtualFile virtualFile = getVirtualFile();
if (virtualFile != null) {
final VirtualFile root = ProjectFileIndex.SERVICE.getInstance(getProject()).getContentRootForFile(virtualFile);
if (root != null) {
final VirtualFile parent = virtualFile.getParent();
final VirtualFile rootParent = root.getParent();
if (rootParent != null && parent != null) {
return VfsUtilCore.getRelativePath(parent, rootParent, File.separatorChar);
}
}
}
}
return null;
}
};
}
}
@@ -0,0 +1,113 @@
/*
* Copyright 2000-2014 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.psi.stubs;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.util.indexing.*;
import com.intellij.util.io.EnumeratorStringDescriptor;
import com.intellij.util.io.KeyDescriptor;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.PythonFileType;
import com.jetbrains.python.psi.PyFile;
import com.jetbrains.python.psi.search.PyProjectScopeBuilder;
import org.jetbrains.annotations.NotNull;
import java.util.*;
/**
* @author vlan
*/
public class PyModuleNameIndex extends ScalarIndexExtension<String> {
public static final ID<String, Void> NAME = ID.create("Py.module.name");
private final EnumeratorStringDescriptor myKeyDescriptor = new EnumeratorStringDescriptor();
private final DataIndexer<String, Void, FileContent> myDataIndexer = new DataIndexer<String, Void, FileContent>() {
@NotNull
@Override
public Map<String, Void> map(FileContent inputData) {
final VirtualFile file = inputData.getFile();
final String name = file.getName();
if (PyNames.INIT_DOT_PY.equals(name)) {
final VirtualFile parent = file.getParent();
if (parent != null && parent.isDirectory()) {
return Collections.singletonMap(parent.getName(), null);
}
}
else {
return Collections.singletonMap(FileUtil.getNameWithoutExtension(name), null);
}
return Collections.emptyMap();
}
};
@NotNull
@Override
public ID<String, Void> getName() {
return NAME;
}
@NotNull
@Override
public DataIndexer<String, Void, FileContent> getIndexer() {
return myDataIndexer;
}
@Override
public KeyDescriptor<String> getKeyDescriptor() {
return myKeyDescriptor;
}
@Override
public FileBasedIndex.InputFilter getInputFilter() {
return new DefaultFileTypeSpecificInputFilter(PythonFileType.INSTANCE);
}
@Override
public boolean dependsOnFileContent() {
return true;
}
@Override
public int getVersion() {
return 0;
}
@NotNull
public static Collection<String> getAllKeys(@NotNull Project project) {
return FileBasedIndex.getInstance().getAllKeys(NAME, project);
}
@NotNull
public static List<PyFile> find(@NotNull String name, @NotNull Project project, boolean includeNonProjectItems) {
final List<PyFile> results = new ArrayList<PyFile>();
final GlobalSearchScope scope = includeNonProjectItems
? PyProjectScopeBuilder.excludeSdkTestsScope(project)
: GlobalSearchScope.projectScope(project);
final Collection<VirtualFile> files = FileBasedIndex.getInstance().getContainingFiles(NAME, name, scope);
for (VirtualFile virtualFile : files) {
final PsiFile psiFile = PsiManager.getInstance(project).findFile(virtualFile);
if (psiFile instanceof PyFile) {
results.add((PyFile)psiFile);
}
}
return results;
}
}
@@ -0,0 +1,44 @@
/*
* Copyright 2000-2014 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;
import com.jetbrains.python.fixtures.PyTestCase;
import com.jetbrains.python.psi.stubs.PyModuleNameIndex;
import java.util.Collection;
/**
* @author vlan
*/
public class PyIndexingTest extends PyTestCase {
public static final String TEST_DIRECTORY = "indexing/";
@Override
protected void setUp() throws Exception {
super.setUp();
final String testName = getTestName(false);
myFixture.copyDirectoryToProject(TEST_DIRECTORY + testName, "");
myFixture.configureFromTempProjectFile("a.py");
}
public void testModuleNameIndex() {
final Collection<String> modules = PyModuleNameIndex.getAllKeys(myFixture.getProject());
assertContainsElements(modules, "foo");
assertContainsElements(modules, "bar");
assertDoesntContain(modules, "__init__");
assertDoesntContain(modules, "baz");
}
}