Skip to content

Commit

Permalink
[dynamic] methodCall Intention
Browse files Browse the repository at this point in the history
  • Loading branch information
AntoniRokitnicki committed Jun 10, 2024
1 parent abeb157 commit e1917da
Show file tree
Hide file tree
Showing 8 changed files with 236 additions and 6 deletions.
6 changes: 5 additions & 1 deletion resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,12 @@
<codeFoldingOptionsProvider instance="com.intellij.advancedExpressionFolding.AdvancedExpressionFoldingOptionsProvider"/>
<applicationService serviceImplementation="com.intellij.advancedExpressionFolding.AdvancedExpressionFoldingSettings"/>
<editorFactoryListener implementation="com.intellij.advancedExpressionFolding.FoldingEditorCreatedListener"/>
</extensions>
<intentionAction>
<language>JAVA</language>
<className>com.intellij.advancedExpressionFolding.extension.methodcall.dynamic.AddDynamicMethodFoldingIntention</className>
</intentionAction>

</extensions>
<actions>
<action id="advanced.folding.toggle.global" class="com.intellij.advancedExpressionFolding.GlobalToggleFoldingAction"
text="Advanced Folding: Global" description="Toggle advanced folding globally across all files">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>AJF2: Dynamic Method Folding</title>
</head>
<body>
<h1>AJF2: Dynamic Method Folding</h1>
<p>This intention allows you to dynamically fold methods in your code. With this intention, you can easily rename or remove method calls using a simple dialog interface.</p>

<h2>Usage</h2>
<ol>
<li>Place the caret on the method call you wish to fold.</li>
<li>Invoke the intention by pressing <strong>Alt+Enter</strong> and select <em>AJF2: Dynamic method folding</em>.</li>
<li>A dialog will appear prompting you to choose an action:</li>
<ul>
<li><strong>Rename:</strong> Enter a new method name.</li>
<li><strong>Remove:</strong> Remove the method call.</li>
<li><strong>Cancel:</strong> Cancel the operation.</li>
</ul>
</ol>

<h2>Dialog Options</h2>
<p>When the dialog is shown, you can choose to rename the method, remove it, or cancel the operation. If you choose to rename the method, you must provide a new name for it. If you choose to remove the method, it will be removed from the configuration file.</p>

<h2>Implementation Details</h2>
<p>The implementation leverages the <code>DialogWrapper</code> class to create a custom dialog for method folding. The actions are defined in the <code>Action</code> enum, and the results are handled appropriately based on the user's selection.</p>

<h2>Conclusion</h2>
<p>With the AJF2: Dynamic Method Folding intention, managing method calls in your code becomes simpler and more efficient. You can easily rename or remove method calls without manually editing the configuration file.</p>
</body>
</html>
14 changes: 14 additions & 0 deletions src/com/intellij/advancedExpressionFolding/FoldingService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import com.intellij.advancedExpressionFolding.extension.Keys
import com.intellij.openapi.components.Service
import com.intellij.openapi.components.service
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.fileEditor.FileEditorManager
import com.intellij.openapi.fileEditor.TextEditor
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiRecursiveElementVisitor
Expand All @@ -24,6 +27,17 @@ class FoldingService {
}
}

fun clearAllKeys(project: Project) {

FileEditorManager.getInstance(project).allEditors.mapNotNull {
(it as? TextEditor)?.editor
}.forEach {
clearAllKeys(it)
}

}


