@@ -10,13 +10,30 @@ import cc.unitmesh.devti.observer.plan.PlanUpdateListener
10
10
import cc.unitmesh.devti.settings.AutoDevSettingsState
11
11
import cc.unitmesh.devti.util.parser.MarkdownCodeHelper
12
12
import com.intellij.openapi.application.ApplicationManager
13
+ import com.intellij.openapi.application.ReadAction
13
14
import com.intellij.openapi.components.Service
14
15
import com.intellij.openapi.diagnostic.logger
15
- import com.intellij.openapi.diff.impl.patch.FilePatch
16
+ import com.intellij.openapi.diff.impl.patch.TextFilePatch
17
+ import com.intellij.openapi.diff.impl.patch.apply.GenericPatchApplier
18
+ import com.intellij.openapi.fileEditor.FileDocumentManager
16
19
import com.intellij.openapi.project.Project
20
+ import com.intellij.openapi.util.ThrowableComputable
21
+ import com.intellij.openapi.vcs.FilePath
17
22
import com.intellij.openapi.vcs.FileStatus
18
- import com.intellij.openapi.vcs.changes.shelf.ShelvedChange
23
+ import com.intellij.openapi.vcs.VcsBundle
24
+ import com.intellij.openapi.vcs.VcsException
25
+ import com.intellij.openapi.vcs.changes.Change
26
+ import com.intellij.openapi.vcs.changes.ContentRevision
27
+ import com.intellij.openapi.vcs.changes.CurrentContentRevision
28
+ import com.intellij.openapi.vcs.changes.TextRevisionNumber
29
+ import com.intellij.openapi.vcs.history.VcsRevisionNumber
30
+ import com.intellij.openapi.vfs.VirtualFile
31
+ import com.intellij.vcsUtil.VcsUtil
32
+ import org.jetbrains.annotations.NonNls
33
+ import java.io.File
34
+ import java.io.IOException
19
35
import java.nio.file.Path
36
+ import java.util.*
20
37
21
38
@Service(Service .Level .PROJECT )
22
39
class AgentStateService (val project : Project ) {
@@ -34,34 +51,89 @@ class AgentStateService(val project: Project) {
34
51
logger<AgentStateService >().info(" Called agent tools:\n ${state.usedTools.joinToString(" \n " )} " )
35
52
}
36
53
37
- fun addToChange (path : Path , change : FilePatch ) {
38
- val shelvedChange = createShelvedChange(project, path, change)
39
- if (shelvedChange != null ) {
40
- state.changes.add(shelvedChange.change)
54
+ fun addToChange (path : Path , patch : TextFilePatch ) {
55
+ val change = createChange(patch)
56
+ state.changes.add(change)
41
57
42
- ApplicationManager .getApplication().messageBus
43
- .syncPublisher(PlanUpdateListener .TOPIC )
44
- .onUpdateChange(state.changes)
45
- }
58
+ ApplicationManager .getApplication().messageBus
59
+ .syncPublisher(PlanUpdateListener .TOPIC )
60
+ .onUpdateChange(state.changes)
46
61
}
47
62
48
- fun createShelvedChange (project : Project , patchPath : Path , patch : FilePatch ): ShelvedChange ? {
49
- val beforeName: String? = patch.beforeName
50
- val afterName: String? = patch.afterName
51
- if (beforeName == null || afterName == null ) {
52
- logger<AgentStateService >().warn(" Failed to parse the file patch: [$patchPath ]:$patch " )
53
- return null
54
- }
63
+ private fun createChange (patch : TextFilePatch ): Change {
64
+ val baseDir = File (project.basePath!! )
65
+ val beforePath = patch.beforeName
66
+ val afterPath = patch.afterName
55
67
56
- val status = if (patch.isNewFile) {
68
+ val fileStatus = if (patch.isNewFile) {
57
69
FileStatus .ADDED
58
70
} else if (patch.isDeletedFile) {
59
71
FileStatus .DELETED
60
72
} else {
61
73
FileStatus .MODIFIED
62
74
}
63
75
64
- return ShelvedChange .create(project, patchPath, beforeName, afterName, status)
76
+ val beforeFilePath = VcsUtil .getFilePath(getAbsolutePath(baseDir, beforePath), false )
77
+ val afterFilePath = VcsUtil .getFilePath(getAbsolutePath(baseDir, afterPath), false )
78
+
79
+ var beforeRevision: ContentRevision ? = null
80
+ if (fileStatus != = FileStatus .ADDED ) {
81
+ beforeRevision = object : CurrentContentRevision (beforeFilePath) {
82
+ override fun getRevisionNumber (): VcsRevisionNumber {
83
+ return TextRevisionNumber (VcsBundle .message(" local.version.title" ))
84
+ }
85
+ }
86
+ }
87
+
88
+ var afterRevision: ContentRevision ? = null
89
+ if (fileStatus != = FileStatus .DELETED ) {
90
+ afterRevision = object : CurrentContentRevision (beforeFilePath) {
91
+ override fun getRevisionNumber (): VcsRevisionNumber =
92
+ TextRevisionNumber (VcsBundle .message(" local.version.title" ))
93
+
94
+ override fun getVirtualFile (): VirtualFile ? = afterFilePath.virtualFile
95
+ override fun getFile (): FilePath = afterFilePath
96
+ override fun getContent (): @NonNls String? {
97
+ if (patch.isNewFile) {
98
+ return patch.singleHunkPatchText
99
+ }
100
+ if (patch.isDeletedFile) {
101
+ return null
102
+ }
103
+
104
+ val localContent: String = loadLocalContent()
105
+ val appliedPatch = GenericPatchApplier .apply (localContent, patch.getHunks())
106
+ if (appliedPatch != null ) {
107
+ return appliedPatch.patchedText
108
+ }
109
+ throw VcsException (VcsBundle .message(" patch.apply.error.conflict" ))
110
+ }
111
+
112
+ @Throws(VcsException ::class )
113
+ private fun loadLocalContent (): String {
114
+ return ReadAction .compute<String ?, VcsException ?>(ThrowableComputable {
115
+ val file: VirtualFile ? = beforeFilePath.virtualFile
116
+ if (file == null ) throw VcsException (" File $beforeFilePath not found" )
117
+ val doc = FileDocumentManager .getInstance().getDocument(file)
118
+ if (doc == null ) throw VcsException (" Document $file not found" )
119
+ doc.text
120
+ })
121
+ }
122
+ }
123
+ }
124
+ return Change (beforeRevision, afterRevision, fileStatus)
125
+ }
126
+
127
+ private fun getAbsolutePath (baseDir : File , relativePath : String ): File {
128
+ var file: File ?
129
+ try {
130
+ file = File (baseDir, relativePath).getCanonicalFile()
131
+ } catch (e: IOException ) {
132
+ logger<AgentStateService >().info(e)
133
+ file = File (baseDir, relativePath)
134
+ }
135
+
136
+ return file
65
137
}
66
138
67
139
fun buildOriginIntention (): String? {
@@ -119,4 +191,4 @@ class AgentStateService(val project: Project) {
119
191
fun getPlan (): MutableList <AgentTaskEntry > {
120
192
return state.plan
121
193
}
122
- }
194
+ }
0 commit comments