diff --git a/src/main/kotlin/ee/carlrobert/codegpt/toolwindow/chat/editor/ResponseEditorPanel.kt b/src/main/kotlin/ee/carlrobert/codegpt/toolwindow/chat/editor/ResponseEditorPanel.kt index c2ab63e0..e469484c 100644 --- a/src/main/kotlin/ee/carlrobert/codegpt/toolwindow/chat/editor/ResponseEditorPanel.kt +++ b/src/main/kotlin/ee/carlrobert/codegpt/toolwindow/chat/editor/ResponseEditorPanel.kt @@ -10,17 +10,22 @@ import com.intellij.openapi.editor.event.BulkAwareDocumentListener import com.intellij.openapi.editor.event.DocumentEvent import com.intellij.openapi.editor.ex.EditorEx import com.intellij.openapi.project.Project +import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.util.Disposer import com.intellij.openapi.util.Key import com.intellij.openapi.vfs.VirtualFile +import com.intellij.openapi.diagnostic.thisLogger +import com.intellij.openapi.vfs.readText import com.intellij.util.ui.JBUI import com.intellij.util.ui.components.BorderLayoutPanel +import ee.carlrobert.codegpt.codecompletions.CompletionProgressNotifier import ee.carlrobert.codegpt.completions.AutoApplyParameters import ee.carlrobert.codegpt.completions.CompletionRequestService import ee.carlrobert.codegpt.toolwindow.chat.editor.diff.DiffSyncManager import ee.carlrobert.codegpt.toolwindow.chat.editor.factory.ComponentFactory import ee.carlrobert.codegpt.toolwindow.chat.editor.factory.ComponentFactory.EXPANDED_KEY import ee.carlrobert.codegpt.toolwindow.chat.editor.factory.ComponentFactory.MIN_LINES_FOR_EXPAND +import ee.carlrobert.codegpt.toolwindow.chat.editor.header.DiffHeaderPanel import ee.carlrobert.codegpt.toolwindow.chat.editor.state.EditorState import ee.carlrobert.codegpt.toolwindow.chat.editor.state.EditorStateManager import ee.carlrobert.codegpt.toolwindow.chat.parser.ReplaceWaiting @@ -42,6 +47,7 @@ class ResponseEditorPanel( val RESPONSE_EDITOR_STATE_KEY = Key.create("proxyai.responseEditorState") } + private val logger = thisLogger() private val stateManager = EditorStateManager(project) private var searchReplaceHandler: SearchReplaceHandler @@ -101,6 +107,66 @@ class ResponseEditorPanel( responseEditorPanel.replaceEditor(oldEditor, newEditor) }) } + + internal fun createReplaceWaitingSegment(searchContent: String, replaceContent: String, virtualFile: VirtualFile): ReplaceWaiting { + return ReplaceWaiting( + search = searchContent, + replace = replaceContent, + language = virtualFile.extension ?: "text", + filePath = virtualFile.path + ) + } + + fun createDiffEditorForDirectApply(searchContent: String, replaceContent: String, virtualFile: VirtualFile) { + try { + val segment = createReplaceWaitingSegment(searchContent, "", virtualFile) + + val oldEditor = stateManager.getCurrentState()?.editor + if (oldEditor == null) { + logger.warn("No current editor state found for direct apply") + return + } + + val currentText = try { + virtualFile.readText() + } catch (e: Exception) { + logger.error("Failed to read file content for direct apply", e) + return + } + + val containsText = currentText.contains(segment.search.trim()) + + val newState = if (containsText) { + stateManager.createFromSegment(segment) + } else { + stateManager.transitionToFailedDiffState( + segment.search, + segment.replace, + virtualFile + ) ?: run { + logger.warn("Failed to transition to failed diff state") + return + } + } + + replaceEditor(oldEditor, newState.editor) + + val finalSegment = createReplaceWaitingSegment(searchContent, replaceContent, virtualFile) + newState.updateContent(finalSegment) + + val currentEditor = newState.editor + val headerPanel = currentEditor.permanentHeaderComponent as? DiffHeaderPanel + + ApplicationManager.getApplication().invokeLater { + if (!project.isDisposed) { + headerPanel?.handleDone() + CompletionProgressNotifier.update(project, false) + } + } + } catch (e: Exception) { + logger.error("Unexpected error during direct apply", e) + } + } override fun dispose() { val state = stateManager.getCurrentState() diff --git a/src/main/kotlin/ee/carlrobert/codegpt/toolwindow/chat/editor/header/DefaultHeaderPanel.kt b/src/main/kotlin/ee/carlrobert/codegpt/toolwindow/chat/editor/header/DefaultHeaderPanel.kt index e463095a..20f735b2 100644 --- a/src/main/kotlin/ee/carlrobert/codegpt/toolwindow/chat/editor/header/DefaultHeaderPanel.kt +++ b/src/main/kotlin/ee/carlrobert/codegpt/toolwindow/chat/editor/header/DefaultHeaderPanel.kt @@ -3,17 +3,12 @@ package ee.carlrobert.codegpt.toolwindow.chat.editor.header import com.intellij.icons.AllIcons import com.intellij.openapi.actionSystem.* import com.intellij.openapi.application.runInEdt -import com.intellij.openapi.application.runUndoTransparentWriteAction import com.intellij.openapi.editor.ex.EditorEx import com.intellij.openapi.project.Project import com.intellij.openapi.ui.JBMenuItem import com.intellij.openapi.ui.JBPopupMenu -import com.intellij.openapi.ui.MessageType -import com.intellij.openapi.ui.popup.JBPopupFactory import com.intellij.openapi.vfs.readText -import com.intellij.openapi.vfs.writeText import com.intellij.ui.AnimatedIcon -import com.intellij.ui.components.AnActionLink import com.intellij.ui.components.JBLabel import com.intellij.util.ui.JBUI import ee.carlrobert.codegpt.CodeGPTBundle @@ -66,7 +61,7 @@ class DefaultHeaderPanel(config: HeaderConfig) : HeaderPanel(config) { actionGroup.add(CopyAction(editor)) } else { actionGroup.add(AutoApplyAction(project, editor, config.filePath, virtualFile) { - handleApply(project, editor, it) + handleApply(project, editor) }) actionGroup.add(CopyAction(editor)) actionGroup.addSeparator() @@ -75,7 +70,7 @@ class DefaultHeaderPanel(config: HeaderConfig) : HeaderPanel(config) { return createToolbar(actionGroup) } - private fun handleApply(project: Project, editor: EditorEx, link: AnActionLink) { + private fun handleApply(project: Project, editor: EditorEx) { val file = virtualFile ?: EditorUtil.getSelectedEditor(project)?.virtualFile ?: throw IllegalStateException("Virtual file is null") @@ -83,22 +78,9 @@ class DefaultHeaderPanel(config: HeaderConfig) : HeaderPanel(config) { val directApplyThreshold = 0.85 val coefficient = StringUtil.getDiceCoefficient(editor.document.text, file.readText()) if (coefficient > directApplyThreshold) { - runUndoTransparentWriteAction { - file.writeText( - com.intellij.openapi.util.text.StringUtil.convertLineSeparators( - editor.document.text - ) - ) - } - val balloon = JBPopupFactory.getInstance() - .createHtmlTextBalloonBuilder( - CodeGPTBundle.get("toolwindow.chat.editor.action.autoApply.successMessage"), - MessageType.INFO, - null - ) - .setFadeoutTime(3000) - .createBalloon() - balloon.showInCenterOf(link) + val responseEditorPanel = editor.component.parent as? ResponseEditorPanel + ?: throw IllegalStateException("Could not find corresponding ResponseEditorPanel") + responseEditorPanel.createDiffEditorForDirectApply(file.readText(), editor.document.text, file) return }