1
+ package cc.unitmesh.git.provider
2
+
3
+ import cc.unitmesh.devti.language.ast.variable.ToolchainVariable
4
+ import cc.unitmesh.devti.language.ast.variable.toolchain.VcsToolchainVariable
5
+ import cc.unitmesh.devti.language.provider.ToolchainVariableProvider
6
+ import cc.unitmesh.devti.language.provider.action.VariableActionEventDataHolder
7
+ import cc.unitmesh.devti.vcs.VcsPrompting
8
+ import com.intellij.ide.DataManager
9
+ import com.intellij.openapi.actionSystem.DataContext
10
+ import com.intellij.openapi.diagnostic.logger
11
+ import com.intellij.openapi.editor.Editor
12
+ import com.intellij.openapi.project.Project
13
+ import com.intellij.openapi.vcs.VcsDataKeys
14
+ import com.intellij.openapi.vcs.changes.Change
15
+ import com.intellij.openapi.vcs.changes.CurrentContentRevision
16
+ import com.intellij.openapi.vfs.VirtualFile
17
+ import com.intellij.psi.PsiElement
18
+ import com.intellij.vcs.commit.CommitWorkflowUi
19
+ import com.intellij.vcs.log.VcsFullCommitDetails
20
+ import com.intellij.vcs.log.VcsLogDataKeys
21
+ import com.intellij.vcs.log.VcsLogFilterCollection
22
+ import com.intellij.vcs.log.VcsLogProvider
23
+ import com.intellij.vcs.log.impl.VcsProjectLog
24
+ import com.intellij.vcs.log.visible.filters.VcsLogFilterObject
25
+ import java.awt.EventQueue.invokeAndWait
26
+
27
+
28
+ class GitToolchainVariableProvider : ToolchainVariableProvider {
29
+ private val logger = logger<GitToolchainVariableProvider >()
30
+
31
+ override fun isResolvable (variable : ToolchainVariable , psiElement : PsiElement ? , project : Project ): Boolean {
32
+ return when (variable) {
33
+ VcsToolchainVariable .CurrentChanges -> true
34
+ VcsToolchainVariable .HistoryCommitMessages -> true
35
+ VcsToolchainVariable .CurrentBranch -> true
36
+ VcsToolchainVariable .Diff -> true
37
+ else -> false
38
+ }
39
+ }
40
+
41
+ override fun resolve (
42
+ variable : ToolchainVariable ,
43
+ project : Project ,
44
+ editor : Editor ,
45
+ psiElement : PsiElement ? ,
46
+ ): ToolchainVariable {
47
+ when (variable) {
48
+ VcsToolchainVariable .CurrentChanges -> {
49
+ val commitWorkflowUi = getCommitWorkflowUi()
50
+ if (commitWorkflowUi !is CommitWorkflowUi ) {
51
+ logger.warn(" Cannot get commit workflow UI, you may not be in a commit workflow." )
52
+ return variable
53
+ }
54
+ var changes: List <Change >? = null
55
+ invokeAndWait {
56
+ changes = getDiff(commitWorkflowUi)
57
+ }
58
+
59
+ if (changes == null ) {
60
+ logger.warn(" Cannot get changes." )
61
+ return variable
62
+ }
63
+
64
+ val diffContext = project.getService(VcsPrompting ::class .java).prepareContext(changes!! )
65
+
66
+ if (diffContext.isEmpty() || diffContext == " \n " ) {
67
+ logger.warn(" Diff context is empty or cannot get enough useful context." )
68
+ return variable
69
+ }
70
+
71
+ variable.value = diffContext
72
+ return variable
73
+ }
74
+
75
+ VcsToolchainVariable .CurrentBranch -> {
76
+ val logProviders = VcsProjectLog .getLogProviders(project)
77
+ val entry = logProviders.entries.firstOrNull() ? : return variable
78
+
79
+ val logProvider = entry.value
80
+ val branch = logProvider.getCurrentBranch(entry.key) ? : return variable
81
+
82
+ variable.value = branch
83
+ }
84
+
85
+ VcsToolchainVariable .HistoryCommitMessages -> {
86
+ val exampleCommitMessages = getHistoryCommitMessages(project)
87
+ if (exampleCommitMessages != null ) {
88
+ variable.value = exampleCommitMessages
89
+ }
90
+ }
91
+
92
+ VcsToolchainVariable .Diff -> {
93
+ val dataContext = VariableActionEventDataHolder .getData()?.dataContext
94
+ variable.value = analysisLog(dataContext, project)
95
+ }
96
+ }
97
+
98
+ return variable
99
+ }
100
+
101
+ private fun analysisLog (dataContext : DataContext ? , project : Project ): String {
102
+ if (dataContext == null ) {
103
+ return " "
104
+ }
105
+
106
+
107
+ val vcsLog = dataContext.getData(VcsLogDataKeys .VCS_LOG ) ? : return " "
108
+
109
+ val details: List <VcsFullCommitDetails > = vcsLog.selectedDetails.toList()
110
+ val selectList = dataContext.getData(VcsDataKeys .SELECTED_CHANGES ).orEmpty().toList()
111
+
112
+ val vcsPrompting = project.getService(VcsPrompting ::class .java)
113
+ val fullChangeContent =
114
+ vcsPrompting.buildDiffPrompt(details, selectList.toList(), project)
115
+
116
+ return fullChangeContent ? : " "
117
+ }
118
+
119
+ /* *
120
+ * Finds example commit messages based on the project's VCS log, takes the first three commits.
121
+ * If the no user or user has committed anything yet, the current branch name is used instead.
122
+ *
123
+ * @param project The project for which to find example commit messages.
124
+ * @return A string containing example commit messages, or null if no example messages are found.
125
+ */
126
+ private fun getHistoryCommitMessages (project : Project ): String? {
127
+ val logProviders = VcsProjectLog .getLogProviders(project)
128
+ val entry = logProviders.entries.firstOrNull() ? : return null
129
+
130
+ val logProvider = entry.value
131
+ val branch = logProvider.getCurrentBranch(entry.key) ? : return null
132
+ val user = logProvider.getCurrentUser(entry.key)
133
+
134
+ val logFilter = if (user != null ) {
135
+ VcsLogFilterObject .collection(VcsLogFilterObject .fromUser(user, setOf ()))
136
+ } else {
137
+ VcsLogFilterObject .collection(VcsLogFilterObject .fromBranch(branch))
138
+ }
139
+
140
+ return collectExamples(logProvider, entry.key, logFilter)
141
+ }
142
+
143
+ /* *
144
+ * Collects examples from the VcsLogProvider based on the provided filter.
145
+ *
146
+ * @param logProvider The VcsLogProvider used to retrieve commit information.
147
+ * @param root The root VirtualFile of the project.
148
+ * @param filter The VcsLogFilterCollection used to filter the commits.
149
+ * @return A string containing the collected examples, or null if no examples are found.
150
+ */
151
+ private fun collectExamples (
152
+ logProvider : VcsLogProvider ,
153
+ root : VirtualFile ,
154
+ filter : VcsLogFilterCollection ,
155
+ ): String? {
156
+ val commits = logProvider.getCommitsMatchingFilter(root, filter, 3 )
157
+
158
+ if (commits.isEmpty()) return null
159
+
160
+ val builder = StringBuilder (" " )
161
+ val commitIds = commits.map { it.id.asString() }
162
+
163
+ logProvider.readMetadata(root, commitIds) {
164
+ val shortMsg = it.fullMessage.split(" \n " ).firstOrNull() ? : it.fullMessage
165
+ builder.append(shortMsg).append(" \n " )
166
+ }
167
+
168
+ return builder.toString()
169
+ }
170
+
171
+ private fun getDiff (commitWorkflowUi : CommitWorkflowUi ): List <Change >? {
172
+ val changes = commitWorkflowUi.getIncludedChanges()
173
+ val unversionedFiles = commitWorkflowUi.getIncludedUnversionedFiles()
174
+
175
+ val changeList = unversionedFiles.map {
176
+ Change (null , CurrentContentRevision (it))
177
+ }
178
+
179
+ if (changes.isNotEmpty() || changeList.isNotEmpty()) {
180
+ return changes + changeList
181
+ }
182
+
183
+ return null
184
+ }
185
+
186
+ }
187
+
188
+ fun getCommitWorkflowUi (): CommitWorkflowUi ? {
189
+ VariableActionEventDataHolder .getData()?.dataContext?.let {
190
+ val commitWorkflowUi = it.getData(VcsDataKeys .COMMIT_WORKFLOW_UI )
191
+ return commitWorkflowUi as CommitWorkflowUi ?
192
+ }
193
+
194
+ val dataContext = DataManager .getInstance().dataContextFromFocus.result
195
+ val commitWorkflowUi = dataContext?.getData(VcsDataKeys .COMMIT_WORKFLOW_UI )
196
+ return commitWorkflowUi
197
+ }
0 commit comments