diff --git a/platform/lvcs-impl/src/com/intellij/history/integration/IdeaGateway.java b/platform/lvcs-impl/src/com/intellij/history/integration/IdeaGateway.java index fce09d52ee38..7d3d2e5a46b7 100644 --- a/platform/lvcs-impl/src/com/intellij/history/integration/IdeaGateway.java +++ b/platform/lvcs-impl/src/com/intellij/history/integration/IdeaGateway.java @@ -16,7 +16,6 @@ package com.intellij.history.integration; -import com.intellij.history.core.Content; import com.intellij.history.core.LocalHistoryFacade; import com.intellij.history.core.Paths; import com.intellij.history.core.StoredContent; @@ -53,7 +52,7 @@ import java.util.Collections; import java.util.List; public class IdeaGateway { - private static final Key> SAVED_DOCUMENT_CONTENT_AND_STAMP_KEY + private static final Key SAVED_DOCUMENT_CONTENT_AND_STAMP_KEY = Key.create("LocalHistory.SAVED_DOCUMENT_CONTENT_AND_STAMP_KEY"); public boolean isVersioned(VirtualFile f) { @@ -170,17 +169,17 @@ public class IdeaGateway { if (!file.isDirectory()) { if (!isVersioned(file)) return null; - Content c; + Pair contentAndStamps; if (forDeletion) { FileDocumentManager m = FileDocumentManager.getInstance(); Document d = m.getCachedDocument(file); // should not try to load document if (d != null && !m.isDocumentUnsaved(d)) d = null; - c = acquireAndClearCurrentContent(file, d); + contentAndStamps = acquireAndClearCurrentContent(file, d); } else { - c = getActualContentNoAcquire(file); + contentAndStamps = getActualContentNoAcquire(file); } - return new FileEntry(file.getName(), c, file.getTimeStamp(), !file.isWritable()); + return new FileEntry(file.getName(), contentAndStamps.first, contentAndStamps.second, !file.isWritable()); } DirectoryEntry newDir = new DirectoryEntry(file.getName()); doCreateChildren(newDir, iterateDBChildren(file), forDeletion); @@ -203,7 +202,6 @@ public class IdeaGateway { VirtualFile f = getFile(d); if (!shouldRegisterDocument(f)) continue; registerDocumentContents(vcs, f, d); - registerDocumentContents(vcs, f, d); } vcs.endChangeSet(null); } @@ -216,70 +214,74 @@ public class IdeaGateway { } private void registerDocumentContents(LocalHistoryFacade vcs, VirtualFile f, Document d) { - Content content = acquireAndUpdateActualContent(f, d); - if (content != null) { - vcs.contentChanged(f.getPath(), content, Clock.getTime()); + Pair contentAndStamp = acquireAndUpdateActualContent(f, d); + if (contentAndStamp != null) { + vcs.contentChanged(f.getPath(), contentAndStamp.first, contentAndStamp.second); } } // returns null is content has not been changes since last time @Nullable - public Content acquireAndUpdateActualContent(VirtualFile f, @Nullable Document d) { - Pair contentAndStamp = f.getUserData(SAVED_DOCUMENT_CONTENT_AND_STAMP_KEY); + public Pair acquireAndUpdateActualContent(VirtualFile f, @Nullable Document d) { + ContentAndTimestamps contentAndStamp = f.getUserData(SAVED_DOCUMENT_CONTENT_AND_STAMP_KEY); if (contentAndStamp == null) { if (d != null) saveDocumentContent(f, d); - return StoredContent.acquireContent(f); + return Pair.create(StoredContent.acquireContent(f), f.getTimeStamp()); } - StoredContent content = contentAndStamp.first; - // if no need to save current document content when simply return and clear stored one if (d == null) { f.putUserData(SAVED_DOCUMENT_CONTENT_AND_STAMP_KEY, null); - return content; + return Pair.create(contentAndStamp.content, contentAndStamp.registeredTimestamp); } // if the stored content equals the current one, do not store it and return null - if (d.getModificationStamp() == contentAndStamp.second) return null; + if (d.getModificationStamp() == contentAndStamp.documentModificationStamp) return null; // is current content has been changed, store it and return the previous one saveDocumentContent(f, d); - return content; + return Pair.create(contentAndStamp.content, contentAndStamp.registeredTimestamp); } private void saveDocumentContent(VirtualFile f, Document d) { f.putUserData(SAVED_DOCUMENT_CONTENT_AND_STAMP_KEY, - Pair.create(StoredContent.acquireContent(bytesFromDocument(d)), d.getModificationStamp())); + new ContentAndTimestamps(Clock.getTime(), + StoredContent.acquireContent(bytesFromDocument(d)), + d.getModificationStamp())); } - public Content acquireAndClearCurrentContent(VirtualFile f, @Nullable Document d) { - Pair contentAndStamp = f.getUserData(SAVED_DOCUMENT_CONTENT_AND_STAMP_KEY); + @NotNull + public Pair acquireAndClearCurrentContent(VirtualFile f, @Nullable Document d) { + ContentAndTimestamps contentAndStamp = f.getUserData(SAVED_DOCUMENT_CONTENT_AND_STAMP_KEY); f.putUserData(SAVED_DOCUMENT_CONTENT_AND_STAMP_KEY, null); if (d != null && contentAndStamp != null) { // if previously stored content was not changed, return it - if (d.getModificationStamp() == contentAndStamp.second) return contentAndStamp.first; + if (d.getModificationStamp() == contentAndStamp.documentModificationStamp) { + return Pair.create(contentAndStamp.content, contentAndStamp.registeredTimestamp); + } } // release previously stored if (contentAndStamp != null) { - contentAndStamp.first.release(); + contentAndStamp.content.release(); } // take document's content if any if (d != null) { - return StoredContent.acquireContent(bytesFromDocument(d)); + return Pair.create(StoredContent.acquireContent(bytesFromDocument(d)), Clock.getTime()); } - return StoredContent.acquireContent(f); + return Pair.create(StoredContent.acquireContent(f), f.getTimeStamp()); } - private Content getActualContentNoAcquire(VirtualFile f) { - Pair result = f.getUserData(SAVED_DOCUMENT_CONTENT_AND_STAMP_KEY); + @NotNull + private Pair getActualContentNoAcquire(VirtualFile f) { + ContentAndTimestamps result = f.getUserData(SAVED_DOCUMENT_CONTENT_AND_STAMP_KEY); if (result == null) { - return StoredContent.transientContent(f); + return Pair.create(StoredContent.transientContent(f), f.getTimeStamp()); } - return result.first; + return Pair.create(result.content, result.registeredTimestamp); } private byte[] bytesFromDocument(Document d) { @@ -319,4 +321,16 @@ public class IdeaGateway { public FileType getFileType(String fileName) { return FileTypeManager.getInstance().getFileTypeByFileName(fileName); } + + private static class ContentAndTimestamps { + long registeredTimestamp; + StoredContent content; + long documentModificationStamp; + + private ContentAndTimestamps(long registeredTimestamp, StoredContent content, long documentModificationStamp) { + this.registeredTimestamp = registeredTimestamp; + this.content = content; + this.documentModificationStamp = documentModificationStamp; + } + } } diff --git a/platform/lvcs-impl/src/com/intellij/history/integration/LocalHistoryEventDispatcher.java b/platform/lvcs-impl/src/com/intellij/history/integration/LocalHistoryEventDispatcher.java index 00180edb042b..55dedc6a0334 100644 --- a/platform/lvcs-impl/src/com/intellij/history/integration/LocalHistoryEventDispatcher.java +++ b/platform/lvcs-impl/src/com/intellij/history/integration/LocalHistoryEventDispatcher.java @@ -16,12 +16,13 @@ package com.intellij.history.integration; -import com.intellij.history.core.Content; import com.intellij.history.core.LocalHistoryFacade; +import com.intellij.history.core.StoredContent; import com.intellij.history.core.tree.Entry; import com.intellij.openapi.command.CommandEvent; import com.intellij.openapi.command.CommandListener; import com.intellij.openapi.util.Key; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.vfs.*; public class LocalHistoryEventDispatcher extends VirtualFileAdapter implements VirtualFileManagerListener, CommandListener { @@ -102,9 +103,9 @@ public class LocalHistoryEventDispatcher extends VirtualFileAdapter implements V if (!areContentChangesVersioned(e)) return; VirtualFile f = e.getFile(); - Content content = myGateway.acquireAndUpdateActualContent(f, null); + Pair content = myGateway.acquireAndUpdateActualContent(f, null); if (content != null) { - myVcs.contentChanged(f.getPath(), content, f.getTimeStamp()); + myVcs.contentChanged(f.getPath(), content.first, content.second); } } diff --git a/platform/lvcs-impl/testSrc/com/intellij/history/integration/GettingContentAtDateTest.java b/platform/lvcs-impl/testSrc/com/intellij/history/integration/GettingContentAtDateTest.java index 5369a4cedc60..8ba94f744223 100644 --- a/platform/lvcs-impl/testSrc/com/intellij/history/integration/GettingContentAtDateTest.java +++ b/platform/lvcs-impl/testSrc/com/intellij/history/integration/GettingContentAtDateTest.java @@ -19,6 +19,8 @@ package com.intellij.history.integration; import com.intellij.history.FileRevisionTimestampComparator; import com.intellij.history.LocalHistory; +import com.intellij.history.LocalHistoryAction; +import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.util.Clock; import com.intellij.openapi.vfs.VirtualFile; @@ -37,7 +39,7 @@ public class GettingContentAtDateTest extends IntegrationTestCase { assertContentAt(0, null); assertContentAt(TIMESTAMP_INCREMENT, "1"); - assertContentAt(TIMESTAMP_INCREMENT + TIMESTAMP_INCREMENT/ 2, null); + assertContentAt(TIMESTAMP_INCREMENT + TIMESTAMP_INCREMENT / 2, null); assertContentAt(TIMESTAMP_INCREMENT * 2, "2"); assertContentAt(TIMESTAMP_INCREMENT * 3, null); } @@ -77,6 +79,51 @@ public class GettingContentAtDateTest extends IntegrationTestCase { assertContentAt(c, "2"); } + public void testWithUnsavedDocuments() throws Exception { + setContent(f, "FILE1", TIMESTAMP_INCREMENT); + + Clock.setTime(TIMESTAMP_INCREMENT * 2); + LocalHistoryAction a = LocalHistory.getInstance().startAction(null); + setDocumentTextFor(f, "DOC1"); + a.finish(); + + Clock.setTime(TIMESTAMP_INCREMENT * 3); + a = LocalHistory.getInstance().startAction(null); + setDocumentTextFor(f, "DOC2"); + a.finish(); + + FileDocumentManager.getInstance().saveAllDocuments(); + setContent(f, "FILE2", TIMESTAMP_INCREMENT * 4); + + assertContentAt(new FileRevisionTimestampComparator() { + @Override + public boolean isSuitable(long revisionTimestamp) { + return revisionTimestamp == TIMESTAMP_INCREMENT * 4; + } + }, "FILE2"); + + assertContentAt(new FileRevisionTimestampComparator() { + @Override + public boolean isSuitable(long revisionTimestamp) { + return revisionTimestamp == TIMESTAMP_INCREMENT * 3; + } + }, "DOC2"); + + assertContentAt(new FileRevisionTimestampComparator() { + @Override + public boolean isSuitable(long revisionTimestamp) { + return revisionTimestamp == TIMESTAMP_INCREMENT * 2; + } + }, "DOC1"); + + assertContentAt(new FileRevisionTimestampComparator() { + @Override + public boolean isSuitable(long revisionTimestamp) { + return revisionTimestamp == TIMESTAMP_INCREMENT; + } + }, "FILE1"); + } + private void assertContentAt(long timestamp, String expected) { assertContentAt(comparator(timestamp), expected); }