diff --git a/java/java-impl/src/com/intellij/psi/RefQueueIndex.java b/java/java-impl/src/com/intellij/psi/RefQueueIndex.java index 8691781301b6..d65fa4473c2f 100644 --- a/java/java-impl/src/com/intellij/psi/RefQueueIndex.java +++ b/java/java-impl/src/com/intellij/psi/RefQueueIndex.java @@ -102,7 +102,7 @@ public class RefQueueIndex extends FileBasedIndexExtension { return new FileBasedIndex.InputFilter() { @Override public boolean acceptInput(@NotNull VirtualFile file) { - return true; + return !file.isDirectory(); } }; } diff --git a/java/java-impl/src/com/intellij/psi/RefResolveServiceImpl.java b/java/java-impl/src/com/intellij/psi/RefResolveServiceImpl.java index b0afc8829b01..1e54bbb7ee8e 100644 --- a/java/java-impl/src/com/intellij/psi/RefResolveServiceImpl.java +++ b/java/java-impl/src/com/intellij/psi/RefResolveServiceImpl.java @@ -41,7 +41,6 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.*; import com.intellij.openapi.vfs.newvfs.BulkFileListener; import com.intellij.openapi.vfs.newvfs.NewVirtualFile; -import com.intellij.openapi.vfs.newvfs.RefreshQueueImpl; import com.intellij.openapi.vfs.newvfs.events.VFileEvent; import com.intellij.openapi.vfs.newvfs.persistent.FSRecords; import com.intellij.openapi.vfs.newvfs.persistent.PersistentFS; @@ -49,6 +48,8 @@ import com.intellij.psi.impl.PersistentIntList; import com.intellij.psi.impl.file.impl.ResolveScopeManagerImpl; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.util.PsiUtilCore; +import com.intellij.psi.xml.XmlElement; +import com.intellij.psi.xml.XmlFile; import com.intellij.util.ArrayUtil; import com.intellij.util.ExceptionUtil; import com.intellij.util.Function; @@ -85,6 +86,7 @@ public class RefResolveServiceImpl extends RefResolveService implements Runnable private final ApplicationEx myApplication; private volatile boolean myDisposed; private volatile boolean upToDate; + private volatile boolean enabled = true; private final FileWriter log; private final ProjectFileIndex myProjectFileIndex; @@ -162,10 +164,6 @@ public class RefResolveServiceImpl extends RefResolveService implements Runnable } private void init(@NotNull MessageBus messageBus, @NotNull PsiManager psiManager) { - //if (true) { - // upToDate = false; - // return; - //} messageBus.connect().subscribe(VirtualFileManager.VFS_CHANGES, new BulkFileListener.Adapter(){ @Override public void after(@NotNull List events) { @@ -198,31 +196,52 @@ public class RefResolveServiceImpl extends RefResolveService implements Runnable messageBus.connect().subscribe(DumbService.DUMB_MODE, new DumbService.DumbModeListener() { @Override public void enteredDumbMode() { - wakeUp(); + disable(); } @Override public void exitDumbMode() { - wakeUp(); + enable(); } }); myApplication.addApplicationListener(new ApplicationAdapter() { + @Override + public void beforeWriteActionStart(Object action) { + disable(); + } + @Override public void writeActionFinished(Object action) { - wakeUp(); + enable(); + } + + @Override + public void applicationExiting() { + disable(); } }, this); VirtualFileManager.getInstance().addVirtualFileManagerListener(new VirtualFileManagerListener() { @Override public void beforeRefreshStart(boolean asynchronous) { - wakeUp(); + disable(); } @Override public void afterRefreshFinish(boolean asynchronous) { - wakeUp(); + enable(); } }, this); + Disposer.register(this, HeavyProcessLatch.INSTANCE.addListener(new HeavyProcessLatch.HeavyProcessListener() { + @Override + public void processStarted() { + disable(); + } + + @Override + public void processFinished() { + enable(); + } + })); startThread(); } @@ -233,10 +252,11 @@ public class RefResolveServiceImpl extends RefResolveService implements Runnable } private boolean toResolve(VirtualFile virtualFile, @NotNull Project project) { - if (virtualFile != null && virtualFile.isValid() && + if (virtualFile != null && + virtualFile.isValid() && project.isInitialized() && - myProjectFileIndex.isContentSourceFile(virtualFile) && - (virtualFile.isDirectory() || virtualFile.getFileType() == StdFileTypes.JAVA)) { + myProjectFileIndex.isInContent(virtualFile) && + (virtualFile.isDirectory() || virtualFile.getFileType() == StdFileTypes.JAVA || virtualFile.getFileType() == StdFileTypes.XML)) { return true; } @@ -286,7 +306,7 @@ public class RefResolveServiceImpl extends RefResolveService implements Runnable synchronized (filesToResolve) { if (!(file instanceof VirtualFileWithId)) return false; int fileId = getAbsId(file); - countAndMarkUnresolved(file, new int[1]); + countAndMarkUnresolved(file, new THashSet(), true); boolean alreadyAdded = fileIsInQueue.set(fileId); if (!alreadyAdded) { filesToResolve.add(file); @@ -349,7 +369,11 @@ public class RefResolveServiceImpl extends RefResolveService implements Runnable @Override public void run() { while (!myDisposed) { - if (!hasSomething()) { + boolean isEmpty; + synchronized (filesToResolve) { + isEmpty = filesToResolve.isEmpty(); + } + if (!enabled || isEmpty) { try { waitForQueue(); } @@ -407,10 +431,11 @@ public class RefResolveServiceImpl extends RefResolveService implements Runnable } filesToResolve.clear(); } - final Set toProcess = Collections.synchronizedSet(set); final ConcurrentIntObjectMap fileToForwardIds = new StripedLockIntObjectConcurrentHashMap(); - final int size = countAndMarkUnresolved(set); - if (size == 0) return; + Set files = countAndMarkUnresolved(set, false); + if (files.isEmpty()) return; + final int size = files.size(); + final Set toProcess = Collections.synchronizedSet(files); log("Started to resolve "+ size + " files (was queued "+queuedSize+")"); indicator.setIndeterminate(false); @@ -422,31 +447,19 @@ public class RefResolveServiceImpl extends RefResolveService implements Runnable double fraction = 1 - toProcess.size() * 1.0 / size; indicator.setFraction(fraction); try { - VfsUtilCore.visitChildrenRecursively(file, new VirtualFileVisitor() { - @Override - public boolean visitFile(@NotNull VirtualFile file) { - if (!toResolve(file, myProject)) { - return true; - } - int fileId = getAbsId(file); - int i = size - toProcess.size(); - indicator.setText(i + "/" + size + ": Resolving " + file.getPresentableUrl()); - int[] forwardIds = processFile(file, fileId, indicator); - if (forwardIds == null) { - //queueUpdate(file); - return false; - } - toProcess.remove(file); - fileToForwardIds.put(fileId, forwardIds); - return true; - } - - @Nullable - @Override - public Iterable getChildrenIterable(@NotNull VirtualFile file) { - return ((NewVirtualFile)file).iterInDbChildren(); - } - }, RuntimeException.class); + if (file.isDirectory() || !toResolve(file, myProject)) { + return true; + } + int fileId = getAbsId(file); + int i = size - toProcess.size(); + indicator.setText(i + "/" + size + ": Resolving " + file.getPresentableUrl()); + int[] forwardIds = processFile(file, fileId, indicator); + if (forwardIds == null) { + //queueUpdate(file); + return false; + } + toProcess.remove(file); + fileToForwardIds.put(fileId, forwardIds); } catch (RuntimeException e) { indicator.checkCanceled(); @@ -457,7 +470,7 @@ public class RefResolveServiceImpl extends RefResolveService implements Runnable boolean success = true; try { success = JobLauncher - .getInstance().invokeConcurrentlyUnderProgress(new ArrayList(set), indicator, false, false, processor); + .getInstance().invokeConcurrentlyUnderProgress(new ArrayList(files), indicator, false, false, processor); } finally { queue(toProcess, "re-added after fail. success=" + success); @@ -472,52 +485,54 @@ public class RefResolveServiceImpl extends RefResolveService implements Runnable return Math.abs(((VirtualFileWithId)file).getId()); } - private int countAndMarkUnresolved(@NotNull Collection files) { - final int[] count = new int[1]; + @NotNull + private Set countAndMarkUnresolved(@NotNull Collection files, boolean inDbOnly) { + Set result = new THashSet(); for (VirtualFile file : files) { - countAndMarkUnresolved(file, count); + countAndMarkUnresolved(file, result, inDbOnly); } - return count[0]; + return result; } - private void countAndMarkUnresolved(@NotNull VirtualFile file, @NotNull final int[] count) { + private void countAndMarkUnresolved(@NotNull VirtualFile file, @NotNull final Set result, final boolean inDbOnly) { if (file.isDirectory()) { VfsUtilCore.visitChildrenRecursively(file, new VirtualFileVisitor() { @Override public boolean visitFile(@NotNull VirtualFile file) { - doCountAndMarkUnresolved(file, count); + doCountAndMarkUnresolved(file, result); return true; } @Nullable @Override public Iterable getChildrenIterable(@NotNull VirtualFile file) { - return ((NewVirtualFile)file).iterInDbChildren(); + return inDbOnly ? ((NewVirtualFile)file).iterInDbChildren() : null; } }); } else { - doCountAndMarkUnresolved(file, count); + doCountAndMarkUnresolved(file, result); } } - private void doCountAndMarkUnresolved(@NotNull VirtualFile file, @NotNull int[] count) { - if (toResolve(file, myProject)) { - count[0]++; + private void doCountAndMarkUnresolved(@NotNull VirtualFile file, @NotNull Set result) { + if (file.isDirectory()) { + fileIsResolved.set(getAbsId(file)); + } + else if (toResolve(file, myProject)) { + result.add(file); fileIsResolved.clear(getAbsId(file)); } } - private boolean hasSomething() { - if (DumbService.isDumb(myProject) || - myApplication.isWriteActionInProgress() || - RefreshQueueImpl.isRefreshInProgress() || - HeavyProcessLatch.INSTANCE.isRunning()) { - return false; - } - synchronized (filesToResolve) { - return !filesToResolve.isEmpty(); - } + private void enable() { + enabled = true; + wakeUp(); + } + + private void disable() { + enabled = false; + wakeUp(); } // returns list of resolved files if updated successfully, or null if write action or dumb mode started @@ -623,24 +638,28 @@ public class RefResolveServiceImpl extends RefResolveService implements Runnable public void run() { indicator.checkCanceled(); - psiFile.accept(new JavaRecursiveElementWalkingVisitor() { - @Override - public void visitReferenceElement(PsiJavaCodeReferenceElement reference) { - indicator.checkCanceled(); - PsiElement element = reference.resolve(); - if (element != null) { - resolved.add(element); + if (psiFile instanceof PsiJavaFile) { + psiFile.accept(new JavaRecursiveElementWalkingVisitor() { + @Override + public void visitReferenceElement(PsiJavaCodeReferenceElement reference) { + resolveReference(reference, indicator, resolved); + + super.visitReferenceElement(reference); } - refCount.incrementAndGet(); + }); + } + else if (psiFile instanceof XmlFile) { + psiFile.accept(new XmlRecursiveElementWalkingVisitor() { + @Override + public void visitXmlElement(XmlElement element) { + for (PsiReference reference : element.getReferences()) { + resolveReference(reference, indicator, resolved); + } + super.visitXmlElement(element); + } + }); + } - super.visitReferenceElement(reference); - } - }); - } - }); - ApplicationUtil.tryRunReadAction(new Runnable() { - @Override - public void run() { indicator.checkCanceled(); for (PsiElement element : resolved) { PsiFile file = element.getContainingFile(); @@ -654,6 +673,15 @@ public class RefResolveServiceImpl extends RefResolveService implements Runnable return forward; } + private void resolveReference(@NotNull PsiReference reference, @NotNull ProgressIndicator indicator, @NotNull Set resolved) { + indicator.checkCanceled(); + PsiElement element = reference.resolve(); + if (element != null) { + resolved.add(element); + } + refCount.incrementAndGet(); + } + private static void addIdAndSuperClasses(PsiFile file, @NotNull TIntHashSet forward) { if (file instanceof PsiJavaFile && file.getName().equals("Object.class") && ((PsiJavaFile)file).getPackageName().equals("java.lang")) { return; diff --git a/platform/indexing-impl/src/com/intellij/psi/impl/PersistentIntList.java b/platform/indexing-impl/src/com/intellij/psi/impl/PersistentIntList.java index 79487d1bad3e..aa5998364db4 100644 --- a/platform/indexing-impl/src/com/intellij/psi/impl/PersistentIntList.java +++ b/platform/indexing-impl/src/com/intellij/psi/impl/PersistentIntList.java @@ -221,6 +221,9 @@ public class PersistentIntList implements Disposable { capacity = 0; } else { + int[] oldIds = get(id); + checkSorted(oldIds); + assertPointer(pointer); int storedListLength = data.getInt(pointer); capacity = data.getInt(pointer + 4); @@ -253,6 +256,9 @@ public class PersistentIntList implements Disposable { i++; } } + int[] mergedInts = fromBytes(mergedBytes, outPtr); + checkSorted(mergedInts); + newListLength = outPtr / 4; assertListLength(newListLength, newListLength); if (newListLength <= capacity) { @@ -274,13 +280,17 @@ public class PersistentIntList implements Disposable { }); int[] ids = get(id); - for (int i = 1; i < ids.length; i++) { - assert ids[i] > ids[i - 1] : ids[i-1] + ", " + ids[i]; - } + checkSorted(ids); TIntHashSet set = new TIntHashSet(ids); assert set.containsAll(values): "ids: "+Arrays.toString(ids)+";\n values:"+Arrays.toString(values); } + private static void checkSorted(int[] oldIds) { + for (int i = 1; i < oldIds.length; i++) { + assert oldIds[i - 1] < oldIds[i] : oldIds[i-1] + ", " + oldIds[i]; + } + } + private static byte[] toBytes(@NotNull int[] values) { byte[] mergedBytes = new byte[4 * values.length]; for (int i = 0; i < values.length; i++) { @@ -290,6 +300,16 @@ public class PersistentIntList implements Disposable { return mergedBytes; } + private static int[] fromBytes(@NotNull byte[] bytes, int length) { + assert length % 4 == 0; + int[] ints = new int[length/4]; + for (int i = 0; i < length; i+=4) { + int value = Bits.getInt(bytes, i); + ints[i/4] = value; + } + return ints; + } + private static void storeArray(@NotNull RandomAccessDataFile data, int storePointer, int newListLength, diff --git a/platform/util/src/com/intellij/util/io/storage/HeavyProcessLatch.java b/platform/util/src/com/intellij/util/io/storage/HeavyProcessLatch.java index e0d349c0c7ad..663cd215cfbe 100644 --- a/platform/util/src/com/intellij/util/io/storage/HeavyProcessLatch.java +++ b/platform/util/src/com/intellij/util/io/storage/HeavyProcessLatch.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * 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. @@ -19,19 +19,47 @@ */ package com.intellij.util.io.storage; +import com.intellij.openapi.Disposable; +import com.intellij.openapi.util.Disposer; +import com.intellij.util.EventDispatcher; +import org.jetbrains.annotations.NotNull; + +import java.util.EventListener; +import java.util.concurrent.atomic.AtomicInteger; + public class HeavyProcessLatch { public static final HeavyProcessLatch INSTANCE = new HeavyProcessLatch(); - private int myHeavyProcessCounter = 0; - - public synchronized void processStarted() { - myHeavyProcessCounter++; + + private final AtomicInteger myHeavyProcessCounter = new AtomicInteger(); + private final EventDispatcher myEventDispatcher = EventDispatcher.create(HeavyProcessListener.class); + + private HeavyProcessLatch() { } - public synchronized void processFinished() { - myHeavyProcessCounter--; + public void processStarted() { + myHeavyProcessCounter.incrementAndGet(); + myEventDispatcher.getMulticaster().processStarted(); } - public synchronized boolean isRunning() { - return myHeavyProcessCounter != 0; + public void processFinished() { + myHeavyProcessCounter.decrementAndGet(); + myEventDispatcher.getMulticaster().processFinished(); + } + + public boolean isRunning() { + return myHeavyProcessCounter.get() != 0; + } + + public interface HeavyProcessListener extends EventListener { + public void processStarted(); + + public void processFinished(); + } + + @NotNull + public Disposable addListener(@NotNull HeavyProcessListener listener) { + Disposable disposable = Disposer.newDisposable(); + myEventDispatcher.addListener(listener, disposable); + return disposable; } } \ No newline at end of file diff --git a/xml/xml-psi-api/src/com/intellij/psi/XmlRecursiveElementWalkingVisitor.java b/xml/xml-psi-api/src/com/intellij/psi/XmlRecursiveElementWalkingVisitor.java new file mode 100644 index 000000000000..490eb62eb75e --- /dev/null +++ b/xml/xml-psi-api/src/com/intellij/psi/XmlRecursiveElementWalkingVisitor.java @@ -0,0 +1,58 @@ +/* + * 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. + */ + +/* + * @author max + */ +package com.intellij.psi; + +import java.util.List; + +public class XmlRecursiveElementWalkingVisitor extends XmlElementVisitor { + private final boolean myVisitAllFileRoots; + private final PsiWalkingState myWalkingState = new PsiWalkingState(this){}; + + public XmlRecursiveElementWalkingVisitor() { + this(false); + } + + public XmlRecursiveElementWalkingVisitor(final boolean visitAllFileRoots) { + myVisitAllFileRoots = visitAllFileRoots; + } + + @Override + public void visitElement(final PsiElement element) { + myWalkingState.elementStarted(element); + } + + @Override + public void visitFile(final PsiFile file) { + if (myVisitAllFileRoots) { + final FileViewProvider viewProvider = file.getViewProvider(); + final List allFiles = viewProvider.getAllFiles(); + if (allFiles.size() > 1) { + if (file == viewProvider.getPsi(viewProvider.getBaseLanguage())) { + for (PsiFile lFile : allFiles) { + lFile.acceptChildren(this); + } + return; + } + } + } + + super.visitFile(file); + } +} \ No newline at end of file