@@ -13,11 +13,16 @@ import com.intellij.openapi.diff.impl.patch.IdeaTextPatchBuilder
1313import com.intellij.openapi.diff.impl.patch.UnifiedDiffWriter
1414import com.intellij.openapi.project.Project
1515import com.intellij.openapi.vcs.changes.Change
16+ import com.intellij.openapi.vfs.VfsUtilCore
1617import com.intellij.tasks.TaskManager
1718import com.intellij.util.text.DateFormatUtil
19+ import com.intellij.vcsUtil.VcsUtil
20+ import git4idea.GitVcs
1821import git4idea.repo.GitRepositoryManager
1922import kotlinx.coroutines.Dispatchers
2023import kotlinx.coroutines.withContext
24+ import org.jetbrains.idea.svn.SvnUtil
25+ import org.jetbrains.idea.svn.SvnVcs
2126import java.io.StringWriter
2227import java.nio.file.FileSystems
2328import java.util.*
@@ -39,11 +44,11 @@ object AICommitsUtils {
3944 return false
4045 }
4146
42- fun constructPrompt (promptContent : String , diff : String , branch : String , hint : String? , project : Project ): String {
47+ fun constructPrompt (promptContent : String , diff : String , branch : String? , hint : String? , project : Project ): String {
4348 var content = promptContent
4449 val locale = project.service<ProjectSettings >().locale
4550 content = content.replace(" {locale}" , locale.getDisplayLanguage(Locale .ENGLISH ))
46- content = content.replace( " {branch} " , branch)
51+ content = replaceBranch(content , branch)
4752 content = replaceHint(content, hint)
4853
4954 // TODO @Blarc: If TaskManager is null, the prompt might be incorrect...
@@ -62,6 +67,18 @@ object AICommitsUtils {
6267 }
6368 }
6469
70+ fun replaceBranch (promptContent : String , branch : String? ): String {
71+ if (promptContent.contains(" {branch}" )) {
72+ if (branch != null ) {
73+ return promptContent.replace(" {branch}" , branch)
74+ } else {
75+ sendNotification(Notification .noCommonBranch())
76+ return promptContent.replace(" {branch}" , " main" )
77+ }
78+ }
79+ return promptContent
80+ }
81+
6582 fun replaceHint (promptContent : String , hint : String? ): String {
6683 val hintRegex = Regex (" \\ {[^{}]*(\\ \$ hint)[^{}]*}" )
6784
@@ -78,25 +95,29 @@ object AICommitsUtils {
7895 return promptContent.replace(" {hint}" , hint.orEmpty())
7996 }
8097
81- suspend fun getCommonBranchOrDefault (changes : List <Change >, project : Project , showNotification : Boolean = true): String {
82- var branch = getCommonBranch(changes, project)
83- if (branch == null ) {
84- // Can't show notification in edit prompt dialog
85- if (showNotification) {
86- sendNotification(Notification .noCommonBranch())
87- }
88- // hardcoded fallback branch
89- branch = " main"
90- }
91- return branch
92- }
93-
9498 suspend fun getCommonBranch (changes : List <Change >, project : Project ): String? {
95- val repositoryManager = GitRepositoryManager .getInstance(project)
9699 return withContext(Dispatchers .IO ) {
97- changes.map {
98- repositoryManager.getRepositoryForFile(it.virtualFile)?.currentBranchName
99- }.filterNotNull().groupingBy { it }.eachCount().maxByOrNull { it.value }?.key
100+ changes.mapNotNull {
101+ it.virtualFile?.let { virtualFile ->
102+ VcsUtil .getVcsFor(project, virtualFile)?.let { vcs ->
103+ when (vcs) {
104+ is SvnVcs -> {
105+ SvnUtil .getUrl(vcs, VfsUtilCore .virtualToIoFile(virtualFile))?.let { url ->
106+ extractSvnBranchName(url.toDecodedString())
107+ }
108+ }
109+ is GitVcs -> {
110+ GitRepositoryManager .getInstance(project)
111+ .getRepositoryForFile(it.virtualFile)
112+ ?.currentBranchName
113+ }
114+ else -> {
115+ null
116+ }
117+ }
118+ }
119+ }
120+ }.groupingBy { it }.eachCount().maxByOrNull { it.value }?.key
100121 }
101122 }
102123
@@ -140,22 +161,27 @@ object AICommitsUtils {
140161 .joinToString(" \n " )
141162 }
142163
143- // TODO @Blarc: This only works for OpenAI
144- // fun isPromptTooLarge(prompt: String): Boolean {
145- // val registry = Encodings.newDefaultEncodingRegistry()
146- //
147- // /*
148- // * Try to find the model type based on the model id by finding the longest matching model type
149- // * If no model type matches, let the request go through and let the OpenAI API handle it
150- // */
151- // val modelType = ModelType.entries
152- // .filter { AppSettings2.instance.getActiveLLMClient().modelId.contains(it.name) }
153- // .maxByOrNull { it.name.length }
154- // ?: return false
155- //
156- // val encoding = registry.getEncoding(modelType.encodingType)
157- // return encoding.countTokens(prompt) > modelType.maxContextLength
158- // }
164+ private fun extractSvnBranchName (url : String ): String? {
165+ val normalizedUrl = url.lowercase()
166+
167+ // Standard SVN layout: repository/trunk, repository/branches/name, repository/tags/name
168+ return when {
169+ normalizedUrl.contains(" /branches/" ) -> {
170+ val branchPart = url.substringAfter(" /branches/" )
171+ val endIndex = branchPart.indexOf(' /' )
172+ if (endIndex > 0 ) branchPart.substring(0 , endIndex) else branchPart
173+ }
174+
175+ normalizedUrl.contains(" /tags/" ) -> {
176+ val tagPart = url.substringAfter(" /tags/" )
177+ val endIndex = tagPart.indexOf(' /' )
178+ if (endIndex > 0 ) " tag: ${tagPart.substring(0 , endIndex)} " else " tag: $tagPart "
179+ }
180+
181+ normalizedUrl.contains(" /trunk" ) -> " trunk"
182+ else -> null // fallback: no branch concept available
183+ }
184+ }
159185
160186 suspend fun retrieveToken (title : String ): OneTimeString ? {
161187 val credentialAttributes = getCredentialAttributes(title)
0 commit comments