diff --git a/src/main/kotlin/ee/carlrobert/codegpt/toolwindow/chat/editor/diff/DiffAcceptedPanel.kt b/src/main/kotlin/ee/carlrobert/codegpt/toolwindow/chat/editor/diff/DiffAcceptedPanel.kt index 1ff94ace..c0db8af1 100644 --- a/src/main/kotlin/ee/carlrobert/codegpt/toolwindow/chat/editor/diff/DiffAcceptedPanel.kt +++ b/src/main/kotlin/ee/carlrobert/codegpt/toolwindow/chat/editor/diff/DiffAcceptedPanel.kt @@ -1,57 +1,117 @@ package ee.carlrobert.codegpt.toolwindow.chat.editor.diff +import com.intellij.diff.DiffContentFactory +import com.intellij.diff.DiffManager +import com.intellij.diff.requests.SimpleDiffRequest import com.intellij.diff.tools.fragmented.UnifiedDiffChange +import com.intellij.diff.util.DiffUserDataKeys +import com.intellij.icons.AllIcons +import com.intellij.openapi.application.runWriteAction import com.intellij.openapi.fileEditor.FileEditorManager import com.intellij.openapi.project.Project import com.intellij.openapi.vfs.LocalFileSystem +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.openapi.vfs.writeText import com.intellij.ui.EditorNotificationPanel import com.intellij.ui.InlineBanner import com.intellij.ui.components.ActionLink import com.intellij.util.ui.JBUI import com.intellij.util.ui.components.BorderLayoutPanel -import java.io.File +import ee.carlrobert.codegpt.CodeGPTBundle +import ee.carlrobert.codegpt.toolwindow.chat.editor.state.DiffEditorState +import javax.swing.Box import javax.swing.BoxLayout import javax.swing.JPanel class DiffAcceptedPanel( project: Project, + virtualFile: VirtualFile, + before: String, + after: String, changes: List, - filePath: String, - onViewDetails: () -> Unit, ) : InlineBanner() { init { isOpaque = false border = JBUI.Borders.empty(8) - val name = File(filePath).name - val fileLink = createFileLink(project, filePath, name) + val fileLink = createFileLink(project, virtualFile.path, virtualFile.name) val statsPanel = DiffStatsComponent.createStatsPanel(changes) val contentPanel = BorderLayoutPanel().andTransparent() .addToLeft(createLeftPanel(fileLink, statsPanel)) - .addToRight(ActionLink("View Details") { onViewDetails() }) + .addToRight(createRightPanel(project, before, after, virtualFile)) add(contentPanel) status = EditorNotificationPanel.Status.Success showCloseButton(false) } - private fun createFileLink(project: Project, filePath: String, name: String): ActionLink { - return ActionLink(name) { - val vFile = LocalFileSystem.getInstance().findFileByPath(filePath) - if (vFile != null) { + private fun createFileLink(project: Project, filePath: String, name: String): ActionLink = + ActionLink(name) { + LocalFileSystem.getInstance().findFileByPath(filePath)?.let { vFile -> FileEditorManager.getInstance(project).openFile(vFile, true) } } - } - private fun createLeftPanel(fileLink: ActionLink, statsPanel: JPanel): JPanel { - return JPanel().apply { + private fun createLeftPanel(fileLink: ActionLink, statsPanel: JPanel): JPanel = + JPanel().apply { layout = BoxLayout(this, BoxLayout.X_AXIS) isOpaque = false add(fileLink) add(statsPanel) } + + private fun createRightPanel( + project: Project, + before: String, + after: String, + virtualFile: VirtualFile + ): JPanel { + val revertChangesLink = ActionLink(CodeGPTBundle.get("diff.acceptedPanel.revertChanges")) { + val contentFactory = DiffContentFactory.getInstance() + val left = contentFactory.create(project, virtualFile) + val right = contentFactory.create(project, before, virtualFile.fileType) + + val diffRequest = SimpleDiffRequest( + CodeGPTBundle.get("diff.acceptedPanel.revertChanges"), + left, right, + CodeGPTBundle.get("diff.acceptedPanel.after"), + CodeGPTBundle.get("diff.acceptedPanel.before") + ).apply { + val revertAllButton = + DiffEditorState.createContextActionButton("Revert All", AllIcons.Actions.Redo) { + runWriteAction { + virtualFile.writeText(before) + } + } + + putUserData(DiffUserDataKeys.CONTEXT_ACTIONS, listOf(revertAllButton)) + } + + DiffManager.getInstance().showDiff(project, diffRequest) + } + val viewDetailsLink = ActionLink(CodeGPTBundle.get("diff.acceptedPanel.viewDetails")) { + val contentFactory = DiffContentFactory.getInstance() + val left = contentFactory.create(project, before, virtualFile.fileType) + val right = contentFactory.create(project, after, virtualFile.fileType) + + val diffRequest = SimpleDiffRequest( + CodeGPTBundle.get("diff.acceptedPanel.viewDetails"), + left, right, + CodeGPTBundle.get("diff.acceptedPanel.before"), + CodeGPTBundle.get("diff.acceptedPanel.after") + ) + + DiffManager.getInstance().showDiff(project, diffRequest) + } + + return JPanel().apply { + layout = BoxLayout(this, BoxLayout.X_AXIS) + isOpaque = false + add(revertChangesLink) + add(Box.createHorizontalStrut(6)) + add(viewDetailsLink) + } } } \ No newline at end of file diff --git a/src/main/kotlin/ee/carlrobert/codegpt/toolwindow/chat/editor/header/DiffHeaderPanel.kt b/src/main/kotlin/ee/carlrobert/codegpt/toolwindow/chat/editor/header/DiffHeaderPanel.kt index c1584913..87a94aba 100644 --- a/src/main/kotlin/ee/carlrobert/codegpt/toolwindow/chat/editor/header/DiffHeaderPanel.kt +++ b/src/main/kotlin/ee/carlrobert/codegpt/toolwindow/chat/editor/header/DiffHeaderPanel.kt @@ -2,6 +2,7 @@ package ee.carlrobert.codegpt.toolwindow.chat.editor.header import com.intellij.diff.tools.fragmented.UnifiedDiffChange import com.intellij.openapi.application.runInEdt +import com.intellij.openapi.editor.Document import com.intellij.ui.AnimatedIcon import com.intellij.ui.components.ActionLink import com.intellij.ui.components.JBLabel @@ -60,20 +61,20 @@ class DiffHeaderPanel( } } - fun handleChangesApplied( - patches: List - ) { + fun handleChangesApplied(before: String, after: String, patches: List) { actionLinksPanel.isVisible = false loadingLabel.isVisible = false - val diffAcceptedPanel = DiffAcceptedPanel(config.project, patches, config.filePath!!) { } - runInEdt { - val container = config.editorEx.component.parent - if (container is ResponseEditorPanel) { - container.removeAll() - container.add(diffAcceptedPanel, BorderLayout.CENTER) - container.revalidate() - container.repaint() + virtualFile?.let { + val diffAcceptedPanel = DiffAcceptedPanel(config.project, it, before, after, patches) + runInEdt { + val container = config.editorEx.component.parent + if (container is ResponseEditorPanel) { + container.removeAll() + container.add(diffAcceptedPanel, BorderLayout.CENTER) + container.revalidate() + container.repaint() + } } } } diff --git a/src/main/kotlin/ee/carlrobert/codegpt/toolwindow/chat/editor/state/DiffEditorState.kt b/src/main/kotlin/ee/carlrobert/codegpt/toolwindow/chat/editor/state/DiffEditorState.kt index 72140199..83515996 100644 --- a/src/main/kotlin/ee/carlrobert/codegpt/toolwindow/chat/editor/state/DiffEditorState.kt +++ b/src/main/kotlin/ee/carlrobert/codegpt/toolwindow/chat/editor/state/DiffEditorState.kt @@ -44,6 +44,39 @@ abstract class DiffEditorState( companion object { private val DIFF_REQUEST_KEY = Key.create("codegpt.autoApply.diffRequest") + + fun createContextActionButton( + text: String, + icon: Icon, + textColor: JBColor? = null, + onAction: (() -> Unit) + ): AnAction { + return object : AnAction(text, null, icon), CustomComponentAction { + override fun actionPerformed(e: AnActionEvent) { + onAction() + } + + override fun createCustomComponent( + presentation: Presentation, + place: String + ): JComponent { + val button = JButton(presentation.text).apply { + font = JBUI.Fonts.smallFont() + isFocusable = false + isOpaque = true + if (textColor != null) { + foreground = textColor + } + preferredSize = JBUI.size(preferredSize.width, 26) + maximumSize = JBUI.size(Int.MAX_VALUE, 26) + addActionListener { + onAction() + } + } + return button + } + } + } } private lateinit var diffRequestId: UUID @@ -133,35 +166,4 @@ abstract class DiffEditorState( fileEditorManager.closeFile(diffFile) } } - - private fun createContextActionButton( - text: String, - icon: Icon, - textColor: JBColor, - onAction: (() -> Unit) - ): AnAction { - return object : AnAction(text, null, icon), CustomComponentAction { - override fun actionPerformed(e: AnActionEvent) { - onAction() - } - - override fun createCustomComponent( - presentation: Presentation, - place: String - ): JComponent { - val button = JButton(presentation.text).apply { - font = JBUI.Fonts.smallFont() - isFocusable = false - isOpaque = true - foreground = textColor - preferredSize = JBUI.size(preferredSize.width, 26) - maximumSize = JBUI.size(Int.MAX_VALUE, 26) - addActionListener { - onAction() - } - } - return button - } - } - } } diff --git a/src/main/kotlin/ee/carlrobert/codegpt/toolwindow/chat/editor/state/StandardDiffEditorState.kt b/src/main/kotlin/ee/carlrobert/codegpt/toolwindow/chat/editor/state/StandardDiffEditorState.kt index 155f31cb..4bdcee8e 100644 --- a/src/main/kotlin/ee/carlrobert/codegpt/toolwindow/chat/editor/state/StandardDiffEditorState.kt +++ b/src/main/kotlin/ee/carlrobert/codegpt/toolwindow/chat/editor/state/StandardDiffEditorState.kt @@ -1,6 +1,7 @@ package ee.carlrobert.codegpt.toolwindow.chat.editor.state import com.intellij.diff.tools.fragmented.UnifiedDiffViewer +import com.intellij.diff.util.Side import com.intellij.ide.actions.OpenFileAction import com.intellij.openapi.application.runInEdt import com.intellij.openapi.editor.EditorKind @@ -24,9 +25,12 @@ class StandardDiffEditorState( ) : DiffEditorState(editor, segment, project, diffViewer, virtualFile) { override fun applyAllChanges() { + val before = diffViewer?.getDocument(Side.LEFT)?.text ?: return + val after = diffViewer.getDocument(Side.RIGHT).text val changes = diffEditorManager.applyAllChanges() if (changes.isNotEmpty()) { - (editor.permanentHeaderComponent as? DiffHeaderPanel)?.handleChangesApplied(changes) + (editor.permanentHeaderComponent as? DiffHeaderPanel) + ?.handleChangesApplied(before, after, changes) virtualFile?.let { OpenFileAction.openFile(it, project) } } } diff --git a/src/main/resources/messages/codegpt.properties b/src/main/resources/messages/codegpt.properties index 2ace4911..00810d4d 100644 --- a/src/main/resources/messages/codegpt.properties +++ b/src/main/resources/messages/codegpt.properties @@ -199,6 +199,10 @@ toolwindow.chat.editor.diff.retrying=Retrying... toolwindow.chat.editor.action.autoApply.error=Something went wrong while applying changes. {0} toolwindow.chat.editor.action.autoApply.taskTitle=Apply changes toolwindow.chat.editor.action.autoApply.loadingMessage=ProxyAI: Applying changes +diff.acceptedPanel.revertChanges=Revert Changes +diff.acceptedPanel.viewDetails=View Details +diff.acceptedPanel.before=Before +diff.acceptedPanel.after=After toolwindow.chat.editor.action.diff.description=Diff editor code against the generated one toolwindow.chat.editor.action.edit.title=Edit Source toolwindow.chat.editor.action.disableEditing.title=Disable Editing