Commit 50d541ec authored by Konstantin Kolosovsky's avatar Konstantin Kolosovsky
Browse files

vcs: Refactor commit dialog - move commit options management logic to workflow handler

parent 0f00e0b1
Showing with 168 additions and 102 deletions
+168 -102
......@@ -41,6 +41,7 @@ import com.intellij.util.ui.AbstractLayoutManager;
import com.intellij.util.ui.JBUI;
import com.intellij.util.ui.components.BorderLayoutPanel;
import com.intellij.vcsUtil.VcsUtil;
import kotlin.sequences.SequencesKt;
import one.util.streamex.StreamEx;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
......@@ -56,7 +57,6 @@ import java.util.*;
import static com.intellij.openapi.diagnostic.Logger.getInstance;
import static com.intellij.openapi.util.text.StringUtil.escapeXmlEntities;
import static com.intellij.openapi.vcs.VcsBundle.message;
import static com.intellij.openapi.vcs.changes.ui.CommitOptionsPanel.*;
import static com.intellij.openapi.vcs.changes.ui.DialogCommitWorkflow.getCommitHandlerFactories;
import static com.intellij.openapi.vcs.changes.ui.SingleChangeListCommitter.moveToFailedList;
import static com.intellij.ui.components.JBBox.createHorizontalBox;
......@@ -305,27 +305,14 @@ public abstract class CommitChangeListDialog extends DialogWrapper implements Ch
mySplitter.setSecondComponent(myCommitMessageArea);
mySplitter.setProportion(PropertiesComponent.getInstance().getFloat(SPLITTER_PROPORTION_OPTION, SPLITTER_PROPORTION_OPTION_DEFAULT));
initOptions();
myWarningLabel.setForeground(JBColor.RED);
myWarningLabel.setBorder(JBUI.Borders.empty(5, 5, 0, 5));
updateWarning();
}
private void initOptions() {
if (isDefaultCommitEnabled()) {
myCommitOptions.setVcsOptions(getVcsOptions(this, getWorkflowVcses(), myWorkflow.getAdditionalDataConsumer()));
}
myCommitOptions.setBeforeOptions(getBeforeOptions(getHandlers()));
myCommitOptions.setAfterOptions(getAfterOptions(getHandlers(), getDisposable()));
restoreState();
}
private void afterInit() {
updateButtons();
updateLegend();
updateCommitOptions();
myCommitMessageArea.setChangeList(getChangeList());
myCommitMessageArea.requestFocusInMessage();
......@@ -418,10 +405,6 @@ public abstract class CommitChangeListDialog extends DialogWrapper implements Ch
changeDetails(false);
}
void updateCommitOptions() {
myCommitOptions.onChangeListSelected(getChangeList(), ChangeListManagerImpl.getInstanceImpl(myProject).getUnversionedFiles());
}
protected void updateWarning() {
// check for null since can be called from constructor before field initialization
//noinspection ConstantConditions
......@@ -636,17 +619,6 @@ public abstract class CommitChangeListDialog extends DialogWrapper implements Ch
myUpdateButtonsRunnable.run();
}
boolean saveCommitOptions() {
try {
saveState();
return true;
}
catch(InputException ex) {
ex.show();
return false;
}
}
private class DefaultListCleaner {
private final boolean myToClean;
......@@ -664,10 +636,6 @@ public abstract class CommitChangeListDialog extends DialogWrapper implements Ch
}
}
void saveChangeListCommitOptions() {
myCommitOptions.saveChangeListComponentsState();
}
@Override
public void doCancelAction() {
myStateEventDispatcher.getMulticaster().cancelled();
......@@ -753,25 +721,25 @@ public abstract class CommitChangeListDialog extends DialogWrapper implements Ch
public void refresh() {
ChangeListManager.getInstance(myProject).invokeAfterUpdate(() -> {
getBrowser().updateDisplayedChangeLists();
myCommitOptions.refresh();
DialogCommitWorkflowKt.refresh(getCommitOptions());
}, InvokeAfterUpdateMode.SILENT, "commit dialog", ModalityState.current());
}
@Override
public void saveState() {
myCommitOptions.saveState();
DialogCommitWorkflowKt.saveState(getCommitOptions());
}
@Override
public void restoreState() {
myCommitOptions.restoreState();
DialogCommitWorkflowKt.restoreState(getCommitOptions());
}
// Used in plugins
@SuppressWarnings("unused")
@NotNull
public List<RefreshableOnComponent> getAdditionalComponents() {
return myCommitOptions.getAdditionalComponents();
return SequencesKt.toList(CommitOptionsKt.getAllOptions(getCommitOptions()));
}
private void updateButtons() {
......@@ -846,6 +814,12 @@ public abstract class CommitChangeListDialog extends DialogWrapper implements Ch
return myCommitMessageArea;
}
@NotNull
@Override
public CommitOptionsUi getCommitOptionsUi() {
return myCommitOptions;
}
@Override
public void addDataProvider(@NotNull DataProvider provider) {
myDataProviders.add(provider);
......@@ -903,6 +877,11 @@ public abstract class CommitChangeListDialog extends DialogWrapper implements Ch
return myWorkflow.getCommitHandlers();
}
@NotNull
private CommitOptions getCommitOptions() {
return myWorkflow.getCommitOptions();
}
private boolean isDefaultCommitEnabled() {
return myWorkflow.isDefaultCommitEnabled();
}
......
// Copyright 2000-2019 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 com.intellij.openapi.vcs.changes.ui
import com.intellij.openapi.vcs.AbstractVcs
import com.intellij.openapi.vcs.ui.RefreshableOnComponent
import com.intellij.util.containers.ContainerUtil.newUnmodifiableList
import com.intellij.util.containers.ContainerUtil.unmodifiableOrEmptyMap
interface CommitOptions {
val vcsOptions: Map<AbstractVcs<*>, RefreshableOnComponent>
val beforeOptions: List<RefreshableOnComponent>
val afterOptions: List<RefreshableOnComponent>
}
val CommitOptions.allOptions: Sequence<RefreshableOnComponent> get() = sequenceOf(vcsOptions.values, beforeOptions, afterOptions).flatten()
val CommitOptions.isEmpty: Boolean get() = allOptions.none()
class CommitOptionsImpl(
override val vcsOptions: Map<AbstractVcs<*>, RefreshableOnComponent>,
override val beforeOptions: List<RefreshableOnComponent>,
override val afterOptions: List<RefreshableOnComponent>
) : CommitOptions
class MutableCommitOptions : CommitOptions {
override val vcsOptions: MutableMap<AbstractVcs<*>, RefreshableOnComponent> = mutableMapOf()
override val beforeOptions: MutableList<RefreshableOnComponent> = mutableListOf()
override val afterOptions: MutableList<RefreshableOnComponent> = mutableListOf()
fun add(options: CommitOptions) {
vcsOptions += options.vcsOptions
beforeOptions += options.beforeOptions
afterOptions += options.afterOptions
}
fun clear() {
vcsOptions.clear()
beforeOptions.clear()
afterOptions.clear()
}
fun toUnmodifiableOptions() =
CommitOptionsImpl(unmodifiableOrEmptyMap(vcsOptions), newUnmodifiableList(beforeOptions), newUnmodifiableList(afterOptions))
}
\ No newline at end of file
// Copyright 2000-2019 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 com.intellij.openapi.vcs.changes.ui
import com.intellij.openapi.Disposable
import com.intellij.openapi.ui.VerticalFlowLayout
import com.intellij.openapi.vcs.AbstractVcs
import com.intellij.openapi.vcs.CheckinProjectPanel
import com.intellij.openapi.vcs.VcsBundle.message
import com.intellij.openapi.vcs.changes.ChangesUtil.getAffectedVcses
import com.intellij.openapi.vcs.changes.ChangesUtil.getAffectedVcsesForFiles
import com.intellij.openapi.vcs.changes.LocalChangeList
import com.intellij.openapi.vcs.checkin.CheckinChangeListSpecificComponent
import com.intellij.openapi.vcs.checkin.CheckinHandler
import com.intellij.openapi.vcs.ui.RefreshableOnComponent
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.ui.IdeBorderFactory.createTitledBorder
import com.intellij.ui.ScrollPaneFactory.createScrollPane
import com.intellij.util.PairConsumer
import com.intellij.util.ui.JBUI
import com.intellij.util.ui.JBUI.Panels.simplePanel
import com.intellij.util.ui.UIUtil.removeMnemonic
import com.intellij.util.ui.components.BorderLayoutPanel
import java.util.Collections.unmodifiableList
import javax.swing.Box
import javax.swing.JPanel
import kotlin.collections.set
private val VCS_COMPARATOR = compareBy<AbstractVcs<*>, String>(String.CASE_INSENSITIVE_ORDER) { it.keyInstanceMethod.name }
class CommitOptionsPanel(private val myCommitPanel: CheckinProjectPanel) : BorderLayoutPanel() {
class CommitOptionsPanel(private val myCommitPanel: CheckinProjectPanel) : BorderLayoutPanel(), CommitOptionsUi {
private val perVcsOptionsPanels = mutableMapOf<AbstractVcs<*>, JPanel>()
private val vcsOptionsPanel = simplePanel()
private val beforeOptionsPanel = simplePanel()
......@@ -35,35 +24,17 @@ class CommitOptionsPanel(private val myCommitPanel: CheckinProjectPanel) : Borde
private val actionName get() = removeMnemonic(myCommitPanel.commitActionName)
private val vcsOptions = mutableMapOf<AbstractVcs<*>, RefreshableOnComponent>()
private val beforeOptions = mutableListOf<RefreshableOnComponent>()
private val afterOptions = mutableListOf<RefreshableOnComponent>()
private val allOptions get() = sequenceOf(vcsOptions.values, beforeOptions, afterOptions).flatten()
private val changeListSpecificOptions get() = allOptions.filterIsInstance<CheckinChangeListSpecificComponent>()
private val options = MutableCommitOptions()
private val vcsOptions get() = options.vcsOptions
private val beforeOptions get() = options.beforeOptions
private val afterOptions get() = options.afterOptions
val isEmpty: Boolean get() = allOptions.none()
val additionalComponents: List<RefreshableOnComponent> get() = unmodifiableList(allOptions.toList())
val isEmpty: Boolean get() = options.isEmpty
init {
buildLayout()
}
fun saveState() = allOptions.forEach { it.saveState() }
fun restoreState() = allOptions.forEach { it.restoreState() }
fun refresh() = allOptions.forEach { it.refresh() }
fun onChangeListSelected(changeList: LocalChangeList, unversionedFiles: List<VirtualFile>) {
val affectedVcses =
getAffectedVcses(changeList.changes, myCommitPanel.project) + getAffectedVcsesForFiles(unversionedFiles, myCommitPanel.project)
for ((vcs, panel) in perVcsOptionsPanels) {
panel.isVisible = affectedVcses.contains(vcs)
}
changeListSpecificOptions.forEach { it.onChangeListSelected(changeList) }
}
fun saveChangeListComponentsState() = changeListSpecificOptions.forEach { it.saveState() }
private fun buildLayout() {
val optionsBox = Box.createVerticalBox().apply {
add(vcsOptionsPanel)
......@@ -74,7 +45,16 @@ class CommitOptionsPanel(private val myCommitPanel: CheckinProjectPanel) : Borde
addToCenter(optionsPane).withBorder(JBUI.Borders.emptyLeft(10))
}
fun setVcsOptions(newOptions: Map<AbstractVcs<*>, RefreshableOnComponent>) {
override fun setOptions(options: CommitOptions) {
setVcsOptions(options.vcsOptions)
setBeforeOptions(options.beforeOptions)
setAfterOptions(options.afterOptions)
}
override fun setVisible(vcses: Collection<AbstractVcs<*>>) =
perVcsOptionsPanels.forEach { vcs, vcsPanel -> vcsPanel.isVisible = vcs in vcses }
private fun setVcsOptions(newOptions: Map<AbstractVcs<*>, RefreshableOnComponent>) {
if (vcsOptions != newOptions) {
vcsOptions.clear()
perVcsOptionsPanels.clear()
......@@ -89,7 +69,7 @@ class CommitOptionsPanel(private val myCommitPanel: CheckinProjectPanel) : Borde
}
}
fun setBeforeOptions(newOptions: List<RefreshableOnComponent>) {
private fun setBeforeOptions(newOptions: List<RefreshableOnComponent>) {
if (beforeOptions != newOptions) {
beforeOptions.clear()
beforeOptionsPanel.removeAll()
......@@ -103,7 +83,7 @@ class CommitOptionsPanel(private val myCommitPanel: CheckinProjectPanel) : Borde
}
}
fun setAfterOptions(newOptions: List<RefreshableOnComponent>) {
private fun setAfterOptions(newOptions: List<RefreshableOnComponent>) {
if (afterOptions != newOptions) {
afterOptions.clear()
afterOptionsPanel.removeAll()
......@@ -121,22 +101,5 @@ class CommitOptionsPanel(private val myCommitPanel: CheckinProjectPanel) : Borde
fun verticalPanel(title: String) = JPanel(VerticalFlowLayout(0, 5)).apply {
border = createTitledBorder(title)
}
@JvmStatic
fun getVcsOptions(commitPanel: CheckinProjectPanel,
vcses: Collection<AbstractVcs<*>>,
additionalData: PairConsumer<Any, Any>): Map<AbstractVcs<*>, RefreshableOnComponent> =
vcses.sortedWith(VCS_COMPARATOR)
.associateWith { it.checkinEnvironment?.createAdditionalOptionsPanel(commitPanel, additionalData) }
.filterValues { it != null }
.mapValues { it.value!! }
@JvmStatic
fun getBeforeOptions(handlers: Collection<CheckinHandler>): List<RefreshableOnComponent> =
handlers.mapNotNull { it.beforeCheckinConfigurationPanel }
@JvmStatic
fun getAfterOptions(handlers: Collection<CheckinHandler>, parent: Disposable): List<RefreshableOnComponent> =
handlers.mapNotNull { it.getAfterCheckinConfigurationPanel(parent) }
}
}
......@@ -19,6 +19,7 @@ import com.intellij.openapi.vcs.changes.actions.ScheduleForAdditionAction.addUnv
import com.intellij.openapi.vcs.changes.ui.CommitChangeListDialog.DIALOG_TITLE
import com.intellij.openapi.vcs.changes.ui.CommitChangeListDialog.getExecutorPresentableText
import com.intellij.openapi.vcs.checkin.BaseCheckinHandlerFactory
import com.intellij.openapi.vcs.checkin.CheckinChangeListSpecificComponent
import com.intellij.openapi.vcs.checkin.CheckinHandler
import com.intellij.openapi.vcs.checkin.CheckinMetaHandler
import com.intellij.openapi.vcs.impl.CheckinHandlersManager
......@@ -31,6 +32,19 @@ import com.intellij.util.containers.ContainerUtil.newUnmodifiableList
private val LOG = logger<DialogCommitWorkflow>()
internal fun CommitOptions.saveState() = allOptions.forEach { it.saveState() }
internal fun CommitOptions.restoreState() = allOptions.forEach { it.restoreState() }
internal fun CommitOptions.refresh() = allOptions.forEach { it.refresh() }
internal val CommitOptions.changeListSpecificOptions: Sequence<CheckinChangeListSpecificComponent>
get() = allOptions.filterIsInstance<CheckinChangeListSpecificComponent>()
internal fun CommitOptions.changeListChanged(changeList: LocalChangeList) = changeListSpecificOptions.forEach {
it.onChangeListSelected(changeList)
}
internal fun CommitOptions.saveChangeListSpecificOptions() = changeListSpecificOptions.forEach { it.saveState() }
open class DialogCommitWorkflow(val project: Project,
val initiallyIncluded: Collection<*>,
val initialChangeList: LocalChangeList? = null,
......@@ -47,12 +61,15 @@ open class DialogCommitWorkflow(val project: Project,
// TODO Probably unify with "CommitContext"
private val _additionalData = PseudoMap<Any, Any>()
protected val additionalDataConsumer: PairConsumer<Any, Any> get() = _additionalData
val additionalDataConsumer: PairConsumer<Any, Any> get() = _additionalData
protected val additionalData: NullableFunction<Any, Any> get() = _additionalData
private val _commitHandlers = mutableListOf<CheckinHandler>()
val commitHandlers: List<CheckinHandler> get() = newUnmodifiableList(_commitHandlers)
private val _commitOptions = MutableCommitOptions()
val commitOptions: CommitOptions get() = _commitOptions.toUnmodifiableOptions()
val commitMessagePolicy: CommitMessagePolicy = CommitMessagePolicy(project, initialCommitMessage)
internal fun initCommitHandlers(handlers: List<CheckinHandler>) {
......@@ -60,6 +77,11 @@ open class DialogCommitWorkflow(val project: Project,
_commitHandlers += handlers
}
internal fun initCommitOptions(options: CommitOptions) {
_commitOptions.clear()
_commitOptions.add(options)
}
fun addUnversionedFiles(changeList: LocalChangeList, unversionedFiles: List<VirtualFile>, callback: (List<Change>) -> Unit): Boolean {
if (unversionedFiles.isEmpty()) return true
......
......@@ -3,15 +3,21 @@ package com.intellij.openapi.vcs.changes.ui
import com.intellij.openapi.Disposable
import com.intellij.openapi.actionSystem.DataProvider
import com.intellij.openapi.ui.InputException
import com.intellij.openapi.util.Disposer
import com.intellij.openapi.vcs.AbstractVcs
import com.intellij.openapi.vcs.CheckinProjectPanel
import com.intellij.openapi.vcs.VcsConfiguration
import com.intellij.openapi.vcs.VcsDataKeys.COMMIT_WORKFLOW_HANDLER
import com.intellij.openapi.vcs.changes.CommitExecutor
import com.intellij.openapi.vcs.changes.CommitExecutorBase
import com.intellij.openapi.vcs.changes.CommitSession
import com.intellij.openapi.vcs.changes.CommitWorkflowHandler
import com.intellij.openapi.vcs.changes.*
import com.intellij.openapi.vcs.changes.ChangesUtil.getAffectedVcses
import com.intellij.openapi.vcs.changes.ChangesUtil.getAffectedVcsesForFiles
import com.intellij.openapi.vcs.changes.ui.DialogCommitWorkflow.Companion.getCommitHandlers
import com.intellij.openapi.vcs.checkin.CheckinHandler
import com.intellij.openapi.vcs.impl.LineStatusTrackerManager
import com.intellij.util.PairConsumer
private val VCS_COMPARATOR = compareBy<AbstractVcs<*>, String>(String.CASE_INSENSITIVE_ORDER) { it.keyInstanceMethod.name }
class SingleChangeListCommitWorkflowHandler(
private val workflow: DialogCommitWorkflow,
......@@ -35,6 +41,7 @@ class SingleChangeListCommitWorkflowHandler(
private val commitHandlers get() = workflow.commitHandlers
private val commitMessagePolicy get() = workflow.commitMessagePolicy
private val commitOptions get() = workflow.commitOptions
init {
Disposer.register(ui, this)
......@@ -52,14 +59,14 @@ class SingleChangeListCommitWorkflowHandler(
workflow.initCommitHandlers(getCommitHandlers(ui, workflow.commitContext))
ui.addInclusionListener(this, this)
initCommitMessage()
initCommitOptions()
return ui.activate()
}
override fun cancelled() {
ui.saveChangeListCommitOptions()
commitOptions.saveChangeListSpecificOptions()
saveCommitMessage(false)
LineStatusTrackerManager.getInstanceImpl(project).resetExcludedFromCommitMarkers()
......@@ -69,7 +76,7 @@ class SingleChangeListCommitWorkflowHandler(
override fun changeListChanged() {
updateCommitMessage()
ui.updateCommitOptions()
updateCommitOptions()
}
override fun executorCalled(executor: CommitExecutor?) = executor?.let { execute(it) } ?: executeDefault(null)
......@@ -93,7 +100,7 @@ class SingleChangeListCommitWorkflowHandler(
private fun executeDefault(executor: CommitExecutor?) {
if (!addUnversionedFiles()) return
if (!checkEmptyCommitMessage()) return
if (!ui.saveCommitOptions()) return
if (!saveCommitOptions()) return
saveCommitMessage(true)
ui.executeDefaultCommitSession(executor)
......@@ -102,7 +109,7 @@ class SingleChangeListCommitWorkflowHandler(
private fun executeCustom(executor: CommitExecutor, session: CommitSession) {
if (!workflow.canExecute(executor, getIncludedChanges())) return
if (!checkEmptyCommitMessage()) return
if (!ui.saveCommitOptions()) return
if (!saveCommitOptions()) return
saveCommitMessage(true)
ui.execute(executor, session)
......@@ -127,5 +134,49 @@ class SingleChangeListCommitWorkflowHandler(
private fun saveCommitMessage(success: Boolean) =
commitMessagePolicy.save(getChangeList(), getIncludedChanges(), getCommitMessage(), success)
private fun initCommitOptions() {
workflow.initCommitOptions(CommitOptionsImpl(
if (workflow.isDefaultCommitEnabled) getVcsOptions(ui, workflow.affectedVcses, workflow.additionalDataConsumer) else emptyMap(),
getBeforeOptions(workflow.commitHandlers),
getAfterOptions(workflow.commitHandlers, this)
))
ui.commitOptionsUi.setOptions(commitOptions)
commitOptions.restoreState()
updateCommitOptions()
}
private fun updateCommitOptions() {
commitOptions.changeListChanged(getChangeList())
updateCommitOptionsVisibility()
}
private fun updateCommitOptionsVisibility() {
val unversionedFiles = ChangeListManagerImpl.getInstanceImpl(project).unversionedFiles
val vcses = getAffectedVcses(getChangeList().changes, project) + getAffectedVcsesForFiles(unversionedFiles, project)
ui.commitOptionsUi.setVisible(vcses)
}
private fun saveCommitOptions() = try {
commitOptions.saveState()
true
}
catch (ex: InputException) {
ex.show()
false
}
private fun getVcsOptions(commitPanel: CheckinProjectPanel, vcses: Collection<AbstractVcs<*>>, additionalData: PairConsumer<Any, Any>) =
vcses.sortedWith(VCS_COMPARATOR)
.associateWith { it.checkinEnvironment?.createAdditionalOptionsPanel(commitPanel, additionalData) }
.filterValues { it != null }
.mapValues { it.value!! }
private fun getBeforeOptions(handlers: Collection<CheckinHandler>) = handlers.mapNotNull { it.beforeCheckinConfigurationPanel }
private fun getAfterOptions(handlers: Collection<CheckinHandler>, parent: Disposable) =
handlers.mapNotNull { it.getAfterCheckinConfigurationPanel(parent) }
override fun dispose() = Unit
}
\ No newline at end of file
......@@ -3,6 +3,7 @@ package com.intellij.openapi.vcs.changes.ui
import com.intellij.openapi.Disposable
import com.intellij.openapi.actionSystem.DataProvider
import com.intellij.openapi.vcs.AbstractVcs
import com.intellij.openapi.vcs.changes.Change
import com.intellij.openapi.vcs.changes.CommitExecutor
import com.intellij.openapi.vcs.changes.LocalChangeList
......@@ -12,6 +13,7 @@ import java.util.*
interface SingleChangeListCommitWorkflowUi : DataProvider, Disposable {
val commitMessageUi: CommitMessageUi
val commitOptionsUi: CommitOptionsUi
fun activate(): Boolean
......@@ -44,6 +46,12 @@ interface CommitMessageUi : TextAccessor {
override fun setText(text: String?)
}
interface CommitOptionsUi {
fun setOptions(options: CommitOptions)
fun setVisible(vcses: Collection<AbstractVcs<*>>)
}
interface CommitExecutorListener : EventListener {
fun executorCalled(executor: CommitExecutor?)
}
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment