1+ package com.github.xepozz.php_dump.panel
2+
3+ import com.github.xepozz.php_dump.actions.RunDumpTokensCommandAction
4+ import com.github.xepozz.php_dump.nonBlocking
5+ import com.github.xepozz.php_dump.services.TokensObjectTreeDumperService
6+ import com.github.xepozz.php_dump.stubs.token_object.TokensList
7+ import com.github.xepozz.php_dump.tree.RootNode
8+ import com.github.xepozz.php_dump.tree.TokenNode
9+ import com.github.xepozz.php_dump.tree.TokensTreeStructure
10+ import com.intellij.ide.util.treeView.AbstractTreeStructure
11+ import com.intellij.openapi.Disposable
12+ import com.intellij.openapi.actionSystem.ActionManager
13+ import com.intellij.openapi.actionSystem.DefaultActionGroup
14+ import com.intellij.openapi.editor.markup.EffectType
15+ import com.intellij.openapi.editor.markup.HighlighterLayer
16+ import com.intellij.openapi.editor.markup.HighlighterTargetArea
17+ import com.intellij.openapi.editor.markup.TextAttributes
18+ import com.intellij.openapi.fileEditor.FileEditorManager
19+ import com.intellij.openapi.project.Project
20+ import com.intellij.openapi.ui.SimpleToolWindowPanel
21+ import com.intellij.pom.Navigatable
22+ import com.intellij.psi.PsiDocumentManager
23+ import com.intellij.ui.JBColor
24+ import com.intellij.ui.TreeUIHelper
25+ import com.intellij.ui.components.JBScrollPane
26+ import com.intellij.ui.tree.AsyncTreeModel
27+ import com.intellij.ui.tree.StructureTreeModel
28+ import com.intellij.ui.treeStructure.Tree
29+ import com.intellij.util.ui.tree.TreeUtil
30+ import kotlinx.coroutines.runBlocking
31+ import org.jdesktop.swingx.VerticalLayout
32+ import java.awt.BorderLayout
33+ import javax.swing.JPanel
34+ import javax.swing.JProgressBar
35+ import javax.swing.SwingUtilities
36+ import javax.swing.tree.DefaultMutableTreeNode
37+ import javax.swing.tree.DefaultTreeModel
38+ import javax.swing.tree.TreePath
39+
40+ class TokenTreePanel (private val project : Project ) :
41+ SimpleToolWindowPanel (false , false ),
42+ RefreshablePanel , Disposable {
43+ private val progressBar = JProgressBar ()
44+
45+ private val treeModel = StructureTreeModel (TokensTreeStructure (RootNode (null )), this )
46+ private val tree = Tree (DefaultTreeModel (DefaultMutableTreeNode ())).apply {
47+ setModel(AsyncTreeModel (treeModel, this @TokenTreePanel))
48+ isRootVisible = true
49+ showsRootHandles = true
50+
51+ TreeUIHelper .getInstance()
52+ .installTreeSpeedSearch(this , { path ->
53+ val treeNode = path.lastPathComponent as ? DefaultMutableTreeNode
54+ val tokenNode = treeNode?.userObject as ? TokenNode
55+
56+ tokenNode?.node?.value
57+ }, true )
58+ }
59+ val service: TokensObjectTreeDumperService = project.getService(TokensObjectTreeDumperService ::class .java)
60+
61+
62+ init {
63+ treeModel.invalidateAsync()
64+
65+ createToolbar()
66+ createContent()
67+
68+ addTreeListeners()
69+
70+ SwingUtilities .invokeLater { refreshData() }
71+ }
72+
73+ fun createToolbar () {
74+ val actionGroup = DefaultActionGroup ().apply {
75+ add(RunDumpTokensCommandAction (service))
76+ addSeparator()
77+ }
78+
79+ val actionToolbar = ActionManager .getInstance().createActionToolbar(" Tree Toolbar" , actionGroup, false )
80+ actionToolbar.targetComponent = this
81+
82+ val toolBarPanel = JPanel (VerticalLayout ()).apply {
83+ add(
84+ JPanel (VerticalLayout ()).apply {
85+ add(createRefreshButton { refreshData() })
86+ add(createExpandsAll(tree))
87+ add(createCollapseAll(tree))
88+ }
89+ )
90+
91+ add(actionToolbar.component)
92+ }
93+ // searchTextField.addDocumentListener(this)
94+
95+ toolbar = toolBarPanel
96+ }
97+
98+ private fun createContent () {
99+ val responsivePanel = JPanel (BorderLayout ())
100+ responsivePanel.add(progressBar, BorderLayout .NORTH )
101+ responsivePanel.add(JBScrollPane (tree))
102+
103+ setContent(responsivePanel)
104+ }
105+
106+ private fun addTreeListeners () {
107+ tree.addTreeSelectionListener { event ->
108+ event.path?.let { navigation(it) }
109+ }
110+ }
111+
112+ private fun navigation (closestPathForLocation : TreePath ? ) {
113+ val lastUserObject = TreeUtil .getLastUserObject(closestPathForLocation)
114+ if (lastUserObject is TokenNode ) {
115+ val fileEditor = FileEditorManager .getInstance(project)
116+ val textEditor = fileEditor.selectedTextEditor!!
117+ val psiFile = PsiDocumentManager .getInstance(project).getPsiFile(textEditor.document)
118+ val node = lastUserObject.node
119+ val element = psiFile?.findElementAt(node.pos)
120+ if (element is Navigatable ) {
121+ val markupModel = textEditor.markupModel
122+ val textAttributes = TextAttributes ().apply {
123+ effectType = EffectType .BOXED
124+ effectColor = JBColor .RED
125+ backgroundColor = null
126+ }
127+
128+ markupModel.removeAllHighlighters()
129+ markupModel.addRangeHighlighter(
130+ node.pos,
131+ node.endPos,
132+ HighlighterLayer .SELECTION + 1 ,
133+ textAttributes,
134+ HighlighterTargetArea .EXACT_RANGE
135+ )
136+ }
137+ }
138+ }
139+
140+ private fun refreshData () {
141+ progressBar.setIndeterminate(true )
142+ progressBar.isVisible = true
143+ tree.emptyText.text = " Loading..."
144+
145+
146+ project.nonBlocking({ getViewData() }) { result ->
147+ tree.emptyText.text = " Nothing to show"
148+ rebuildTree(result)
149+
150+ progressBar.setIndeterminate(false )
151+ progressBar.isVisible = false
152+ }
153+ }
154+
155+ private fun rebuildTree (list : TokensList ? ) {
156+ val treeModel = StructureTreeModel <AbstractTreeStructure >(TokensTreeStructure (RootNode (list)), this )
157+ tree.setModel(AsyncTreeModel (treeModel, this ))
158+ tree.setRootVisible(false )
159+ treeModel.invalidateAsync()
160+
161+ TreeUtil .expandAll(tree)
162+ }
163+
164+ private fun getViewData (): TokensList {
165+ val result = TokensList ()
166+ val editor = FileEditorManager .getInstance(project).selectedTextEditor ? : return result
167+ val virtualFile = editor.virtualFile ? : return result
168+
169+ val runBlocking = runBlocking {
170+ val a = service.dump(virtualFile)
171+
172+ a
173+ }
174+ println (" result is $runBlocking " )
175+
176+ return runBlocking as ? TokensList ? : result
177+ }
178+
179+ override fun refresh (project : Project ) {
180+ refreshData()
181+ }
182+
183+ override fun dispose () {
184+ }
185+ }
0 commit comments