mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[space plugin] simplify space project context
GitOrigin-RevId: c541706891543f2775c26e16d6c9366109b9d3d6
This commit is contained in:
committed by
intellij-monorepo-bot
parent
5b63042f7f
commit
76c51918c3
@@ -12,7 +12,6 @@ import circlet.ui.AccountsMenuListPopup
|
||||
import circlet.vcs.*
|
||||
import circlet.vcs.clone.*
|
||||
import circlet.workspaces.*
|
||||
import com.intellij.ide.*
|
||||
import com.intellij.openapi.actionSystem.*
|
||||
import com.intellij.openapi.project.*
|
||||
import com.intellij.openapi.ui.popup.*
|
||||
@@ -133,30 +132,28 @@ class CircletMainToolBarAction : DumbAwareAction() {
|
||||
{ CircletCloneAction.runClone(project) },
|
||||
showSeparatorAbove = true)
|
||||
val projectContext = CircletProjectContext.getInstance(project)
|
||||
val projectInfos = projectContext.projectDescriptions
|
||||
|
||||
if (projectInfos != null) {
|
||||
val descriptions = projectInfos.second
|
||||
val context = projectContext.context.value
|
||||
if (!context.empty) {
|
||||
val descriptions = context.reposInProject.keys
|
||||
if (descriptions.size > 1) {
|
||||
menuItems += AccountMenuItem.Group("Code Reviews", descriptions.map {
|
||||
val reviewsUrl = Navigator.p.project(it.projectKey).reviews.absoluteHref(host)
|
||||
browseAction("Open for ${it.project.name} project",reviewsUrl)
|
||||
val reviewsUrl = Navigator.p.project(it.key).reviews.absoluteHref(host)
|
||||
browseAction("Open for ${it.project.name} project", reviewsUrl)
|
||||
}.toList())
|
||||
|
||||
menuItems += AccountMenuItem.Group("Checklists", descriptions.map {
|
||||
val checklistsUrl = Navigator.p.project(it.projectKey).checklists().absoluteHref(host)
|
||||
browseAction("Open for ${it.project.name} project",checklistsUrl)
|
||||
val checklistsUrl = Navigator.p.project(it.key).checklists().absoluteHref(host)
|
||||
browseAction("Open for ${it.project.name} project", checklistsUrl)
|
||||
}.toList())
|
||||
|
||||
menuItems += AccountMenuItem.Group("Issues", descriptions.map {
|
||||
val issuesUrl = Navigator.p.project(it.projectKey).issues().absoluteHref(host)
|
||||
browseAction("Open for ${it.project.name} project",issuesUrl)
|
||||
val issuesUrl = Navigator.p.project(it.key).issues().absoluteHref(host)
|
||||
browseAction("Open for ${it.project.name} project", issuesUrl)
|
||||
}.toList())
|
||||
}
|
||||
else {
|
||||
val p = Navigator.p.project(descriptions.first().projectKey)
|
||||
} else {
|
||||
val p = Navigator.p.project(descriptions.first().key)
|
||||
|
||||
menuItems += browseAction("Code Reviews",p.reviews.absoluteHref(host))
|
||||
menuItems += browseAction("Code Reviews", p.reviews.absoluteHref(host))
|
||||
menuItems += browseAction("Checklists", p.checklists().absoluteHref(host))
|
||||
menuItems += browseAction("Issues", p.issues().absoluteHref(host))
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ import git4idea.GitUtil
|
||||
import git4idea.history.GitHistoryUtils
|
||||
import git4idea.repo.GitRepository
|
||||
import icons.SpaceIcons
|
||||
import runtime.routing.Location
|
||||
import runtime.routing.*
|
||||
import com.intellij.openapi.util.Ref as Ref1
|
||||
|
||||
abstract class CircletOpenInBrowserActionGroup<T>(groupName: String) :
|
||||
@@ -57,15 +57,15 @@ abstract class CircletOpenInBrowserActionGroup<T>(groupName: String) :
|
||||
}
|
||||
|
||||
abstract class CircletOpenInBrowserAction(groupName: String) :
|
||||
CircletOpenInBrowserActionGroup<Pair<CircletProjectDescription, String>>(groupName) {
|
||||
CircletOpenInBrowserActionGroup<Pair<CircletProjectInfo, String>>(groupName) {
|
||||
|
||||
override fun update(e: AnActionEvent) {
|
||||
CircletActionUtils.showIconInActionSearch(e)
|
||||
val project = e.project
|
||||
if (project != null) {
|
||||
val projectContext = CircletProjectContext.getInstance(project)
|
||||
val projectDescriptions = projectContext.projectDescriptions
|
||||
if (projectDescriptions != null) {
|
||||
val projectDescriptions = !projectContext.context.value.empty
|
||||
if (projectDescriptions) {
|
||||
e.presentation.isEnabled = true
|
||||
|
||||
return
|
||||
@@ -74,19 +74,19 @@ abstract class CircletOpenInBrowserAction(groupName: String) :
|
||||
e.presentation.isEnabled = false
|
||||
}
|
||||
|
||||
override fun buildAction(it: Pair<CircletProjectDescription, String>): AnAction =
|
||||
object : AnAction("Open for ${it.first.projectKey.key} project") {
|
||||
override fun buildAction(it: Pair<CircletProjectInfo, String>): AnAction =
|
||||
object : AnAction("Open for ${it.first.key.key} project") {
|
||||
override fun actionPerformed(e: AnActionEvent) = BrowserUtil.browse(it.second)
|
||||
}
|
||||
|
||||
companion object {
|
||||
internal fun getProjectAwareUrls(endpoint: (ProjectLocation) -> Location, context: DataContext): List<Pair<CircletProjectDescription, String>>? {
|
||||
internal fun getProjectAwareUrls(endpoint: (ProjectLocation) -> Location, context: DataContext): List<Pair<CircletProjectInfo, String>>? {
|
||||
val project = context.getData(CommonDataKeys.PROJECT) ?: return null
|
||||
val server = circletWorkspace.workspace.value?.client?.server?.removeSuffix("/") ?: return null
|
||||
val description = CircletProjectContext.getInstance(project).projectDescriptions ?: return null
|
||||
val description = CircletProjectContext.getInstance(project).context.value
|
||||
|
||||
return description.second.map {
|
||||
val projectLocation = Navigator.p.project(it.projectKey)
|
||||
return description.reposInProject.keys.map {
|
||||
val projectLocation = Navigator.p.project(it.key)
|
||||
val url = endpoint(projectLocation).absoluteHref(server)
|
||||
|
||||
it to url
|
||||
@@ -96,19 +96,19 @@ abstract class CircletOpenInBrowserAction(groupName: String) :
|
||||
}
|
||||
|
||||
class OpenReviews : CircletOpenInBrowserAction("Code reviews") {
|
||||
override fun getData(dataContext: DataContext): List<Pair<CircletProjectDescription, String>>? {
|
||||
override fun getData(dataContext: DataContext): List<Pair<CircletProjectInfo, String>>? {
|
||||
return getProjectAwareUrls(ProjectLocation::reviews, dataContext)
|
||||
}
|
||||
}
|
||||
|
||||
class OpenChecklists : CircletOpenInBrowserAction("Checklists") {
|
||||
override fun getData(dataContext: DataContext): List<Pair<CircletProjectDescription, String>>? {
|
||||
override fun getData(dataContext: DataContext): List<Pair<CircletProjectInfo, String>>? {
|
||||
return getProjectAwareUrls(ProjectLocation::checklists, dataContext)
|
||||
}
|
||||
}
|
||||
|
||||
class OpenIssues : CircletOpenInBrowserAction("Issues") {
|
||||
override fun getData(dataContext: DataContext): List<Pair<CircletProjectDescription, String>>? {
|
||||
override fun getData(dataContext: DataContext): List<Pair<CircletProjectInfo, String>>? {
|
||||
return getProjectAwareUrls(ProjectLocation::issues, dataContext)
|
||||
}
|
||||
}
|
||||
@@ -143,9 +143,9 @@ class CircletVcsOpenInBrowserActionGroup :
|
||||
val change = changeListManager.getChange(virtualFile)
|
||||
if (change != null && change.type == Change.Type.NEW) return null
|
||||
|
||||
val repoKeysInfo = findProjectInfo(gitRepository, project) ?: return null
|
||||
val repoDescription = findProjectInfo(gitRepository, project) ?: return null
|
||||
|
||||
return repoKeysInfo.second.map { OpenData.File(project, server, it, repoKeysInfo.first, virtualFile, gitRepository) }
|
||||
return repoDescription.projectInfos.map { OpenData.File(project, server, it, repoDescription.name, virtualFile, gitRepository) }
|
||||
|
||||
}
|
||||
|
||||
@@ -154,26 +154,26 @@ class CircletVcsOpenInBrowserActionGroup :
|
||||
val fileRevision = dataContext.getData(VcsDataKeys.VCS_FILE_REVISION) ?: return null
|
||||
if (fileRevision !is VcsFileRevisionEx) return null
|
||||
val gitRepository = GitUtil.getRepositoryManager(project).getRepositoryForFileQuick(filePath) ?: return null
|
||||
val repoKeysInfo = findProjectInfo(gitRepository, project) ?: return null
|
||||
val repoDescription = findProjectInfo(gitRepository, project) ?: return null
|
||||
|
||||
return repoKeysInfo.second.map { OpenData.FileRevision(project, server, it, repoKeysInfo.first, fileRevision) }
|
||||
return repoDescription.projectInfos.map { OpenData.FileRevision(project, server, it, repoDescription.name, fileRevision) }
|
||||
}
|
||||
|
||||
private fun getDataFromLog(dataContext: DataContext, project: Project, server: String): List<OpenData>? {
|
||||
val vcsLog = dataContext.getData(VcsLogDataKeys.VCS_LOG) ?: return null
|
||||
val selectedCommit = vcsLog.selectedCommits.firstOrNull() ?: return null
|
||||
val gitRepository = GitUtil.getRepositoryManager(project).getRepositoryForFileQuick(selectedCommit.root) ?: return null
|
||||
val repoKeysInfo = findProjectInfo(gitRepository, project) ?: return null
|
||||
val repoDescription = findProjectInfo(gitRepository, project) ?: return null
|
||||
|
||||
return repoKeysInfo.second.map { OpenData.Commit(project, server, it, repoKeysInfo.first, selectedCommit) }
|
||||
return repoDescription.projectInfos.map { OpenData.Commit(project, server, it, repoDescription.name, selectedCommit) }
|
||||
}
|
||||
|
||||
private fun findProjectInfo(gitRepository: GitRepository, project: Project): Pair<String, List<CircletProjectDescription>>? {
|
||||
private fun findProjectInfo(gitRepository: GitRepository, project: Project): CircletRepoInfo? {
|
||||
val circletContext = CircletProjectContext.getInstance(project)
|
||||
|
||||
return getRemoteUrls(gitRepository)
|
||||
.mapNotNull { circletContext.findProjectInfo(it) }
|
||||
.firstOrNull() ?: return null
|
||||
.mapNotNull { circletContext.getRepoDescriptionByUrl(it) }
|
||||
.firstOrNull()
|
||||
}
|
||||
|
||||
private fun getRemoteUrls(gitRepository: GitRepository): List<String> {
|
||||
@@ -181,7 +181,7 @@ class CircletVcsOpenInBrowserActionGroup :
|
||||
}
|
||||
|
||||
companion object {
|
||||
class OpenAction(private val data: OpenData) : DumbAwareAction("Open for ${data.description.project.name} project") {
|
||||
class OpenAction(private val data: OpenData) : DumbAwareAction("Open for ${data.info.project.name} project") {
|
||||
|
||||
override fun actionPerformed(e: AnActionEvent) {
|
||||
data.url?.let { BrowserUtil.browse(it) }
|
||||
@@ -190,20 +190,20 @@ class CircletVcsOpenInBrowserActionGroup :
|
||||
|
||||
sealed class OpenData(val project: Project,
|
||||
val server: String,
|
||||
val description: CircletProjectDescription,
|
||||
val info: CircletProjectInfo,
|
||||
val repo: String
|
||||
) {
|
||||
abstract val url: String?
|
||||
|
||||
class Commit(project: Project,
|
||||
server: String,
|
||||
projectKey: CircletProjectDescription,
|
||||
projectKey: CircletProjectInfo,
|
||||
repo: String,
|
||||
private val commit: CommitId) : OpenData(project, server, projectKey, repo) {
|
||||
|
||||
override val url: String?
|
||||
get() {
|
||||
return Navigator.p.project(description.projectKey)
|
||||
return Navigator.p.project(info.key)
|
||||
.commits(repo, "", commit.hash.asString())
|
||||
.absoluteHref(server)
|
||||
}
|
||||
@@ -211,13 +211,13 @@ class CircletVcsOpenInBrowserActionGroup :
|
||||
|
||||
class FileRevision(project: Project,
|
||||
server: String,
|
||||
projectKey: CircletProjectDescription,
|
||||
projectKey: CircletProjectInfo,
|
||||
repo: String,
|
||||
private val vcsFileRevisionEx: VcsFileRevisionEx) : OpenData(project, server, projectKey, repo) {
|
||||
|
||||
override val url: String?
|
||||
get() {
|
||||
return Navigator.p.project(description.projectKey)
|
||||
return Navigator.p.project(info.key)
|
||||
.revision(repo, vcsFileRevisionEx.revisionNumber.asString())
|
||||
.absoluteHref(server)
|
||||
}
|
||||
@@ -225,7 +225,7 @@ class CircletVcsOpenInBrowserActionGroup :
|
||||
|
||||
class File(project: Project,
|
||||
server: String,
|
||||
projectKey: CircletProjectDescription,
|
||||
projectKey: CircletProjectInfo,
|
||||
repo: String,
|
||||
private val virtualFile: VirtualFile, private val gitRepository: GitRepository) : OpenData(project, server, projectKey, repo) {
|
||||
override val url: String?
|
||||
@@ -233,7 +233,7 @@ class CircletVcsOpenInBrowserActionGroup :
|
||||
val relativePath = VfsUtilCore.getRelativePath(virtualFile, gitRepository.root) ?: return null
|
||||
val hash = getCurrentFileRevisionHash(project, virtualFile) ?: return null
|
||||
|
||||
return Navigator.p.project(description.projectKey)
|
||||
return Navigator.p.project(info.key)
|
||||
.fileAnnotate(repo, hash, relativePath)
|
||||
.absoluteHref(server)
|
||||
}
|
||||
|
||||
@@ -1,16 +1,22 @@
|
||||
package circlet.vcs
|
||||
|
||||
import circlet.client.*
|
||||
import circlet.client.api.*
|
||||
import circlet.components.*
|
||||
import circlet.platform.client.*
|
||||
import circlet.workspaces.*
|
||||
import com.intellij.openapi.*
|
||||
import com.intellij.openapi.project.*
|
||||
import git4idea.*
|
||||
import git4idea.repo.*
|
||||
import libraries.coroutines.extra.*
|
||||
import runtime.async.*
|
||||
import circlet.client.api.PR_Project
|
||||
import circlet.client.api.ProjectKey
|
||||
import circlet.client.api.Projects
|
||||
import circlet.client.api.RepositoryService
|
||||
import circlet.client.pr
|
||||
import circlet.client.repoService
|
||||
import circlet.components.circletWorkspace
|
||||
import circlet.platform.client.ConnectionStatus
|
||||
import circlet.platform.client.resolve
|
||||
import circlet.workspaces.Workspace
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.project.Project
|
||||
import git4idea.GitUtil
|
||||
import git4idea.repo.GitRepository
|
||||
import git4idea.repo.GitRepositoryChangeListener
|
||||
import libraries.coroutines.extra.LifetimeSource
|
||||
import runtime.async.backoff
|
||||
import runtime.reactive.*
|
||||
|
||||
class CircletProjectContext(project: Project) : Disposable {
|
||||
@@ -18,15 +24,12 @@ class CircletProjectContext(project: Project) : Disposable {
|
||||
|
||||
private val remoteUrls: MutableProperty<Set<String>> = Property.createMutable(findRemoteUrls(project))
|
||||
|
||||
private val projectsInfo: Property<Map<String, Pair<String, List<CircletProjectDescription>>?>> = lifetime.mapInit(circletWorkspace.workspace, remoteUrls, emptyMap()) { ws, urls ->
|
||||
ws ?: return@mapInit emptyMap<String, Pair<String, List<CircletProjectDescription>>?>()
|
||||
val context: Property<Context> = lifetime.mapInit(circletWorkspace.workspace, remoteUrls, EMPTY) { ws, urls ->
|
||||
ws ?: return@mapInit EMPTY
|
||||
ws.client.connectionStatus.filter { it is ConnectionStatus.Connected }.awaitFirst(ws.lifetime)
|
||||
reloadProjectKeys(ws, urls)
|
||||
}
|
||||
|
||||
val projectDescriptions: Pair<String, List<CircletProjectDescription>>?
|
||||
get() = projectsInfo.value.values.filterNotNull().firstOrNull()
|
||||
|
||||
init {
|
||||
project.messageBus
|
||||
.connect(this)
|
||||
@@ -36,8 +39,8 @@ class CircletProjectContext(project: Project) : Disposable {
|
||||
})
|
||||
}
|
||||
|
||||
fun findProjectInfo(remoteUrl: String): Pair<String, List<CircletProjectDescription>>? {
|
||||
return projectsInfo.value[remoteUrl]
|
||||
fun getRepoDescriptionByUrl(remoteUrl: String): CircletRepoInfo? {
|
||||
return context.value.repoByUrl[remoteUrl]
|
||||
}
|
||||
|
||||
private fun findRemoteUrls(project: Project): Set<String> = GitUtil.getRepositoryManager(project).repositories
|
||||
@@ -45,20 +48,33 @@ class CircletProjectContext(project: Project) : Disposable {
|
||||
.flatMap { it.urls }
|
||||
.toSet<String>()
|
||||
|
||||
private suspend fun reloadProjectKeys(ws: Workspace, urls: Set<String>): Map<String, Pair<String, List<CircletProjectDescription>>?> {
|
||||
return urls.map { url ->
|
||||
private suspend fun reloadProjectKeys(ws: Workspace, urls: Set<String>): Context {
|
||||
val reposByUrl: Map<String, CircletRepoInfo?> = urls.map { url ->
|
||||
backoff {
|
||||
url to loadProjectKeysForUrl(ws, url)
|
||||
}
|
||||
}.toMap()
|
||||
val reposInProject = HashMap<CircletProjectInfo, MutableSet<CircletRepoInfo>>()
|
||||
|
||||
for (repoDescription in reposByUrl.values.filterNotNull()) {
|
||||
for (projectDescription in repoDescription.projectInfos) {
|
||||
reposInProject.getOrPut(projectDescription, { HashSet() })
|
||||
.add(repoDescription)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return Context(reposByUrl, reposInProject)
|
||||
}
|
||||
|
||||
private suspend fun loadProjectKeysForUrl(ws: Workspace, url: String): Pair<String, List<CircletProjectDescription>>? {
|
||||
private suspend fun loadProjectKeysForUrl(ws: Workspace, url: String): CircletRepoInfo? {
|
||||
val repoService: RepositoryService = ws.client.repoService
|
||||
val repoProjectKeys = repoService.findByRepositoryUrl(url) ?: return null
|
||||
val (repoName, projectKeys) = repoService.findByRepositoryUrl(url) ?: return null
|
||||
val projectService: Projects = ws.client.pr
|
||||
val list = repoProjectKeys.second.map { CircletProjectDescription(it, projectService.getProjectByKey(it).resolve()) }.toList()
|
||||
return repoProjectKeys.first to list
|
||||
val projectDescriptions = projectKeys.map { CircletProjectInfo(it, projectService.getProjectByKey(it).resolve()) }.toSet()
|
||||
return CircletRepoInfo(url,
|
||||
repoName,
|
||||
projectDescriptions)
|
||||
}
|
||||
|
||||
override fun dispose() {
|
||||
@@ -71,3 +87,27 @@ class CircletProjectContext(project: Project) : Disposable {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class Context(
|
||||
val repoByUrl: Map<String, CircletRepoInfo?>,
|
||||
|
||||
val reposInProject: Map<CircletProjectInfo, Set<CircletRepoInfo>>
|
||||
) {
|
||||
val empty: Boolean
|
||||
get() {
|
||||
return this == EMPTY
|
||||
}
|
||||
}
|
||||
|
||||
private val EMPTY: Context = Context(emptyMap(), emptyMap())
|
||||
|
||||
data class CircletRepoInfo(
|
||||
val url: String,
|
||||
val name: String,
|
||||
val projectInfos: Set<CircletProjectInfo>
|
||||
)
|
||||
|
||||
data class CircletProjectInfo(
|
||||
val key: ProjectKey,
|
||||
val project: PR_Project
|
||||
)
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
package circlet.vcs
|
||||
|
||||
import circlet.client.api.*
|
||||
|
||||
data class CircletProjectDescription(
|
||||
val projectKey: ProjectKey,
|
||||
val project: PR_Project
|
||||
)
|
||||
@@ -44,8 +44,8 @@ class CircletShareProjectAction : DumbAwareAction() {
|
||||
}
|
||||
|
||||
val context = CircletProjectContext.getInstance(project)
|
||||
val descriptions = context.projectDescriptions
|
||||
if (descriptions != null && descriptions.second.isNotEmpty()) {
|
||||
val descriptions = context.context.value.empty
|
||||
if (descriptions) {
|
||||
e.presentation.isEnabledAndVisible = false
|
||||
return
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user