[github] Pull request merge actions

This commit is contained in:
Ivan Semenov
2018-10-02 18:45:39 +03:00
parent a3d4eddacf
commit 9aa6f3e3a2
19 changed files with 442 additions and 14 deletions
@@ -175,14 +175,12 @@ public final class HttpRequests {
@NotNull
public static RequestBuilder post(@NotNull String url, @Nullable String contentType) {
return new RequestBuilderImpl(url, rawConnection -> {
HttpURLConnection connection = (HttpURLConnection)rawConnection;
connection.setRequestMethod("POST");
connection.setDoOutput(true);
if (contentType != null) {
connection.setRequestProperty("Content-Type", contentType);
}
});
return requestWithBody(url, "POST", contentType, null);
}
@NotNull
public static RequestBuilder put(@NotNull String url, @Nullable String contentType) {
return requestWithBody(url, "PUT", contentType, null);
}
/**
@@ -193,14 +191,23 @@ public final class HttpRequests {
*/
@NotNull
public static RequestBuilder patch(@NotNull String url, @Nullable String contentType) {
return requestWithBody(url, "POST", contentType,
connection -> connection.setRequestProperty("X-HTTP-Method-Override", "PATCH"));
}
@NotNull
private static RequestBuilder requestWithBody(@NotNull String url,
@NotNull String requestMethod,
@Nullable String contentType,
@Nullable ConnectionTuner tuner) {
return new RequestBuilderImpl(url, rawConnection -> {
HttpURLConnection connection = (HttpURLConnection)rawConnection;
connection.setRequestMethod("POST");
connection.setRequestProperty("X-HTTP-Method-Override", "PATCH");
connection.setRequestMethod(requestMethod);
connection.setDoOutput(true);
if (contentType != null) {
connection.setRequestProperty("Content-Type", contentType);
}
if (tuner != null) tuner.tune(connection);
});
}
@@ -592,12 +599,12 @@ public final class HttpRequests {
HttpURLConnection httpURLConnection = (HttpURLConnection)connection;
String method = httpURLConnection.getRequestMethod();
if (method.equals("POST")) {
if (method.equals("POST") || method.equals("PUT")) {
return connection;
}
LOG.assertTrue(method.equals("GET") || method.equals("HEAD") || method.equals("DELETE"),
"'" + method + "' not supported; please use GET, HEAD, DELETE or POST");
"'" + method + "' not supported; please use GET, HEAD, DELETE, PUT or POST");
if (LOG.isDebugEnabled()) LOG.debug("connecting to " + url);
int responseCode = httpURLConnection.getResponseCode();
@@ -75,11 +75,27 @@
<add-to-group group-id="EditorTabPopupMenu"/>
<add-to-group group-id="ConsoleEditorPopupMenu"/>
</action>
<group id="Github.PullRequest.Merge.Group">
<action id="Github.PullRequest.Merge.Commit"
class="org.jetbrains.plugins.github.pullrequest.action.GithubPullRequestMergeCommitAction"/>
<action id="Github.PullRequest.Merge.Squash" class="org.jetbrains.plugins.github.pullrequest.action.GithubPullRequestSquashAction"/>
<action id="Github.PullRequest.Merge.Rebase" class="org.jetbrains.plugins.github.pullrequest.action.GithubPullRequestRebaseAction"/>
</group>
<group id="Github.PullRequest.ToolWindow.List.Toolbar">
<group class="org.jetbrains.plugins.github.pullrequest.action.merge.GithubPullRequestMergeActionGroup"
popup="true"
text="Merge Pull Request"
icon="AllIcons.Vcs.Merge">
<reference id="Github.PullRequest.Merge.Group"/>
</group>
<action id="Github.PullRequest.List.Refresh"
class="org.jetbrains.plugins.github.pullrequest.action.GithubPullRequestRefreshListAction"/>
</group>
<group id="Github.PullRequest.ToolWindow.List.Popup">
<reference id="Github.PullRequest.Merge.Group"/>
<separator/>
<action id="Github.PullRequest.Branch.Create"
class="org.jetbrains.plugins.github.pullrequest.action.GithubPullRequestCreateBranchAction"/>
<reference id="Github.Open.In.Browser"/>
@@ -118,6 +118,24 @@ sealed class GithubApiRequest<T>(val url: String) {
}
}
abstract class Put<T> @JvmOverloads constructor(override val body: String,
override val bodyMimeType: String,
url: String,
override val acceptMimeType: String? = null) : GithubApiRequest.WithBody<T>(url) {
companion object {
inline fun <reified T> json(url: String, body: Any): Put<T> = Json(url, body, T::class.java)
}
open class Json<T>(url: String, body: Any, clazz: Class<T>) : Put<T>(GithubApiContentHelper.toJson(body),
GithubApiContentHelper.JSON_MIME_TYPE,
url,
GithubApiContentHelper.V3_JSON_MIME_TYPE) {
private val typeToken = TypeToken.get(clazz)
override fun extractResult(response: GithubApiResponse): T = parseJsonResponse(response, typeToken)
}
}
abstract class Patch<T> @JvmOverloads constructor(override val body: String,
override val bodyMimeType: String,
url: String,
@@ -142,6 +142,7 @@ sealed class GithubApiRequestExecutor {
return when (request) {
is GithubApiRequest.Get -> HttpRequests.request(request.url)
is GithubApiRequest.Post -> HttpRequests.post(request.url, request.bodyMimeType)
is GithubApiRequest.Put -> HttpRequests.put(request.url, request.bodyMimeType)
is GithubApiRequest.Patch -> HttpRequests.patch(request.url, request.bodyMimeType)
is GithubApiRequest.Head -> HttpRequests.head(request.url)
is GithubApiRequest.Delete -> HttpRequests.delete(request.url)
@@ -163,6 +163,26 @@ object GithubApiRequests {
Post.json<GithubPullRequestDetailed>(getUrl(server, Repos.urlSuffix, "/$username/$repoName", urlSuffix),
GithubPullRequestRequest(title, description, head, base))
.withOperationName("create pull request in $username/$repoName")
@JvmStatic
fun merge(pullRequest: GithubPullRequest, commitSubject: String, commitBody: String, headSha: String) =
Put.json<Unit>(getMergeUrl(pullRequest),
GithubPullRequestMergeRequest(commitSubject, commitBody, headSha, GithubPullRequestMergeMethod.merge))
.withOperationName("merge pull request ${pullRequest.number}")
@JvmStatic
fun squashMerge(pullRequest: GithubPullRequest, commitSubject: String, commitBody: String, headSha: String) =
Put.json<Unit>(getMergeUrl(pullRequest),
GithubPullRequestMergeRequest(commitSubject, commitBody, headSha, GithubPullRequestMergeMethod.squash))
.withOperationName("squash and merge pull request ${pullRequest.number}")
@JvmStatic
fun rebaseMerge(pullRequest: GithubPullRequest, headSha: String) =
Put.json<Unit>(getMergeUrl(pullRequest),
GithubPullRequestMergeRebaseRequest(headSha))
.withOperationName("rebase and merge pull request ${pullRequest.number}")
private fun getMergeUrl(pullRequest: GithubPullRequest) = pullRequest.url + "/merge"
}
}
@@ -46,6 +46,11 @@ public class GithubPullRequest {
@Mandatory private Links _links;
private String authorAssociation;
@NotNull
public String getUrl() {
return url;
}
@NotNull
public String getHtmlUrl() {
return htmlUrl;
@@ -1,12 +1,13 @@
// Copyright 2000-2018 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.api.data;
import org.jetbrains.io.mandatory.Mandatory;
import org.jetbrains.io.mandatory.RestModel;
@RestModel
@SuppressWarnings("UnusedDeclaration")
public class GithubPullRequestDetailed extends GithubPullRequest {
private Boolean merged;
@Mandatory private Boolean merged;
private Boolean mergeable;
private Boolean rebaseable;
private String mergeableState;
@@ -19,4 +20,16 @@ public class GithubPullRequestDetailed extends GithubPullRequest {
private Integer additions;
private Integer deletions;
private Integer changedFiles;
public boolean getMerged() {
return merged;
}
public boolean getMergeable() {
return mergeable != null && mergeable;
}
public boolean getRebaseable() {
return rebaseable != null && rebaseable;
}
}
@@ -0,0 +1,7 @@
// Copyright 2000-2018 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.api.data
@Suppress("EnumEntryName")
enum class GithubPullRequestMergeMethod {
merge, squash, rebase
}
@@ -0,0 +1,16 @@
// Copyright 2000-2018 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.api.requests;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.github.api.data.GithubPullRequestMergeMethod;
@SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
public class GithubPullRequestMergeRebaseRequest {
@NotNull private final String sha;
@NotNull private final GithubPullRequestMergeMethod method;
public GithubPullRequestMergeRebaseRequest(@NotNull String sha) {
this.sha = sha;
this.method = GithubPullRequestMergeMethod.rebase;
}
}
@@ -0,0 +1,27 @@
// Copyright 2000-2018 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.api.requests;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.github.api.data.GithubPullRequestMergeMethod;
@SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
public class GithubPullRequestMergeRequest {
@NotNull private final String commitTitle;
@NotNull private final String commitMessage;
@NotNull private final String sha;
@NotNull private final GithubPullRequestMergeMethod method;
public GithubPullRequestMergeRequest(@NotNull String commitTitle,
@NotNull String commitMessage,
@NotNull String sha,
@NotNull GithubPullRequestMergeMethod method) {
if (method != GithubPullRequestMergeMethod.merge && method != GithubPullRequestMergeMethod.squash) {
throw new IllegalArgumentException("Invalid merge method");
}
this.commitTitle = commitTitle;
this.commitMessage = commitMessage;
this.sha = sha;
this.method = method;
}
}
@@ -48,7 +48,7 @@ class GithubPullRequestsComponentFactory(private val project: Project,
return GithubPullRequestsComponent(requestExecutor, avatarIconsProviderFactory, repository, remote, repoDetails, account)
}
private inner class GithubPullRequestsComponent(requestExecutor: GithubApiRequestExecutor,
private inner class GithubPullRequestsComponent(private val requestExecutor: GithubApiRequestExecutor,
avatarIconsProviderFactory: CachingGithubAvatarIconsProvider.Factory,
private val repository: GitRepository, private val remote: GitRemote,
private val repoDetails: GithubRepoDetailed,
@@ -99,6 +99,7 @@ class GithubPullRequestsComponentFactory(private val project: Project,
GithubPullRequestKeys.REMOTE.`is`(dataId) -> remote
GithubPullRequestKeys.REPO_DETAILS.`is`(dataId) -> repoDetails
GithubPullRequestKeys.SERVER_PATH.`is`(dataId) -> account.server
GithubPullRequestKeys.API_REQUEST_EXECUTOR.`is`(dataId) -> requestExecutor
GithubPullRequestKeys.PULL_REQUESTS_LIST_COMPONENT.`is`(dataId) -> list
GithubPullRequestKeys.SELECTED_PULL_REQUEST_DATA_PROVIDER.`is`(dataId) ->
list.selectionModel.current?.let(dataLoader::getDataProvider)
@@ -4,6 +4,7 @@ package org.jetbrains.plugins.github.pullrequest.action
import com.intellij.openapi.actionSystem.DataKey
import git4idea.repo.GitRemote
import git4idea.repo.GitRepository
import org.jetbrains.plugins.github.api.GithubApiRequestExecutor
import org.jetbrains.plugins.github.api.GithubServerPath
import org.jetbrains.plugins.github.api.data.GithubRepoDetailed
import org.jetbrains.plugins.github.api.data.GithubSearchedIssue
@@ -12,6 +13,9 @@ import org.jetbrains.plugins.github.pullrequest.ui.GithubPullRequestsListCompone
object GithubPullRequestKeys {
@JvmStatic
val API_REQUEST_EXECUTOR =
DataKey.create<GithubApiRequestExecutor>("org.jetbrains.plugins.github.pullrequest.requestexecutor")
@JvmStatic
val PULL_REQUESTS_LIST_COMPONENT =
DataKey.create<GithubPullRequestsListComponent>("org.jetbrains.plugins.github.pullrequest.list.component")
@JvmStatic
@@ -0,0 +1,46 @@
// Copyright 2000-2018 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.action
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.progress.Task
import com.intellij.openapi.project.Project
import org.jetbrains.plugins.github.api.GithubApiRequestExecutor
import org.jetbrains.plugins.github.api.GithubApiRequests
import org.jetbrains.plugins.github.api.data.GithubPullRequestDetailed
import org.jetbrains.plugins.github.pullrequest.action.merge.GithubPullRequestMergeActionBase
import org.jetbrains.plugins.github.pullrequest.action.ui.GithubMergeCommitMessageDialog
import org.jetbrains.plugins.github.util.GithubNotifications
class GithubPullRequestMergeCommitAction : GithubPullRequestMergeActionBase("Create a Merge Commit...") {
override fun canMerge(details: GithubPullRequestDetailed) = details.mergeable
override fun mergePullRequest(e: AnActionEvent,
project: Project, requestExecutor: GithubApiRequestExecutor, details: GithubPullRequestDetailed) {
val dialog = GithubMergeCommitMessageDialog(project,
"Merge Pull Request",
"Merge pull request #${details.number} from ${details.head.label}",
details.title)
if (!dialog.showAndGet()) return
val commitMessage = dialog.message
object : Task.Backgroundable(project, "Merging Pull Request", true) {
override fun run(indicator: ProgressIndicator) {
requestExecutor.execute(indicator,
GithubApiRequests.Repos.PullRequests.merge(details,
commitMessage.first, commitMessage.second,
details.head.sha))
}
override fun onSuccess() {
GithubNotifications.showInfo(project, "Pull Request Merged", "Successfully merged pull request #${details.number}")
//TODO: refresh table and details
}
override fun onThrowable(error: Throwable) {
GithubNotifications.showError(project, "Failed To Merge Pull Request", error)
}
}.queue()
}
}
@@ -0,0 +1,35 @@
// Copyright 2000-2018 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.action
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.progress.Task
import com.intellij.openapi.project.Project
import org.jetbrains.plugins.github.api.GithubApiRequestExecutor
import org.jetbrains.plugins.github.api.GithubApiRequests
import org.jetbrains.plugins.github.api.data.GithubPullRequestDetailed
import org.jetbrains.plugins.github.pullrequest.action.merge.GithubPullRequestMergeActionBase
import org.jetbrains.plugins.github.util.GithubNotifications
class GithubPullRequestRebaseAction : GithubPullRequestMergeActionBase("Rebase and Merge") {
override fun canMerge(details: GithubPullRequestDetailed) = details.rebaseable
override fun mergePullRequest(e: AnActionEvent,
project: Project, requestExecutor: GithubApiRequestExecutor, details: GithubPullRequestDetailed) {
object : Task.Backgroundable(project, "Rebasing and Merging Pull Request", true) {
override fun run(indicator: ProgressIndicator) {
requestExecutor.execute(indicator,
GithubApiRequests.Repos.PullRequests.rebaseMerge(details, details.head.sha))
}
override fun onSuccess() {
GithubNotifications.showInfo(project, "Pull Request Rebased and Merged",
"Successfully rebased and merged pull request #${details.number}")
}
override fun onThrowable(error: Throwable) {
GithubNotifications.showError(project, "Failed To Rebase and Merge Pull Request", error)
}
}.queue()
}
}
@@ -0,0 +1,54 @@
// Copyright 2000-2018 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.action
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.progress.Task
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.text.StringUtil
import org.jetbrains.plugins.github.api.GithubApiRequestExecutor
import org.jetbrains.plugins.github.api.GithubApiRequests
import org.jetbrains.plugins.github.api.data.GithubPullRequestDetailed
import org.jetbrains.plugins.github.pullrequest.action.merge.GithubPullRequestMergeActionBase
import org.jetbrains.plugins.github.pullrequest.action.ui.GithubMergeCommitMessageDialog
import org.jetbrains.plugins.github.util.GithubAsyncUtil
import org.jetbrains.plugins.github.util.GithubNotifications
class GithubPullRequestSquashAction : GithubPullRequestMergeActionBase("Squash and Merge...") {
override fun canMerge(details: GithubPullRequestDetailed) = details.mergeable
override fun mergePullRequest(e: AnActionEvent,
project: Project, requestExecutor: GithubApiRequestExecutor, details: GithubPullRequestDetailed) {
val commitsRequest = e.getRequiredData(GithubPullRequestKeys.SELECTED_PULL_REQUEST_DATA_PROVIDER).logCommitsRequest
GithubAsyncUtil.awaitFutureAndRunOnEdt(commitsRequest,
project, "Loading Pull Request Commits", "Failed to Load Pull Request Commits") { commits ->
val body = "* " + StringUtil.join(commits, { it.subject }, "\n\n* ")
val dialog = GithubMergeCommitMessageDialog(project,
"Merge Pull Request",
"${details.title} (#${details.number})",
body)
if (!dialog.showAndGet()) return@awaitFutureAndRunOnEdt
val commitMessage = dialog.message
object : Task.Backgroundable(project, "Squashing and Merging Pull Request", true) {
override fun run(indicator: ProgressIndicator) {
requestExecutor.execute(indicator,
GithubApiRequests.Repos.PullRequests.squashMerge(details,
commitMessage.first, commitMessage.second,
details.head.sha))
}
override fun onSuccess() {
GithubNotifications.showInfo(project, "Pull Request Squashed and Merged",
"Successfully squashed amd merged pull request #${details.number}")
}
override fun onThrowable(error: Throwable) {
GithubNotifications.showError(project, "Failed To Squash and Merge Pull Request", error)
}
}.queue()
}
}
}
@@ -0,0 +1,56 @@
// Copyright 2000-2018 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.action.merge
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.CommonDataKeys
import com.intellij.openapi.project.DumbAwareAction
import com.intellij.openapi.project.Project
import org.jetbrains.plugins.github.api.GithubApiRequestExecutor
import org.jetbrains.plugins.github.api.data.GithubIssueState
import org.jetbrains.plugins.github.api.data.GithubPullRequestDetailed
import org.jetbrains.plugins.github.pullrequest.action.GithubPullRequestKeys
import org.jetbrains.plugins.github.util.GithubAsyncUtil
import org.jetbrains.plugins.github.util.GithubNotifications
abstract class GithubPullRequestMergeActionBase(text: String) : DumbAwareAction(text) {
override fun update(e: AnActionEvent) {
//TODO: project-level switch
val permissions = e.getRequiredData(GithubPullRequestKeys.REPO_DETAILS).permissions
e.presentation.isEnabledAndVisible = (permissions.isPush || permissions.isAdmin) && isEnabled(e)
}
private fun isEnabled(e: AnActionEvent): Boolean {
val pullRequest = e.getData(GithubPullRequestKeys.SELECTED_PULL_REQUEST) ?: return false
return pullRequest.state == GithubIssueState.open
}
final override fun actionPerformed(e: AnActionEvent) {
val project = e.getRequiredData(CommonDataKeys.PROJECT)
val detailsRequest = e.getRequiredData(GithubPullRequestKeys.SELECTED_PULL_REQUEST_DATA_PROVIDER).detailsRequest
GithubAsyncUtil.awaitFutureAndRunOnEdt(detailsRequest,
project, "Loading Pull Request Details", "Failed to Load Pull Request Details") { details ->
if (!preCheck(project, details)) return@awaitFutureAndRunOnEdt
mergePullRequest(e, project, e.getRequiredData(GithubPullRequestKeys.API_REQUEST_EXECUTOR), details)
}
}
private fun preCheck(project: Project, details: GithubPullRequestDetailed): Boolean {
if (details.merged) {
GithubNotifications.showError(project, "Failed to Merge Pull Request", "Pull request #${details.number} is already merged")
return false
}
if (!canMerge(details)) {
GithubNotifications.showError(project, "Failed to Merge Pull Request",
"Cannot merge pull request #${details.number} due to conflicts")
return false
}
return true
}
protected abstract fun canMerge(details: GithubPullRequestDetailed): Boolean
protected abstract fun mergePullRequest(e: AnActionEvent,
project: Project, requestExecutor: GithubApiRequestExecutor, details: GithubPullRequestDetailed)
}
@@ -0,0 +1,14 @@
// Copyright 2000-2018 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.action.merge
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.DefaultActionGroup
import com.intellij.openapi.project.DumbAware
import org.jetbrains.plugins.github.pullrequest.action.GithubPullRequestKeys
class GithubPullRequestMergeActionGroup : DefaultActionGroup(), DumbAware {
override fun update(e: AnActionEvent) {
val permissions = e.getRequiredData(GithubPullRequestKeys.REPO_DETAILS).permissions
e.presentation.isVisible = (permissions.isPush || permissions.isAdmin)
}
}
@@ -0,0 +1,47 @@
// Copyright 2000-2018 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.action.ui
import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.DialogWrapper
import com.intellij.openapi.util.Disposer
import com.intellij.openapi.vcs.ui.CommitMessage
import com.intellij.util.ui.JBDimension
import com.intellij.util.ui.JBUI
import com.intellij.util.ui.UIUtil
import javax.swing.JComponent
import javax.swing.JLabel
class GithubMergeCommitMessageDialog(project: Project, title: String, subject: String, body: String) : DialogWrapper(project) {
private val commitMessage = CommitMessage(project, false, false, true).apply {
setCommitMessage("$subject\n\n$body")
preferredSize = JBDimension(500, 85)
}
init {
Disposer.register(disposable, commitMessage)
setTitle(title)
setOKButtonText("Merge")
init()
}
override fun createCenterPanel(): JComponent? {
return JBUI.Panels.simplePanel(0, UIUtil.DEFAULT_VGAP)
.addToTop(JLabel("Merge commit message:"))
.addToCenter(commitMessage)
}
val message: Pair<String, String>
get() {
val text = commitMessage.comment
val idx = text.indexOf("\n\n")
return if (idx < 0) "" to text
else {
val subject = text.substring(0, idx)
if (subject.contains("\n")) "" to text
else subject to text.substring(idx + 2)
}
}
}
@@ -2,12 +2,53 @@
package org.jetbrains.plugins.github.util
import com.intellij.openapi.application.runInEdt
import com.intellij.openapi.progress.PerformInBackgroundOption
import com.intellij.openapi.progress.ProcessCanceledException
import com.intellij.openapi.progress.ProgressIndicator
import com.intellij.openapi.progress.Task
import com.intellij.openapi.project.Project
import java.util.concurrent.*
import java.util.function.BiFunction
object GithubAsyncUtil {
/**
* Run [consumer] on EDT with the result of [future]
* If future is cancelled, [consumer] will not be executed
*
* This is a naive implementation with timeout waiting
*/
@JvmStatic
fun <R : Future<T>, T> awaitFutureAndRunOnEdt(future: R,
project: Project, title: String, errorTitle: String,
consumer: (T) -> Unit) {
object : Task.Backgroundable(project, title, true, PerformInBackgroundOption.DEAF) {
var result: T? = null
override fun run(indicator: ProgressIndicator) {
while (true) {
try {
result = future.get(50, TimeUnit.MILLISECONDS)
break
}
catch (e: TimeoutException) {
indicator.checkCanceled()
}
}
indicator.checkCanceled()
}
override fun onSuccess() {
result?.let(consumer)
}
override fun onThrowable(error: Throwable) {
if (isCancellation(error)) return
GithubNotifications.showError(project, errorTitle, error)
}
}.queue()
}
fun isCancellation(error: Throwable): Boolean {
return error is ProcessCanceledException
|| error is CancellationException