Local History: correctly registering timestamp for unsaved document contents (IDEA-57598)

This commit is contained in:
Anton Makeev
2010-12-14 16:25:34 +03:00
parent 6a32c6468f
commit 6d5a16a575
3 changed files with 95 additions and 33 deletions
@@ -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<Pair<StoredContent, Long>> SAVED_DOCUMENT_CONTENT_AND_STAMP_KEY
private static final Key<ContentAndTimestamps> 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<StoredContent, Long> 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<StoredContent, Long> 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<StoredContent, Long> contentAndStamp = f.getUserData(SAVED_DOCUMENT_CONTENT_AND_STAMP_KEY);
public Pair<StoredContent, Long> 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<StoredContent, Long> contentAndStamp = f.getUserData(SAVED_DOCUMENT_CONTENT_AND_STAMP_KEY);
@NotNull
public Pair<StoredContent, Long> 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<StoredContent, Long> result = f.getUserData(SAVED_DOCUMENT_CONTENT_AND_STAMP_KEY);
@NotNull
private Pair<StoredContent, Long> 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;
}
}
}
@@ -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<StoredContent, Long> content = myGateway.acquireAndUpdateActualContent(f, null);
if (content != null) {
myVcs.contentChanged(f.getPath(), content, f.getTimeStamp());
myVcs.contentChanged(f.getPath(), content.first, content.second);
}
}
@@ -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);
}