mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IJPL-191999: vcs: Add asynchronous repository color syncing
- Introduced `GitRepositoryColorsApi` for syncing repository colors. - Implemented `GitRepositoryColorsApiImpl` and supporting classes. - Updated repository color retrieval using `GitRepositoryColorsHolder`. - Adjusted usage in affected UI components and constructors. IJ-MR-166259 GitOrigin-RevId: da87ca59c1cb5b2e76d52d7c5ea3d528eb40a79a
This commit is contained in:
committed by
intellij-monorepo-bot
parent
778814aef5
commit
d5159c2e51
@@ -7,6 +7,7 @@ import com.intellij.openapi.startup.ProjectActivity
|
||||
import com.intellij.util.application
|
||||
import com.intellij.vcs.git.shared.branch.GitInOutStateHolder
|
||||
import com.intellij.vcs.git.shared.repo.GitRepositoriesHolder
|
||||
import com.intellij.vcs.git.shared.repo.GitRepositoryColorsHolder
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@@ -20,5 +21,6 @@ internal class GitDataHoldersInitializer : ProjectActivity {
|
||||
}
|
||||
}
|
||||
GitInOutStateHolder.getInstance(project)
|
||||
GitRepositoryColorsHolder.getInstance(project)
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -76,7 +76,7 @@ abstract class GitBranchesTreeRenderer(
|
||||
val value = treeNode ?: return null
|
||||
return when (value) {
|
||||
is PopupFactoryImpl.ActionItem -> value.getIcon(isSelected)
|
||||
is GitBranchesTreeModel.RepositoryNode -> GitBranchesTreeIconProvider.forRepository(repoColorGenerator, value.repository.repositoryId)
|
||||
is GitBranchesTreeModel.RepositoryNode -> GitBranchesTreeIconProvider.forRepository(treePopupStep.project, value.repository.repositoryId)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
// 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.repo
|
||||
|
||||
import com.intellij.openapi.components.Service
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.platform.project.projectId
|
||||
import com.intellij.platform.vcs.impl.shared.rpc.RepositoryId
|
||||
import com.intellij.vcs.git.shared.repo.GitRepositoryColor.Companion.toAwtColor
|
||||
import com.intellij.vcs.git.shared.rpc.GitRepositoryColorsApi
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.serialization.Serializable
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import java.awt.Color
|
||||
|
||||
@ApiStatus.Internal
|
||||
@Serializable
|
||||
data class GitRepositoryColorsState(
|
||||
val colors: Map<RepositoryId, GitRepositoryColor> = emptyMap(),
|
||||
)
|
||||
|
||||
/**
|
||||
* Bring both bright and dark colors not to handle LaF changes
|
||||
*/
|
||||
@JvmInline
|
||||
@ApiStatus.Internal
|
||||
@Serializable
|
||||
value class GitRepositoryColor(val rgb: Int) {
|
||||
companion object {
|
||||
fun of(color: Color): GitRepositoryColor = GitRepositoryColor(color.rgb)
|
||||
|
||||
fun GitRepositoryColor.toAwtColor(): Color = Color(rgb)
|
||||
}
|
||||
}
|
||||
|
||||
@Service(Service.Level.PROJECT)
|
||||
internal class GitRepositoryColorsHolder(project: Project, cs: CoroutineScope) {
|
||||
private var state = GitRepositoryColorsState()
|
||||
|
||||
init {
|
||||
cs.launch {
|
||||
GitRepositoryColorsApi.getInstance().syncColors(project.projectId()).collect { newState ->
|
||||
LOG.debug("Received new colors - ${newState.colors.size} repos")
|
||||
state = newState
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getColor(repositoryId: RepositoryId): Color? {
|
||||
val repoColor = state.colors[repositoryId]
|
||||
|
||||
if (repoColor == null) {
|
||||
LOG.warn("Color for $repositoryId not loaded")
|
||||
return null
|
||||
}
|
||||
|
||||
return repoColor.toAwtColor()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val LOG = Logger.getInstance(GitRepositoryColorsHolder::class.java)
|
||||
|
||||
fun getInstance(project: Project): GitRepositoryColorsHolder = project.service()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// 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.rpc
|
||||
|
||||
import com.intellij.platform.project.ProjectId
|
||||
import com.intellij.platform.rpc.RemoteApiProviderService
|
||||
import com.intellij.vcs.git.shared.repo.GitRepositoryColorsState
|
||||
import fleet.rpc.RemoteApi
|
||||
import fleet.rpc.Rpc
|
||||
import fleet.rpc.remoteApiDescriptor
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
|
||||
@Rpc
|
||||
@ApiStatus.Internal
|
||||
interface GitRepositoryColorsApi: RemoteApi<Unit> {
|
||||
suspend fun syncColors(projectId: ProjectId) : Flow<GitRepositoryColorsState>
|
||||
|
||||
companion object {
|
||||
suspend fun getInstance() : GitRepositoryColorsApi =
|
||||
RemoteApiProviderService.resolve(remoteApiDescriptor<GitRepositoryColorsApi>())
|
||||
}
|
||||
}
|
||||
+4
-5
@@ -4,9 +4,9 @@ package com.intellij.vcs.git.shared.ui
|
||||
import com.intellij.icons.AllIcons
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.platform.vcs.impl.shared.rpc.RepositoryId
|
||||
import com.intellij.platform.vcs.impl.shared.ui.RepositoryColorGenerator
|
||||
import com.intellij.util.PlatformIcons
|
||||
import com.intellij.util.ui.CheckboxIcon
|
||||
import com.intellij.vcs.git.shared.repo.GitRepositoryColorsHolder
|
||||
import git4idea.GitReference
|
||||
import git4idea.GitTag
|
||||
import icons.DvcsImplIcons
|
||||
@@ -24,10 +24,9 @@ object GitBranchesTreeIconProvider {
|
||||
else -> AllIcons.Vcs.BranchNode
|
||||
}
|
||||
|
||||
fun forRepository(project: Project, repositoryId: RepositoryId): Icon = GitRepoIconProvider.getIcon(project, repositoryId)
|
||||
|
||||
fun forRepository(repositoryColorGenerator: RepositoryColorGenerator<RepositoryId>, repositoryId: RepositoryId): Icon {
|
||||
val color = repositoryColorGenerator.getColor(repositoryId)
|
||||
fun forRepository(project: Project, repositoryId: RepositoryId): Icon {
|
||||
val color = GitRepositoryColorsHolder.getInstance(project).getColor(repositoryId)
|
||||
if (color == null) return GitRepoIconProvider.getIcon(project, repositoryId)
|
||||
return CheckboxIcon.createAndScale(color)
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@ import com.intellij.openapi.ui.validation.WHEN_DOCUMENT_CHANGED
|
||||
import com.intellij.openapi.ui.validation.WHEN_STATE_CHANGED
|
||||
import com.intellij.openapi.util.NlsContexts
|
||||
import com.intellij.openapi.util.text.HtmlBuilder
|
||||
import com.intellij.platform.vcs.impl.shared.ui.RepositoryColorGeneratorFactory
|
||||
import com.intellij.ui.CollectionComboBoxModel
|
||||
import com.intellij.ui.EditorTextField
|
||||
import com.intellij.ui.dsl.builder.*
|
||||
@@ -189,7 +188,6 @@ internal class GitNewBranchDialog @JvmOverloads constructor(
|
||||
|
||||
private fun createRepositoriesCombobox(): ComboBox<GitRepository?> {
|
||||
val items = listOf(ALL_REPOSITORIES, *allRepositories.toTypedArray())
|
||||
val colorGenerator = RepositoryColorGeneratorFactory.create(allRepositories.map { it.rpcId })
|
||||
|
||||
return ComboBox(CollectionComboBoxModel(items)).apply {
|
||||
renderer = listCellRenderer<GitRepository?>(GitBundle.message("new.branch.dialog.branch.root.all.name")) {
|
||||
@@ -198,7 +196,7 @@ internal class GitNewBranchDialog @JvmOverloads constructor(
|
||||
icon(AllIcons.Empty)
|
||||
}
|
||||
else if (repo != null) {
|
||||
icon(GitBranchesTreeIconProvider.forRepository(colorGenerator, repo.rpcId))
|
||||
icon(GitBranchesTreeIconProvider.forRepository(project, repo.rpcId))
|
||||
text(DvcsUtil.getShortRepositoryName(repo))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,10 +4,7 @@ package git4idea.remoteApi
|
||||
import com.intellij.openapi.client.currentSession
|
||||
import com.intellij.platform.rpc.backend.RemoteApiProvider
|
||||
import com.intellij.util.application
|
||||
import com.intellij.vcs.git.shared.rpc.GitIncomingOutgoingStateApi
|
||||
import com.intellij.vcs.git.shared.rpc.GitRepositoryApi
|
||||
import com.intellij.vcs.git.shared.rpc.GitUiSettingsApi
|
||||
import com.intellij.vcs.git.shared.rpc.GitWidgetApi
|
||||
import com.intellij.vcs.git.shared.rpc.*
|
||||
import fleet.rpc.remoteApiDescriptor
|
||||
|
||||
internal class GitApiProvider : RemoteApiProvider {
|
||||
@@ -21,6 +18,9 @@ internal class GitApiProvider : RemoteApiProvider {
|
||||
remoteApi(remoteApiDescriptor<GitIncomingOutgoingStateApi>()) {
|
||||
GitIncomingOutgoingStateApiImpl()
|
||||
}
|
||||
remoteApi(remoteApiDescriptor<GitRepositoryColorsApi>()) {
|
||||
GitRepositoryColorsApiImpl()
|
||||
}
|
||||
remoteApi(remoteApiDescriptor<GitUiSettingsApi>()) {
|
||||
GitUiSettingsApiImpl()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package git4idea.remoteApi
|
||||
|
||||
import com.intellij.dvcs.repo.VcsRepositoryManager
|
||||
import com.intellij.dvcs.repo.VcsRepositoryMappingListener
|
||||
import com.intellij.dvcs.ui.RepositoryChangesBrowserNode
|
||||
import com.intellij.ide.ui.LafManagerListener
|
||||
import com.intellij.openapi.application.readAction
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.platform.project.ProjectId
|
||||
import com.intellij.platform.project.findProjectOrNull
|
||||
import com.intellij.platform.vcs.impl.shared.rpc.RepositoryId
|
||||
import com.intellij.vcs.git.shared.repo.GitRepositoryColor
|
||||
import com.intellij.vcs.git.shared.repo.GitRepositoryColorsState
|
||||
import com.intellij.vcs.git.shared.rpc.GitRepositoryColorsApi
|
||||
import git4idea.GitDisposable
|
||||
import git4idea.repo.GitRepositoryManager
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.channels.BufferOverflow
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
|
||||
|
||||
internal class GitRepositoryColorsApiImpl : GitRepositoryColorsApi {
|
||||
|
||||
@OptIn(FlowPreview::class)
|
||||
override suspend fun syncColors(projectId: ProjectId): Flow<GitRepositoryColorsState> {
|
||||
requireOwner()
|
||||
|
||||
val project = projectId.findProjectOrNull() ?: return emptyFlow()
|
||||
|
||||
return callbackFlow {
|
||||
val notifier = MutableSharedFlow<Unit>(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
|
||||
|
||||
val messageBusConnection = readAction {
|
||||
if (project.isDisposed) {
|
||||
close()
|
||||
return@readAction null
|
||||
}
|
||||
|
||||
val coroutineScope = GitDisposable.getInstance(project).coroutineScope
|
||||
coroutineScope.launch {
|
||||
send(calcColorsState(project))
|
||||
notifier.debounce(100.milliseconds).collectLatest {
|
||||
LOG.debug("Sending new colors")
|
||||
send(calcColorsState(project))
|
||||
}
|
||||
}
|
||||
|
||||
project.messageBus.connect(coroutineScope).also {
|
||||
it.subscribe(VcsRepositoryManager.VCS_REPOSITORY_MAPPING_UPDATED, VcsRepositoryMappingListener {
|
||||
LOG.debug("VCS mapping changed")
|
||||
notifier.tryEmit(Unit)
|
||||
})
|
||||
it.subscribe(LafManagerListener.TOPIC, LafManagerListener {
|
||||
LOG.debug("LAF changed")
|
||||
notifier.tryEmit(Unit)
|
||||
})
|
||||
}
|
||||
}
|
||||
awaitClose {
|
||||
LOG.debug("Connection closed")
|
||||
messageBusConnection?.disconnect()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun calcColorsState(project: Project): GitRepositoryColorsState {
|
||||
val colorManager = RepositoryChangesBrowserNode.getColorManager(project)
|
||||
|
||||
val gitRepositories = GitRepositoryManager.getInstance(project).repositories
|
||||
val repositoryColors: MutableMap<RepositoryId, GitRepositoryColor> = mutableMapOf()
|
||||
|
||||
for (gitRepository in gitRepositories) {
|
||||
val color = colorManager.getRootColor(gitRepository.root)
|
||||
repositoryColors[gitRepository.rpcId] = GitRepositoryColor.of(color)
|
||||
}
|
||||
|
||||
return GitRepositoryColorsState(repositoryColors)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val LOG = Logger.getInstance(GitRepositoryColorsApiImpl::class.java)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user