[github] support PR templates

Fixes IDEA-142717

(cherry picked from commit d36da06b7e5add5c715cd7a85fbb6b9f7461461a)

IJ-MR-5963

GitOrigin-RevId: 3f6e397e09d455711a3260bf52056a3aea0db42e
This commit is contained in:
Ivan Semenov
2021-02-19 11:35:24 +00:00
committed by intellij-monorepo-bot
parent a1080da514
commit c844754347
5 changed files with 97 additions and 14 deletions
@@ -178,6 +178,7 @@ pull.request.create.direction.save=Save
pull.request.create.process.title=Creating a pull request...
pull.request.create.title=Title
pull.request.create.description=Description
pull.request.create.loading.template=Loading template...
pull.request.create.collect.diff.data.process.title=Collecting diff data
pull.request.create.push.failed=Push failed:<br/>{0}
pull.request.create.input.remote.branch.name=Remote branch name:
@@ -13,6 +13,7 @@ import org.jetbrains.plugins.github.i18n.GithubBundle
import org.jetbrains.plugins.github.pullrequest.data.GHPRDataContext
import org.jetbrains.plugins.github.pullrequest.ui.GHCompletableFutureLoadingModel
import org.jetbrains.plugins.github.pullrequest.ui.toolwindow.GHPRToolWindowTabComponentController
import org.jetbrains.plugins.github.ui.util.DisableableDocument
import javax.swing.*
import javax.swing.text.PlainDocument
@@ -43,7 +44,7 @@ internal class GHPRCreateComponentFactory(private val project: Project,
val directionModel = GHPRCreateDirectionModelImpl(repositoryDataService.repositoryMapping)
val titleDocument = PlainDocument()
val descriptionDocument = PlainDocument()
val descriptionDocument = DisableableDocument()
val metadataModel = GHPRCreateMetadataModel(repositoryDataService, dataContext.securityService.currentUser)
val createLoadingModel = GHCompletableFutureLoadingModel<GHPullRequestShort>(uiDisposable)
@@ -52,6 +52,7 @@ import org.jetbrains.plugins.github.pullrequest.ui.GHLoadingModel
import org.jetbrains.plugins.github.pullrequest.ui.SimpleEventListener
import org.jetbrains.plugins.github.pullrequest.ui.details.GHPRMetadataPanelFactory
import org.jetbrains.plugins.github.pullrequest.ui.toolwindow.GHPRToolWindowTabComponentController
import org.jetbrains.plugins.github.ui.util.DisableableDocument
import org.jetbrains.plugins.github.util.*
import org.jetbrains.plugins.github.util.GithubUtil.runInterruptable
import java.awt.event.ActionEvent
@@ -67,7 +68,7 @@ internal class GHPRCreateInfoComponentFactory(private val project: Project,
fun create(directionModel: GHPRCreateDirectionModel,
titleDocument: Document,
descriptionDocument: Document,
descriptionDocument: DisableableDocument,
metadataModel: GHPRCreateMetadataModel,
createLoadingModel: GHCompletableFutureLoadingModel<GHPullRequestShort>): JComponent {
@@ -87,15 +88,12 @@ internal class GHPRCreateInfoComponentFactory(private val project: Project,
progressIndicator.cancel()
viewController.viewList()
directionModel.reset()
titleDocument.remove(0, titleDocument.length)
descriptionDocument.remove(0, descriptionDocument.length)
metadataModel.reset()
resetForm(directionModel, titleDocument, descriptionDocument, metadataModel)
createLoadingModel.future = null
}
}
InfoController(directionModel, createAction, createDraftAction)
directionModel.reset()
resetForm(directionModel, titleDocument, descriptionDocument, metadataModel)
val directionSelector = GHPRCreateDirectionComponentFactory(repositoriesManager, directionModel).create().apply {
border = BorderFactory.createCompoundBorder(IdeBorderFactory.createBorder(SideBorder.BOTTOM),
@@ -118,6 +116,10 @@ internal class GHPRCreateInfoComponentFactory(private val project: Project,
emptyText.text = GithubBundle.message("pull.request.create.description")
lineWrap = true
}
descriptionDocument.addAndInvokeEnabledStateListener {
descriptionField.isEnabled = descriptionDocument.enabled
}
val descriptionPane = JBScrollPane(descriptionField,
ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER).apply {
@@ -203,7 +205,7 @@ internal class GHPRCreateInfoComponentFactory(private val project: Project,
}
private inner class CreateAction(private val directionModel: GHPRCreateDirectionModel,
private val titleDocument: Document, private val descriptionDocument: Document,
private val titleDocument: Document, private val descriptionDocument: DisableableDocument,
private val metadataModel: GHPRCreateMetadataModel,
private val draft: Boolean,
private val loadingModel: GHCompletableFutureLoadingModel<GHPullRequestShort>,
@@ -240,17 +242,15 @@ internal class GHPRCreateInfoComponentFactory(private val project: Project,
CollectionDelta(emptyList(), assignees))
if (labels.isNotEmpty()) detailsData.adjustLabels(ProgressWrapper.wrap(progressIndicator),
CollectionDelta(emptyList(), labels))
} finally {
}
finally {
Disposer.dispose(disposable)
}
pullRequest
}
}.successOnEdt {
viewController.viewPullRequest(it)
directionModel.reset()
titleDocument.remove(0, titleDocument.length)
descriptionDocument.remove(0, descriptionDocument.length)
metadataModel.reset()
resetForm(directionModel, titleDocument, descriptionDocument, metadataModel)
it
}
}
@@ -304,7 +304,8 @@ internal class GHPRCreateInfoComponentFactory(private val project: Project,
GithubBundle.message("pull.request.create.input.remote.branch.name"),
GithubBundle.message("pull.request.create.input.remote.branch.title"),
null, localBranch.name, null, null,
GithubBundle.message("pull.request.create.input.remote.branch.comment", localBranch.name, remote.name))
GithubBundle.message("pull.request.create.input.remote.branch.comment",
localBranch.name, remote.name))
?: return null
//always set tracking
return GitPushTarget(GitStandardRemoteBranch(remote, branchName), true)
@@ -343,6 +344,26 @@ internal class GHPRCreateInfoComponentFactory(private val project: Project,
dialog.show()
}
private fun resetForm(directionModel: GHPRCreateDirectionModel,
titleDocument: Document,
descriptionDocument: DisableableDocument,
metadataModel: GHPRCreateMetadataModel) {
directionModel.reset()
titleDocument.remove(0, titleDocument.length)
descriptionDocument.remove(0, descriptionDocument.length)
descriptionDocument.enabled = false
descriptionDocument.insertString(0, GithubBundle.message("pull.request.create.loading.template"), null)
GHPRTemplateLoader.readTemplate(project).successOnEdt {
if (!descriptionDocument.enabled) {
descriptionDocument.remove(0, descriptionDocument.length)
descriptionDocument.insertString(0, it, null)
}
}.completionOnEdt {
if (!descriptionDocument.enabled) descriptionDocument.enabled = true
}
metadataModel.reset()
}
companion object {
private val Document.text: String get() = getText(0, length)
@@ -0,0 +1,44 @@
// 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.create
import com.intellij.openapi.diagnostic.logger
import com.intellij.openapi.progress.EmptyProgressIndicator
import com.intellij.openapi.progress.ProgressManager
import com.intellij.openapi.project.Project
import com.intellij.util.TimeoutUtil
import com.intellij.util.concurrency.annotations.RequiresBackgroundThread
import org.jetbrains.plugins.github.util.submitIOTask
import java.nio.file.Files
import java.nio.file.Paths
import java.util.concurrent.CompletableFuture
object GHPRTemplateLoader {
private val LOG = logger<GHPRTemplateLoader>()
private val paths = listOf(
".github/pull_request_template.md",
"pull_request_template.md",
"docs/pull_request_template.md"
)
fun readTemplate(project: Project): CompletableFuture<String?> {
return ProgressManager.getInstance().submitIOTask(EmptyProgressIndicator()) {
doLoad(project)
}
}
@RequiresBackgroundThread
private fun doLoad(project: Project): String? {
val basePath = project.basePath ?: return null
try {
val files = paths.map { Paths.get(basePath, it) }
val fileContent = files.find(Files::exists)?.let(Files::readString)
if (fileContent != null) return fileContent
}
catch (e: Exception) {
LOG.warn("Failed to read PR template", e)
}
return null
}
}
@@ -0,0 +1,16 @@
// 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.ui.util
import com.intellij.util.EventDispatcher
import org.jetbrains.plugins.github.pullrequest.ui.SimpleEventListener
import org.jetbrains.plugins.github.util.GithubUtil.Delegates.observableField
import javax.swing.text.PlainDocument
class DisableableDocument : PlainDocument() {
private val eventDispatcher = EventDispatcher.create(SimpleEventListener::class.java)
var enabled by observableField(true, eventDispatcher)
fun addAndInvokeEnabledStateListener(listener: () -> Unit) = SimpleEventListener.addAndInvokeListener(eventDispatcher, listener)
}