201: Convert KotlinCallHierarchyBrowser and KotlinCallHierarchyBrowser

Need convert to use Kotlin typealias feature.
This commit is contained in:
Nikolay Krasko
2020-01-28 21:55:08 +03:00
committed by Nikolay Krasko
parent b795b38794
commit b92a228fb3
2 changed files with 220 additions and 253 deletions
@@ -13,88 +13,85 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.jetbrains.kotlin.idea.hierarchy.calls
package org.jetbrains.kotlin.idea.hierarchy.calls; import com.intellij.ide.hierarchy.CallHierarchyBrowserBase
import com.intellij.ide.hierarchy.HierarchyNodeDescriptor
import com.intellij.ide.hierarchy.HierarchyTreeStructure
import com.intellij.ide.hierarchy.JavaHierarchyUtil
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.psi.PsiClass
import com.intellij.psi.PsiElement
import com.intellij.ui.PopupHandler
import org.jetbrains.kotlin.psi.KtElement
import java.util.*
import javax.swing.JTree
import com.intellij.ide.hierarchy.CallHierarchyBrowserBase; class KotlinCallHierarchyBrowser(element: PsiElement) :
import com.intellij.ide.hierarchy.HierarchyNodeDescriptor; CallHierarchyBrowserBase(element.project, element) {
import com.intellij.ide.hierarchy.HierarchyTreeStructure; override fun createTrees(type2TreeMap: MutableMap<String, JTree>) {
import com.intellij.ide.hierarchy.JavaHierarchyUtil; val group =
import com.intellij.ide.util.treeView.NodeDescriptor; ActionManager.getInstance().getAction(IdeActions.GROUP_CALL_HIERARCHY_POPUP) as ActionGroup
import com.intellij.openapi.actionSystem.ActionGroup; val tree1 = createTree(false)
import com.intellij.openapi.actionSystem.ActionManager; PopupHandler.installPopupHandler(
import com.intellij.openapi.actionSystem.ActionPlaces; tree1,
import com.intellij.openapi.actionSystem.IdeActions; group,
import com.intellij.psi.PsiClass; ActionPlaces.CALL_HIERARCHY_VIEW_POPUP,
import com.intellij.psi.PsiElement; ActionManager.getInstance()
import com.intellij.ui.PopupHandler; )
import org.jetbrains.annotations.NotNull; val baseOnThisMethodAction =
import org.jetbrains.kotlin.psi.KtElement; BaseOnThisMethodAction()
import javax.swing.*;
import java.util.Comparator;
import java.util.Map;
public class KotlinCallHierarchyBrowser extends CallHierarchyBrowserBase {
public KotlinCallHierarchyBrowser(@NotNull PsiElement element) {
super(element.getProject(), element);
}
@Override
protected void createTrees(@NotNull Map<String, JTree> type2TreeMap) {
ActionGroup group = (ActionGroup) ActionManager.getInstance().getAction(IdeActions.GROUP_CALL_HIERARCHY_POPUP);
JTree tree1 = createTree(false);
PopupHandler.installPopupHandler(tree1, group, ActionPlaces.CALL_HIERARCHY_VIEW_POPUP, ActionManager.getInstance());
BaseOnThisMethodAction baseOnThisMethodAction = new BaseOnThisMethodAction();
baseOnThisMethodAction.registerCustomShortcutSet( baseOnThisMethodAction.registerCustomShortcutSet(
ActionManager.getInstance().getAction(IdeActions.ACTION_CALL_HIERARCHY).getShortcutSet(), tree1 ActionManager.getInstance().getAction(IdeActions.ACTION_CALL_HIERARCHY).shortcutSet,
); tree1
type2TreeMap.put(CALLEE_TYPE, tree1); )
type2TreeMap[CALLEE_TYPE] = tree1
JTree tree2 = createTree(false); val tree2 = createTree(false)
PopupHandler.installPopupHandler(tree2, group, ActionPlaces.CALL_HIERARCHY_VIEW_POPUP, ActionManager.getInstance()); PopupHandler.installPopupHandler(
tree2,
group,
ActionPlaces.CALL_HIERARCHY_VIEW_POPUP,
ActionManager.getInstance()
)
baseOnThisMethodAction.registerCustomShortcutSet( baseOnThisMethodAction.registerCustomShortcutSet(
ActionManager.getInstance().getAction(IdeActions.ACTION_CALL_HIERARCHY).getShortcutSet(), tree2 ActionManager.getInstance().getAction(IdeActions.ACTION_CALL_HIERARCHY).shortcutSet,
); tree2
type2TreeMap.put(CALLER_TYPE, tree2); )
type2TreeMap[CALLER_TYPE] = tree2
} }
private static PsiElement getTargetElement(@NotNull HierarchyNodeDescriptor descriptor) { override fun getElementFromDescriptor(descriptor: HierarchyNodeDescriptor): PsiElement? {
if (descriptor instanceof KotlinCallHierarchyNodeDescriptor) { return getTargetElement(descriptor)
return descriptor.getPsiElement(); }
override fun isApplicableElement(element: PsiElement): Boolean {
return if (element is PsiClass) false else isCallHierarchyElement(element) // PsiClass is not allowed at the hierarchy root
}
override fun createHierarchyTreeStructure(
typeName: String,
psiElement: PsiElement
): HierarchyTreeStructure? {
if (psiElement !is KtElement) return null
if (typeName == CALLER_TYPE) {
return KotlinCallerTreeStructure(psiElement, currentScopeType)
} }
return null; return if (typeName == CALLEE_TYPE) {
KotlinCalleeTreeStructure(psiElement, currentScopeType)
} else null
} }
@Override override fun getComparator(): Comparator<NodeDescriptor<*>> {
protected PsiElement getElementFromDescriptor(@NotNull HierarchyNodeDescriptor descriptor) { return JavaHierarchyUtil.getComparator(myProject)
return getTargetElement(descriptor);
} }
@Override companion object {
protected boolean isApplicableElement(@NotNull PsiElement element) { private fun getTargetElement(descriptor: HierarchyNodeDescriptor): PsiElement? {
if (element instanceof PsiClass) return false; // PsiClass is not allowed at the hierarchy root return (descriptor as? KotlinCallHierarchyNodeDescriptor)?.psiElement
return CallHierarchyUtilsKt.isCallHierarchyElement(element);
}
@Override
protected HierarchyTreeStructure createHierarchyTreeStructure(@NotNull String typeName, @NotNull PsiElement psiElement) {
if (!(psiElement instanceof KtElement)) return null;
if (typeName.equals(CALLER_TYPE)) {
return new KotlinCallerTreeStructure((KtElement) psiElement, getCurrentScopeType());
} }
if (typeName.equals(CALLEE_TYPE)) {
return new KotlinCalleeTreeStructure((KtElement) psiElement, getCurrentScopeType());
}
return null;
} }
}
@Override
protected Comparator<NodeDescriptor> getComparator() {
return JavaHierarchyUtil.getComparator(myProject);
}
}
@@ -13,221 +13,191 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.jetbrains.kotlin.idea.hierarchy.calls
package org.jetbrains.kotlin.idea.hierarchy.calls; import com.intellij.icons.AllIcons
import com.intellij.ide.IdeBundle
import com.intellij.ide.hierarchy.HierarchyNodeDescriptor
import com.intellij.ide.hierarchy.call.CallHierarchyNodeDescriptor
import com.intellij.openapi.editor.markup.TextAttributes
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.text.StringUtil
import com.intellij.pom.Navigatable
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiReference
import com.intellij.ui.LayeredIcon
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import java.awt.Font
import java.util.*
import com.intellij.icons.AllIcons; class KotlinCallHierarchyNodeDescriptor(
import com.intellij.ide.IdeBundle; parentDescriptor: HierarchyNodeDescriptor?,
import com.intellij.ide.hierarchy.HierarchyNodeDescriptor; element: KtElement,
import com.intellij.ide.hierarchy.call.CallHierarchyNodeDescriptor; isBase: Boolean,
import com.intellij.openapi.editor.markup.TextAttributes; navigateToReference: Boolean
import com.intellij.openapi.roots.ui.util.CompositeAppearance; ) : HierarchyNodeDescriptor(element.project, parentDescriptor, element, isBase),
import com.intellij.openapi.util.Comparing; Navigatable {
import com.intellij.openapi.util.Iconable; private var usageCount = 1
import com.intellij.openapi.util.text.StringUtil; private val references: MutableSet<PsiReference> =
import com.intellij.pom.Navigatable; HashSet()
import com.intellij.psi.PsiElement; private val javaDelegate: CallHierarchyNodeDescriptor
import com.intellij.psi.PsiReference; fun incrementUsageCount() {
import com.intellij.ui.LayeredIcon; usageCount++
import org.jetbrains.annotations.NotNull; javaDelegate.incrementUsageCount()
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.descriptors.*;
import org.jetbrains.kotlin.idea.caches.resolve.ResolutionUtils;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.renderer.DescriptorRenderer;
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode;
import javax.swing.*;
import java.awt.*;
import java.util.HashSet;
import java.util.Set;
public class KotlinCallHierarchyNodeDescriptor extends HierarchyNodeDescriptor implements Navigatable {
private int usageCount = 1;
private final Set<PsiReference> references = new HashSet<>();
private final CallHierarchyNodeDescriptor javaDelegate;
public KotlinCallHierarchyNodeDescriptor(
@Nullable HierarchyNodeDescriptor parentDescriptor,
@NotNull KtElement element,
boolean isBase,
boolean navigateToReference) {
super(element.getProject(), parentDescriptor, element, isBase);
this.javaDelegate = new CallHierarchyNodeDescriptor(myProject, null, element, isBase, navigateToReference);
} }
public final void incrementUsageCount() { fun addReference(reference: PsiReference) {
usageCount++; references.add(reference)
javaDelegate.incrementUsageCount(); javaDelegate.addReference(reference)
} }
public final void addReference(PsiReference reference) { override fun isValid(): Boolean {
references.add(reference); val myElement = psiElement
javaDelegate.addReference(reference); return myElement != null && myElement.isValid
} }
@Override override fun update(): Boolean {
public final boolean isValid(){ val oldText = myHighlightedText
//noinspection ConstantConditions val oldIcon = icon
PsiElement myElement = getPsiElement(); var flags = Iconable.ICON_FLAG_VISIBILITY
return myElement != null && myElement.isValid(); if (isMarkReadOnly) {
} flags = flags or Iconable.ICON_FLAG_READ_STATUS
@Override
public final boolean update(){
CompositeAppearance oldText = myHighlightedText;
Icon oldIcon = getIcon();
int flags = Iconable.ICON_FLAG_VISIBILITY;
if (isMarkReadOnly()) {
flags |= Iconable.ICON_FLAG_READ_STATUS;
} }
var changes = super.update()
boolean changes = super.update(); val targetElement = psiElement
val elementText = renderElement(targetElement)
PsiElement targetElement = getPsiElement();
String elementText = renderElement(targetElement);
if (elementText == null) { if (elementText == null) {
String invalidPrefix = IdeBundle.message("node.hierarchy.invalid"); val invalidPrefix = IdeBundle.message("node.hierarchy.invalid")
if (!myHighlightedText.getText().startsWith(invalidPrefix)) { if (!myHighlightedText.text.startsWith(invalidPrefix)) {
myHighlightedText.getBeginning().addText(invalidPrefix, HierarchyNodeDescriptor.getInvalidPrefixAttributes()); myHighlightedText.beginning
.addText(invalidPrefix, getInvalidPrefixAttributes())
} }
return true; return true
} }
var newIcon = targetElement!!.getIcon(flags)
Icon newIcon = targetElement.getIcon(flags);
if (changes && myIsBase) { if (changes && myIsBase) {
LayeredIcon icon = new LayeredIcon(2); val icon = LayeredIcon(2)
icon.setIcon(newIcon, 0); icon.setIcon(newIcon, 0)
icon.setIcon(AllIcons.General.Modified, 1, -AllIcons.General.Modified.getIconWidth() / 2, 0); icon.setIcon(AllIcons.General.Modified, 1, -AllIcons.General.Modified.iconWidth / 2, 0)
newIcon = icon; newIcon = icon
} }
setIcon(newIcon); icon = newIcon
myHighlightedText = CompositeAppearance()
myHighlightedText = new CompositeAppearance(); var mainTextAttributes: TextAttributes? = null
TextAttributes mainTextAttributes = null;
if (myColor != null) { if (myColor != null) {
mainTextAttributes = new TextAttributes(myColor, null, null, null, Font.PLAIN); mainTextAttributes = TextAttributes(myColor, null, null, null, Font.PLAIN)
} }
var packageName =
String packageName = KtPsiUtil.getPackageName((KtElement) targetElement); KtPsiUtil.getPackageName((targetElement as KtElement?)!!)
myHighlightedText.ending.addText(elementText, mainTextAttributes)
myHighlightedText.getEnding().addText(elementText, mainTextAttributes);
if (usageCount > 1) { if (usageCount > 1) {
myHighlightedText.getEnding().addText( myHighlightedText.ending.addText(
IdeBundle.message("node.call.hierarchy.N.usages", usageCount), IdeBundle.message("node.call.hierarchy.N.usages", usageCount),
HierarchyNodeDescriptor.getUsageCountPrefixAttributes() getUsageCountPrefixAttributes()
); )
} }
if (packageName == null) { if (packageName == null) {
packageName = ""; packageName = ""
} }
myHighlightedText.getEnding().addText(" (" + packageName + ")", HierarchyNodeDescriptor.getPackageNameAttributes()); myHighlightedText.ending
.addText(" ($packageName)", getPackageNameAttributes())
myName = myHighlightedText.getText(); myName = myHighlightedText.text
if (!(Comparing.equal(myHighlightedText, oldText) && Comparing.equal(icon, oldIcon))
if (!(Comparing.equal(myHighlightedText, oldText) && Comparing.equal(getIcon(), oldIcon))) { ) {
changes = true; changes = true
} }
return changes; return changes
} }
@Nullable override fun navigate(requestFocus: Boolean) {
private static String renderElement(@Nullable PsiElement element) { javaDelegate.navigate(requestFocus)
if (element instanceof KtFile) { }
return ((KtFile) element).getName();
}
if (!(element instanceof KtNamedDeclaration)) { override fun canNavigate(): Boolean {
return null; return javaDelegate.canNavigate()
} }
DeclarationDescriptor descriptor = ResolutionUtils.resolveToDescriptorIfAny((KtNamedDeclaration) element, BodyResolveMode.PARTIAL); override fun canNavigateToSource(): Boolean {
if (descriptor == null) return null; return javaDelegate.canNavigateToSource()
}
String elementText; companion object {
if (element instanceof KtClassOrObject) { private fun renderElement(element: PsiElement?): String? {
if (element instanceof KtObjectDeclaration && ((KtObjectDeclaration) element).isCompanion()) { if (element is KtFile) {
descriptor = descriptor.getContainingDeclaration(); return element.name
if (!(descriptor instanceof ClassDescriptor)) return null;
elementText = renderClassOrObject((ClassDescriptor) descriptor);
} }
else if (element instanceof KtEnumEntry) { if (element !is KtNamedDeclaration) {
elementText = ((KtEnumEntry) element).getName(); return null
} }
else { var descriptor: DeclarationDescriptor? =
if (((KtClassOrObject) element).getName() != null) { element as KtNamedDeclaration?. resolveToDescriptorIfAny BodyResolveMode.PARTIAL
elementText = renderClassOrObject((ClassDescriptor) descriptor); ?: return null
val elementText: String?
if (element is KtClassOrObject) {
if (element is KtObjectDeclaration && element.isCompanion()) {
descriptor = descriptor.containingDeclaration
if (descriptor !is ClassDescriptor) return null
elementText = renderClassOrObject(descriptor)
} else if (element is KtEnumEntry) {
elementText = element.name
} else {
elementText = if (element.name != null) {
renderClassOrObject(descriptor as ClassDescriptor)
} else {
"[anonymous]"
}
} }
else { } else if (element is KtNamedFunction || element is KtConstructor<*>) {
elementText = "[anonymous]"; if (descriptor !is FunctionDescriptor) return null
elementText = renderNamedFunction(descriptor)
} else if (element is KtProperty) {
elementText = element.name
} else return null
if (elementText == null) return null
var containerText: String? = null
var containerDescriptor = descriptor.containingDeclaration
while (containerDescriptor != null) {
if (containerDescriptor is PackageFragmentDescriptor || containerDescriptor is ModuleDescriptor) {
break
} }
val name = containerDescriptor.name
if (!name.isSpecial) {
val identifier = name.identifier
containerText = if (containerText != null) "$identifier.$containerText" else identifier
}
containerDescriptor = containerDescriptor.containingDeclaration
} }
} return if (containerText != null) "$containerText.$elementText" else elementText
else if ((element instanceof KtNamedFunction || element instanceof KtConstructor)) {
if (!(descriptor instanceof FunctionDescriptor)) return null;
elementText = renderNamedFunction((FunctionDescriptor) descriptor);
}
else if (element instanceof KtProperty) {
elementText = ((KtProperty) element).getName();
}
else return null;
if (elementText == null) return null;
String containerText = null;
DeclarationDescriptor containerDescriptor = descriptor.getContainingDeclaration();
while (containerDescriptor != null) {
if (containerDescriptor instanceof PackageFragmentDescriptor || containerDescriptor instanceof ModuleDescriptor) {
break;
}
Name name = containerDescriptor.getName();
if (!name.isSpecial()) {
String identifier = name.getIdentifier();
containerText = containerText != null ? identifier + "." + containerText : identifier;
}
containerDescriptor = containerDescriptor.getContainingDeclaration();
} }
return containerText != null ? containerText + "." + elementText : elementText; fun renderNamedFunction(descriptor: FunctionDescriptor): String {
} val descriptorForName: DeclarationDescriptor =
(descriptor as? ConstructorDescriptor)?.containingDeclaration ?: descriptor
val name = descriptorForName.name.asString()
val paramTypes =
StringUtil.join(
descriptor.valueParameters,
{ descriptor1: ValueParameterDescriptor ->
DescriptorRenderer
.SHORT_NAMES_IN_TYPES
.renderType(descriptor1.type)
},
", "
)
return "$name($paramTypes)"
}
public static String renderNamedFunction(@NotNull FunctionDescriptor descriptor) { private fun renderClassOrObject(descriptor: ClassDescriptor): String {
DeclarationDescriptor descriptorForName = descriptor instanceof ConstructorDescriptor return descriptor.name.asString()
? descriptor.getContainingDeclaration() }
: descriptor;
String name = descriptorForName.getName().asString();
String paramTypes = StringUtil.join(
descriptor.getValueParameters(),
descriptor1 -> DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(descriptor1.getType()),
", "
);
return name + "(" + paramTypes + ")";
} }
private static String renderClassOrObject(ClassDescriptor descriptor) { init {
return descriptor.getName().asString(); javaDelegate = CallHierarchyNodeDescriptor(myProject, null, element, isBase, navigateToReference)
} }
}
@Override
public void navigate(boolean requestFocus) {
javaDelegate.navigate(requestFocus);
}
@Override
public boolean canNavigate() {
return javaDelegate.canNavigate();
}
@Override
public boolean canNavigateToSource() {
return javaDelegate.canNavigateToSource();
}
}