From 55b8eada71b012b3df850ea7817abfce211a561f Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Sun, 14 Jul 2019 21:35:12 +0300 Subject: [PATCH] diff: extract LST-managed diff computation logic GitOrigin-RevId: e4b2003e7bf7cbbd7c919e1dfff5a93b2d3331c0 --- .../diff/tools/simple/SimpleDiffViewer.java | 50 +++-- .../actions/diff/lst/LocalTrackerDiffUtil.kt | 127 +++++++++++ .../lst/SimpleLocalChangeListDiffViewer.java | 200 ++++++------------ 3 files changed, 221 insertions(+), 156 deletions(-) create mode 100644 platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/diff/lst/LocalTrackerDiffUtil.kt diff --git a/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleDiffViewer.java b/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleDiffViewer.java index 9dbb5eb8c6cc..84b6cb5baed2 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleDiffViewer.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleDiffViewer.java @@ -185,28 +185,7 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer { @NotNull protected Runnable performRediff(@NotNull final ProgressIndicator indicator) { try { - indicator.checkCanceled(); - - final Document document1 = getContent1().getDocument(); - final Document document2 = getContent2().getDocument(); - - CharSequence[] texts = ReadAction.compute(() -> new CharSequence[]{document1.getImmutableCharSequence(), document2.getImmutableCharSequence()}); - - List lineFragments = myTextDiffProvider.compare(texts[0], texts[1], indicator); - - boolean isContentsEqual = (lineFragments == null || lineFragments.isEmpty()) && - StringUtil.equals(texts[0], texts[1]); - - if (lineFragments == null) { - return apply(null, isContentsEqual); - } - else { - List changes = new ArrayList<>(); - for (LineFragment fragment : lineFragments) { - changes.add(new SimpleDiffChange(changes.size(), fragment)); - } - return apply(changes, isContentsEqual); - } + return computeDifferences(indicator); } catch (DiffTooBigException e) { return applyNotification(DiffNotifications.createDiffTooBig()); @@ -220,6 +199,33 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer { } } + @NotNull + protected Runnable computeDifferences(@NotNull ProgressIndicator indicator) { + indicator.checkCanceled(); + + final Document document1 = getContent1().getDocument(); + final Document document2 = getContent2().getDocument(); + + CharSequence[] texts = + ReadAction.compute(() -> new CharSequence[]{document1.getImmutableCharSequence(), document2.getImmutableCharSequence()}); + + List lineFragments = myTextDiffProvider.compare(texts[0], texts[1], indicator); + + boolean isContentsEqual = (lineFragments == null || lineFragments.isEmpty()) && + StringUtil.equals(texts[0], texts[1]); + + if (lineFragments == null) { + return apply(null, isContentsEqual); + } + else { + List changes = new ArrayList<>(); + for (LineFragment fragment : lineFragments) { + changes.add(new SimpleDiffChange(changes.size(), fragment)); + } + return apply(changes, isContentsEqual); + } + } + @NotNull protected Runnable apply(@Nullable List changes, boolean isContentsEqual) { diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/diff/lst/LocalTrackerDiffUtil.kt b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/diff/lst/LocalTrackerDiffUtil.kt new file mode 100644 index 000000000000..825156e4a231 --- /dev/null +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/diff/lst/LocalTrackerDiffUtil.kt @@ -0,0 +1,127 @@ +// 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.actions.diff.lst + +import com.intellij.diff.fragments.LineFragment +import com.intellij.diff.tools.util.text.TwosideTextDiffProvider +import com.intellij.diff.util.Range +import com.intellij.openapi.application.runReadAction +import com.intellij.openapi.editor.Document +import com.intellij.openapi.progress.ProgressIndicator +import com.intellij.openapi.util.text.StringUtil +import com.intellij.openapi.vcs.ex.LineStatusTracker +import com.intellij.openapi.vcs.ex.LocalRange +import com.intellij.openapi.vcs.ex.PartialLocalLineStatusTracker +import com.intellij.openapi.vcs.ex.SimpleLocalLineStatusTracker + +object LocalTrackerDiffUtil { + @JvmStatic + fun computeDifferences(tracker: LineStatusTracker<*>?, + document1: Document, + document2: Document, + changelistId: String, + textDiffProvider: TwosideTextDiffProvider, + indicator: ProgressIndicator, + handler: LocalTrackerDiffHandler): Runnable { + if (tracker is SimpleLocalLineStatusTracker) { + // partial changes are disabled for file (ex: it is marked as "unmodified") + return handler.fallback() + } + + val partialTracker = tracker as? PartialLocalLineStatusTracker + if (partialTracker == null || document2 != tracker.document) { + return handler.error() // DiffRequest is out of date + } + + indicator.checkCanceled() + val data = runReadAction { + partialTracker.readLock { + val isReleased = partialTracker.isReleased + val isOperational = partialTracker.isOperational() + val affectedChangelistIds = partialTracker.getAffectedChangeListsIds() + + if (!isOperational) { + TrackerData(isReleased, affectedChangelistIds, null) + } + else { + val ranges = partialTracker.getRanges() + + val localText = document2.immutableCharSequence + val vcsText = document1.immutableCharSequence + val trackerVcsText = partialTracker.vcsDocument.immutableCharSequence + + val diffData = TrackerDiffData(ranges, localText, vcsText, trackerVcsText) + TrackerData(isReleased, affectedChangelistIds, diffData) + } + } + } + + if (data.isReleased) { + return handler.error() // DiffRequest is out of date + } + + val diffData = data.diffData + if (diffData?.ranges == null) { + if (data.affectedChangelist.size == 1 && data.affectedChangelist.contains(changelistId)) { + // tracker is waiting for initialisation + // there are only one changelist, so it's safe to fallback to default logic + return handler.fallbackWithProgress() + } + + return handler.retryLater() + } + + val ranges = diffData.ranges + val isContentsEqual = ranges.isEmpty() + + if (!StringUtil.equals(diffData.vcsText, diffData.trackerVcsText)) { + return handler.error() // DiffRequest is out of date + } + + if (textDiffProvider.isHighlightingDisabled) { + return handler.done(isContentsEqual, emptyList(), emptyList()) + } + + + val linesRanges = ranges.map { range -> Range(range.vcsLine1, range.vcsLine2, range.line1, range.line2) } + + val newFragments = textDiffProvider.compare(diffData.vcsText, diffData.localText, linesRanges, indicator)!! + + val fragments = mutableListOf() + val fragmentsData = mutableListOf() + + for (i in ranges.indices) { + val localRange = ranges[i] + val rangeFragments = newFragments[i] + + fragments.addAll(rangeFragments) + + val fragmentData = LineFragmentData(localRange.isExcludedFromCommit, localRange.changelistId) + repeat(rangeFragments.size) { fragmentsData.add(fragmentData) } + } + + return handler.done(isContentsEqual, fragments, fragmentsData) + } + + interface LocalTrackerDiffHandler { + fun done(isContentsEqual: Boolean, + fragments: List, + fragmentsData: List): Runnable + + fun retryLater(): Runnable + fun fallback(): Runnable + fun fallbackWithProgress(): Runnable + fun error(): Runnable + } + + data class LineFragmentData(val isExcluded: Boolean, + val changelistId: String) + + private data class TrackerData(val isReleased: Boolean, + val affectedChangelist: List, + val diffData: TrackerDiffData?) + + private data class TrackerDiffData(val ranges: List?, + val localText: CharSequence, + val vcsText: CharSequence, + val trackerVcsText: CharSequence) +} diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/diff/lst/SimpleLocalChangeListDiffViewer.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/diff/lst/SimpleLocalChangeListDiffViewer.java index 0b69095807e5..6b18a500543d 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/diff/lst/SimpleLocalChangeListDiffViewer.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/diff/lst/SimpleLocalChangeListDiffViewer.java @@ -2,7 +2,6 @@ package com.intellij.openapi.vcs.changes.actions.diff.lst; import com.intellij.diff.DiffContext; -import com.intellij.diff.comparison.DiffTooBigException; import com.intellij.diff.fragments.LineFragment; import com.intellij.diff.tools.simple.SimpleDiffChange; import com.intellij.diff.tools.simple.SimpleDiffChangeUi; @@ -10,7 +9,6 @@ import com.intellij.diff.tools.simple.SimpleDiffViewer; import com.intellij.diff.tools.util.DiffNotifications; import com.intellij.diff.util.DiffGutterRenderer; import com.intellij.diff.util.DiffUtil; -import com.intellij.diff.util.Range; import com.intellij.diff.util.Side; import com.intellij.icons.AllIcons; import com.intellij.idea.ActionsBundle; @@ -21,7 +19,6 @@ import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.ex.ActionUtil; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ModalityState; -import com.intellij.openapi.application.ReadAction; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.ex.EditorGutterComponentEx; import com.intellij.openapi.editor.markup.GutterIconRenderer; @@ -50,8 +47,6 @@ import java.awt.event.ComponentEvent; import java.util.List; import java.util.*; -import static com.intellij.util.ObjectUtils.notNull; - public class SimpleLocalChangeListDiffViewer extends SimpleDiffViewer { @NotNull private final LocalChangeListDiffRequest myLocalRequest; @NotNull private final String myChangelistId; @@ -122,117 +117,85 @@ public class SimpleLocalChangeListDiffViewer extends SimpleDiffViewer { return super.createUi(change); } - @Override @NotNull - protected Runnable performRediff(@NotNull final ProgressIndicator indicator) { - LineStatusTracker tracker = myLocalRequest.getLineStatusTracker(); - if (tracker instanceof SimpleLocalLineStatusTracker) { - // partial changes are disabled for file (ex: it is marked as "unmodified") - return super.performRediff(indicator); - } - - PartialLocalLineStatusTracker partialTracker = ObjectUtils.tryCast(tracker, PartialLocalLineStatusTracker.class); - if (partialTracker == null || getContent2().getDocument() != tracker.getDocument()) { - return applyNotification(DiffNotifications.createError()); // DiffRequest is out of date - } - - try { - indicator.checkCanceled(); - - TrackerData data = ReadAction.compute(() -> partialTracker.readLock(() -> { - boolean isReleased = partialTracker.isReleased(); - boolean isOperational = partialTracker.isOperational(); - List affectedChangelistIds = partialTracker.getAffectedChangeListsIds(); - - if (!isOperational) { - return new TrackerData(isReleased, affectedChangelistIds, null); - } - - List ranges = partialTracker.getRanges(); - - CharSequence localText = getContent2().getDocument().getImmutableCharSequence(); - CharSequence vcsText = getContent1().getDocument().getImmutableCharSequence(); - CharSequence trackerVcsText = partialTracker.getVcsDocument().getImmutableCharSequence(); - - TrackerDiffData diffData = new TrackerDiffData(ranges, localText, vcsText, trackerVcsText); - return new TrackerData(isReleased, affectedChangelistIds, diffData); - })); - - - if (data.isReleased) { - return applyNotification(DiffNotifications.createError()); // DiffRequest is out of date - } - - TrackerDiffData diffData = data.diffData; - - if (diffData == null || diffData.ranges == null) { - if (data.affectedChangelist.size() == 1 && - data.affectedChangelist.contains(myChangelistId)) { - // tracker is waiting for initialisation - // there are only one changelist, so it's safe to fallback to default logic - Runnable callback = super.performRediff(indicator); - return () -> { - callback.run(); - getStatusPanel().setBusy(true); - }; - } - - scheduleRediff(); - throw new ProcessCanceledException(); - } - - return performRediffUsingPartialTracker(diffData.ranges, diffData.localText, diffData.vcsText, diffData.trackerVcsText, indicator); - } - catch (DiffTooBigException e) { - return applyNotification(DiffNotifications.createDiffTooBig()); - } - catch (ProcessCanceledException e) { - throw e; - } - catch (Throwable e) { - LOG.error(e); - return applyNotification(DiffNotifications.createError()); - } + private Runnable superComputeDifferences(@NotNull ProgressIndicator indicator) { + return super.computeDifferences(indicator); } + @Override @NotNull - private Runnable performRediffUsingPartialTracker(@NotNull List ranges, - @NotNull CharSequence localText, - @NotNull CharSequence vcsText, - @NotNull CharSequence trackerVcsText, - @NotNull ProgressIndicator indicator) { - if (!StringUtil.equals(trackerVcsText, vcsText)) { - return applyNotification(DiffNotifications.createError()); // DiffRequest is out of date + protected Runnable computeDifferences(@NotNull ProgressIndicator indicator) { + return LocalTrackerDiffUtil.computeDifferences( + myLocalRequest.getLineStatusTracker(), + getContent1().getDocument(), + getContent2().getDocument(), + myChangelistId, + myTextDiffProvider, + indicator, + new MyLocalTrackerDiffHandler(indicator) + ); + } + + private class MyLocalTrackerDiffHandler implements LocalTrackerDiffUtil.LocalTrackerDiffHandler { + @NotNull private final ProgressIndicator myIndicator; + + private MyLocalTrackerDiffHandler(@NotNull ProgressIndicator indicator) { + myIndicator = indicator; } - if (myTextDiffProvider.isHighlightingDisabled()) { - return apply(null, ranges.isEmpty()); - } + @NotNull + @Override + public Runnable done(boolean isContentsEqual, + @NotNull List fragments, + @NotNull List fragmentsData) { + List changes = new ArrayList<>(); + for (int i = 0; i < fragments.size(); i++) { + LineFragment fragment = fragments.get(i); + LocalTrackerDiffUtil.LineFragmentData data = fragmentsData.get(i); - List linesRanges = ContainerUtil.map(ranges, range -> new Range(range.getVcsLine1(), range.getVcsLine2(), range.getLine1(), range.getLine2())); + boolean isExcludedFromCommit = data.isExcluded(); + boolean isFromActiveChangelist = data.getChangelistId().equals(myChangelistId); + boolean isSkipped = !isFromActiveChangelist; + boolean isExcluded = !isFromActiveChangelist || + (myAllowExcludeChangesFromCommit && isExcludedFromCommit); - List> newFragments = notNull(myTextDiffProvider.compare(vcsText, localText, linesRanges, indicator)); - - boolean isContentsEqual = ranges.isEmpty(); - List changes = new ArrayList<>(); - - for (int i = 0; i < ranges.size(); i++) { - LocalRange localRange = ranges.get(i); - List rangeFragments = newFragments.get(i); - - boolean isExcludedFromCommit = localRange.isExcludedFromCommit(); - boolean isFromActiveChangelist = localRange.getChangelistId().equals(myChangelistId); - boolean isSkipped = !isFromActiveChangelist; - boolean isExcluded = !isFromActiveChangelist || (myAllowExcludeChangesFromCommit && isExcludedFromCommit); - - for (LineFragment fragment : rangeFragments) { changes.add(new MySimpleDiffChange(changes.size(), fragment, isExcluded, isSkipped, - localRange.getChangelistId(), isFromActiveChangelist, isExcludedFromCommit)); + data.getChangelistId(), isFromActiveChangelist, + isExcludedFromCommit)); } + + return apply(changes, isContentsEqual); } - return apply(changes, isContentsEqual); + @NotNull + @Override + public Runnable retryLater() { + scheduleRediff(); + throw new ProcessCanceledException(); + } + + @NotNull + @Override + public Runnable fallback() { + return superComputeDifferences(myIndicator); + } + + @NotNull + @Override + public Runnable fallbackWithProgress() { + Runnable callback = superComputeDifferences(myIndicator); + return () -> { + callback.run(); + getStatusPanel().setBusy(true); + }; + } + + @NotNull + @Override + public Runnable error() { + return applyNotification(DiffNotifications.createError()); + } } @Override @@ -606,35 +569,4 @@ public class SimpleLocalChangeListDiffViewer extends SimpleDiffViewer { } } } - - private static class TrackerData { - private final boolean isReleased; - @NotNull public final List affectedChangelist; - @Nullable public final TrackerDiffData diffData; - - TrackerData(boolean isReleased, - @NotNull List affectedChangelist, - @Nullable TrackerDiffData diffData) { - this.isReleased = isReleased; - this.affectedChangelist = affectedChangelist; - this.diffData = diffData; - } - } - - private static class TrackerDiffData { - @Nullable public final List ranges; - @NotNull public final CharSequence localText; - @NotNull public final CharSequence vcsText; - @NotNull public final CharSequence trackerVcsText; - - TrackerDiffData(@Nullable List ranges, - @NotNull CharSequence localText, - @NotNull CharSequence vcsText, - @NotNull CharSequence trackerVcsText) { - this.ranges = ranges; - this.localText = localText; - this.vcsText = vcsText; - this.trackerVcsText = trackerVcsText; - } - } }