diff --git a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/ChatToolWindowTabPanel.java b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/ChatToolWindowTabPanel.java index a28729f8..13f9a19b 100644 --- a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/ChatToolWindowTabPanel.java +++ b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/ChatToolWindowTabPanel.java @@ -34,6 +34,7 @@ import ee.carlrobert.codegpt.ui.textarea.AppliedActionInlay; import ee.carlrobert.codegpt.ui.textarea.UserInputPanel; import ee.carlrobert.codegpt.util.EditorUtil; import ee.carlrobert.codegpt.util.file.FileUtil; +import git4idea.GitCommit; import java.awt.BorderLayout; import java.io.File; import java.io.IOException; @@ -117,6 +118,14 @@ public class ChatToolWindowTabPanel implements Disposable { totalTokensPanel.updateConversationTokens(conversation); } + public void addSelection(String fileName, SelectionModel selectionModel) { + userInputPanel.addSelection(fileName, selectionModel); + } + + public void addCommitReferences(List gitCommits) { + userInputPanel.addCommitReferences(gitCommits); + } + public List getReferencedFiles() { var referencedFiles = new LinkedHashMap(); @@ -417,8 +426,4 @@ public class ChatToolWindowTabPanel implements Disposable { rootPanel.add(createUserPromptPanel(), BorderLayout.SOUTH); return rootPanel; } - - public void addSelection(String fileName, SelectionModel selectionModel) { - userInputPanel.addSelection(fileName, selectionModel); - } } diff --git a/src/main/kotlin/ee/carlrobert/codegpt/actions/ExplainGitCommitAction.kt b/src/main/kotlin/ee/carlrobert/codegpt/actions/ExplainGitCommitAction.kt new file mode 100644 index 00000000..abef07b1 --- /dev/null +++ b/src/main/kotlin/ee/carlrobert/codegpt/actions/ExplainGitCommitAction.kt @@ -0,0 +1,77 @@ +package ee.carlrobert.codegpt.actions + +import com.intellij.openapi.actionSystem.ActionUpdateThread +import com.intellij.openapi.actionSystem.AnAction +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.application.runInEdt +import com.intellij.openapi.components.service +import com.intellij.openapi.project.Project +import com.intellij.openapi.vcs.VcsDataKeys +import com.intellij.openapi.vcs.history.VcsRevisionNumber +import ee.carlrobert.codegpt.CodeGPTBundle +import ee.carlrobert.codegpt.Icons +import ee.carlrobert.codegpt.toolwindow.chat.ChatToolWindowContentManager +import ee.carlrobert.codegpt.util.GitUtil +import git4idea.GitCommit +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.launch + +class ExplainGitCommitAction : AnAction( + CodeGPTBundle.get("action.explainGitCommit.title"), + CodeGPTBundle.get("action.explainGitCommit.description"), + Icons.DefaultSmall +) { + + private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default) + + override fun getActionUpdateThread(): ActionUpdateThread { + return ActionUpdateThread.BGT + } + + override fun actionPerformed(e: AnActionEvent) { + val project = e.project ?: return + + scope.launch { + val gitCommits = + getCommitsForRevisions(project, e.getData(VcsDataKeys.VCS_REVISION_NUMBERS)) + + project.service().apply { + runInEdt { + displayChatTab() + tryFindActiveChatTabPanel() + .ifPresent { + it.addCommitReferences(gitCommits) + } + } + } + } + } + + private fun getCommitsForRevisions( + project: Project, + revisionNumbers: Array? + ): List { + if (revisionNumbers == null) { + throw IllegalArgumentException("No commit revisions found") + } + + val gitCommits = GitUtil.getProjectRepository(project)?.let { repository -> + GitUtil.getCommitsForHashes( + project, + repository, + revisionNumbers.map { it.asString() }) + } ?: throw IllegalStateException("Unable to find git repository") + + if (gitCommits.isEmpty()) { + throw IllegalStateException( + "Unable to find commits for given revisions: ${ + revisionNumbers.joinToString(",") { it.asString() } + }" + ) + } + + return gitCommits + } +} \ No newline at end of file diff --git a/src/main/kotlin/ee/carlrobert/codegpt/ui/textarea/PromptTextField.kt b/src/main/kotlin/ee/carlrobert/codegpt/ui/textarea/PromptTextField.kt index 4c19b2a1..e5fbaf86 100644 --- a/src/main/kotlin/ee/carlrobert/codegpt/ui/textarea/PromptTextField.kt +++ b/src/main/kotlin/ee/carlrobert/codegpt/ui/textarea/PromptTextField.kt @@ -71,9 +71,9 @@ class PromptTextField( fun addInlayElement(actionPrefix: String, text: String?, actionItem: SuggestionActionItem?) { editor?.let { - val startOffset = it.document.text.lastIndexOf(AT_CHAR) + var startOffset = it.document.text.lastIndexOf(AT_CHAR) if (startOffset == -1) { - throw IllegalStateException("No '@' symbol found in the text") + startOffset = it.document.textLength } addInlayElement(startOffset, actionPrefix, text, actionItem) diff --git a/src/main/kotlin/ee/carlrobert/codegpt/ui/textarea/UserInputPanel.kt b/src/main/kotlin/ee/carlrobert/codegpt/ui/textarea/UserInputPanel.kt index 1c9d1a3c..8b8d80db 100644 --- a/src/main/kotlin/ee/carlrobert/codegpt/ui/textarea/UserInputPanel.kt +++ b/src/main/kotlin/ee/carlrobert/codegpt/ui/textarea/UserInputPanel.kt @@ -5,6 +5,7 @@ import com.intellij.openapi.actionSystem.ActionPlaces import com.intellij.openapi.actionSystem.AnAction import com.intellij.openapi.actionSystem.AnActionEvent import com.intellij.openapi.application.invokeLater +import com.intellij.openapi.application.runInEdt import com.intellij.openapi.components.service import com.intellij.openapi.editor.SelectionModel import com.intellij.openapi.observable.properties.AtomicBooleanProperty @@ -28,7 +29,9 @@ import ee.carlrobert.codegpt.toolwindow.chat.ChatToolWindowContentManager import ee.carlrobert.codegpt.toolwindow.chat.ui.textarea.ModelComboBoxAction import ee.carlrobert.codegpt.toolwindow.chat.ui.textarea.TotalTokensPanel import ee.carlrobert.codegpt.ui.IconActionButton +import ee.carlrobert.codegpt.ui.textarea.suggestion.item.GitCommitActionItem import ee.carlrobert.llm.client.openai.completion.OpenAIChatCompletionModel +import git4idea.GitCommit import java.awt.* import java.awt.geom.Area import java.awt.geom.Rectangle2D @@ -96,7 +99,14 @@ class UserInputPanel( g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON) val area = Area(Rectangle2D.Float(0f, 0f, width.toFloat(), height.toFloat())) - val roundedRect = RoundRectangle2D.Float(0f, 0f, width.toFloat(), height.toFloat(), CORNER_RADIUS.toFloat(), CORNER_RADIUS.toFloat()) + val roundedRect = RoundRectangle2D.Float( + 0f, + 0f, + width.toFloat(), + height.toFloat(), + CORNER_RADIUS.toFloat(), + CORNER_RADIUS.toFloat() + ) area.intersect(Area(roundedRect)) g2.clip = area @@ -210,4 +220,25 @@ class UserInputPanel( promptTextField.requestFocusInWindow() selectionModel.removeSelection() } + + fun addCommitReferences(gitCommits: List) { + runInEdt { + if (promptTextField.text.isEmpty()) { + promptTextField.text = if (gitCommits.size == 1) { + "Explain the commit: " + } else { + "Explain the commits: " + } + } + + gitCommits.forEach { + promptTextField.addInlayElement( + "commit", + it.id.toShortString(), + GitCommitActionItem(project, it) + ) + } + promptTextField.requestFocusInWindow() + } + } } diff --git a/src/main/kotlin/ee/carlrobert/codegpt/ui/textarea/suggestion/item/SuggestionActionItems.kt b/src/main/kotlin/ee/carlrobert/codegpt/ui/textarea/suggestion/item/SuggestionActionItems.kt index 5bc621b0..9f143a73 100644 --- a/src/main/kotlin/ee/carlrobert/codegpt/ui/textarea/suggestion/item/SuggestionActionItems.kt +++ b/src/main/kotlin/ee/carlrobert/codegpt/ui/textarea/suggestion/item/SuggestionActionItems.kt @@ -114,7 +114,7 @@ class GitCommitActionItem( ?: return@runProcessWithProgressSynchronously "" val commitId = gitCommit.id.asString() - val diff = GitUtil.getCommitDiff(project, repository, commitId) + val diff = GitUtil.getCommitDiffs(project, repository, commitId) .joinToString("\n") service().truncateText(diff, MAX_TOKENS, true) diff --git a/src/main/kotlin/ee/carlrobert/codegpt/util/GitUtil.kt b/src/main/kotlin/ee/carlrobert/codegpt/util/GitUtil.kt index 834375c4..a959b812 100644 --- a/src/main/kotlin/ee/carlrobert/codegpt/util/GitUtil.kt +++ b/src/main/kotlin/ee/carlrobert/codegpt/util/GitUtil.kt @@ -71,7 +71,25 @@ object GitUtil { } @Throws(VcsException::class) - fun getCommitDiff( + fun getCommitsForHashes( + project: Project, + repository: GitRepository, + commitHashes: List + ): List { + val result = mutableListOf() + + GitHistoryUtils + .loadDetails(project, repository.root, { commit -> + if (commitHashes.contains(commit.id.asString())) { + result.add(commit) + } + }) + + return result + } + + @Throws(VcsException::class) + fun getCommitDiffs( project: Project, gitRepository: GitRepository, commitHash: String diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 360fce94..a3340871 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -171,6 +171,18 @@ + + + + + + +