mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-18 09:34:34 +07:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
package org.jetbrains.idea.devkit.dom.impl;
|
||||
|
||||
import com.intellij.ide.plugins.PluginManager;
|
||||
import com.intellij.ide.plugins.PluginManagerCore;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
@@ -50,57 +50,6 @@ import java.util.*;
|
||||
public class ExtensionDomExtender extends DomExtender<Extensions> {
|
||||
private static final PsiClassConverter CLASS_CONVERTER = new PluginPsiClassConverter();
|
||||
private static final Converter LANGUAGE_CONVERTER = new LanguageResolvingConverter();
|
||||
|
||||
private static class MyRequired implements Required {
|
||||
@Override
|
||||
public boolean value() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean nonEmpty() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean identifier() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Annotation> annotationType() {
|
||||
return Required.class;
|
||||
}
|
||||
}
|
||||
|
||||
private static class MyExtendClass extends ExtendClassImpl {
|
||||
private final String myInterfaceName;
|
||||
|
||||
private MyExtendClass(String interfaceName) {
|
||||
myInterfaceName = interfaceName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean allowAbstract() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean allowInterface() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean allowEnum() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String value() {
|
||||
return myInterfaceName;
|
||||
}
|
||||
}
|
||||
|
||||
private static final DomExtender EXTENSION_EXTENDER = new DomExtender() {
|
||||
public void registerExtensions(@NotNull final DomElement domElement, @NotNull final DomExtensionsRegistrar registrar) {
|
||||
final ExtensionPoint extensionPoint = (ExtensionPoint)domElement.getChildDescription().getDomDeclaration();
|
||||
@@ -127,31 +76,6 @@ public class ExtensionDomExtender extends DomExtender<Extensions> {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
public void registerExtensions(@NotNull final Extensions extensions, @NotNull final DomExtensionsRegistrar registrar) {
|
||||
final XmlElement xmlElement = extensions.getXmlElement();
|
||||
if (xmlElement == null) return;
|
||||
|
||||
IdeaPlugin ideaPlugin = extensions.getParentOfType(IdeaPlugin.class, true);
|
||||
|
||||
if (ideaPlugin == null) return;
|
||||
|
||||
String epPrefix = extensions.getEpPrefix();
|
||||
for (IdeaPlugin plugin : getVisiblePlugins(ideaPlugin)) {
|
||||
final String pluginId = StringUtil.notNullize(plugin.getPluginId(), "com.intellij");
|
||||
for (ExtensionPoints points : plugin.getExtensionPoints()) {
|
||||
for (ExtensionPoint point : points.getExtensionPoints()) {
|
||||
registerExtensionPoint(registrar, point, epPrefix, pluginId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsStubs() {
|
||||
return false;
|
||||
}
|
||||
|
||||
private static Set<IdeaPlugin> getVisiblePlugins(IdeaPlugin ideaPlugin) {
|
||||
Set<IdeaPlugin> result = ContainerUtil.newHashSet();
|
||||
MultiMap<String, IdeaPlugin> byId = getPluginMap(ideaPlugin.getManager().getProject());
|
||||
@@ -385,11 +309,10 @@ public class ExtensionDomExtender extends DomExtender<Extensions> {
|
||||
return PsiTypesUtil.getPsiClass(elementType);
|
||||
}
|
||||
|
||||
|
||||
public static Collection<String> getDependencies(IdeaPlugin ideaPlugin) {
|
||||
Set<String> result = new HashSet<String>();
|
||||
|
||||
result.add(PluginManager.CORE_PLUGIN_ID);
|
||||
result.add(PluginManagerCore.CORE_PLUGIN_ID);
|
||||
|
||||
for (Dependency dependency : ideaPlugin.getDependencies()) {
|
||||
ContainerUtil.addIfNotNull(dependency.getStringValue(), result);
|
||||
@@ -422,10 +345,84 @@ public class ExtensionDomExtender extends DomExtender<Extensions> {
|
||||
return result;
|
||||
}
|
||||
|
||||
public void registerExtensions(@NotNull final Extensions extensions, @NotNull final DomExtensionsRegistrar registrar) {
|
||||
final XmlElement xmlElement = extensions.getXmlElement();
|
||||
if (xmlElement == null) return;
|
||||
|
||||
IdeaPlugin ideaPlugin = extensions.getParentOfType(IdeaPlugin.class, true);
|
||||
|
||||
if (ideaPlugin == null) return;
|
||||
|
||||
String epPrefix = extensions.getEpPrefix();
|
||||
for (IdeaPlugin plugin : getVisiblePlugins(ideaPlugin)) {
|
||||
final String pluginId = StringUtil.notNullize(plugin.getPluginId(), "com.intellij");
|
||||
for (ExtensionPoints points : plugin.getExtensionPoints()) {
|
||||
for (ExtensionPoint point : points.getExtensionPoints()) {
|
||||
registerExtensionPoint(registrar, point, epPrefix, pluginId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsStubs() {
|
||||
return false;
|
||||
}
|
||||
|
||||
interface SimpleTagValue extends DomElement {
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
@TagValue
|
||||
String getTagValue();
|
||||
}
|
||||
|
||||
private static class MyRequired implements Required {
|
||||
@Override
|
||||
public boolean value() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean nonEmpty() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean identifier() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Annotation> annotationType() {
|
||||
return Required.class;
|
||||
}
|
||||
}
|
||||
|
||||
private static class MyExtendClass extends ExtendClassImpl {
|
||||
private final String myInterfaceName;
|
||||
|
||||
private MyExtendClass(String interfaceName) {
|
||||
myInterfaceName = interfaceName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean allowAbstract() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean allowInterface() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean allowEnum() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String value() {
|
||||
return myInterfaceName;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
<idea-plugin>
|
||||
<extensionPoints>
|
||||
<extensionPoint name="extension" beanClass="foo.Bar"/>
|
||||
</extensionPoints>
|
||||
</idea-plugin>
|
||||
@@ -0,0 +1,10 @@
|
||||
<idea-plugin xmlns:xi="http://www.w3.org/2001/XInclude" version="2">
|
||||
|
||||
<id>com.intellij</id>
|
||||
|
||||
<extensionPoints>
|
||||
<extensionPoint name="bar"/>
|
||||
<xi:include href="/extensionPoints.xml" xpointer="xpointer(/extensionPoints/*)"/>
|
||||
</extensionPoints>
|
||||
|
||||
</idea-plugin>
|
||||
@@ -0,0 +1,15 @@
|
||||
<idea-plugin version="2">
|
||||
|
||||
<id>foo</id>
|
||||
|
||||
<extensionPoints>
|
||||
<extensionPoint name="bar"/>
|
||||
</extensionPoints>
|
||||
|
||||
<extensions defaultExtensionNs="foo">
|
||||
|
||||
<bar/>
|
||||
<extension/>
|
||||
</extensions>
|
||||
|
||||
</idea-plugin>
|
||||
@@ -44,6 +44,13 @@ public class PluginXmlDomStubsTest extends DomStubTest {
|
||||
);
|
||||
}
|
||||
|
||||
public void testXInclude() throws Exception {
|
||||
prepareFile("pluginWithXInclude-extensionPoints.xml");
|
||||
prepareFile("pluginWithXInclude-main.xml");
|
||||
prepareFile("pluginWithXInclude.xml");
|
||||
myFixture.testHighlighting("pluginWithXInclude.xml");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getBasePath() {
|
||||
return PluginPathManager.getPluginHomePathRelative("devkit") + "/testData/pluginXmlDomStubs";
|
||||
|
||||
@@ -137,6 +137,7 @@
|
||||
<gotoTargetRendererProvider implementation="com.jetbrains.python.codeInsight.PyGotoTargetRendererProvider"/>
|
||||
|
||||
<typeHierarchyProvider language="Python" implementationClass="com.jetbrains.python.hierarchy.PyTypeHierachyProvider"/>
|
||||
<callHierarchyProvider language="Python" implementationClass="com.jetbrains.python.hierarchy.call.PyCallHierarchyProvider"/>
|
||||
<highlightUsagesHandlerFactory implementation="com.jetbrains.python.codeInsight.highlighting.PyHighlightExitPointsHandlerFactory"/>
|
||||
|
||||
<joinLinesHandler implementation="com.jetbrains.python.editor.PyJoinLinesHandler"/>
|
||||
@@ -476,6 +477,9 @@
|
||||
<projectService serviceInterface="com.jetbrains.python.debugger.PySignatureCacheManager"
|
||||
serviceImplementation="com.jetbrains.python.debugger.PySignatureCacheManagerImpl"/>
|
||||
|
||||
<projectService serviceInterface="com.jetbrains.python.hierarchy.call.PyStaticCallDataManager"
|
||||
serviceImplementation="com.jetbrains.python.hierarchy.call.PyStaticCallDataManagerImpl"/>
|
||||
|
||||
<projectService serviceInterface="com.jetbrains.python.psi.PyPsiFacade"
|
||||
serviceImplementation="com.jetbrains.python.psi.impl.PyPsiFacadeImpl"/>
|
||||
<applicationService serviceInterface="com.jetbrains.python.packaging.PyPackageManagers"
|
||||
@@ -647,7 +651,29 @@
|
||||
<reference ref="CompareFileWithEditor"/>
|
||||
</group>
|
||||
|
||||
<group id="PyCallHierarchyPopupMenu">
|
||||
<action id="CallHierarchy.BaseOnThisType" text="Base On This Function" class="com.jetbrains.python.hierarchy.call.PyCallHierarchyBrowser$BaseOnThisFunctionAction" description="Show call hierarchy for selected method"/>
|
||||
<reference ref="EditSource"/>
|
||||
<separator/>
|
||||
<reference ref="FindUsages"/>
|
||||
<reference ref="RefactoringMenu"/>
|
||||
<separator/>
|
||||
<reference ref="AddToFavorites"/>
|
||||
<separator/>
|
||||
<reference ref="RunContextPopupGroup"/>
|
||||
<separator/>
|
||||
|
||||
<reference ref="ReformatCode"/>
|
||||
<reference ref="OptimizeImports"/>
|
||||
<separator/>
|
||||
<reference ref="VersionControlsGroup"/>
|
||||
|
||||
<separator/>
|
||||
<reference ref="ExternalToolsGroup"/>
|
||||
<separator/>
|
||||
<reference ref="CompareTwoFiles"/>
|
||||
<reference ref="CompareFileWithEditor"/>
|
||||
</group>
|
||||
|
||||
<action id="com.jetbrains.python.console.PyOpenDebugConsoleAction"
|
||||
class="com.jetbrains.python.console.PyOpenDebugConsoleAction"
|
||||
|
||||
@@ -27,12 +27,12 @@ import java.util.List;
|
||||
public class PyFunctionFindUsagesHandler extends FindUsagesHandler {
|
||||
private final List<PsiElement> myAllElements;
|
||||
|
||||
protected PyFunctionFindUsagesHandler(@NotNull PsiElement psiElement) {
|
||||
public PyFunctionFindUsagesHandler(@NotNull PsiElement psiElement) {
|
||||
super(psiElement);
|
||||
myAllElements = null;
|
||||
}
|
||||
|
||||
protected PyFunctionFindUsagesHandler(@NotNull PsiElement psiElement, List<PsiElement> allElements) {
|
||||
public PyFunctionFindUsagesHandler(@NotNull PsiElement psiElement, List<PsiElement> allElements) {
|
||||
super(psiElement);
|
||||
myAllElements = allElements;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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.hierarchy.call;
|
||||
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.jetbrains.python.psi.PyElement;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
|
||||
/**
|
||||
* @author novokrest
|
||||
*/
|
||||
public interface PyCallDataManager {
|
||||
Collection<PsiElement> getCallees(@NotNull PyElement pyElement);
|
||||
|
||||
Collection<PsiElement> getCallers(@NotNull PyElement pyElement);
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* 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.hierarchy.call;
|
||||
|
||||
import com.intellij.ide.hierarchy.CallHierarchyBrowserBase;
|
||||
import com.intellij.ide.hierarchy.HierarchyNodeDescriptor;
|
||||
import com.intellij.ide.hierarchy.HierarchyTreeStructure;
|
||||
import com.intellij.ide.util.treeView.NodeDescriptor;
|
||||
import com.intellij.openapi.actionSystem.ActionGroup;
|
||||
import com.intellij.openapi.actionSystem.ActionManager;
|
||||
import com.intellij.openapi.actionSystem.ActionPlaces;
|
||||
import com.intellij.openapi.actionSystem.IdeActions;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.ui.PopupHandler;
|
||||
import com.jetbrains.python.hierarchy.PyHierarchyUtils;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.psi.PyFile;
|
||||
import com.jetbrains.python.psi.PyFunction;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.Comparator;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author novokrest
|
||||
*/
|
||||
public class PyCallHierarchyBrowser extends CallHierarchyBrowserBase {
|
||||
private static final Logger LOG = Logger.getInstance("#com.jetbrains.python.hierarchy.call.PyCallHierarchyBrowser");
|
||||
private static final String GROUP_PY_CALL_HIERARCHY_POPUP = "PyCallHierarchyPopupMenu";
|
||||
|
||||
public PyCallHierarchyBrowser(PsiElement function) {
|
||||
super(function.getProject(), function);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected PsiElement getElementFromDescriptor(@NotNull HierarchyNodeDescriptor descriptor) {
|
||||
if (descriptor instanceof PyCallHierarchyNodeDescriptor) {
|
||||
PyCallHierarchyNodeDescriptor nodeDescriptor = (PyCallHierarchyNodeDescriptor)descriptor;
|
||||
return nodeDescriptor.getEnclosingElement();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PsiElement getOpenFileElementFromDescriptor(@NotNull HierarchyNodeDescriptor descriptor) {
|
||||
if (descriptor instanceof PyCallHierarchyNodeDescriptor) {
|
||||
PyCallHierarchyNodeDescriptor nodeDescriptor = (PyCallHierarchyNodeDescriptor)descriptor;
|
||||
return nodeDescriptor.getTargetElement();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void createTrees(@NotNull Map<String, JTree> type2TreeMap) {
|
||||
ActionGroup group = (ActionGroup)ActionManager.getInstance().getAction(GROUP_PY_CALL_HIERARCHY_POPUP);
|
||||
final JTree tree1 = createTree(false);
|
||||
PopupHandler.installPopupHandler(tree1, group, ActionPlaces.CALL_HIERARCHY_VIEW_POPUP, ActionManager.getInstance());
|
||||
final BaseOnThisFunctionAction baseOnThisFunctionAction = new BaseOnThisFunctionAction();
|
||||
baseOnThisFunctionAction
|
||||
.registerCustomShortcutSet(ActionManager.getInstance().getAction(IdeActions.ACTION_CALL_HIERARCHY).getShortcutSet(), tree1);
|
||||
type2TreeMap.put(CALLER_TYPE, tree1);
|
||||
|
||||
final JTree tree2 = createTree(false);
|
||||
PopupHandler.installPopupHandler(tree2, group, ActionPlaces.CALL_HIERARCHY_VIEW_POPUP, ActionManager.getInstance());
|
||||
baseOnThisFunctionAction
|
||||
.registerCustomShortcutSet(ActionManager.getInstance().getAction(IdeActions.ACTION_CALL_HIERARCHY).getShortcutSet(), tree2);
|
||||
type2TreeMap.put(CALLEE_TYPE, tree2);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isApplicableElement(@NotNull PsiElement element) {
|
||||
return element instanceof PyFunction || element instanceof PyClass || element instanceof PyFile;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected HierarchyTreeStructure createHierarchyTreeStructure(@NotNull String typeName, @NotNull PsiElement psiElement) {
|
||||
if (CALLER_TYPE.equals(typeName)) {
|
||||
return new PyCallerFunctionTreeStructure(myProject, psiElement, getCurrentScopeType());
|
||||
}
|
||||
else if (CALLEE_TYPE.equals(typeName)) {
|
||||
return new PyCalleeFunctionTreeStructure(myProject, psiElement, getCurrentScopeType());
|
||||
}
|
||||
else {
|
||||
LOG.error("unexpected type: " + typeName);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected Comparator<NodeDescriptor> getComparator() {
|
||||
return PyHierarchyUtils.getComparator(myProject);
|
||||
}
|
||||
|
||||
public static final class BaseOnThisFunctionAction extends CallHierarchyBrowserBase.BaseOnThisMethodAction {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
/*
|
||||
* 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.hierarchy.call;
|
||||
|
||||
import com.intellij.codeInsight.highlighting.HighlightManager;
|
||||
import com.intellij.icons.AllIcons;
|
||||
import com.intellij.ide.IdeBundle;
|
||||
import com.intellij.ide.hierarchy.HierarchyNodeDescriptor;
|
||||
import com.intellij.ide.util.treeView.NodeDescriptor;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.colors.EditorColors;
|
||||
import com.intellij.openapi.editor.colors.EditorColorsManager;
|
||||
import com.intellij.openapi.editor.markup.RangeHighlighter;
|
||||
import com.intellij.openapi.editor.markup.TextAttributes;
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.ui.util.CompositeAppearance;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.Iconable;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.pom.Navigatable;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.PsiReference;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtilBase;
|
||||
import com.intellij.ui.LayeredIcon;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author novokrest
|
||||
*/
|
||||
public class PyCallHierarchyNodeDescriptor extends HierarchyNodeDescriptor implements Navigatable {
|
||||
private int myUsageCount = 1;
|
||||
private final List<PsiReference> myReferences = new ArrayList<PsiReference>();
|
||||
private final boolean myNavigateToReference;
|
||||
|
||||
protected PyCallHierarchyNodeDescriptor(@NotNull Project project,
|
||||
@Nullable NodeDescriptor parentDescriptor,
|
||||
@NotNull PsiElement element,
|
||||
boolean isBase,
|
||||
boolean navigateToReference) {
|
||||
super(project, parentDescriptor, element, isBase);
|
||||
myNavigateToReference = navigateToReference;
|
||||
}
|
||||
|
||||
public final void incrementCount() {
|
||||
myUsageCount++;
|
||||
}
|
||||
|
||||
public final PyFunction getPyFunction() {
|
||||
return (PyFunction)myElement;
|
||||
}
|
||||
|
||||
public final PyElement getEnclosingElement() {
|
||||
return PsiTreeUtil.getNonStrictParentOfType(myElement, PyFunction.class, PyClass.class, PyFile.class);
|
||||
}
|
||||
|
||||
public final PsiElement getTargetElement() {
|
||||
return myElement;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return myElement != null && myElement.isValid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean update() {
|
||||
final CompositeAppearance oldText = myHighlightedText;
|
||||
final Icon oldIcon = getIcon();
|
||||
|
||||
int flags = Iconable.ICON_FLAG_VISIBILITY;
|
||||
if (isMarkReadOnly()) {
|
||||
flags |= Iconable.ICON_FLAG_READ_STATUS;
|
||||
}
|
||||
|
||||
final PsiElement element = getEnclosingElement();
|
||||
if (element == null) {
|
||||
final String invalidPrefix = IdeBundle.message("node.hierarchy.invalid");
|
||||
if (!myHighlightedText.getText().startsWith(invalidPrefix)) {
|
||||
myHighlightedText.getBeginning().addText(invalidPrefix, HierarchyNodeDescriptor.getInvalidPrefixAttributes());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean changes = super.update();
|
||||
|
||||
Icon newIcon = element.getIcon(flags);
|
||||
if (changes && myIsBase) {
|
||||
final LayeredIcon icon = new LayeredIcon(2);
|
||||
icon.setIcon(newIcon, 0);
|
||||
icon.setIcon(AllIcons.Hierarchy.Base, 1, -AllIcons.Hierarchy.Base.getIconWidth() / 2, 0);
|
||||
newIcon = icon;
|
||||
}
|
||||
setIcon(newIcon);
|
||||
|
||||
myHighlightedText = new CompositeAppearance();
|
||||
final TextAttributes mainTextAttributes = myColor != null ? null : new TextAttributes(myColor, null, null, null, Font.PLAIN);
|
||||
|
||||
PyElementVisitor visitor = new PyElementVisitor() {
|
||||
@Override
|
||||
public void visitPyClass(PyClass pyClass) {
|
||||
myHighlightedText.getEnding().addText(pyClass.getName(), mainTextAttributes);
|
||||
myHighlightedText.getEnding().addText("(" + pyClass.getContainingFile().getName() + ")",
|
||||
HierarchyNodeDescriptor.getPackageNameAttributes());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPyFunction(PyFunction function) {
|
||||
final StringBuilder buffer = new StringBuilder();
|
||||
final PyClass pyClass = function.getContainingClass();
|
||||
if (pyClass != null) {
|
||||
buffer.append(pyClass.getName());
|
||||
buffer.append('.');
|
||||
}
|
||||
buffer.append(function.getName());
|
||||
|
||||
myHighlightedText.getEnding().addText(buffer.toString(), mainTextAttributes);
|
||||
myHighlightedText.getEnding().addText("(" + function.getContainingFile().getName() + ")",
|
||||
HierarchyNodeDescriptor.getPackageNameAttributes());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPyLambdaExpression(PyLambdaExpression node) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPyFile(PyFile pyFile) {
|
||||
myHighlightedText.getEnding().addText(pyFile.getName(), mainTextAttributes);
|
||||
}
|
||||
};
|
||||
|
||||
element.accept(visitor);
|
||||
|
||||
if (myUsageCount > 1) {
|
||||
myHighlightedText.getEnding()
|
||||
.addText(IdeBundle.message("node.call.hierarchy.N.usages", myUsageCount), HierarchyNodeDescriptor.getUsageCountPrefixAttributes());
|
||||
}
|
||||
|
||||
myName = myHighlightedText.getText();
|
||||
if (!Comparing.equal(myHighlightedText, oldText)
|
||||
|| !Comparing.equal(getIcon(), oldIcon)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return changes;
|
||||
}
|
||||
|
||||
public void addReference(final PsiReference reference) {
|
||||
myReferences.add(reference);
|
||||
}
|
||||
|
||||
public boolean hasReference(final PsiReference reference) {
|
||||
return myReferences.contains(reference);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void navigate(boolean requestFocus) {
|
||||
if (!myNavigateToReference) {
|
||||
if (myElement instanceof Navigatable && ((Navigatable)myElement).canNavigate()) {
|
||||
((Navigatable)myElement).navigate(requestFocus);
|
||||
}
|
||||
return;
|
||||
}
|
||||
final PsiReference firstReference = myReferences.get(0);
|
||||
final PsiElement element = firstReference.getElement();
|
||||
if (element == null) return;
|
||||
final PsiElement callElement = element.getParent();
|
||||
if (callElement instanceof Navigatable && ((Navigatable)callElement).canNavigate()) {
|
||||
((Navigatable)callElement).navigate(requestFocus);
|
||||
}
|
||||
else {
|
||||
final PsiFile file = callElement.getContainingFile();
|
||||
if (file == null || file.getVirtualFile() == null) return;
|
||||
FileEditorManager.getInstance(myProject).openFile(file.getVirtualFile(), requestFocus);
|
||||
}
|
||||
|
||||
Editor editor = PsiUtilBase.findEditor(callElement);
|
||||
if (editor != null) {
|
||||
HighlightManager highlightManager = HighlightManager.getInstance(myProject);
|
||||
EditorColorsManager colorsManager = EditorColorsManager.getInstance();
|
||||
TextAttributes attributes = colorsManager.getGlobalScheme().getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES);
|
||||
List<RangeHighlighter> highlighters = new ArrayList<RangeHighlighter>();
|
||||
for (PsiReference reference : myReferences) {
|
||||
final PsiElement eachElement = reference.getElement();
|
||||
if (eachElement != null) {
|
||||
PsiElement eachCallElement = eachElement.getParent();
|
||||
if (eachCallElement != null) {
|
||||
final TextRange textRange = eachCallElement.getTextRange();
|
||||
highlightManager
|
||||
.addRangeHighlight(editor, textRange.getStartOffset(), textRange.getEndOffset(), attributes, false, highlighters);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canNavigate() {
|
||||
if (!myNavigateToReference) {
|
||||
return myElement instanceof Navigatable && ((Navigatable)myElement).canNavigate();
|
||||
}
|
||||
if (myReferences.isEmpty()) return false;
|
||||
final PsiReference reference = myReferences.get(0);
|
||||
final PsiElement callElement = reference.getElement().getParent();
|
||||
if (callElement == null || !callElement.isValid()) return false;
|
||||
if (!(callElement instanceof Navigatable) || !((Navigatable)callElement).canNavigate()) {
|
||||
final PsiFile file = callElement.getContainingFile();
|
||||
if (file == null) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canNavigateToSource() {
|
||||
return canNavigate();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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.hierarchy.call;
|
||||
|
||||
import com.intellij.codeInsight.TargetElementUtilBase;
|
||||
import com.intellij.ide.hierarchy.CallHierarchyBrowserBase;
|
||||
import com.intellij.ide.hierarchy.HierarchyBrowser;
|
||||
import com.intellij.ide.hierarchy.HierarchyProvider;
|
||||
import com.intellij.openapi.actionSystem.CommonDataKeys;
|
||||
import com.intellij.openapi.actionSystem.DataContext;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.PsiDocumentManager;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.psi.PyFile;
|
||||
import com.jetbrains.python.psi.PyFunction;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* @author novokrest
|
||||
*/
|
||||
public class PyCallHierarchyProvider implements HierarchyProvider {
|
||||
@Nullable
|
||||
@Override
|
||||
public PsiElement getTarget(@NotNull DataContext dataContext) {
|
||||
Project project = CommonDataKeys.PROJECT.getData(dataContext);
|
||||
if (project == null) return null;
|
||||
|
||||
PsiElement element = CommonDataKeys.PSI_ELEMENT.getData(dataContext);
|
||||
if (element == null) {
|
||||
Editor editor = CommonDataKeys.EDITOR.getData(dataContext);
|
||||
if (editor != null) {
|
||||
PsiFile file = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument());
|
||||
if (file == null) return null;
|
||||
|
||||
element = TargetElementUtilBase.findTargetElement(editor, TargetElementUtilBase.ELEMENT_NAME_ACCEPTED |
|
||||
TargetElementUtilBase.REFERENCED_ELEMENT_ACCEPTED |
|
||||
TargetElementUtilBase.LOOKUP_ITEM_ACCEPTED);
|
||||
if (element instanceof PyFunction || element instanceof PyClass || element instanceof PyFile) {
|
||||
return element;
|
||||
}
|
||||
|
||||
element = file.findElementAt(editor.getCaretModel().getOffset());
|
||||
}
|
||||
}
|
||||
return PsiTreeUtil.getNonStrictParentOfType(element, PyFunction.class, PyClass.class, PyFile.class);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public HierarchyBrowser createHierarchyBrowser(PsiElement target) {
|
||||
return new PyCallHierarchyBrowser(target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void browserActivated(@NotNull HierarchyBrowser hierarchyBrowser) {
|
||||
((PyCallHierarchyBrowser)hierarchyBrowser).changeView(CallHierarchyBrowserBase.CALLER_TYPE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* 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.hierarchy.call;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.intellij.ide.hierarchy.HierarchyNodeDescriptor;
|
||||
import com.intellij.ide.hierarchy.HierarchyTreeStructure;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.psi.PyElement;
|
||||
import com.jetbrains.python.psi.PyFile;
|
||||
import com.jetbrains.python.psi.PyFunction;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author novokrest
|
||||
*/
|
||||
public class PyCalleeFunctionTreeStructure extends HierarchyTreeStructure {
|
||||
private final String myScopeType;
|
||||
|
||||
public PyCalleeFunctionTreeStructure(Project project, PsiElement element, String currentScopeType) {
|
||||
super(project, new PyCallHierarchyNodeDescriptor(project, null, element, true, false));
|
||||
myScopeType = currentScopeType;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected Object[] buildChildren(@NotNull HierarchyNodeDescriptor descriptor) {
|
||||
final PyElement element = ((PyCallHierarchyNodeDescriptor)descriptor).getEnclosingElement();
|
||||
final boolean isCallable = element instanceof PyFunction || element instanceof PyClass || element instanceof PyFile;
|
||||
HierarchyNodeDescriptor nodeDescriptor = getBaseDescriptor();
|
||||
if (!isCallable || nodeDescriptor == null) {
|
||||
return ArrayUtil.EMPTY_OBJECT_ARRAY;
|
||||
}
|
||||
|
||||
final List<PsiElement> callees = Lists.newArrayList();
|
||||
|
||||
PyCallDataManager[] functionManagers = {
|
||||
// TODO: Add dynamic call data manager
|
||||
PyStaticCallDataManager.getInstance(myProject),
|
||||
};
|
||||
|
||||
for (PyCallDataManager functionManager : functionManagers) {
|
||||
callees.addAll(functionManager.getCallees(element));
|
||||
}
|
||||
|
||||
final Map<PsiElement, PyCallHierarchyNodeDescriptor> calleeToDescriptorMap = new HashMap<PsiElement, PyCallHierarchyNodeDescriptor>();
|
||||
final List<PyCallHierarchyNodeDescriptor> descriptors = Lists.newArrayList();
|
||||
PsiElement baseClass = element instanceof PyFunction ? ((PyFunction)element).getContainingClass() : null;
|
||||
|
||||
for (PsiElement callee : callees) {
|
||||
if (baseClass != null && !isInScope(baseClass, callee, myScopeType)) continue;
|
||||
|
||||
PyCallHierarchyNodeDescriptor calleeDescriptor = calleeToDescriptorMap.get(callee);
|
||||
if (calleeDescriptor == null) {
|
||||
calleeDescriptor = new PyCallHierarchyNodeDescriptor(myProject, null, callee, false, false);
|
||||
calleeToDescriptorMap.put(callee, calleeDescriptor);
|
||||
descriptors.add(calleeDescriptor);
|
||||
}
|
||||
}
|
||||
|
||||
return ArrayUtil.toObjectArray(descriptors);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAlwaysShowPlus() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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.hierarchy.call;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.intellij.ide.hierarchy.HierarchyNodeDescriptor;
|
||||
import com.intellij.ide.hierarchy.HierarchyTreeStructure;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.containers.HashMap;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.psi.PyElement;
|
||||
import com.jetbrains.python.psi.PyFile;
|
||||
import com.jetbrains.python.psi.PyFunction;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author novokrest
|
||||
*/
|
||||
public class PyCallerFunctionTreeStructure extends HierarchyTreeStructure {
|
||||
private final String myScopeType;
|
||||
|
||||
public PyCallerFunctionTreeStructure(Project project, PsiElement element, String currentScopeType) {
|
||||
super(project, new PyCallHierarchyNodeDescriptor(project, null, element, true, false));
|
||||
myScopeType = currentScopeType;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected Object[] buildChildren(@NotNull HierarchyNodeDescriptor descriptor) {
|
||||
final PyElement element = ((PyCallHierarchyNodeDescriptor)descriptor).getEnclosingElement();
|
||||
final boolean isCallable = element instanceof PyFunction || element instanceof PyClass || element instanceof PyFile;
|
||||
HierarchyNodeDescriptor nodeDescriptor = getBaseDescriptor();
|
||||
if (!isCallable || nodeDescriptor == null) {
|
||||
return ArrayUtil.EMPTY_OBJECT_ARRAY;
|
||||
}
|
||||
|
||||
final List<PsiElement> callers = Lists.newArrayList();
|
||||
|
||||
PyCallDataManager[] functionManagers = {
|
||||
// TODO: Add dynamic call data manager
|
||||
PyStaticCallDataManager.getInstance(myProject),
|
||||
};
|
||||
|
||||
for (PyCallDataManager functionManager : functionManagers) {
|
||||
callers.addAll(functionManager.getCallers(element));
|
||||
}
|
||||
|
||||
final HashMap<PsiElement, PyCallHierarchyNodeDescriptor> callerToDescriptorMap = new HashMap<PsiElement, PyCallHierarchyNodeDescriptor>();
|
||||
final List<PyCallHierarchyNodeDescriptor> descriptors = Lists.newArrayList();
|
||||
PsiElement baseClass = element instanceof PyFunction ? ((PyFunction)element).getContainingClass() : null;
|
||||
|
||||
for (PsiElement caller : callers) {
|
||||
if (baseClass != null && !isInScope(baseClass, caller, myScopeType)) continue;
|
||||
|
||||
PyCallHierarchyNodeDescriptor callerDescriptor = callerToDescriptorMap.get(caller);
|
||||
if (callerDescriptor == null) {
|
||||
callerDescriptor = new PyCallHierarchyNodeDescriptor(myProject, null, caller, false, false);
|
||||
callerToDescriptorMap.put(caller, callerDescriptor);
|
||||
descriptors.add(callerDescriptor);
|
||||
}
|
||||
}
|
||||
|
||||
return ArrayUtil.toObjectArray(descriptors);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAlwaysShowPlus() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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.hierarchy.call;
|
||||
|
||||
import com.intellij.openapi.components.ServiceManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
|
||||
/**
|
||||
* @author novokrest
|
||||
*/
|
||||
public abstract class PyStaticCallDataManager implements PyCallDataManager {
|
||||
public static PyCallDataManager getInstance(Project project) {
|
||||
return ServiceManager.getService(project, PyStaticCallDataManager.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* 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.hierarchy.call;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.intellij.find.findUsages.FindUsagesHandler;
|
||||
import com.intellij.find.findUsages.FindUsagesOptions;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.usageView.UsageInfo;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.CommonProcessors;
|
||||
import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.findUsages.PyClassFindUsagesHandler;
|
||||
import com.jetbrains.python.findUsages.PyFunctionFindUsagesHandler;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.impl.PyBuiltinCache;
|
||||
import com.jetbrains.python.psi.resolve.PyResolveContext;
|
||||
import com.jetbrains.python.psi.search.PySuperMethodsSearch;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author novokrest
|
||||
*/
|
||||
public class PyStaticCallDataManagerImpl extends PyStaticCallDataManager {
|
||||
@Override
|
||||
public Collection<PsiElement> getCallees(@NotNull PyElement element) {
|
||||
final List<PsiElement> callees = Lists.newArrayList();
|
||||
|
||||
final PyRecursiveElementVisitor visitor = new PyRecursiveElementVisitor() {
|
||||
@Override
|
||||
public void visitPyParameterList(PyParameterList node) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPyLambdaExpression(PyLambdaExpression node) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPyFunction(PyFunction innerFunction) {
|
||||
for (PyParameter parameter : innerFunction.getParameterList().getParameters()) {
|
||||
PsiElement defaultValue = parameter.getDefaultValue();
|
||||
if (defaultValue != null) {
|
||||
defaultValue.accept(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPyCallExpression(PyCallExpression callExpression) {
|
||||
super.visitPyCallExpression(callExpression);
|
||||
PsiElement calleeFunction = callExpression.resolveCalleeFunction(PyResolveContext.defaultContext());
|
||||
if (calleeFunction instanceof PyFunction) {
|
||||
callees.add(calleeFunction);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
visitor.visitElement(element);
|
||||
|
||||
return callees;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<PsiElement> getCallers(@NotNull PyElement pyElement) {
|
||||
final List<PsiElement> callers = Lists.newArrayList();
|
||||
final Collection<UsageInfo> usages = findUsages(pyElement);
|
||||
|
||||
for (UsageInfo usage : usages) {
|
||||
PsiElement element = usage.getElement();
|
||||
if (element == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
element = element.getParent();
|
||||
while (element instanceof PyParenthesizedExpression) {
|
||||
element = element.getParent();
|
||||
}
|
||||
|
||||
if (element instanceof PyCallExpression) {
|
||||
PsiElement caller = PsiTreeUtil.getParentOfType(element, PyParameterList.class, PyFunction.class);
|
||||
if (caller instanceof PyFunction) {
|
||||
callers.add(caller);
|
||||
}
|
||||
else if (caller instanceof PyParameterList) {
|
||||
PsiElement innerFunction = PsiTreeUtil.getParentOfType(caller, PyFunction.class);
|
||||
PsiElement outerFunction = PsiTreeUtil.getParentOfType(innerFunction, PyFunction.class);
|
||||
if (innerFunction != null && outerFunction != null) {
|
||||
callers.add(outerFunction);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return callers;
|
||||
}
|
||||
|
||||
private static Collection<UsageInfo> findUsages(@NotNull final PsiElement element) {
|
||||
final FindUsagesHandler handler = createFindUsageHandler(element);
|
||||
if (handler == null) {
|
||||
return Lists.newArrayList();
|
||||
}
|
||||
final CommonProcessors.CollectProcessor<UsageInfo> processor = new CommonProcessors.CollectProcessor<UsageInfo>();
|
||||
final PsiElement[] psiElements = ArrayUtil.mergeArrays(handler.getPrimaryElements(), handler.getSecondaryElements());
|
||||
final FindUsagesOptions options = handler.getFindUsagesOptions(null);
|
||||
for (PsiElement psiElement : psiElements) {
|
||||
handler.processElementUsages(psiElement, processor, options);
|
||||
}
|
||||
return processor.getResults();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see {@link com.jetbrains.python.findUsages.PyFindUsagesHandlerFactory#createFindUsagesHandler(com.intellij.psi.PsiElement, boolean) createFindUsagesHandler}
|
||||
*/
|
||||
@Nullable
|
||||
private static FindUsagesHandler createFindUsageHandler(@NotNull final PsiElement element) {
|
||||
if (element instanceof PyFunction) {
|
||||
final Collection<PsiElement> superMethods = PySuperMethodsSearch.search((PyFunction)element, true).findAll();
|
||||
if (superMethods.size() > 0) {
|
||||
final PsiElement next = superMethods.iterator().next();
|
||||
if (next instanceof PyFunction && !isInObject((PyFunction)next)) {
|
||||
List<PsiElement> allMethods = Lists.newArrayList();
|
||||
allMethods.add(element);
|
||||
allMethods.addAll(superMethods);
|
||||
|
||||
return new PyFunctionFindUsagesHandler(element, allMethods);
|
||||
}
|
||||
}
|
||||
return new PyFunctionFindUsagesHandler(element);
|
||||
}
|
||||
if (element instanceof PyClass) {
|
||||
return new PyClassFindUsagesHandler((PyClass)element);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see {@link com.jetbrains.python.findUsages.PyFindUsagesHandlerFactory#isInObject(com.jetbrains.python.psi.PyFunction) isInObject}
|
||||
*/
|
||||
private static boolean isInObject(PyFunction fun) {
|
||||
final PyClass containingClass = fun.getContainingClass();
|
||||
if (containingClass == null) {
|
||||
return false;
|
||||
}
|
||||
return (PyNames.FAKE_OLD_BASE.equals(containingClass.getName()) ||
|
||||
(PyNames.OBJECT.equals(containingClass.getName()) && PyBuiltinCache.getInstance(fun).isBuiltin(containingClass)));
|
||||
}
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
<node text="target_func(file_1.py)" base="true"/>
|
||||
+1
@@ -0,0 +1 @@
|
||||
<node text="target_func(file_1.py)" base="true"/>
|
||||
@@ -0,0 +1,8 @@
|
||||
def target_func():
|
||||
pass
|
||||
|
||||
def func1(f):
|
||||
f()
|
||||
|
||||
def func2(f):
|
||||
return f
|
||||
@@ -0,0 +1,8 @@
|
||||
from file_1 import *
|
||||
|
||||
target_<caret>func()
|
||||
func1(target_func)
|
||||
func1(target_func())
|
||||
func2(target_func)
|
||||
func2(target_func())
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
<node text="A.__init__(main.py)" base="true">
|
||||
<node text="invoke1(main.py)">
|
||||
<node text="A.method1(main.py)"/>
|
||||
</node>
|
||||
<node text="invoke2(main.py)">
|
||||
<node text="A.method2(main.py)"/>
|
||||
</node>
|
||||
</node>
|
||||
@@ -0,0 +1,6 @@
|
||||
<node text="A.__init__(main.py)" base="true">
|
||||
<node text="invokeA(main.py)">
|
||||
<node text="C.bar(main.py)"/>
|
||||
</node>
|
||||
<node text="C.bar(main.py)"/>
|
||||
</node>
|
||||
@@ -0,0 +1,32 @@
|
||||
class A():
|
||||
def __init__(self):
|
||||
invoke1(self)
|
||||
invoke2(self)
|
||||
|
||||
def method1(self):
|
||||
pass
|
||||
|
||||
def method2(self):
|
||||
pass
|
||||
|
||||
def invoke1(p):
|
||||
p.method1()
|
||||
|
||||
|
||||
def invoke2(p):
|
||||
p.method2()
|
||||
|
||||
|
||||
def invokeA():
|
||||
a = A()
|
||||
a.method1()
|
||||
a.method2()
|
||||
|
||||
def new_class_func():
|
||||
class C():
|
||||
def bar(self):
|
||||
invokeA(A())
|
||||
return C()
|
||||
|
||||
a = A()
|
||||
A.__init_<caret>_(a)
|
||||
+1
@@ -0,0 +1 @@
|
||||
<node text="target_func(main.py)" base="true"/>
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
<node text="target_func(main.py)" base="true">
|
||||
<node text="func2(main.py)"/>
|
||||
<node text="func4(main.py)"/>
|
||||
</node>
|
||||
@@ -0,0 +1,30 @@
|
||||
def target_func():
|
||||
pass
|
||||
|
||||
|
||||
def func1():
|
||||
def inner_func1(x=target_func):
|
||||
pass
|
||||
|
||||
return inner_func1(target_func)
|
||||
|
||||
|
||||
def func2():
|
||||
def inner_func2(x=target_func()):
|
||||
pass
|
||||
|
||||
return inner_func2(target_func)
|
||||
|
||||
|
||||
def func3(x=target_func()):
|
||||
pass
|
||||
|
||||
|
||||
def func4():
|
||||
def inner_func4(x=target_func):
|
||||
pass
|
||||
|
||||
return inner_func4(target_func())
|
||||
|
||||
|
||||
target_<caret>func()
|
||||
@@ -0,0 +1 @@
|
||||
<node text="A.target_func(main.py)" base="true"/>
|
||||
@@ -0,0 +1,4 @@
|
||||
<node text="A.target_func(main.py)" base="true">
|
||||
<node text="C.func(main.py)"/>
|
||||
<node text="foo2(main.py)"/>
|
||||
</node>
|
||||
@@ -0,0 +1,36 @@
|
||||
|
||||
class A(object):
|
||||
def target_func(self):
|
||||
pass
|
||||
|
||||
|
||||
class B(A):
|
||||
pass
|
||||
|
||||
|
||||
class C(object):
|
||||
def func(self, a):
|
||||
a.target_func()
|
||||
|
||||
|
||||
def foo1(b):
|
||||
f = b.target_func
|
||||
|
||||
|
||||
def foo2(b):
|
||||
b.target_func()
|
||||
|
||||
|
||||
def bar1(*args):
|
||||
pass
|
||||
|
||||
|
||||
def bar2(*args):
|
||||
pass
|
||||
|
||||
|
||||
b = B()
|
||||
foo1(b)
|
||||
foo2(b)
|
||||
bar1(b.target_<caret>func)
|
||||
bar2(b.target_func())
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
<node text="target_func(main.py)" base="true">
|
||||
<node text="foo(main.py)">
|
||||
<node text="bar(main.py)"/>
|
||||
</node>
|
||||
</node>
|
||||
+1
@@ -0,0 +1 @@
|
||||
<node text="target_func(main.py)" base="true"/>
|
||||
@@ -0,0 +1,9 @@
|
||||
def bar():
|
||||
pass
|
||||
|
||||
def target_func():
|
||||
def foo():
|
||||
return bar()
|
||||
foo()
|
||||
|
||||
target_<caret>func()
|
||||
@@ -0,0 +1,8 @@
|
||||
<node text="target_func(main.py)" base="true">
|
||||
<node text="func8(file_1.py)"/>
|
||||
<node text="func13(file_1.py)"/>
|
||||
<node text="func15(file_1.py)"/>
|
||||
<node text="inner(main.py)">
|
||||
<node text="func11(file_1.py)"/>
|
||||
</node>
|
||||
</node>
|
||||
@@ -0,0 +1 @@
|
||||
<node text="target_func(main.py)" base="true"/>
|
||||
@@ -0,0 +1,18 @@
|
||||
def func1(): pass
|
||||
def func2(): pass
|
||||
def func3(): pass
|
||||
def func4(): pass
|
||||
def func5(): pass
|
||||
def func6(): pass
|
||||
def func7(): pass
|
||||
def func8(): pass
|
||||
def func9(): pass
|
||||
def func10(): pass
|
||||
def func11(): pass
|
||||
def func12(): pass
|
||||
def func13(): pass
|
||||
def func14(): pass
|
||||
def func15(): pass
|
||||
def func16(): pass
|
||||
def func17(): pass
|
||||
def func18(): pass
|
||||
@@ -0,0 +1,18 @@
|
||||
from file_1 import *
|
||||
|
||||
|
||||
def target_func(x=func1, y=func2(), z=lambda: func3, w=lambda: func4()):
|
||||
p1 = lambda: func5()
|
||||
p2 = lambda: func6
|
||||
p1(), p2()
|
||||
def inner(ix=func7, iy=func8(), iz=lambda: func9, iw=lambda: func10()):
|
||||
func11()
|
||||
ip = lambda: func12()
|
||||
ip()
|
||||
func13()
|
||||
inner(func14, func15(), lambda: func16, lambda: func17())
|
||||
|
||||
return func18
|
||||
|
||||
|
||||
target_<caret>func()
|
||||
@@ -0,0 +1,12 @@
|
||||
<node text="target_func(main.py)" base="true">
|
||||
<node text="func5(file_1.py)"/>
|
||||
<node text="func3(file_1.py)"/>
|
||||
<node text="func6(file_1.py)"/>
|
||||
<node text="func7(file_1.py)"/>
|
||||
<node text="func2(file_1.py)"/>
|
||||
<node text="func1(file_1.py)"/>
|
||||
<node text="func10(file_1.py)"/>
|
||||
<node text="inner(main.py)">
|
||||
<node text="func1(file_1.py)"/>
|
||||
</node>
|
||||
</node>
|
||||
@@ -0,0 +1 @@
|
||||
<node text="target_func(main.py)" base="true"/>
|
||||
@@ -0,0 +1,10 @@
|
||||
def func1(*args): pass
|
||||
def func2(*args): pass
|
||||
def func3(*args): pass
|
||||
def func4(*args): pass
|
||||
def func5(*args): pass
|
||||
def func6(*args): pass
|
||||
def func7(*args): pass
|
||||
def func8(*args): pass
|
||||
def func9(*args): pass
|
||||
def func10(*args): pass
|
||||
@@ -0,0 +1,10 @@
|
||||
from file_1 import *
|
||||
|
||||
def target_func():
|
||||
def inner(*args):
|
||||
return func1()
|
||||
|
||||
return inner(func1(func2(func3(func4, func5()), func6(), (((func7)))()), func8), func9, func10())
|
||||
|
||||
|
||||
target_<caret>func()
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
<node text="B.target_func(main.py)" base="true">
|
||||
<node text="A.another_func(file_1.py)"/>
|
||||
</node>
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
<node text="B.target_func(main.py)" base="true">
|
||||
<node text="C.func1(main.py)"/>
|
||||
<node text="bar1(main.py)"/>
|
||||
<node text="C.func2(main.py)"/>
|
||||
</node>
|
||||
@@ -0,0 +1,8 @@
|
||||
|
||||
class A(object):
|
||||
def target_func(self, p):
|
||||
pass
|
||||
|
||||
def another_func(self):
|
||||
pass
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
from file_1 import A
|
||||
|
||||
|
||||
class B(A):
|
||||
def target_func(self, p):
|
||||
p.another_func()
|
||||
|
||||
|
||||
class C(object):
|
||||
def func1(self, a):
|
||||
a.target_func(A())
|
||||
|
||||
def func2(self):
|
||||
a = A()
|
||||
b = B()
|
||||
a.target_func(b)
|
||||
|
||||
|
||||
def bar1(a):
|
||||
a.target_func(a)
|
||||
|
||||
|
||||
def bar2(a, b):
|
||||
atf, btf = a.target_func, b.target_func
|
||||
|
||||
|
||||
bar1(A())
|
||||
bar2(A(), B())
|
||||
B().target_<caret>func(A())
|
||||
@@ -0,0 +1,3 @@
|
||||
<node text="target_func(file_1.py)" base="true">
|
||||
<node text="nothing(main.py)"/>
|
||||
</node>
|
||||
@@ -0,0 +1,3 @@
|
||||
<node text="target_func(file_1.py)" base="true">
|
||||
<node text="foo(file_1.py)"/>
|
||||
</node>
|
||||
@@ -0,0 +1,20 @@
|
||||
import main
|
||||
|
||||
def target_func():
|
||||
main.nothing(None)
|
||||
|
||||
|
||||
def nothing(x):
|
||||
pass
|
||||
|
||||
|
||||
def foo(x=bar()):
|
||||
((target_func))()
|
||||
|
||||
|
||||
def bar():
|
||||
main.nothing((target_func))
|
||||
|
||||
|
||||
def another():
|
||||
((((target_func), 1))(), 2)()
|
||||
@@ -0,0 +1,7 @@
|
||||
from file_1 import target_func
|
||||
|
||||
def nothing(x):
|
||||
pass
|
||||
|
||||
|
||||
target_<caret>func()
|
||||
@@ -0,0 +1,3 @@
|
||||
<node text="target_func(main.py)" base="true">
|
||||
<node text="func1(main.py)"/>
|
||||
</node>
|
||||
@@ -0,0 +1,3 @@
|
||||
<node text="target_func(main.py)" base="true">
|
||||
<node text="func2(main.py)"/>
|
||||
</node>
|
||||
@@ -0,0 +1,10 @@
|
||||
def func1():
|
||||
pass
|
||||
|
||||
def func2():
|
||||
target_func()
|
||||
|
||||
def target_func():
|
||||
func1()
|
||||
|
||||
target_<caret>func()
|
||||
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* 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.hierarchy;
|
||||
|
||||
|
||||
import com.intellij.ide.hierarchy.HierarchyNodeDescriptor;
|
||||
import com.intellij.ide.hierarchy.HierarchyTreeStructure;
|
||||
import junit.framework.TestCase;
|
||||
import org.jdom.Document;
|
||||
import org.jdom.Element;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author novokrest
|
||||
* @see {@link com.intellij.testFramework.codeInsight.hierarchy.HierarchyViewTestBase}
|
||||
*/
|
||||
public abstract class HierarchyTreeStructureViewer extends TestCase {
|
||||
|
||||
private static final String NODE_ELEMENT_NAME = "node";
|
||||
private static final String ANY_NODES_ELEMENT_NAME = "any";
|
||||
private static final String TEXT_ATTR_NAME = "text";
|
||||
private static final String BASE_ATTR_NAME = "base";
|
||||
|
||||
/**
|
||||
* @see {@link com.intellij.testFramework.codeInsight.hierarchy.HierarchyViewTestBase}
|
||||
*/
|
||||
public static void checkHierarchyTreeStructure(final HierarchyTreeStructure treeStructure, final Document document) {
|
||||
final HierarchyNodeDescriptor rootNodeDescriptor = (HierarchyNodeDescriptor)treeStructure.getRootElement();
|
||||
rootNodeDescriptor.update();
|
||||
final Element rootElement = document.getRootElement();
|
||||
if (rootElement == null || !NODE_ELEMENT_NAME.equals(rootElement.getName())) {
|
||||
throw new IllegalArgumentException("Incorrect root element in verification resource");
|
||||
}
|
||||
checkNodeDescriptorRecursively(treeStructure, rootNodeDescriptor, rootElement);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see {@link com.intellij.testFramework.codeInsight.hierarchy.HierarchyViewTestBase}
|
||||
*/
|
||||
private static void checkNodeDescriptorRecursively(final HierarchyTreeStructure treeStructure,
|
||||
final HierarchyNodeDescriptor descriptor,
|
||||
final Element expectedElement) {
|
||||
checkBaseNode(treeStructure, descriptor, expectedElement);
|
||||
checkContent(descriptor, expectedElement);
|
||||
checkChildren(treeStructure, descriptor, expectedElement);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see {@link com.intellij.testFramework.codeInsight.hierarchy.HierarchyViewTestBase}
|
||||
*/
|
||||
private static void checkBaseNode(final HierarchyTreeStructure treeStructure,
|
||||
final HierarchyNodeDescriptor descriptor,
|
||||
final Element expectedElement) {
|
||||
final String baseAttrValue = expectedElement.getAttributeValue(BASE_ATTR_NAME);
|
||||
final HierarchyNodeDescriptor baseDescriptor = treeStructure.getBaseDescriptor();
|
||||
final boolean mustBeBase = "true".equalsIgnoreCase(baseAttrValue);
|
||||
assertTrue("Incorrect base node", mustBeBase ? baseDescriptor == descriptor : baseDescriptor != descriptor);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see {@link com.intellij.testFramework.codeInsight.hierarchy.HierarchyViewTestBase}
|
||||
*/
|
||||
private static void checkContent(final HierarchyNodeDescriptor descriptor, final Element expectedElement) {
|
||||
assertEquals(expectedElement.getAttributeValue(TEXT_ATTR_NAME), descriptor.getHighlightedText().getText());
|
||||
}
|
||||
|
||||
/**
|
||||
* @see {@link com.intellij.testFramework.codeInsight.hierarchy.HierarchyViewTestBase}
|
||||
*/
|
||||
private static void checkChildren(final HierarchyTreeStructure treeStructure,
|
||||
final HierarchyNodeDescriptor descriptor,
|
||||
final Element element) {
|
||||
if (element.getChild(ANY_NODES_ELEMENT_NAME) != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Object[] children = treeStructure.getChildElements(descriptor);
|
||||
//noinspection unchecked
|
||||
final List<Element> expectedChildren = new ArrayList<Element>(element.getChildren(NODE_ELEMENT_NAME));
|
||||
|
||||
final StringBuilder messageBuilder = new StringBuilder("Actual children of [" + descriptor.getHighlightedText().getText() + "]:\n");
|
||||
for (Object child : children) {
|
||||
final HierarchyNodeDescriptor nodeDescriptor = (HierarchyNodeDescriptor)child;
|
||||
nodeDescriptor.update();
|
||||
messageBuilder.append(" [").append(nodeDescriptor.getHighlightedText().getText()).append("]\n");
|
||||
}
|
||||
assertEquals(messageBuilder.toString(), expectedChildren.size(), children.length);
|
||||
|
||||
Arrays.sort(children, new Comparator<Object>() {
|
||||
@Override
|
||||
public int compare(final Object first, final Object second) {
|
||||
return ((HierarchyNodeDescriptor)first).getHighlightedText().getText()
|
||||
.compareTo(((HierarchyNodeDescriptor)second).getHighlightedText().getText());
|
||||
}
|
||||
});
|
||||
|
||||
Collections.sort(expectedChildren, new Comparator<Element>() {
|
||||
@Override
|
||||
public int compare(final Element first, final Element second) {
|
||||
return first.getAttributeValue(TEXT_ATTR_NAME).compareTo(second.getAttributeValue(TEXT_ATTR_NAME));
|
||||
}
|
||||
});
|
||||
|
||||
//noinspection unchecked
|
||||
final Iterator<Element> iterator = expectedChildren.iterator();
|
||||
for (Object child : children) {
|
||||
checkNodeDescriptorRecursively(treeStructure, ((HierarchyNodeDescriptor)child), iterator.next());
|
||||
}
|
||||
}
|
||||
|
||||
public static String dump(final HierarchyTreeStructure treeStructure, @Nullable HierarchyNodeDescriptor descriptor, int level) {
|
||||
StringBuilder s = new StringBuilder();
|
||||
dump(treeStructure, descriptor, level, s);
|
||||
return s.toString();
|
||||
}
|
||||
|
||||
private static void dump(final HierarchyTreeStructure treeStructure,
|
||||
@Nullable HierarchyNodeDescriptor descriptor,
|
||||
int level,
|
||||
StringBuilder b) {
|
||||
if (level > 10) {
|
||||
for(int i = 0; i<level; i++) b.append(" ");
|
||||
b.append("<Probably infinite part skipped>\n");
|
||||
return;
|
||||
}
|
||||
if(descriptor==null) descriptor = (HierarchyNodeDescriptor)treeStructure.getRootElement();
|
||||
for(int i = 0; i<level; i++) b.append(" ");
|
||||
descriptor.update();
|
||||
b.append("<node text=\"").append(descriptor.getHighlightedText().getText()).append("\"")
|
||||
.append(treeStructure.getBaseDescriptor() == descriptor ? " base=\"true\"" : "");
|
||||
|
||||
final Object[] children = treeStructure.getChildElements(descriptor);
|
||||
if(children.length>0) {
|
||||
b.append(">\n");
|
||||
for (Object o : children) {
|
||||
HierarchyNodeDescriptor d = (HierarchyNodeDescriptor)o;
|
||||
dump(treeStructure, d, level + 1, b);
|
||||
}
|
||||
for(int i = 0; i<level; i++) b.append(" ");
|
||||
b.append("</node>\n");
|
||||
} else {
|
||||
b.append("/>\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* 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.hierarchy;
|
||||
|
||||
import com.intellij.codeInsight.TargetElementUtilBase;
|
||||
import com.intellij.ide.hierarchy.HierarchyBrowserBaseEx;
|
||||
import com.intellij.openapi.util.JDOMUtil;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.jetbrains.python.fixtures.PyTestCase;
|
||||
import com.jetbrains.python.hierarchy.call.PyCalleeFunctionTreeStructure;
|
||||
import com.jetbrains.python.hierarchy.call.PyCallerFunctionTreeStructure;
|
||||
import com.jetbrains.python.psi.PyFunction;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* @author novokrest
|
||||
*/
|
||||
public class PyCallHierarchyTest extends PyTestCase {
|
||||
private static final String CALLER_VERIFICATION_SUFFIX = "_caller_verification.xml";
|
||||
private static final String CALLEE_VERIFICATION_SUFFIX = "_callee_verification.xml";
|
||||
|
||||
private String getBasePath() {
|
||||
return "hierarchy/call/Static/" + getTestName(false);
|
||||
}
|
||||
|
||||
private void configureByFiles(String ... fileNames) {
|
||||
String[] filePaths = new String[fileNames.length];
|
||||
int i = 0;
|
||||
for (String fileName: fileNames) {
|
||||
filePaths[i] = getBasePath() + "/" + fileName;
|
||||
i++;
|
||||
}
|
||||
myFixture.configureByFiles(filePaths);
|
||||
}
|
||||
|
||||
private String getVerificationFilePath(final String suffix) {
|
||||
return getTestDataPath() + "/" + getBasePath() + "/" + getTestName(false) + suffix;
|
||||
}
|
||||
|
||||
private String getVerificationCallerFilePath() {
|
||||
return getVerificationFilePath(CALLER_VERIFICATION_SUFFIX);
|
||||
}
|
||||
|
||||
private String getVerificationCalleeFilePath() {
|
||||
return getVerificationFilePath(CALLEE_VERIFICATION_SUFFIX);
|
||||
}
|
||||
|
||||
private void checkFunctionHierarchyTreeStructure(PyFunction function) throws Exception {
|
||||
HierarchyTreeStructureViewer.checkHierarchyTreeStructure(new PyCallerFunctionTreeStructure(myFixture.getProject(), function, HierarchyBrowserBaseEx.SCOPE_PROJECT),
|
||||
JDOMUtil.loadDocument(new File(getVerificationCallerFilePath())));
|
||||
HierarchyTreeStructureViewer.checkHierarchyTreeStructure(new PyCalleeFunctionTreeStructure(myFixture.getProject(), function, HierarchyBrowserBaseEx.SCOPE_PROJECT),
|
||||
JDOMUtil.loadDocument(new File(getVerificationCalleeFilePath())));
|
||||
}
|
||||
|
||||
private void doTestCallHierarchy(String ... fileNames) throws Exception {
|
||||
configureByFiles(fileNames);
|
||||
|
||||
final PsiElement targetElement = TargetElementUtilBase
|
||||
.findTargetElement(myFixture.getEditor(),
|
||||
TargetElementUtilBase.ELEMENT_NAME_ACCEPTED | TargetElementUtilBase.REFERENCED_ELEMENT_ACCEPTED);
|
||||
assert targetElement != null : "Cannot find referenced element";
|
||||
assert targetElement instanceof PyFunction : "Referenced element is not PyFunction";
|
||||
|
||||
PyFunction function = (PyFunction) targetElement;
|
||||
checkFunctionHierarchyTreeStructure(function);
|
||||
}
|
||||
|
||||
public void testSimple() throws Exception {
|
||||
doTestCallHierarchy("main.py");
|
||||
}
|
||||
|
||||
public void testArgumentList() throws Exception {
|
||||
doTestCallHierarchy("main.py", "file_1.py");
|
||||
}
|
||||
|
||||
public void testDefaultValue() throws Exception {
|
||||
doTestCallHierarchy("main.py");
|
||||
}
|
||||
|
||||
public void testLambda() throws Exception {
|
||||
doTestCallHierarchy("main.py", "file_1.py");
|
||||
}
|
||||
|
||||
public void testNestedCall() throws Exception {
|
||||
doTestCallHierarchy("main.py", "file_1.py");
|
||||
}
|
||||
|
||||
public void testInheritance() throws Exception {
|
||||
doTestCallHierarchy("main.py");
|
||||
}
|
||||
|
||||
public void testOverriddenMethod() throws Exception {
|
||||
doTestCallHierarchy("main.py", "file_1.py");
|
||||
}
|
||||
|
||||
public void testInnerFunction() throws Exception {
|
||||
doTestCallHierarchy("main.py");
|
||||
}
|
||||
|
||||
public void testConstructor() throws Exception {
|
||||
doTestCallHierarchy("main.py");
|
||||
}
|
||||
|
||||
public void testParentheses() throws Exception {
|
||||
doTestCallHierarchy("main.py", "file_1.py");
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import com.intellij.psi.xml.XmlElement;
|
||||
import com.intellij.psi.xml.XmlFile;
|
||||
import com.intellij.psi.xml.XmlTag;
|
||||
import com.intellij.util.xml.impl.*;
|
||||
import com.intellij.xml.util.XmlUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -32,6 +33,11 @@ import java.util.List;
|
||||
public class StubParentStrategy implements DomParentStrategy {
|
||||
|
||||
private final static Logger LOG = Logger.getInstance(StubParentStrategy.class);
|
||||
protected final DomStub myStub;
|
||||
|
||||
public StubParentStrategy(@NotNull DomStub stub) {
|
||||
myStub = stub;
|
||||
}
|
||||
|
||||
public static StubParentStrategy createAttributeStrategy(@Nullable AttributeStub stub, @NotNull final DomStub parent) {
|
||||
if (stub == null) {
|
||||
@@ -59,12 +65,6 @@ public class StubParentStrategy implements DomParentStrategy {
|
||||
}
|
||||
}
|
||||
|
||||
protected final DomStub myStub;
|
||||
|
||||
public StubParentStrategy(@NotNull DomStub stub) {
|
||||
myStub = stub;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DomInvocationHandler getParentHandler() {
|
||||
DomStub parentStub = myStub.getParentStub();
|
||||
@@ -82,7 +82,14 @@ public class StubParentStrategy implements DomParentStrategy {
|
||||
|
||||
// for custom elements, namespace information is lost
|
||||
// todo: propagate ns info through DomChildDescriptions
|
||||
XmlTag[] tags = parentTag.getSubTags();
|
||||
XmlTag[] tags;
|
||||
try {
|
||||
XmlUtil.BUILDING_DOM_STUBS.set(true);
|
||||
tags = parentTag.getSubTags();
|
||||
}
|
||||
finally {
|
||||
XmlUtil.BUILDING_DOM_STUBS.set(false);
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
String nameToFind = myStub.getName();
|
||||
|
||||
@@ -52,6 +52,19 @@ public abstract class DomStubTest extends LightCodeInsightFixtureTestCase {
|
||||
}
|
||||
};
|
||||
|
||||
public static ElementStub getRootStub(@TestDataFile String filePath, JavaCodeInsightTestFixture fixture) {
|
||||
PsiFile psiFile = fixture.configureByFile(filePath);
|
||||
|
||||
StubTreeLoader loader = StubTreeLoader.getInstance();
|
||||
VirtualFile file = psiFile.getVirtualFile();
|
||||
assertTrue(loader.canHaveStub(file));
|
||||
ObjectStubTree stubTree = loader.readFromVFile(fixture.getProject(), file);
|
||||
assertNotNull(stubTree);
|
||||
ElementStub root = (ElementStub)stubTree.getRoot();
|
||||
assertNotNull(root);
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
@@ -67,19 +80,6 @@ public abstract class DomStubTest extends LightCodeInsightFixtureTestCase {
|
||||
return getRootStub(filePath, myFixture);
|
||||
}
|
||||
|
||||
public static ElementStub getRootStub(@TestDataFile String filePath, JavaCodeInsightTestFixture fixture) {
|
||||
PsiFile psiFile = fixture.configureByFile(filePath);
|
||||
|
||||
StubTreeLoader loader = StubTreeLoader.getInstance();
|
||||
VirtualFile file = psiFile.getVirtualFile();
|
||||
assertTrue(loader.canHaveStub(file));
|
||||
ObjectStubTree stubTree = loader.readFromVFile(fixture.getProject(), file);
|
||||
assertNotNull(stubTree);
|
||||
ElementStub root = (ElementStub)stubTree.getRoot();
|
||||
assertNotNull(root);
|
||||
return root;
|
||||
}
|
||||
|
||||
protected void doBuilderTest(@TestDataFile String file, String stubText) {
|
||||
ElementStub stub = getRootStub(file);
|
||||
assertEquals(stubText, DebugUtil.stubTreeToString(stub));
|
||||
@@ -99,7 +99,7 @@ public abstract class DomStubTest extends LightCodeInsightFixtureTestCase {
|
||||
XmlFile file = (XmlFile)((PsiManagerEx)getPsiManager()).getFileManager().findFile(virtualFile);
|
||||
assertFalse(file.getNode().isParsed());
|
||||
ObjectStubTree tree = StubTreeLoader.getInstance().readOrBuild(getProject(), virtualFile, file);
|
||||
assertNotNull(tree);
|
||||
assertNotNull("Can't build stubs for " + path, tree);
|
||||
|
||||
((PsiManagerImpl)getPsiManager()).cleanupForNextTest();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user