Skip to content

Commit c4f9b5e

Browse files
committed
feat: Add notification for empty diff.
1 parent a967b03 commit c4f9b5e

File tree

8 files changed

+189
-15
lines changed

8 files changed

+189
-15
lines changed

build.gradle.kts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,16 +97,17 @@ tasks.withType<KotlinCompile>().configureEach {
9797
}
9898

9999
dependencies {
100-
// implementation("com.squareup.okhttp3:okhttp:4.10.0")
101100
implementation("com.aallam.openai:openai-client:3.2.0") {
102101
exclude(group = "org.slf4j", module = "slf4j-api")
102+
// Prevents java.lang.LinkageError: java.lang.LinkageError: loader constraint violation:when resolving method 'long kotlin.time.Duration.toLong-impl(long, kotlin.time.DurationUnit)'
103103
exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib")
104104
exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib-common")
105105
exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib-jdk7")
106106
exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib-jdk8")
107107
}
108108
implementation("io.ktor:ktor-client-cio:2.2.4") {
109109
exclude(group = "org.slf4j", module = "slf4j-api")
110+
// Prevents java.lang.LinkageError: java.lang.LinkageError: loader constraint violation: when resolving method 'long kotlin.time.Duration.toLong-impl(long, kotlin.time.DurationUnit)'
110111
exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib")
111112
exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib-common")
112113
exclude(group = "org.jetbrains.kotlin", module = "kotlin-stdlib-jdk7")
Lines changed: 38 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
package com.github.blarc.ai.commits.intellij.plugin
22

33
import com.github.blarc.ai.commits.intellij.plugin.AICommitsBundle.message
4+
import com.github.blarc.ai.commits.intellij.plugin.notifications.Notification
5+
import com.github.blarc.ai.commits.intellij.plugin.notifications.sendNotification
46
import com.intellij.openapi.actionSystem.AnAction
57
import com.intellij.openapi.actionSystem.AnActionEvent
68
import com.intellij.openapi.progress.runBackgroundableTask
79
import com.intellij.openapi.project.DumbAware
10+
import com.intellij.openapi.project.Project
811
import com.intellij.openapi.vcs.VcsDataKeys
12+
import com.intellij.openapi.vcs.changes.Change
913
import com.intellij.vcs.commit.AbstractCommitWorkflowHandler
1014
import git4idea.commands.Git
1115
import git4idea.commands.GitCommand
@@ -21,22 +25,17 @@ class AICommitAction : AnAction(), DumbAware {
2125
override fun actionPerformed(e: AnActionEvent) {
2226
val project = e.project ?: return
2327

28+
val commitWorkflowHandler =
29+
e.getData(VcsDataKeys.COMMIT_WORKFLOW_HANDLER) as AbstractCommitWorkflowHandler<*, *>
30+
val includedChanges = commitWorkflowHandler.ui.getIncludedChanges()
2431
val commitMessage = VcsDataKeys.COMMIT_MESSAGE_CONTROL.getData(e.dataContext)
25-
val commitWorkflowHandler = e.getData(VcsDataKeys.COMMIT_WORKFLOW_HANDLER) as AbstractCommitWorkflowHandler<*, *>
2632

2733
runBackgroundableTask(message("action.background"), project) {
28-
val diff = commitWorkflowHandler.ui.getIncludedChanges().joinToString("\n") {
29-
30-
val repository = GitRepositoryManager.getInstance(project).getRepositoryForFile(it.virtualFile)
31-
val diffCommand = GitLineHandler(
32-
project,
33-
repository!!.root,
34-
GitCommand.DIFF
35-
)
36-
diffCommand.addParameters(it.virtualFile!!.path)
37-
38-
val commandResult = Git.getInstance().runCommand(diffCommand)
39-
commandResult.outputAsJoinedString
34+
val diff = computeDiff(includedChanges, project)
35+
36+
if (diff.isBlank()) {
37+
sendNotification(Notification.emptyDiff())
38+
return@runBackgroundableTask
4039
}
4140

4241
val openAIService = OpenAIService.instance
@@ -46,4 +45,30 @@ class AICommitAction : AnAction(), DumbAware {
4645
}
4746
}
4847
}
48+
private fun computeDiff(
49+
includedChanges: List<Change>,
50+
project: Project
51+
): String {
52+
val diff = includedChanges.joinToString("\n") {
53+
54+
// Try to get the virtual file for the change and return an empty string if it doesn't exist
55+
val file = it.virtualFile ?: return@joinToString ""
56+
57+
// Try to get the git repository for the file and return an empty string if it doesn't exist
58+
val repository = GitRepositoryManager.getInstance(project).getRepositoryForFile(file)
59+
?: return@joinToString ""
60+
61+
val diffCommand = GitLineHandler(
62+
project,
63+
repository.root,
64+
GitCommand.DIFF
65+
)
66+
diffCommand.addParameters("--cached")
67+
diffCommand.addParameters(file.path)
68+
69+
val commandResult = Git.getInstance().runCommand(diffCommand)
70+
commandResult.outputAsJoinedString
71+
}
72+
return diff
73+
}
4974
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.github.blarc.ai.commits.intellij.plugin
2+
3+
import com.intellij.openapi.util.IconLoader
4+
5+
object Icons {
6+
val AI_COMMITS = IconLoader.getIcon("/icons/aiCommits15.svg", javaClass)
7+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package com.github.blarc.ai.commits.intellij.plugin.notifications
2+
3+
import com.github.blarc.ai.commits.intellij.plugin.AICommitsBundle
4+
import com.github.blarc.ai.commits.intellij.plugin.AICommitsBundle.message
5+
import com.github.blarc.ai.commits.intellij.plugin.AICommitsBundle.openPluginSettings
6+
import com.github.blarc.ai.commits.intellij.plugin.settings.AppSettings
7+
import com.intellij.ide.browsers.BrowserLauncher
8+
import com.intellij.openapi.components.service
9+
import com.intellij.openapi.project.Project
10+
import java.net.URI
11+
12+
data class Notification(
13+
val title: String? = null,
14+
val message: String,
15+
val actions: Set<NotificationAction> = setOf(),
16+
val type: Type = Type.PERSISTENT
17+
) {
18+
enum class Type {
19+
PERSISTENT,
20+
TRANSIENT
21+
}
22+
23+
companion object {
24+
private val DEFAULT_TITLE = message("notifications.title")
25+
26+
fun welcome(version: String) = Notification(message = message("notifications.welcome", version))
27+
28+
fun star() = Notification(
29+
message = """
30+
Finding Gitlab Template Lint useful? Show your support 💖 and ⭐ the repository 🙏.
31+
""".trimIndent(),
32+
actions = setOf(
33+
NotificationAction.openRepository() {
34+
service<AppSettings>().requestSupport = false;
35+
},
36+
NotificationAction.doNotAskAgain() {
37+
service<AppSettings>().requestSupport = false;
38+
}
39+
)
40+
)
41+
42+
fun emptyDiff() = Notification(DEFAULT_TITLE, message = message("notifications.empty-diff"))
43+
fun unsuccessfulRequest(message: String) = Notification(message = message("notifications.unsuccessful-request", message))
44+
45+
}
46+
47+
fun isTransient() = type == Type.TRANSIENT
48+
fun isPersistent() = !isTransient();
49+
}
50+
51+
data class NotificationAction(val title: String, val run: (dismiss: () -> Unit) -> Unit) {
52+
companion object {
53+
fun settings(project: Project, title: String = message("settings.title")) = NotificationAction(title) { dismiss ->
54+
dismiss()
55+
openPluginSettings(project)
56+
}
57+
58+
fun openRepository(onComplete: () -> Unit) = NotificationAction(message("actions.sure-take-me-there")) { dismiss ->
59+
AICommitsBundle.openRepository()
60+
dismiss()
61+
onComplete()
62+
}
63+
64+
fun doNotAskAgain(onComplete: () -> Unit) = NotificationAction(message("actions.do-not-ask-again")) { dismiss ->
65+
dismiss()
66+
onComplete()
67+
}
68+
69+
fun openUrl(url: URI, title: String = message("actions.take-me-there")) = NotificationAction(title) { dismiss ->
70+
dismiss()
71+
BrowserLauncher.instance.open(url.toString());
72+
}
73+
}
74+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package com.github.blarc.ai.commits.intellij.plugin.notifications
2+
3+
import com.github.blarc.ai.commits.intellij.plugin.Icons
4+
import com.intellij.notification.NotificationGroupManager
5+
import com.intellij.notification.NotificationType
6+
import com.intellij.openapi.project.DumbAwareAction
7+
import com.intellij.openapi.project.Project
8+
9+
private const val IMPORTANT_GROUP_ID = "ai.commits.notification.important"
10+
private const val GENERAL_GROUP_ID = "ai.commits.notification.general"
11+
12+
fun sendNotification(notification : Notification, project : Project? = null) {
13+
val groupId = when(notification.type) {
14+
Notification.Type.PERSISTENT -> IMPORTANT_GROUP_ID
15+
Notification.Type.TRANSIENT -> GENERAL_GROUP_ID
16+
}
17+
18+
val notificationManager = NotificationGroupManager
19+
.getInstance()
20+
.getNotificationGroup(groupId)
21+
22+
val intellijNotification = notificationManager.createNotification(
23+
notification.title ?: "",
24+
notification.message,
25+
NotificationType.INFORMATION
26+
)
27+
28+
intellijNotification.icon = Icons.AI_COMMITS
29+
30+
notification.actions.forEach { action ->
31+
intellijNotification.addAction(DumbAwareAction.create(action.title) {
32+
action.run() {
33+
intellijNotification.expire()
34+
}
35+
})
36+
}
37+
38+
intellijNotification.notify(project)
39+
}

src/main/kotlin/com/github/blarc/ai/commits/intellij/plugin/settings/AppSettings.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@ import java.util.Locale
1616
)
1717
class AppSettings : PersistentStateComponent<AppSettings> {
1818

19-
var locale: Locale = Locale.getDefault()
2019
private val openAITokenTitle = "OpenAIToken"
20+
var locale: Locale = Locale.getDefault()
21+
var requestSupport = true
2122
companion object {
2223
const val SERVICE_NAME = "com.github.blarc.ai.commits.intellij.plugin.settings.AppSettings"
2324

src/main/resources/META-INF/plugin.xml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,17 @@
4848
<applicationConfigurable
4949
parentId="tools"
5050
instance="com.github.blarc.ai.commits.intellij.plugin.settings.AppSettingsConfigurable"/>
51+
52+
<notificationGroup
53+
id="ai.commits.notification.general"
54+
displayType="BALLOON"
55+
key="notification.group.general.name"
56+
/>
57+
<notificationGroup
58+
id="ai.commits.notification.important"
59+
displayType="STICKY_BALLOON"
60+
key="notification.group.important.name"
61+
/>
5162
</extensions>
5263

5364
<actions>
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,25 @@
11
name=AI Commits
22

3+
settings.title=Settings
34
settings.general.group.title=AI Commits
45
settings.openAIToken=OpenAI token
56
settings.locale=Locale
67
settings.openAITokenComment=\
78
<p>You can get your token <a href="https://platform.openai.com/account/api-keys">here.</a/></p>
89

910
action.background=Generating commit message
11+
12+
13+
notifications.title=AI Commits
14+
notifications.welcome=Thanks for installing AI Commits <strong>{0}</strong>
15+
notifications.unsuccessful-request=Error occurred: {0}
16+
notifications.internal-server-error=Internal server error ({0}) occurred.
17+
notifications.unknown-error=Unknown error ({0}).
18+
notification.group.important.name=AI Commits important
19+
notification.group.general.name=AI Commits general
20+
notifications.empty-diff=Git diff is empty.
21+
22+
actions.do-not-ask-again = Do not ask me again.
23+
actions.take-me-there = Take me there.
24+
actions.sure-take-me-there=Sure, take me there.
25+

0 commit comments

Comments
 (0)