fun clearAllKeys(editor: Editor) {
val project = editor.project ?: return
val psiFile = PsiDocumentManager.getInstance(project).getPsiFile(editor.document) ?: return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import com.intellij.advancedExpressionFolding.extension.methodcall.date.CreateDa
import com.intellij.advancedExpressionFolding.extension.methodcall.date.IsAfterDateMethodCall
import com.intellij.advancedExpressionFolding.extension.methodcall.date.IsBeforeDateMethodCall
import com.intellij.advancedExpressionFolding.extension.methodcall.dynamic.ConfigurationParser
import com.intellij.advancedExpressionFolding.extension.methodcall.dynamic.DynamicMethodCall
import com.intellij.advancedExpressionFolding.extension.methodcall.dynamic.IDynamicDataProvider
import com.intellij.advancedExpressionFolding.extension.methodcall.nullable.CheckNotNullMethodCall
import com.intellij.advancedExpressionFolding.extension.on
Expand Down Expand Up @@ -78,7 +79,7 @@ object MethodCallFactory : BaseExtension(){

) + (loadDynamicMethods() ?: emptyList())

private fun loadDynamicMethods() = dynamic.on(dynamicProvider?.parse())
private fun loadDynamicMethods(): List<DynamicMethodCall>? = dynamic.on(dynamicProvider?.parse())

private fun createSupportedClasses(): Collection<ClassName> =
methodCallMap.values
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package com.intellij.advancedExpressionFolding.extension.methodcall.dynamic

import com.intellij.advancedExpressionFolding.FoldingService
import com.intellij.advancedExpressionFolding.extension.methodcall.MethodCallFactory
import com.intellij.advancedExpressionFolding.extension.methodcall.MethodName
import com.intellij.codeInsight.folding.CodeFoldingManager
import com.intellij.codeInsight.intention.IntentionAction
import com.intellij.openapi.application.runInEdt
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.Messages
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiMethodCallExpression
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.annotations.Nls

class AddDynamicMethodFoldingIntention : IntentionAction {

override fun getText(): @Nls(capitalization = Nls.Capitalization.Sentence) String = "AJF2: Dynamic method folding"

override fun getFamilyName(): @Nls(capitalization = Nls.Capitalization.Sentence) String = "AJF2"

override fun isAvailable(project: Project, editor: Editor, file: PsiFile): Boolean {
val element = file.findElementAt(editor.caretModel.offset)
val methodCall = PsiTreeUtil.getParentOfType(element, PsiMethodCallExpression::class.java)
return methodCall != null
}

override fun invoke(project: Project, editor: Editor, file: PsiFile) {

val element = file.findElementAt(editor.caretModel.offset)
val methodCall = PsiTreeUtil.getParentOfType(element, PsiMethodCallExpression::class.java)

methodCall?.methodExpression?.referenceName?.let { methodName ->
when {
methodName.exists() -> {
val dialogResult = methodName.showRenameDialog()
dialogResult?.run {
val (action, newMethodName) = dialogResult
when (action) {
Action.RENAME -> {
ConfigurationParser.addOrUpdateMethod(methodName, newMethodName)
}
Action.REMOVE -> methodName.remove()
Action.CANCEL -> return
}
runInEdt {
FoldingService.get().clearAllKeys(project)
MethodCallFactory.refreshMethodCallMappings()
CodeFoldingManager.getInstance(project).updateFoldRegions(editor)
}
}
}
else -> {
val newName = methodName.getNewNameFromUser() ?: return
ConfigurationParser.addOrUpdateMethod(methodName, newName)
}
}
}
}

override fun startInWriteAction() = true

private fun MethodName.exists() = MethodCallFactory.findByMethodName(this)?.any {
it is DynamicMethodCall
} == true

private fun MethodName.getNewNameFromUser(): String? {
return Messages.showInputDialog(
"Enter new method name:",
"Add Dynamic Method Folding",
Messages.getQuestionIcon(),
this,
null
)
}

private fun MethodName.remove() = ConfigurationParser.remove(this)

}

Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.intellij.advancedExpressionFolding.extension.methodcall.dynamic

import com.intellij.util.io.readText
import java.io.File
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
Expand All @@ -16,4 +17,35 @@ object ConfigurationParser : IDynamicDataProvider {
val text = filePath.readText()
return parseToml(text)
}

fun addOrUpdateMethod(methodName: String, newName: String) {
val tomlFile = File(filePath.toUri())
val tomlMap = if (tomlFile.exists()) {
objectMapper.readValue(tomlFile, MutableMap::class.java) as MutableMap<String, Any>
} else {
mutableMapOf()
}

val methodDetails = mutableMapOf(
"method" to methodName,
"newName" to newName
)

tomlMap[methodName] = methodDetails

objectMapper.writeValue(tomlFile, tomlMap)
}

fun remove(methodName: String) {
val tomlFile = File(filePath.toUri())
if (!tomlFile.exists()) {
return
}

val tomlMap = objectMapper.readValue(tomlFile, MutableMap::class.java) as MutableMap<String, Any>
tomlMap.remove(methodName)

objectMapper.writeValue(tomlFile, tomlMap)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package com.intellij.advancedExpressionFolding.extension.methodcall.dynamic

import com.intellij.advancedExpressionFolding.extension.methodcall.MethodName
import com.intellij.openapi.ui.DialogWrapper
import com.intellij.ui.components.JBTextField
import com.intellij.ui.dsl.builder.AlignX
import com.intellij.ui.dsl.builder.panel

enum class Action {
RENAME, REMOVE, CANCEL
}

fun MethodName.showRenameDialog(): Pair<Action, String>? {
var selectedAction: Action? = null
var newMethodName: String? = null

val name = this
val textField = JBTextField(name, 20)

val dialogWrapper = object : DialogWrapper(true) {
init {
title = "Choose a new folding for method $name"
init()
}

override fun createCenterPanel() = panel {
row("") {
cell(textField)
.align(AlignX.LEFT)
.focused()
}
row("") {
button("Remove") {
selectedAction = Action.REMOVE
close(OK_EXIT_CODE)
}
}
}

override fun getPreferredFocusedComponent() = textField

override fun doOKAction() {
selectedAction = Action.RENAME
newMethodName = textField.text
super.doOKAction()
}

override fun doCancelAction() {
selectedAction = Action.CANCEL
super.doCancelAction()
}
}

dialogWrapper.show()

return when (selectedAction) {
Action.RENAME -> newMethodName?.takeIf {
it.isNotBlank()
}?.let {
Action.RENAME to it
}
Action.REMOVE -> Action.REMOVE to name
else -> null
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,17 @@ import com.fasterxml.jackson.dataformat.toml.TomlFactory
import com.intellij.advancedExpressionFolding.extension.asInstance

interface IDynamicDataProvider {
private val objectMapper: ObjectMapper
val objectMapper: ObjectMapper
get() = ObjectMapper(TomlFactory())

fun parse(): List<DynamicMethodCall>

fun parseToml(text: String): List<DynamicMethodCall> {
val mapOfMaps = objectMapper.readValue(text, Map::class.java)
val asInstance = mapOfMaps.values.asInstance<Collection<Map<String, String>>>()
return asInstance?.map {
val listOfMaps =
objectMapper.readValue(text, Map::class.java).values.asInstance<Collection<Map<String, String>>>()
return listOfMaps?.map {
DynamicMethodCall(DynamicMethodCallData(it))
} ?: emptyList()
}

}

0 comments on commit e1917da

Please sign in to comment.