From 27548eefc986aeed8b0f64ef97327da773fa3df4 Mon Sep 17 00:00:00 2001 From: Konstantin Kolosovsky Date: Fri, 14 Jun 2019 21:46:50 +0300 Subject: [PATCH] vcs: Refactor "MultipleLocalChangeListsBrowser" - provide custom "InclusionModel" instead of custom "ChangesTree" GitOrigin-RevId: 9f367ca795be0759f7f0108b984f569e892bd8e1 --- .../openapi/vcs/changes/ui/ChangesTree.java | 24 +-- .../ui/MultipleLocalChangeListsBrowser.java | 143 ++---------------- .../ui/PartiallyExcludedFilesStateHolder.java | 26 +--- .../vcs/commit/PartialCommitInclusionModel.kt | 67 ++++++++ .../vcs/BasePartiallyExcludedChangesTest.kt | 7 +- 5 files changed, 101 insertions(+), 166 deletions(-) create mode 100644 platform/vcs-impl/src/com/intellij/vcs/commit/PartialCommitInclusionModel.kt diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesTree.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesTree.java index 85fbb2ebdb96..285b04776b2e 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesTree.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/ChangesTree.java @@ -497,10 +497,8 @@ public abstract class ChangesTree extends Tree implements DataProvider { myTreeInclusionListener = runnable; } - protected void notifyInclusionListener() { - if (myTreeInclusionListener != null) { - myTreeInclusionListener.run(); - } + private void notifyInclusionListener() { + if (myTreeInclusionListener != null) myTreeInclusionListener.run(); } public void setIncludedChanges(@NotNull Collection changes) { @@ -533,8 +531,8 @@ public abstract class ChangesTree extends Tree implements DataProvider { protected void toggleChanges(@NotNull Collection changes) { boolean hasExcluded = false; - for (Object value : changes) { - if (!isIncluded(value)) { + for (Object item : changes) { + if (getInclusionModel().getInclusionState(item) != State.SELECTED) { hasExcluded = true; break; } @@ -695,16 +693,22 @@ public abstract class ChangesTree extends Tree implements DataProvider { } } - - protected State getNodeStatus(@NotNull ChangesBrowserNode node) { + @NotNull + private State getNodeStatus(@NotNull ChangesBrowserNode node) { boolean hasIncluded = false; boolean hasExcluded = false; - for (Object change : getUserObjectsUnder(node)) { - if (isIncluded(change)) { + for (Object item : getUserObjectsUnder(node)) { + State state = getInclusionModel().getInclusionState(item); + + if (state == State.SELECTED) { hasIncluded = true; } + else if (state == State.NOT_SELECTED) { + hasExcluded = true; + } else { + hasIncluded = true; hasExcluded = true; } } diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/MultipleLocalChangeListsBrowser.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/MultipleLocalChangeListsBrowser.java index b7fac66f8a73..189729e281ca 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/MultipleLocalChangeListsBrowser.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/MultipleLocalChangeListsBrowser.java @@ -24,10 +24,7 @@ import com.intellij.openapi.vcs.changes.*; import com.intellij.openapi.vcs.changes.actions.RollbackDialogAction; import com.intellij.openapi.vcs.changes.actions.diff.UnversionedDiffRequestProducer; import com.intellij.openapi.vcs.changes.actions.diff.lst.LocalChangeListDiffTool; -import com.intellij.openapi.vcs.ex.ExclusionState; -import com.intellij.openapi.vcs.ex.PartialLocalLineStatusTracker; import com.intellij.openapi.vcs.impl.LineStatusTrackerManager; -import com.intellij.openapi.vcs.impl.PartialChangesUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.ui.CollectionComboBoxModel; import com.intellij.ui.ColoredListCellRenderer; @@ -38,6 +35,7 @@ import com.intellij.util.ui.ThreeStateCheckBox.State; import com.intellij.util.ui.update.MergingUpdateQueue; import com.intellij.util.ui.update.Update; import com.intellij.vcs.commit.PartialCommitChangeNodeDecorator; +import com.intellij.vcs.commit.PartialCommitInclusionModel; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -46,9 +44,10 @@ import javax.swing.tree.DefaultTreeModel; import java.awt.*; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; -import java.util.*; -import java.util.stream.Stream; +import java.util.Optional; import static com.intellij.openapi.util.text.StringUtil.shortenTextWithEllipsis; import static com.intellij.openapi.vcs.changes.ui.ChangesListView.UNVERSIONED_FILES_DATA_KEY; @@ -65,12 +64,12 @@ class MultipleLocalChangeListsBrowser extends CommitDialogChangesBrowser impleme @NotNull private final ChangeListChooser myChangeListChooser; @NotNull private final DeleteProvider myDeleteProvider = new VirtualFileDeleteProvider(); + @NotNull private final PartialCommitInclusionModel myInclusionModel; + @NotNull private LocalChangeList myChangeList; private final List myChanges = new ArrayList<>(); private final List myUnversioned = new ArrayList<>(); private boolean myHasHiddenUnversioned; - @NotNull private LocalChangeList myChangeList; - @Nullable private Runnable mySelectedListChangeListener; private final RollbackDialogAction myRollbackDialogAction; @@ -96,6 +95,11 @@ class MultipleLocalChangeListsBrowser extends CommitDialogChangesBrowser impleme } } + myInclusionModel = new PartialCommitInclusionModel(myProject, myChangeList); + Disposer.register(this, myInclusionModel); + getViewer().setInclusionModel(myInclusionModel); + Disposer.register(myInclusionModel, () -> getViewer().setInclusionModel(null)); + ChangeListManager.getInstance(myProject).addChangeListListener(new MyChangeListListener(), this); init(); @@ -103,13 +107,6 @@ class MultipleLocalChangeListsBrowser extends CommitDialogChangesBrowser impleme updateSelectedChangeList(myChangeList); } - @NotNull - @Override - protected ChangesBrowserTreeList createTreeList(@NotNull Project project, boolean showCheckboxes, boolean highlightProblems) { - String changelistId = ChangeListManager.getInstance(project).getDefaultChangeList().getId(); - return new MyChangesBrowserTreeList(project, showCheckboxes, highlightProblems, changelistId, this); - } - @Nullable @Override protected JComponent createHeaderPanel() { @@ -228,7 +225,7 @@ class MultipleLocalChangeListsBrowser extends CommitDialogChangesBrowser impleme updateDisplayedChanges(); if (isListChanged && mySelectedListChangeListener != null) mySelectedListChangeListener.run(); - ((MyChangesBrowserTreeList)myViewer).setChangelistId(list.getId()); + myInclusionModel.setChangeList(myChangeList); } @Override @@ -450,7 +447,7 @@ class MultipleLocalChangeListsBrowser extends CommitDialogChangesBrowser impleme public State isSelected(AnActionEvent e) { Object object = getUserObject(e); if (object == null) return State.NOT_SELECTED; - return ((MyChangesBrowserTreeList)myViewer).getUserObjectState(object); + return myInclusionModel.getInclusionState(object); } @Override @@ -486,118 +483,4 @@ class MultipleLocalChangeListsBrowser extends CommitDialogChangesBrowser impleme }); } } - - private class MyChangesBrowserTreeList extends ChangesBrowserTreeList { - private final MyStateHolder myStateHolder; - - MyChangesBrowserTreeList(@NotNull Project project, boolean showCheckboxes, boolean highlightProblems, - @NotNull String changelistId, @NotNull Disposable disposable) { - super(MultipleLocalChangeListsBrowser.this, project, showCheckboxes, highlightProblems); - - myStateHolder = new MyStateHolder(project, changelistId); - Disposer.register(disposable, myStateHolder); - } - - @NotNull - private State getUserObjectState(@NotNull Object change) { - ExclusionState exclusionState = myStateHolder.getExclusionState(change); - return PartialChangesUtil.convertExclusionState(exclusionState); - } - - @NotNull - @Override - protected State getNodeStatus(@NotNull ChangesBrowserNode node) { - boolean hasIncluded = false; - boolean hasExcluded = false; - - for (Object change : VcsTreeModelData.children(node).userObjects()) { - ExclusionState exclusionState = myStateHolder.getExclusionState(change); - - if (exclusionState == ExclusionState.ALL_INCLUDED) { - hasIncluded = true; - } - else if (exclusionState == ExclusionState.ALL_EXCLUDED) { - hasExcluded = true; - } - else { - hasIncluded = true; - hasExcluded = true; - } - } - - if (hasIncluded && hasExcluded) return State.DONT_CARE; - if (hasIncluded) return State.SELECTED; - return State.NOT_SELECTED; - } - - @Override - public boolean isIncluded(@NotNull Object change) { - return myStateHolder.isIncluded(change); - } - - @NotNull - @Override - public Set getIncludedSet() { - return myStateHolder.getIncludedSet(); - } - - @Override - public void setIncludedChanges(@NotNull Collection changes) { - myStateHolder.setIncludedElements(changes); - } - - @Override - public void includeChanges(@NotNull Collection changes) { - myStateHolder.includeElements(changes); - } - - @Override - public void excludeChanges(@NotNull Collection changes) { - myStateHolder.excludeElements(changes); - } - - @Override - protected void toggleChanges(@NotNull Collection changes) { - myStateHolder.toggleElements(changes); - } - - public void setChangelistId(@NotNull String changelistId) { - myStateHolder.setChangelistId(changelistId); - } - - private class MyStateHolder extends PartiallyExcludedFilesStateHolder { - MyStateHolder(@NotNull Project project, @NotNull String changelistId) { - super(project, changelistId); - } - - @NotNull - @Override - protected Stream getTrackableElementsStream() { - return VcsTreeModelData.all(MyChangesBrowserTreeList.this).userObjectsStream(Change.class); - } - - @Nullable - @Override - protected Object findElementFor(@NotNull PartialLocalLineStatusTracker tracker) { - return getTrackableElementsStream().filter(change -> tracker.getVirtualFile().equals(PartialChangesUtil.getVirtualFile(change))).findFirst().orElse(null); - } - - @Nullable - @Override - protected PartialLocalLineStatusTracker findTrackerFor(@NotNull Object element) { - if (element instanceof Change) { - return PartialChangesUtil.getPartialTracker(myProject, (Change)element); - } - return null; - } - - @Override - public void updateExclusionStates() { - super.updateExclusionStates(); - - MyChangesBrowserTreeList.this.notifyInclusionListener(); - MyChangesBrowserTreeList.this.repaint(); - } - } - } } \ No newline at end of file diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/PartiallyExcludedFilesStateHolder.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/PartiallyExcludedFilesStateHolder.java index 94babbb939c1..0287fc730e06 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/PartiallyExcludedFilesStateHolder.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/ui/PartiallyExcludedFilesStateHolder.java @@ -1,4 +1,4 @@ -// 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. +// 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; @@ -170,12 +170,6 @@ public abstract class PartiallyExcludedFilesStateHolder implements Disposable } } - - public boolean isIncluded(@NotNull T element) { - ExclusionState trackerState = getExclusionState(element); - return trackerState != ExclusionState.ALL_EXCLUDED; - } - @NotNull public Set getIncludedSet() { HashSet set = new HashSet<>(myIncludedElements); @@ -234,22 +228,4 @@ public abstract class PartiallyExcludedFilesStateHolder implements Disposable updateExclusionStates(); } - - public void toggleElements(@NotNull Collection elements) { - boolean hasExcluded = false; - for (T element : elements) { - ExclusionState exclusionState = getExclusionState(element); - if (exclusionState != ExclusionState.ALL_INCLUDED) { - hasExcluded = true; - break; - } - } - - if (hasExcluded) { - includeElements(elements); - } - else { - excludeElements(elements); - } - } } diff --git a/platform/vcs-impl/src/com/intellij/vcs/commit/PartialCommitInclusionModel.kt b/platform/vcs-impl/src/com/intellij/vcs/commit/PartialCommitInclusionModel.kt new file mode 100644 index 000000000000..f91808c176b1 --- /dev/null +++ b/platform/vcs-impl/src/com/intellij/vcs/commit/PartialCommitInclusionModel.kt @@ -0,0 +1,67 @@ +// 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.vcs.commit + +import com.intellij.openapi.Disposable +import com.intellij.openapi.project.Project +import com.intellij.openapi.util.Disposer +import com.intellij.openapi.vcs.changes.Change +import com.intellij.openapi.vcs.changes.LocalChangeList +import com.intellij.openapi.vcs.changes.ui.BaseInclusionModel +import com.intellij.openapi.vcs.changes.ui.PartiallyExcludedFilesStateHolder +import com.intellij.openapi.vcs.ex.PartialLocalLineStatusTracker +import com.intellij.openapi.vcs.impl.PartialChangesUtil +import com.intellij.openapi.vcs.impl.PartialChangesUtil.convertExclusionState +import com.intellij.openapi.vcs.impl.PartialChangesUtil.getPartialTracker +import com.intellij.util.ui.ThreeStateCheckBox +import java.util.stream.Stream + +class PartialCommitInclusionModel( + private val project: Project, + changeList: LocalChangeList +) : BaseInclusionModel(), Disposable { + + var changeList: LocalChangeList = changeList + set(value) { + field = value + stateHolder.setChangelistId(value.id) + } + + private val stateHolder = StateHolder() + + init { + Disposer.register(this, stateHolder) + } + + override fun getInclusion(): Set = stateHolder.includedSet + override fun getInclusionState(item: Any): ThreeStateCheckBox.State = convertExclusionState(stateHolder.getExclusionState(item)) + override fun isInclusionEmpty(): Boolean = getInclusion().isEmpty() + + override fun addInclusion(items: Collection) = stateHolder.includeElements(items) + override fun removeInclusion(items: Collection) = stateHolder.excludeElements(items) + override fun setInclusion(items: Collection) = stateHolder.setIncludedElements(items) + override fun retainInclusion(items: Collection) { + val toRemove = getInclusion() - items + if (toRemove.isNotEmpty()) removeInclusion(toRemove) + } + + override fun clearInclusion() { + if (getInclusion().isNotEmpty()) setInclusion(emptySet()) + } + + override fun dispose() = Unit + + private inner class StateHolder : PartiallyExcludedFilesStateHolder(project, changeList.id) { + override fun getTrackableElementsStream(): Stream = changeList.changes.stream() + + override fun findElementFor(tracker: PartialLocalLineStatusTracker): Any? = + changeList.changes.find { tracker.virtualFile == PartialChangesUtil.getVirtualFile(it) } + + override fun findTrackerFor(element: Any): PartialLocalLineStatusTracker? = + (element as? Change)?.let { getPartialTracker(project, it) } + + override fun updateExclusionStates() { + super.updateExclusionStates() + fireInclusionChanged() + } + } +} \ No newline at end of file diff --git a/platform/vcs-tests/testSrc/com/intellij/openapi/vcs/BasePartiallyExcludedChangesTest.kt b/platform/vcs-tests/testSrc/com/intellij/openapi/vcs/BasePartiallyExcludedChangesTest.kt index 97d81518cd20..e3a638861f2a 100644 --- a/platform/vcs-tests/testSrc/com/intellij/openapi/vcs/BasePartiallyExcludedChangesTest.kt +++ b/platform/vcs-tests/testSrc/com/intellij/openapi/vcs/BasePartiallyExcludedChangesTest.kt @@ -1,4 +1,4 @@ -// 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. +// 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 import com.intellij.openapi.util.Disposer @@ -36,6 +36,11 @@ abstract class BasePartiallyExcludedChangesTest : BaseLineStatusTrackerManagerTe return PartialChangesUtil.getPartialTracker(getProject(), file) } + fun toggleElements(elements: Collection) { + val hasExcluded = elements.any { getExclusionState(it) != ExclusionState.ALL_INCLUDED } + if (hasExcluded) includeElements(elements) else excludeElements(elements) + } + fun waitExclusionStateUpdate() { myUpdateQueue.flush() }