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