Skip to content

Commit 60bb370

Browse files
committed
feat(coder): add auto repair diff functionality
- Add enableAutoRepairDiff setting in AutoDevCoderConfigurable - Implement auto repair diff logic in SingleFileDiffSketch - Update AutoDevIcons and AutoDevStatus to use new InProgress icon - Add new properties for auto repair diff in AutoDevBundle
1 parent 76c718e commit 60bb370

File tree

10 files changed

+94
-11
lines changed

10 files changed

+94
-11
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ developer experience within the IDE.
3737
| **Go Playground Sketch`*`** | Go Playground with bidirectional code preview/edit | ![](https://unitmesh.cc/auto-dev/sketch-go-playground.png) |
3838
| **Mermaid Sketch`*`** | Real-time flowchart preview/edit with bidirectional binding | ![](https://shire.run/images/shire-sketch-mermaid.png) |
3939
| **PlantUML Sketch`*`** | UML diagram editor with bidirectional code-diagram synchronization | ![](https://shire.run/images/shire-sketch-plantuml.png) |
40-
| **PlanSketch `** | Display AutoDev Planner planning for resovling code issues | ![](https://unitmesh.cc/auto-dev/autodev-plan-sketch.png) |
40+
| **PlanSketch** | Display AutoDev Planner planning for resovling code issues | ![](https://unitmesh.cc/auto-dev/autodev-plan-sketch.png) |
4141

4242
`*` means requires additional plugin installation.
4343

core/src/main/kotlin/cc/unitmesh/devti/AutoDevIcons.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ object AutoDevIcons {
2424
val COMMAND: Icon = IconLoader.getIcon("/icons/devins-command.svg", AutoDevIcons::class.java)
2525

2626
@JvmField
27-
val IntProgress = AnimatedIcon.Default()
27+
val InProgress = AnimatedIcon.Default()
2828

2929
@JvmField
3030
val Send: Icon = IconLoader.getIcon("/icons/send.svg", AutoDevIcons::class.java)

core/src/main/kotlin/cc/unitmesh/devti/settings/coder/AutoDevCoderConfigurable.kt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ class AutoDevCoderConfigurable(private val project: Project) : BoundConfigurable
2424
private val enableObserver = JCheckBox()
2525
private val teamPromptsField = JTextField()
2626
private val trimCodeBeforeSend = JCheckBox()
27+
private val enableAutoRepairDiff = JCheckBox()
2728

2829

2930
val settings = project.service<AutoDevCoderSettingService>()
@@ -102,6 +103,15 @@ class AutoDevCoderConfigurable(private val project: Project) : BoundConfigurable
102103
)
103104
}
104105

106+
row(jLabel("settings.autodev.coder.enableAutoRepairDiff")) {
107+
fullWidthCell(enableAutoRepairDiff)
108+
.bind(
109+
componentGet = { it.isSelected },
110+
componentSet = { component, value -> component.isSelected = value },
111+
prop = state::enableAutoRepairDiff.toMutableProperty()
112+
)
113+
}
114+
105115
row(jLabel("settings.external.team.prompts.path")) {
106116
fullWidthCell(teamPromptsField)
107117
.bind(
@@ -122,6 +132,7 @@ class AutoDevCoderConfigurable(private val project: Project) : BoundConfigurable
122132
it.teamPromptsDir = state.teamPromptsDir
123133
it.enableExportAsMcpServer = state.enableExportAsMcpServer
124134
it.enableObserver = state.enableObserver
135+
it.enableAutoRepairDiff = state.enableAutoRepairDiff
125136
}
126137
}
127138
}

core/src/main/kotlin/cc/unitmesh/devti/settings/coder/AutoDevCoderSettingService.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ class AutoDevCoderSettingService(
2424
var disableAdvanceContext by property(false)
2525
var enableExportAsMcpServer by property(false)
2626
var enableObserver by property(true)
27+
var enableAutoRepairDiff by property(true)
2728
var inEditorCompletion by property(false)
2829
var noChatHistory by property(false)
2930
var trimCodeBeforeSend by property(false)

core/src/main/kotlin/cc/unitmesh/devti/settings/customize/CustomizeConfigurable.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ class CustomizeConfigurable(val project: Project) : BoundConfigurable(AutoDevBun
6161
}
6262
}
6363

64-
link(AutoDevBundle.message("open documents"), {
64+
link(AutoDevBundle.message("custom.agent.open.documents"), {
6565
BrowserUtil.browse("https://ide.unitmesh.cc/agent/custom-ai-agent")
6666
})
6767
}

core/src/main/kotlin/cc/unitmesh/devti/sketch/ui/patch/DiffRepair.kt

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,31 @@ fun applyDiffRepairSuggestion(project: Project, editor: Editor, oldCode: String,
4040
}
4141
}
4242
}
43+
}
44+
45+
fun applyDiffRepairSuggestionSync(
46+
project: Project,
47+
oldCode: String,
48+
patchedCode: String,
49+
callback: (newContent: String) -> Unit
50+
) {
51+
val templateRender = TemplateRender(GENIUS_CODE)
52+
val template = templateRender.getTemplate("repair-diff.vm")
53+
54+
val intention = project.getService(AgentStateService::class.java).buildOriginIntention()
55+
56+
templateRender.context = DiffRepairContext(intention, patchedCode, oldCode)
57+
val prompt = templateRender.renderTemplate(template)
58+
59+
val flow: Flow<String> = LlmFactory.create(project, ModelType.FastApply).stream(prompt, "", false)
60+
AutoDevCoroutineScope.Companion.scope(project).launch {
61+
val suggestion = StringBuilder()
62+
flow.cancellable().collect { char ->
63+
suggestion.append(char)
64+
return@collect
65+
}
66+
67+
val code = CodeFence.Companion.parse(suggestion.toString())
68+
callback.invoke(code.text)
69+
}
4370
}

core/src/main/kotlin/cc/unitmesh/devti/sketch/ui/patch/SingleFileDiffSketch.kt

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1+
// filepath: /Volumes/source/ai/autocrud/core/src/main/kotlin/cc/unitmesh/devti/sketch/ui/patch/SingleFileDiffSketch.kt
12
package cc.unitmesh.devti.sketch.ui.patch
23

34
import cc.unitmesh.devti.AutoDevBundle
5+
import cc.unitmesh.devti.AutoDevIcons
6+
import cc.unitmesh.devti.settings.coder.coderSetting
47
import cc.unitmesh.devti.sketch.lint.SketchCodeInspection
58
import cc.unitmesh.devti.sketch.ui.LangSketch
69
import cc.unitmesh.devti.template.context.TemplateContext
@@ -14,8 +17,10 @@ import com.intellij.openapi.command.CommandProcessor
1417
import com.intellij.openapi.command.WriteCommandAction
1518
import com.intellij.openapi.diagnostic.logger
1619
import com.intellij.openapi.diff.impl.patch.ApplyPatchStatus
20+
import com.intellij.openapi.diff.impl.patch.PatchHunk
1721
import com.intellij.openapi.diff.impl.patch.PatchLine
1822
import com.intellij.openapi.diff.impl.patch.TextFilePatch
23+
import com.intellij.openapi.diff.impl.patch.TextPatchBuilder
1924
import com.intellij.openapi.diff.impl.patch.apply.GenericPatchApplier
2025
import com.intellij.openapi.fileEditor.FileDocumentManager
2126
import com.intellij.openapi.fileEditor.FileEditorManager
@@ -33,6 +38,7 @@ import com.intellij.util.LocalTimeCounter
3338
import java.awt.BorderLayout
3439
import java.awt.event.MouseAdapter
3540
import java.awt.event.MouseEvent
41+
import java.nio.charset.Charset
3642
import javax.swing.BorderFactory
3743
import javax.swing.JButton
3844
import javax.swing.JComponent
@@ -42,21 +48,22 @@ import javax.swing.JPanel
4248
class SingleFileDiffSketch(
4349
private val myProject: Project,
4450
private val currentFile: VirtualFile,
45-
val patch: TextFilePatch,
51+
var patch: TextFilePatch,
4652
val viewDiffAction: () -> Unit
4753
) : LangSketch {
4854
private val mainPanel: JPanel = JPanel(VerticalLayout(5))
4955
private val myHeaderPanel: JPanel = JPanel(BorderLayout())
5056
private var patchActionPanel: JPanel? = null
5157
private val oldCode = currentFile.readText()
52-
private val appliedPatch = try {
58+
private var appliedPatch = try {
5359
GenericPatchApplier.apply(oldCode, patch.hunks)
5460
} catch (e: Exception) {
5561
logger<SingleFileDiffSketch>().warn("Failed to apply patch: ${patch.beforeFileName}", e)
5662
null
5763
}
5864

5965
private val newCode = appliedPatch?.patchedText ?: ""
66+
private val isAutoRepair = myProject.coderSetting.state.enableAutoRepairDiff
6067

6168
init {
6269
val contentPanel = JPanel(BorderLayout())
@@ -132,6 +139,25 @@ class SingleFileDiffSketch(
132139
ApplicationManager.getApplication().executeOnPooledThread {
133140
lintCheckForNewCode(currentFile)
134141
}
142+
143+
if (isAutoRepair) {
144+
ApplicationManager.getApplication().executeOnPooledThread {
145+
applyDiffRepairSuggestionSync(myProject, oldCode, newCode, { fixedCode ->
146+
createPatchFromCode(oldCode, fixedCode)?.let { patch ->
147+
this@SingleFileDiffSketch.patch = patch
148+
appliedPatch = try {
149+
GenericPatchApplier.apply(oldCode, patch.hunks)
150+
} catch (e: Exception) {
151+
logger<SingleFileDiffSketch>().warn("Failed to apply patch: ${patch.beforeFileName}", e)
152+
null
153+
}
154+
155+
this.mainPanel.revalidate()
156+
this.mainPanel.repaint()
157+
}
158+
});
159+
}
160+
}
135161
}
136162

137163
private fun createActionButtons(): List<JButton> {
@@ -171,7 +197,12 @@ class SingleFileDiffSketch(
171197
}
172198

173199
val repairButton = JButton("Repair").apply {
174-
icon = AllIcons.Toolwindows.ToolWindowBuild
200+
icon = if (isAutoRepair) {
201+
AutoDevIcons.InProgress
202+
} else {
203+
AllIcons.Toolwindows.ToolWindowBuild
204+
}
205+
175206
toolTipText = AutoDevBundle.message("sketch.patch.action.repairDiff.tooltip")
176207
isEnabled = appliedPatch?.status != ApplyPatchStatus.SUCCESS
177208
foreground = if (isEnabled) JBColor(0xFF0000, 0xFF0000) else JPanel().background
@@ -223,3 +254,14 @@ data class DiffRepairContext(
223254
fun VirtualFile.readText(): String {
224255
return VfsUtilCore.loadText(this)
225256
}
257+
258+
fun createPatchFromCode(oldCode: String, newCode: String): TextFilePatch? {
259+
val buildPatchHunks: List<PatchHunk> = TextPatchBuilder.buildPatchHunks(oldCode, newCode)
260+
val textFilePatch = TextFilePatch(Charset.defaultCharset())
261+
buildPatchHunks.forEach { hunk ->
262+
textFilePatch.addHunk(hunk)
263+
}
264+
265+
return textFilePatch
266+
}
267+

core/src/main/kotlin/cc/unitmesh/devti/statusbar/AutoDevStatus.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ enum class AutoDevStatus {
1515
return when (this) {
1616
WAITING -> AutoDevIcons.DARK
1717
Ready -> AutoDevIcons.AI_COPILOT
18-
InProgress -> AutoDevIcons.IntProgress
18+
InProgress -> AutoDevIcons.InProgress
1919
Error -> AutoDevIcons.ERROR
2020
Done -> AutoDevIcons.AI_COPILOT
2121
}

core/src/main/resources/messages/AutoDevBundle_en.properties

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,10 +123,11 @@ prompts.autodev.fixProblem= Help me fix problem:
123123
prompts.autodev.generateReleaseNote= generate release note
124124
prompts.autodev.generateTestData=Generate API test request (with `http request` code block) based on given {0} code and request/response info. So that we can use it to test for APIs.
125125
settings.autodev.coder.enableRenameSuggestion=Enable Rename suggestion
126-
shell.command.suggestion.action.default.text=How to checkout a branch?
127-
batch.nothing.to.testing=No thing to AutoTest
126+
settings.autodev.coder.enableAutoRepairDiff=Enable auto repair diff
127+
shell.command.suggestion.action.default.text=How to check out a branch?
128+
batch.nothing.to.testing=Nothing to AutoTest
128129
intentions.chat.code.test.verify=Verify test
129-
open\ documents=Open Documents
130+
custom.agent.open.documents=Open Documents
130131
settings.autodev.coder.testConnectionButton.tips=Don't forget to APPLY change before test connection!
131132

132133
sketch.patch.action.accept=Accept

core/src/main/resources/messages/AutoDevBundle_zh.properties

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,10 +124,11 @@ prompts.autodev.fixProblem=帮我修复问题:
124124
prompts.autodev.generateReleaseNote=生成发布说明
125125
prompts.autodev.generateTestData=基于给定的 {0} 代码和请求/响应信息生成 API 测试请求(使用 markdown 代码块)。这样我们就可以用它来测试 API
126126
settings.autodev.coder.enableRenameSuggestion=启用重命名建议
127+
settings.autodev.coder.enableAutoRepairDiff=启用自动修复 diff
127128
shell.command.suggestion.action.default.text=如何创建一个新的分支?
128129
batch.nothing.to.testing=没有要 AutoTest 的内容
129130
intentions.chat.code.test.verify=验证测试中
130-
open\ documents=Open Documents
131+
custom.agent.open.documents=打开文档
131132
settings.autodev.coder.testConnectionButton.tips=请记得在修改后点击应用,再进行测试!
132133

133134
sketch.patch.action.accept=Accept

0 commit comments

Comments
 (0)