@@ -15,12 +15,15 @@ import com.intellij.openapi.application.runInEdt
15
15
import com.intellij.openapi.command.WriteCommandAction
16
16
import com.intellij.openapi.components.service
17
17
import com.intellij.openapi.editor.Editor
18
+ import com.intellij.openapi.editor.ex.EditorEx
18
19
import com.intellij.openapi.fileEditor.FileEditorManager
19
20
import com.intellij.openapi.progress.ProgressIndicator
20
21
import com.intellij.openapi.progress.ProgressManager
21
22
import com.intellij.openapi.progress.Task
22
23
import com.intellij.openapi.project.Project
24
+ import com.intellij.openapi.ui.Messages
23
25
import com.intellij.openapi.util.Key
26
+ import com.intellij.openapi.vfs.LocalFileSystem
24
27
import com.intellij.openapi.vfs.VirtualFile
25
28
import com.intellij.testFramework.LightVirtualFile
26
29
import com.intellij.ui.JBColor
@@ -36,12 +39,14 @@ import ee.carlrobert.codegpt.completions.CompletionClientProvider
36
39
import ee.carlrobert.codegpt.settings.GeneralSettings
37
40
import ee.carlrobert.codegpt.settings.service.ServiceType
38
41
import ee.carlrobert.codegpt.toolwindow.chat.editor.HeaderPanel
42
+ import ee.carlrobert.codegpt.toolwindow.chat.editor.ToolWindowEditorFileDetails
39
43
import ee.carlrobert.codegpt.ui.OverlayUtil
40
44
import ee.carlrobert.codegpt.util.EditorDiffUtil.createDiffRequest
41
45
import ee.carlrobert.codegpt.util.file.FileUtil
42
46
import ee.carlrobert.llm.client.codegpt.request.AutoApplyRequest
43
47
import ee.carlrobert.llm.client.codegpt.response.CodeGPTException
44
48
import java.awt.FlowLayout
49
+ import java.io.File
45
50
import java.util.*
46
51
import javax.swing.Icon
47
52
import javax.swing.JButton
@@ -70,24 +75,13 @@ class AutoApplyAction(
70
75
e.presentation.disableAction(CodeGPTBundle .get(" toolwindow.chat.editor.action.autoApply.disabledTitle" ))
71
76
return
72
77
}
73
-
74
- val editorVirtualFile = CodeGPTKeys .TOOLWINDOW_EDITOR_VIRTUAL_FILE .get(toolwindowEditor)
75
- if (editorVirtualFile == null ) {
76
- e.presentation.disableAction(CodeGPTBundle .get(" toolwindow.chat.editor.action.autoApply.notApplicable" ))
77
- }
78
78
}
79
79
80
- override fun handleAction (event : AnActionEvent ) {
81
- val editorVirtualFile = CodeGPTKeys .TOOLWINDOW_EDITOR_VIRTUAL_FILE .get(toolwindowEditor)
82
- ? : return
83
-
84
- val request = AutoApplyRequest ().apply {
85
- suggestedChanges = toolwindowEditor.document.text
86
- fileContent = FileUtil .readContent(editorVirtualFile)
87
- }
88
-
89
- val acceptLink = createDisabledActionLink(CodeGPTBundle .get(" toolwindow.chat.editor.action.autoApply.accept" ))
90
- val rejectLink = createDisabledActionLink(CodeGPTBundle .get(" toolwindow.chat.editor.action.autoApply.reject" ))
80
+ private fun handleApply (request : AutoApplyRequest , editorVirtualFile : VirtualFile ) {
81
+ val acceptLink =
82
+ createDisabledActionLink(CodeGPTBundle .get(" toolwindow.chat.editor.action.autoApply.accept" ))
83
+ val rejectLink =
84
+ createDisabledActionLink(CodeGPTBundle .get(" toolwindow.chat.editor.action.autoApply.reject" ))
91
85
92
86
val newLinksPanel = JPanel (FlowLayout (FlowLayout .TRAILING , 8 , 0 )).apply {
93
87
isOpaque = false
@@ -108,7 +102,9 @@ class AutoApplyAction(
108
102
acceptLink.isEnabled = true
109
103
acceptLink.addActionListener {
110
104
WriteCommandAction .runWriteCommandAction(project) {
111
- editorVirtualFile.setBinaryContent(modifiedFileContent.toByteArray(editorVirtualFile.charset))
105
+ editorVirtualFile.setBinaryContent(
106
+ modifiedFileContent.toByteArray(editorVirtualFile.charset)
107
+ )
112
108
}
113
109
resetState(editorVirtualFile)
114
110
}
@@ -137,6 +133,25 @@ class AutoApplyAction(
137
133
)
138
134
}
139
135
136
+ override fun handleAction (event : AnActionEvent ) {
137
+ val fileDetails = CodeGPTKeys .TOOLWINDOW_EDITOR_FILE_DETAILS .get(toolwindowEditor)
138
+ ? : return
139
+
140
+ if (fileDetails.virtualFile == null || fileDetails.virtualFile.isDirectory) {
141
+ showAdditionalOptionsDialog(fileDetails, toolwindowEditor.document.text)
142
+ return
143
+ }
144
+
145
+ val editorVirtualFile = fileDetails.virtualFile
146
+
147
+ val request = AutoApplyRequest ().apply {
148
+ suggestedChanges = toolwindowEditor.document.text
149
+ fileContent = FileUtil .readContent(editorVirtualFile)
150
+ }
151
+
152
+ handleApply(request, editorVirtualFile)
153
+ }
154
+
140
155
override fun getActionUpdateThread (): ActionUpdateThread {
141
156
return ActionUpdateThread .EDT
142
157
}
@@ -182,7 +197,6 @@ class AutoApplyAction(
182
197
}
183
198
184
199
private fun resetState (virtualFile : VirtualFile ) {
185
- // Restore the action toolbar
186
200
headerPanel.restoreActionToolbar()
187
201
linksPanel = null
188
202
@@ -238,6 +252,95 @@ class AutoApplyAction(
238
252
}
239
253
}
240
254
}
255
+
256
+ private fun createNewFile (filePath : String , content : String ) {
257
+
258
+ ProgressManager .getInstance().run (object : Task .Backgroundable (
259
+ project,
260
+ CodeGPTBundle .get(" toolwindow.chat.editor.action.autoApply.creatingFile" ),
261
+ true
262
+ ) {
263
+ override fun run (indicator : ProgressIndicator ) {
264
+ try {
265
+ val file = File (filePath)
266
+ file.parentFile?.mkdirs()
267
+
268
+ WriteCommandAction .runWriteCommandAction(project) {
269
+ file.writeText(content)
270
+
271
+ val virtualFile =
272
+ LocalFileSystem .getInstance().refreshAndFindFileByIoFile(file)
273
+
274
+ if (virtualFile != null ) {
275
+ runInEdt {
276
+ FileEditorManager .getInstance(project).openFile(virtualFile, true )
277
+ }
278
+ }
279
+ }
280
+ } catch (ex: Exception ) {
281
+ runInEdt {
282
+ OverlayUtil .showNotification(
283
+ CodeGPTBundle .get(
284
+ " toolwindow.chat.editor.action.autoApply.fileCreationError" ,
285
+ ex.message ? : " "
286
+ ),
287
+ NotificationType .ERROR
288
+ )
289
+ }
290
+ }
291
+ }
292
+ })
293
+ }
294
+
295
+ private fun applyToExistingFile (content : String ) {
296
+ val editor =
297
+ (FileEditorManager .getInstance(project).selectedTextEditor as ? EditorEx ) ? : return
298
+ val request = AutoApplyRequest ().apply {
299
+ suggestedChanges = content
300
+ fileContent = FileUtil .readContent(editor.virtualFile)
301
+ }
302
+
303
+ handleApply(request, editor.virtualFile)
304
+ }
305
+
306
+ private fun showAdditionalOptionsDialog (
307
+ fileDetails : ToolWindowEditorFileDetails ,
308
+ content : String
309
+ ) {
310
+ val actions = mutableListOf<Pair <String , () - > Unit >> ()
311
+ val canCreateNewFile =
312
+ fileDetails.virtualFile == null || ! fileDetails.virtualFile.isDirectory
313
+
314
+ if (canCreateNewFile) {
315
+ actions.add(CodeGPTBundle .get(" toolwindow.chat.editor.action.autoApply.dialog.createNew" ) to {
316
+ createNewFile(fileDetails.path, content)
317
+ })
318
+ }
319
+ actions.add(CodeGPTBundle .get(" toolwindow.chat.editor.action.autoApply.dialog.applyToOpenFile" ) to {
320
+ applyToExistingFile(content)
321
+ })
322
+ actions.add(CodeGPTBundle .get(" toolwindow.chat.editor.action.autoApply.dialog.cancel" ) to {})
323
+
324
+ val optionTexts = actions.map { it.first }.toTypedArray()
325
+ val defaultOptionIndex = 0
326
+
327
+ val result = Messages .showDialog(
328
+ project,
329
+ CodeGPTBundle .get(
330
+ " toolwindow.chat.editor.action.autoApply.dialog.message" ,
331
+ fileDetails.path
332
+ ),
333
+ CodeGPTBundle .get(" toolwindow.chat.editor.action.autoApply.dialog.title" ),
334
+ optionTexts,
335
+ defaultOptionIndex,
336
+ Messages .getQuestionIcon()
337
+ )
338
+
339
+ if (result >= 0 && result < actions.size) {
340
+ actions[result].second.invoke()
341
+ }
342
+ }
343
+
241
344
}
242
345
243
346
internal class ApplyChangesBackgroundTask (
@@ -265,4 +368,4 @@ internal class ApplyChangesBackgroundTask(
265
368
onFailure(ex)
266
369
}
267
370
}
268
- }
371
+ }
0 commit comments