From 5b2efaaa08c4d92b70cdb2467addd1e509669f6f Mon Sep 17 00:00:00 2001 From: Artem Bukhonov Date: Thu, 14 May 2020 22:10:47 +0300 Subject: [PATCH] IDEA-CR-1488: Extend and improve API of PinToTop feature. Type names were replaced with more flexible abstract tags. Now a member can provide parent tag itself which will be used instead of traversing to parent node. Members can provide custom name to use it instead of node name. Members can provide their pinned status itself. All checking logic (canBePinned, isPinned, getPinInfo) were extracted to a separate file. (cherry picked from commit 85b5c3796d3a61701bcf890645f4ed6b475e62cc) GitOrigin-RevId: 1f370966512f49c57ecf2332016712dffcfd0d64 --- .../impl/pinned/items/PinToTopMemberValue.kt | 23 ++++++++++ .../impl/pinned/items/PinToTopParentValue.kt | 14 +++++- .../impl/pinned/items/PinToTopUtil.kt | 43 +++++++++++++++++++ .../impl/pinned/items/PinnedItemInfo.kt | 8 ++-- .../pinned/items/XDebuggerPinToTopManager.kt | 34 ++++++--------- .../items/actions/XDebuggerPinToTopAction.kt | 34 +++++---------- .../impl/ui/tree/nodes/XValueNodeImpl.java | 16 +------ 7 files changed, 109 insertions(+), 63 deletions(-) create mode 100644 platform/xdebugger-impl/src/com/intellij/xdebugger/impl/pinned/items/PinToTopUtil.kt diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/pinned/items/PinToTopMemberValue.kt b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/pinned/items/PinToTopMemberValue.kt index 9752bf217ad1..868d6e4c8bcc 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/pinned/items/PinToTopMemberValue.kt +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/pinned/items/PinToTopMemberValue.kt @@ -6,4 +6,27 @@ import org.jetbrains.annotations.ApiStatus @ApiStatus.Experimental interface PinToTopMemberValue : PinToTopValue { fun canBePinned() : Boolean + + /** + * When not null the name will be used as member name in PinToTop instead of node name. May be useful in a case when + * a value name presentation differs from its real name + */ + @JvmDefault + val customMemberName: String? + get() = null + + /** + * When not null this tag will be used instead of getting tag from parent node + */ + @JvmDefault + val customParentTag: String? + get() = null + + /** + * When not null the value will be used as 'pinned' status instead of checking the status inside [XDebuggerPinToTopManager] maps. + * It may be useful if you want to implement pinning logic inside your values by listening [XDebuggerPinToTopListener] + */ + @JvmDefault + val isPinned: Boolean? + get() = null } \ No newline at end of file diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/pinned/items/PinToTopParentValue.kt b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/pinned/items/PinToTopParentValue.kt index f4aee1212bde..81ba19256826 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/pinned/items/PinToTopParentValue.kt +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/pinned/items/PinToTopParentValue.kt @@ -5,5 +5,17 @@ import org.jetbrains.annotations.ApiStatus @ApiStatus.Experimental interface PinToTopParentValue : PinToTopValue { - fun getTypeName() : String? + + @ApiStatus.ScheduledForRemoval(inVersion = "2021.1") + @Deprecated("Implement property 'tag' instead", ReplaceWith("tag")) + fun getTypeName() : String? = null + + /** + * Tag used as identifier of a parent node. Pinned status is determined by checking of presence a 'tag - member name' pair + * in [XDebuggerPinToTopManager] + */ + @Suppress("DEPRECATION") + @JvmDefault + val tag: String? + get() = getTypeName() } \ No newline at end of file diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/pinned/items/PinToTopUtil.kt b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/pinned/items/PinToTopUtil.kt new file mode 100644 index 000000000000..d832ece18fe0 --- /dev/null +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/pinned/items/PinToTopUtil.kt @@ -0,0 +1,43 @@ +package com.intellij.xdebugger.impl.pinned.items + +import com.intellij.xdebugger.impl.ui.tree.nodes.XValueContainerNode +import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl + +fun XValueNodeImpl?.isPinned(pinToTopManager: XDebuggerPinToTopManager): Boolean { + val container = this?.valueContainer + if (container is PinToTopMemberValue) { + val pinned = container.isPinned + if (pinned != null) + return pinned + + val pinInfo = this.getPinInfo() + if (pinInfo != null) + return pinToTopManager.isPinned(pinInfo) + return false + } + return false +} + +fun XValueNodeImpl?.canBePinned(): Boolean { + val container = this?.valueContainer + if (container is PinToTopMemberValue) { + return container.canBePinned() && this.getPinInfo() != null + } + return false +} + +fun XValueNodeImpl?.getPinInfo() : PinnedItemInfo? { + val container = this?.valueContainer + if (container is PinToTopMemberValue) { + val parentTag = container.customParentTag ?: parentPinToTopValue?.tag + val memberName = container.customMemberName ?: this?.name + if (parentTag != null && memberName != null) + return PinnedItemInfo(parentTag, memberName) + return null + } + return null +} + +val XValueNodeImpl?.parentPinToTopValue: PinToTopParentValue? get() { + return ((this?.parent as? XValueContainerNode<*>)?.valueContainer as? PinToTopParentValue) +} \ No newline at end of file diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/pinned/items/PinnedItemInfo.kt b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/pinned/items/PinnedItemInfo.kt index 9302373e212b..930cddced5b2 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/pinned/items/PinnedItemInfo.kt +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/pinned/items/PinnedItemInfo.kt @@ -4,15 +4,15 @@ package com.intellij.xdebugger.impl.pinned.items import com.intellij.util.xmlb.annotations.Attribute data class PinnedItemInfo( - @Attribute("typeName") var typeName: String, - @Attribute("fieldName") var fieldName: String) { + @Attribute("parentTag") var parentTag: String, + @Attribute("memberName") var memberName: String) { companion object { - fun getKey(typeName: String, fieldName: String) = "$typeName:$fieldName" + fun getKey(parentTag: String, memberName: String) = "$parentTag:$memberName" } @Suppress("unused") constructor() : this("", "") - fun getKey() = getKey(typeName, fieldName) + fun getKey() = getKey(parentTag, memberName) } \ No newline at end of file diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/pinned/items/XDebuggerPinToTopManager.kt b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/pinned/items/XDebuggerPinToTopManager.kt index 73a3cdebebfa..b3dcd15a53ef 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/pinned/items/XDebuggerPinToTopManager.kt +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/pinned/items/XDebuggerPinToTopManager.kt @@ -26,7 +26,7 @@ open class XDebuggerPinToTopManager { private var myNodeHoverLifetime : Disposable? = null private var myActiveNode: XDebuggerTreeNode? = null - private var myPinnedMembers = HashMap() + private var myPinnedMembers = HashSet() private val myPinToTopIconAlarm = Alarm() val pinToTopComparator : Comparator = Comparator.comparing { !isItemPinned(it) } @@ -51,12 +51,7 @@ open class XDebuggerPinToTopManager { } val valueNode = node as? XValueNodeImpl ?: return - val pinnedValue = node.valueContainer as? PinToTopMemberValue ?: return - if ((valueNode.parent as? XValueNodeImpl)?.valueContainer !is PinToTopParentValue) { - return - } - - if (!pinnedValue.canBePinned() || isItemPinned(node)) { + if (!valueNode.canBePinned() || node.isPinned(this)) { return } var oldIcon = valueNode.icon @@ -88,29 +83,26 @@ open class XDebuggerPinToTopManager { myListeners.remove(listener) } - fun getPinnedItemInfos() = myPinnedMembers.values.toList() + fun getPinnedItemInfos() = myPinnedMembers.toList() - fun addItemInfo(typeName: String, fieldName: String) { - val info = PinnedItemInfo(typeName, fieldName) - myPinnedMembers[info.getKey()] = info + fun addItemInfo(info: PinnedItemInfo) { + myPinnedMembers.add(info) for (listener in myListeners) { listener.onPinnedItemAdded(info) } } - fun removeItemInfo(typeName: String, fieldName: String) { - val key = PinnedItemInfo.getKey(typeName, fieldName) - val info = myPinnedMembers[key] ?: return - myPinnedMembers.remove(key) + fun removeItemInfo(info: PinnedItemInfo) { + myPinnedMembers.remove(info) for (listener in myListeners) { listener.onPinnedItemRemoved(info) } } - fun isItemPinned(node: XValueNodeImpl?) : Boolean { - val typeName = ((node?.parent as? XValueContainerNode<*>)?.valueContainer as? PinToTopParentValue)?.getTypeName() ?: return false - return myPinnedMembers.containsKey( - PinnedItemInfo.getKey(typeName, node.name ?: "")) + fun isItemPinned(node: XValueNodeImpl?) : Boolean = node.isPinned(this) + + fun isPinned(pinnedItemInfo: PinnedItemInfo): Boolean { + return myPinnedMembers.contains(pinnedItemInfo) } private fun disposeCurrentNodeHoverSubscription() { @@ -119,11 +111,11 @@ open class XDebuggerPinToTopManager { } fun saveState(state: PinToTopManagerState) { - state.pinnedMembersList = myPinnedMembers.toList().map { it.second }.toMutableList() + state.pinnedMembersList = myPinnedMembers.toMutableList() } fun loadState(state: PinToTopManagerState) { - myPinnedMembers.putAll(state.pinnedMembersList.map { Pair(it.getKey(), it) }) + myPinnedMembers.addAll(state.pinnedMembersList) } fun isPinToTopSupported(node: XDebuggerTreeNode?) : Boolean { diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/pinned/items/actions/XDebuggerPinToTopAction.kt b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/pinned/items/actions/XDebuggerPinToTopAction.kt index 377ee3f7fece..2ce29e79544a 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/pinned/items/actions/XDebuggerPinToTopAction.kt +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/pinned/items/actions/XDebuggerPinToTopAction.kt @@ -2,12 +2,9 @@ package com.intellij.xdebugger.impl.pinned.items.actions import com.intellij.openapi.actionSystem.* -import com.intellij.openapi.project.Project import com.intellij.xdebugger.XDebuggerBundle import com.intellij.xdebugger.impl.XDebuggerUtilImpl -import com.intellij.xdebugger.impl.pinned.items.PinToTopMemberValue -import com.intellij.xdebugger.impl.pinned.items.PinToTopParentValue -import com.intellij.xdebugger.impl.pinned.items.XDebuggerPinToTopManager +import com.intellij.xdebugger.impl.pinned.items.* import com.intellij.xdebugger.impl.ui.tree.XDebuggerTree import com.intellij.xdebugger.impl.ui.tree.actions.XDebuggerTreeActionBase import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl @@ -57,7 +54,7 @@ class XDebuggerPinToTopAction : XDebuggerTreeActionBase() { return } presentation.isVisible = true - presentation.isEnabled = (node.parent as? XValueNodeImpl)?.valueContainer is PinToTopParentValue && valueContainer.canBePinned() + presentation.isEnabled = node.canBePinned() presentation.icon = if (pinToTopManager.isItemPinned(node)) PlatformDebuggerImplIcons.PinToTop.UnpinnedItem else PlatformDebuggerImplIcons.PinToTop.PinnedItem presentation.text = if (pinToTopManager.isItemPinned(node)) XDebuggerBundle.message("xdebugger.unpin.action") else XDebuggerBundle.message("xdebugger.pin.to.top.action") @@ -66,30 +63,21 @@ class XDebuggerPinToTopAction : XDebuggerTreeActionBase() { override fun perform(node: XValueNodeImpl?, nodeName: String, e: AnActionEvent) { node ?: return val project = e.project ?: return - val nodeValue = node.valueContainer as? PinToTopMemberValue ?: return - if (!nodeValue.canBePinned()) { - return - } - val parentType = ((node.parent as? XValueNodeImpl)?.valueContainer as? PinToTopParentValue)?.getTypeName() - if (parentType.isNullOrEmpty()) { + if (!node.canBePinned()) return - } - if (XDebuggerPinToTopManager.getInstance(project).isItemPinned(node)) { - removePrioritizedItem(parentType, nodeName, project) + val pinToTopManager = XDebuggerPinToTopManager.getInstance(project) + + val pinInfo = node.getPinInfo() + ?: return + + if (node.isPinned(pinToTopManager)) { + pinToTopManager.removeItemInfo(pinInfo) } else { - addPrioritizedItem(parentType, nodeName, project) + pinToTopManager.addItemInfo(pinInfo) } XDebuggerUtilImpl.rebuildTreeAndViews(node.tree) } - - private fun addPrioritizedItem(parentType: String, nodeName: String, project: Project) { - XDebuggerPinToTopManager.getInstance(project).addItemInfo(parentType, nodeName) - } - - private fun removePrioritizedItem(parentType: String, nodeName: String, project: Project) { - XDebuggerPinToTopManager.getInstance(project).removeItemInfo(parentType, nodeName) - } } \ No newline at end of file diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/nodes/XValueNodeImpl.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/nodes/XValueNodeImpl.java index 25135106b7a5..f85a600e0be0 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/nodes/XValueNodeImpl.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/nodes/XValueNodeImpl.java @@ -24,6 +24,7 @@ import com.intellij.xdebugger.impl.frame.XValueWithInlinePresentation; import com.intellij.xdebugger.impl.frame.XVariablesView; import com.intellij.xdebugger.impl.pinned.items.PinToTopMemberValue; import com.intellij.xdebugger.impl.pinned.items.PinToTopParentValue; +import com.intellij.xdebugger.impl.pinned.items.PinToTopUtilKt; import com.intellij.xdebugger.impl.pinned.items.actions.XDebuggerPinToTopAction; import com.intellij.xdebugger.impl.ui.DebuggerUIUtil; import com.intellij.xdebugger.impl.ui.XDebuggerUIConstants; @@ -307,21 +308,8 @@ public class XValueNodeImpl extends XValueContainerNode implements XValu return null; } - if (!(myValueContainer instanceof PinToTopMemberValue)) { + if (!PinToTopUtilKt.canBePinned(this)) return null; - } - - final PinToTopMemberValue pinToTopMemberValue = (PinToTopMemberValue)myValueContainer; - if (!pinToTopMemberValue.canBePinned()) { - return null; - } - if(!(myParent instanceof XValueNodeImpl)) { - return null; - } - - if (!(((XValueNodeImpl)myParent).myValueContainer instanceof PinToTopParentValue)) { - return null; - } return new XDebuggerTreeNodeHyperlink(XDebuggerBundle.message("xdebugger.pin.to.top.action")) { @Override