/* * Copyright 2000-2015 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.simple; import com.intellij.diff.DiffContext; import com.intellij.diff.comparison.DiffTooBigException; 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.TextDiffViewerUtil; import com.intellij.diff.tools.util.side.ThreesideTextDiffViewer; 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.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.event.DocumentEvent; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.CalledInAwt; import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.List; public class SimpleThreesideDiffViewer extends ThreesideTextDiffViewerEx { @NotNull private final SimpleThreesideTextDiffProvider myTextDiffProvider; @NotNull private final List<SimpleThreesideDiffChange> myDiffChanges = new ArrayList<>(); @NotNull private final List<SimpleThreesideDiffChange> myInvalidDiffChanges = new ArrayList<>(); public SimpleThreesideDiffViewer(@NotNull DiffContext context, @NotNull DiffRequest request) { super(context, (ContentDiffRequest)request); myTextDiffProvider = new SimpleThreesideTextDiffProvider(getTextSettings(), this::rediff, this); } @NotNull @Override protected List<AnAction> createToolbarActions() { List<AnAction> group = new ArrayList<>(); group.addAll(myTextDiffProvider.getToolbarActions()); group.add(new MyToggleExpandByDefaultAction()); group.add(new MyToggleAutoScrollAction()); group.add(new MyEditorReadOnlyLockAction()); group.add(myEditorSettingsAction); group.add(Separator.getInstance()); group.add(new TextShowPartialDiffAction(PartialDiffMode.MIDDLE_LEFT, false)); group.add(new TextShowPartialDiffAction(PartialDiffMode.MIDDLE_RIGHT, false)); group.add(new TextShowPartialDiffAction(PartialDiffMode.LEFT_RIGHT, false)); group.add(Separator.getInstance()); group.addAll(super.createToolbarActions()); return group; } @NotNull @Override protected List<AnAction> createPopupActions() { List<AnAction> group = new ArrayList<>(); group.addAll(myTextDiffProvider.getPopupActions()); group.add(new MyToggleAutoScrollAction()); group.add(new MyToggleExpandByDefaultAction()); group.add(Separator.getInstance()); group.addAll(super.createPopupActions()); return group; } // // Diff // @Override protected void onSlowRediff() { super.onSlowRediff(); myStatusPanel.setBusy(true); myInitialScrollHelper.onSlowRediff(); } @Override @NotNull protected Runnable performRediff(@NotNull final ProgressIndicator indicator) { try { indicator.checkCanceled(); List<CharSequence> sequences = ContainerUtil.map(getContents(), content -> { return content.getDocument().getImmutableCharSequence(); }); List<FineMergeLineFragment> lineFragments = myTextDiffProvider.compare(sequences.get(0), sequences.get(1), sequences.get(2), indicator); return apply(lineFragments); } catch (DiffTooBigException e) { return applyNotification(DiffNotifications.createDiffTooBig()); } catch (ProcessCanceledException e) { throw e; } catch (Throwable e) { LOG.error(e); return applyNotification(DiffNotifications.createError()); } } @NotNull private Runnable apply(@NotNull final List<FineMergeLineFragment> fragments) { return () -> { myFoldingModel.updateContext(myRequest, getFoldingModelSettings()); clearDiffPresentation(); resetChangeCounters(); for (FineMergeLineFragment fragment : fragments) { MergeConflictType conflictType = fragment.getConflictType(); MergeInnerDifferences innerFragments = fragment.getInnerFragments(); SimpleThreesideDiffChange change = new SimpleThreesideDiffChange(fragment, conflictType, innerFragments, this); myDiffChanges.add(change); onChangeAdded(change); } myFoldingModel.install(fragments, myRequest, getFoldingModelSettings()); myInitialScrollHelper.onRediff(); myContentPanel.repaintDividers(); myStatusPanel.update(); }; } @Override @CalledInAwt protected void destroyChangedBlocks() { super.destroyChangedBlocks(); for (SimpleThreesideDiffChange change : myDiffChanges) { change.destroy(); } myDiffChanges.clear(); for (SimpleThreesideDiffChange change : myInvalidDiffChanges) { change.destroy(); } myInvalidDiffChanges.clear(); } // // Impl // @Override @CalledInAwt protected void onBeforeDocumentChange(@NotNull DocumentEvent e) { super.onBeforeDocumentChange(e); if (myDiffChanges.isEmpty()) return; List<Document> documents = ContainerUtil.map(getEditors(), Editor::getDocument); ThreeSide side = ThreeSide.fromValue(documents, e.getDocument()); if (side == null) { LOG.warn("Unknown document changed"); return; } LineRange lineRange = DiffUtil.getAffectedLineRange(e); int shift = DiffUtil.countLinesShift(e); List<SimpleThreesideDiffChange> invalid = new ArrayList<>(); for (SimpleThreesideDiffChange change : myDiffChanges) { if (change.processChange(lineRange.start, lineRange.end, shift, side)) { invalid.add(change); } } if (!invalid.isEmpty()) { myDiffChanges.removeAll(invalid); myInvalidDiffChanges.addAll(invalid); } } // // Getters // @NotNull @Override public List<SimpleThreesideDiffChange> getChanges() { return myDiffChanges; } @NotNull @Override protected DiffDividerDrawUtil.DividerPaintable getDividerPaintable(@NotNull Side side) { return new MyDividerPaintable(side); } // // Misc // @SuppressWarnings("MethodOverridesStaticMethodOfSuperclass") public static boolean canShowRequest(@NotNull DiffContext context, @NotNull DiffRequest request) { return ThreesideTextDiffViewer.canShowRequest(context, request); } // // Actions // protected class MyEditorReadOnlyLockAction extends TextDiffViewerUtil.EditorReadOnlyLockAction { public MyEditorReadOnlyLockAction() { super(getContext(), getEditableEditors()); } } // // Helpers // private class MyDividerPaintable implements DiffDividerDrawUtil.DividerPaintable { @NotNull private final Side mySide; public MyDividerPaintable(@NotNull Side side) { mySide = side; } @Override public void process(@NotNull Handler handler) { ThreeSide left = mySide.select(ThreeSide.LEFT, ThreeSide.BASE); ThreeSide right = mySide.select(ThreeSide.BASE, ThreeSide.RIGHT); for (SimpleThreesideDiffChange diffChange : myDiffChanges) { if (!diffChange.isChange(mySide)) continue; if (!handler.process(diffChange.getStartLine(left), diffChange.getEndLine(left), diffChange.getStartLine(right), diffChange.getEndLine(right), diffChange.getDiffType().getColor(getEditor(ThreeSide.BASE)))) { return; } } } } }