vcs: do not create fake Changes to show diff from update dialog

Someone could try to use them and will get errors
This commit is contained in:
Aleksey Pivovarov
2015-09-03 19:28:11 +03:00
parent 57c1dbd758
commit 783d104e5e
@@ -15,35 +15,38 @@
*/
package com.intellij.openapi.vcs.update;
import com.intellij.diff.DiffDialogHints;
import com.intellij.diff.*;
import com.intellij.diff.actions.impl.GoToChangePopupBuilder;
import com.intellij.diff.chains.DiffRequestChain;
import com.intellij.diff.chains.DiffRequestProducer;
import com.intellij.diff.chains.DiffRequestProducerException;
import com.intellij.diff.contents.DiffContent;
import com.intellij.diff.requests.DiffRequest;
import com.intellij.diff.requests.SimpleDiffRequest;
import com.intellij.history.ByteContent;
import com.intellij.history.Label;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.fileEditor.impl.LoadTextUtil;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.UserDataHolder;
import com.intellij.openapi.util.UserDataHolderBase;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vcs.FilePath;
import com.intellij.openapi.vcs.FileStatus;
import com.intellij.openapi.vcs.VcsDataKeys;
import com.intellij.openapi.vcs.VcsException;
import com.intellij.openapi.vcs.changes.Change;
import com.intellij.openapi.vcs.changes.ContentRevision;
import com.intellij.openapi.vcs.changes.actions.diff.ShowDiffAction;
import com.intellij.openapi.vcs.changes.actions.diff.ShowDiffContext;
import com.intellij.openapi.vcs.history.VcsRevisionNumber;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.encoding.EncodingProjectManager;
import com.intellij.openapi.vcs.changes.actions.diff.ChangeGoToChangePopupAction;
import com.intellij.openapi.vfs.pointers.VirtualFilePointer;
import com.intellij.util.Consumer;
import com.intellij.vcsUtil.VcsUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.lang.ref.SoftReference;
import java.util.Iterator;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class ShowUpdatedDiffAction extends AnAction implements DumbAware {
@Override
@@ -62,179 +65,154 @@ public class ShowUpdatedDiffAction extends AnAction implements DumbAware {
}
private boolean isEnabled(final DataContext dc) {
final Iterable<Pair<VirtualFilePointer,FileStatus>> iterable = VcsDataKeys.UPDATE_VIEW_FILES_ITERABLE.getData(dc);
final Iterable<Pair<VirtualFilePointer, FileStatus>> iterable = VcsDataKeys.UPDATE_VIEW_FILES_ITERABLE.getData(dc);
return iterable != null;
}
public void actionPerformed(AnActionEvent e) {
final DataContext dc = e.getDataContext();
if ((! isVisible(dc)) || (! isEnabled(dc))) return;
if ((!isVisible(dc)) || (!isEnabled(dc))) return;
final Project project = CommonDataKeys.PROJECT.getData(dc);
final Iterable<Pair<VirtualFilePointer, FileStatus>> iterable = VcsDataKeys.UPDATE_VIEW_FILES_ITERABLE.getData(dc);
final Label before = (Label) VcsDataKeys.LABEL_BEFORE.getData(dc);
final Label after = (Label) VcsDataKeys.LABEL_AFTER.getData(dc);
final Iterable<Pair<VirtualFilePointer, FileStatus>> iterable = e.getRequiredData(VcsDataKeys.UPDATE_VIEW_FILES_ITERABLE);
final Label before = (Label)e.getRequiredData(VcsDataKeys.LABEL_BEFORE);
final Label after = (Label)e.getRequiredData(VcsDataKeys.LABEL_AFTER);
final String selectedUrl = VcsDataKeys.UPDATE_VIEW_SELECTED_PATH.getData(dc);
Iterable<Change> changes = new MyIterableWrapper(iterable.iterator(), before, after, project);
Condition<Change> selection = new MySelectionMarker(selectedUrl);
ShowDiffAction.showDiffForChange(project, changes, selection, new ShowDiffContext(DiffDialogHints.FRAME));
MyDiffRequestChain requestChain = new MyDiffRequestChain(project, iterable, before, after, selectedUrl);
DiffManager.getInstance().showDiff(project, requestChain, DiffDialogHints.FRAME);
}
private static class MySelectionMarker implements Condition<Change> {
private final String mySelectedPath;
private boolean myFirstSelected;
private static class MyDiffRequestChain extends UserDataHolderBase implements DiffRequestChain, GoToChangePopupBuilder.Chain {
@Nullable private final Project myProject;
@NotNull private final Label myBefore;
@NotNull private final Label myAfter;
@NotNull private final List<MyDiffRequestProducer> myRequests = new ArrayList<MyDiffRequestProducer>();
public MySelectionMarker(String selectedPath) {
mySelectedPath = selectedPath;
}
private int myIndex;
public boolean value(Change change) {
if (mySelectedPath == null) {
if (myFirstSelected) {
myFirstSelected = true;
return true;
}
return false;
}
final MyCheckpointContentRevision revision = (MyCheckpointContentRevision)(change.getBeforeRevision() == null ? change.getAfterRevision() : change.getBeforeRevision());
final String url = revision.getUrl();
return mySelectedPath.equals(url);
}
}
private static class MyIterableWrapper implements Iterable<Change> {
private final Iterator<Pair<VirtualFilePointer, FileStatus>> myVfIterator;
private final Label myBefore;
private final Label myAfter;
@NotNull private final Project myProject;
private MyIterableWrapper(Iterator<Pair<VirtualFilePointer, FileStatus>> vfIterator,
final Label before,
final Label after,
@NotNull Project project) {
myVfIterator = vfIterator;
public MyDiffRequestChain(@Nullable Project project,
@NotNull Iterable<Pair<VirtualFilePointer, FileStatus>> iterable,
@NotNull Label before,
@NotNull Label after,
@Nullable String selectedUrl) {
myProject = project;
myBefore = before;
myAfter = after;
myProject = project;
}
public Iterator<Change> iterator() {
return new MyIteratorWrapper(myVfIterator, myBefore, myAfter, myProject);
}
}
private static class MyLoader {
private final Label myLabel;
private MyLoader(Label label) {
myLabel = label;
}
@Nullable
public String convert(final VirtualFilePointer pointer, @NotNull Project project) {
if (pointer == null) return null;
final String path = pointer.getPresentableUrl();
final ByteContent byteContent = myLabel.getByteContent(FileUtil.toSystemIndependentName(path));
if (byteContent == null || byteContent.isDirectory() || byteContent.getBytes() == null) {
return null;
int selected = -1;
for (Pair<VirtualFilePointer, FileStatus> pair : iterable) {
if (selected == -1 && pair.first.getUrl().equals(selectedUrl)) selected = myRequests.size();
myRequests.add(new MyDiffRequestProducer(pair.first, pair.second));
}
final VirtualFile vf = pointer.getFile();
if (vf == null) {
return LoadTextUtil.getTextByBinaryPresentation(byteContent.getBytes(), EncodingProjectManager.getInstance(project).getDefaultCharset()).toString();
}
else {
return LoadTextUtil.getTextByBinaryPresentation(byteContent.getBytes(), vf).toString();
}
}
}
private static class MyCheckpointContentRevision implements ContentRevision {
private SoftReference<String> myContent;
private final MyLoader myLoader;
private final VirtualFilePointer myPointer;
private final boolean myBefore;
@NotNull private final Project myProject;
private MyCheckpointContentRevision(final VirtualFilePointer pointer, final MyLoader loader, final boolean before, @NotNull Project project) {
myLoader = loader;
myPointer = pointer;
myBefore = before;
myProject = project;
}
public String getContent() throws VcsException {
final String s = com.intellij.reference.SoftReference.dereference(myContent);
if (s != null) {
return s;
}
final String loaded = myLoader.convert(myPointer, myProject);
myContent = new SoftReference<String>(loaded);
return loaded;
}
public String getUrl() {
return myPointer.getUrl();
if (selected != -1) myIndex = selected;
}
@NotNull
public FilePath getFile() {
return VcsUtil.getFilePath(myPointer.getPresentableUrl(), false);
@Override
public List<MyDiffRequestProducer> getRequests() {
return myRequests;
}
@Override
public int getIndex() {
return myIndex;
}
@Override
public void setIndex(int index) {
myIndex = index;
}
@NotNull
public VcsRevisionNumber getRevisionNumber() {
return new VcsRevisionNumber() {
public String asString() {
return myBefore ? "Before update" : "After update";
@Override
public AnAction createGoToChangeAction(@NotNull Consumer<Integer> onSelected) {
return new ChangeGoToChangePopupAction.Fake<MyDiffRequestChain>(this, myIndex, onSelected) {
@NotNull
@Override
protected FilePath getFilePath(int index) {
return myRequests.get(index).getFilePath();
}
public int compareTo(VcsRevisionNumber o) {
return myBefore ? -1 : 1;
@NotNull
@Override
protected FileStatus getFileStatus(int index) {
return myRequests.get(index).getFileStatus();
}
};
}
}
private static class MyIteratorWrapper implements Iterator<Change> {
private final MyLoader myBeforeLoader;
private final MyLoader myAfterLoader;
private final Iterator<Pair<VirtualFilePointer, FileStatus>> myVfIterator;
@NotNull private final Project myProject;
private class MyDiffRequestProducer implements DiffRequestProducer {
@NotNull private final VirtualFilePointer myFilePointer;
@NotNull private final FileStatus myFileStatus;
@NotNull private final FilePath myFilePath;
public MyIteratorWrapper(final Iterator<Pair<VirtualFilePointer, FileStatus>> vfIterator,
final Label before,
final Label after,
@NotNull Project project) {
myVfIterator = vfIterator;
myProject = project;
myBeforeLoader = new MyLoader(before);
myAfterLoader = new MyLoader(after);
}
public MyDiffRequestProducer(@NotNull VirtualFilePointer filePointer, @NotNull FileStatus fileStatus) {
myFilePointer = filePointer;
myFileStatus = fileStatus;
public boolean hasNext() {
return myVfIterator.hasNext();
}
public Change next() {
final Pair<VirtualFilePointer, FileStatus> pair = myVfIterator.next();
final VirtualFilePointer pointer = pair.getFirst();
MyCheckpointContentRevision before = new MyCheckpointContentRevision(pointer, myBeforeLoader, true, myProject);
MyCheckpointContentRevision after = new MyCheckpointContentRevision(pointer, myAfterLoader, false, myProject);
if (FileStatus.ADDED.equals(pair.getSecond())) {
before = null;
} else if (FileStatus.DELETED.equals(pair.getSecond())) {
after = null;
myFilePath = VcsUtil.getFilePath(myFilePointer.getPresentableUrl(), false);
}
@NotNull
@Override
public String getName() {
return myFilePointer.getUrl();
}
@NotNull
public FilePath getFilePath() {
return myFilePath;
}
@NotNull
public FileStatus getFileStatus() {
return myFileStatus;
}
@NotNull
@Override
public DiffRequest process(@NotNull UserDataHolder context, @NotNull ProgressIndicator indicator)
throws DiffRequestProducerException, ProcessCanceledException {
try {
DiffContent content1;
DiffContent content2;
if (FileStatus.ADDED.equals(myFileStatus)) {
content1 = DiffContentFactory.getInstance().createEmpty();
}
else {
byte[] bytes1 = loadContent(myFilePointer, myBefore);
content1 = DiffContentFactoryImpl.getInstanceImpl().createFromBytes(myProject, myFilePath, bytes1);
}
if (FileStatus.DELETED.equals(myFileStatus)) {
content2 = DiffContentFactory.getInstance().createEmpty();
}
else {
byte[] bytes2 = loadContent(myFilePointer, myAfter);
content2 = DiffContentFactoryImpl.getInstanceImpl().createFromBytes(myProject, myFilePath, bytes2);
}
String title = DiffRequestFactoryImpl.getContentTitle(myFilePath);
return new SimpleDiffRequest(title, content1, content2, "Before update", "After update");
}
catch (IOException e) {
throw new DiffRequestProducerException("Can't load content", e);
}
}
return new Change(before, after, pair.getSecond());
}
public void remove() {
throw new UnsupportedOperationException();
@NotNull
private static byte[] loadContent(@NotNull VirtualFilePointer filePointer, @NotNull Label label) throws DiffRequestProducerException {
String path = filePointer.getPresentableUrl();
ByteContent byteContent = label.getByteContent(FileUtil.toSystemIndependentName(path));
if (byteContent == null || byteContent.isDirectory() || byteContent.getBytes() == null) {
throw new DiffRequestProducerException("Can't load content");
}
return byteContent.getBytes();
}
}
}