mirror of
https://github.com/carlrobertoh/ProxyAI.git
synced 2026-05-10 03:59:43 +00:00
feat: explain commits from vcs log tree (relates #688)
This commit is contained in:
parent
5ee8ef941d
commit
2ab6d1a54c
8 changed files with 154 additions and 9 deletions
|
|
@ -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<GitCommit> gitCommits) {
|
||||
userInputPanel.addCommitReferences(gitCommits);
|
||||
}
|
||||
|
||||
public List<ReferencedFile> getReferencedFiles() {
|
||||
var referencedFiles = new LinkedHashMap<String, ReferencedFile>();
|
||||
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<ChatToolWindowContentManager>().apply {
|
||||
runInEdt {
|
||||
displayChatTab()
|
||||
tryFindActiveChatTabPanel()
|
||||
.ifPresent {
|
||||
it.addCommitReferences(gitCommits)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getCommitsForRevisions(
|
||||
project: Project,
|
||||
revisionNumbers: Array<VcsRevisionNumber>?
|
||||
): List<GitCommit> {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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<GitCommit>) {
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<EncodingManager>().truncateText(diff, MAX_TOKENS, true)
|
||||
|
|
|
|||
|
|
@ -71,7 +71,25 @@ object GitUtil {
|
|||
}
|
||||
|
||||
@Throws(VcsException::class)
|
||||
fun getCommitDiff(
|
||||
fun getCommitsForHashes(
|
||||
project: Project,
|
||||
repository: GitRepository,
|
||||
commitHashes: List<String>
|
||||
): List<GitCommit> {
|
||||
val result = mutableListOf<GitCommit>()
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -171,6 +171,18 @@
|
|||
<separator/>
|
||||
</group>
|
||||
|
||||
<group id="CodeGPT.VcsLogContextMenu">
|
||||
<separator/>
|
||||
<action
|
||||
id="CodeGPT.ExplainGitCommitAction"
|
||||
class="ee.carlrobert.codegpt.actions.ExplainGitCommitAction"/>
|
||||
<add-to-group
|
||||
group-id="Vcs.Log.ContextMenu"
|
||||
relative-to-action="Vcs.Log.CompareRevisions"
|
||||
anchor="after"/>
|
||||
<separator/>
|
||||
</group>
|
||||
|
||||
<group id="CodeGPT.FloatingCodeToolbarMenuRootGroup">
|
||||
<action
|
||||
id="CodeGPT.FloatingMenuEditCodeAction"
|
||||
|
|
|
|||
|
|
@ -27,6 +27,8 @@ action.statusbar.disableCompletions.description=Disable Code Completions
|
|||
action.statusbar.disableCompletions.MainMenu.text=Disable Completions
|
||||
action.compareWithOriginal.title=Compare with Original
|
||||
action.applyDirectly.title=Auto Apply
|
||||
action.explainGitCommit.title=Explain Commit with CodeGPT
|
||||
action.explainGitCommit.description=Generate a detailed explanation of the commit changes using CodeGPT
|
||||
settings.displayName=CodeGPT: Settings
|
||||
settings.openaiQuotaExceeded=OpenAI quota exceeded.
|
||||
settingsConfigurable.displayName.label=Display name:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue