[^maxim] build psi and index on the last committed document text (IDEA-117183, IDEA-116721)

This commit is contained in:
peter
2014-01-13 17:14:17 +01:00
parent f85e0e5bb5
commit dd0278244c
15 changed files with 305 additions and 64 deletions
@@ -22,9 +22,7 @@ import com.intellij.openapi.util.Factory;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.VfsUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.JavaPsiFacade;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiManagerEx;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.testFramework.IdeaTestCase;
@@ -221,12 +219,19 @@ public class IndexTest extends IdeaTestCase {
PlatformTestUtil.tryGcSoftlyReachableObjects();
assertNull(((PsiManagerEx)PsiManager.getInstance(getProject())).getFileManager().getCachedPsiFile(vFile));
PsiClass foo = facade.findClass("Foo", scope);
assertNotNull(foo);
assertTrue(foo.isValid());
assertEquals("class Foo {}", foo.getText());
assertTrue(foo.isValid());
PsiDocumentManager.getInstance(myProject).commitAllDocuments();
assertNull(facade.findClass("Foo", scope));
}
});
}
public void _testSavedUncommittedDocument() throws IOException {
public void testSavedUncommittedDocument() throws IOException {
VirtualFile dir = getVirtualFile(createTempDirectory());
PsiTestUtil.addSourceContentToRoots(myModule, dir);
@@ -247,11 +252,15 @@ public class IndexTest extends IdeaTestCase {
Document document = FileDocumentManager.getInstance().getDocument(vFile);
document.insertString(0, "class Foo {}");
FileDocumentManager.getInstance().saveDocument(document);
// if Foo exists now, mod count should be different
//assertTrue(count != PsiManager.getInstance(myProject).getModificationTracker().getModificationCount());
assertTrue(count == PsiManager.getInstance(myProject).getModificationTracker().getModificationCount());
assertNull(facade.findClass("Foo", scope));
PsiDocumentManager.getInstance(myProject).commitAllDocuments();
assertNotNull(facade.findClass("Foo", scope));
assertNotNull(facade.findClass("Foo", scope).getText());
// if Foo exists now, mod count should be different
assertTrue(count != PsiManager.getInstance(myProject).getModificationTracker().getModificationCount());
}
});
}
@@ -14,6 +14,7 @@ import com.intellij.testFramework.IdeaTestCase;
import com.intellij.testFramework.PlatformTestUtil;
import com.intellij.testFramework.fixtures.LightPlatformCodeInsightFixtureTestCase;
import com.intellij.util.Processor;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NonNls;
import java.io.IOException;
@@ -149,39 +150,79 @@ public class PsiModificationTrackerTest extends LightPlatformCodeInsightFixtureT
assertEquals(count + 1, modificationTracker.getJavaStructureModificationCount());
}
public void _testClassShouldNotAppearWithoutEvents() throws IOException {
VirtualFile file = myFixture.getTempDirFixture().createFile("Foo.java", "");
public void testClassShouldNotAppearWithoutEvents_WithPsi() throws IOException {
final VirtualFile file = myFixture.getTempDirFixture().createFile("Foo.java", "");
final Document document = FileDocumentManager.getInstance().getDocument(file);
assertNotNull(document);
new WriteCommandAction.Simple(getProject()) {
@Override
protected void run() throws Throwable {
assertNull(JavaPsiFacade.getInstance(getProject()).findClass("Foo", GlobalSearchScope.allScope(getProject())));
PsiModificationTracker tracker = PsiManager.getInstance(getProject()).getModificationTracker();
PsiManager psiManager = PsiManager.getInstance(getProject());
PsiModificationTracker tracker = psiManager.getModificationTracker();
long count1 = tracker.getJavaStructureModificationCount();
PsiJavaFile psiFile = (PsiJavaFile)psiManager.findFile(file);
document.insertString(0, "class Foo {}");
assertEquals(count1, tracker.getJavaStructureModificationCount()); // no PSI changes yet
//so the class should not exist
assertNull(JavaPsiFacade.getInstance(getProject()).findClass("Foo", GlobalSearchScope.allScope(getProject())));
assertSize(0, psiFile.getClasses());
assertEquals("", psiManager.findFile(file).getText());
PlatformTestUtil.tryGcSoftlyReachableObjects();
PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
assertFalse(count1 == tracker.getJavaStructureModificationCount());
assertNotNull(JavaPsiFacade.getInstance(getProject()).findClass("Foo", GlobalSearchScope.allScope(getProject())));
assertEquals("class Foo {}", psiManager.findFile(file).getText());
assertEquals("class Foo {}", psiManager.findFile(file).getNode().getText());
assertSize(1, psiFile.getClasses());
}
}.execute();
}
public void testClassShouldNotAppearWithoutEvents_WithoutPsi() throws IOException {
final GlobalSearchScope allScope = GlobalSearchScope.allScope(getProject());
final JavaPsiFacade facade = JavaPsiFacade.getInstance(getProject());
final PsiManager psiManager = PsiManager.getInstance(getProject());
final PsiModificationTracker tracker = psiManager.getModificationTracker();
final VirtualFile file = myFixture.getTempDirFixture().createFile("Foo.java", "");
final Document document = FileDocumentManager.getInstance().getDocument(file);
assertNotNull(document);
new WriteCommandAction.Simple(getProject()) {
@Override
protected void run() throws Throwable {
assertNull(facade.findClass("Foo", allScope));
long count1 = tracker.getJavaStructureModificationCount();
PlatformTestUtil.tryGcSoftlyReachableObjects();
assertNull(PsiDocumentManager.getInstance(getProject()).getCachedPsiFile(document));
document.insertString(0, "class Foo {}");
assertFalse(count1 == tracker.getJavaStructureModificationCount());
assertTrue(PsiDocumentManager.getInstance(getProject()).isCommitted(document));
assertNotNull(facade.findClass("Foo", allScope));
PsiJavaFile psiFile = (PsiJavaFile)psiManager.findFile(file);
assertSize(1, psiFile.getClasses());
assertEquals("class Foo {}", psiFile.getText());
assertEquals("class Foo {}", psiFile.getNode().getText());
}
}.execute();
}
public void _testClassShouldNotDisappearWithoutEvents() throws IOException {
public void testClassShouldNotDisappearWithoutEvents() throws IOException {
new WriteCommandAction.Simple(getProject()) {
@Override
protected void run() throws Throwable {
PsiModificationTracker tracker = PsiManager.getInstance(getProject()).getModificationTracker();
long count0 = tracker.getJavaStructureModificationCount();
VirtualFile file = myFixture.addFileToProject("Foo.java", "class Foo {}").getVirtualFile();
final VirtualFile file = myFixture.addFileToProject("Foo.java", "class Foo {}").getVirtualFile();
final Document document = FileDocumentManager.getInstance().getDocument(file);
assertNotNull(document);
@@ -191,13 +232,20 @@ public class PsiModificationTrackerTest extends LightPlatformCodeInsightFixtureT
document.deleteString(0, document.getTextLength());
// some plugins (e.g. Copyright) hold file reference in an invokeLater runnable, let them pass
UIUtil.dispatchAllInvocationEvents();
// gc softly-referenced file and AST
PlatformTestUtil.tryGcSoftlyReachableObjects();
assertNull(((PsiManagerEx)PsiManager.getInstance(getProject())).getFileManager().getCachedPsiFile(file));
final PsiManagerEx psiManager = (PsiManagerEx)PsiManager.getInstance(getProject());
assertNull(psiManager.getFileManager().getCachedPsiFile(file));
assertEquals(count1, tracker.getJavaStructureModificationCount()); // no PSI changes yet
//so the class should exist
//so the class should still be there
assertNotNull(JavaPsiFacade.getInstance(getProject()).findClass("Foo", GlobalSearchScope.allScope(getProject())));
assertSize(1, ((PsiJavaFile)psiManager.findFile(file)).getClasses());
assertEquals("class Foo {}", psiManager.findFile(file).getText());
PlatformTestUtil.tryGcSoftlyReachableObjects();
PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
@@ -19,6 +19,7 @@ import com.intellij.openapi.editor.Document;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.text.ImmutableCharSequence;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -100,6 +101,13 @@ public abstract class PsiDocumentManager {
*/
public abstract void commitDocument(@NotNull Document document);
/**
* @param document
* @return the document text that PSI should be based upon. For changed documents, it's their old text until the document is committed
*/
@NotNull
public abstract ImmutableCharSequence getLastCommittedText(@NotNull Document document);
/**
* Returns the list of documents which have been modified but not committed.
*
@@ -492,7 +492,7 @@ public class SingleRootFileViewProvider extends UserDataHolderBase implements Fi
final VirtualFile virtualFile = getVirtualFile();
if (virtualFile instanceof LightVirtualFile) {
Document doc = getCachedDocument();
if (doc != null) return doc.getCharsSequence();
if (doc != null) return getLastCommittedText(doc);
return ((LightVirtualFile)virtualFile).getContent();
}
@@ -501,7 +501,7 @@ public class SingleRootFileViewProvider extends UserDataHolderBase implements Fi
return LoadTextUtil.loadText(virtualFile);
}
else {
return document.getCharsSequence();
return getLastCommittedText(document);
}
}
@@ -517,6 +517,10 @@ public class SingleRootFileViewProvider extends UserDataHolderBase implements Fi
}
}
private CharSequence getLastCommittedText(Document document) {
return PsiDocumentManager.getInstance(myManager.getProject()).getLastCommittedText(document);
}
private class DocumentContent implements Content {
@NonNls
@Override
@@ -530,7 +534,7 @@ public class SingleRootFileViewProvider extends UserDataHolderBase implements Fi
public CharSequence getText() {
final Document document = getDocument();
assert document != null;
return document.getCharsSequence();
return getLastCommittedText(document);
}
@Override
@@ -76,7 +76,7 @@ public abstract class DocumentCommitProcessor {
@Override
public String toString() {
return "Project: " + project.getName()
+ ", Doc: "+ document +" ("+ StringUtil.first(document.getText(), 12, true).replaceAll("\n"," ")+")"
+ ", Doc: "+ document +" ("+ StringUtil.first(document.getImmutableCharSequence(), 12, true).toString().replaceAll("\n", " ")+")"
+(indicator.isCanceled() ? " (Canceled)" : "") + (removed ? "Removed" : "");
}
@@ -103,10 +103,9 @@ public abstract class DocumentCommitProcessor {
@NotNull final PsiFile file,
final boolean synchronously) {
Document document = task.document;
if (PsiDocumentManager.getInstance(task.project).isCommitted(document)) return null;
final long startDocModificationTimeStamp = document.getModificationStamp();
final FileElement myTreeElementBeingReparsedSoItWontBeCollected = ((PsiFileImpl)file).calcTreeElement();
final CharSequence chars = document.getCharsSequence();
final CharSequence chars = document.getImmutableCharSequence();
final Boolean data = document.getUserData(BlockSupport.DO_NOT_REPARSE_INCREMENTALLY);
if (data != null) {
document.putUserData(BlockSupport.DO_NOT_REPARSE_INCREMENTALLY, null);
@@ -130,7 +129,8 @@ public abstract class DocumentCommitProcessor {
public boolean process(Document document) {
ApplicationManager.getApplication().assertWriteAccessAllowed();
log("Finishing", task, synchronously, document.getModificationStamp(), startDocModificationTimeStamp);
if (document.getModificationStamp() != startDocModificationTimeStamp) {
if (document.getModificationStamp() != startDocModificationTimeStamp ||
((PsiDocumentManagerBase)PsiDocumentManager.getInstance(file.getProject())).getCachedViewProvider(document) != file.getViewProvider()) {
return false; // optimistic locking failed
}
@@ -228,7 +228,7 @@ public abstract class DocumentCommitProcessor {
}
}
public void log(@NonNls String msg, CommitTask task, boolean synchronously, @NonNls Object... args) {
public void log(@NonNls String msg, @Nullable CommitTask task, boolean synchronously, @NonNls Object... args) {
}
@NotNull
@@ -30,6 +30,7 @@ import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.FileIndexFacade;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.Ref;
@@ -37,15 +38,17 @@ import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.impl.smartPointers.SmartPointerManagerImpl;
import com.intellij.psi.impl.source.PsiFileImpl;
import com.intellij.psi.impl.source.text.BlockSupportImpl;
import com.intellij.psi.text.BlockSupport;
import com.intellij.util.FileContentUtilCore;
import com.intellij.util.Processor;
import com.intellij.util.SmartList;
import com.intellij.util.SystemProperties;
import com.intellij.util.concurrency.Semaphore;
import com.intellij.util.containers.ConcurrentHashSet;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.messages.MessageBus;
import gnu.trove.THashSet;
import com.intellij.util.text.ImmutableCharSequence;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -56,19 +59,20 @@ import java.util.*;
public abstract class PsiDocumentManagerBase extends PsiDocumentManager implements ProjectComponent, DocumentListener {
protected static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.PsiDocumentManagerImpl");
protected static final Key<PsiFile> HARD_REF_TO_PSI = new Key<PsiFile>("HARD_REFERENCE_TO_PSI");
protected static final Key<List<Runnable>> ACTION_AFTER_COMMIT = Key.create("ACTION_AFTER_COMMIT");
private static final Key<PsiFile> HARD_REF_TO_PSI = new Key<PsiFile>("HARD_REFERENCE_TO_PSI");
private static final Key<List<Runnable>> ACTION_AFTER_COMMIT = Key.create("ACTION_AFTER_COMMIT");
protected final Project myProject;
protected final PsiManager myPsiManager;
protected final DocumentCommitProcessor myDocumentCommitProcessor;
protected final Set<Document> myUncommittedDocuments = Collections.synchronizedSet(new THashSet<Document>());
private final PsiManager myPsiManager;
private final DocumentCommitProcessor myDocumentCommitProcessor;
protected final Set<Document> myUncommittedDocuments = new ConcurrentHashSet<Document>();
private final Map<Document, ImmutableCharSequence> myLastCommittedTexts = ContainerUtil.newConcurrentMap();
protected volatile boolean myIsCommitInProgress;
protected final PsiToDocumentSynchronizer mySynchronizer;
private volatile boolean myIsCommitInProgress;
private final PsiToDocumentSynchronizer mySynchronizer;
protected final List<Listener> myListeners = ContainerUtil.createLockFreeCopyOnWriteList();
protected final SmartPointerManagerImpl mySmartPointerManager;
private final List<Listener> myListeners = ContainerUtil.createLockFreeCopyOnWriteList();
private final SmartPointerManagerImpl mySmartPointerManager;
public PsiDocumentManagerBase(@NotNull final Project project,
@NotNull PsiManager psiManager,
@@ -152,7 +156,7 @@ public abstract class PsiDocumentManagerBase extends PsiDocumentManager implemen
}
@Nullable
protected PsiFile getPsiFile(VirtualFile virtualFile) {
private PsiFile getPsiFile(VirtualFile virtualFile) {
return ((PsiManagerEx)myPsiManager).getFileManager().findFile(virtualFile);
}
@@ -319,7 +323,10 @@ public abstract class PsiDocumentManagerBase extends PsiDocumentManager implemen
break;
}
}
myLastCommittedTexts.remove(document);
viewProvider.contentsSynchronized();
} else {
handleCommitWithoutPsi(document);
}
}
finally {
@@ -541,6 +548,13 @@ public abstract class PsiDocumentManagerBase extends PsiDocumentManager implemen
}
}
@Override
@NotNull
public ImmutableCharSequence getLastCommittedText(@NotNull Document document) {
ImmutableCharSequence text = myLastCommittedTexts.get(document);
return text != null ? text : document.getImmutableCharSequence();
}
@Override
@NotNull
public Document[] getUncommittedDocuments() {
@@ -573,6 +587,9 @@ public abstract class PsiDocumentManagerBase extends PsiDocumentManager implemen
@Override
public void beforeDocumentChange(DocumentEvent event) {
final Document document = event.getDocument();
if (!(document instanceof DocumentWindow) && !myLastCommittedTexts.containsKey(document)) {
myLastCommittedTexts.put(document, document.getImmutableCharSequence());
}
final FileViewProvider viewProvider = getCachedViewProvider(document);
if (viewProvider == null) return;
@@ -608,8 +625,14 @@ public abstract class PsiDocumentManagerBase extends PsiDocumentManager implemen
public void documentChanged(DocumentEvent event) {
final Document document = event.getDocument();
final FileViewProvider viewProvider = getCachedViewProvider(document);
if (viewProvider == null) return;
if (!isRelevant(viewProvider)) return;
if (viewProvider == null) {
handleCommitWithoutPsi(document);
return;
}
if (!isRelevant(viewProvider)) {
myLastCommittedTexts.remove(document);
return;
}
ApplicationManager.getApplication().assertWriteAccessAllowed();
final List<PsiFile> files = viewProvider.getAllFiles();
@@ -647,15 +670,56 @@ public abstract class PsiDocumentManagerBase extends PsiDocumentManager implemen
if (commitNecessary) {
assert !(document instanceof DocumentWindow);
myUncommittedDocuments.add(document);
myDocumentCommitProcessor.log("added uncommitted doc", null, false, myProject, document, ((DocumentEx)document).isInBulkUpdate());
if (forceCommit) {
commitDocument(document);
}
else if (!((DocumentEx)document).isInBulkUpdate()) {
myDocumentCommitProcessor.commitAsynchronously(myProject, document, event);
}
} else {
myLastCommittedTexts.remove(document);
}
}
public void handleCommitWithoutPsi(final Document document) {
final ImmutableCharSequence prevText = myLastCommittedTexts.remove(document);
if (prevText == null) {
return;
}
if (!myProject.isInitialized() || myProject.isDisposed()) {
return;
}
VirtualFile virtualFile = FileDocumentManager.getInstance().getFile(document);
if (virtualFile == null || !FileIndexFacade.getInstance(myProject).isInContent(virtualFile)) {
return;
}
final PsiFileImpl psiFile = (PsiFileImpl)getPsiFile(document);
if (psiFile == null) {
return;
}
ApplicationManager.getApplication().runWriteAction(new Runnable() {
public void run() {
psiFile.getViewProvider().beforeContentsSynchronized();
synchronized (PsiLock.LOCK) {
final int oldLength = prevText.length();
PsiManagerImpl manager = (PsiManagerImpl)psiFile.getManager();
BlockSupportImpl.sendBeforeChildrenChangeEvent(manager, psiFile, true);
BlockSupportImpl.sendBeforeChildrenChangeEvent(manager, psiFile, false);
psiFile.onContentReload();
BlockSupportImpl.sendAfterChildrenChangedEvent(manager, psiFile, oldLength, false);
BlockSupportImpl.sendAfterChildrenChangedEvent(manager, psiFile, oldLength, true);
}
psiFile.getViewProvider().contentsSynchronized();
}
});
}
private boolean isRelevant(@NotNull FileViewProvider viewProvider) {
VirtualFile virtualFile = viewProvider.getVirtualFile();
return !virtualFile.getFileType().isBinary() &&
@@ -729,6 +793,7 @@ public abstract class PsiDocumentManagerBase extends PsiDocumentManager implemen
@TestOnly
public void clearUncommittedDocuments() {
myLastCommittedTexts.clear();
myUncommittedDocuments.clear();
mySynchronizer.cleanupForNextTest();
}
@@ -17,13 +17,17 @@ package com.intellij.psi.stubs;
import com.intellij.lang.Language;
import com.intellij.lang.LanguageParserDefinitions;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.LanguageFileType;
import com.intellij.openapi.util.Key;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiFile;
import com.intellij.psi.tree.IFileElementType;
import com.intellij.psi.tree.IStubFileElementType;
import com.intellij.util.indexing.FileContent;
import com.intellij.util.indexing.FileContentImpl;
import com.intellij.util.indexing.IndexingDataKeys;
import com.intellij.util.indexing.SubstitutedFileType;
import org.jetbrains.annotations.Nullable;
@@ -55,9 +59,21 @@ public class StubTreeBuilder {
Language l = languageFileType.getLanguage();
final IFileElementType type = LanguageParserDefinitions.INSTANCE.forLanguage(l).getFileNodeType();
PsiFile psi = inputData.getPsiFile();
PsiFile psi = null;
CharSequence contentAsText = null;
Document document = FileDocumentManager.getInstance().getCachedDocument(inputData.getFile());
if (document != null) {
PsiFile existingPsi = PsiDocumentManager.getInstance(inputData.getProject()).getPsiFile(document);
if (existingPsi != null) {
contentAsText = existingPsi.getText();
psi = ((FileContentImpl) inputData).createFileFromText(contentAsText);
}
}
if (contentAsText == null) {
contentAsText = inputData.getContentAsText();
psi = inputData.getPsiFile();
}
psi = psi.getViewProvider().getStubBindingRoot();
CharSequence contentAsText = inputData.getContentAsText();
psi.putUserData(IndexingDataKeys.FILE_TEXT_CONTENT_KEY, contentAsText);
try {
@@ -66,20 +66,23 @@ public final class FileContentImpl extends UserDataHolderBase implements FileCon
}
if (psi == null) {
Project project = getProject();
if (project == null) {
project = DefaultProjectFactory.getInstance().getDefaultProject();
}
final Language language = ((LanguageFileType)getFileTypeWithoutSubstitution()).getLanguage();
final Language substitutedLanguage = LanguageSubstitutors.INSTANCE.substituteLanguage(language, getFile(), project);
psi = PsiFileFactory.getInstance(project).createFileFromText(getFileName(), substitutedLanguage, getContentAsText(), false, false, true);
psi = createFileFromText(getContentAsText());
psi.putUserData(IndexingDataKeys.VIRTUAL_FILE, getFile());
putUserData(CACHED_PSI, psi);
}
return psi;
}
public PsiFile createFileFromText(CharSequence text) {
Project project = getProject();
if (project == null) {
project = DefaultProjectFactory.getInstance().getDefaultProject();
}
final Language language = ((LanguageFileType)getFileTypeWithoutSubstitution()).getLanguage();
final Language substitutedLanguage = LanguageSubstitutors.INSTANCE.substituteLanguage(language, getFile(), project);
return PsiFileFactory.getInstance(project).createFileFromText(getFileName(), substitutedLanguage, text, false, false, true);
}
public static class IllegalDataException extends RuntimeException {
public IllegalDataException(final String message) {
super(message);
@@ -45,7 +45,9 @@ import org.jetbrains.annotations.TestOnly;
import javax.swing.*;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class DocumentCommitThread extends DocumentCommitProcessor implements Runnable, Disposable {
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.DocumentCommitThread");
@@ -143,7 +145,7 @@ public class DocumentCommitThread extends DocumentCommitProcessor implements Run
private final StringBuilder log = new StringBuilder();
@Override
public void log(@NonNls String msg, CommitTask task, boolean synchronously, @NonNls Object... args) {
public void log(@NonNls String msg, @Nullable CommitTask task, boolean synchronously, @NonNls Object... args) {
if (true) return;
String indent = new SimpleDateFormat("mm:ss:SSSS").format(new Date()) +
@@ -400,9 +402,16 @@ public class DocumentCommitThread extends DocumentCommitProcessor implements Run
public void run() {
ApplicationManager.getApplication().assertReadAccessAllowed();
if (project.isDisposed()) return;
final PsiDocumentManagerImpl documentManager = (PsiDocumentManagerImpl)PsiDocumentManager.getInstance(project);
if (documentManager.isCommitted(document)) return;
FileViewProvider viewProvider = documentManager.getCachedViewProvider(document);
if (viewProvider == null) return;
if (viewProvider == null) {
finishProcessors.add(handleCommitWithoutPsi(documentManager, document, task, synchronously));
return;
}
List<PsiFile> psiFiles = viewProvider.getAllFiles();
for (PsiFile file : psiFiles) {
if (file.isValid()) {
@@ -482,6 +491,25 @@ public class DocumentCommitThread extends DocumentCommitProcessor implements Run
return finishRunnable;
}
private Processor<Document> handleCommitWithoutPsi(final PsiDocumentManagerImpl documentManager,
Document document,
final CommitTask task, final boolean synchronously) {
final long startDocModificationTimeStamp = document.getModificationStamp();
return new Processor<Document>() {
@Override
public boolean process(Document document) {
log("Finishing without PSI", task, synchronously, document.getModificationStamp(), startDocModificationTimeStamp);
if (document.getModificationStamp() != startDocModificationTimeStamp ||
documentManager.getCachedViewProvider(document) != null) {
return false;
}
documentManager.handleCommitWithoutPsi(document);
return true;
}
};
}
private boolean processAll(final Processor<CommitTask> processor) {
final boolean[] result = {true};
synchronized (documentsToCommit) {
@@ -109,11 +109,12 @@ public class StubTreeLoaderImpl extends StubTreeLoader {
SerializedStubTree stubTree = datas.get(0);
if (!stubTree.contentLengthMatches(vFile.getLength(), getCurrentTextContentLength(project, vFile, document))) {
return processError(vFile,
"Outdated stub in index: " + StubUpdatingIndex.getIndexingStampInfo(vFile) +
", docSaved=" + saved +
", queried at " + vFile.getTimeStamp(),
null);
//todo find another way of early stub-ast mismatch prevention
//return processError(vFile,
// "Outdated stub in index: " + StubUpdatingIndex.getIndexingStampInfo(vFile) +
// ", docSaved=" + saved +
// ", queried at " + vFile.getTimeStamp(),
// null);
}
Stub stub;
@@ -43,7 +43,7 @@ import java.util.concurrent.Callable;
/*
* @author max
*/
public class StubUpdatingIndex extends CustomImplementationFileBasedIndexExtension<Integer, SerializedStubTree, FileContent> {
public class StubUpdatingIndex extends CustomImplementationFileBasedIndexExtension<Integer, SerializedStubTree, FileContent> implements PsiDependentIndex {
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.stubs.StubUpdatingIndex");
// todo remove once we don't need this for stub-ast mismatch debug info
@@ -56,6 +56,9 @@ import com.intellij.openapi.vfs.newvfs.persistent.FlushingDaemon;
import com.intellij.openapi.vfs.newvfs.persistent.PersistentFS;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiDocumentTransactionListener;
import com.intellij.psi.impl.PsiManagerImpl;
import com.intellij.psi.impl.PsiTreeChangeEventImpl;
import com.intellij.psi.impl.PsiTreeChangePreprocessor;
import com.intellij.psi.impl.cache.impl.id.PlatformIdTableBuilding;
import com.intellij.psi.impl.source.PsiFileImpl;
import com.intellij.psi.search.EverythingGlobalScope;
@@ -106,6 +109,7 @@ public class FileBasedIndexImpl extends FileBasedIndex {
private final TObjectIntHashMap<ID<?, ?>> myIndexIdToVersionMap = new TObjectIntHashMap<ID<?, ?>>();
private final Set<ID<?, ?>> myNotRequiringContentIndices = new THashSet<ID<?, ?>>();
private final Set<ID<?, ?>> myRequiringContentIndices = new THashSet<ID<?, ?>>();
private final Set<ID<?, ?>> myPsiDependentIndices = new THashSet<ID<?, ?>>();
private final Set<FileType> myNoLimitCheckTypes = new THashSet<FileType>();
private final PerIndexDocumentVersionMap myLastIndexedDocStamps = new PerIndexDocumentVersionMap();
@@ -442,6 +446,7 @@ public class FileBasedIndexImpl extends FileBasedIndex {
else {
myRequiringContentIndices.add(name);
}
if (extension instanceof PsiDependentIndex) myPsiDependentIndices.add(name);
myNoLimitCheckTypes.addAll(extension.getFileTypesWithSizeLimitNotApplicable());
break;
}
@@ -1392,12 +1397,15 @@ public class FileBasedIndexImpl extends FileBasedIndex {
}
@NotNull
private Set<Document> getUnsavedOrTransactedDocuments() {
final Set<Document> docs = new THashSet<Document>(Arrays.asList(myFileDocumentManager.getUnsavedDocuments()));
private Set<Document> getUnsavedDocuments() {
return new THashSet<Document>(Arrays.asList(myFileDocumentManager.getUnsavedDocuments()));
}
@NotNull
private Set<Document> getTransactedDocuments() {
synchronized (myTransactionMap) {
docs.addAll(myTransactionMap.keySet());
return new THashSet<Document>(myTransactionMap.keySet());
}
return docs;
}
private void indexUnsavedDocuments(@NotNull ID<?, ?> indexId,
@@ -1408,7 +1416,8 @@ public class FileBasedIndexImpl extends FileBasedIndex {
return; // no need to index unsaved docs
}
final Set<Document> documents = getUnsavedOrTransactedDocuments();
Set<Document> documents = myPsiDependentIndices.contains(indexId) ? getTransactedDocuments() : getUnsavedDocuments();
if (!documents.isEmpty()) {
// now index unsaved data
final StorageGuard.Holder guard = setDataBufferingEnabled(true);
@@ -1419,11 +1428,9 @@ public class FileBasedIndexImpl extends FileBasedIndex {
semaphore.down();
boolean allDocsProcessed = true;
boolean hasUncommittedDocuments = project == null;
try {
for (Document document : documents) {
allDocsProcessed &= indexUnsavedDocument(document, indexId, project, filter, restrictedFile);
if (!hasUncommittedDocuments) hasUncommittedDocuments = PsiDocumentManager.getInstance(project).isUncommited(document);
ProgressManager.checkCanceled();
}
}
@@ -1443,7 +1450,6 @@ public class FileBasedIndexImpl extends FileBasedIndex {
// if we have uncommitted documents in unsaved documents, we may index old psi with new uncommitted doc,
// to properly reindex with new psi / new doc we don't mark index up to date in this case (IDEA-111448)
if (!hasUncommittedDocuments) myUpToDateIndices.add(indexId);
}
}
}
@@ -2439,6 +2445,27 @@ public class FileBasedIndexImpl extends FileBasedIndex {
public void registerIndexableSet(@NotNull IndexableFileSet set, @Nullable Project project) {
myIndexableSets.add(set);
myIndexableSetToProjectMap.put(set, project);
if (project != null) {
((PsiManagerImpl)PsiManager.getInstance(project)).addTreeChangePreprocessor(new PsiTreeChangePreprocessor() {
@Override
public void treeChanged(@NotNull PsiTreeChangeEventImpl event) {
if (event.isGenericChange() &&
event.getCode() == PsiTreeChangeEventImpl.PsiEventType.CHILDREN_CHANGED) {
PsiFile file = event.getFile();
if (file != null) {
VirtualFile virtualFile = file.getVirtualFile();
if (virtualFile instanceof VirtualFileWithId) {
for(ID<?,?> psiBackedIndex:myPsiDependentIndices) {
IndexingStamp.update(virtualFile, psiBackedIndex, IndexInfrastructure.INVALID_STAMP2);
}
myChangedFilesCollector.scheduleForUpdate(virtualFile);
IndexingStamp.flushCache(virtualFile);
}
}
}
}
});
}
}
@Override
@@ -24,6 +24,7 @@ import com.intellij.openapi.util.Ref;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.util.Processor;
import com.intellij.util.SmartList;
import com.intellij.util.io.PersistentHashMap;
import gnu.trove.THashMap;
import gnu.trove.TObjectObjectProcedure;
@@ -204,7 +205,6 @@ public class MapReduceIndex<Key, Value, Input> implements UpdatableIndex<Key,Val
@Override
public final Computable<Boolean> update(final int inputId, @Nullable final Input content) {
assert myInputsIndex != null;
final Map<Key, Value> data = content != null ? myIndexer.map(content) : Collections.<Key, Value>emptyMap();
@@ -222,6 +222,9 @@ public class MapReduceIndex<Key, Value, Input> implements UpdatableIndex<Key,Val
updateWithMap(inputId, data, new Callable<Collection<Key>>() {
@Override
public Collection<Key> call() throws Exception {
if (myInputsIndex == null) {
return new SmartList<Key>((Key)(Integer)inputId);
}
final Collection<Key> oldKeys = myInputsIndex.get(inputId);
return oldKeys == null? Collections.<Key>emptyList() : oldKeys;
}
@@ -0,0 +1,22 @@
/*
* Copyright 2000-2014 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.util.indexing;
/**
* Created by Maxim.Mossienko on 1/4/14.
*/
public interface PsiDependentIndex {
}
@@ -9,6 +9,7 @@ import com.intellij.openapi.util.Computable;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiFile;
import com.intellij.util.text.ImmutableCharSequence;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -56,6 +57,12 @@ public class MockPsiDocumentManager extends PsiDocumentManager {
public void commitDocument(@NotNull Document document) {
}
@NotNull
@Override
public ImmutableCharSequence getLastCommittedText(@NotNull Document document) {
return document.getImmutableCharSequence();
}
@Override
@NotNull
public Document[] getUncommittedDocuments() {