mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[github] rework project repo manager
Make it more flexible to allow further code reuse Switch to coroutines GitOrigin-RevId: 6bf3de73c1b1eaf47342eef4fd6d2552119a5e21
This commit is contained in:
committed by
intellij-monorepo-bot
parent
762fb7287b
commit
65045c00ac
@@ -0,0 +1,47 @@
|
||||
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.collaboration.async
|
||||
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.MainScope
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
@ApiStatus.Experimental
|
||||
@Suppress("FunctionName")
|
||||
fun DisposingMainScope(parentDisposable: Disposable): CoroutineScope =
|
||||
MainScope().also {
|
||||
Disposer.register(parentDisposable) {
|
||||
it.cancel()
|
||||
}
|
||||
}
|
||||
|
||||
@ApiStatus.Experimental
|
||||
interface ScopedDisposable : Disposable {
|
||||
val scope: CoroutineScope
|
||||
}
|
||||
|
||||
@ApiStatus.Experimental
|
||||
class CancellingScopedDisposable(context: CoroutineContext = SupervisorJob()) : ScopedDisposable {
|
||||
|
||||
override val scope: CoroutineScope = CoroutineScope(context)
|
||||
|
||||
override fun dispose() {
|
||||
scope.cancel()
|
||||
}
|
||||
}
|
||||
|
||||
@ApiStatus.Experimental
|
||||
fun <T1, T2, R> combineState(scope: CoroutineScope,
|
||||
state1: StateFlow<T1>,
|
||||
state2: StateFlow<T2>,
|
||||
transform: (T1, T2) -> R): StateFlow<R> =
|
||||
combine(state1, state2, transform)
|
||||
.stateIn(scope, SharingStarted.Eagerly, transform(state1.value, state2.value))
|
||||
@@ -76,13 +76,6 @@
|
||||
<registryKey defaultValue="true" description="Applying suggested changes" key="github.suggested.changes.apply"/>
|
||||
</extensions>
|
||||
|
||||
<projectListeners>
|
||||
<listener class="org.jetbrains.plugins.github.util.GHProjectRepositoriesManager$RemoteUrlsListener"
|
||||
topic="com.intellij.dvcs.repo.VcsRepositoryMappingListener"/>
|
||||
<listener class="org.jetbrains.plugins.github.util.GHProjectRepositoriesManager$RemoteUrlsListener"
|
||||
topic="git4idea.repo.GitRepositoryChangeListener"/>
|
||||
</projectListeners>
|
||||
|
||||
<extensions defaultExtensionNs="Git4Idea">
|
||||
<GitHttpAuthDataProvider implementation="org.jetbrains.plugins.github.extensions.GHHttpAuthDataProvider"/>
|
||||
<GitHttpAuthDataProvider implementation="org.jetbrains.plugins.github.extensions.GHComHttpAuthDataProvider"/>
|
||||
|
||||
@@ -33,7 +33,7 @@ import org.jetbrains.annotations.Nls
|
||||
import org.jetbrains.plugins.github.api.GHRepositoryCoordinates
|
||||
import org.jetbrains.plugins.github.i18n.GithubBundle
|
||||
import org.jetbrains.plugins.github.pullrequest.action.GHPRActionKeys
|
||||
import org.jetbrains.plugins.github.util.GHProjectRepositoriesManager
|
||||
import org.jetbrains.plugins.github.util.GHHostedRepositoriesManager
|
||||
import org.jetbrains.plugins.github.util.GithubNotificationIdsHolder
|
||||
import org.jetbrains.plugins.github.util.GithubNotifications
|
||||
import org.jetbrains.plugins.github.util.GithubUtil
|
||||
@@ -87,7 +87,7 @@ open class GHOpenInBrowserActionGroup
|
||||
val repository = GitUtil.getRepositoryManager(project).getRepositoryForFileQuick(fileRevision.path)
|
||||
if (repository == null) return null
|
||||
|
||||
val accessibleRepositories = project.service<GHProjectRepositoriesManager>().findKnownRepositories(repository)
|
||||
val accessibleRepositories = project.service<GHHostedRepositoriesManager>().findKnownRepositories(repository)
|
||||
if (accessibleRepositories.isEmpty()) return null
|
||||
|
||||
return accessibleRepositories.map { Data.Revision(project, it.ghRepositoryCoordinates, fileRevision.revisionNumber.asString()) }
|
||||
@@ -105,7 +105,7 @@ open class GHOpenInBrowserActionGroup
|
||||
if (repository == null) return null
|
||||
|
||||
|
||||
val accessibleRepositories = project.service<GHProjectRepositoriesManager>().findKnownRepositories(repository)
|
||||
val accessibleRepositories = project.service<GHHostedRepositoriesManager>().findKnownRepositories(repository)
|
||||
if (accessibleRepositories.isEmpty()) return null
|
||||
|
||||
return accessibleRepositories.map { Data.Revision(project, it.ghRepositoryCoordinates, commit.hash.asString()) }
|
||||
@@ -118,7 +118,7 @@ open class GHOpenInBrowserActionGroup
|
||||
if (repository == null) return null
|
||||
|
||||
|
||||
val accessibleRepositories = project.service<GHProjectRepositoriesManager>().findKnownRepositories(repository)
|
||||
val accessibleRepositories = project.service<GHHostedRepositoriesManager>().findKnownRepositories(repository)
|
||||
if (accessibleRepositories.isEmpty()) return null
|
||||
|
||||
val changeListManager = ChangeListManager.getInstance(project)
|
||||
|
||||
+2
-2
@@ -8,7 +8,7 @@ import com.intellij.openapi.vcs.annotate.UpToDateLineNumberListener
|
||||
import com.intellij.vcsUtil.VcsUtil
|
||||
import git4idea.GitUtil
|
||||
import git4idea.annotate.GitFileAnnotation
|
||||
import org.jetbrains.plugins.github.util.GHProjectRepositoriesManager
|
||||
import org.jetbrains.plugins.github.util.GHHostedRepositoriesManager
|
||||
|
||||
|
||||
class GHOpenInBrowserFromAnnotationActionGroup(val annotation: FileAnnotation)
|
||||
@@ -25,7 +25,7 @@ class GHOpenInBrowserFromAnnotationActionGroup(val annotation: FileAnnotation)
|
||||
val filePath = VcsUtil.getFilePath(virtualFile)
|
||||
val repository = GitUtil.getRepositoryManager(project).getRepositoryForFileQuick(filePath) ?: return null
|
||||
|
||||
val accessibleRepositories = project.service<GHProjectRepositoriesManager>().findKnownRepositories(repository)
|
||||
val accessibleRepositories = project.service<GHHostedRepositoriesManager>().findKnownRepositories(repository)
|
||||
if (accessibleRepositories.isEmpty()) return null
|
||||
|
||||
val revisionHash = annotation.getLineRevisionNumber(myLineNumber)?.asString()
|
||||
|
||||
@@ -9,7 +9,7 @@ import com.intellij.openapi.vcs.FileStatus
|
||||
import com.intellij.openapi.vcs.changes.ChangeListManager
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import git4idea.GitUtil
|
||||
import org.jetbrains.plugins.github.util.GHProjectRepositoriesManager
|
||||
import org.jetbrains.plugins.github.util.GHHostedRepositoriesManager
|
||||
|
||||
class GithubCopyPathProvider: DumbAwareCopyPathProvider() {
|
||||
override fun getPathToElement(project: Project, virtualFile: VirtualFile?, editor: Editor?): String? {
|
||||
@@ -21,7 +21,7 @@ class GithubCopyPathProvider: DumbAwareCopyPathProvider() {
|
||||
val repository = GitUtil.getRepositoryManager(project).getRepositoryForFileQuick(virtualFile)
|
||||
if (repository == null) return null
|
||||
|
||||
val accessibleRepositories = project.service<GHProjectRepositoriesManager>().findKnownRepositories(repository)
|
||||
val accessibleRepositories = project.service<GHHostedRepositoriesManager>().findKnownRepositories(repository)
|
||||
if (accessibleRepositories.isEmpty()) return null
|
||||
|
||||
val refs = accessibleRepositories
|
||||
|
||||
@@ -107,7 +107,7 @@ class GithubShareAction : DumbAwareAction(GithubBundle.messagePointer("share.act
|
||||
|
||||
val gitRepository = GithubGitHelper.findGitRepository(project, file)
|
||||
val possibleRemotes = gitRepository
|
||||
?.let(project.service<GHProjectRepositoriesManager>()::findKnownRepositories)
|
||||
?.let(project.service<GHHostedRepositoriesManager>()::findKnownRepositories)
|
||||
?.map { it.gitRemoteUrlCoordinates.url }.orEmpty()
|
||||
if (possibleRemotes.isNotEmpty()) {
|
||||
val existingRemotesDialog = GithubExistingRemotesDialog(project, possibleRemotes)
|
||||
|
||||
@@ -90,7 +90,7 @@ public class GithubSyncForkAction extends DumbAwareAction {
|
||||
return;
|
||||
}
|
||||
|
||||
GHProjectRepositoriesManager ghRepositoriesManager = project.getServiceIfCreated(GHProjectRepositoriesManager.class);
|
||||
GHHostedRepositoriesManager ghRepositoriesManager = project.getServiceIfCreated(GHHostedRepositoriesManager.class);
|
||||
if (ghRepositoriesManager == null) {
|
||||
LOG.warn("Unable to get the GHProjectRepositoriesManager service");
|
||||
return;
|
||||
@@ -158,7 +158,7 @@ public class GithubSyncForkAction extends DumbAwareAction {
|
||||
Project project = e.getData(CommonDataKeys.PROJECT);
|
||||
if (project == null || project.isDefault()) return false;
|
||||
|
||||
GHProjectRepositoriesManager repositoriesManager = project.getServiceIfCreated(GHProjectRepositoriesManager.class);
|
||||
GHHostedRepositoriesManager repositoriesManager = project.getServiceIfCreated(GHHostedRepositoriesManager.class);
|
||||
if (repositoriesManager == null) return false;
|
||||
|
||||
return !repositoriesManager.getKnownRepositories().isEmpty();
|
||||
|
||||
+3
-2
@@ -18,7 +18,7 @@ import org.jetbrains.plugins.github.api.GithubApiRequestExecutorManager
|
||||
import org.jetbrains.plugins.github.api.util.SimpleGHGQLPagesLoader
|
||||
import org.jetbrains.plugins.github.authentication.GithubAuthenticationManager
|
||||
import org.jetbrains.plugins.github.i18n.GithubBundle
|
||||
import org.jetbrains.plugins.github.util.GHProjectRepositoriesManager
|
||||
import org.jetbrains.plugins.github.util.GHHostedRepositoriesManager
|
||||
import org.jetbrains.plugins.github.util.GithubProjectSettings
|
||||
|
||||
private val LOG = logger<GHProtectedBranchRulesLoader>()
|
||||
@@ -63,7 +63,8 @@ internal class GHProtectedBranchRulesLoader : GitFetchHandler {
|
||||
val requestExecutor = GithubApiRequestExecutorManager.getInstance().getExecutor(account)
|
||||
|
||||
val githubRepositoryMapping =
|
||||
project.service<GHProjectRepositoriesManager>().findKnownRepositories(repository).find { it.gitRemoteUrlCoordinates.remote == remote }
|
||||
project.service<GHHostedRepositoriesManager>().findKnownRepositories(repository)
|
||||
.find { it.gitRemoteUrlCoordinates.remote == remote }
|
||||
?: continue
|
||||
|
||||
val repositoryCoordinates = githubRepositoryMapping.ghRepositoryCoordinates
|
||||
|
||||
+10
-10
@@ -1,10 +1,10 @@
|
||||
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.plugins.github.pullrequest
|
||||
|
||||
import com.intellij.collaboration.async.DisposingMainScope
|
||||
import com.intellij.openapi.actionSystem.CommonShortcuts
|
||||
import com.intellij.openapi.actionSystem.DefaultActionGroup
|
||||
import com.intellij.openapi.actionSystem.EmptyAction
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.project.DumbAware
|
||||
import com.intellij.openapi.project.Project
|
||||
@@ -13,6 +13,7 @@ import com.intellij.openapi.wm.ToolWindow
|
||||
import com.intellij.openapi.wm.ToolWindowFactory
|
||||
import com.intellij.openapi.wm.ex.ToolWindowEx
|
||||
import com.intellij.openapi.wm.impl.content.ToolWindowContentUi
|
||||
import kotlinx.coroutines.launch
|
||||
import org.jetbrains.plugins.github.authentication.GithubAuthenticationManager
|
||||
import org.jetbrains.plugins.github.pullrequest.action.GHPRSelectPullRequestForFileAction
|
||||
import org.jetbrains.plugins.github.pullrequest.action.GHPRSwitchRemoteAction
|
||||
@@ -20,18 +21,17 @@ import org.jetbrains.plugins.github.pullrequest.config.GithubPullRequestsProject
|
||||
import org.jetbrains.plugins.github.pullrequest.data.GHPRDataContextRepository
|
||||
import org.jetbrains.plugins.github.pullrequest.ui.toolwindow.GHPRToolWindowTabController
|
||||
import org.jetbrains.plugins.github.pullrequest.ui.toolwindow.GHPRToolWindowTabControllerImpl
|
||||
import org.jetbrains.plugins.github.util.GHGitRepositoryMapping
|
||||
import org.jetbrains.plugins.github.util.GHProjectRepositoriesManager
|
||||
import org.jetbrains.plugins.github.util.GHHostedRepositoriesManager
|
||||
import javax.swing.JPanel
|
||||
|
||||
internal class GHPRToolWindowFactory : ToolWindowFactory, DumbAware {
|
||||
override fun init(toolWindow: ToolWindow) {
|
||||
ApplicationManager.getApplication().messageBus.connect(toolWindow.disposable)
|
||||
.subscribe(GHProjectRepositoriesManager.LIST_CHANGES_TOPIC, object : GHProjectRepositoriesManager.ListChangeListener {
|
||||
override fun repositoryListChanged(newList: Set<GHGitRepositoryMapping>, project: Project) {
|
||||
toolWindow.isAvailable = newList.isNotEmpty()
|
||||
}
|
||||
})
|
||||
val repositoriesManager = toolWindow.project.service<GHHostedRepositoriesManager>()
|
||||
DisposingMainScope(toolWindow.disposable).launch {
|
||||
repositoriesManager.knownRepositoriesState.collect {
|
||||
toolWindow.isAvailable = it.isNotEmpty()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun createToolWindowContent(project: Project, toolWindow: ToolWindow) = with(toolWindow as ToolWindowEx) {
|
||||
@@ -45,7 +45,7 @@ internal class GHPRToolWindowFactory : ToolWindowFactory, DumbAware {
|
||||
setDisposer(Disposer.newDisposable("GHPR tab disposable"))
|
||||
}.also {
|
||||
val authManager = GithubAuthenticationManager.getInstance()
|
||||
val repositoryManager = project.service<GHProjectRepositoriesManager>()
|
||||
val repositoryManager = project.service<GHHostedRepositoriesManager>()
|
||||
val dataContextRepository = GHPRDataContextRepository.getInstance(project)
|
||||
val projectString = GithubPullRequestsProjectUISettings.getInstance(project)
|
||||
it.putUserData(GHPRToolWindowTabController.KEY,
|
||||
|
||||
+2
-3
@@ -3,14 +3,13 @@ package org.jetbrains.plugins.github.pullrequest.config
|
||||
|
||||
import com.intellij.openapi.components.*
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import org.jetbrains.plugins.github.api.GHRepositoryCoordinates
|
||||
import org.jetbrains.plugins.github.api.GHRepositoryPath
|
||||
import org.jetbrains.plugins.github.api.GithubServerPath
|
||||
import org.jetbrains.plugins.github.authentication.accounts.GHAccountSerializer
|
||||
import org.jetbrains.plugins.github.authentication.accounts.GithubAccount
|
||||
import org.jetbrains.plugins.github.util.GHGitRepositoryMapping
|
||||
import org.jetbrains.plugins.github.util.GHProjectRepositoriesManager
|
||||
import org.jetbrains.plugins.github.util.GHHostedRepositoriesManager
|
||||
|
||||
@Service
|
||||
@State(name = "GithubPullRequestsUISettings", storages = [Storage(StoragePathMacros.WORKSPACE_FILE)], reportStatistic = false)
|
||||
@@ -27,7 +26,7 @@ class GithubPullRequestsProjectUISettings(private val project: Project)
|
||||
var selectedRepoAndAccount: Pair<GHGitRepositoryMapping, GithubAccount>?
|
||||
get() {
|
||||
val (url, accountId) = state.selectedUrlAndAccountId ?: return null
|
||||
val repo = project.service<GHProjectRepositoriesManager>().knownRepositories.find {
|
||||
val repo = project.service<GHHostedRepositoriesManager>().knownRepositories.find {
|
||||
it.gitRemoteUrlCoordinates.url == url
|
||||
} ?: return null
|
||||
val account = GHAccountSerializer.deserialize(accountId) ?: return null
|
||||
|
||||
+19
-22
@@ -1,11 +1,11 @@
|
||||
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.plugins.github.pullrequest.ui.toolwindow
|
||||
|
||||
import com.intellij.collaboration.async.DisposingMainScope
|
||||
import com.intellij.collaboration.auth.AccountsListener
|
||||
import com.intellij.collaboration.ui.CollaborationToolsUIUtil.defaultButton
|
||||
import com.intellij.ide.plugins.newui.HorizontalLayout
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.application.invokeAndWaitIfNeeded
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.ui.components.ActionLink
|
||||
@@ -13,6 +13,7 @@ import com.intellij.util.castSafelyTo
|
||||
import com.intellij.util.ui.JBUI
|
||||
import com.intellij.util.ui.UI
|
||||
import com.intellij.util.ui.UIUtil
|
||||
import kotlinx.coroutines.launch
|
||||
import net.miginfocom.layout.CC
|
||||
import net.miginfocom.layout.LC
|
||||
import net.miginfocom.layout.PlatformDefaults
|
||||
@@ -26,8 +27,7 @@ import org.jetbrains.plugins.github.ui.component.GHAccountSelectorComponentFacto
|
||||
import org.jetbrains.plugins.github.ui.component.GHRepositorySelectorComponentFactory
|
||||
import org.jetbrains.plugins.github.ui.util.getName
|
||||
import org.jetbrains.plugins.github.util.GHGitRepositoryMapping
|
||||
import org.jetbrains.plugins.github.util.GHProjectRepositoriesManager
|
||||
import org.jetbrains.plugins.github.util.GHProjectRepositoriesManager.ListChangeListener
|
||||
import org.jetbrains.plugins.github.util.GHHostedRepositoriesManager
|
||||
import java.awt.event.ActionEvent
|
||||
import javax.swing.*
|
||||
import javax.swing.event.ListDataEvent
|
||||
@@ -35,7 +35,7 @@ import javax.swing.event.ListDataListener
|
||||
|
||||
class GHPRRepositorySelectorComponentFactory(private val project: Project,
|
||||
private val authManager: GithubAuthenticationManager,
|
||||
private val repositoryManager: GHProjectRepositoriesManager) {
|
||||
private val repositoryManager: GHHostedRepositoriesManager) {
|
||||
fun create(disposable: Disposable, onSelected: (GHGitRepositoryMapping, GithubAccount) -> Unit): JComponent {
|
||||
val repositoriesModel = ComboBoxWithActionsModel<GHGitRepositoryMapping>().apply {
|
||||
//todo: add remote action
|
||||
@@ -72,7 +72,7 @@ class GHPRRepositorySelectorComponentFactory(private val project: Project,
|
||||
}
|
||||
}
|
||||
|
||||
Controller(project = project, authManager = authManager, repositoryManager = repositoryManager,
|
||||
Controller(project = project, authManager = authManager, repositoriesManager = repositoryManager,
|
||||
repositoriesModel = repositoriesModel, accountsModel = accountsModel,
|
||||
applyAction = applyAction, githubLoginAction = githubLoginAction, tokenLoginAction = tokenLoginAction,
|
||||
gheLoginActon = gheLoginAction,
|
||||
@@ -126,7 +126,7 @@ class GHPRRepositorySelectorComponentFactory(private val project: Project,
|
||||
|
||||
private class Controller(private val project: Project,
|
||||
private val authManager: GithubAuthenticationManager,
|
||||
private val repositoryManager: GHProjectRepositoriesManager,
|
||||
private val repositoriesManager: GHHostedRepositoriesManager,
|
||||
private val repositoriesModel: ComboBoxWithActionsModel<GHGitRepositoryMapping>,
|
||||
private val accountsModel: ComboBoxWithActionsModel<GithubAccount>,
|
||||
private val applyAction: Action,
|
||||
@@ -135,33 +135,30 @@ class GHPRRepositorySelectorComponentFactory(private val project: Project,
|
||||
private val gheLoginActon: Action,
|
||||
disposable: Disposable) {
|
||||
|
||||
private val scope = DisposingMainScope(disposable)
|
||||
|
||||
init {
|
||||
ApplicationManager.getApplication().messageBus.connect(disposable)
|
||||
.subscribe(GHProjectRepositoriesManager.LIST_CHANGES_TOPIC, object : ListChangeListener {
|
||||
override fun repositoryListChanged(newList: Set<GHGitRepositoryMapping>, project: Project) {
|
||||
updateRepositories()
|
||||
}
|
||||
})
|
||||
repositoriesModel.addSelectionChangeListener(::updateAccounts)
|
||||
repositoriesModel.addSelectionChangeListener(::updateActions)
|
||||
accountsModel.addSelectionChangeListener(::updateActions)
|
||||
|
||||
scope.launch {
|
||||
repositoriesManager.knownRepositoriesState.collect {
|
||||
repositoriesModel.items = it.toList()
|
||||
repositoriesModel.preSelect()
|
||||
}
|
||||
}
|
||||
|
||||
authManager.addListener(disposable, object : AccountsListener<GithubAccount> {
|
||||
override fun onAccountListChanged(old: Collection<GithubAccount>, new: Collection<GithubAccount>) {
|
||||
invokeAndWaitIfNeeded(runnable = ::updateAccounts)
|
||||
}
|
||||
})
|
||||
|
||||
repositoriesModel.addSelectionChangeListener(::updateAccounts)
|
||||
repositoriesModel.addSelectionChangeListener(::updateActions)
|
||||
accountsModel.addSelectionChangeListener(::updateActions)
|
||||
|
||||
updateRepositories()
|
||||
updateAccounts()
|
||||
updateActions()
|
||||
}
|
||||
|
||||
private fun updateRepositories() {
|
||||
repositoriesModel.items = repositoryManager.knownRepositories.sortedBy { it.gitRemoteUrlCoordinates.remote.name }
|
||||
repositoriesModel.preSelect()
|
||||
}
|
||||
|
||||
private fun updateAccounts() {
|
||||
val serverPath = repositoriesModel.selectedItem?.wrappee?.ghRepositoryCoordinates?.serverPath
|
||||
if (serverPath == null) {
|
||||
|
||||
+32
-14
@@ -1,11 +1,11 @@
|
||||
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.plugins.github.pullrequest.ui.toolwindow
|
||||
|
||||
import com.intellij.collaboration.async.DisposingMainScope
|
||||
import com.intellij.collaboration.auth.AccountsListener
|
||||
import com.intellij.ide.DataManager
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.actionSystem.ActionManager
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.util.ClearableLazyValue
|
||||
import com.intellij.openapi.util.Disposer
|
||||
@@ -14,6 +14,11 @@ import com.intellij.ui.components.panels.Wrapper
|
||||
import com.intellij.ui.content.Content
|
||||
import com.intellij.util.IJSwingUtilities
|
||||
import com.intellij.util.ui.UIUtil
|
||||
import kotlinx.coroutines.channels.BufferOverflow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.launch
|
||||
import org.jetbrains.plugins.github.api.GithubApiRequestExecutor
|
||||
import org.jetbrains.plugins.github.api.GithubApiRequestExecutorManager
|
||||
import org.jetbrains.plugins.github.api.data.pullrequest.GHPullRequestShort
|
||||
@@ -32,14 +37,14 @@ import org.jetbrains.plugins.github.pullrequest.ui.GHLoadingPanelFactory
|
||||
import org.jetbrains.plugins.github.pullrequest.ui.toolwindow.create.GHPRCreateComponentHolder
|
||||
import org.jetbrains.plugins.github.ui.util.GHUIUtil
|
||||
import org.jetbrains.plugins.github.util.GHGitRepositoryMapping
|
||||
import org.jetbrains.plugins.github.util.GHProjectRepositoriesManager
|
||||
import org.jetbrains.plugins.github.util.GHHostedRepositoriesManager
|
||||
import java.awt.BorderLayout
|
||||
import javax.swing.JComponent
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
internal class GHPRToolWindowTabControllerImpl(private val project: Project,
|
||||
private val authManager: GithubAuthenticationManager,
|
||||
private val repositoryManager: GHProjectRepositoriesManager,
|
||||
private val repositoryManager: GHHostedRepositoriesManager,
|
||||
private val dataContextRepository: GHPRDataContextRepository,
|
||||
private val projectSettings: GithubPullRequestsProjectUISettings,
|
||||
private val tab: Content) : GHPRToolWindowTabController {
|
||||
@@ -54,6 +59,12 @@ internal class GHPRToolWindowTabControllerImpl(private val project: Project,
|
||||
private val tabDisposable = Disposer.newCheckedDisposable().also {
|
||||
Disposer.register(tab.disposer!!, it)
|
||||
}
|
||||
private val scope = DisposingMainScope(tabDisposable)
|
||||
|
||||
private val resetRequestFlow = MutableSharedFlow<Unit>(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST).apply {
|
||||
tryEmit(Unit)
|
||||
}
|
||||
|
||||
private var contentDisposable by Delegates.observable<Disposable?>(null) { _, oldValue, newValue ->
|
||||
if (oldValue != null) Disposer.dispose(oldValue)
|
||||
if (newValue != null) Disposer.register(tabDisposable, newValue)
|
||||
@@ -71,22 +82,29 @@ internal class GHPRToolWindowTabControllerImpl(private val project: Project,
|
||||
}
|
||||
|
||||
init {
|
||||
val accountsFlow = MutableSharedFlow<Set<GithubAccount>>(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
|
||||
authManager.addListener(tabDisposable, object : AccountsListener<GithubAccount> {
|
||||
override fun onAccountListChanged(old: Collection<GithubAccount>, new: Collection<GithubAccount>) = scheduleUpdate()
|
||||
override fun onAccountCredentialsChanged(account: GithubAccount) = scheduleUpdate()
|
||||
override fun onAccountListChanged(old: Collection<GithubAccount>, new: Collection<GithubAccount>) {
|
||||
accountsFlow.tryEmit(new.toSet())
|
||||
}
|
||||
|
||||
private fun scheduleUpdate() = ApplicationManager.getApplication()
|
||||
.invokeLater(Runnable { Updater().update() }) { tabDisposable.isDisposed }
|
||||
override fun onAccountCredentialsChanged(account: GithubAccount) {
|
||||
accountsFlow.tryEmit(authManager.getAccounts())
|
||||
}
|
||||
})
|
||||
repositoryManager.addRepositoryListChangedListener(tabDisposable) {
|
||||
Updater().update()
|
||||
accountsFlow.tryEmit(authManager.getAccounts())
|
||||
|
||||
scope.launch {
|
||||
combine(repositoryManager.knownRepositoriesState, accountsFlow, resetRequestFlow) { repos, accounts, _ ->
|
||||
Updater(repos, accounts)
|
||||
}.collectLatest {
|
||||
it.update()
|
||||
}
|
||||
}
|
||||
Updater().update()
|
||||
}
|
||||
|
||||
private inner class Updater {
|
||||
private val repos = repositoryManager.knownRepositories
|
||||
private val accounts = authManager.getAccounts()
|
||||
private inner class Updater(private val repos: Set<GHGitRepositoryMapping>,
|
||||
private val accounts: Set<GithubAccount>) {
|
||||
|
||||
fun update() {
|
||||
val wasReset = resetIfMissing()
|
||||
@@ -230,7 +248,7 @@ internal class GHPRToolWindowTabControllerImpl(private val project: Project,
|
||||
currentRepository = null
|
||||
currentAccount = null
|
||||
projectSettings.selectedRepoAndAccount = null
|
||||
Updater().update()
|
||||
resetRequestFlow.tryEmit(Unit)
|
||||
}
|
||||
|
||||
private inner class ComponentController(private val dataContext: GHPRDataContext,
|
||||
|
||||
+2
-2
@@ -53,7 +53,7 @@ import org.jetbrains.plugins.github.ui.util.DisableableDocument
|
||||
import org.jetbrains.plugins.github.util.ChangeDiffRequestProducerFactory
|
||||
import org.jetbrains.plugins.github.util.DiffRequestChainProducer
|
||||
import org.jetbrains.plugins.github.util.GHGitRepositoryMapping
|
||||
import org.jetbrains.plugins.github.util.GHProjectRepositoriesManager
|
||||
import org.jetbrains.plugins.github.util.GHHostedRepositoriesManager
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import javax.swing.JComponent
|
||||
import javax.swing.JPanel
|
||||
@@ -64,7 +64,7 @@ import javax.swing.text.PlainDocument
|
||||
internal class GHPRCreateComponentHolder(private val actionManager: ActionManager,
|
||||
private val project: Project,
|
||||
private val settings: GithubPullRequestsProjectUISettings,
|
||||
private val repositoriesManager: GHProjectRepositoriesManager,
|
||||
private val repositoriesManager: GHHostedRepositoriesManager,
|
||||
private val dataContext: GHPRDataContext,
|
||||
private val viewController: GHPRToolWindowTabComponentController,
|
||||
disposable: Disposable) {
|
||||
|
||||
+2
-2
@@ -7,11 +7,11 @@ import git4idea.GitBranch
|
||||
import git4idea.GitRemoteBranch
|
||||
import git4idea.ui.branch.MergeDirectionModel
|
||||
import org.jetbrains.plugins.github.util.GHGitRepositoryMapping
|
||||
import org.jetbrains.plugins.github.util.GHProjectRepositoriesManager
|
||||
import org.jetbrains.plugins.github.util.GHHostedRepositoriesManager
|
||||
import org.jetbrains.plugins.github.util.GithubUtil.Delegates.observableField
|
||||
|
||||
class GHPRMergeDirectionModelImpl(override val baseRepo: GHGitRepositoryMapping,
|
||||
private val repositoriesManager: GHProjectRepositoriesManager) : MergeDirectionModel<GHGitRepositoryMapping> {
|
||||
private val repositoriesManager: GHHostedRepositoriesManager) : MergeDirectionModel<GHGitRepositoryMapping> {
|
||||
|
||||
private val changeEventDispatcher = EventDispatcher.create(SimpleEventListener::class.java)
|
||||
|
||||
|
||||
@@ -0,0 +1,200 @@
|
||||
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.plugins.github.util
|
||||
|
||||
import com.intellij.collaboration.async.CancellingScopedDisposable
|
||||
import com.intellij.collaboration.async.ScopedDisposable
|
||||
import com.intellij.collaboration.async.combineState
|
||||
import com.intellij.collaboration.auth.AccountsListener
|
||||
import com.intellij.collaboration.hosting.GitHostingUrlUtil
|
||||
import com.intellij.dvcs.repo.VcsRepositoryManager
|
||||
import com.intellij.dvcs.repo.VcsRepositoryMappingListener
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
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 git4idea.repo.GitRepository
|
||||
import git4idea.repo.GitRepositoryChangeListener
|
||||
import git4idea.repo.GitRepositoryManager
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.future.await
|
||||
import kotlinx.coroutines.launch
|
||||
import org.jetbrains.plugins.github.api.GithubServerPath
|
||||
import org.jetbrains.plugins.github.authentication.accounts.GHAccountManager
|
||||
import org.jetbrains.plugins.github.authentication.accounts.GithubAccount
|
||||
import org.jetbrains.plugins.github.pullrequest.GHPRStatisticsCollector
|
||||
|
||||
@Service
|
||||
class GHHostedRepositoriesManager(private val project: Project) : ScopedDisposable by CancellingScopedDisposable() {
|
||||
|
||||
private val accountManager: GHAccountManager get() = service()
|
||||
private val repositoryManager: GitRepositoryManager get() = project.service()
|
||||
private val metadataLoader: GHEnterpriseServerMetadataLoader get() = service()
|
||||
|
||||
val knownRepositoriesState: StateFlow<Set<GHGitRepositoryMapping>>
|
||||
val knownRepositories: Set<GHGitRepositoryMapping>
|
||||
get() = knownRepositoriesState.value
|
||||
|
||||
init {
|
||||
val accountsServersFlow = createAccountsServersFlow().also {
|
||||
checkServerVersions(it)
|
||||
}
|
||||
|
||||
val unitTestMode = ApplicationManager.getApplication().isUnitTestMode
|
||||
if (unitTestMode) {
|
||||
checkServerVersions(accountsServersFlow)
|
||||
}
|
||||
|
||||
val remotesFlow = createRemotesFlow()
|
||||
val discoveredServersState = if (unitTestMode) {
|
||||
MutableStateFlow(emptySet())
|
||||
}
|
||||
else {
|
||||
startServerDiscovery(accountsServersFlow, remotesFlow)
|
||||
}
|
||||
|
||||
val knownServersFlow = combineState(scope, accountsServersFlow, discoveredServersState, ::collectServers)
|
||||
|
||||
knownRepositoriesState = combineState(scope, knownServersFlow, remotesFlow, ::collectMappings)
|
||||
}
|
||||
|
||||
private fun createAccountsServersFlow(): StateFlow<Set<GithubServerPath>> {
|
||||
val flow = MutableStateFlow<Set<GithubServerPath>>(emptySet())
|
||||
accountManager.addListener(this, object : AccountsListener<GithubAccount> {
|
||||
override fun onAccountListChanged(old: Collection<GithubAccount>, new: Collection<GithubAccount>) {
|
||||
flow.value = new.map { it.server }.toSet()
|
||||
}
|
||||
})
|
||||
flow.value = accountManager.accounts.map { it.server }.toSet()
|
||||
return flow
|
||||
}
|
||||
|
||||
private fun createRemotesFlow(): StateFlow<Set<GitRemoteUrlCoordinates>> {
|
||||
val flow = MutableStateFlow(collectRemotes())
|
||||
project.messageBus.connect(this).subscribe(VcsRepositoryManager.VCS_REPOSITORY_MAPPING_UPDATED, VcsRepositoryMappingListener {
|
||||
flow.update { collectRemotes() }
|
||||
})
|
||||
project.messageBus.connect(this).subscribe(GitRepository.GIT_REPO_CHANGE, GitRepositoryChangeListener {
|
||||
flow.update { collectRemotes() }
|
||||
})
|
||||
// update after initial bc can happen outside EDT
|
||||
flow.update { collectRemotes() }
|
||||
return flow
|
||||
}
|
||||
|
||||
private fun collectRemotes(): Set<GitRemoteUrlCoordinates> {
|
||||
val gitRepositories = repositoryManager.repositories
|
||||
if (gitRepositories.isEmpty()) {
|
||||
LOG.debug("No repositories found")
|
||||
return emptySet()
|
||||
}
|
||||
|
||||
return gitRepositories.flatMap { repo ->
|
||||
repo.remotes.flatMap { remote ->
|
||||
remote.urls.mapNotNull { url ->
|
||||
GitRemoteUrlCoordinates(url, remote, repo)
|
||||
}
|
||||
}
|
||||
}.toSet()
|
||||
}
|
||||
|
||||
private fun startServerDiscovery(accountsServersFlow: StateFlow<Set<GithubServerPath>>,
|
||||
remotesFlow: StateFlow<Collection<GitRemoteUrlCoordinates>>): StateFlow<Set<GithubServerPath>> {
|
||||
val stateFlow = MutableStateFlow<Set<GithubServerPath>>(emptySet())
|
||||
|
||||
scope.launch {
|
||||
combine(accountsServersFlow, remotesFlow) { servers, remotes ->
|
||||
remotes.filter { remote -> servers.none { it.matches(remote.url) } }
|
||||
}.collect { remotes ->
|
||||
remotes.chunked(URLS_CHECK_PARALLELISM).forEach { remotesChunk ->
|
||||
remotesChunk.map { remote ->
|
||||
async {
|
||||
val server = checkForEnterpriseServer(remote)
|
||||
if (server != null) stateFlow.update { it + server }
|
||||
}
|
||||
}.awaitAll()
|
||||
}
|
||||
}
|
||||
}
|
||||
return stateFlow
|
||||
}
|
||||
|
||||
private fun collectServers(accountsServers: Set<GithubServerPath>, discoveredServers: Set<GithubServerPath>): Set<GithubServerPath> {
|
||||
val servers = mutableSetOf<GithubServerPath>(GithubServerPath.DEFAULT_SERVER)
|
||||
servers.addAll(accountsServers)
|
||||
servers.addAll(discoveredServers)
|
||||
return servers
|
||||
}
|
||||
|
||||
private fun collectMappings(servers: Set<GithubServerPath>, remotes: Collection<GitRemoteUrlCoordinates>): Set<GHGitRepositoryMapping> {
|
||||
val mappings = HashSet<GHGitRepositoryMapping>()
|
||||
for (remote in remotes) {
|
||||
val repository = servers.find { it.matches(remote.url) }?.let { GHGitRepositoryMapping.create(it, remote) }
|
||||
if (repository != null) {
|
||||
mappings.add(repository)
|
||||
}
|
||||
}
|
||||
LOG.debug("New list of known repos: $mappings")
|
||||
return mappings
|
||||
}
|
||||
|
||||
fun findKnownRepositories(repository: GitRepository): List<GHGitRepositoryMapping> {
|
||||
return knownRepositoriesState.value.filter {
|
||||
it.gitRemoteUrlCoordinates.repository == repository
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun checkForEnterpriseServer(remote: GitRemoteUrlCoordinates): GithubServerPath? {
|
||||
val uri = GitHostingUrlUtil.getUriFromRemoteUrl(remote.url)
|
||||
LOG.debug("Extracted URI $uri from remote ${remote.url}")
|
||||
if (uri == null) return null
|
||||
|
||||
val host = uri.host ?: return null
|
||||
val path = uri.path ?: return null
|
||||
val pathParts = path.removePrefix("/").split('/').takeIf { it.size >= 2 } ?: return null
|
||||
val serverSuffix = if (pathParts.size == 2) null else pathParts.subList(0, pathParts.size - 2).joinToString("/", "/")
|
||||
|
||||
for (server in listOf(
|
||||
GithubServerPath(false, host, null, serverSuffix),
|
||||
GithubServerPath(true, host, null, serverSuffix),
|
||||
GithubServerPath(true, host, 8080, serverSuffix)
|
||||
)) {
|
||||
LOG.debug("Looking for GHE server at $server")
|
||||
try {
|
||||
metadataLoader.loadMetadata(server).await()
|
||||
LOG.debug("Found GHE server at $server")
|
||||
return server
|
||||
}
|
||||
catch (ignored: Throwable) {
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun checkServerVersions(serversFlow: Flow<Set<GithubServerPath>>) {
|
||||
scope.launch {
|
||||
serversFlow.collectLatest { servers ->
|
||||
for (server in servers) {
|
||||
if (server.isGithubDotCom) {
|
||||
continue
|
||||
}
|
||||
|
||||
try {
|
||||
val metadata = metadataLoader.loadMetadata(server).await()
|
||||
GHPRStatisticsCollector.logEnterpriseServerMeta(project, server, metadata)
|
||||
}
|
||||
catch (ignore: Exception) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val LOG = logger<GHHostedRepositoriesManager>()
|
||||
|
||||
private const val URLS_CHECK_PARALLELISM = 10
|
||||
}
|
||||
}
|
||||
+18
-168
@@ -1,198 +1,48 @@
|
||||
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.plugins.github.util
|
||||
|
||||
import com.intellij.collaboration.async.CompletableFutureUtil.errorOnEdt
|
||||
import com.intellij.collaboration.async.CompletableFutureUtil.successOnEdt
|
||||
import com.intellij.collaboration.auth.AccountsListener
|
||||
import com.intellij.collaboration.hosting.GitHostingUrlUtil
|
||||
import com.intellij.dvcs.repo.VcsRepositoryMappingListener
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.application.invokeLater
|
||||
import com.intellij.openapi.application.runInEdt
|
||||
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.util.SingleAlarm
|
||||
import com.intellij.util.concurrency.annotations.RequiresEdt
|
||||
import com.intellij.util.messages.Topic
|
||||
import git4idea.repo.GitRepository
|
||||
import git4idea.repo.GitRepositoryChangeListener
|
||||
import git4idea.repo.GitRepositoryManager
|
||||
import org.jetbrains.annotations.CalledInAny
|
||||
import org.jetbrains.plugins.github.api.GithubServerPath
|
||||
import org.jetbrains.plugins.github.authentication.accounts.GHAccountManager
|
||||
import org.jetbrains.plugins.github.authentication.accounts.GithubAccount
|
||||
import org.jetbrains.plugins.github.pullrequest.GHPRStatisticsCollector
|
||||
import kotlin.properties.Delegates.observable
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Deprecated("Use GHHostedRepositoriesManager")
|
||||
@Service
|
||||
class GHProjectRepositoriesManager(private val project: Project) : Disposable {
|
||||
private val updateAlarm = SingleAlarm(task = ::doUpdateRepositories, delay = 50, parentDisposable = this)
|
||||
|
||||
private val accountManager: GHAccountManager
|
||||
get() = service()
|
||||
private val scope = CoroutineScope(SupervisorJob())
|
||||
private val repositoriesManager: GHHostedRepositoriesManager get() = project.service()
|
||||
|
||||
var knownRepositories by observable(emptySet<GHGitRepositoryMapping>()) { _, oldValue, newValue ->
|
||||
if (oldValue != newValue) {
|
||||
ApplicationManager.getApplication().messageBus.syncPublisher(LIST_CHANGES_TOPIC).repositoryListChanged(newValue, project)
|
||||
}
|
||||
}
|
||||
private set
|
||||
|
||||
private val serversFromDiscovery = HashSet<GithubServerPath>()
|
||||
val knownRepositories: Set<GHGitRepositoryMapping>
|
||||
get() = repositoriesManager.knownRepositoriesState.value
|
||||
|
||||
init {
|
||||
accountManager.addListener(this, object : AccountsListener<GithubAccount> {
|
||||
override fun onAccountListChanged(old: Collection<GithubAccount>, new: Collection<GithubAccount>) = runInEdt {
|
||||
updateRepositories()
|
||||
}
|
||||
})
|
||||
updateRepositories()
|
||||
}
|
||||
|
||||
fun findKnownRepositories(repository: GitRepository): List<GHGitRepositoryMapping> {
|
||||
return knownRepositories.filter {
|
||||
it.gitRemoteUrlCoordinates.repository == repository
|
||||
}
|
||||
}
|
||||
|
||||
@CalledInAny
|
||||
private fun updateRepositories() {
|
||||
if (ApplicationManager.getApplication().isUnitTestMode) {
|
||||
doUpdateRepositories()
|
||||
}
|
||||
else {
|
||||
updateAlarm.request()
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: execute on pooled thread - need to make GithubAccountManager ready
|
||||
@RequiresEdt
|
||||
private fun doUpdateRepositories() {
|
||||
LOG.debug("Repository list update started")
|
||||
val gitRepositories = project.service<GitRepositoryManager>().repositories
|
||||
if (gitRepositories.isEmpty()) {
|
||||
knownRepositories = emptySet()
|
||||
LOG.debug("No repositories found")
|
||||
return
|
||||
}
|
||||
|
||||
val remotes = gitRepositories.flatMap { repo ->
|
||||
repo.remotes.flatMap { remote ->
|
||||
remote.urls.mapNotNull { url ->
|
||||
GitRemoteUrlCoordinates(url, remote, repo)
|
||||
}
|
||||
scope.launch {
|
||||
repositoriesManager.knownRepositoriesState.collect {
|
||||
ApplicationManager.getApplication().messageBus.syncPublisher(LIST_CHANGES_TOPIC).repositoryListChanged(it, project)
|
||||
}
|
||||
}
|
||||
LOG.debug("Found remotes: $remotes")
|
||||
|
||||
val authenticatedServers = accountManager.accounts.map { it.server }
|
||||
val servers = mutableListOf<GithubServerPath>().apply {
|
||||
add(GithubServerPath.DEFAULT_SERVER)
|
||||
addAll(authenticatedServers)
|
||||
addAll(serversFromDiscovery)
|
||||
}
|
||||
|
||||
val repositories = HashSet<GHGitRepositoryMapping>()
|
||||
for (remote in remotes) {
|
||||
val repository = servers.find { it.matches(remote.url) }?.let { GHGitRepositoryMapping.create(it, remote) }
|
||||
if (repository != null) {
|
||||
repositories.add(repository)
|
||||
}
|
||||
else {
|
||||
scheduleEnterpriseServerDiscovery(remote)
|
||||
}
|
||||
}
|
||||
LOG.debug("New list of known repos: $repositories")
|
||||
knownRepositories = repositories
|
||||
|
||||
for (server in authenticatedServers) {
|
||||
if (server.isGithubDotCom) {
|
||||
continue
|
||||
}
|
||||
service<GHEnterpriseServerMetadataLoader>().loadMetadata(server).successOnEdt {
|
||||
GHPRStatisticsCollector.logEnterpriseServerMeta(project, server, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresEdt
|
||||
private fun scheduleEnterpriseServerDiscovery(remote: GitRemoteUrlCoordinates) {
|
||||
val uri = GitHostingUrlUtil.getUriFromRemoteUrl(remote.url)
|
||||
LOG.debug("Extracted URI $uri from remote ${remote.url}")
|
||||
if (uri == null) return
|
||||
|
||||
val host = uri.host ?: return
|
||||
val path = uri.path ?: return
|
||||
val pathParts = path.removePrefix("/").split('/').takeIf { it.size >= 2 } ?: return
|
||||
val serverSuffix = if (pathParts.size == 2) null else pathParts.subList(0, pathParts.size - 2).joinToString("/", "/")
|
||||
|
||||
val server = GithubServerPath(false, host, null, serverSuffix)
|
||||
val serverHttp = GithubServerPath(true, host, null, serverSuffix)
|
||||
val server8080 = GithubServerPath(true, host, 8080, serverSuffix)
|
||||
LOG.debug("Scheduling GHE server discovery for $server, $serverHttp and $server8080")
|
||||
|
||||
val serverManager = service<GHEnterpriseServerMetadataLoader>()
|
||||
serverManager.loadMetadata(server).successOnEdt {
|
||||
LOG.debug("Found GHE server at $server")
|
||||
serversFromDiscovery.add(server)
|
||||
invokeLater(runnable = ::doUpdateRepositories)
|
||||
}.errorOnEdt {
|
||||
serverManager.loadMetadata(serverHttp).successOnEdt {
|
||||
LOG.debug("Found GHE server at $serverHttp")
|
||||
serversFromDiscovery.add(serverHttp)
|
||||
invokeLater(runnable = ::doUpdateRepositories)
|
||||
}.errorOnEdt {
|
||||
serverManager.loadMetadata(server8080).successOnEdt {
|
||||
LOG.debug("Found GHE server at $server8080")
|
||||
serversFromDiscovery.add(server8080)
|
||||
invokeLater(runnable = ::doUpdateRepositories)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun addRepositoryListChangedListener(disposable: Disposable, listener: () -> Unit) {
|
||||
ApplicationManager.getApplication().messageBus.connect(disposable).subscribe(LIST_CHANGES_TOPIC, object : ListChangeListener {
|
||||
override fun repositoryListChanged(newList: Set<GHGitRepositoryMapping>, project: Project) = listener()
|
||||
})
|
||||
}
|
||||
|
||||
internal class RemoteUrlsListener(private val project: Project) : VcsRepositoryMappingListener, GitRepositoryChangeListener {
|
||||
override fun mappingChanged() = runInEdt(project) { updateRepositories(project) }
|
||||
override fun repositoryChanged(repository: GitRepository) = runInEdt(project) { updateRepositories(project) }
|
||||
}
|
||||
|
||||
interface ListChangeListener {
|
||||
fun repositoryListChanged(newList: Set<GHGitRepositoryMapping>, project: Project)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val LOG = logger<GHProjectRepositoriesManager>()
|
||||
override fun dispose() {
|
||||
scope.cancel()
|
||||
}
|
||||
|
||||
@Deprecated("Use GHHostedRepositoriesManager", level = DeprecationLevel.ERROR)
|
||||
companion object {
|
||||
@Deprecated("Use GHHostedRepositoriesManager.knownRepositoriesState")
|
||||
@JvmField
|
||||
@Topic.AppLevel
|
||||
val LIST_CHANGES_TOPIC = Topic(ListChangeListener::class.java, Topic.BroadcastDirection.NONE)
|
||||
|
||||
private inline fun runInEdt(project: Project, crossinline runnable: () -> Unit) {
|
||||
val application = ApplicationManager.getApplication()
|
||||
if (application.isDispatchThread) runnable()
|
||||
else application.invokeLater({ runnable() }) { project.isDisposed }
|
||||
}
|
||||
|
||||
private fun updateRepositories(project: Project) {
|
||||
try {
|
||||
if (!project.isDisposed) {
|
||||
project.service<GHProjectRepositoriesManager>().updateRepositories()
|
||||
}
|
||||
}
|
||||
catch (e: Exception) {
|
||||
LOG.info("Error occurred while updating repositories", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun dispose() {}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ import git4idea.config.GitConfigUtil
|
||||
import git4idea.repo.GitRepository
|
||||
import git4idea.test.GitHttpAuthTestService
|
||||
import git4idea.test.git
|
||||
import org.jetbrains.plugins.github.util.GHProjectRepositoriesManager
|
||||
import org.jetbrains.plugins.github.util.GHHostedRepositoriesManager
|
||||
import org.jetbrains.plugins.github.util.GithubUtil
|
||||
|
||||
abstract class GithubGitRepoTest : GithubTest() {
|
||||
@@ -60,7 +60,8 @@ abstract class GithubGitRepoTest : GithubTest() {
|
||||
|
||||
protected fun checkRemoteConfigured() {
|
||||
assertNotNull(repository)
|
||||
assertTrue("GitHub remote is not configured", project.service<GHProjectRepositoriesManager>().knownRepositories.any {
|
||||
val mappings = project.service<GHHostedRepositoriesManager>().knownRepositories
|
||||
assertTrue("GitHub remote is not configured, current mappings: $mappings", mappings.any {
|
||||
it.gitRemoteUrlCoordinates.repository == repository
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user