From 7f59e537428f6b92aa1cf2d1b6864e8b2e7d028d Mon Sep 17 00:00:00 2001 From: Irina Chernushina Date: Thu, 23 Apr 2009 16:24:50 +0400 Subject: [PATCH] VCS: Ctrl+D for Version Control | Updated Files. not completed, does not work from very start of IDEA. to be completed (+ anton makeev changes) --- .../history/core/ByteContentRetriever.java | 2 +- .../intellij/history/core/ChangeSetsProcessor.java | 6 +- .../src/com/intellij/history/core/LocalVcs.java | 30 +++ .../intellij/history/core/RevisionsCollector.java | 2 +- .../history/integration/CheckpointImpl.java | 13 +- .../src/com/intellij/history/ByteContent.java | 19 ++ .../src/messages/VcsBundle.properties | 2 + .../src/com/intellij/openapi/vcs/VcsDataKeys.java | 5 + .../intellij/openapi/vcs/update/UpdatedFiles.java | 2 +- .../vcs/changes/actions/ChangeDiffRequest.java | 48 +++-- .../vcs/changes/actions/ShowDiffAction.java | 78 ++++++-- .../vcs/update/AbstractCommonUpdateAction.java | 13 ++ .../openapi/vcs/update/ShowUpdatedDiffAction.java | 220 +++++++++++++++++++++ .../openapi/vcs/update/UpdateInfoTree.java | 80 +++++++- 14 files changed, 473 insertions(+), 47 deletions(-) create mode 100644 lvcs/openapi/src/com/intellij/history/ByteContent.java create mode 100644 vcs-impl/src/com/intellij/openapi/vcs/update/ShowUpdatedDiffAction.java diff --git a/lvcs/impl/src/com/intellij/history/core/ByteContentRetriever.java b/lvcs/impl/src/com/intellij/history/core/ByteContentRetriever.java index a5651ebfd3..159558a5ea 100644 --- a/lvcs/impl/src/com/intellij/history/core/ByteContentRetriever.java +++ b/lvcs/impl/src/com/intellij/history/core/ByteContentRetriever.java @@ -60,7 +60,7 @@ public class ByteContentRetriever extends ChangeSetsProcessor { } @Override - protected void nothingToVisit(long timestamp) { + protected void nothingToVisit() { // visit current version doVisit(); } diff --git a/lvcs/impl/src/com/intellij/history/core/ChangeSetsProcessor.java b/lvcs/impl/src/com/intellij/history/core/ChangeSetsProcessor.java index 9d0ed39faf..20722990cf 100644 --- a/lvcs/impl/src/com/intellij/history/core/ChangeSetsProcessor.java +++ b/lvcs/impl/src/com/intellij/history/core/ChangeSetsProcessor.java @@ -20,7 +20,7 @@ public abstract class ChangeSetsProcessor { List changes = collectChanges(); if (changes.isEmpty()) { - nothingToVisit(myEntry.getTimestamp()); + nothingToVisit(); return; } @@ -41,11 +41,11 @@ public abstract class ChangeSetsProcessor { protected abstract List collectChanges(); - protected abstract void nothingToVisit(long timestamp); + protected abstract void nothingToVisit(); protected abstract void visitLabel(Change c); protected abstract void visitRegular(Change c); protected abstract void visitFirstAvailableNonCreational(Change c); -} \ No newline at end of file +} diff --git a/lvcs/impl/src/com/intellij/history/core/LocalVcs.java b/lvcs/impl/src/com/intellij/history/core/LocalVcs.java index a79a760a34..da77356b00 100644 --- a/lvcs/impl/src/com/intellij/history/core/LocalVcs.java +++ b/lvcs/impl/src/com/intellij/history/core/LocalVcs.java @@ -1,6 +1,7 @@ package com.intellij.history.core; import com.intellij.diagnostic.Diagnostic; +import com.intellij.history.ByteContent; import com.intellij.history.Clock; import com.intellij.history.FileRevisionTimestampComparator; import com.intellij.history.core.changes.*; @@ -17,6 +18,7 @@ import com.intellij.util.concurrency.JBLock; import com.intellij.util.concurrency.JBReentrantReadWriteLock; import com.intellij.util.concurrency.LockFactory; import org.jetbrains.annotations.TestOnly; +import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.util.ArrayList; @@ -386,6 +388,34 @@ public class LocalVcs { } } + @Nullable + public ByteContent getByteContentBefore(String path, Change change) { + readAll(); + try { + Change found = null; + for (Change each : myChangeList.getChanges()) { + for (Change eachSubChange : each.getChanges()) { + if (change == eachSubChange) { + found = each; + break; + } + } + } + if (found == null) return null; + + Revision revision = new RevisionAfterChange(myRoot, myRoot, myChangeList, found); + Entry entry = revision.getEntry().findEntry(path); + if (entry == null) return null; + if (entry.isDirectory()) { + return new ByteContent(true, null); + } + return new ByteContent(false, entry.getContent().getBytesIfAvailable()); + } + finally { + unreadAll(); + } + } + public List getRevisionsFor(String path) { readAll(); try { diff --git a/lvcs/impl/src/com/intellij/history/core/RevisionsCollector.java b/lvcs/impl/src/com/intellij/history/core/RevisionsCollector.java index 9e83e7f5d4..563d8d7e8c 100644 --- a/lvcs/impl/src/com/intellij/history/core/RevisionsCollector.java +++ b/lvcs/impl/src/com/intellij/history/core/RevisionsCollector.java @@ -32,7 +32,7 @@ public class RevisionsCollector extends ChangeSetsProcessor { } @Override - protected void nothingToVisit(long timestamp) { + protected void nothingToVisit() { myResult.add(new CurrentRevision(myEntry)); } diff --git a/lvcs/impl/src/com/intellij/history/integration/CheckpointImpl.java b/lvcs/impl/src/com/intellij/history/integration/CheckpointImpl.java index 15d5b88d47..218102cb4f 100644 --- a/lvcs/impl/src/com/intellij/history/integration/CheckpointImpl.java +++ b/lvcs/impl/src/com/intellij/history/integration/CheckpointImpl.java @@ -1,13 +1,15 @@ package com.intellij.history.integration; +import com.intellij.history.ByteContent; +import com.intellij.history.Checkpoint; import com.intellij.history.core.LocalVcs; -import com.intellij.history.core.tree.Entry; import com.intellij.history.core.changes.Change; -import com.intellij.history.core.changes.ContentChange; import com.intellij.history.core.changes.ChangeVisitor; +import com.intellij.history.core.changes.ContentChange; import com.intellij.history.core.changes.StructuralChange; +import com.intellij.history.core.tree.Entry; import com.intellij.history.integration.revertion.ChangeRevertionVisitor; -import com.intellij.history.Checkpoint; +import org.jetbrains.annotations.Nullable; import java.io.IOException; @@ -30,6 +32,11 @@ public class CheckpointImpl implements Checkpoint { doRevert(false); } + @Nullable + public ByteContent getByteContentBefore(String path) { + return myVcs.getByteContentBefore(path, myLastChange); + } + private void doRevert(boolean revertLastChange) throws IOException { ChangeVisitor v = new ChangeRevertionVisitor(myGateway); myVcs.acceptWrite(new SelectiveChangeVisitor(v, revertLastChange)); diff --git a/lvcs/openapi/src/com/intellij/history/ByteContent.java b/lvcs/openapi/src/com/intellij/history/ByteContent.java new file mode 100644 index 0000000000..1c70dd44e3 --- /dev/null +++ b/lvcs/openapi/src/com/intellij/history/ByteContent.java @@ -0,0 +1,19 @@ +package com.intellij.history; + +public class ByteContent { + private final boolean myIsDirectory; + private final byte[] myBytes; + + public ByteContent(boolean isDirectory, byte[] bytes) { + myIsDirectory = isDirectory; + myBytes = bytes; + } + + public boolean isIsDirectory() { + return myIsDirectory; + } + + public byte[] getBytes() { + return myBytes; + } +} diff --git a/platform-resources_eng/src/messages/VcsBundle.properties b/platform-resources_eng/src/messages/VcsBundle.properties index a9d7e8b7bc..e3b7ceec15 100644 --- a/platform-resources_eng/src/messages/VcsBundle.properties +++ b/platform-resources_eng/src/messages/VcsBundle.properties @@ -195,6 +195,8 @@ toolwindow.title.update.action.info={0} Info update.tree.node.size.statistics={0,choice, 0#no items|1#1 item|2#{0, number} items} toolwindow.title.update.project=Update Project ({0}) action.name.group.by.packages=Group by Packages +updated.info.tree.show.diff.text=Show Diff +updated.info.tree.show.diff.description=Show Diff with version before update messge.text.cannot.save.settings=Cannot perform operation: {0} action.name.check.status=Chec_k Status action.name.check.scope.status=Chec_k {0} Status diff --git a/vcs-api/src/com/intellij/openapi/vcs/VcsDataKeys.java b/vcs-api/src/com/intellij/openapi/vcs/VcsDataKeys.java index 4bb0520959..b346606d38 100644 --- a/vcs-api/src/com/intellij/openapi/vcs/VcsDataKeys.java +++ b/vcs-api/src/com/intellij/openapi/vcs/VcsDataKeys.java @@ -29,6 +29,7 @@ import com.intellij.openapi.vcs.changes.ChangeList; import com.intellij.openapi.vcs.changes.ChangeRequestChain; import com.intellij.openapi.vcs.history.VcsFileRevision; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.pointers.VirtualFilePointer; import org.jetbrains.annotations.NonNls; import java.io.File; @@ -50,4 +51,8 @@ public interface VcsDataKeys { DataKey SELECTED_CHANGES = DataKey.create("ChangeListView.SelectedChange"); DataKey CHANGE_LEAD_SELECTION = DataKey.create("ChangeListView.ChangeLeadSelection"); DataKey DIFF_REQUEST_CHAIN = DataKey.create("diffRequestChain"); + DataKey UPDATE_VIEW_SELECTED_PATH = DataKey.create("AbstractCommonUpdateAction.UpdateViewSelectedPath"); + DataKey> UPDATE_VIEW_FILES_ITERABLE = DataKey.create("AbstractCommonUpdateAction.UpdatedFilesIterable"); + DataKey CHECKPOINT_BEFORE = DataKey.create("CHECKPOINT_BEFORE"); + DataKey CHECKPOINT_AFTER = DataKey.create("CHECKPOINT_AFTER"); } \ No newline at end of file diff --git a/vcs-api/src/com/intellij/openapi/vcs/update/UpdatedFiles.java b/vcs-api/src/com/intellij/openapi/vcs/update/UpdatedFiles.java index fff932a522..2eba941d0f 100644 --- a/vcs-api/src/com/intellij/openapi/vcs/update/UpdatedFiles.java +++ b/vcs-api/src/com/intellij/openapi/vcs/update/UpdatedFiles.java @@ -18,8 +18,8 @@ package com.intellij.openapi.vcs.update; import com.intellij.openapi.util.InvalidDataException; import com.intellij.openapi.util.JDOMExternalizable; import com.intellij.openapi.util.WriteExternalException; -import com.intellij.openapi.vcs.VcsBundle; import com.intellij.openapi.vcs.AbstractVcs; +import com.intellij.openapi.vcs.VcsBundle; import com.intellij.openapi.vcs.history.VcsRevisionNumber; import org.jdom.Element; diff --git a/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/ChangeDiffRequest.java b/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/ChangeDiffRequest.java index bcad1fdfb1..8c9936198f 100644 --- a/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/ChangeDiffRequest.java +++ b/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/ChangeDiffRequest.java @@ -1,30 +1,28 @@ package com.intellij.openapi.vcs.changes.actions; +import com.intellij.CommonBundle; import com.intellij.openapi.actionSystem.ActionManager; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.diff.*; -import com.intellij.openapi.fileTypes.FileTypes; import com.intellij.openapi.fileTypes.FileType; +import com.intellij.openapi.fileTypes.FileTypes; import com.intellij.openapi.fileTypes.ex.FileTypeChooser; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.NullableFactory; -import com.intellij.openapi.vcs.VcsBundle; import com.intellij.openapi.vcs.FilePath; +import com.intellij.openapi.vcs.VcsBundle; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.changes.Change; +import com.intellij.openapi.vcs.changes.ChangeRequestChain; import com.intellij.openapi.vcs.changes.ContentRevision; import com.intellij.openapi.vcs.changes.CurrentContentRevision; -import com.intellij.openapi.vcs.changes.ChangeRequestChain; -import com.intellij.openapi.ui.Messages; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.CommonBundle; -import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import javax.swing.*; -import java.util.ArrayList; -import java.util.Collections; import java.util.List; /** @@ -40,10 +38,9 @@ public class ChangeDiffRequest implements ChangeRequestChain { private final AnAction myNextChangeAction; private final Project myProject; - public ChangeDiffRequest(final Project project, final Change[] changes, final ShowDiffAction.DiffExtendUIFactory actionsFactory) { + public ChangeDiffRequest(final Project project, final List changes, final ShowDiffAction.DiffExtendUIFactory actionsFactory) { myProject = project; - myChanges = new ArrayList(changes.length); - Collections.addAll(myChanges, changes); + myChanges = changes; myIndex = 0; myActionsFactory = actionsFactory; @@ -52,6 +49,15 @@ public class ChangeDiffRequest implements ChangeRequestChain { myNextChangeAction = ActionManager.getInstance().getAction("Diff.NextChange"); } + public boolean quickCheckHaveStuff() { + if (myChanges.isEmpty()) return false; + if (myChanges.size() == 1) { + final Change change = myChanges.get(0); + return checkContentsAvailable(change.getBeforeRevision(), change.getAfterRevision()); + } + return true; + } + @Nullable public SimpleDiffRequest init(final int idx) { if (idx < 0 || idx > (myChanges.size() - 1)) return null; @@ -235,9 +241,11 @@ public class ChangeDiffRequest implements ChangeRequestChain { if ((bRev != null && (bRev.getFile().getFileType().isBinary() || bRev.getFile().isDirectory())) || (aRev != null && (aRev.getFile().getFileType().isBinary() || aRev.getFile().isDirectory()))) { if (bRev != null && bRev.getFile().getFileType() == FileTypes.UNKNOWN && !bRev.getFile().isDirectory()) { + if (! checkContentsAvailable(bRev, aRev)) return false; if (!checkAssociate(myProject, bRev.getFile())) return false; } else if (aRev != null && aRev.getFile().getFileType() == FileTypes.UNKNOWN && !aRev.getFile().isDirectory()) { + if (! checkContentsAvailable(bRev, aRev)) return false; if (!checkAssociate(myProject, aRev.getFile())) return false; } else { @@ -247,6 +255,22 @@ public class ChangeDiffRequest implements ChangeRequestChain { return true; } + private static boolean checkContentsAvailable(final ContentRevision bRev, final ContentRevision aRev) { + String bContents = null; + try { + bContents = bRev.getContent(); + } catch (VcsException e) { + // + } + String aContents = null; + try { + aContents = aRev.getContent(); + } catch (VcsException e) { + // + } + return (bContents != null) || (aContents != null); + } + private static boolean checkAssociate(final Project project, final FilePath file) { int rc = Messages.showDialog(project, VcsBundle.message("diff.unknown.file.type.prompt", file.getName()), @@ -261,4 +285,4 @@ public class ChangeDiffRequest implements ChangeRequestChain { } return false; } -} \ No newline at end of file +} diff --git a/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/ShowDiffAction.java b/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/ShowDiffAction.java index 68aacaf320..f5761f898f 100644 --- a/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/ShowDiffAction.java +++ b/vcs-impl/src/com/intellij/openapi/vcs/changes/actions/ShowDiffAction.java @@ -19,6 +19,7 @@ import com.intellij.openapi.vcs.VcsBundle; import com.intellij.openapi.vcs.VcsDataKeys; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.changes.*; +import com.intellij.util.NotNullFunction; import org.jetbrains.annotations.Nullable; import javax.swing.*; @@ -156,23 +157,52 @@ public class ShowDiffAction extends AnAction implements DumbAware { JComponent createBottomComponent(); } + public static void showDiffForChange(final Iterable changes, final NotNullFunction selectionChecker, + final Project project, @Nullable DiffExtendUIFactory actionsFactory, final boolean showFrame) { + int cnt = 0; + int newIndex = -1; + final List changeList = new ArrayList(); + for (Change change : changes) { + if (! directoryOrBinary(change)) { + changeList.add(change); + if ((newIndex == -1) && selectionChecker.fun(change)) { + newIndex = cnt; + } + ++ cnt; + } + } + if (changeList.isEmpty()) { + return; + } + if (newIndex < 0) { + newIndex = 0; + } + + showDiffImpl(project, changeList, newIndex, actionsFactory, showFrame); + } + public static void showDiffForChange(Change[] changes, int index, final Project project, @Nullable DiffExtendUIFactory actionsFactory, final boolean showFrame) { Change selectedChange = changes [index]; - changes = filterDirectoryAndBinaryChanges(changes); - if (changes.length == 0) { + final List changeList = filterDirectoryAndBinaryChanges(changes); + if (changeList.isEmpty()) { return; } index = 0; - for(int i=0; i changeList, int index, DiffExtendUIFactory actionsFactory, boolean showFrame) { final DiffTool tool = DiffManager.getInstance().getDiffTool(); - final ChangeDiffRequest request = new ChangeDiffRequest(project, changes, actionsFactory); + final ChangeDiffRequest request = new ChangeDiffRequest(project, changeList, actionsFactory); + if (! request.quickCheckHaveStuff()) return; final SimpleDiffRequest simpleRequest = request.init(index); if (simpleRequest != null) { simpleRequest.passForDataContext(VcsDataKeys.DIFF_REQUEST_CHAIN, request); @@ -183,34 +213,40 @@ public class ShowDiffAction extends AnAction implements DumbAware { else { simpleRequest.addHint(DiffTool.HINT_SHOW_MODAL_DIALOG); } - if (changes.length > 1) { + if (changeList.size() > 1) { simpleRequest.addHint(DiffTool.HINT_ALLOW_NO_DIFFERENCES); } tool.show(simpleRequest); } } - private static Change[] filterDirectoryAndBinaryChanges(final Change[] changes) { - ArrayList changesList = new ArrayList(); + private static boolean directoryOrBinary(final Change change) { + // todo instead for repository tab, filter directories (? ask remotely ? non leaf nodes) + /*if ((change.getBeforeRevision() instanceof BinaryContentRevision) || (change.getAfterRevision() instanceof BinaryContentRevision)) { + changesList.remove(i); + continue; + }*/ + final FilePath path = ChangesUtil.getFilePath(change); + if (path.isDirectory()) { + return true; + } + final FileType type = path.getFileType(); + if ((! FileTypes.UNKNOWN.equals(type)) && (type.isBinary())) { + return true; + } + return false; + } + + private static List filterDirectoryAndBinaryChanges(final Change[] changes) { + final ArrayList changesList = new ArrayList(); Collections.addAll(changesList, changes); for(int i=changesList.size()-1; i >= 0; i--) { final Change change = changesList.get(i); - // todo instead for repository tab, filter directories (? ask remotely ? non leaf nodes) - /*if ((change.getBeforeRevision() instanceof BinaryContentRevision) || (change.getAfterRevision() instanceof BinaryContentRevision)) { - changesList.remove(i); - continue; - }*/ - final FilePath path = ChangesUtil.getFilePath(change); - if (path.isDirectory()) { - changesList.remove(i); - continue; - } - final FileType type = path.getFileType(); - if ((! FileTypes.UNKNOWN.equals(type)) && (type.isBinary())) { + if (directoryOrBinary(change)) { changesList.remove(i); } } - return changesList.toArray(new Change[changesList.size()]); + return changesList; } private static boolean checkNotifyBinaryDiff(final Change selectedChange) { diff --git a/vcs-impl/src/com/intellij/openapi/vcs/update/AbstractCommonUpdateAction.java b/vcs-impl/src/com/intellij/openapi/vcs/update/AbstractCommonUpdateAction.java index 2c2f5c5e99..c4d025f9a7 100644 --- a/vcs-impl/src/com/intellij/openapi/vcs/update/AbstractCommonUpdateAction.java +++ b/vcs-impl/src/com/intellij/openapi/vcs/update/AbstractCommonUpdateAction.java @@ -31,6 +31,7 @@ */ package com.intellij.openapi.vcs.update; +import com.intellij.history.Checkpoint; import com.intellij.history.LocalHistory; import com.intellij.history.LocalHistoryAction; import com.intellij.openapi.actionSystem.AnActionEvent; @@ -308,6 +309,9 @@ public abstract class AbstractCommonUpdateAction extends AbstractVcsAction { private final Map myContextInfo; private VcsDirtyScopeManager myDirtyScopeManager; + private Checkpoint myBefore; + private Checkpoint myAfter; + public Updater(final Project project, final FilePath[] roots, final Map> vcsToVirtualFiles) { super(project, getTemplatePresentation().getText(), true, VcsConfiguration.getInstance(project).getUpdateOption()); myProject = project; @@ -364,6 +368,9 @@ public abstract class AbstractCommonUpdateAction extends AbstractVcsAction { private void runImpl(@NotNull final ProgressIndicator indicator) { ProjectManagerEx.getInstanceEx().blockReloadingProjectOnExternalChanges(); myProjectLevelVcsManager.startBackgroundVcsOperation(); + + myBefore = LocalHistory.putCheckpoint(myProject); + ProgressIndicator progressIndicator = ProgressManager.getInstance().getProgressIndicator(); try { @@ -394,6 +401,7 @@ public abstract class AbstractCommonUpdateAction extends AbstractVcsAction { } doVfsRefresh(); } finally { + myAfter = LocalHistory.putCheckpoint(myProject); myProjectLevelVcsManager.stopBackgroundVcsOperation(); } } @@ -556,6 +564,11 @@ public abstract class AbstractCommonUpdateAction extends AbstractVcsAction { restoreUpdateTree.registerUpdateInformation(myUpdatedFiles, myActionInfo); final String text = getTemplatePresentation().getText() + ((willBeContinued || (myUpdateNumber > 1)) ? ("#" + myUpdateNumber) : ""); final UpdateInfoTree updateInfoTree = myProjectLevelVcsManager.showUpdateProjectInfo(myUpdatedFiles, text, myActionInfo); + + updateInfoTree.setBefore(myBefore); + updateInfoTree.setAfter(myAfter); + + // todo make temporal listener of changes reload if (updateInfoTree != null) { updateInfoTree.setCanGroupByChangeList(canGroupByChangelist(myVcsToVirtualFiles.keySet())); final MessageBusConnection messageBusConnection = myProject.getMessageBus().connect(); diff --git a/vcs-impl/src/com/intellij/openapi/vcs/update/ShowUpdatedDiffAction.java b/vcs-impl/src/com/intellij/openapi/vcs/update/ShowUpdatedDiffAction.java new file mode 100644 index 0000000000..ca1ed31edf --- /dev/null +++ b/vcs-impl/src/com/intellij/openapi/vcs/update/ShowUpdatedDiffAction.java @@ -0,0 +1,220 @@ +package com.intellij.openapi.vcs.update; + +import com.intellij.history.ByteContent; +import com.intellij.history.Checkpoint; +import com.intellij.openapi.actionSystem.*; +import com.intellij.openapi.fileEditor.impl.LoadTextUtil; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.IconLoader; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vcs.*; +import com.intellij.openapi.vcs.changes.Change; +import com.intellij.openapi.vcs.changes.ContentRevision; +import com.intellij.openapi.vcs.changes.actions.ShowDiffAction; +import com.intellij.openapi.vcs.history.VcsRevisionNumber; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.encoding.EncodingManager; +import com.intellij.openapi.vfs.pointers.VirtualFilePointer; +import com.intellij.util.NotNullFunction; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.io.File; +import java.lang.ref.SoftReference; +import java.util.Iterator; + +public class ShowUpdatedDiffAction extends AnAction { + private static Icon ourIcon; + private static String ourText; + private static String ourDescription; + + private static void initData() { + ourIcon = IconLoader.getIcon("/actions/diff.png"); + ourText = VcsBundle.message("updated.info.tree.show.diff.text"); + ourDescription = VcsBundle.message("updated.info.tree.show.diff.description"); + } + + @Override + public void update(AnActionEvent e) { + if (ourIcon == null) { + initData(); + } + + final DataContext dc = e.getDataContext(); + + final Presentation presentation = e.getPresentation(); + presentation.setIcon(ourIcon); + presentation.setText(ourText); + presentation.setDescription(ourDescription); + + //presentation.setVisible(isVisible(dc)); + presentation.setEnabled(isVisible(dc) && isEnabled(dc)); + } + + private boolean isVisible(final DataContext dc) { + final Project project = PlatformDataKeys.PROJECT.getData(dc); + return (project != null) && (VcsDataKeys.CHECKPOINT_BEFORE.getData(dc) != null) && (VcsDataKeys.CHECKPOINT_AFTER.getData(dc) != null); + } + + private boolean isEnabled(final DataContext dc) { + final Iterable 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; + + final Project project = PlatformDataKeys.PROJECT.getData(dc); + final Iterable iterable = VcsDataKeys.UPDATE_VIEW_FILES_ITERABLE.getData(dc); + final Checkpoint before = (Checkpoint) VcsDataKeys.CHECKPOINT_BEFORE.getData(dc); + final Checkpoint after = (Checkpoint) VcsDataKeys.CHECKPOINT_AFTER.getData(dc); + + final String selectedUrl = VcsDataKeys.UPDATE_VIEW_SELECTED_PATH.getData(dc); + + ShowDiffAction.showDiffForChange(new MyIterableWrapper(iterable.iterator(), before, after), new MySelectionMarker(selectedUrl), + project, ShowDiffAction.DiffExtendUIFactory.NONE, true); + } + + private static class MySelectionMarker implements NotNullFunction { + private final String mySelectedPath; + private boolean myFirstSelected; + + public MySelectionMarker(String selectedPath) { + mySelectedPath = selectedPath; + } + + @NotNull + public Boolean fun(Change change) { + if (mySelectedPath == null) { + if (myFirstSelected) { + myFirstSelected = true; + return true; + } + return false; + } + final String url = ((MyCheckpointContentRevision)change.getBeforeRevision()).getUrl(); + return mySelectedPath.equals(url); + } + } + + private static class MyIterableWrapper implements Iterable { + private final Iterator myVfIterator; + private final Checkpoint myBefore; + private final Checkpoint myAfter; + + private MyIterableWrapper(Iterator vfIterator, final Checkpoint before, final Checkpoint after) { + myVfIterator = vfIterator; + myBefore = before; + myAfter = after; + } + + public Iterator iterator() { + return new MyIteratorWrapper(myVfIterator, myBefore, myAfter); + } + } + + private static class MyLoader { + private final Checkpoint myCheckpoint; + + private MyLoader(Checkpoint checkpoint) { + myCheckpoint = checkpoint; + } + + @Nullable + public String convert(final VirtualFilePointer pointer) { + final String path = pointer.getPresentableUrl(); + final ByteContent byteContent = myCheckpoint.getByteContentBefore(FileUtil.toSystemIndependentName(path)); + if (byteContent == null || byteContent.isIsDirectory()) { + return null; + } + final VirtualFile vf = pointer.getFile(); + if (vf == null) { + return LoadTextUtil.getTextByBinaryPresentation(byteContent.getBytes(), EncodingManager.getInstance().getDefaultCharset()).toString(); + } else { + return LoadTextUtil.getTextByBinaryPresentation(byteContent.getBytes(), vf).toString(); + } + } + } + + private static class MyCheckpointContentRevision implements ContentRevision { + private SoftReference myContent; + private final MyLoader myLoader; + private final VirtualFilePointer myPointer; + private final boolean myBefore; + + private MyCheckpointContentRevision(final VirtualFilePointer pointer, final MyLoader loader, final boolean before) { + myLoader = loader; + myPointer = pointer; + myBefore = before; + } + + public String getContent() throws VcsException { + if (myContent != null) { + final String s = myContent.get(); + if (s != null) { + return s; + } + } + + final String loaded = myLoader.convert(myPointer); + myContent = new SoftReference(loaded); + + return loaded; + } + + public String getUrl() { + return myPointer.getUrl(); + } + + @NotNull + public FilePath getFile() { + final VirtualFile vf = myPointer.getFile(); + if (vf != null) { + return new FilePathImpl(vf); + } + return new FilePathImpl(new File(myPointer.getPresentableUrl()), false); + } + + @NotNull + public VcsRevisionNumber getRevisionNumber() { + return new VcsRevisionNumber() { + public String asString() { + return myBefore ? "Before update" : "After update"; + } + + public int compareTo(VcsRevisionNumber o) { + return myBefore ? -1 : 1; + } + }; + } + } + + private static class MyIteratorWrapper implements Iterator { + private final MyLoader myBeforeLoader; + private final MyLoader myAfterLoader; + private final Iterator myVfIterator; + + public MyIteratorWrapper(final Iterator vfIterator, final Checkpoint before, final Checkpoint after) { + myVfIterator = vfIterator; + myBeforeLoader = new MyLoader(before); + myAfterLoader = new MyLoader(after); + } + + public boolean hasNext() { + return myVfIterator.hasNext(); + } + + public Change next() { + final VirtualFilePointer pointer = myVfIterator.next(); + + return new Change(new MyCheckpointContentRevision(pointer, myBeforeLoader, true), + new MyCheckpointContentRevision(pointer, myAfterLoader, false)); + } + + public void remove() { + throw new UnsupportedOperationException(); + } + } +} diff --git a/vcs-impl/src/com/intellij/openapi/vcs/update/UpdateInfoTree.java b/vcs-impl/src/com/intellij/openapi/vcs/update/UpdateInfoTree.java index 45110255da..dc3090033a 100644 --- a/vcs-impl/src/com/intellij/openapi/vcs/update/UpdateInfoTree.java +++ b/vcs-impl/src/com/intellij/openapi/vcs/update/UpdateInfoTree.java @@ -31,6 +31,7 @@ */ package com.intellij.openapi.vcs.update; +import com.intellij.history.Checkpoint; import com.intellij.ide.DefaultTreeExpander; import com.intellij.ide.TreeExpander; import com.intellij.openapi.Disposable; @@ -50,6 +51,7 @@ import com.intellij.openapi.vcs.changes.committed.CommittedChangesTreeBrowser; import com.intellij.openapi.vcs.changes.committed.RefreshIncomingChangesAction; import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.pointers.VirtualFilePointer; import com.intellij.ui.*; import com.intellij.ui.content.ContentManager; import com.intellij.ui.treeStructure.Tree; @@ -71,15 +73,16 @@ import javax.swing.tree.TreePath; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; import java.io.File; -import java.util.ArrayList; -import java.util.Collections; +import java.util.*; import java.util.List; public class UpdateInfoTree extends PanelWithActionsAndCloseButton implements Disposable { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.vcs.update.UpdateInfoTree"); private VirtualFile mySelectedFile; + private String mySelectedUrl; private final Tree myTree = new Tree(); @NotNull private final Project myProject; private final UpdatedFiles myUpdatedFiles; @@ -98,6 +101,10 @@ public class UpdateInfoTree extends PanelWithActionsAndCloseButton implements Di @NonNls private static final String CARD_CHANGES = "Changes"; private CommittedChangesTreeBrowser myTreeBrowser; private final TreeExpander myTreeExpander; + private final MyTreeIterable myTreeIterable; + + private Checkpoint myBefore; + private Checkpoint myAfter; public UpdateInfoTree(@NotNull ContentManager contentManager, @NotNull Project project, @@ -126,6 +133,7 @@ public class UpdateInfoTree extends PanelWithActionsAndCloseButton implements Di createTree(); init(); myTreeExpander = new DefaultTreeExpander(myTree); + myTreeIterable = new MyTreeIterable(); } public void dispose() { @@ -155,6 +163,9 @@ public class UpdateInfoTree extends PanelWithActionsAndCloseButton implements Di group.add(new GroupByChangeListAction()); group.add(ActionManager.getInstance().getAction(IdeActions.ACTION_EXPAND_ALL)); group.add(ActionManager.getInstance().getAction(IdeActions.ACTION_COLLAPSE_ALL)); + final ShowUpdatedDiffAction diffAction = new ShowUpdatedDiffAction(); + diffAction.registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_D, KeyEvent.CTRL_DOWN_MASK)), this); + group.add(diffAction); } protected JComponent createCenterPanel() { @@ -174,9 +185,12 @@ public class UpdateInfoTree extends PanelWithActionsAndCloseButton implements Di public void valueChanged(TreeSelectionEvent e) { AbstractTreeNode treeNode = (AbstractTreeNode)e.getPath().getLastPathComponent(); if (treeNode instanceof FileTreeNode) { - mySelectedFile = ((FileTreeNode)treeNode).getFilePointer().getFile(); + final VirtualFilePointer pointer = ((FileTreeNode)treeNode).getFilePointer(); + mySelectedUrl = pointer.getUrl(); + mySelectedFile = pointer.getFile(); } else { + mySelectedUrl = null; mySelectedFile = null; } } @@ -232,19 +246,67 @@ public class UpdateInfoTree extends PanelWithActionsAndCloseButton implements Di } else if (VcsDataConstants.IO_FILE_ARRAY.equals(dataId)) { return getFileArray(); - } - else if (DataConstantsEx.TREE_EXPANDER.equals(dataId)) { + } else if (DataConstantsEx.TREE_EXPANDER.equals(dataId)) { if (myGroupByChangeList) { return myTreeBrowser.getTreeExpander(); } else { return myTreeExpander; } + } else if (VcsDataKeys.UPDATE_VIEW_SELECTED_PATH.getName().equals(dataId)) { + return mySelectedUrl; + } else if (VcsDataKeys.UPDATE_VIEW_FILES_ITERABLE.getName().equals(dataId)) { + return myTreeIterable; + } else if (VcsDataKeys.CHECKPOINT_BEFORE.getName().equals(dataId)) { + return myBefore; + } else if (VcsDataKeys.CHECKPOINT_AFTER.getName().equals(dataId)) { + return myAfter; } return super.getData(dataId); } + private class MyTreeIterator implements Iterator { + private final Enumeration myEnum; + private VirtualFilePointer myNext; + + private MyTreeIterator() { + myEnum = myRoot.depthFirstEnumeration(); + step(); + } + + public boolean hasNext() { + return myNext != null; + } + + public VirtualFilePointer next() { + final VirtualFilePointer result = myNext; + step(); + return result; + } + + private void step() { + myNext = null; + while (myEnum.hasMoreElements()) { + final Object o = myEnum.nextElement(); + if (o instanceof FileTreeNode) { + myNext = ((FileTreeNode) o).getFilePointer(); + break; + } + } + } + + public void remove() { + throw new UnsupportedOperationException(); + } + } + + private class MyTreeIterable implements Iterable { + public Iterator iterator() { + return new MyTreeIterator(); + } + } + private VirtualFile[] getVirtualFileArray() { ArrayList result = new ArrayList(); TreePath[] selectionPaths = myTree.getSelectionPaths(); @@ -345,4 +407,12 @@ public class UpdateInfoTree extends PanelWithActionsAndCloseButton implements Di e.getPresentation().setVisible(myCanGroupByChangeList); } } + + public void setBefore(Checkpoint before) { + myBefore = before; + } + + public void setAfter(Checkpoint after) { + myAfter = after; + } } -- 2.11.4.GIT