feat: add open code assistant diff in editor action

This commit is contained in:
Carl-Robert Linnupuu 2025-01-08 00:52:39 +00:00
parent c3031bbda8
commit f2bb264244
5 changed files with 103 additions and 13 deletions

View file

@ -7,6 +7,7 @@ import com.intellij.openapi.actionSystem.CommonDataKeys
import com.intellij.openapi.actionSystem.DataContext
import ee.carlrobert.codegpt.codecompletions.AcceptNextLineInlayAction
import ee.carlrobert.codegpt.codecompletions.AcceptNextWordInlayAction
import ee.carlrobert.codegpt.predictions.OpenPredictionAction
import ee.carlrobert.codegpt.predictions.TriggerCustomPredictionAction
class InlayActionPromoter : ActionPromoter {
@ -14,6 +15,7 @@ class InlayActionPromoter : ActionPromoter {
val editor = CommonDataKeys.EDITOR.getData(context) ?: return emptyList()
actions.filterIsInstance<TriggerCustomPredictionAction>().takeIf { it.isNotEmpty() }?.let { return it }
actions.filterIsInstance<OpenPredictionAction>().takeIf { it.isNotEmpty() }?.let { return it }
if (InlineCompletionContext.getOrNull(editor) == null) {
return emptyList()

View file

@ -13,6 +13,7 @@ import com.intellij.diff.util.DiffUtil
import com.intellij.ide.plugins.newui.TagComponent
import com.intellij.openapi.Disposable
import com.intellij.openapi.actionSystem.ActionManager
import com.intellij.openapi.components.service
import com.intellij.openapi.editor.Document
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.LogicalPosition
@ -30,11 +31,14 @@ import com.intellij.openapi.util.TextRange
import com.intellij.openapi.util.UserDataHolder
import com.intellij.openapi.util.UserDataHolderBase
import com.intellij.testFramework.LightVirtualFile
import com.intellij.ui.components.JBLabel
import com.intellij.ui.components.JBScrollPane
import com.intellij.util.concurrency.annotations.RequiresEdt
import com.intellij.util.ui.JBUI
import com.intellij.util.ui.components.BorderLayoutPanel
import ee.carlrobert.codegpt.CodeGPTBundle
import ee.carlrobert.codegpt.CodeGPTKeys
import java.awt.BorderLayout
import java.awt.Dimension
import java.awt.FlowLayout
import java.awt.Point
@ -157,17 +161,24 @@ class CodeSuggestionDiffViewer(
}
private fun getTagPanel(): JComponent {
val tagPanel = JPanel(FlowLayout(FlowLayout.LEADING, 0, 0))
if (!isManuallyOpened) {
tagPanel.add(
TagComponent(
"Trigger manually: ${getShortcutText(TriggerCustomPredictionAction.ID)}"
).apply {
font = JBUI.Fonts.smallFont()
}
)
tagPanel.add(Box.createHorizontalStrut(6))
val tagPanel = JPanel(FlowLayout(FlowLayout.LEADING, 0, 0)).apply {
isOpaque = false
}
tagPanel.add(
TagComponent(
"Open: ${getShortcutText(OpenPredictionAction.ID)}"
).apply {
setListener({ _, _ ->
service<PredictionService>().openDirectPrediction(
mainEditor,
content2.document.text
)
popup.dispose()
}, component)
font = JBUI.Fonts.smallFont()
}
)
tagPanel.add(Box.createHorizontalStrut(6))
tagPanel.add(TagComponent("Accept: ${getShortcutText(AcceptNextPredictionRevisionAction.ID)}").apply {
setListener({ _, _ ->
applyChanges()
@ -183,6 +194,25 @@ class CodeSuggestionDiffViewer(
.andTransparent()
.withBorder(JBUI.Borders.empty(4))
.addToRight(getTagPanel())
val footerText = if (isManuallyOpened) {
CodeGPTBundle.get("shared.escToCancel")
} else {
"Trigger manually: ${getShortcutText(OpenPredictionAction.ID)} · ${CodeGPTBundle.get("shared.escToCancel")}"
}
myEditor.component.add(
BorderLayoutPanel()
.addToRight(JBLabel(footerText)
.apply {
font = JBUI.Fonts.miniFont()
})
.apply {
background = editor.backgroundColor
border = JBUI.Borders.empty(4)
},
BorderLayout.SOUTH
)
}
private fun getVisibleAreaListener(): VisibleAreaListener {

View file

@ -0,0 +1,30 @@
package ee.carlrobert.codegpt.predictions
import com.intellij.codeInsight.hint.HintManagerImpl
import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.application.runInEdt
import com.intellij.openapi.components.service
import com.intellij.openapi.editor.Caret
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.editor.actionSystem.EditorAction
import com.intellij.openapi.editor.actionSystem.EditorWriteActionHandler
import ee.carlrobert.codegpt.CodeGPTKeys.EDITOR_PREDICTION_DIFF_VIEWER
class OpenPredictionAction : EditorAction(Handler()), HintManagerImpl.ActionToIgnore {
companion object {
const val ID = "codegpt.openPrediction"
}
private class Handler : EditorWriteActionHandler() {
override fun doExecute(editor: Editor, caret: Caret?, dataContext: DataContext?) {
val diffViewer = EDITOR_PREDICTION_DIFF_VIEWER.get(editor) ?: return
val nextRevision = diffViewer.content2.document.text
runInEdt {
diffViewer.dispose()
}
service<PredictionService>().openDirectPrediction(editor, nextRevision)
}
}
}

View file

@ -1,6 +1,7 @@
package ee.carlrobert.codegpt.predictions
import com.intellij.codeInsight.lookup.LookupEvent
import com.intellij.diff.DiffManager
import com.intellij.notification.NotificationListener
import com.intellij.notification.NotificationType
import com.intellij.openapi.application.runInEdt
@ -8,6 +9,8 @@ import com.intellij.openapi.application.runReadAction
import com.intellij.openapi.components.Service
import com.intellij.openapi.components.service
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import com.intellij.testFramework.LightVirtualFile
import ee.carlrobert.codegpt.CodeGPTKeys
import ee.carlrobert.codegpt.CodeGPTKeys.IS_FETCHING_COMPLETION
import ee.carlrobert.codegpt.CodeGPTKeys.PENDING_PREDICTION_CALL
@ -18,6 +21,7 @@ import ee.carlrobert.codegpt.settings.prompts.PromptsSettings
import ee.carlrobert.codegpt.settings.service.codegpt.CodeGPTServiceSettings
import ee.carlrobert.codegpt.ui.OverlayUtil
import ee.carlrobert.codegpt.ui.OverlayUtil.getDefaultNotification
import ee.carlrobert.codegpt.util.EditorDiffUtil.createDiffRequest
import ee.carlrobert.codegpt.util.EditorUtil
import ee.carlrobert.codegpt.util.GitUtil
import ee.carlrobert.llm.client.codegpt.request.prediction.AutocompletionPredictionRequest
@ -35,7 +39,7 @@ class PredictionService {
fun displayDirectPrediction(editor: Editor) {
val request = CompletionClientProvider.getCodeGPTClient()
.buildDirectPredictionRequest(createDirectPredictionRequest(editor))
displayInlineDiff(editor, request)
displayInlineDiff(editor, request, true)
}
fun displayAutocompletePrediction(editor: Editor, textToInsert: String, beforeApply: String) {
@ -78,11 +82,19 @@ class PredictionService {
}
}
private fun displayInlineDiff(editor: Editor, request: Request) {
private fun displayInlineDiff(
editor: Editor,
request: Request,
isManuallyOpened: Boolean = false
) {
val prediction = getPrediction(editor, request)
if (prediction != null && !prediction.nextRevision.isNullOrEmpty()) {
runInEdt {
CodeSuggestionDiffViewer.displayInlineDiff(editor, prediction.nextRevision)
CodeSuggestionDiffViewer.displayInlineDiff(
editor,
prediction.nextRevision,
isManuallyOpened
)
}
}
}
@ -169,4 +181,13 @@ class PredictionService {
conversationMessages = messages.toList()
}
}
fun openDirectPrediction(editor: Editor, nextRevision: String) {
val project: Project = editor.project ?: return
val tempDiffFile = LightVirtualFile(editor.virtualFile.name, nextRevision)
val diffRequest = createDiffRequest(project, tempDiffFile, editor)
runInEdt {
service<DiffManager>().showDiff(project, diffRequest)
}
}
}

View file

@ -108,6 +108,13 @@
<keyboard-shortcut first-keystroke="ctrl ENTER" keymap="$default"/>
</action>
<action
id="codegpt.openPrediction"
text="Open Prediction"
class="ee.carlrobert.codegpt.predictions.OpenPredictionAction">
<keyboard-shortcut first-keystroke="ctrl O" keymap="$default"/>
</action>
<action
id="codegpt.acceptNextInlayWord"
text="Apply next word"