diff --git a/plugins/github/resources/messages/GithubBundle.properties b/plugins/github/resources/messages/GithubBundle.properties
index 879685cd9f0d..c90a457a0374 100644
--- a/plugins/github/resources/messages/GithubBundle.properties
+++ b/plugins/github/resources/messages/GithubBundle.properties
@@ -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:
{0}
pull.request.create.input.remote.branch.name=Remote branch name:
diff --git a/plugins/github/src/org/jetbrains/plugins/github/pullrequest/ui/toolwindow/create/GHPRCreateComponentFactory.kt b/plugins/github/src/org/jetbrains/plugins/github/pullrequest/ui/toolwindow/create/GHPRCreateComponentFactory.kt
index 9d9ff7737458..1390b7143836 100644
--- a/plugins/github/src/org/jetbrains/plugins/github/pullrequest/ui/toolwindow/create/GHPRCreateComponentFactory.kt
+++ b/plugins/github/src/org/jetbrains/plugins/github/pullrequest/ui/toolwindow/create/GHPRCreateComponentFactory.kt
@@ -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(uiDisposable)
diff --git a/plugins/github/src/org/jetbrains/plugins/github/pullrequest/ui/toolwindow/create/GHPRCreateInfoComponentFactory.kt b/plugins/github/src/org/jetbrains/plugins/github/pullrequest/ui/toolwindow/create/GHPRCreateInfoComponentFactory.kt
index 0e3ba0592bba..0610d12f6a3a 100644
--- a/plugins/github/src/org/jetbrains/plugins/github/pullrequest/ui/toolwindow/create/GHPRCreateInfoComponentFactory.kt
+++ b/plugins/github/src/org/jetbrains/plugins/github/pullrequest/ui/toolwindow/create/GHPRCreateInfoComponentFactory.kt
@@ -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): 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,
@@ -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)
diff --git a/plugins/github/src/org/jetbrains/plugins/github/pullrequest/ui/toolwindow/create/GHPRTemplateLoader.kt b/plugins/github/src/org/jetbrains/plugins/github/pullrequest/ui/toolwindow/create/GHPRTemplateLoader.kt
new file mode 100644
index 000000000000..d7b7072382b2
--- /dev/null
+++ b/plugins/github/src/org/jetbrains/plugins/github/pullrequest/ui/toolwindow/create/GHPRTemplateLoader.kt
@@ -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()
+
+ private val paths = listOf(
+ ".github/pull_request_template.md",
+ "pull_request_template.md",
+ "docs/pull_request_template.md"
+ )
+
+ fun readTemplate(project: Project): CompletableFuture {
+ 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
+ }
+}
\ No newline at end of file
diff --git a/plugins/github/src/org/jetbrains/plugins/github/ui/util/DisableableDocument.kt b/plugins/github/src/org/jetbrains/plugins/github/ui/util/DisableableDocument.kt
new file mode 100644
index 000000000000..9966ee979de2
--- /dev/null
+++ b/plugins/github/src/org/jetbrains/plugins/github/ui/util/DisableableDocument.kt
@@ -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)
+}
\ No newline at end of file