From b0f96c60a43206102f916dab8d2b35c7baf3e0e4 Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Thu, 19 Jan 2017 20:29:37 +0300 Subject: [PATCH] diff: extract diff computation logic --- .../intellij/diff/merge/TextMergeViewer.java | 73 +++----- .../tools/fragmented/UnifiedDiffViewer.java | 69 ++----- .../diff/tools/simple/SimpleDiffViewer.java | 56 ++---- .../tools/simple/SimpleOnesideDiffViewer.java | 40 +---- .../simple/SimpleThreesideDiffViewer.java | 147 ++------------- .../simple/ThreesideTextDiffViewerEx.java | 2 +- .../tools/util/base/TextDiffViewerUtil.java | 168 ++++++++++++------ .../util/text/FineMergeLineFragment.java | 29 +++ .../util/text/FineMergeLineFragmentImpl.java | 47 +++++ .../util/text/MergeInnerDifferences.java | 5 +- .../util/text/SimpleTextDiffProvider.java | 108 +++++++++++ .../text/SimpleThreesideTextDiffProvider.java | 98 ++++++++++ .../tools/util/text/TextDiffProvider.java | 29 +++ .../tools/util/text/TextDiffProviderBase.java | 72 ++++++++ .../util/text/TwosideTextDiffProvider.java | 43 +++++ .../src/com/intellij/diff/util/DiffUtil.java | 61 ++----- 16 files changed, 632 insertions(+), 415 deletions(-) create mode 100644 platform/diff-impl/src/com/intellij/diff/tools/util/text/FineMergeLineFragment.java create mode 100644 platform/diff-impl/src/com/intellij/diff/tools/util/text/FineMergeLineFragmentImpl.java create mode 100644 platform/diff-impl/src/com/intellij/diff/tools/util/text/SimpleTextDiffProvider.java create mode 100644 platform/diff-impl/src/com/intellij/diff/tools/util/text/SimpleThreesideTextDiffProvider.java create mode 100644 platform/diff-impl/src/com/intellij/diff/tools/util/text/TextDiffProvider.java create mode 100644 platform/diff-impl/src/com/intellij/diff/tools/util/text/TextDiffProviderBase.java create mode 100644 platform/diff-impl/src/com/intellij/diff/tools/util/text/TwosideTextDiffProvider.java diff --git a/platform/diff-impl/src/com/intellij/diff/merge/TextMergeViewer.java b/platform/diff-impl/src/com/intellij/diff/merge/TextMergeViewer.java index ab01ceae3ecb..b2f893b138ce 100644 --- a/platform/diff-impl/src/com/intellij/diff/merge/TextMergeViewer.java +++ b/platform/diff-impl/src/com/intellij/diff/merge/TextMergeViewer.java @@ -29,13 +29,15 @@ import com.intellij.diff.contents.DocumentContent; import com.intellij.diff.fragments.MergeLineFragment; import com.intellij.diff.requests.ContentDiffRequest; import com.intellij.diff.requests.SimpleDiffRequest; -import com.intellij.diff.tools.util.text.MergeInnerDifferences; import com.intellij.diff.tools.simple.ThreesideTextDiffViewerEx; import com.intellij.diff.tools.util.DiffNotifications; import com.intellij.diff.tools.util.KeyboardModifierListener; import com.intellij.diff.tools.util.base.HighlightPolicy; +import com.intellij.diff.tools.util.base.IgnorePolicy; import com.intellij.diff.tools.util.base.TextDiffViewerUtil; import com.intellij.diff.tools.util.text.LineOffsets; +import com.intellij.diff.tools.util.text.MergeInnerDifferences; +import com.intellij.diff.tools.util.text.TextDiffProviderBase; import com.intellij.diff.util.*; import com.intellij.icons.AllIcons; import com.intellij.openapi.actionSystem.*; @@ -180,6 +182,8 @@ public class TextMergeViewer implements MergeTool.MergeViewer { @NotNull private final MyInnerDiffWorker myInnerDiffWorker; @NotNull private final MyLineStatusTracker myLineStatusTracker; + @NotNull private final TextDiffProviderBase myTextDiffProvider; + // all changes - both applied and unapplied ones @NotNull private final List myAllMergeChanges = new ArrayList<>(); @@ -197,6 +201,11 @@ public class TextMergeViewer implements MergeTool.MergeViewer { myLineStatusTracker = new MyLineStatusTracker(getProject(), getEditor().getDocument()); + myTextDiffProvider = new TextDiffProviderBase(getTextSettings(), + myInnerDiffWorker::onSettingsChanged, + new IgnorePolicy[]{IgnorePolicy.DEFAULT}, + new HighlightPolicy[]{HighlightPolicy.BY_LINE, HighlightPolicy.BY_WORD}); + DiffUtil.registerAction(new ApplySelectedChangesAction(Side.LEFT, true), myPanel); DiffUtil.registerAction(new ApplySelectedChangesAction(Side.RIGHT, true), myPanel); DiffUtil.registerAction(new IgnoreSelectedChangesSideAction(Side.LEFT, true), myPanel); @@ -225,7 +234,7 @@ public class TextMergeViewer implements MergeTool.MergeViewer { protected List createToolbarActions() { List group = new ArrayList<>(); - group.add(new MyHighlightPolicySettingAction()); + group.addAll(myTextDiffProvider.getToolbarActions()); group.add(new MyToggleAutoScrollAction()); group.add(myEditorSettingsAction); @@ -271,6 +280,7 @@ public class TextMergeViewer implements MergeTool.MergeViewer { protected List createPopupActions() { List group = new ArrayList<>(); + group.addAll(myTextDiffProvider.getPopupActions()); group.add(Separator.getInstance()); group.add(new MyToggleAutoScrollAction()); @@ -503,7 +513,7 @@ public class TextMergeViewer implements MergeTool.MergeViewer { @CalledInAwt public void onSettingsChanged() { - boolean enabled = getHighlightPolicy() == HighlightPolicy.BY_WORD; + boolean enabled = myTextDiffProvider.getHighlightPolicy() == HighlightPolicy.BY_WORD; if (myEnabled == enabled) return; myEnabled = enabled; @@ -632,14 +642,6 @@ public class TextMergeViewer implements MergeTool.MergeViewer { } } - @NotNull - private HighlightPolicy getHighlightPolicy() { - HighlightPolicy policy = getTextSettings().getHighlightPolicy(); - if (policy == HighlightPolicy.BY_WORD_SPLIT) return HighlightPolicy.BY_WORD; - if (policy == HighlightPolicy.DO_NOT_HIGHLIGHT) return HighlightPolicy.BY_LINE; - return policy; - } - // // Getters // @@ -893,29 +895,6 @@ public class TextMergeViewer implements MergeTool.MergeViewer { if (firstConflict != null) doScrollToChange(firstConflict, true); } - private class MyHighlightPolicySettingAction extends TextDiffViewerUtil.HighlightPolicySettingAction { - public MyHighlightPolicySettingAction() { - super(getTextSettings()); - } - - @NotNull - @Override - protected HighlightPolicy getCurrentSetting() { - return getHighlightPolicy(); - } - - @NotNull - @Override - protected List getAvailableSettings() { - return ContainerUtil.list(HighlightPolicy.BY_LINE, HighlightPolicy.BY_WORD); - } - - @Override - protected void onSettingsChanged() { - myInnerDiffWorker.onSettingsChanged(); - } - } - private abstract class ApplySelectedChangesActionBase extends AnAction implements DumbAware { private final boolean myShortcut; @@ -1410,21 +1389,21 @@ public class TextMergeViewer implements MergeTool.MergeViewer { @NotNull public final List text; public InnerChunkData(@NotNull TextMergeChange change, @NotNull List documents) { - text = ThreeSide.map(side -> { - if (!change.isChange(side) || change.isResolved(side)) return null; - return getChunkContent(change, documents, side); - }); + text = getChunks(change, documents); } - @Nullable - @CalledWithReadLock - private static CharSequence getChunkContent(@NotNull TextMergeChange change, - @NotNull List documents, - @NotNull ThreeSide side) { - int startLine = change.getStartLine(side); - int endLine = change.getEndLine(side); - if (startLine == endLine) return null; - return DiffUtil.getLinesContent(side.select(documents), startLine, endLine); + @NotNull + private static List getChunks(@NotNull TextMergeChange change, + @NotNull List documents) { + return ThreeSide.map(side -> { + if (!change.isChange(side) || change.isResolved(side)) return null; + + int startLine = change.getStartLine(side); + int endLine = change.getEndLine(side); + if (startLine == endLine) return null; + + return DiffUtil.getLinesContent(side.select(documents), startLine, endLine); + }); } } } diff --git a/platform/diff-impl/src/com/intellij/diff/tools/fragmented/UnifiedDiffViewer.java b/platform/diff-impl/src/com/intellij/diff/tools/fragmented/UnifiedDiffViewer.java index 9928ead710ba..9a727b4bc03e 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/fragmented/UnifiedDiffViewer.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/fragmented/UnifiedDiffViewer.java @@ -26,9 +26,12 @@ import com.intellij.diff.fragments.LineFragment; import com.intellij.diff.requests.ContentDiffRequest; import com.intellij.diff.requests.DiffRequest; import com.intellij.diff.tools.util.*; -import com.intellij.diff.tools.util.base.*; +import com.intellij.diff.tools.util.base.InitialScrollPositionSupport; +import com.intellij.diff.tools.util.base.ListenerDiffViewerBase; import com.intellij.diff.tools.util.base.TextDiffSettingsHolder.TextDiffSettings; +import com.intellij.diff.tools.util.base.TextDiffViewerUtil; import com.intellij.diff.tools.util.side.TwosideTextDiffViewer; +import com.intellij.diff.tools.util.text.TwosideTextDiffProvider; import com.intellij.diff.util.*; import com.intellij.diff.util.DiffUserDataKeysEx.ScrollToPolicy; import com.intellij.icons.AllIcons; @@ -77,6 +80,8 @@ public class UnifiedDiffViewer extends ListenerDiffViewerBase { @NotNull private final MyInitialScrollHelper myInitialScrollHelper = new MyInitialScrollHelper(); @NotNull private final MyFoldingModel myFoldingModel; + @NotNull private final TwosideTextDiffProvider.NoIgnore myTextDiffProvider; + @NotNull protected Side myMasterSide = Side.RIGHT; @Nullable private ChangedBlockData myChangedBlockData; @@ -117,6 +122,8 @@ public class UnifiedDiffViewer extends ListenerDiffViewerBase { myEditorSettingsAction = new SetEditorSettingsAction(getTextSettings(), getEditors()); myEditorSettingsAction.applyDefaults(); + myTextDiffProvider = DiffUtil.createNoIgnoreTextDiffProvider(getRequest(), getTextSettings(), this::rediff); + new MyOpenInEditorWithMouseAction().install(getEditors()); TextDiffViewerUtil.checkDifferentDocuments(myRequest); @@ -184,9 +191,7 @@ public class UnifiedDiffViewer extends ListenerDiffViewerBase { public List createToolbarActions() { List group = new ArrayList<>(); - // TODO: allow to choose myMasterSide - group.add(new MyIgnorePolicySettingAction()); - group.add(new MyHighlightPolicySettingAction()); + group.addAll(myTextDiffProvider.getToolbarActions()); group.add(new MyToggleExpandByDefaultAction()); group.add(new MyReadOnlyLockAction()); group.add(myEditorSettingsAction); @@ -203,11 +208,7 @@ public class UnifiedDiffViewer extends ListenerDiffViewerBase { public List createPopupActions() { List group = new ArrayList<>(); - group.add(Separator.getInstance()); - group.add(new MyIgnorePolicySettingAction().getPopupGroup()); - group.add(Separator.getInstance()); - group.add(new MyHighlightPolicySettingAction().getPopupGroup()); - group.add(Separator.getInstance()); + group.addAll(myTextDiffProvider.getPopupActions()); group.add(new MyToggleExpandByDefaultAction()); group.add(Separator.getInstance()); @@ -260,7 +261,7 @@ public class UnifiedDiffViewer extends ListenerDiffViewerBase { return new CharSequence[]{document1.getImmutableCharSequence(), document2.getImmutableCharSequence()}; }); - final List fragments = DiffUtil.compare(myRequest, texts[0], texts[1], getDiffConfig(), indicator); + final List fragments = myTextDiffProvider.compare(texts[0], texts[1], indicator); final DocumentContent content1 = getContent1(); final DocumentContent content2 = getContent2(); @@ -788,18 +789,6 @@ public class UnifiedDiffViewer extends ListenerDiffViewerBase { return TextDiffViewerUtil.getFoldingModelSettings(myContext); } - @NotNull - private DiffUtil.DiffConfig getDiffConfig() { - return new DiffUtil.DiffConfig(getTextSettings().getIgnorePolicy(), getHighlightPolicy()); - } - - @NotNull - private HighlightPolicy getHighlightPolicy() { - HighlightPolicy policy = getTextSettings().getHighlightPolicy(); - if (policy == HighlightPolicy.DO_NOT_HIGHLIGHT) return HighlightPolicy.BY_LINE; - return policy; - } - // // Getters // @@ -995,42 +984,6 @@ public class UnifiedDiffViewer extends ListenerDiffViewerBase { } } - private class MyHighlightPolicySettingAction extends TextDiffViewerUtil.HighlightPolicySettingAction { - public MyHighlightPolicySettingAction() { - super(getTextSettings()); - } - - @NotNull - @Override - protected HighlightPolicy getCurrentSetting() { - return getHighlightPolicy(); - } - - @NotNull - @Override - protected List getAvailableSettings() { - ArrayList settings = ContainerUtil.newArrayList(HighlightPolicy.values()); - settings.remove(HighlightPolicy.DO_NOT_HIGHLIGHT); - return settings; - } - - @Override - protected void onSettingsChanged() { - rediff(); - } - } - - private class MyIgnorePolicySettingAction extends TextDiffViewerUtil.IgnorePolicySettingAction { - public MyIgnorePolicySettingAction() { - super(getTextSettings()); - } - - @Override - protected void onSettingsChanged() { - rediff(); - } - } - private class MyReadOnlyLockAction extends TextDiffViewerUtil.ReadOnlyLockAction { public MyReadOnlyLockAction() { super(getContext()); 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 3c8a524c68fe..a4f3e08144e8 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 @@ -23,9 +23,9 @@ import com.intellij.diff.fragments.LineFragment; import com.intellij.diff.requests.ContentDiffRequest; import com.intellij.diff.requests.DiffRequest; import com.intellij.diff.tools.util.*; -import com.intellij.diff.tools.util.base.HighlightPolicy; import com.intellij.diff.tools.util.base.TextDiffViewerUtil; import com.intellij.diff.tools.util.side.TwosideTextDiffViewer; +import com.intellij.diff.tools.util.text.TwosideTextDiffProvider; import com.intellij.diff.util.*; import com.intellij.diff.util.DiffUserDataKeysEx.ScrollToPolicy; import com.intellij.openapi.Disposable; @@ -68,6 +68,8 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer { @NotNull private final MyInitialScrollHelper myInitialScrollHelper = new MyInitialScrollHelper(); @NotNull private final ModifierProvider myModifierProvider; + @NotNull private final TwosideTextDiffProvider myTextDiffProvider; + public SimpleDiffViewer(@NotNull DiffContext context, @NotNull DiffRequest request) { super(context, (ContentDiffRequest)request); @@ -78,6 +80,8 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer { myModifierProvider = new ModifierProvider(); + myTextDiffProvider = DiffUtil.createTextDiffProvider(getRequest(), getTextSettings(), this::rediff); + DiffUtil.registerAction(new ReplaceSelectedChangesAction(Side.LEFT, true), myPanel); DiffUtil.registerAction(new AppendSelectedChangesAction(Side.LEFT, true), myPanel); DiffUtil.registerAction(new ReplaceSelectedChangesAction(Side.RIGHT, true), myPanel); @@ -105,8 +109,7 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer { protected List createToolbarActions() { List group = new ArrayList<>(); - group.add(new MyIgnorePolicySettingAction()); - group.add(new MyHighlightPolicySettingAction()); + group.addAll(myTextDiffProvider.getToolbarActions()); group.add(new MyToggleExpandByDefaultAction()); group.add(new MyToggleAutoScrollAction()); group.add(new MyReadOnlyLockAction()); @@ -123,11 +126,7 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer { protected List createPopupActions() { List group = new ArrayList<>(); - group.add(Separator.getInstance()); - group.add(new MyIgnorePolicySettingAction().getPopupGroup()); - group.add(Separator.getInstance()); - group.add(new MyHighlightPolicySettingAction().getPopupGroup()); - group.add(Separator.getInstance()); + group.addAll(myTextDiffProvider.getPopupActions()); group.add(new MyToggleAutoScrollAction()); group.add(new MyToggleExpandByDefaultAction()); @@ -197,10 +196,7 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer { return new CharSequence[]{document1.getImmutableCharSequence(), document2.getImmutableCharSequence()}; }); - List lineFragments = null; - if (getHighlightPolicy().isShouldCompare()) { - lineFragments = DiffUtil.compare(myRequest, texts[0], texts[1], getDiffConfig(), indicator); - } + List lineFragments = myTextDiffProvider.compare(texts[0], texts[1], indicator); boolean isContentsEqual = (lineFragments == null || lineFragments.isEmpty()) && StringUtil.equals(texts[0], texts[1]); @@ -267,16 +263,6 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer { destroyChangedBlocks(); } - @NotNull - private DiffUtil.DiffConfig getDiffConfig() { - return new DiffUtil.DiffConfig(getTextSettings().getIgnorePolicy(), getHighlightPolicy()); - } - - @NotNull - private HighlightPolicy getHighlightPolicy() { - return getTextSettings().getHighlightPolicy(); - } - // // Impl // @@ -662,28 +648,6 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer { myDiffChanges.remove(change); } - private class MyHighlightPolicySettingAction extends TextDiffViewerUtil.HighlightPolicySettingAction { - public MyHighlightPolicySettingAction() { - super(getTextSettings()); - } - - @Override - protected void onSettingsChanged() { - rediff(); - } - } - - private class MyIgnorePolicySettingAction extends TextDiffViewerUtil.IgnorePolicySettingAction { - public MyIgnorePolicySettingAction() { - super(getTextSettings()); - } - - @Override - protected void onSettingsChanged() { - rediff(); - } - } - private class MyToggleExpandByDefaultAction extends TextDiffViewerUtil.ToggleExpandByDefaultAction { public MyToggleExpandByDefaultAction() { super(getTextSettings()); @@ -808,7 +772,9 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer { @Nullable @Override protected String getMessage() { - if (getHighlightPolicy() == HighlightPolicy.DO_NOT_HIGHLIGHT) return DiffBundle.message("diff.highlighting.disabled.text"); + if (myTextDiffProvider.isHighlightingDisabled()) { + return DiffBundle.message("diff.highlighting.disabled.text"); + } int changesCount = myDiffChanges.size() + myInvalidDiffChanges.size(); if (changesCount == 0 && !myIsContentsEqual) { return DiffBundle.message("diff.all.differences.ignored.text"); diff --git a/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleOnesideDiffViewer.java b/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleOnesideDiffViewer.java index bf1c0bfba4c4..dbf05e00e8cc 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleOnesideDiffViewer.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleOnesideDiffViewer.java @@ -24,6 +24,7 @@ import com.intellij.diff.tools.util.DiffDataKeys; import com.intellij.diff.tools.util.base.HighlightPolicy; import com.intellij.diff.tools.util.base.TextDiffViewerUtil; import com.intellij.diff.tools.util.side.OnesideTextDiffViewer; +import com.intellij.diff.tools.util.text.TextDiffProvider; import com.intellij.diff.util.DiffDrawUtil; import com.intellij.diff.util.DiffUtil; import com.intellij.diff.util.LineRange; @@ -48,10 +49,14 @@ import static com.intellij.diff.util.DiffUtil.getLineCount; public class SimpleOnesideDiffViewer extends OnesideTextDiffViewer { @NotNull private final MyInitialScrollHelper myInitialScrollHelper = new MyInitialScrollHelper(); + @NotNull private final TextDiffProvider myTextDiffProvider; + @NotNull private final List myHighlighters = new ArrayList<>(); public SimpleOnesideDiffViewer(@NotNull DiffContext context, @NotNull DiffRequest request) { super(context, (ContentDiffRequest)request); + + myTextDiffProvider = DiffUtil.createTextDiffProvider(getRequest(), getTextSettings(), this::rediff); } @Override @@ -69,8 +74,7 @@ public class SimpleOnesideDiffViewer extends OnesideTextDiffViewer { protected List createToolbarActions() { List group = new ArrayList<>(); - group.add(new MyIgnorePolicySettingAction()); - group.add(new MyHighlightPolicySettingAction()); + group.addAll(myTextDiffProvider.getToolbarActions()); group.add(new MyReadOnlyLockAction()); group.add(myEditorSettingsAction); @@ -85,11 +89,7 @@ public class SimpleOnesideDiffViewer extends OnesideTextDiffViewer { protected List createPopupActions() { List group = new ArrayList<>(); - group.add(Separator.getInstance()); - group.add(new MyIgnorePolicySettingAction().getPopupGroup()); - group.add(Separator.getInstance()); - group.add(new MyHighlightPolicySettingAction().getPopupGroup()); - + group.addAll(myTextDiffProvider.getPopupActions()); group.add(Separator.getInstance()); group.addAll(super.createPopupActions()); @@ -183,32 +183,6 @@ public class SimpleOnesideDiffViewer extends OnesideTextDiffViewer { } } - // - // Modification operations - // - - private class MyHighlightPolicySettingAction extends TextDiffViewerUtil.HighlightPolicySettingAction { - public MyHighlightPolicySettingAction() { - super(getTextSettings()); - } - - @Override - protected void onSettingsChanged() { - rediff(); - } - } - - private class MyIgnorePolicySettingAction extends TextDiffViewerUtil.IgnorePolicySettingAction { - public MyIgnorePolicySettingAction() { - super(getTextSettings()); - } - - @Override - protected void onSettingsChanged() { - rediff(); - } - } - // // Helpers // diff --git a/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleThreesideDiffViewer.java b/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleThreesideDiffViewer.java index c84dd386377e..c56f9631da6e 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleThreesideDiffViewer.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleThreesideDiffViewer.java @@ -16,24 +16,18 @@ package com.intellij.diff.tools.simple; import com.intellij.diff.DiffContext; -import com.intellij.diff.comparison.ComparisonManager; -import com.intellij.diff.comparison.ComparisonPolicy; import com.intellij.diff.comparison.DiffTooBigException; -import com.intellij.diff.contents.DocumentContent; -import com.intellij.diff.fragments.MergeLineFragment; import com.intellij.diff.requests.ContentDiffRequest; import com.intellij.diff.requests.DiffRequest; import com.intellij.diff.tools.util.DiffNotifications; -import com.intellij.diff.tools.util.base.HighlightPolicy; -import com.intellij.diff.tools.util.base.IgnorePolicy; import com.intellij.diff.tools.util.base.TextDiffViewerUtil; import com.intellij.diff.tools.util.side.ThreesideTextDiffViewer; -import com.intellij.diff.tools.util.text.LineOffsets; +import com.intellij.diff.tools.util.text.FineMergeLineFragment; import com.intellij.diff.tools.util.text.MergeInnerDifferences; +import com.intellij.diff.tools.util.text.SimpleThreesideTextDiffProvider; import com.intellij.diff.util.*; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.Separator; -import com.intellij.openapi.application.ReadAction; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.event.DocumentEvent; @@ -42,17 +36,20 @@ import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.CalledInAwt; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; public class SimpleThreesideDiffViewer extends ThreesideTextDiffViewerEx { + @NotNull private final SimpleThreesideTextDiffProvider myTextDiffProvider; + @NotNull private final List myDiffChanges = new ArrayList<>(); @NotNull private final List myInvalidDiffChanges = new ArrayList<>(); public SimpleThreesideDiffViewer(@NotNull DiffContext context, @NotNull DiffRequest request) { super(context, (ContentDiffRequest)request); + + myTextDiffProvider = new SimpleThreesideTextDiffProvider(getTextSettings(), this::rediff); } @NotNull @@ -60,8 +57,7 @@ public class SimpleThreesideDiffViewer extends ThreesideTextDiffViewerEx { protected List createToolbarActions() { List group = new ArrayList<>(); - group.add(new MyIgnorePolicySettingAction()); - group.add(new MyHighlightPolicySettingAction()); + group.addAll(myTextDiffProvider.getToolbarActions()); group.add(new MyToggleExpandByDefaultAction()); group.add(new MyToggleAutoScrollAction()); group.add(new MyEditorReadOnlyLockAction()); @@ -83,11 +79,7 @@ public class SimpleThreesideDiffViewer extends ThreesideTextDiffViewerEx { protected List createPopupActions() { List group = new ArrayList<>(); - group.add(Separator.getInstance()); - group.add(new MyIgnorePolicySettingAction().getPopupGroup()); - //group.add(Separator.getInstance()); - //group.add(new MyHighlightPolicySettingAction().getPopupGroup()); - group.add(Separator.getInstance()); + group.addAll(myTextDiffProvider.getPopupActions()); group.add(new MyToggleAutoScrollAction()); group.add(new MyToggleExpandByDefaultAction()); @@ -114,42 +106,14 @@ public class SimpleThreesideDiffViewer extends ThreesideTextDiffViewerEx { try { indicator.checkCanceled(); - List contents = getContents(); - List sequences = ReadAction.compute(() -> { - indicator.checkCanceled(); - return ContainerUtil.map(contents, content -> content.getDocument().getImmutableCharSequence()); - }); - List lineOffsets = ContainerUtil.map(sequences, LineOffsets::create); - - final ComparisonPolicy comparisonPolicy = getIgnorePolicy().getComparisonPolicy(); - - ComparisonManager manager = ComparisonManager.getInstance(); - List lineFragments = manager.compareLines(sequences.get(0), sequences.get(1), sequences.get(2), - comparisonPolicy, indicator); - - List conflictTypes = ContainerUtil.map(lineFragments, fragment -> { - return DiffUtil.getLineMergeType(fragment, sequences, lineOffsets, comparisonPolicy); + List sequences = ContainerUtil.map(getContents(), content -> { + return content.getDocument().getImmutableCharSequence(); }); - List innerFragments = null; - if (getHighlightPolicy().isFineFragments()) { - innerFragments = new ArrayList<>(lineFragments.size()); + List lineFragments = myTextDiffProvider.compare(sequences.get(0), sequences.get(1), sequences.get(2), + indicator); - for (int i = 0; i < lineFragments.size(); i++) { - final MergeLineFragment fragment = lineFragments.get(i); - final MergeConflictType conflictType = conflictTypes.get(i); - - indicator.checkCanceled(); - List chunks = ThreeSide.map(side -> { - if (!conflictType.isChange(side)) return null; - return getChunkContent(fragment, sequences, lineOffsets, side); - }); - - innerFragments.add(DiffUtil.compareThreesideInner(chunks, comparisonPolicy, indicator)); - } - } - - return apply(lineFragments, conflictTypes, innerFragments); + return apply(lineFragments); } catch (DiffTooBigException e) { return applyNotification(DiffNotifications.createDiffTooBig()); @@ -163,30 +127,16 @@ public class SimpleThreesideDiffViewer extends ThreesideTextDiffViewerEx { } } - @Nullable - private static CharSequence getChunkContent(@NotNull MergeLineFragment fragment, - @NotNull List sequences, - @NotNull List lineOffsets, - @NotNull ThreeSide side) { - int startLine = fragment.getStartLine(side); - int endLine = fragment.getEndLine(side); - if (startLine == endLine) return null; - return DiffUtil.getLinesContent(side.select(sequences), side.select(lineOffsets), startLine, endLine); - } - @NotNull - private Runnable apply(@NotNull final List fragments, - @NotNull final List conflictTypes, - @Nullable final List innerDifferences) { + private Runnable apply(@NotNull final List fragments) { return () -> { myFoldingModel.updateContext(myRequest, getFoldingModelSettings()); clearDiffPresentation(); resetChangeCounters(); - for (int i = 0; i < fragments.size(); i++) { - MergeLineFragment fragment = fragments.get(i); - MergeConflictType conflictType = conflictTypes.get(i); - MergeInnerDifferences innerFragments = innerDifferences != null ? innerDifferences.get(i) : null; + for (FineMergeLineFragment fragment : fragments) { + MergeConflictType conflictType = fragment.getConflictType(); + MergeInnerDifferences innerFragments = fragment.getInnerFragments(); SimpleThreesideDiffChange change = new SimpleThreesideDiffChange(fragment, conflictType, innerFragments, this); myDiffChanges.add(change); @@ -250,21 +200,6 @@ public class SimpleThreesideDiffViewer extends ThreesideTextDiffViewerEx { } } - @NotNull - private IgnorePolicy getIgnorePolicy() { - IgnorePolicy policy = getTextSettings().getIgnorePolicy(); - if (policy == IgnorePolicy.IGNORE_WHITESPACES_CHUNKS) return IgnorePolicy.IGNORE_WHITESPACES; - return policy; - } - - @NotNull - private HighlightPolicy getHighlightPolicy() { - HighlightPolicy policy = getTextSettings().getHighlightPolicy(); - if (policy == HighlightPolicy.BY_WORD_SPLIT) return HighlightPolicy.BY_WORD; - if (policy == HighlightPolicy.DO_NOT_HIGHLIGHT) return HighlightPolicy.BY_LINE; - return policy; - } - // // Getters // @@ -294,54 +229,6 @@ public class SimpleThreesideDiffViewer extends ThreesideTextDiffViewerEx { // Actions // - private class MyIgnorePolicySettingAction extends TextDiffViewerUtil.IgnorePolicySettingAction { - public MyIgnorePolicySettingAction() { - super(getTextSettings()); - } - - @NotNull - @Override - protected IgnorePolicy getCurrentSetting() { - return getIgnorePolicy(); - } - - @NotNull - @Override - protected List getAvailableSettings() { - ArrayList settings = ContainerUtil.newArrayList(IgnorePolicy.values()); - settings.remove(IgnorePolicy.IGNORE_WHITESPACES_CHUNKS); - return settings; - } - - @Override - protected void onSettingsChanged() { - rediff(); - } - } - - private class MyHighlightPolicySettingAction extends TextDiffViewerUtil.HighlightPolicySettingAction { - public MyHighlightPolicySettingAction() { - super(getTextSettings()); - } - - @NotNull - @Override - protected HighlightPolicy getCurrentSetting() { - return getHighlightPolicy(); - } - - @NotNull - @Override - protected List getAvailableSettings() { - return ContainerUtil.list(HighlightPolicy.BY_LINE, HighlightPolicy.BY_WORD); - } - - @Override - protected void onSettingsChanged() { - rediff(); - } - } - protected class MyEditorReadOnlyLockAction extends TextDiffViewerUtil.EditorReadOnlyLockAction { public MyEditorReadOnlyLockAction() { super(getContext(), getEditableEditors()); diff --git a/platform/diff-impl/src/com/intellij/diff/tools/simple/ThreesideTextDiffViewerEx.java b/platform/diff-impl/src/com/intellij/diff/tools/simple/ThreesideTextDiffViewerEx.java index 097e21126c89..8902dc080de2 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/simple/ThreesideTextDiffViewerEx.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/simple/ThreesideTextDiffViewerEx.java @@ -446,7 +446,7 @@ public abstract class ThreesideTextDiffViewerEx extends ThreesideTextDiffViewer assert editors.length == 3; } - public void install(@Nullable List fragments, + public void install(@Nullable List fragments, @NotNull UserDataHolder context, @NotNull FoldingModelSupport.Settings settings) { Iterator it = map(fragments, fragment -> new int[]{ diff --git a/platform/diff-impl/src/com/intellij/diff/tools/util/base/TextDiffViewerUtil.java b/platform/diff-impl/src/com/intellij/diff/tools/util/base/TextDiffViewerUtil.java index 421e76c39be1..5e04606409ad 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/util/base/TextDiffViewerUtil.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/util/base/TextDiffViewerUtil.java @@ -39,6 +39,7 @@ import com.intellij.openapi.editor.ex.EditorEx; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.util.Condition; import com.intellij.ui.ToggleActionButton; +import com.intellij.util.ArrayUtil; import com.intellij.util.EditorPopupHandler; import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; @@ -51,6 +52,8 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import static com.intellij.util.containers.ContainerUtil.list; + public class TextDiffViewerUtil { private static final Logger LOG = Logger.getInstance(TextDiffViewerUtil.class); @@ -162,136 +165,185 @@ public class TextDiffViewerUtil { // Actions // - // TODO: pretty icons ? public static abstract class ComboBoxSettingAction extends ComboBoxAction implements DumbAware { - private DefaultActionGroup myChildren; + private DefaultActionGroup myActions; @Override public void update(AnActionEvent e) { Presentation presentation = e.getPresentation(); - presentation.setText(getText(getCurrentSetting())); - } - - @NotNull - public DefaultActionGroup getPopupGroup() { - initChildren(); - return myChildren; + presentation.setText(getText(getValue())); } @NotNull @Override protected DefaultActionGroup createPopupActionGroup(JComponent button) { - initChildren(); - return myChildren; + return getActions(); } - private void initChildren() { - if (myChildren == null) { - myChildren = new DefaultActionGroup(); - for (T setting : getAvailableSettings()) { - myChildren.add(new MyAction(setting)); + @NotNull + public DefaultActionGroup getActions() { + if (myActions == null) { + myActions = new DefaultActionGroup(); + for (T setting : getAvailableOptions()) { + myActions.add(new MyAction(setting)); } } + return myActions; } @NotNull - protected abstract List getAvailableSettings(); + protected abstract List getAvailableOptions(); @NotNull - protected abstract String getText(@NotNull T setting); + protected abstract T getValue(); + + protected abstract void setValue(@NotNull T option); @NotNull - protected abstract T getCurrentSetting(); - - protected abstract void applySetting(@NotNull T setting, @NotNull AnActionEvent e); + protected abstract String getText(@NotNull T option); private class MyAction extends AnAction implements DumbAware { - @NotNull private final T mySetting; + @NotNull private final T myOption; - public MyAction(@NotNull T setting) { - super(getText(setting)); - mySetting = setting; + public MyAction(@NotNull T option) { + super(getText(option)); + myOption = option; } @Override public void actionPerformed(@NotNull AnActionEvent e) { - applySetting(mySetting, e); + setValue(myOption); } } } - public static abstract class HighlightPolicySettingAction extends ComboBoxSettingAction { - @NotNull protected final TextDiffSettings mySettings; + private static abstract class EnumPolicySettingAction extends TextDiffViewerUtil.ComboBoxSettingAction { + @NotNull private final T[] myPolicies; - public HighlightPolicySettingAction(@NotNull TextDiffSettings settings) { - mySettings = settings; + public EnumPolicySettingAction(@NotNull T[] policies) { + assert policies.length > 0; + myPolicies = policies; } @Override - protected void applySetting(@NotNull HighlightPolicy setting, @NotNull AnActionEvent e) { - if (getCurrentSetting() == setting) return; - UsageTrigger.trigger("diff.TextDiffSettings.HighlightPolicy." + setting.name()); - mySettings.setHighlightPolicy(setting); - update(e); - onSettingsChanged(); + public void update(AnActionEvent e) { + super.update(e); + e.getPresentation().setEnabledAndVisible(myPolicies.length > 1); } @NotNull @Override - protected HighlightPolicy getCurrentSetting() { + protected List getAvailableOptions() { + //noinspection unchecked + return ContainerUtil.sorted(Arrays.asList(myPolicies)); + } + + @NotNull + @Override + public T getValue() { + T value = getStoredValue(); + if (ArrayUtil.contains(value, myPolicies)) return value; + + List substitutes = getValueSubstitutes(value); + for (T substitute : substitutes) { + if (ArrayUtil.contains(substitute, myPolicies)) return substitute; + } + + return myPolicies[0]; + } + + @NotNull + protected abstract T getStoredValue(); + + @NotNull + protected abstract List getValueSubstitutes(@NotNull T value); + } + + public static class HighlightPolicySettingAction extends EnumPolicySettingAction { + @NotNull protected final TextDiffSettings mySettings; + @NotNull private final Runnable myRediffRunnable; + + public HighlightPolicySettingAction(@NotNull TextDiffSettings settings, + @NotNull Runnable rediff, + @NotNull HighlightPolicy... policies) { + super(policies); + mySettings = settings; + myRediffRunnable = rediff; + } + + @Override + protected void setValue(@NotNull HighlightPolicy option) { + if (getValue() == option) return; + UsageTrigger.trigger("diff.TextDiffSettings.HighlightPolicy." + option.name()); + mySettings.setHighlightPolicy(option); + myRediffRunnable.run(); + } + + @NotNull + @Override + protected HighlightPolicy getStoredValue() { return mySettings.getHighlightPolicy(); } @NotNull @Override - protected String getText(@NotNull HighlightPolicy setting) { - return setting.getText(); + protected List getValueSubstitutes(@NotNull HighlightPolicy value) { + if (value == HighlightPolicy.BY_WORD_SPLIT) { + return list(HighlightPolicy.BY_WORD); + } + if (value == HighlightPolicy.DO_NOT_HIGHLIGHT) { + return list(HighlightPolicy.BY_LINE); + } + return list(HighlightPolicy.BY_WORD); } @NotNull @Override - protected List getAvailableSettings() { - return Arrays.asList(HighlightPolicy.values()); + protected String getText(@NotNull HighlightPolicy option) { + return option.getText(); } - - protected abstract void onSettingsChanged(); } - public static abstract class IgnorePolicySettingAction extends ComboBoxSettingAction { + public static class IgnorePolicySettingAction extends EnumPolicySettingAction { @NotNull protected final TextDiffSettings mySettings; + @NotNull private final Runnable myRediffRunnable; - public IgnorePolicySettingAction(@NotNull TextDiffSettings settings) { + public IgnorePolicySettingAction(@NotNull TextDiffSettings settings, + @NotNull Runnable rediff, + @NotNull IgnorePolicy... policies) { + super(policies); mySettings = settings; + myRediffRunnable = rediff; } @Override - protected void applySetting(@NotNull IgnorePolicy setting, @NotNull AnActionEvent e) { - if (getCurrentSetting() == setting) return; - UsageTrigger.trigger("diff.TextDiffSettings.IgnorePolicy." + setting.name()); - mySettings.setIgnorePolicy(setting); - update(e); - onSettingsChanged(); + protected void setValue(@NotNull IgnorePolicy option) { + if (getValue() == option) return; + UsageTrigger.trigger("diff.TextDiffSettings.IgnorePolicy." + option.name()); + mySettings.setIgnorePolicy(option); + myRediffRunnable.run(); } @NotNull @Override - protected IgnorePolicy getCurrentSetting() { + protected IgnorePolicy getStoredValue() { return mySettings.getIgnorePolicy(); } @NotNull @Override - protected String getText(@NotNull IgnorePolicy setting) { - return setting.getText(); + protected List getValueSubstitutes(@NotNull IgnorePolicy value) { + if (value == IgnorePolicy.IGNORE_WHITESPACES_CHUNKS) { + return list(IgnorePolicy.IGNORE_WHITESPACES); + } + return list(IgnorePolicy.DEFAULT); } @NotNull @Override - protected List getAvailableSettings() { - return Arrays.asList(IgnorePolicy.values()); + protected String getText(@NotNull IgnorePolicy option) { + return option.getText(); } - - protected abstract void onSettingsChanged(); } public static class ToggleAutoScrollAction extends ToggleActionButton implements DumbAware { diff --git a/platform/diff-impl/src/com/intellij/diff/tools/util/text/FineMergeLineFragment.java b/platform/diff-impl/src/com/intellij/diff/tools/util/text/FineMergeLineFragment.java new file mode 100644 index 000000000000..bb8d7769be80 --- /dev/null +++ b/platform/diff-impl/src/com/intellij/diff/tools/util/text/FineMergeLineFragment.java @@ -0,0 +1,29 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.diff.tools.util.text; + +import com.intellij.diff.fragments.MergeLineFragment; +import com.intellij.diff.util.MergeConflictType; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public interface FineMergeLineFragment extends MergeLineFragment { + @Nullable + MergeInnerDifferences getInnerFragments(); + + @NotNull + MergeConflictType getConflictType(); +} diff --git a/platform/diff-impl/src/com/intellij/diff/tools/util/text/FineMergeLineFragmentImpl.java b/platform/diff-impl/src/com/intellij/diff/tools/util/text/FineMergeLineFragmentImpl.java new file mode 100644 index 000000000000..2142612e5528 --- /dev/null +++ b/platform/diff-impl/src/com/intellij/diff/tools/util/text/FineMergeLineFragmentImpl.java @@ -0,0 +1,47 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.diff.tools.util.text; + +import com.intellij.diff.fragments.MergeLineFragment; +import com.intellij.diff.fragments.MergeLineFragmentImpl; +import com.intellij.diff.util.MergeConflictType; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class FineMergeLineFragmentImpl extends MergeLineFragmentImpl implements FineMergeLineFragment { + @NotNull private final MergeConflictType myConflictType; + @Nullable private final MergeInnerDifferences myInnerDifferences; + + public FineMergeLineFragmentImpl(@NotNull MergeLineFragment fragment, + @NotNull MergeConflictType conflictType, + @Nullable MergeInnerDifferences innerDifferences) { + super(fragment); + myConflictType = conflictType; + myInnerDifferences = innerDifferences; + } + + @NotNull + @Override + public MergeConflictType getConflictType() { + return myConflictType; + } + + @Nullable + @Override + public MergeInnerDifferences getInnerFragments() { + return myInnerDifferences; + } +} diff --git a/platform/diff-impl/src/com/intellij/diff/tools/util/text/MergeInnerDifferences.java b/platform/diff-impl/src/com/intellij/diff/tools/util/text/MergeInnerDifferences.java index 53397815c5c3..b00131ca3ecd 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/util/text/MergeInnerDifferences.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/util/text/MergeInnerDifferences.java @@ -22,7 +22,7 @@ import org.jetbrains.annotations.Nullable; import java.util.List; -public class MergeInnerDifferences { +public final class MergeInnerDifferences { @Nullable private final List myLeft; @Nullable private final List myBase; @Nullable private final List myRight; @@ -33,6 +33,9 @@ public class MergeInnerDifferences { myRight = right; } + /** + * NB: ranges might overlap and might be not in order + */ @Nullable public List get(@NotNull ThreeSide side) { return side.select(myLeft, myBase, myRight); diff --git a/platform/diff-impl/src/com/intellij/diff/tools/util/text/SimpleTextDiffProvider.java b/platform/diff-impl/src/com/intellij/diff/tools/util/text/SimpleTextDiffProvider.java new file mode 100644 index 000000000000..a28d4bf95e57 --- /dev/null +++ b/platform/diff-impl/src/com/intellij/diff/tools/util/text/SimpleTextDiffProvider.java @@ -0,0 +1,108 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.diff.tools.util.text; + +import com.intellij.diff.comparison.ComparisonManager; +import com.intellij.diff.comparison.ComparisonPolicy; +import com.intellij.diff.fragments.LineFragment; +import com.intellij.diff.tools.util.base.HighlightPolicy; +import com.intellij.diff.tools.util.base.IgnorePolicy; +import com.intellij.diff.tools.util.base.TextDiffSettingsHolder.TextDiffSettings; +import com.intellij.diff.util.DiffUserDataKeysEx; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.util.ArrayUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +import static com.intellij.diff.tools.util.base.HighlightPolicy.*; +import static com.intellij.diff.tools.util.base.IgnorePolicy.*; + +public class SimpleTextDiffProvider extends TextDiffProviderBase implements TwosideTextDiffProvider { + private static final DiffUserDataKeysEx.DiffComputer DEFAULT_COMPUTER = (text1, text2, policy, innerChanges, indicator) -> { + if (innerChanges) { + return ComparisonManager.getInstance().compareLinesInner(text1, text2, policy, indicator); + } + else { + return ComparisonManager.getInstance().compareLines(text1, text2, policy, indicator); + } + }; + + private static final IgnorePolicy[] IGNORE_POLICIES = {DEFAULT, TRIM_WHITESPACES, IGNORE_WHITESPACES, IGNORE_WHITESPACES_CHUNKS}; + private static final HighlightPolicy[] HIGHLIGHT_POLICIES = {BY_LINE, BY_WORD, BY_WORD_SPLIT, DO_NOT_HIGHLIGHT}; + + @NotNull private final DiffUserDataKeysEx.DiffComputer myDiffComputer; + + public SimpleTextDiffProvider(@NotNull TextDiffSettings settings, + @NotNull Runnable rediff) { + this(settings, rediff, DEFAULT_COMPUTER); + } + + public SimpleTextDiffProvider(@NotNull TextDiffSettings settings, + @NotNull Runnable rediff, + @NotNull DiffUserDataKeysEx.DiffComputer diffComputer) { + this(settings, rediff, diffComputer, IGNORE_POLICIES, HIGHLIGHT_POLICIES); + } + + private SimpleTextDiffProvider(@NotNull TextDiffSettings settings, + @NotNull Runnable rediff, + @NotNull DiffUserDataKeysEx.DiffComputer diffComputer, + @NotNull IgnorePolicy[] ignorePolicies, + @NotNull HighlightPolicy[] highlightPolicies) { + super(settings, rediff, ignorePolicies, highlightPolicies); + myDiffComputer = diffComputer; + } + + @Nullable + @Override + public List compare(@NotNull CharSequence text1, @NotNull CharSequence text2, @NotNull ProgressIndicator indicator) { + IgnorePolicy ignorePolicy = getIgnorePolicy(); + HighlightPolicy highlightPolicy = getHighlightPolicy(); + + if (!highlightPolicy.isShouldCompare()) return null; + + ComparisonPolicy policy = ignorePolicy.getComparisonPolicy(); + boolean innerFragments = highlightPolicy.isFineFragments(); + boolean squashFragments = highlightPolicy.isShouldSquash(); + boolean trimFragments = ignorePolicy.isShouldTrimChunks(); + + indicator.checkCanceled(); + List fragments = myDiffComputer.compute(text1, text2, policy, innerFragments, indicator); + + indicator.checkCanceled(); + return ComparisonManager.getInstance().processBlocks(fragments, text1, text2, + policy, squashFragments, trimFragments); + } + + + public static class NoIgnore extends SimpleTextDiffProvider implements TwosideTextDiffProvider.NoIgnore { + public NoIgnore(@NotNull TextDiffSettings settings, @NotNull Runnable rediff) { + this(settings, rediff, DEFAULT_COMPUTER); + } + + public NoIgnore(@NotNull TextDiffSettings settings, @NotNull Runnable rediff, @NotNull DiffUserDataKeysEx.DiffComputer diffComputer) { + super(settings, rediff, diffComputer, IGNORE_POLICIES, ArrayUtil.remove(HIGHLIGHT_POLICIES, DO_NOT_HIGHLIGHT)); + } + + @NotNull + @Override + public List compare(@NotNull CharSequence text1, @NotNull CharSequence text2, @NotNull ProgressIndicator indicator) { + //noinspection ConstantConditions + return super.compare(text1, text2, indicator); + } + } +} diff --git a/platform/diff-impl/src/com/intellij/diff/tools/util/text/SimpleThreesideTextDiffProvider.java b/platform/diff-impl/src/com/intellij/diff/tools/util/text/SimpleThreesideTextDiffProvider.java new file mode 100644 index 000000000000..9033bd4e875b --- /dev/null +++ b/platform/diff-impl/src/com/intellij/diff/tools/util/text/SimpleThreesideTextDiffProvider.java @@ -0,0 +1,98 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.diff.tools.util.text; + +import com.intellij.diff.comparison.ComparisonManager; +import com.intellij.diff.comparison.ComparisonPolicy; +import com.intellij.diff.fragments.MergeLineFragment; +import com.intellij.diff.tools.util.base.HighlightPolicy; +import com.intellij.diff.tools.util.base.IgnorePolicy; +import com.intellij.diff.tools.util.base.TextDiffSettingsHolder.TextDiffSettings; +import com.intellij.diff.util.DiffUtil; +import com.intellij.diff.util.MergeConflictType; +import com.intellij.diff.util.ThreeSide; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.util.containers.ContainerUtil; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.List; + +import static com.intellij.diff.tools.util.base.HighlightPolicy.BY_LINE; +import static com.intellij.diff.tools.util.base.HighlightPolicy.BY_WORD; +import static com.intellij.diff.tools.util.base.IgnorePolicy.*; + +public class SimpleThreesideTextDiffProvider extends TextDiffProviderBase { + private static final IgnorePolicy[] IGNORE_POLICIES = {DEFAULT, TRIM_WHITESPACES, IGNORE_WHITESPACES}; + private static final HighlightPolicy[] HIGHLIGHT_POLICIES = {BY_LINE, BY_WORD}; + + public SimpleThreesideTextDiffProvider(@NotNull TextDiffSettings settings, + @NotNull Runnable rediff) { + super(settings, rediff, IGNORE_POLICIES, HIGHLIGHT_POLICIES); + } + + @NotNull + public List compare(@NotNull CharSequence text1, + @NotNull CharSequence text2, + @NotNull CharSequence text3, + @NotNull ProgressIndicator indicator) { + + IgnorePolicy ignorePolicy = getIgnorePolicy(); + HighlightPolicy highlightPolicy = getHighlightPolicy(); + ComparisonPolicy comparisonPolicy = ignorePolicy.getComparisonPolicy(); + + List sequences = ContainerUtil.list(text1, text2, text3); + List lineOffsets = ContainerUtil.map(sequences, LineOffsets::create); + + indicator.checkCanceled(); + List lineFragments = ComparisonManager.getInstance().compareLines(text1, text2, text3, comparisonPolicy, indicator); + + indicator.checkCanceled(); + List result = new ArrayList<>(lineFragments.size()); + for (MergeLineFragment fragment : lineFragments) { + MergeConflictType conflictType = DiffUtil.getLineMergeType(fragment, sequences, lineOffsets, comparisonPolicy); + + MergeInnerDifferences innerDifferences; + if (highlightPolicy.isFineFragments()) { + List chunks = getChunks(fragment, sequences, lineOffsets, conflictType); + innerDifferences = DiffUtil.compareThreesideInner(chunks, comparisonPolicy, indicator); + } + else { + innerDifferences = null; + } + + result.add(new FineMergeLineFragmentImpl(fragment, conflictType, innerDifferences)); + } + + return result; + } + + @NotNull + private static List getChunks(@NotNull MergeLineFragment fragment, + @NotNull List sequences, + @NotNull List lineOffsets, + @NotNull MergeConflictType conflictType) { + return ThreeSide.map(side -> { + if (!conflictType.isChange(side)) return null; + + int startLine = fragment.getStartLine(side); + int endLine = fragment.getEndLine(side); + if (startLine == endLine) return null; + + return DiffUtil.getLinesContent(side.select(sequences), side.select(lineOffsets), startLine, endLine); + }); + } +} diff --git a/platform/diff-impl/src/com/intellij/diff/tools/util/text/TextDiffProvider.java b/platform/diff-impl/src/com/intellij/diff/tools/util/text/TextDiffProvider.java new file mode 100644 index 000000000000..80b678dbc205 --- /dev/null +++ b/platform/diff-impl/src/com/intellij/diff/tools/util/text/TextDiffProvider.java @@ -0,0 +1,29 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.diff.tools.util.text; + +import com.intellij.openapi.actionSystem.AnAction; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public interface TextDiffProvider { + @NotNull + List getToolbarActions(); + + @NotNull + List getPopupActions(); +} diff --git a/platform/diff-impl/src/com/intellij/diff/tools/util/text/TextDiffProviderBase.java b/platform/diff-impl/src/com/intellij/diff/tools/util/text/TextDiffProviderBase.java new file mode 100644 index 000000000000..62134cf7b089 --- /dev/null +++ b/platform/diff-impl/src/com/intellij/diff/tools/util/text/TextDiffProviderBase.java @@ -0,0 +1,72 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.diff.tools.util.text; + +import com.intellij.diff.tools.util.base.HighlightPolicy; +import com.intellij.diff.tools.util.base.IgnorePolicy; +import com.intellij.diff.tools.util.base.TextDiffSettingsHolder.TextDiffSettings; +import com.intellij.diff.tools.util.base.TextDiffViewerUtil.HighlightPolicySettingAction; +import com.intellij.diff.tools.util.base.TextDiffViewerUtil.IgnorePolicySettingAction; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.Separator; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +import static com.intellij.util.containers.ContainerUtil.list; + +public class TextDiffProviderBase implements TextDiffProvider { + private final IgnorePolicySettingAction myIgnorePolicySettingAction; + private final HighlightPolicySettingAction myHighlightPolicySettingAction; + + public TextDiffProviderBase(@NotNull TextDiffSettings settings, + @NotNull Runnable rediff, + @NotNull IgnorePolicy[] ignorePolicies, + @NotNull HighlightPolicy[] highlightPolicies) { + myIgnorePolicySettingAction = new IgnorePolicySettingAction(settings, rediff, ignorePolicies); + myHighlightPolicySettingAction = new HighlightPolicySettingAction(settings, rediff, highlightPolicies); + } + + @NotNull + @Override + public List getToolbarActions() { + return list(myIgnorePolicySettingAction, myHighlightPolicySettingAction); + } + + @NotNull + @Override + public List getPopupActions() { + return list(Separator.getInstance(), + myIgnorePolicySettingAction.getActions(), + Separator.getInstance(), + myHighlightPolicySettingAction.getActions(), + Separator.getInstance()); + } + + @NotNull + public IgnorePolicy getIgnorePolicy() { + return myIgnorePolicySettingAction.getValue(); + } + + @NotNull + public HighlightPolicy getHighlightPolicy() { + return myHighlightPolicySettingAction.getValue(); + } + + public boolean isHighlightingDisabled() { + return myHighlightPolicySettingAction.getValue() == HighlightPolicy.DO_NOT_HIGHLIGHT; + } +} diff --git a/platform/diff-impl/src/com/intellij/diff/tools/util/text/TwosideTextDiffProvider.java b/platform/diff-impl/src/com/intellij/diff/tools/util/text/TwosideTextDiffProvider.java new file mode 100644 index 000000000000..5a1cd00e422d --- /dev/null +++ b/platform/diff-impl/src/com/intellij/diff/tools/util/text/TwosideTextDiffProvider.java @@ -0,0 +1,43 @@ +/* + * Copyright 2000-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.diff.tools.util.text; + +import com.intellij.diff.fragments.LineFragment; +import com.intellij.openapi.progress.ProgressIndicator; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; + +public interface TwosideTextDiffProvider extends TextDiffProvider { + @Nullable + List compare(@NotNull CharSequence text1, + @NotNull CharSequence text2, + @NotNull ProgressIndicator indicator); + + default boolean isHighlightingDisabled() { + return false; + } + + + interface NoIgnore extends TwosideTextDiffProvider { + @NotNull + @Override + List compare(@NotNull CharSequence text1, + @NotNull CharSequence text2, + @NotNull ProgressIndicator indicator); + } +} diff --git a/platform/diff-impl/src/com/intellij/diff/util/DiffUtil.java b/platform/diff-impl/src/com/intellij/diff/util/DiffUtil.java index 42088a518f10..0904028a4f5c 100644 --- a/platform/diff-impl/src/com/intellij/diff/util/DiffUtil.java +++ b/platform/diff-impl/src/com/intellij/diff/util/DiffUtil.java @@ -34,11 +34,12 @@ import com.intellij.diff.fragments.MergeWordFragment; import com.intellij.diff.impl.DiffSettingsHolder.DiffSettings; import com.intellij.diff.requests.ContentDiffRequest; import com.intellij.diff.requests.DiffRequest; -import com.intellij.diff.tools.util.text.MergeInnerDifferences; -import com.intellij.diff.tools.util.base.HighlightPolicy; -import com.intellij.diff.tools.util.base.IgnorePolicy; +import com.intellij.diff.tools.util.base.TextDiffSettingsHolder.TextDiffSettings; import com.intellij.diff.tools.util.base.TextDiffViewerUtil; import com.intellij.diff.tools.util.text.LineOffsets; +import com.intellij.diff.tools.util.text.MergeInnerDifferences; +import com.intellij.diff.tools.util.text.SimpleTextDiffProvider; +import com.intellij.diff.tools.util.text.TwosideTextDiffProvider; import com.intellij.icons.AllIcons; import com.intellij.lang.Language; import com.intellij.openapi.Disposable; @@ -599,31 +600,25 @@ public class DiffUtil { // @NotNull - public static List compare(@NotNull DiffRequest request, - @NotNull CharSequence text1, - @NotNull CharSequence text2, - @NotNull DiffConfig config, - @NotNull ProgressIndicator indicator) { - indicator.checkCanceled(); - + public static TwosideTextDiffProvider createTextDiffProvider(@NotNull ContentDiffRequest request, + @NotNull TextDiffSettings settings, + @NotNull Runnable rediff) { DiffUserDataKeysEx.DiffComputer diffComputer = request.getUserData(DiffUserDataKeysEx.CUSTOM_DIFF_COMPUTER); - - List fragments; if (diffComputer != null) { - fragments = diffComputer.compute(text1, text2, config.policy, config.innerFragments, indicator); - } - else { - if (config.innerFragments) { - fragments = ComparisonManager.getInstance().compareLinesInner(text1, text2, config.policy, indicator); - } - else { - fragments = ComparisonManager.getInstance().compareLines(text1, text2, config.policy, indicator); - } + return new SimpleTextDiffProvider(settings, rediff, diffComputer); } + return new SimpleTextDiffProvider(settings, rediff); + } - indicator.checkCanceled(); - return ComparisonManager.getInstance().processBlocks(fragments, text1, text2, - config.policy, config.squashFragments, config.trimFragments); + @NotNull + public static TwosideTextDiffProvider.NoIgnore createNoIgnoreTextDiffProvider(@NotNull ContentDiffRequest request, + @NotNull TextDiffSettings settings, + @NotNull Runnable rediff) { + DiffUserDataKeysEx.DiffComputer diffComputer = request.getUserData(DiffUserDataKeysEx.CUSTOM_DIFF_COMPUTER); + if (diffComputer != null) { + return new SimpleTextDiffProvider.NoIgnore(settings, rediff, diffComputer); + } + return new SimpleTextDiffProvider.NoIgnore(settings, rediff); } @Nullable @@ -1392,24 +1387,6 @@ public class DiffUtil { // Helpers // - public static class DiffConfig { - @NotNull public final ComparisonPolicy policy; - public final boolean innerFragments; - public final boolean squashFragments; - public final boolean trimFragments; - - public DiffConfig(@NotNull ComparisonPolicy policy, boolean innerFragments, boolean squashFragments, boolean trimFragments) { - this.policy = policy; - this.innerFragments = innerFragments; - this.squashFragments = squashFragments; - this.trimFragments = trimFragments; - } - - public DiffConfig(@NotNull IgnorePolicy ignorePolicy, @NotNull HighlightPolicy highlightPolicy) { - this(ignorePolicy.getComparisonPolicy(), highlightPolicy.isFineFragments(), highlightPolicy.isShouldSquash(), - ignorePolicy.isShouldTrimChunks()); - } - } private static class SyncHeightComponent extends JPanel { @NotNull private final List myComponents;