diff --git a/CHANGELOG.md b/CHANGELOG.md index 416be12..b4769ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Added - Option to choose prompt per project. +- Amending commits now adds the changes from previous commit to the prompt. ### Fixed diff --git a/src/main/kotlin/com/github/blarc/ai/commits/intellij/plugin/AICommitAction.kt b/src/main/kotlin/com/github/blarc/ai/commits/intellij/plugin/AICommitAction.kt index cd67da9..6951fa3 100644 --- a/src/main/kotlin/com/github/blarc/ai/commits/intellij/plugin/AICommitAction.kt +++ b/src/main/kotlin/com/github/blarc/ai/commits/intellij/plugin/AICommitAction.kt @@ -1,8 +1,5 @@ package com.github.blarc.ai.commits.intellij.plugin -import com.github.blarc.ai.commits.intellij.plugin.AICommitsUtils.commonBranch -import com.github.blarc.ai.commits.intellij.plugin.AICommitsUtils.computeDiff -import com.github.blarc.ai.commits.intellij.plugin.AICommitsUtils.constructPrompt import com.github.blarc.ai.commits.intellij.plugin.notifications.Notification import com.github.blarc.ai.commits.intellij.plugin.notifications.sendNotification import com.github.blarc.ai.commits.intellij.plugin.settings.AppSettings2 @@ -17,10 +14,9 @@ class AICommitAction : AnAction(), DumbAware { override fun actionPerformed(e: AnActionEvent) { val llmClient = AppSettings2.instance.getActiveLLMClientConfiguration() if (llmClient == null) { - Notification.clientNotSet() + sendNotification(Notification.clientNotSet()) return } - val project = e.project ?: return val commitWorkflowHandler = e.getData(VcsDataKeys.COMMIT_WORKFLOW_HANDLER) as AbstractCommitWorkflowHandler<*, *>? if (commitWorkflowHandler == null) { @@ -28,31 +24,13 @@ class AICommitAction : AnAction(), DumbAware { return } - val includedChanges = commitWorkflowHandler.ui.getIncludedChanges() val commitMessage = VcsDataKeys.COMMIT_MESSAGE_CONTROL.getData(e.dataContext) as CommitMessage? - - val diff = computeDiff(includedChanges, false, project) - if (diff.isBlank()) { - sendNotification(Notification.emptyDiff()) - return - } - - val branch = commonBranch(includedChanges, project) - val hint = commitMessage?.text - - val prompt = constructPrompt(AppSettings2.instance.activePrompt.content, diff, branch, hint, project) - - // TODO @Blarc: add support for different clients -// if (isPromptTooLarge(prompt)) { -// sendNotification(Notification.promptTooLarge()) -// return@runBackgroundableTask -// } - if (commitMessage == null) { sendNotification(Notification.noCommitMessage()) return } - llmClient.generateCommitMessage(prompt, project, commitMessage) + val project = e.project ?: return + llmClient.generateCommitMessage(commitWorkflowHandler, commitMessage, project) } } diff --git a/src/main/kotlin/com/github/blarc/ai/commits/intellij/plugin/settings/clients/LLMClientConfiguration.kt b/src/main/kotlin/com/github/blarc/ai/commits/intellij/plugin/settings/clients/LLMClientConfiguration.kt index b7f6b15..3123b00 100644 --- a/src/main/kotlin/com/github/blarc/ai/commits/intellij/plugin/settings/clients/LLMClientConfiguration.kt +++ b/src/main/kotlin/com/github/blarc/ai/commits/intellij/plugin/settings/clients/LLMClientConfiguration.kt @@ -4,6 +4,7 @@ import com.intellij.openapi.project.Project import com.intellij.openapi.ui.ComboBox import com.intellij.openapi.vcs.ui.CommitMessage import com.intellij.util.xmlb.annotations.Attribute +import com.intellij.vcs.commit.AbstractCommitWorkflowHandler import java.util.* import javax.swing.Icon @@ -38,7 +39,7 @@ abstract class LLMClientConfiguration( getSharedState().modelIds.add(modelId) } - abstract fun generateCommitMessage(prompt: String, project: Project, commitMessage: CommitMessage) + abstract fun generateCommitMessage(commitWorkflowHandler: AbstractCommitWorkflowHandler<*, *>, commitMessage: CommitMessage, project: Project) abstract fun getRefreshModelsFunction(): ((ComboBox) -> Unit)? diff --git a/src/main/kotlin/com/github/blarc/ai/commits/intellij/plugin/settings/clients/LLMClientService.kt b/src/main/kotlin/com/github/blarc/ai/commits/intellij/plugin/settings/clients/LLMClientService.kt index 32d9300..382a70a 100644 --- a/src/main/kotlin/com/github/blarc/ai/commits/intellij/plugin/settings/clients/LLMClientService.kt +++ b/src/main/kotlin/com/github/blarc/ai/commits/intellij/plugin/settings/clients/LLMClientService.kt @@ -1,6 +1,11 @@ package com.github.blarc.ai.commits.intellij.plugin.settings.clients import com.github.blarc.ai.commits.intellij.plugin.AICommitsBundle.message +import com.github.blarc.ai.commits.intellij.plugin.AICommitsUtils.commonBranch +import com.github.blarc.ai.commits.intellij.plugin.AICommitsUtils.computeDiff +import com.github.blarc.ai.commits.intellij.plugin.AICommitsUtils.constructPrompt +import com.github.blarc.ai.commits.intellij.plugin.notifications.Notification +import com.github.blarc.ai.commits.intellij.plugin.notifications.sendNotification import com.github.blarc.ai.commits.intellij.plugin.settings.AppSettings2 import com.github.blarc.ai.commits.intellij.plugin.wrap import com.intellij.icons.AllIcons @@ -8,24 +13,50 @@ import com.intellij.openapi.application.EDT import com.intellij.openapi.application.ModalityState import com.intellij.openapi.application.asContextElement import com.intellij.openapi.project.Project +import com.intellij.openapi.vcs.changes.Change import com.intellij.openapi.vcs.ui.CommitMessage import com.intellij.platform.ide.progress.withBackgroundProgress import com.intellij.ui.components.JBLabel +import com.intellij.vcs.commit.AbstractCommitWorkflowHandler +import com.intellij.vcs.commit.isAmendCommitMode import dev.langchain4j.data.message.UserMessage import dev.langchain4j.model.chat.ChatLanguageModel +import git4idea.GitCommit +import git4idea.history.GitHistoryUtils +import git4idea.repo.GitRepositoryManager import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext -abstract class LLMClientService(private val cs: CoroutineScope) { +abstract class LLMClientService(private val cs: CoroutineScope) { - abstract suspend fun buildChatModel(client: T): ChatLanguageModel + abstract suspend fun buildChatModel(client: C): ChatLanguageModel - fun generateCommitMessage(client: T, prompt: String, project: Project, commitMessage: CommitMessage) { - cs.launch(Dispatchers.IO + ModalityState.current().asContextElement()) { + fun generateCommitMessage(clientConfiguration: C, commitWorkflowHandler: AbstractCommitWorkflowHandler<*, *>, commitMessage: CommitMessage, project: Project) { + + val commitContext = commitWorkflowHandler.workflow.commitContext + val includedChanges = commitWorkflowHandler.ui.getIncludedChanges().toMutableList() + + cs.launch(ModalityState.current().asContextElement()) { withBackgroundProgress(project, message("action.background")) { - sendRequest(client, prompt, onSuccess = { + + if (commitContext.isAmendCommitMode) { + includedChanges += getLastCommitChanges(project) + } + + val diff = computeDiff(includedChanges, false, project) + if (diff.isBlank()) { + withContext(Dispatchers.EDT) { + sendNotification(Notification.emptyDiff()) + } + return@withBackgroundProgress + } + + val branch = commonBranch(includedChanges, project) + val prompt = constructPrompt(AppSettings2.instance.activePrompt.content, diff, branch, commitMessage.text, project) + + sendRequest(clientConfiguration, prompt, onSuccess = { withContext(Dispatchers.EDT) { commitMessage.setCommitMessage(it) } @@ -39,9 +70,9 @@ abstract class LLMClientService(private val cs: Coro } } - fun verifyConfiguration(client: T, label: JBLabel) { + fun verifyConfiguration(client: C, label: JBLabel) { label.text = message("settings.verify.running") - cs.launch(Dispatchers.IO + ModalityState.current().asContextElement()) { + cs.launch(ModalityState.current().asContextElement()) { sendRequest(client, "test", onSuccess = { withContext(Dispatchers.EDT) { label.text = message("settings.verify.valid") @@ -56,7 +87,7 @@ abstract class LLMClientService(private val cs: Coro } } - private suspend fun sendRequest(client: T, text: String, onSuccess: suspend (r: String) -> Unit, onError: suspend (r: String) -> Unit) { + private suspend fun sendRequest(client: C, text: String, onSuccess: suspend (r: String) -> Unit, onError: suspend (r: String) -> Unit) { try { val model = buildChatModel(client) val response = withContext(Dispatchers.IO) { @@ -78,4 +109,16 @@ abstract class LLMClientService(private val cs: Coro throw e } } + + private suspend fun getLastCommitChanges(project: Project): List { + return withContext(Dispatchers.IO) { + GitRepositoryManager.getInstance(project).repositories.map { repo -> + GitHistoryUtils.history(project, repo.root, "--max-count=1") + }.filter { commits -> + commits.isNotEmpty() + }.map { commits -> + (commits.first() as GitCommit).changes + }.flatten() + } + } } diff --git a/src/main/kotlin/com/github/blarc/ai/commits/intellij/plugin/settings/clients/anthropic/AnthropicClientConfiguration.kt b/src/main/kotlin/com/github/blarc/ai/commits/intellij/plugin/settings/clients/anthropic/AnthropicClientConfiguration.kt index 9157705..ad33d4d 100644 --- a/src/main/kotlin/com/github/blarc/ai/commits/intellij/plugin/settings/clients/anthropic/AnthropicClientConfiguration.kt +++ b/src/main/kotlin/com/github/blarc/ai/commits/intellij/plugin/settings/clients/anthropic/AnthropicClientConfiguration.kt @@ -1,4 +1,4 @@ -package com.github.blarc.ai.commits.intellij.plugin.settings.clients.anthropic; +package com.github.blarc.ai.commits.intellij.plugin.settings.clients.anthropic import com.github.blarc.ai.commits.intellij.plugin.Icons import com.github.blarc.ai.commits.intellij.plugin.settings.clients.LLMClientConfiguration @@ -7,6 +7,7 @@ import com.intellij.openapi.project.Project import com.intellij.openapi.vcs.ui.CommitMessage import com.intellij.util.xmlb.annotations.Attribute import com.intellij.util.xmlb.annotations.Transient +import com.intellij.vcs.commit.AbstractCommitWorkflowHandler import dev.langchain4j.model.anthropic.AnthropicChatModelName import javax.swing.Icon @@ -44,8 +45,8 @@ class AnthropicClientConfiguration : LLMClientConfiguration( return AnthropicClientSharedState.getInstance() } - override fun generateCommitMessage(prompt: String, project: Project, commitMessage: CommitMessage) { - return AnthropicClientService.getInstance().generateCommitMessage(this, prompt, project, commitMessage) + override fun generateCommitMessage(commitWorkflowHandler: AbstractCommitWorkflowHandler<*, *>, commitMessage: CommitMessage, project: Project) { + return AnthropicClientService.getInstance().generateCommitMessage(this, commitWorkflowHandler, commitMessage, project) } override fun getRefreshModelsFunction() = null diff --git a/src/main/kotlin/com/github/blarc/ai/commits/intellij/plugin/settings/clients/gemini/GeminiClientConfiguration.kt b/src/main/kotlin/com/github/blarc/ai/commits/intellij/plugin/settings/clients/gemini/GeminiClientConfiguration.kt index 6d58717..d37717f 100644 --- a/src/main/kotlin/com/github/blarc/ai/commits/intellij/plugin/settings/clients/gemini/GeminiClientConfiguration.kt +++ b/src/main/kotlin/com/github/blarc/ai/commits/intellij/plugin/settings/clients/gemini/GeminiClientConfiguration.kt @@ -6,6 +6,7 @@ import com.github.blarc.ai.commits.intellij.plugin.settings.clients.LLMClientSha import com.intellij.openapi.project.Project import com.intellij.openapi.vcs.ui.CommitMessage import com.intellij.util.xmlb.annotations.Attribute +import com.intellij.vcs.commit.AbstractCommitWorkflowHandler import javax.swing.Icon class GeminiClientConfiguration : LLMClientConfiguration( @@ -34,8 +35,8 @@ class GeminiClientConfiguration : LLMClientConfiguration( return GeminiClientSharedState.getInstance() } - override fun generateCommitMessage(prompt: String, project: Project, commitMessage: CommitMessage) { - return GeminiClientService.getInstance().generateCommitMessage(this, prompt, project, commitMessage) + override fun generateCommitMessage(commitWorkflowHandler: AbstractCommitWorkflowHandler<*, *>, commitMessage: CommitMessage, project: Project) { + return GeminiClientService.getInstance().generateCommitMessage(this, commitWorkflowHandler, commitMessage, project) } // Model names are hard-coded and do not need to be refreshed. diff --git a/src/main/kotlin/com/github/blarc/ai/commits/intellij/plugin/settings/clients/ollama/OllamaClientConfiguration.kt b/src/main/kotlin/com/github/blarc/ai/commits/intellij/plugin/settings/clients/ollama/OllamaClientConfiguration.kt index 51dc4de..e99c45b 100644 --- a/src/main/kotlin/com/github/blarc/ai/commits/intellij/plugin/settings/clients/ollama/OllamaClientConfiguration.kt +++ b/src/main/kotlin/com/github/blarc/ai/commits/intellij/plugin/settings/clients/ollama/OllamaClientConfiguration.kt @@ -7,6 +7,7 @@ import com.intellij.openapi.project.Project import com.intellij.openapi.ui.ComboBox import com.intellij.openapi.vcs.ui.CommitMessage import com.intellij.util.xmlb.annotations.Attribute +import com.intellij.vcs.commit.AbstractCommitWorkflowHandler import javax.swing.Icon class OllamaClientConfiguration : LLMClientConfiguration( @@ -36,8 +37,8 @@ class OllamaClientConfiguration : LLMClientConfiguration( return OllamaClientSharedState.getInstance() } - override fun generateCommitMessage(prompt: String, project: Project, commitMessage: CommitMessage) { - return OllamaClientService.getInstance().generateCommitMessage(this, prompt, project, commitMessage) + override fun generateCommitMessage(commitWorkflowHandler: AbstractCommitWorkflowHandler<*, *>, commitMessage: CommitMessage, project: Project) { + return OllamaClientService.getInstance().generateCommitMessage(this, commitWorkflowHandler, commitMessage, project) } override fun getRefreshModelsFunction() = fun (cb: ComboBox) { diff --git a/src/main/kotlin/com/github/blarc/ai/commits/intellij/plugin/settings/clients/openAi/OpenAiClientConfiguration.kt b/src/main/kotlin/com/github/blarc/ai/commits/intellij/plugin/settings/clients/openAi/OpenAiClientConfiguration.kt index 5a1572a..466e0f5 100644 --- a/src/main/kotlin/com/github/blarc/ai/commits/intellij/plugin/settings/clients/openAi/OpenAiClientConfiguration.kt +++ b/src/main/kotlin/com/github/blarc/ai/commits/intellij/plugin/settings/clients/openAi/OpenAiClientConfiguration.kt @@ -6,6 +6,7 @@ import com.intellij.openapi.project.Project import com.intellij.openapi.vcs.ui.CommitMessage import com.intellij.util.xmlb.annotations.Attribute import com.intellij.util.xmlb.annotations.Transient +import com.intellij.vcs.commit.AbstractCommitWorkflowHandler import javax.swing.Icon class OpenAiClientConfiguration : LLMClientConfiguration( @@ -43,8 +44,8 @@ class OpenAiClientConfiguration : LLMClientConfiguration( return OpenAiClientSharedState.getInstance() } - override fun generateCommitMessage(prompt: String, project: Project, commitMessage: CommitMessage) { - return OpenAiClientService.getInstance().generateCommitMessage(this, prompt, project, commitMessage) + override fun generateCommitMessage(commitWorkflowHandler: AbstractCommitWorkflowHandler<*, *>, commitMessage: CommitMessage, project: Project) { + return OpenAiClientService.getInstance().generateCommitMessage(this, commitWorkflowHandler, commitMessage, project) } // Model names are retrieved from Enum and do not need to be refreshed. diff --git a/src/main/kotlin/com/github/blarc/ai/commits/intellij/plugin/settings/clients/qianfan/QianfanClientConfiguration.kt b/src/main/kotlin/com/github/blarc/ai/commits/intellij/plugin/settings/clients/qianfan/QianfanClientConfiguration.kt index d0bc546..b0877a4 100644 --- a/src/main/kotlin/com/github/blarc/ai/commits/intellij/plugin/settings/clients/qianfan/QianfanClientConfiguration.kt +++ b/src/main/kotlin/com/github/blarc/ai/commits/intellij/plugin/settings/clients/qianfan/QianfanClientConfiguration.kt @@ -6,6 +6,7 @@ import com.intellij.openapi.project.Project import com.intellij.openapi.vcs.ui.CommitMessage import com.intellij.util.xmlb.annotations.Attribute import com.intellij.util.xmlb.annotations.Transient +import com.intellij.vcs.commit.AbstractCommitWorkflowHandler import dev.langchain4j.model.qianfan.QianfanChatModelNameEnum import javax.swing.Icon @@ -41,8 +42,8 @@ class QianfanClientConfiguration : LLMClientConfiguration( return QianfanClientSharedState.getInstance() } - override fun generateCommitMessage(prompt: String, project: Project, commitMessage: CommitMessage) { - return QianfanClientService.getInstance().generateCommitMessage(this, prompt, project, commitMessage) + override fun generateCommitMessage(commitWorkflowHandler: AbstractCommitWorkflowHandler<*, *>, commitMessage: CommitMessage, project: Project) { + return QianfanClientService.getInstance().generateCommitMessage(this, commitWorkflowHandler, commitMessage, project) } // Model names are retrieved from Enum and do not need to be refreshed.