diff --git a/plugins/git4idea/shared/BUILD.bazel b/plugins/git4idea/shared/BUILD.bazel index 555d2623e15f..c61d5654c698 100644 --- a/plugins/git4idea/shared/BUILD.bazel +++ b/plugins/git4idea/shared/BUILD.bazel @@ -25,6 +25,7 @@ jvm_library( "//platform/dvcs-impl/shared", "//platform/core-ui", "//platform/util/coroutines", + "@lib//:caffeine", ], runtime_deps = [":shared_resources"] ) diff --git a/plugins/git4idea/shared/intellij.vcs.git.shared.iml b/plugins/git4idea/shared/intellij.vcs.git.shared.iml index 883dffec8147..1792af2c3733 100644 --- a/plugins/git4idea/shared/intellij.vcs.git.shared.iml +++ b/plugins/git4idea/shared/intellij.vcs.git.shared.iml @@ -46,5 +46,6 @@ + \ No newline at end of file diff --git a/plugins/git4idea/src/git4idea/ui/branch/GitBranchesClippedNamesCache.kt b/plugins/git4idea/shared/src/com/intellij/vcs/git/shared/branch/GitBranchesClippedNamesCache.kt similarity index 86% rename from plugins/git4idea/src/git4idea/ui/branch/GitBranchesClippedNamesCache.kt rename to plugins/git4idea/shared/src/com/intellij/vcs/git/shared/branch/GitBranchesClippedNamesCache.kt index a9dd8f539b25..d9cf4905b617 100644 --- a/plugins/git4idea/src/git4idea/ui/branch/GitBranchesClippedNamesCache.kt +++ b/plugins/git4idea/shared/src/com/intellij/vcs/git/shared/branch/GitBranchesClippedNamesCache.kt @@ -1,5 +1,5 @@ -// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -package git4idea.ui.branch +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.vcs.git.shared.branch import com.github.benmanes.caffeine.cache.Cache import com.github.benmanes.caffeine.cache.Caffeine @@ -7,7 +7,7 @@ import com.intellij.openapi.Disposable import com.intellij.openapi.components.Service import com.intellij.openapi.project.Project import com.intellij.openapi.util.Disposer -import git4idea.ui.branch.GitBranchPopupActions.truncateBranchName +import com.intellij.vcs.git.shared.ui.GitBranchPresentation import java.util.concurrent.TimeUnit @Service(Service.Level.PROJECT) @@ -42,7 +42,7 @@ class GitBranchesClippedNamesCache(private val project: Project) : Disposable { } private fun truncateBranchName(branchName: String, maxBranchNameLength: Int): ClippedBranch { - return ClippedBranch(truncateBranchName(project, branchName, maxBranchNameLength, 0, 0), maxBranchNameLength) + return ClippedBranch(GitBranchPresentation.truncateBranchName(project, branchName, maxBranchNameLength, 0, 0), maxBranchNameLength) } fun clear() = cache.invalidateAll() @@ -55,4 +55,4 @@ class GitBranchesClippedNamesCache(private val project: Project) : Disposable { private const val CACHE_MAX_SIZE = 1_000L private const val CACHE_ACCESS_EXPIRATION_MIN = 5L } -} +} \ No newline at end of file diff --git a/plugins/git4idea/shared/src/com/intellij/vcs/git/shared/ui/GitBranchPresentation.java b/plugins/git4idea/shared/src/com/intellij/vcs/git/shared/ui/GitBranchPresentation.java new file mode 100644 index 000000000000..b8ecc6b7ad00 --- /dev/null +++ b/plugins/git4idea/shared/src/com/intellij/vcs/git/shared/ui/GitBranchPresentation.java @@ -0,0 +1,91 @@ +// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.vcs.git.shared.ui; + +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.NlsSafe; +import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vcs.IssueNavigationConfiguration; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +@ApiStatus.Internal +public final class GitBranchPresentation { + public static final int BRANCH_NAME_LENGTH_DELTA = 4; + public static final int BRANCH_NAME_SUFFIX_LENGTH = 5; + + public static @NlsSafe @NotNull String truncateBranchName(@NotNull Project project, + @NotNull @NlsSafe String branchName, + int maxBranchNameLength) { + return truncateBranchName(project, branchName, maxBranchNameLength, BRANCH_NAME_SUFFIX_LENGTH, BRANCH_NAME_LENGTH_DELTA); + } + + public static @NlsSafe @NotNull String truncateBranchName(@NotNull Project project, @NotNull @NlsSafe String branchName, + int maxBranchNameLength, int suffixLength, int delta) { + int branchNameLength = branchName.length(); + + if (branchNameLength <= maxBranchNameLength + delta) { + return branchName; + } + + IssueNavigationConfiguration issueNavigationConfiguration = IssueNavigationConfiguration.getInstance(project); + List issueMatches = issueNavigationConfiguration.findIssueLinks(branchName); + int affectedMaxBranchNameLength = maxBranchNameLength - StringUtil.ELLIPSIS.length(); + if (!issueMatches.isEmpty()) { + // never truncate the first occurrence of the issue id + IssueNavigationConfiguration.LinkMatch firstMatch = issueMatches.get(0); + TextRange firstMatchRange = firstMatch.getRange(); + return truncateAndSaveIssueId(firstMatchRange, branchName, affectedMaxBranchNameLength, suffixLength, delta); + } + + if (affectedMaxBranchNameLength - suffixLength - StringUtil.ELLIPSIS.length() < 0) { + return branchName; + } + + return StringUtil.shortenTextWithEllipsis(branchName, affectedMaxBranchNameLength, suffixLength, true); + } + + private static @NlsSafe @NotNull String truncateAndSaveIssueId(@NotNull TextRange issueIdRange, + @NotNull String branchName, + int maxBranchNameLength, + int suffixLength, int delta) { + String truncatedByDefault = StringUtil.shortenTextWithEllipsis(branchName, + maxBranchNameLength, + suffixLength, true); + String issueId = issueIdRange.substring(branchName); + + if (truncatedByDefault.contains(issueId)) return truncatedByDefault; + + try { + int branchNameLength = branchName.length(); + int endOffset = issueIdRange.getEndOffset(); + int startOffset = issueIdRange.getStartOffset(); + + // suffix intersects with the issue id + if (endOffset >= branchNameLength - suffixLength - delta) { + return StringUtil.shortenTextWithEllipsis(branchName, + maxBranchNameLength, + branchNameLength - startOffset, true); + } + + String suffix = branchName.substring(branchNameLength - suffixLength); + int prefixLength = maxBranchNameLength - suffixLength - issueId.length(); + + String prefixAndIssue; + if (Math.abs(startOffset - prefixLength) <= delta) { + prefixAndIssue = branchName.substring(0, endOffset); + } + else { + String prefix = branchName.substring(0, prefixLength); + prefixAndIssue = prefix + StringUtil.ELLIPSIS + issueId; + } + + return prefixAndIssue + StringUtil.ELLIPSIS + suffix; + } + catch (Throwable e) { + return truncatedByDefault; + } + } +} diff --git a/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopupActions.java b/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopupActions.java index 966b7ea4039a..3bf1427e50a0 100644 --- a/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopupActions.java +++ b/plugins/git4idea/src/git4idea/ui/branch/GitBranchPopupActions.java @@ -12,11 +12,10 @@ import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.NlsContexts; import com.intellij.openapi.util.NlsSafe; -import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.openapi.vcs.IssueNavigationConfiguration; import com.intellij.util.concurrency.annotations.RequiresEdt; import com.intellij.util.ui.EmptyIcon; +import com.intellij.vcs.git.shared.ui.GitBranchPresentation; import git4idea.*; import git4idea.branch.GitBranchIncomingOutgoingManager; import git4idea.branch.GitBrancher; @@ -48,8 +47,6 @@ public final class GitBranchPopupActions { public static final @NonNls String EXPERIMENTAL_BRANCH_POPUP_ACTION_GROUP = "Git.Experimental.Branch.Popup.Actions"; private static final int MAX_BRANCH_NAME_LENGTH = 40; - public static final int BRANCH_NAME_LENGTH_DELTA = 4; - public static final int BRANCH_NAME_SUFFIX_LENGTH = 5; private GitBranchPopupActions() { } @@ -95,75 +92,7 @@ public final class GitBranchPopupActions { } public static @NlsSafe @NotNull String truncateBranchName(@NotNull @NlsSafe String branchName, @NotNull Project project) { - return truncateBranchName(project, branchName, - MAX_BRANCH_NAME_LENGTH, BRANCH_NAME_SUFFIX_LENGTH, BRANCH_NAME_LENGTH_DELTA); - } - - public static @NlsSafe @NotNull String truncateBranchName(@NotNull Project project, @NotNull @NlsSafe String branchName, - int maxBranchNameLength, int suffixLength, int delta) { - int branchNameLength = branchName.length(); - - if (branchNameLength <= maxBranchNameLength + delta) { - return branchName; - } - - IssueNavigationConfiguration issueNavigationConfiguration = IssueNavigationConfiguration.getInstance(project); - List issueMatches = issueNavigationConfiguration.findIssueLinks(branchName); - int affectedMaxBranchNameLength = maxBranchNameLength - StringUtil.ELLIPSIS.length(); - if (!issueMatches.isEmpty()) { - // never truncate the first occurrence of the issue id - IssueNavigationConfiguration.LinkMatch firstMatch = issueMatches.get(0); - TextRange firstMatchRange = firstMatch.getRange(); - return truncateAndSaveIssueId(firstMatchRange, branchName, affectedMaxBranchNameLength, suffixLength, delta); - } - - if (affectedMaxBranchNameLength - suffixLength - StringUtil.ELLIPSIS.length() < 0) { - return branchName; - } - - return StringUtil.shortenTextWithEllipsis(branchName, affectedMaxBranchNameLength, suffixLength, true); - } - - private static @NlsSafe @NotNull String truncateAndSaveIssueId(@NotNull TextRange issueIdRange, - @NotNull String branchName, - int maxBranchNameLength, - int suffixLength, int delta) { - String truncatedByDefault = StringUtil.shortenTextWithEllipsis(branchName, - maxBranchNameLength, - suffixLength, true); - String issueId = issueIdRange.substring(branchName); - - if (truncatedByDefault.contains(issueId)) return truncatedByDefault; - - try { - int branchNameLength = branchName.length(); - int endOffset = issueIdRange.getEndOffset(); - int startOffset = issueIdRange.getStartOffset(); - - // suffix intersects with the issue id - if (endOffset >= branchNameLength - suffixLength - delta) { - return StringUtil.shortenTextWithEllipsis(branchName, - maxBranchNameLength, - branchNameLength - startOffset, true); - } - - String suffix = branchName.substring(branchNameLength - suffixLength); - int prefixLength = maxBranchNameLength - suffixLength - issueId.length(); - - String prefixAndIssue; - if (Math.abs(startOffset - prefixLength) <= delta) { - prefixAndIssue = branchName.substring(0, endOffset); - } - else { - String prefix = branchName.substring(0, prefixLength); - prefixAndIssue = prefix + StringUtil.ELLIPSIS + issueId; - } - - return prefixAndIssue + StringUtil.ELLIPSIS + suffix; - } - catch (Throwable e) { - return truncatedByDefault; - } + return GitBranchPresentation.truncateBranchName(project, branchName, MAX_BRANCH_NAME_LENGTH); } private static @NlsSafe @NotNull String wrapWithQuotes(@NlsSafe @NotNull String branchName) { diff --git a/plugins/git4idea/src/git4idea/ui/branch/GitCurrentBranchPresenter.kt b/plugins/git4idea/src/git4idea/ui/branch/GitCurrentBranchPresenter.kt index 4ae4dd22e709..a82d0291b5fa 100644 --- a/plugins/git4idea/src/git4idea/ui/branch/GitCurrentBranchPresenter.kt +++ b/plugins/git4idea/src/git4idea/ui/branch/GitCurrentBranchPresenter.kt @@ -11,6 +11,7 @@ import com.intellij.openapi.util.text.HtmlBuilder import com.intellij.openapi.util.text.StringUtil import com.intellij.util.messages.Topic import com.intellij.vcs.git.shared.branch.calcTooltip +import com.intellij.vcs.git.shared.ui.GitBranchPresentation import git4idea.GitTag import git4idea.GitUtil import git4idea.GitVcs @@ -84,10 +85,7 @@ private fun getDefaultPresentation(repository: GitRepository): GitCurrentBranchP private fun calcText(repository: GitRepository): @NlsSafe String = StringUtil.escapeMnemonics(GitBranchUtil.getDisplayableBranchText(repository) { branchName -> - GitBranchPopupActions.truncateBranchName(repository.project, branchName, - GitToolbarWidgetAction.BRANCH_NAME_MAX_LENGTH, - GitBranchPopupActions.BRANCH_NAME_SUFFIX_LENGTH, - GitBranchPopupActions.BRANCH_NAME_LENGTH_DELTA) + GitBranchPresentation.truncateBranchName(repository.project, branchName, GitToolbarWidgetAction.BRANCH_NAME_MAX_LENGTH) }) private fun GitRepository.calcIcon(): Icon? { diff --git a/plugins/git4idea/src/git4idea/ui/branch/tree/GitBranchesTreeRenderer.kt b/plugins/git4idea/src/git4idea/ui/branch/tree/GitBranchesTreeRenderer.kt index 363263e84ce6..1c0529ec5fcf 100644 --- a/plugins/git4idea/src/git4idea/ui/branch/tree/GitBranchesTreeRenderer.kt +++ b/plugins/git4idea/src/git4idea/ui/branch/tree/GitBranchesTreeRenderer.kt @@ -17,11 +17,11 @@ import com.intellij.util.ui.JBUI import com.intellij.util.ui.UpdateScaleHelper import com.intellij.util.ui.components.BorderLayoutPanel import com.intellij.util.ui.tree.TreeUtil +import com.intellij.vcs.git.shared.branch.GitBranchesClippedNamesCache import com.intellij.vcs.git.shared.repo.GitRepositoryFrontendModel import com.intellij.vcs.git.shared.ui.GitBranchesTreeIconProvider import git4idea.GitBranch import git4idea.GitReference -import git4idea.ui.branch.GitBranchesClippedNamesCache import git4idea.ui.branch.popup.GitBranchesTreePopupBase import git4idea.ui.branch.popup.GitBranchesTreePopupStepBase import git4idea.ui.branch.tree.GitBranchesTreeModel.RefUnderRepository