/* * Copyright 2000-2009 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.openapi.vcs.changes.actions.diff; import com.intellij.diff.DiffManager; import com.intellij.diff.chains.DiffRequestChain; import com.intellij.diff.util.DiffUserDataKeys; import com.intellij.idea.ActionsBundle; import com.intellij.openapi.actionSystem.ActionPlaces; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Key; import com.intellij.openapi.vcs.VcsDataKeys; import com.intellij.openapi.vcs.changes.*; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; public class ShowDiffAction extends AnAction implements DumbAware { private static final Logger LOG = Logger.getInstance(ShowDiffAction.class); public void update(@NotNull AnActionEvent e) { Change[] changes = e.getData(VcsDataKeys.CHANGES); Project project = e.getData(CommonDataKeys.PROJECT); if (ActionPlaces.MAIN_MENU.equals(e.getPlace())) { e.getPresentation().setEnabled(project != null && changes != null && changes.length > 0); } else { e.getPresentation().setEnabled(project != null && canShowDiff(project, changes)); } } public static boolean canShowDiff(@Nullable Project project, @Nullable Change[] changes) { return changes != null && canShowDiff(project, Arrays.asList(changes)); } public static boolean canShowDiff(@Nullable Project project, @Nullable List<Change> changes) { if (changes == null || changes.size() == 0) return false; for (Change change : changes) { if (ChangeDiffRequestProducer.canCreate(project, change)) return true; } return false; } public void actionPerformed(@NotNull final AnActionEvent e) { final Project project = e.getData(CommonDataKeys.PROJECT); final Change[] changes = e.getData(VcsDataKeys.CHANGES); if (project == null || !canShowDiff(project, changes)) return; if (ChangeListManager.getInstance(project).isFreezedWithNotification(null)) return; final boolean needsConversion = checkIfThereAreFakeRevisions(project, changes); final List<Change> changesInList = e.getData(VcsDataKeys.CHANGES_IN_LIST_KEY); // this trick is essential since we are under some conditions to refresh changes; // but we can only rely on callback after refresh final Runnable performer = () -> { Change[] convertedChanges; if (needsConversion) { convertedChanges = loadFakeRevisions(project, changes); } else { convertedChanges = changes; } if (convertedChanges == null || convertedChanges.length == 0) { return; } Change selectedChane = null; List<Change> result = null; if (convertedChanges.length == 1) { selectedChane = convertedChanges[0]; ChangeList changeList = ((ChangeListManagerImpl)ChangeListManager.getInstance(project)).getIdentityChangeList(selectedChane); if (changeList != null) { result = changesInList != null ? changesInList : new ArrayList<>(changeList.getChanges()); } } if (result == null) result = ContainerUtil.newArrayList(convertedChanges); //ContainerUtil.sort(result, ChangesComparator.getInstance(false)); int index = selectedChane == null ? 0 : Math.max(0, ContainerUtil.indexOfIdentity(result, selectedChane)); showDiffForChange(project, result, index); }; if (needsConversion) { ChangeListManager.getInstance(project).invokeAfterUpdate(performer, InvokeAfterUpdateMode.BACKGROUND_CANCELLABLE, ActionsBundle.actionText("ChangesView.Diff"), ModalityState.current()); } else { performer.run(); } } private static boolean checkIfThereAreFakeRevisions(@NotNull Project project, @NotNull Change[] changes) { boolean needsConversion = false; for (Change change : changes) { final ContentRevision beforeRevision = change.getBeforeRevision(); final ContentRevision afterRevision = change.getAfterRevision(); if (beforeRevision instanceof FakeRevision) { VcsDirtyScopeManager.getInstance(project).fileDirty(beforeRevision.getFile()); needsConversion = true; } if (afterRevision instanceof FakeRevision) { VcsDirtyScopeManager.getInstance(project).fileDirty(afterRevision.getFile()); needsConversion = true; } } return needsConversion; } @Nullable private static Change[] loadFakeRevisions(@NotNull Project project, @NotNull Change[] changes) { List<Change> matchingChanges = new ArrayList<>(); for (Change change : changes) { matchingChanges.addAll(ChangeListManager.getInstance(project).getChangesIn(ChangesUtil.getFilePath(change))); } return matchingChanges.toArray(new Change[matchingChanges.size()]); } // // Impl // public static void showDiffForChange(@Nullable Project project, @NotNull Iterable<Change> changes) { showDiffForChange(project, changes, 0); } public static void showDiffForChange(@Nullable Project project, @NotNull Iterable<Change> changes, int index) { showDiffForChange(project, changes, index, new ShowDiffContext()); } public static void showDiffForChange(@Nullable Project project, @NotNull Iterable<Change> changes, @NotNull Condition<Change> condition, @NotNull ShowDiffContext context) { int index = 0; List<ChangeDiffRequestProducer> presentables = new ArrayList<>(); for (Change change : changes) { if (condition.value(change)) index = presentables.size(); ChangeDiffRequestProducer presentable = ChangeDiffRequestProducer.create(project, change, context.getChangeContext(change)); if (presentable != null) presentables.add(presentable); } showDiffForChange(project, presentables, index, context); } public static void showDiffForChange(@Nullable Project project, @NotNull Iterable<Change> changes, int index, @NotNull ShowDiffContext context) { int i = 0; int newIndex = 0; List<ChangeDiffRequestProducer> presentables = new ArrayList<>(); for (Change change : changes) { if (i == index) newIndex = presentables.size(); ChangeDiffRequestProducer presentable = ChangeDiffRequestProducer.create(project, change, context.getChangeContext(change)); if (presentable != null) { presentables.add(presentable); } i++; } showDiffForChange(project, presentables, newIndex, context); } private static void showDiffForChange(@Nullable Project project, @NotNull List<ChangeDiffRequestProducer> presentables, int index, @NotNull ShowDiffContext context) { if (presentables.isEmpty()) return; if (index < 0 || index >= presentables.size()) index = 0; DiffRequestChain chain = new ChangeDiffRequestChain(presentables); chain.setIndex(index); for (Map.Entry<Key, Object> entry : context.getChainContext().entrySet()) { chain.putUserData(entry.getKey(), entry.getValue()); } chain.putUserData(DiffUserDataKeys.CONTEXT_ACTIONS, context.getActions()); DiffManager.getInstance().showDiff(project, chain, context.getDialogHints()); } }