Skip to content

Added graph of inverse dependencies of definitions #457

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/main/kotlin/org/arend/ArendIcons.kt
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ object ArendIcons {
val TURNSTILE = getIcon("/icons/turnstile.svg")
val CHECKMARK = getIcon("/icons/checkmark.svg")

val GRAPH = getIcon("/icons/graph.svg")
val ORTHOGONAL_GRAPH = getIcon("/icons/orthogonal_graph.svg")

// Source code elements

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package org.arend.actions

import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.PlatformDataKeys
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiElementVisitor
import org.arend.ArendIcons
import org.arend.graph.GraphEdge
import org.arend.graph.GraphNode
import org.arend.graph.GraphSimulator
import org.arend.psi.ext.*

class ArendDefinitionInverseDependenciesGraphAction : AnAction(ArendIcons.ORTHOGONAL_GRAPH) {

private val usedNodes = mutableSetOf<String>()
private val edges = mutableSetOf<GraphEdge>()

private fun findEdge(nameFrom: String, element: PsiElement) {
element.accept(object : PsiElementVisitor() {
override fun visitElement(element: PsiElement) {
if (element is ArendReferenceElement) {
val resolve = element.resolve
if (resolve is ArendDefinition<*>) {
val nameTo = resolve.getName()
if (nameTo != null) {
edges.add(GraphEdge(nameFrom, nameTo))
if (!usedNodes.contains(nameTo)) {
findEdges(resolve)
}
}
}
}
element.children.forEach { it.accept(this) }
}
})
}

private fun findEdges(definition: ArendDefinition<*>, optionalNameFrom: String? = null) {
val nameFrom = optionalNameFrom ?: definition.getName() ?: return
usedNodes.add(nameFrom)

for (param in definition.parametersExt) {
(param.type as? PsiElement?)?.let { findEdge(nameFrom, it) }
}
when (definition) {
is ArendFunctionDefinition -> {
definition.returnExpr?.let { findEdge(nameFrom, it) }
definition.body?.let { findEdge(nameFrom, it) }
}
is ArendDefClass -> {
definition.fields.forEach { findEdge(nameFrom, it) }
if (optionalNameFrom == null) {
definition.superClassList.forEach { findEdges(it.longName.resolve as ArendDefinition<*>, nameFrom) }
} else {
definition.superClassList.forEach { findEdges(it.longName.resolve as ArendDefinition<*>, optionalNameFrom) }
}
}
is ArendDefData -> definition.dataBody?.let { findEdge(nameFrom, it) }
is ArendDefMeta -> definition.expr?.let { findEdge(nameFrom, it) }
}
}

override fun actionPerformed(e: AnActionEvent) {
usedNodes.clear()
edges.clear()

val definition = getArendDefinition(e) as? ArendDefinition<*> ?: return
findEdges(definition)

val graphSimulator = GraphSimulator(this.toString(), edges, usedNodes.map { GraphNode(it) }.toSet())
graphSimulator.displayOrthogonal()
}

override fun update(e: AnActionEvent) {
e.presentation.isEnabled = getArendDefinition(e) != null
}

private fun getArendDefinition(e: AnActionEvent): PsiElement? {
val editor = e.getData(PlatformDataKeys.EDITOR)
val psiFile = e.getData(PlatformDataKeys.PSI_FILE)

if (editor == null || psiFile == null) {
return null
}

val offset = editor.caretModel.offset
var currentPsiElement = psiFile.findElementAt(offset)
while (currentPsiElement != null && currentPsiElement !is ArendDefinition<*>) {
currentPsiElement = currentPsiElement.parent
}
return currentPsiElement
}
}
8 changes: 5 additions & 3 deletions src/main/kotlin/org/arend/graph/GraphSimulator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,19 @@ import javax.swing.ImageIcon
import javax.swing.JFrame
import javax.swing.JLabel

data class GraphNode(val id: String)

data class GraphEdge(val from: String, val to: String)

class GraphSimulator(
private val graphId: String, private val edges: Set<GraphEdge>, private val vertices: Set<String>
private val graphId: String, private val edges: Set<GraphEdge>, private val vertices: Set<GraphNode>
) {

fun display() {
fun displayOrthogonal() {
var graph = graph(graphId).directed()

for (vertex in vertices) {
graph = graph.with(node(vertex))
graph = graph.with(node(vertex.id))
}

for (edge in edges) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import com.intellij.ui.tree.StructureTreeModel
import com.intellij.util.ui.tree.TreeUtil
import org.arend.ArendIcons
import org.arend.graph.GraphEdge
import org.arend.graph.GraphNode
import org.arend.graph.GraphSimulator
import org.arend.hierarchy.ArendHierarchyNodeDescriptor
import org.arend.psi.ext.ArendDefClass
Expand Down Expand Up @@ -166,14 +167,14 @@ class ArendClassHierarchyBrowser(project: Project, method: PsiElement) : TypeHie
}
}

inner class ArendHierarchyGraphAction : AnAction(ArendIcons.GRAPH) {
inner class ArendHierarchyGraphAction : AnAction(ArendIcons.ORTHOGONAL_GRAPH) {
private val usedNodes = mutableSetOf<TreeNode>()
private val edges = mutableSetOf<GraphEdge>()

private fun findEdges(currentNode: DefaultMutableTreeNode): Set<GraphEdge> {
private fun findEdges(currentNode: DefaultMutableTreeNode) {
usedNodes.add(currentNode)

val from = ((currentNode.userObject as ArendHierarchyNodeDescriptor).psiElement as ArendDefClass).fullName
val edges = mutableSetOf<GraphEdge>()

val children = TreeUtil.listChildren(currentNode)
for (child in children) {
Expand All @@ -182,25 +183,29 @@ class ArendClassHierarchyBrowser(project: Project, method: PsiElement) : TypeHie
edges.add(GraphEdge(from, to))

if (!usedNodes.contains(child)) {
edges.addAll(findEdges(child))
findEdges(child)
}
}
return edges
}

override fun actionPerformed(e: AnActionEvent) {
usedNodes.clear()
edges.clear()

val tree = getJTree(currentViewType) ?: return
val root = tree.model.root as DefaultMutableTreeNode

usedNodes.clear()
findEdges(root)

val simulator = GraphSimulator(
this.toString(),
findEdges(root),
usedNodes.map { (((it as DefaultMutableTreeNode).userObject as ArendHierarchyNodeDescriptor).psiElement as? ArendDefClass?)?.fullName!! }
.toSet()
edges,
usedNodes.map {
GraphNode(
(((it as DefaultMutableTreeNode).userObject as ArendHierarchyNodeDescriptor).psiElement as? ArendDefClass?)?.fullName!!
) }.toSet()
)

simulator.display()
simulator.displayOrthogonal()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,45 +6,46 @@ import com.intellij.openapi.module.Module
import com.intellij.openapi.module.ModuleManager
import org.arend.ArendIcons
import org.arend.graph.GraphEdge
import org.arend.graph.GraphNode
import org.arend.graph.GraphSimulator
import org.arend.module.config.ArendModuleConfigService

class ArendModuleDependenciesGraphAction : AnAction(ArendIcons.GRAPH) {
class ArendModuleDependenciesGraphAction : AnAction(ArendIcons.ORTHOGONAL_GRAPH) {
private val usedNodes = mutableSetOf<Module>()
private val edges = mutableSetOf<GraphEdge>()

private fun findEdges(currentNode: Module, modules: List<Module>): Set<GraphEdge> {
private fun findEdges(currentNode: Module, modules: List<Module>) {
usedNodes.add(currentNode)

val from = currentNode.name
val edges = mutableSetOf<GraphEdge>()

val arendModuleConfigService = ArendModuleConfigService.getInstance(currentNode) ?: return emptySet()
val arendModuleConfigService = ArendModuleConfigService.getInstance(currentNode) ?: return
val dependencyNames = arendModuleConfigService.dependencies.map { it.name }
val children = modules.filter { dependencyNames.contains(it.name) }

for (child in children) {
edges.add(GraphEdge(from, child.name))

if (!usedNodes.contains(child)) {
edges.addAll(findEdges(child, modules))
findEdges(child, modules)
}
}
return edges
}

override fun actionPerformed(e: AnActionEvent) {
usedNodes.clear()
edges.clear()

val moduleManager = e.project?.let { ModuleManager.getInstance(it) } ?: return
val modules = moduleManager.modules.toList()

usedNodes.clear()
val edges = mutableSetOf<GraphEdge>()
for (module in modules) {
if (!usedNodes.contains(module)) {
edges.addAll(findEdges(module, modules))
findEdges(module, modules)
}
}

val simulator = GraphSimulator(this.toString(), edges, usedNodes.map { it.name }.toSet())
simulator.display()
val simulator = GraphSimulator(this.toString(), edges, usedNodes.map { GraphNode(it.name) }.toSet())
simulator.displayOrthogonal()
}
}
3 changes: 3 additions & 0 deletions src/main/kotlin/org/arend/psi/ext/ArendDefClass.kt
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ class ArendDefClass : ArendDefinition<ArendDefClassStub>, ClassReferable, TCDefi
val extendsKw: PsiElement?
get() = findChildByType(EXTENDS_KW)

override val parametersExt: List<Abstract.Parameter>
get() = parameters

override fun getReferable() = this

override fun isRecord(): Boolean = hasChildOfType(RECORD_KW)
Expand Down
3 changes: 3 additions & 0 deletions src/main/kotlin/org/arend/psi/ext/ArendDefMeta.kt
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ class ArendDefMeta : ArendDefinition<ArendDefMetaStub>, Abstract.MetaDefinition,
tcReferableCache = value
}

override val parametersExt: List<Abstract.Parameter>
get() = parameters

override fun getDescription() = documentation?.toString() ?: ""

private fun prepareTCRef(data: SmartPsiElementPointer<PsiLocatedReferable>?, parent: LocatedReferable?) =
Expand Down
2 changes: 1 addition & 1 deletion src/main/kotlin/org/arend/psi/ext/ArendDefinition.kt
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ where StubT : ArendNamedStub, StubT : StubElement<*> {
return null
}

protected open val parametersExt: List<Abstract.Parameter>
open val parametersExt: List<Abstract.Parameter>
get() = emptyList()

override fun getExternalParameters(): List<ParameterReferable> {
Expand Down
3 changes: 3 additions & 0 deletions src/main/kotlin/org/arend/psi/ext/ArendFunctionDefinition.kt
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ where StubT : ArendNamedStub, StubT : StubElement<*> {
val returnExpr: ArendReturnExpr?
get() = childOfType()

override val parametersExt: List<Abstract.Parameter>
get() = parameters

override fun getParameters(): List<ArendNameTele> = getChildrenOfType()

override fun getResultType() = returnExpr?.type
Expand Down
7 changes: 6 additions & 1 deletion src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,7 @@

<action id="ArendModuleDependenciesGraphAction"
class="org.arend.module.ArendModuleDependenciesGraphAction"
text="Show Graph of Arend Modules">
text="Show a Graph of Arend Modules">
<add-to-group group-id="ProjectViewPopupMenu" anchor="after" relative-to-action="ProjectViewPopupMenuModifyGroup"/>
</action>

Expand Down Expand Up @@ -563,6 +563,11 @@
<action id="ArendUnmarkRootAction" class="org.arend.actions.ArendUnmarkRootAction">
<add-to-group group-id="MarkRootGroup" anchor="first"/>
</action>
<action id="ArendDefinitionInverseDependenciesGraphAction"
class="org.arend.actions.ArendDefinitionInverseDependenciesGraphAction"
text="Show a Graph of Inverse Dependencies of Definitions">
<add-to-group group-id="EditorPopupMenu" anchor="after" relative-to-action="EditorPopupMenu3"/>
</action>
</actions>

<resource-bundle>messages.ArendBundle</resource-bundle>
Expand Down
34 changes: 0 additions & 34 deletions src/main/resources/icons/graph.svg

This file was deleted.

42 changes: 42 additions & 0 deletions src/main/resources/icons/orthogonal_graph.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.