Skip to content

Commit 9eb7a97

Browse files
committed
feat: add opcache settings panel
1 parent 29d5adc commit 9eb7a97

File tree

13 files changed

+356
-11
lines changed

13 files changed

+356
-11
lines changed

build.gradle.kts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ plugins {
1010
alias(libs.plugins.changelog) // Gradle Changelog Plugin
1111
alias(libs.plugins.qodana) // Gradle Qodana Plugin
1212
alias(libs.plugins.kover) // Gradle Kover Plugin
13-
kotlin("plugin.serialization") version "2.1.20"
13+
kotlin("plugin.serialization") version "2.2.0"
1414
}
1515

1616
group = providers.gradleProperty("pluginGroup").get()
@@ -33,7 +33,8 @@ repositories {
3333

3434
// Dependencies are managed with Gradle version catalog - read more: https://docs.gradle.org/current/userguide/platforms.html#sub:version-catalog
3535
dependencies {
36-
compileOnly("org.jetbrains.kotlinx:kotlinx-serialization-json:1.8.1")
36+
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.9.0")
37+
// compileOnly("org.jetbrains.kotlinx:kotlinx-serialization-json:1.9.0")
3738

3839
testImplementation(libs.junit)
3940
testImplementation(libs.opentest4j)
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
function dump(array $config)
4+
{
5+
$result = [];
6+
foreach ($config as $key => $value) {
7+
if (is_array($value)) {
8+
$result[] = [
9+
'name' => $key,
10+
'value' => '',
11+
'children' => dump($value),
12+
];
13+
} else {
14+
$result[] = [
15+
'name' => $key,
16+
'value' => $value,
17+
'children' => [],
18+
];
19+
}
20+
}
21+
return $result;
22+
}
23+
24+
echo json_encode(dump([
25+
'configuration' => opcache_get_configuration(),
26+
'status' => opcache_get_status(true),
27+
]));

src/main/kotlin/com/github/xepozz/php_dump/CompositeWindowFactory.kt

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.github.xepozz.php_dump
22

3+
import com.github.xepozz.php_dump.panel.OpcacheSettingsPanel
34
import com.github.xepozz.php_dump.panel.OpcodesTerminalPanel
45
import com.github.xepozz.php_dump.panel.TokenTreePanel
56
import com.github.xepozz.php_dump.panel.TokensObjectTerminalPanel
@@ -16,12 +17,21 @@ open class CompositeWindowFactory : ToolWindowFactory, DumbAware {
1617
val contentManager = toolWindow.contentManager
1718

1819
val opcodesTerminalLayout = OpcodesTerminalPanel(project)
20+
val opcodesSettingsLayout = OpcacheSettingsPanel(project)
1921
val tokensTerminalLayout = TokensTerminalPanel(project)
2022
val tokensObjectTerminalLayout = TokensObjectTerminalPanel(project)
2123
val tokenTreeLayout = TokenTreePanel(project)
2224

2325
contentFactory.apply {
24-
this.createContent(opcodesTerminalLayout, "Opcodes", false).apply {
26+
this.createContent(opcodesTerminalLayout, "OpCodes", false).apply {
27+
contentManager.addContent(
28+
this.apply {
29+
this.isPinnable = true
30+
this.isCloseable = false
31+
}
32+
)
33+
}
34+
this.createContent(opcodesSettingsLayout, "OpCache Settings", false).apply {
2535
contentManager.addContent(
2636
this.apply {
2737
this.isPinnable = true
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
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.OpcacheSettingsTreeDumperService
6+
import com.github.xepozz.php_dump.stubs.any_tree.AnyNodeList
7+
import com.github.xepozz.php_dump.stubs.any_tree.AnyRootNode
8+
import com.github.xepozz.php_dump.stubs.any_tree.AnyTreeStructure
9+
import com.github.xepozz.php_dump.stubs.any_tree.LeafNode
10+
import com.github.xepozz.php_dump.tree.RootNode
11+
import com.github.xepozz.php_dump.tree.TokensTreeStructure
12+
import com.intellij.ide.util.treeView.AbstractTreeStructure
13+
import com.intellij.openapi.Disposable
14+
import com.intellij.openapi.actionSystem.ActionManager
15+
import com.intellij.openapi.actionSystem.DefaultActionGroup
16+
import com.intellij.openapi.fileEditor.FileEditorManager
17+
import com.intellij.openapi.project.Project
18+
import com.intellij.openapi.ui.SimpleToolWindowPanel
19+
import com.intellij.ui.TreeUIHelper
20+
import com.intellij.ui.components.JBScrollPane
21+
import com.intellij.ui.tree.AsyncTreeModel
22+
import com.intellij.ui.tree.StructureTreeModel
23+
import com.intellij.ui.treeStructure.Tree
24+
import com.intellij.util.ui.tree.TreeUtil
25+
import kotlinx.coroutines.runBlocking
26+
import org.jdesktop.swingx.VerticalLayout
27+
import java.awt.BorderLayout
28+
import javax.swing.JPanel
29+
import javax.swing.JProgressBar
30+
import javax.swing.SwingUtilities
31+
import javax.swing.tree.DefaultMutableTreeNode
32+
import javax.swing.tree.DefaultTreeModel
33+
34+
class OpcacheSettingsPanel(private val project: Project) :
35+
SimpleToolWindowPanel(false, false),
36+
RefreshablePanel, Disposable {
37+
private val progressBar = JProgressBar()
38+
39+
private val treeModel = StructureTreeModel(TokensTreeStructure(RootNode(null)), this)
40+
private val tree = Tree(DefaultTreeModel(DefaultMutableTreeNode())).apply {
41+
setModel(AsyncTreeModel(treeModel, this@OpcacheSettingsPanel))
42+
isRootVisible = true
43+
showsRootHandles = true
44+
45+
TreeUIHelper.getInstance()
46+
.installTreeSpeedSearch(this, { path ->
47+
val treeNode = path.lastPathComponent as? DefaultMutableTreeNode
48+
val tokenNode = treeNode?.userObject as? LeafNode
49+
50+
tokenNode?.node?.value
51+
}, true)
52+
}
53+
val service: OpcacheSettingsTreeDumperService = project.getService(OpcacheSettingsTreeDumperService::class.java)
54+
55+
56+
init {
57+
treeModel.invalidateAsync()
58+
59+
createToolbar()
60+
createContent()
61+
62+
SwingUtilities.invokeLater { refreshData() }
63+
}
64+
65+
fun createToolbar() {
66+
val actionGroup = DefaultActionGroup().apply {
67+
add(RunDumpTokensCommandAction(service, "Dump Tree"))
68+
addSeparator()
69+
}
70+
71+
val actionToolbar = ActionManager.getInstance().createActionToolbar("Tree Toolbar", actionGroup, false)
72+
actionToolbar.targetComponent = this
73+
74+
val toolBarPanel = JPanel(VerticalLayout()).apply {
75+
add(
76+
JPanel(VerticalLayout()).apply {
77+
add(createRefreshButton { refreshData() })
78+
add(createExpandsAll(tree))
79+
add(createCollapseAll(tree))
80+
}
81+
)
82+
83+
add(actionToolbar.component)
84+
}
85+
// searchTextField.addDocumentListener(this)
86+
87+
toolbar = toolBarPanel
88+
}
89+
90+
private fun createContent() {
91+
val responsivePanel = JPanel(BorderLayout())
92+
responsivePanel.add(progressBar, BorderLayout.NORTH)
93+
responsivePanel.add(JBScrollPane(tree))
94+
95+
setContent(responsivePanel)
96+
}
97+
98+
private fun refreshData() {
99+
progressBar.setIndeterminate(true)
100+
progressBar.isVisible = true
101+
tree.emptyText.text = "Loading..."
102+
103+
104+
project.nonBlocking({ getViewData() }) { result ->
105+
tree.emptyText.text = "Nothing to show"
106+
rebuildTree(result)
107+
108+
progressBar.setIndeterminate(false)
109+
progressBar.isVisible = false
110+
}
111+
}
112+
113+
private fun rebuildTree(list: AnyNodeList?) {
114+
val treeModel = StructureTreeModel<AbstractTreeStructure>(AnyTreeStructure(AnyRootNode(list)), this)
115+
tree.setModel(AsyncTreeModel(treeModel, this))
116+
tree.setRootVisible(false)
117+
treeModel.invalidateAsync()
118+
119+
TreeUtil.expandAll(tree)
120+
}
121+
122+
private fun getViewData(): AnyNodeList {
123+
val result = AnyNodeList()
124+
val editor = FileEditorManager.getInstance(project).selectedTextEditor ?: return result
125+
val virtualFile = editor.virtualFile ?: return result
126+
127+
val runBlocking = runBlocking { service.dump(virtualFile) }
128+
println("result is $runBlocking")
129+
130+
return runBlocking as? AnyNodeList ?: result
131+
}
132+
133+
override fun refresh(project: Project, type: RefreshType) {
134+
refreshData()
135+
}
136+
137+
override fun dispose() {
138+
}
139+
}

src/main/kotlin/com/github/xepozz/php_dump/panel/TokenTreePanel.kt

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -166,12 +166,8 @@ class TokenTreePanel(private val project: Project) :
166166
val editor = FileEditorManager.getInstance(project).selectedTextEditor ?: return result
167167
val virtualFile = editor.virtualFile ?: return result
168168

169-
val runBlocking = runBlocking {
170-
val a = service.dump(virtualFile)
171-
172-
a
173-
}
174-
println("result is $runBlocking")
169+
val runBlocking = runBlocking { service.dump(virtualFile) }
170+
// println("result is $runBlocking")
175171

176172
return runBlocking as? TokensList ?: result
177173
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package com.github.xepozz.php_dump.services
2+
3+
import com.github.xepozz.php_dump.stubs.any_tree.AnyNodeList
4+
import com.github.xepozz.php_dump.stubs.any_tree.AnyNodeParser
5+
import com.intellij.execution.process.ProcessAdapter
6+
import com.intellij.execution.process.ProcessEvent
7+
import com.intellij.execution.process.ProcessOutputTypes
8+
import com.intellij.openapi.components.Service
9+
import com.intellij.openapi.project.Project
10+
import com.intellij.openapi.util.Key
11+
import kotlinx.coroutines.Dispatchers
12+
import kotlinx.coroutines.withContext
13+
14+
@Service(Service.Level.PROJECT)
15+
class OpcacheSettingsTreeDumperService(var project: Project) : DumperServiceInterface {
16+
override suspend fun dump(file: String): Any {
17+
// language=injectablephp
18+
val phpSnippet = $$"""
19+
function dump(array $config)
20+
{
21+
$result = [];
22+
foreach ($config as $key => $value) {
23+
if (is_array($value)) {
24+
$result[] = [
25+
'name' => $key,
26+
'value' => '',
27+
'children' => dump($value),
28+
];
29+
} else {
30+
$result[] = [
31+
'name' => $key,
32+
'value' => $value,
33+
'children' => [],
34+
];
35+
}
36+
}
37+
return $result;
38+
}
39+
40+
echo json_encode(dump([
41+
'configuration' => opcache_get_configuration(),
42+
'status' => opcache_get_status(true),
43+
]));
44+
""".trimIndent()
45+
46+
return withContext(Dispatchers.IO) {
47+
val output = StringBuilder()
48+
49+
PhpCommandExecutor.execute(
50+
file,
51+
phpSnippet,
52+
project,
53+
object : ProcessAdapter() {
54+
override fun onTextAvailable(event: ProcessEvent, outputType: Key<*>) {
55+
when (outputType) {
56+
ProcessOutputTypes.STDERR -> output.append(event.text)
57+
ProcessOutputTypes.STDOUT -> output.append(event.text)
58+
}
59+
}
60+
},
61+
listOf("-dopcache.enable_cli=1"),
62+
)
63+
64+
65+
val jsonString = output.toString()
66+
// println("jsonString: $jsonString")
67+
68+
val tree: AnyNodeList = try {
69+
AnyNodeParser.parseAnyNode(jsonString)
70+
} catch (e: Throwable) {
71+
AnyNodeList()
72+
}
73+
// println("result tree: $tree")
74+
75+
return@withContext tree
76+
}
77+
}
78+
}

src/main/kotlin/com/github/xepozz/php_dump/services/PhpCommandExecutor.kt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,21 @@ import com.jetbrains.php.config.interpreters.PhpInterpretersManagerImpl
1010
import kotlin.coroutines.suspendCoroutine
1111

1212
object PhpCommandExecutor {
13-
suspend fun execute(file: String, phpSnippet: String, project: Project, processListener: ProcessAdapter) {
13+
suspend fun execute(
14+
file: String,
15+
phpSnippet: String,
16+
project: Project,
17+
processListener: ProcessAdapter,
18+
processArguments: List<String> = emptyList()
19+
) {
1420
val interpretersManager = PhpInterpretersManagerImpl.getInstance(project)
1521
val interpreter = PhpProjectConfigurationFacade.getInstance(project).interpreter
1622
?: interpretersManager.interpreters.firstOrNull() ?: return
1723

1824
val interpreterPath = interpreter.pathToPhpExecutable ?: return
1925
val commandArgs = buildList {
2026
add(interpreterPath)
27+
addAll(processArguments)
2128
add("-r")
2229
add(phpSnippet)
2330

src/main/kotlin/com/github/xepozz/php_dump/services/TokensObjectTreeDumperService.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ class TokensObjectTreeDumperService(var project: Project) : DumperServiceInterfa
4444

4545

4646
val jsonString = output.toString()
47-
// println("jsonString: $jsonString")
47+
println("jsonString: $jsonString")
4848

4949
val tree = TokenParser.parseTokens(jsonString)
5050
// println("result tree: $tree")
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.github.xepozz.php_dump.stubs.any_tree
2+
3+
import kotlinx.serialization.ExperimentalSerializationApi
4+
import kotlinx.serialization.Serializable
5+
import kotlinx.serialization.json.JsonIgnoreUnknownKeys
6+
7+
@Serializable
8+
data class AnyNodeList(
9+
val children: List<AnyNode> = listOf(),
10+
)
11+
12+
@OptIn(ExperimentalSerializationApi::class)
13+
@Serializable
14+
@JsonIgnoreUnknownKeys
15+
data class AnyNode(
16+
val name: String,
17+
val value: String = "",
18+
// @kotlinx.serialization.Transient
19+
val children: Collection<AnyNode> = emptyList()
20+
) {
21+
override fun toString(): String {
22+
return "AnyNode(name='$name', value='$value', children=$children)"
23+
}
24+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.github.xepozz.php_dump.stubs.any_tree
2+
3+
import kotlinx.serialization.json.Json
4+
5+
object AnyNodeParser {
6+
private val json = Json {
7+
ignoreUnknownKeys = true
8+
isLenient = true
9+
}
10+
11+
fun parseAnyNode(jsonString: String) = AnyNodeList(json.decodeFromString<List<AnyNode>>(jsonString))
12+
13+
fun serializeAnyNodes(tokens: List<AnyNode>): String = json.encodeToString(AnyNodeList.serializer(), AnyNodeList(tokens))
14+
}

0 commit comments

Comments
 (0)