mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[^maxim] build psi and index on the last committed document text (IDEA-117183, IDEA-116721)
This commit is contained in:
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user