mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
usage view memory optimization
This commit is contained in:
@@ -15,12 +15,11 @@
|
||||
*/
|
||||
package com.intellij.psi.impl.smartPointers;
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.RangeMarker;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.PsiIdentifier;
|
||||
import com.intellij.psi.PsiJavaCodeReferenceElement;
|
||||
import com.intellij.psi.xml.XmlToken;
|
||||
import com.intellij.psi.xml.XmlTokenType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -29,72 +28,21 @@ import org.jetbrains.annotations.Nullable;
|
||||
/**
|
||||
* User: cdr
|
||||
*/
|
||||
class AnchorElementInfo implements SmartPointerElementInfo {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.smartPointers.AnchorElementInfo");
|
||||
private PsiFile myFile;
|
||||
private final RangeMarker myMarker;
|
||||
private int mySyncStartOffset;
|
||||
private int mySyncEndOffset;
|
||||
private boolean mySyncMarkerIsValid;
|
||||
private final Project myProject;
|
||||
|
||||
AnchorElementInfo(@NotNull PsiElement anchor) {
|
||||
LOG.assertTrue(anchor.isPhysical());
|
||||
LOG.assertTrue(anchor.isValid());
|
||||
myFile = anchor.getContainingFile();
|
||||
myProject = myFile.getProject();
|
||||
TextRange range = anchor.getTextRange();
|
||||
|
||||
final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(myProject);
|
||||
Document document = documentManager.getDocument(myFile);
|
||||
LOG.assertTrue(!documentManager.isUncommited(document));
|
||||
if (myFile.getTextLength() != document.getTextLength()) {
|
||||
final String docText = document.getText();
|
||||
myFile.accept(new PsiRecursiveElementWalkingVisitor() {
|
||||
@Override
|
||||
public void visitElement(PsiElement element) {
|
||||
super.visitElement(element);
|
||||
TextRange elementRange = element.getTextRange();
|
||||
final String rangeText = docText.length() <= elementRange.getEndOffset() ? "(IOOBE: "+docText.length()+" is out of "+elementRange+")" : elementRange.substring(docText);
|
||||
final String elemText = element.getText();
|
||||
if (!rangeText.equals(elemText)) {
|
||||
throw new AssertionError("PSI text doesn't equal to the document's one: element" + element + "\ndocText=" + rangeText + "\npsiText" + elemText);
|
||||
}
|
||||
}
|
||||
});
|
||||
LOG.error("File=" + myFile);
|
||||
}
|
||||
myMarker = document.createRangeMarker(range.getStartOffset(), range.getEndOffset(), true);
|
||||
|
||||
mySyncStartOffset = range.getStartOffset();
|
||||
mySyncEndOffset = range.getEndOffset();
|
||||
mySyncMarkerIsValid = true;
|
||||
}
|
||||
|
||||
public Document getDocumentToSynchronize() {
|
||||
return myMarker.getDocument();
|
||||
}
|
||||
|
||||
public void documentAndPsiInSync() {
|
||||
if (!myMarker.isValid()) {
|
||||
mySyncMarkerIsValid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
mySyncStartOffset = myMarker.getStartOffset();
|
||||
mySyncEndOffset = myMarker.getEndOffset();
|
||||
class AnchorElementInfo extends SelfElementInfo {
|
||||
AnchorElementInfo(@NotNull PsiElement anchor, PsiFile containingFile) {
|
||||
super(anchor, containingFile);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public PsiElement restoreElement() {
|
||||
if (!mySyncMarkerIsValid) return null;
|
||||
myFile = SelfElementInfo.restoreFile(myFile, myProject);
|
||||
if (myFile == null) return null;
|
||||
PsiElement anchor = myFile.findElementAt(mySyncStartOffset);
|
||||
PsiFile file = SelfElementInfo.restoreFileFromVirtual(myVirtualFile, myProject);
|
||||
if (file == null) return null;
|
||||
PsiElement anchor = file.findElementAt(getSyncStartOffset());
|
||||
if (anchor == null) return null;
|
||||
|
||||
TextRange range = anchor.getTextRange();
|
||||
if (range.getStartOffset() != mySyncStartOffset || range.getEndOffset() != mySyncEndOffset) return null;
|
||||
if (range.getStartOffset() != getSyncStartOffset() || range.getEndOffset() != getSyncEndOffset()) return null;
|
||||
|
||||
if (anchor instanceof PsiIdentifier) {
|
||||
PsiElement parent = anchor.getParent();
|
||||
@@ -106,20 +54,10 @@ class AnchorElementInfo implements SmartPointerElementInfo {
|
||||
|
||||
return parent;
|
||||
}
|
||||
else if (anchor instanceof XmlToken) {
|
||||
if (anchor instanceof XmlToken) {
|
||||
XmlToken token = (XmlToken)anchor;
|
||||
|
||||
return token.getTokenType() == XmlTokenType.XML_NAME ? token.getParent() : null;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
if (myMarker != null) {
|
||||
myMarker.dispose();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -29,7 +29,7 @@ public class AnchorElementInfoFactory implements SmartPointerElementInfoFactory
|
||||
public SmartPointerElementInfo createElementInfo(@NotNull PsiElement element) {
|
||||
PsiElement anchor = getAnchor(element);
|
||||
if (anchor != null) {
|
||||
return new AnchorElementInfo(anchor);
|
||||
return new AnchorElementInfo(anchor, element.getContainingFile());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -55,7 +55,7 @@ public class AnchorElementInfoFactory implements SmartPointerElementInfoFactory
|
||||
else if (element instanceof XmlTag) {
|
||||
anchor = XmlTagUtil.getStartTagNameElement((XmlTag)element);
|
||||
}
|
||||
if (anchor != null && !anchor.isPhysical()) return null;
|
||||
if (anchor != null && (!anchor.isPhysical() || anchor.getTextRange()==null)) return null;
|
||||
return anchor;
|
||||
}
|
||||
|
||||
|
||||
+43
-5
@@ -15,15 +15,20 @@
|
||||
*/
|
||||
package com.intellij.psi.impl.smartPointers;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.ImplicitVariable;
|
||||
import com.intellij.psi.PsiIdentifier;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.Segment;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.ImplicitVariable;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiIdentifier;
|
||||
import com.intellij.psi.util.PsiUtilBase;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class ImplicitVariableElementInfoFactory implements SmartPointerElementInfoFactory {
|
||||
@Nullable
|
||||
public SmartPointerElementInfo createElementInfo(final PsiElement element) {
|
||||
public SmartPointerElementInfo createElementInfo(@NotNull final PsiElement element) {
|
||||
if (element instanceof ImplicitVariable) {
|
||||
return new ImplicitVariableInfo((ImplicitVariable) element);
|
||||
}
|
||||
@@ -54,5 +59,38 @@ public class ImplicitVariableElementInfoFactory implements SmartPointerElementIn
|
||||
|
||||
public void documentAndPsiInSync() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fastenBelt(int offset) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unfastenBelt(int offset) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int elementHashCode() {
|
||||
return myVar.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean pointsToTheSameElementAs(SmartPointerElementInfo other) {
|
||||
if (other instanceof ImplicitVariableInfo) {
|
||||
return myVar == ((ImplicitVariableInfo)other).myVar;
|
||||
}
|
||||
return Comparing.equal(restoreElement(), other.restoreElement());
|
||||
}
|
||||
|
||||
@Override
|
||||
public VirtualFile getVirtualFile() {
|
||||
return PsiUtilBase.getVirtualFile(myVar);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Segment getSegment() {
|
||||
PsiIdentifier psiIdentifier = myVar.getNameIdentifier();
|
||||
if (psiIdentifier == null || !psiIdentifier.isValid()) return null;
|
||||
return psiIdentifier.getTextRange();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+10
-22
@@ -15,42 +15,30 @@
|
||||
*/
|
||||
package com.intellij.psi.impl.smartPointers;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiJavaFile;
|
||||
import com.intellij.psi.PsiImportList;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.psi.PsiJavaFile;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class ImportListElementInfoFactory implements SmartPointerElementInfoFactory {
|
||||
@Nullable
|
||||
public SmartPointerElementInfo createElementInfo(final PsiElement element) {
|
||||
public SmartPointerElementInfo createElementInfo(@NotNull final PsiElement element) {
|
||||
if (element instanceof PsiImportList) {
|
||||
return new ImportListInfo((PsiJavaFile)element.getContainingFile());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static class ImportListInfo implements SmartPointerElementInfo {
|
||||
private final PsiJavaFile myFile;
|
||||
|
||||
public ImportListInfo(PsiJavaFile file) {
|
||||
myFile = file;
|
||||
private static class ImportListInfo extends FileElementInfo {
|
||||
public ImportListInfo(@NotNull PsiJavaFile file) {
|
||||
super(file);
|
||||
}
|
||||
|
||||
public PsiElement restoreElement() {
|
||||
if (!myFile.isValid()) return null;
|
||||
return myFile.getImportList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
}
|
||||
|
||||
public Document getDocumentToSynchronize() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void documentAndPsiInSync() {
|
||||
PsiElement element = super.restoreElement();
|
||||
if (!(element instanceof PsiJavaFile)) return null;
|
||||
return ((PsiJavaFile)element).getImportList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,20 +100,16 @@ public class ClassGroupingRule implements UsageGroupingRule {
|
||||
private final SmartPsiElementPointer myClassPointer;
|
||||
private final String myText;
|
||||
private final String myQName;
|
||||
private Icon myIcon;
|
||||
private final Icon myIcon;
|
||||
|
||||
public ClassUsageGroup(PsiClass aClass) {
|
||||
myQName = aClass.getQualifiedName();
|
||||
myText = createText(aClass);
|
||||
SmartPointerManager smartPointerManager = SmartPointerManager.getInstance(aClass.getProject());
|
||||
myClassPointer = smartPointerManager.createLazyPointer(aClass);
|
||||
update();
|
||||
myClassPointer = SmartPointerManager.getInstance(aClass.getProject()).createSmartPsiElementPointer(aClass);
|
||||
myIcon = getPsiClass().getIcon(Iconable.ICON_FLAG_VISIBILITY | Iconable.ICON_FLAG_READ_STATUS);
|
||||
}
|
||||
|
||||
public void update() {
|
||||
if (isValid()) {
|
||||
myIcon = getPsiClass().getIcon(Iconable.ICON_FLAG_VISIBILITY | Iconable.ICON_FLAG_READ_STATUS);
|
||||
}
|
||||
}
|
||||
|
||||
private static String createText(PsiClass aClass) {
|
||||
|
||||
@@ -23,6 +23,7 @@ import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.Iconable;
|
||||
import com.intellij.openapi.util.Segment;
|
||||
import com.intellij.openapi.vcs.FileStatus;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
|
||||
@@ -69,7 +70,7 @@ public class MethodGroupingRule implements UsageGroupingRule {
|
||||
private static class MethodUsageGroup implements UsageGroup, TypeSafeDataProvider {
|
||||
private final SmartPsiElementPointer<PsiMethod> myMethodPointer;
|
||||
private final String myName;
|
||||
private Icon myIcon;
|
||||
private final Icon myIcon;
|
||||
private final Project myProject;
|
||||
|
||||
public MethodUsageGroup(PsiMethod psiMethod) {
|
||||
@@ -80,15 +81,12 @@ public class MethodGroupingRule implements UsageGroupingRule {
|
||||
PsiFormatUtilBase.SHOW_TYPE
|
||||
);
|
||||
myProject = psiMethod.getProject();
|
||||
myMethodPointer = SmartPointerManager.getInstance(myProject).createLazyPointer(psiMethod);
|
||||
myMethodPointer = SmartPointerManager.getInstance(myProject).createSmartPsiElementPointer(psiMethod);
|
||||
|
||||
myIcon = getIconImpl(psiMethod);
|
||||
}
|
||||
|
||||
public void update() {
|
||||
if (isValid()) {
|
||||
myIcon = getIconImpl(getMethod());
|
||||
}
|
||||
}
|
||||
|
||||
private static Icon getIconImpl(PsiMethod psiMethod) {
|
||||
@@ -104,10 +102,8 @@ public class MethodGroupingRule implements UsageGroupingRule {
|
||||
return false;
|
||||
}
|
||||
MethodUsageGroup group = (MethodUsageGroup) object;
|
||||
if (isValid() && group.isValid()) {
|
||||
return getMethod().getManager().areElementsEquivalent(getMethod(), group.getMethod());
|
||||
}
|
||||
return Comparing.equal(myName, ((MethodUsageGroup)object).myName);
|
||||
return Comparing.equal(myName, ((MethodUsageGroup)object).myName)
|
||||
&& SmartPointerManager.getInstance(myProject).pointToTheSameElement(myMethodPointer, group.myMethodPointer);
|
||||
}
|
||||
|
||||
public Icon getIcon(boolean isOpen) {
|
||||
@@ -151,10 +147,15 @@ public class MethodGroupingRule implements UsageGroupingRule {
|
||||
LOG.error("MethodUsageGroup expected but " + usageGroup.getClass() + " found");
|
||||
}
|
||||
MethodUsageGroup other = (MethodUsageGroup)usageGroup;
|
||||
PsiMethod myMethod = myMethodPointer.getElement();
|
||||
PsiMethod otherMethod = other.myMethodPointer.getElement();
|
||||
if (myMethod != null && otherMethod != null && !UsageViewSettings.getInstance().IS_SORT_MEMBERS_ALPHABETICALLY) {
|
||||
return myMethod.getTextOffset() < otherMethod.getTextOffset() ? -1 : 1;
|
||||
if (SmartPointerManager.getInstance(myProject).pointToTheSameElement(myMethodPointer, other.myMethodPointer)) {
|
||||
return 0;
|
||||
}
|
||||
if (!UsageViewSettings.getInstance().IS_SORT_MEMBERS_ALPHABETICALLY) {
|
||||
Segment segment1 = myMethodPointer.getSegment();
|
||||
Segment segment2 = other.myMethodPointer.getSegment();
|
||||
if (segment1 != null && segment2 != null) {
|
||||
return segment1.getStartOffset() - segment2.getStartOffset();
|
||||
}
|
||||
}
|
||||
|
||||
return myName.compareTo(other.myName);
|
||||
|
||||
@@ -20,14 +20,15 @@
|
||||
package com.intellij.psi;
|
||||
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.Segment;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class IdentitySmartPointer<T extends PsiElement> implements SmartPsiElementPointer<T> {
|
||||
private T myElement;
|
||||
private final PsiFile myFile;
|
||||
|
||||
public IdentitySmartPointer(T element, PsiFile file) {
|
||||
public IdentitySmartPointer(@NotNull T element, @NotNull PsiFile file) {
|
||||
myElement = element;
|
||||
myFile = file;
|
||||
}
|
||||
@@ -41,11 +42,17 @@ public class IdentitySmartPointer<T extends PsiElement> implements SmartPsiEleme
|
||||
return myFile.getProject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public VirtualFile getVirtualFile() {
|
||||
return myFile.getVirtualFile();
|
||||
}
|
||||
|
||||
public T getElement() {
|
||||
if (myElement != null && !myElement.isValid()) {
|
||||
myElement = null;
|
||||
T element = myElement;
|
||||
if (element != null && !element.isValid()) {
|
||||
element = null;
|
||||
}
|
||||
return myElement;
|
||||
return element;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
@@ -54,10 +61,17 @@ public class IdentitySmartPointer<T extends PsiElement> implements SmartPsiEleme
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof SmartPsiElementPointer && Comparing.equal(getElement(), ((SmartPsiElementPointer)obj).getElement());
|
||||
return obj instanceof SmartPsiElementPointer
|
||||
&& SmartPointerManager.getInstance(getProject()).pointToTheSameElement(this, (SmartPsiElementPointer)obj);
|
||||
}
|
||||
|
||||
public PsiFile getContainingFile() {
|
||||
return myFile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Segment getSegment() {
|
||||
T element = myElement;
|
||||
return element == null ? null : element.getTextRange();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,9 +39,17 @@ public abstract class SmartPointerManager {
|
||||
/**
|
||||
* Creates a smart pointer to the specified PSI element which doesn't hold a strong reference to the PSI
|
||||
* element.
|
||||
*
|
||||
* @deprecated use {@link #createSmartPsiElementPointer(PsiElement)} instead
|
||||
* @param element the element to create a pointer to.
|
||||
* @return the smart pointer instance.
|
||||
*/
|
||||
@NotNull public abstract <E extends PsiElement> SmartPsiElementPointer<E> createLazyPointer(@NotNull E element);
|
||||
|
||||
/**
|
||||
* @param pointer1 smart pointer to compare
|
||||
* @param pointer2 smart pointer to compare
|
||||
* @return true if both pointers point to the same PSI element.
|
||||
* This method is cheaper than dereferencing both pointers and comparing the result.
|
||||
*/
|
||||
public abstract boolean pointToTheSameElement(@NotNull SmartPsiElementPointer pointer1, @NotNull SmartPsiElementPointer pointer2);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
package com.intellij.psi;
|
||||
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Segment;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -41,4 +43,8 @@ public interface SmartPsiElementPointer<E extends PsiElement> {
|
||||
|
||||
@NotNull
|
||||
Project getProject();
|
||||
|
||||
VirtualFile getVirtualFile();
|
||||
|
||||
Segment getSegment();
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ import com.intellij.openapi.fileTypes.NativeFileType;
|
||||
import com.intellij.openapi.fileTypes.UnknownFileType;
|
||||
import com.intellij.openapi.progress.ProcessCanceledException;
|
||||
import com.intellij.openapi.project.IndexNotReadyException;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.IconLoader;
|
||||
import com.intellij.openapi.util.Iconable;
|
||||
import com.intellij.openapi.util.UserDataHolderBase;
|
||||
@@ -31,6 +30,8 @@ import com.intellij.openapi.util.registry.Registry;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.SmartPointerManager;
|
||||
import com.intellij.psi.SmartPsiElementPointer;
|
||||
import com.intellij.ui.IconDeferrer;
|
||||
import com.intellij.ui.LayeredIcon;
|
||||
import com.intellij.ui.RowIcon;
|
||||
@@ -48,6 +49,14 @@ public abstract class ElementBase extends UserDataHolderBase implements Iconable
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.ElementBase");
|
||||
|
||||
public static final int FLAGS_LOCKED = 0x800;
|
||||
private static final NullableFunction<ElementIconRequest,Icon> ICON_COMPUTE = new NullableFunction<ElementIconRequest, Icon>() {
|
||||
public Icon fun(ElementIconRequest request) {
|
||||
final PsiElement element = request.getElement();
|
||||
if (!element.isValid()) return null;
|
||||
if (element.getProject().isDisposed()) return null;
|
||||
return computeIconNow(element, request.getFlags());
|
||||
}
|
||||
};
|
||||
private TIntObjectHashMap<Icon> myBaseIcon;
|
||||
|
||||
private static final Icon VISIBILITY_ICON_PLACHOLDER = new EmptyIcon(Icons.PUBLIC_ICON);
|
||||
@@ -88,18 +97,10 @@ public abstract class ElementBase extends UserDataHolderBase implements Iconable
|
||||
baseIcon = myBaseIcon.get(flags);
|
||||
}
|
||||
|
||||
final Project project = psiElement.getProject();
|
||||
if (isToDeferIconLoading()) {
|
||||
return IconDeferrer.getInstance().defer(baseIcon, new ElementIconRequest(psiElement, flags), new NullableFunction<ElementIconRequest, Icon>() {
|
||||
public Icon fun(ElementIconRequest request) {
|
||||
if (project.isDisposed()) return null;
|
||||
final PsiElement element = request.getElement();
|
||||
if (!element.isValid()) return null;
|
||||
if (element.getProject().isDisposed()) return null;
|
||||
return computeIconNow(element, request.getFlags());
|
||||
}
|
||||
});
|
||||
} else {
|
||||
return IconDeferrer.getInstance().defer(baseIcon, new ElementIconRequest(psiElement, flags), ICON_COMPUTE);
|
||||
}
|
||||
else {
|
||||
if (!psiElement.isValid()) return null;
|
||||
return computeIconNow(psiElement, flags);
|
||||
}
|
||||
@@ -110,12 +111,12 @@ public abstract class ElementBase extends UserDataHolderBase implements Iconable
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Icon computeIconNow(PsiElement element, int flags) {
|
||||
private static Icon computeIconNow(PsiElement element, int flags) {
|
||||
final Icon providersIcon = PsiIconUtil.getProvidersIcon(element, flags);
|
||||
if (providersIcon != null) {
|
||||
return providersIcon instanceof RowIcon ? (RowIcon)providersIcon : createLayeredIcon(providersIcon, flags);
|
||||
}
|
||||
return getElementIcon(flags);
|
||||
return ((ElementBase)element).getElementIcon(flags);
|
||||
}
|
||||
|
||||
protected Icon computeBaseIcon(int flags) {
|
||||
@@ -177,11 +178,16 @@ public abstract class ElementBase extends UserDataHolderBase implements Iconable
|
||||
|
||||
public static class ElementIconRequest extends ComparableObject.Impl {
|
||||
public ElementIconRequest(PsiElement element, int flags) {
|
||||
super(new Object[] {element, flags});
|
||||
super(new Object[] {createPointer(element), flags});
|
||||
}
|
||||
|
||||
private static Object createPointer(PsiElement element) {
|
||||
return SmartPointerManager.getInstance(element.getProject()).createSmartPsiElementPointer(element);
|
||||
}
|
||||
|
||||
public PsiElement getElement() {
|
||||
return (PsiElement)getEqualityObjects()[0];
|
||||
SmartPsiElementPointer pointer = (SmartPsiElementPointer)getEqualityObjects()[0];
|
||||
return pointer.getElement();
|
||||
}
|
||||
|
||||
public int getFlags() {
|
||||
|
||||
@@ -15,11 +15,12 @@
|
||||
*/
|
||||
package com.intellij.usageView;
|
||||
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager;
|
||||
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.Segment;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.*;
|
||||
@@ -29,8 +30,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
public class UsageInfo {
|
||||
public static final UsageInfo[] EMPTY_ARRAY = new UsageInfo[0];
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.usageView.UsageInfo");
|
||||
private final SmartPsiElementPointer mySmartPointer;
|
||||
private final VirtualFile myVirtualFile;
|
||||
private final SmartPsiElementPointer<?> mySmartPointer;
|
||||
public final int startOffset; // in navigation element
|
||||
public final int endOffset; // in navigation element
|
||||
|
||||
@@ -38,9 +38,19 @@ public class UsageInfo {
|
||||
|
||||
public UsageInfo(@NotNull PsiElement element, int startOffset, int endOffset, boolean isNonCodeUsage) {
|
||||
LOG.assertTrue(element.isValid());
|
||||
LOG.assertTrue(element == element.getNavigationElement());
|
||||
mySmartPointer = SmartPointerManager.getInstance(element.getProject()).createSmartPsiElementPointer(element);
|
||||
myVirtualFile = element.getContainingFile().getVirtualFile();
|
||||
element = element.getNavigationElement();
|
||||
SmartPointerManager smartPointerManager = SmartPointerManager.getInstance(element.getProject());
|
||||
mySmartPointer = smartPointerManager.createSmartPsiElementPointer(element);
|
||||
|
||||
if (startOffset == -1 && endOffset == -1) {
|
||||
// calculate natural element range
|
||||
TextRange range = element.getTextRange();
|
||||
if (range == null) {
|
||||
LOG.error("text range null for " + element + "; " + element.getClass());
|
||||
}
|
||||
startOffset = element.getTextOffset() - range.getStartOffset();
|
||||
endOffset = range.getEndOffset() - range.getStartOffset();
|
||||
}
|
||||
this.startOffset = startOffset;
|
||||
this.endOffset = endOffset;
|
||||
this.isNonCodeUsage = isNonCodeUsage;
|
||||
@@ -49,22 +59,7 @@ public class UsageInfo {
|
||||
}
|
||||
|
||||
public UsageInfo(@NotNull PsiElement element, boolean isNonCodeUsage) {
|
||||
LOG.assertTrue(element.isValid());
|
||||
element = element.getNavigationElement();
|
||||
|
||||
mySmartPointer = SmartPointerManager.getInstance(element.getProject()).createSmartPsiElementPointer(element);
|
||||
myVirtualFile = element.getContainingFile().getVirtualFile();
|
||||
|
||||
TextRange range = element.getTextRange();
|
||||
if (range == null) {
|
||||
LOG.error("text range null for " + element+"; "+element.getClass());
|
||||
}
|
||||
startOffset = element.getTextOffset() - range.getStartOffset();
|
||||
endOffset = range.getEndOffset() - range.getStartOffset();
|
||||
|
||||
this.isNonCodeUsage = isNonCodeUsage;
|
||||
LOG.assertTrue(startOffset >= 0, startOffset);
|
||||
LOG.assertTrue(endOffset >= startOffset, endOffset-startOffset);
|
||||
this(element, -1, -1, isNonCodeUsage);
|
||||
}
|
||||
|
||||
public UsageInfo(@NotNull PsiElement element, int startOffset, int endOffset) {
|
||||
@@ -105,15 +100,25 @@ public class UsageInfo {
|
||||
}
|
||||
|
||||
public final void navigateTo(boolean requestFocus) {
|
||||
PsiElement element = getElement();
|
||||
if (element == null) return;
|
||||
VirtualFile file = element.getContainingFile().getVirtualFile();
|
||||
TextRange range = element.getTextRange();
|
||||
int offset = range.getStartOffset() + startOffset;
|
||||
Project project = element.getProject();
|
||||
int offset = getNavigationOffset();
|
||||
VirtualFile file = getVirtualFile();
|
||||
Project project = getProject();
|
||||
FileEditorManager.getInstance(project).openTextEditor(new OpenFileDescriptor(project, file, offset), requestFocus);
|
||||
}
|
||||
|
||||
public int getNavigationOffset() {
|
||||
PsiElement element = getElement();
|
||||
if (element == null) return -1;
|
||||
TextRange range = element.getTextRange();
|
||||
return range.getStartOffset() + startOffset;
|
||||
}
|
||||
public Segment getSegment() {
|
||||
PsiElement element = getElement();
|
||||
if (element == null) return null;
|
||||
TextRange range = element.getTextRange();
|
||||
return new TextRange(range.getStartOffset() + startOffset, Math.min(range.getEndOffset(), range.getStartOffset() + endOffset));
|
||||
}
|
||||
|
||||
public Project getProject() {
|
||||
return mySmartPointer.getProject();
|
||||
}
|
||||
@@ -132,9 +137,8 @@ public class UsageInfo {
|
||||
if (endOffset != usageInfo.endOffset) return false;
|
||||
if (isNonCodeUsage != usageInfo.isNonCodeUsage) return false;
|
||||
if (startOffset != usageInfo.startOffset) return false;
|
||||
PsiElement thisElement = mySmartPointer.getElement();
|
||||
PsiElement thatElement = usageInfo.mySmartPointer.getElement();
|
||||
return Comparing.equal(thisElement, thatElement);
|
||||
|
||||
return SmartPointerManager.getInstance(getProject()).pointToTheSameElement(mySmartPointer, usageInfo.mySmartPointer);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
@@ -150,7 +154,12 @@ public class UsageInfo {
|
||||
return mySmartPointer.getContainingFile();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public VirtualFile getVirtualFile() {
|
||||
return myVirtualFile;
|
||||
return mySmartPointer.getVirtualFile();
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
((Disposable)mySmartPointer).dispose();
|
||||
}
|
||||
}
|
||||
|
||||
+16
-10
@@ -29,6 +29,7 @@ import com.intellij.openapi.editor.ex.EditorEx;
|
||||
import com.intellij.openapi.editor.highlighter.EditorHighlighterFactory;
|
||||
import com.intellij.openapi.editor.markup.TextAttributes;
|
||||
import com.intellij.openapi.fileTypes.FileType;
|
||||
import com.intellij.openapi.util.Segment;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.util.ui.RangeBlinker;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
@@ -78,23 +79,28 @@ class IntentionUsagePanel extends JPanel{
|
||||
}
|
||||
|
||||
private void setupSpots(Document document) {
|
||||
List<RangeMarker> markers = new ArrayList<RangeMarker>();
|
||||
List<Segment> markers = new ArrayList<Segment>();
|
||||
while (true) {
|
||||
String text = document.getText();
|
||||
int spotStart = text.indexOf("<" + SPOT_MARKER + ">");
|
||||
final int spotStart = text.indexOf("<" + SPOT_MARKER + ">");
|
||||
if (spotStart < 0) break;
|
||||
int spotEnd = text.indexOf("</" + SPOT_MARKER + ">", spotStart);
|
||||
final int spotEnd = text.indexOf("</" + SPOT_MARKER + ">", spotStart);
|
||||
if (spotEnd < 0) break;
|
||||
|
||||
document.deleteString(spotEnd, spotEnd + SPOT_MARKER.length() + 3);
|
||||
document.deleteString(spotStart, spotStart + SPOT_MARKER.length() + 2);
|
||||
final RangeMarker spotMarker = document.createRangeMarker(spotStart, spotEnd - SPOT_MARKER.length() - 2);
|
||||
if (spotMarker == null) {
|
||||
break;
|
||||
}
|
||||
else {
|
||||
markers.add(spotMarker);
|
||||
}
|
||||
Segment spotMarker = new Segment() {
|
||||
@Override
|
||||
public int getStartOffset() {
|
||||
return spotStart;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getEndOffset() {
|
||||
return spotEnd - SPOT_MARKER.length() - 2;
|
||||
}
|
||||
};
|
||||
markers.add(spotMarker);
|
||||
}
|
||||
myRangeBlinker.resetMarkers(markers);
|
||||
if (!markers.isEmpty()) {
|
||||
|
||||
+33
-30
@@ -29,11 +29,11 @@ import com.intellij.openapi.command.CommandProcessor;
|
||||
import com.intellij.openapi.components.ServiceManager;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.RangeMarker;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.util.Computable;
|
||||
import com.intellij.openapi.util.Factory;
|
||||
import com.intellij.openapi.util.Segment;
|
||||
import com.intellij.openapi.vfs.ReadonlyStatusHandler;
|
||||
import com.intellij.openapi.vfs.VfsUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
@@ -322,37 +322,40 @@ public class ReplaceInProjectManager {
|
||||
return;
|
||||
}
|
||||
|
||||
List<RangeMarker> markers = ((UsageInfo2UsageAdapter)usage).getRangeMarkers();
|
||||
for (RangeMarker marker : markers) {
|
||||
Document document = marker.getDocument();
|
||||
if (!document.isWritable()) return;
|
||||
final Document document = ((UsageInfo2UsageAdapter)usage).getDocument();
|
||||
((UsageInfo2UsageAdapter)usage).processRangeMarkers(new Processor<Segment>() {
|
||||
@Override
|
||||
public boolean process(Segment segment) {
|
||||
if (!document.isWritable()) return false;
|
||||
|
||||
final int textOffset = marker.getStartOffset();
|
||||
if (textOffset < 0 || textOffset >= document.getTextLength()) {
|
||||
return;
|
||||
final int textOffset = segment.getStartOffset();
|
||||
if (textOffset < 0 || textOffset >= document.getTextLength()) {
|
||||
return true;
|
||||
}
|
||||
final int textEndOffset = segment.getEndOffset();
|
||||
if (textEndOffset < 0 || textOffset > document.getTextLength()) {
|
||||
return true;
|
||||
}
|
||||
FindManager findManager = FindManager.getInstance(myProject);
|
||||
final CharSequence foundString = document.getCharsSequence().subSequence(textOffset, textEndOffset);
|
||||
FindResult findResult = findManager.findString(document.getCharsSequence(), textOffset, replaceContext.getFindModel());
|
||||
if (!findResult.isStringFound()) {
|
||||
return true;
|
||||
}
|
||||
String stringToReplace = null;
|
||||
try {
|
||||
stringToReplace =
|
||||
findManager.getStringToReplace(foundString.toString(), replaceContext.getFindModel(), textOffset, document.getText());
|
||||
}
|
||||
catch (FindManager.MalformedReplacementStringException e) {
|
||||
Messages.showErrorDialog(myProject, e.getMessage(), FindBundle.message("find.replace.invalid.replacement.string.title"));
|
||||
}
|
||||
if (stringToReplace != null) {
|
||||
document.replaceString(textOffset, textEndOffset, stringToReplace);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
final int textEndOffset = marker.getEndOffset();
|
||||
if (textEndOffset < 0 || textOffset > document.getTextLength()) {
|
||||
return;
|
||||
}
|
||||
FindManager findManager = FindManager.getInstance(myProject);
|
||||
final CharSequence foundString = document.getCharsSequence().subSequence(textOffset, textEndOffset);
|
||||
FindResult findResult = findManager.findString(document.getCharsSequence(), textOffset, replaceContext.getFindModel());
|
||||
if (!findResult.isStringFound()) {
|
||||
return;
|
||||
}
|
||||
String stringToReplace = null;
|
||||
try {
|
||||
stringToReplace =
|
||||
findManager.getStringToReplace(foundString.toString(), replaceContext.getFindModel(), textOffset, document.getText());
|
||||
}
|
||||
catch (FindManager.MalformedReplacementStringException e) {
|
||||
Messages.showErrorDialog(myProject, e.getMessage(), FindBundle.message("find.replace.invalid.replacement.string.title"));
|
||||
}
|
||||
if (stringToReplace != null) {
|
||||
document.replaceString(textOffset, textEndOffset, stringToReplace);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -30,15 +30,13 @@ import com.intellij.psi.PsiDirectoryContainer;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.usages.Usage;
|
||||
import com.intellij.usages.UsageDataUtil;
|
||||
import com.intellij.usages.UsageTarget;
|
||||
import com.intellij.usages.UsageView;
|
||||
import com.intellij.usages.rules.UsageInFile;
|
||||
import com.intellij.usages.rules.UsageInFiles;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class VirtualFileArrayRule implements GetDataRule {
|
||||
public Object getData(final DataProvider dataProvider) {
|
||||
@@ -84,44 +82,13 @@ public class VirtualFileArrayRule implements GetDataRule {
|
||||
|
||||
Usage[] usages = UsageView.USAGES_KEY.getData(dataProvider);
|
||||
UsageTarget[] usageTargets = UsageView.USAGE_TARGETS_KEY.getData(dataProvider);
|
||||
if (usages != null || usageTargets != null) {
|
||||
return getFilesFromUsages(usages, usageTargets);
|
||||
if (usages != null || usageTargets != null) {
|
||||
return UsageDataUtil.provideVirtualFileArray(usages, usageTargets);
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static VirtualFile[] getFilesFromUsages(Usage[] usages, UsageTarget[] usageTargets) {
|
||||
Set<VirtualFile> result = new HashSet<VirtualFile>();
|
||||
|
||||
if (usages != null) {
|
||||
for (Usage usage : usages) {
|
||||
if (!usage.isValid()) continue;
|
||||
if (usage instanceof UsageInFile) {
|
||||
UsageInFile usageInFile = (UsageInFile)usage;
|
||||
result.add(usageInFile.getFile());
|
||||
}
|
||||
|
||||
if (usage instanceof UsageInFiles) {
|
||||
UsageInFiles usageInFiles = (UsageInFiles)usage;
|
||||
ContainerUtil.addAll(result, usageInFiles.getFiles());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (usageTargets != null) {
|
||||
for (UsageTarget usageTarget : usageTargets) {
|
||||
if (!usageTarget.isValid()) continue;
|
||||
VirtualFile[] files = usageTarget.getFiles();
|
||||
if (files != null) {
|
||||
ContainerUtil.addAll(result, files);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return VfsUtil.toVirtualFileArray(result);
|
||||
}
|
||||
|
||||
private static Object getFilesFromPsiElement(PsiElement elem) {
|
||||
if (elem instanceof PsiFile) {
|
||||
|
||||
@@ -21,10 +21,12 @@ import com.intellij.lang.Language;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.NullableComputable;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.impl.light.LightElement;
|
||||
import com.intellij.psi.impl.smartPointers.SelfElementInfo;
|
||||
import com.intellij.psi.impl.source.PsiFileImpl;
|
||||
import com.intellij.psi.impl.source.PsiFileWithStubSupport;
|
||||
import com.intellij.psi.stubs.IStubElementType;
|
||||
@@ -61,6 +63,8 @@ public abstract class PsiAnchor {
|
||||
if (file == null) {
|
||||
return new HardReference(element);
|
||||
}
|
||||
VirtualFile virtualFile = file.getVirtualFile();
|
||||
if (virtualFile == null) return new HardReference(element);
|
||||
|
||||
if (element instanceof StubBasedPsiElement && element.isPhysical() && (element instanceof PsiCompiledElement || ((PsiFileImpl)file).getContentElementType() instanceof IStubFileElementType)) {
|
||||
final StubBasedPsiElement elt = (StubBasedPsiElement)element;
|
||||
@@ -88,7 +92,7 @@ public abstract class PsiAnchor {
|
||||
}
|
||||
|
||||
if (lang == null) lang = element.getLanguage();
|
||||
return new TreeRangeReference(file, textRange.getStartOffset(), textRange.getEndOffset(), element.getClass(), lang);
|
||||
return new TreeRangeReference(file, textRange.getStartOffset(), textRange.getEndOffset(), element.getClass(), lang, virtualFile);
|
||||
}
|
||||
|
||||
private static int calcStubIndex(StubBasedPsiElement psi) {
|
||||
@@ -112,16 +116,22 @@ public abstract class PsiAnchor {
|
||||
return -1; // it is possible via custom stub builder intentionally not producing stubs for stubbed elements
|
||||
}
|
||||
|
||||
|
||||
private static class TreeRangeReference extends PsiAnchor {
|
||||
private final PsiFile myFile;
|
||||
private final VirtualFile myVirtualFile;
|
||||
private final Project myProject;
|
||||
private final Language myLanguage;
|
||||
private final int myStartOffset;
|
||||
private final int myEndOffset;
|
||||
private final Class myClass;
|
||||
|
||||
private TreeRangeReference(final PsiFile file, final int startOffset, final int endOffset, final Class aClass, final Language language) {
|
||||
myFile = file;
|
||||
private TreeRangeReference(@NotNull PsiFile file,
|
||||
int startOffset,
|
||||
int endOffset,
|
||||
@NotNull Class aClass,
|
||||
@NotNull Language language,
|
||||
@NotNull VirtualFile virtualFile) {
|
||||
myVirtualFile = virtualFile;
|
||||
myProject = file.getProject();
|
||||
myStartOffset = startOffset;
|
||||
myEndOffset = endOffset;
|
||||
myClass = aClass;
|
||||
@@ -130,7 +140,9 @@ public abstract class PsiAnchor {
|
||||
|
||||
@Nullable
|
||||
public PsiElement retrieve() {
|
||||
PsiElement element = myFile.getViewProvider().findElementAt(myStartOffset, myLanguage);
|
||||
PsiFile psiFile = getFile();
|
||||
if (psiFile == null || !psiFile.isValid()) return null;
|
||||
PsiElement element = psiFile.getViewProvider().findElementAt(myStartOffset, myLanguage);
|
||||
if (element == null) return null;
|
||||
|
||||
while (!element.getClass().equals(myClass) ||
|
||||
@@ -144,7 +156,7 @@ public abstract class PsiAnchor {
|
||||
}
|
||||
|
||||
public PsiFile getFile() {
|
||||
return myFile;
|
||||
return SelfElementInfo.restoreFileFromVirtual(myVirtualFile, myProject);
|
||||
}
|
||||
|
||||
public int getStartOffset() {
|
||||
@@ -161,24 +173,26 @@ public abstract class PsiAnchor {
|
||||
|
||||
final TreeRangeReference that = (TreeRangeReference)o;
|
||||
|
||||
if (myEndOffset != that.myEndOffset) return false;
|
||||
if (myStartOffset != that.myStartOffset) return false;
|
||||
if (myClass != null ? !myClass.equals(that.myClass) : that.myClass != null) return false;
|
||||
if (myFile != null ? !myFile.equals(that.myFile) : that.myFile != null) return false;
|
||||
|
||||
return true;
|
||||
return myEndOffset == that.myEndOffset &&
|
||||
myStartOffset == that.myStartOffset &&
|
||||
myClass.equals(that.myClass) &&
|
||||
myVirtualFile.equals(that.myVirtualFile);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int result = myClass != null ? myClass.getName().hashCode() : 0;
|
||||
result = 31 * result + myStartOffset; //todo
|
||||
result = 31 * result + myEndOffset;
|
||||
if (myFile != null) {
|
||||
result = 31 * result + myFile.getName().hashCode();
|
||||
}
|
||||
result = 31 * result + myVirtualFile.getName().hashCode();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean pointsToTheSameElementAs(PsiAnchor other) {
|
||||
if (other instanceof TreeRangeReference) return equals(other);
|
||||
return Comparing.equal(retrieve(), other.retrieve());
|
||||
}
|
||||
}
|
||||
|
||||
private static class HardReference extends PsiAnchor {
|
||||
@@ -217,9 +231,14 @@ public abstract class PsiAnchor {
|
||||
public int hashCode() {
|
||||
return myElement.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean pointsToTheSameElementAs(PsiAnchor other) {
|
||||
return myElement == other.retrieve();
|
||||
}
|
||||
}
|
||||
|
||||
public static class StubIndexReference extends PsiAnchor {
|
||||
private static class StubIndexReference extends PsiAnchor {
|
||||
private final VirtualFile myVirtualFile;
|
||||
private final Project myProject;
|
||||
private final int myIndex;
|
||||
@@ -313,6 +332,14 @@ public abstract class PsiAnchor {
|
||||
if (resolved == null) throw new PsiInvalidElementAccessException(null);
|
||||
return resolved.getTextRange().getEndOffset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean pointsToTheSameElementAs(PsiAnchor other) {
|
||||
if (other instanceof StubIndexReference) return equals(other);
|
||||
return Comparing.equal(retrieve(), other.retrieve());
|
||||
}
|
||||
}
|
||||
|
||||
public abstract boolean pointsToTheSameElementAs(PsiAnchor other);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.intellij.psi.impl;
|
||||
|
||||
import com.intellij.AppTopics;
|
||||
import com.intellij.injected.editor.DocumentWindow;
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.openapi.application.Application;
|
||||
@@ -31,6 +32,7 @@ import com.intellij.openapi.editor.event.DocumentListener;
|
||||
import com.intellij.openapi.editor.ex.DocumentEx;
|
||||
import com.intellij.openapi.editor.impl.DocumentImpl;
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManagerAdapter;
|
||||
import com.intellij.openapi.progress.ProgressIndicator;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
@@ -48,6 +50,7 @@ import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
|
||||
import com.intellij.psi.text.BlockSupport;
|
||||
import com.intellij.util.SmartList;
|
||||
import com.intellij.util.concurrency.Semaphore;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.messages.MessageBus;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -74,8 +77,7 @@ public class PsiDocumentManagerImpl extends PsiDocumentManager implements Projec
|
||||
private volatile boolean myIsCommitInProgress;
|
||||
private final PsiToDocumentSynchronizer mySynchronizer;
|
||||
|
||||
private final List<Listener> myListeners = new ArrayList<Listener>();
|
||||
private Listener[] myCachedListeners = null; //guarded by mylisteners
|
||||
private final List<Listener> myListeners = ContainerUtil.createEmptyCOWList();
|
||||
private final SmartPointerManagerImpl mySmartPointerManager;
|
||||
|
||||
public PsiDocumentManagerImpl(Project project,
|
||||
@@ -91,6 +93,17 @@ public class PsiDocumentManagerImpl extends PsiDocumentManager implements Projec
|
||||
mySynchronizer = new PsiToDocumentSynchronizer(this, bus);
|
||||
myPsiManager.addPsiTreeChangeListener(mySynchronizer);
|
||||
editorFactory.getEventMulticaster().addDocumentListener(this, myProject);
|
||||
bus.connect().subscribe(AppTopics.FILE_DOCUMENT_SYNC, new FileDocumentManagerAdapter() {
|
||||
@Override
|
||||
public void fileContentLoaded(final VirtualFile virtualFile, Document document) {
|
||||
PsiFile psiFile = ApplicationManager.getApplication().runReadAction(new Computable<PsiFile>() {
|
||||
public PsiFile compute() {
|
||||
return getCachedPsiFile(virtualFile);
|
||||
}
|
||||
});
|
||||
fireDocumentCreated(document, psiFile);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void projectOpened() {
|
||||
@@ -183,8 +196,6 @@ public class PsiDocumentManagerImpl extends PsiDocumentManager implements Projec
|
||||
}
|
||||
}
|
||||
|
||||
fireDocumentCreated(document, file);
|
||||
|
||||
return document;
|
||||
}
|
||||
|
||||
@@ -366,27 +377,12 @@ public class PsiDocumentManagerImpl extends PsiDocumentManager implements Projec
|
||||
}
|
||||
}
|
||||
|
||||
private Listener[] getCachedListeners() {
|
||||
synchronized (myListeners) {
|
||||
if (myCachedListeners == null) {
|
||||
myCachedListeners = myListeners.toArray(new Listener[myListeners.size()]);
|
||||
}
|
||||
return myCachedListeners;
|
||||
}
|
||||
}
|
||||
|
||||
public void addListener(@NotNull Listener listener) {
|
||||
synchronized (myListeners) {
|
||||
myListeners.add(listener);
|
||||
myCachedListeners = null;
|
||||
}
|
||||
myListeners.add(listener);
|
||||
}
|
||||
|
||||
public void removeListener(@NotNull Listener listener) {
|
||||
synchronized (myListeners) {
|
||||
myListeners.remove(listener);
|
||||
myCachedListeners = null;
|
||||
}
|
||||
myListeners.remove(listener);
|
||||
}
|
||||
|
||||
public boolean isDocumentBlockedByPsi(@NotNull Document doc) {
|
||||
@@ -402,15 +398,13 @@ public class PsiDocumentManagerImpl extends PsiDocumentManager implements Projec
|
||||
}
|
||||
|
||||
private void fireDocumentCreated(Document document, PsiFile file) {
|
||||
Listener[] listeners = getCachedListeners();
|
||||
for (Listener listener : listeners) {
|
||||
for (Listener listener : myListeners) {
|
||||
listener.documentCreated(document, file);
|
||||
}
|
||||
}
|
||||
|
||||
private void fireFileCreated(Document document, PsiFile file) {
|
||||
Listener[] listeners = getCachedListeners();
|
||||
for (Listener listener : listeners) {
|
||||
for (Listener listener : myListeners) {
|
||||
listener.fileCreated(file, document);
|
||||
}
|
||||
}
|
||||
@@ -425,11 +419,7 @@ public class PsiDocumentManagerImpl extends PsiDocumentManager implements Projec
|
||||
if (textBlock.isEmpty()) return false;
|
||||
((DocumentImpl)document).normalizeRangeMarkers();
|
||||
myIsCommitInProgress = true;
|
||||
try{
|
||||
if (mySmartPointerManager != null) { // mock tests
|
||||
SmartPointerManagerImpl.synchronizePointers(file);
|
||||
}
|
||||
|
||||
try {
|
||||
myTreeElementBeingReparsedSoItWontBeCollected = ((PsiFileImpl)file).calcTreeElement();
|
||||
|
||||
if (textBlock.isEmpty()) return false ; // if tree was just loaded above textBlock will be cleared by contentsLoaded
|
||||
@@ -479,9 +469,6 @@ public class PsiDocumentManagerImpl extends PsiDocumentManager implements Projec
|
||||
}
|
||||
myBlockSupport.reparseRange(file, startOffset, endOffset, lengthShift, chars);
|
||||
|
||||
textBlock.unlock();
|
||||
textBlock.clear();
|
||||
|
||||
if (file.getTextLength() != document.getTextLength()) {
|
||||
if (ApplicationManagerEx.getApplicationEx().isInternal()) {
|
||||
boolean x = false;
|
||||
@@ -502,8 +489,15 @@ public class PsiDocumentManagerImpl extends PsiDocumentManager implements Projec
|
||||
}
|
||||
}
|
||||
finally {
|
||||
textBlock.unlock();
|
||||
textBlock.clear();
|
||||
|
||||
myTreeElementBeingReparsedSoItWontBeCollected = null;
|
||||
myIsCommitInProgress = false;
|
||||
|
||||
if (mySmartPointerManager != null) { // mock tests
|
||||
SmartPointerManagerImpl.synchronizePointers(file);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -530,12 +524,18 @@ public class PsiDocumentManagerImpl extends PsiDocumentManager implements Projec
|
||||
final FileViewProvider provider = getCachedViewProvider(document);
|
||||
if (provider == null) return;
|
||||
|
||||
if (provider.getVirtualFile().getFileType().isBinary()) return;
|
||||
VirtualFile virtualFile = provider.getVirtualFile();
|
||||
if (virtualFile.getFileType().isBinary()) return;
|
||||
|
||||
final List<PsiFile> files = provider.getAllFiles();
|
||||
boolean hasLockedBlocks = false;
|
||||
for (PsiFile file : files) {
|
||||
if (file == null) continue;
|
||||
|
||||
if (file.isPhysical() && mySmartPointerManager != null) { // mock tests
|
||||
SmartPointerManagerImpl.fastenBelts(file, event.getOffset());
|
||||
}
|
||||
|
||||
final TextBlock textBlock = getTextBlock(document, file);
|
||||
if (textBlock.isLocked()) {
|
||||
hasLockedBlocks = true;
|
||||
@@ -553,12 +553,6 @@ public class PsiDocumentManagerImpl extends PsiDocumentManager implements Projec
|
||||
myIsCommitInProgress = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (file.isPhysical()) {
|
||||
if (mySmartPointerManager != null) { // mock tests
|
||||
SmartPointerManagerImpl.fastenBelts(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasLockedBlocks) {
|
||||
@@ -570,19 +564,20 @@ public class PsiDocumentManagerImpl extends PsiDocumentManager implements Projec
|
||||
final Document document = event.getDocument();
|
||||
final FileViewProvider viewProvider = getCachedViewProvider(document);
|
||||
if (viewProvider == null) return;
|
||||
if (viewProvider.getVirtualFile().getFileType().isBinary()) return;
|
||||
VirtualFile virtualFile = viewProvider.getVirtualFile();
|
||||
if (virtualFile.getFileType().isBinary()) return;
|
||||
if (viewProvider.getManager() != myPsiManager) return;
|
||||
|
||||
final List<PsiFile> files = viewProvider.getAllFiles();
|
||||
boolean commitNecessary = false;
|
||||
for (PsiFile file : files) {
|
||||
if (file == null || file instanceof PsiFileImpl && ((PsiFileImpl)file).getTreeElement() == null) continue;
|
||||
if (mySmartPointerManager != null) { // mock tests
|
||||
SmartPointerManagerImpl.unfastenBelts(file, event.getOffset());
|
||||
}
|
||||
final TextBlock textBlock = getTextBlock(document, file);
|
||||
if (textBlock.isLocked()) continue;
|
||||
|
||||
if (mySmartPointerManager != null) { // mock tests
|
||||
SmartPointerManagerImpl.unfastenBelts(file);
|
||||
}
|
||||
|
||||
textBlock.documentChanged(event);
|
||||
assert file instanceof PsiFileImpl || "mock.file".equals(file.getName()) && ApplicationManager.getApplication().isUnitTestMode() : event + "; file="+file+"; allFiles="+files+"; viewProvider="+viewProvider;
|
||||
|
||||
@@ -26,7 +26,6 @@ import com.intellij.openapi.editor.ex.DocumentEx;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.smartPointers.SmartPointerManagerImpl;
|
||||
import com.intellij.util.messages.MessageBus;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -88,7 +87,6 @@ public class PsiToDocumentSynchronizer extends PsiTreeChangeAdapter {
|
||||
final boolean insideTransaction = myTransactionsMap.containsKey(document);
|
||||
if(!insideTransaction){
|
||||
document.setModificationStamp(psiFile.getModificationStamp());
|
||||
SmartPointerManagerImpl.synchronizePointers(psiFile);
|
||||
if (LOG.isDebugEnabled()) {
|
||||
PsiDocumentManagerImpl.checkConsistency(psiFile, document);
|
||||
}
|
||||
|
||||
@@ -866,7 +866,7 @@ public class FileManagerImpl implements FileManager {
|
||||
}
|
||||
|
||||
if (oldPsiFile != null && oldPsiFile.isPhysical()) {
|
||||
SmartPointerManagerImpl.fastenBelts(oldPsiFile);
|
||||
SmartPointerManagerImpl.fastenBelts(oldPsiFile, 0);
|
||||
}
|
||||
ApplicationManager.getApplication().runWriteAction(
|
||||
new ExternalChangeAction() {
|
||||
@@ -1085,7 +1085,9 @@ public class FileManagerImpl implements FileManager {
|
||||
removeFilesAndDirsRecursively(child);
|
||||
}
|
||||
}
|
||||
else myVFileToViewProviderMap.remove(vFile);
|
||||
else {
|
||||
myVFileToViewProviderMap.remove(vFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,9 +15,12 @@
|
||||
*/
|
||||
package com.intellij.psi.impl.smartPointers;
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.Segment;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -26,15 +29,12 @@ import org.jetbrains.annotations.NotNull;
|
||||
* User: cdr
|
||||
*/
|
||||
class FileElementInfo implements SmartPointerElementInfo {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.smartPointers.FileElementInfo");
|
||||
private PsiFile myFile;
|
||||
private final VirtualFile myVirtualFile;
|
||||
private final Project myProject;
|
||||
|
||||
public FileElementInfo(@NotNull PsiFile file) {
|
||||
LOG.assertTrue(file.isPhysical());
|
||||
myFile = file;
|
||||
|
||||
myProject = myFile.getProject();
|
||||
myVirtualFile = file.getVirtualFile();
|
||||
myProject = file.getProject();
|
||||
}
|
||||
|
||||
public Document getDocumentToSynchronize() {
|
||||
@@ -44,12 +44,42 @@ class FileElementInfo implements SmartPointerElementInfo {
|
||||
public void documentAndPsiInSync() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fastenBelt(int offset) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unfastenBelt(int offset) {
|
||||
}
|
||||
|
||||
public PsiElement restoreElement() {
|
||||
myFile = SelfElementInfo.restoreFile(myFile, myProject);
|
||||
return myFile;
|
||||
return SelfElementInfo.restoreFileFromVirtual(myVirtualFile, myProject);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int elementHashCode() {
|
||||
return myVirtualFile.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean pointsToTheSameElementAs(SmartPointerElementInfo other) {
|
||||
if (other instanceof FileElementInfo) {
|
||||
return myVirtualFile == ((FileElementInfo)other).myVirtualFile;
|
||||
}
|
||||
return Comparing.equal(restoreElement(), other.restoreElement());
|
||||
}
|
||||
|
||||
@Override
|
||||
public VirtualFile getVirtualFile() {
|
||||
return myVirtualFile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Segment getSegment() {
|
||||
return new TextRange(0, (int)myVirtualFile.getLength());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2000-2011 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.psi.impl.smartPointers;
|
||||
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.Segment;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.util.PsiUtilBase;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* User: cdr
|
||||
*/
|
||||
class HardElementInfo implements SmartPointerElementInfo {
|
||||
private final PsiElement myElement;
|
||||
|
||||
public HardElementInfo(@NotNull PsiElement element) {
|
||||
myElement = element;
|
||||
}
|
||||
|
||||
public Document getDocumentToSynchronize() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void documentAndPsiInSync() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fastenBelt(int offset) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unfastenBelt(int offset) {
|
||||
}
|
||||
|
||||
public PsiElement restoreElement() {
|
||||
return myElement;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int elementHashCode() {
|
||||
return myElement.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean pointsToTheSameElementAs(SmartPointerElementInfo other) {
|
||||
return Comparing.equal(myElement, other.restoreElement());
|
||||
}
|
||||
|
||||
@Override
|
||||
public VirtualFile getVirtualFile() {
|
||||
return PsiUtilBase.getVirtualFile(myElement);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Segment getSegment() {
|
||||
return myElement.getTextRange();
|
||||
}
|
||||
}
|
||||
+10
-6
@@ -18,7 +18,9 @@ package com.intellij.psi.impl.smartPointers;
|
||||
import com.intellij.injected.editor.DocumentWindow;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.PsiDocumentManager;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
@@ -27,14 +29,16 @@ import org.jetbrains.annotations.NotNull;
|
||||
class InjectedSelfElementInfo extends SelfElementInfo {
|
||||
private DocumentWindow myDocument;
|
||||
|
||||
InjectedSelfElementInfo(@NotNull PsiElement anchor, @NotNull Document document) {
|
||||
super(anchor, document);
|
||||
|
||||
assert myFile.getContext() != null;
|
||||
InjectedSelfElementInfo(@NotNull PsiElement anchor, PsiFile containingFile) {
|
||||
super(anchor, containingFile);
|
||||
assert containingFile.getContext() != null;
|
||||
}
|
||||
|
||||
protected TextRange getPersistentAnchorRange(final PsiElement anchor, final Document document) {
|
||||
final TextRange textRange = super.getPersistentAnchorRange(anchor, document);
|
||||
protected TextRange getPersistentAnchorRange(final PsiElement anchor) {
|
||||
final TextRange textRange = super.getPersistentAnchorRange(anchor);
|
||||
Document document = PsiDocumentManager.getInstance(anchor.getProject()).getDocument(anchor.getContainingFile());
|
||||
// must be non-text file
|
||||
|
||||
if (!(document instanceof DocumentWindow)) return textRange;
|
||||
myDocument = (DocumentWindow)document;
|
||||
return myDocument.injectedToHost(textRange);
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
package com.intellij.psi.impl.smartPointers;
|
||||
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.Segment;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.util.ReflectionCache;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -27,7 +30,7 @@ public class LazyPointerImpl<E extends PsiElement> implements SmartPointerEx<E>
|
||||
private final Class<? extends PsiElement> myElementClass;
|
||||
private final Project myProject;
|
||||
|
||||
public LazyPointerImpl(E element) {
|
||||
LazyPointerImpl(@NotNull E element) {
|
||||
myElementClass = element.getClass();
|
||||
if (element instanceof PsiCompiledElement) {
|
||||
myElement = element;
|
||||
@@ -38,21 +41,35 @@ public class LazyPointerImpl<E extends PsiElement> implements SmartPointerEx<E>
|
||||
myProject = element.getProject();
|
||||
}
|
||||
|
||||
public boolean pointsToTheSameElementAs(LazyPointerImpl pointer) {
|
||||
if (myElementClass != pointer.myElementClass) return false;
|
||||
if (myElement != null) return pointer.myElement == myElement;
|
||||
if (myAnchor != null && pointer.myAnchor != null) {
|
||||
return myAnchor.pointsToTheSameElementAs(pointer.myAnchor);
|
||||
}
|
||||
if (myPointer != null && pointer.myPointer != null) {
|
||||
return SmartPointerManager.getInstance(myProject).pointToTheSameElement(myPointer, pointer.myPointer);
|
||||
}
|
||||
return Comparing.equal(getElement(), pointer.getElement());
|
||||
}
|
||||
|
||||
private static SmartPsiElementPointer setupPointer(PsiElement element) {
|
||||
return SmartPointerManager.getInstance(element.getProject()).createSmartPsiElementPointer(element);
|
||||
}
|
||||
|
||||
public void fastenBelt() {
|
||||
public void fastenBelt(int offset) {
|
||||
if (myAnchor != null) {
|
||||
final PsiElement element = myAnchor.retrieve();
|
||||
if (element != null) {
|
||||
myPointer = setupPointer(element);
|
||||
((SmartPointerEx)myPointer).fastenBelt();
|
||||
//((SmartPointerEx)myPointer).fastenBelt(offset);
|
||||
myAnchor = null;
|
||||
}
|
||||
else myAnchor = null;
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void unfastenBelt(int offset) {
|
||||
}
|
||||
|
||||
public void documentAndPsiInSync() {
|
||||
}
|
||||
@@ -60,20 +77,12 @@ public class LazyPointerImpl<E extends PsiElement> implements SmartPointerEx<E>
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof LazyPointerImpl)) return false;
|
||||
|
||||
final LazyPointerImpl that = (LazyPointerImpl)o;
|
||||
|
||||
if (myAnchor != null ? !myAnchor.equals(that.myAnchor) : that.myAnchor != null) return false;
|
||||
if (myElement != null ? !myElement.equals(that.myElement) : that.myElement != null) return false;
|
||||
if (myElementClass != null ? !myElementClass.equals(that.myElementClass) : that.myElementClass != null) return false;
|
||||
if (myPointer != null ? !myPointer.equals(that.myPointer) : that.myPointer != null) return false;
|
||||
|
||||
return true;
|
||||
return pointsToTheSameElementAs(that);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
int result;
|
||||
result = (myElement != null ? myElement.hashCode() : 0);
|
||||
int result = myElement != null ? myElement.hashCode() : 0;
|
||||
result = 31 * result + (myAnchor != null ? myAnchor.hashCode() : 0);
|
||||
result = 31 * result + (myPointer != null ? myPointer.hashCode() : 0);
|
||||
result = 31 * result + (myElementClass != null ? myElementClass.hashCode() : 0);
|
||||
@@ -85,11 +94,20 @@ public class LazyPointerImpl<E extends PsiElement> implements SmartPointerEx<E>
|
||||
return myProject;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VirtualFile getVirtualFile() {
|
||||
PsiFile psiFile = getContainingFile();
|
||||
return psiFile == null ? null : psiFile.getVirtualFile();
|
||||
}
|
||||
|
||||
public E getElement() {
|
||||
if (myElement != null) return myElement.isValid() ? myElement : null;
|
||||
if (myPointer != null) return (E) myPointer.getElement();
|
||||
if (myAnchor != null) {
|
||||
final PsiElement psiElement = myAnchor.retrieve();
|
||||
E element = myElement;
|
||||
if (element != null) return element.isValid() ? element : null;
|
||||
SmartPsiElementPointer pointer = myPointer;
|
||||
if (pointer != null) return (E) pointer.getElement();
|
||||
PsiAnchor anchor = myAnchor;
|
||||
if (anchor != null) {
|
||||
final PsiElement psiElement = anchor.retrieve();
|
||||
if (psiElement != null) {
|
||||
return ReflectionCache.isAssignable(myElementClass, psiElement.getClass()) ? (E) psiElement : null;
|
||||
}
|
||||
@@ -99,12 +117,19 @@ public class LazyPointerImpl<E extends PsiElement> implements SmartPointerEx<E>
|
||||
}
|
||||
|
||||
public PsiFile getContainingFile() {
|
||||
if (myElement != null) {
|
||||
return myElement.getContainingFile();
|
||||
E element = myElement;
|
||||
if (element != null) {
|
||||
return element.getContainingFile();
|
||||
}
|
||||
|
||||
if (myAnchor != null) {
|
||||
return myAnchor.getFile();
|
||||
PsiAnchor anchor = myAnchor;
|
||||
if (anchor != null) {
|
||||
return anchor.getFile();
|
||||
}
|
||||
|
||||
SmartPsiElementPointer pointer = myPointer;
|
||||
if (pointer != null) {
|
||||
return pointer.getContainingFile();
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -114,4 +139,21 @@ public class LazyPointerImpl<E extends PsiElement> implements SmartPointerEx<E>
|
||||
public void dispose() {
|
||||
if (myPointer != null) ((SmartPointerEx)myPointer).dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Segment getSegment() {
|
||||
E element = myElement;
|
||||
if (element != null && element.isValid()) return element.getTextRange();
|
||||
SmartPsiElementPointer pointer = myPointer;
|
||||
if (pointer != null) return pointer.getSegment();
|
||||
PsiAnchor anchor = myAnchor;
|
||||
if (anchor != null) {
|
||||
final PsiElement psiElement = anchor.retrieve();
|
||||
if (psiElement != null) {
|
||||
return psiElement.getTextRange();
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,10 @@ package com.intellij.psi.impl.smartPointers;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.RangeMarker;
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.Segment;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiDocumentManager;
|
||||
@@ -28,70 +31,144 @@ import com.intellij.psi.PsiManager;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.ref.Reference;
|
||||
import java.lang.ref.SoftReference;
|
||||
|
||||
/**
|
||||
* User: cdr
|
||||
*/
|
||||
class SelfElementInfo implements SmartPointerElementInfo {
|
||||
public class SelfElementInfo implements SmartPointerElementInfo {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.smartPointers.SelfElementInfo");
|
||||
protected PsiFile myFile;
|
||||
private final RangeMarker myMarker;
|
||||
protected final VirtualFile myVirtualFile;
|
||||
private Reference<RangeMarker> myMarkerRef; // create marker only in case of live document
|
||||
private int mySyncStartOffset;
|
||||
private int mySyncEndOffset;
|
||||
private boolean mySyncMarkerIsValid;
|
||||
protected boolean mySyncMarkerIsValid;
|
||||
private Class myType;
|
||||
private final Project myProject;
|
||||
protected final Project myProject;
|
||||
private RangeMarker myRangeMarker; //maintain hard reference during modification
|
||||
|
||||
public SelfElementInfo(@NotNull PsiElement anchor, @NotNull Document document) {
|
||||
public SelfElementInfo(@NotNull PsiElement anchor, PsiFile containingFile) {
|
||||
LOG.assertTrue(anchor.isPhysical());
|
||||
myFile = anchor.getContainingFile();
|
||||
LOG.assertTrue(anchor.isValid());
|
||||
PsiFile file = anchor.getContainingFile();
|
||||
myVirtualFile = file.getVirtualFile();
|
||||
TextRange range = anchor.getTextRange();
|
||||
LOG.assertTrue(range != null, anchor);
|
||||
range = getPersistentAnchorRange(anchor);
|
||||
|
||||
myProject = myFile.getProject();
|
||||
final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(myProject);
|
||||
|
||||
if (documentManager.isUncommited(document)) {
|
||||
mySyncMarkerIsValid = false;
|
||||
myMarker = document.createRangeMarker(0, 0, false);
|
||||
}
|
||||
else {
|
||||
myMarker = document.createRangeMarker(range.getStartOffset(), range.getEndOffset(), true);
|
||||
range = getPersistentAnchorRange(anchor, document);
|
||||
|
||||
mySyncStartOffset = range.getStartOffset();
|
||||
mySyncEndOffset = range.getEndOffset();
|
||||
mySyncMarkerIsValid = true;
|
||||
myType = anchor.getClass();
|
||||
}
|
||||
}
|
||||
|
||||
protected TextRange getPersistentAnchorRange(final PsiElement anchor, final Document document) {
|
||||
return anchor.getTextRange();
|
||||
}
|
||||
|
||||
public Document getDocumentToSynchronize() {
|
||||
return myMarker.getDocument();
|
||||
}
|
||||
|
||||
public void documentAndPsiInSync() {
|
||||
if (!myMarker.isValid()) {
|
||||
myProject = file.getProject();
|
||||
PsiDocumentManager documentManager = PsiDocumentManager.getInstance(myProject);
|
||||
Document document = documentManager.getDocument(file);
|
||||
if (document == null || documentManager.isUncommited(document)) {
|
||||
mySyncMarkerIsValid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
mySyncStartOffset = myMarker.getStartOffset();
|
||||
mySyncEndOffset = myMarker.getEndOffset();
|
||||
//if (file.getTextLength() != document.getTextLength()) {
|
||||
// final String docText = document.getText();
|
||||
// file.accept(new PsiRecursiveElementWalkingVisitor() {
|
||||
// @Override
|
||||
// public void visitElement(PsiElement element) {
|
||||
// super.visitElement(element);
|
||||
// TextRange elementRange = element.getTextRange();
|
||||
// final String rangeText = docText.length() < elementRange.getEndOffset() ? "(IOOBE: "+elementRange +" is out of (0,"+docText.length()+"))" : elementRange.substring(docText);
|
||||
// final String elemText = element.getText();
|
||||
// if (!rangeText.equals(elemText)) {
|
||||
// throw new AssertionError("PSI text doesn't equal to the document's one: element: " + element + "\ndocText=" + rangeText + "\npsiText: " + elemText);
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
// LOG.error("File=" + file);
|
||||
//}
|
||||
|
||||
if (documentManager.isUncommited(document)) {
|
||||
mySyncMarkerIsValid = false;
|
||||
}
|
||||
else {
|
||||
mySyncMarkerIsValid = true;
|
||||
myType = anchor.getClass();
|
||||
}
|
||||
mySyncStartOffset = range.getStartOffset();
|
||||
mySyncEndOffset = range.getEndOffset();
|
||||
}
|
||||
|
||||
protected TextRange getPersistentAnchorRange(final PsiElement anchor) {
|
||||
return anchor.getTextRange();
|
||||
}
|
||||
|
||||
public Document getDocumentToSynchronize() {
|
||||
RangeMarker marker = getMarker();
|
||||
if (marker != null) {
|
||||
return marker.getDocument();
|
||||
}
|
||||
return FileDocumentManager.getInstance().getCachedDocument(myVirtualFile);
|
||||
}
|
||||
|
||||
// before change
|
||||
@Override
|
||||
public void fastenBelt(int offset) {
|
||||
if (!mySyncMarkerIsValid) return;
|
||||
RangeMarker marker = getMarker();
|
||||
int actualEndOffset = marker == null || !marker.isValid() ? getSyncEndOffset() : marker.getEndOffset();
|
||||
if (offset > actualEndOffset) {
|
||||
return; // no need to update, the change is far after
|
||||
}
|
||||
if (marker == null) {
|
||||
Document document = FileDocumentManager.getInstance().getDocument(myVirtualFile);
|
||||
if (document == null) {
|
||||
mySyncMarkerIsValid = false;
|
||||
return;
|
||||
}
|
||||
//PsiToDocumentSynchronizer synchronizer = ((PsiDocumentManagerImpl)PsiDocumentManager.getInstance(myProject)).getSynchronizer();
|
||||
//boolean inSynchronization = synchronizer.isInSynchronization(document);
|
||||
|
||||
//if (!inSynchronization) { // otherwise doc offsets are incorrect
|
||||
int start = Math.min(getSyncStartOffset(), document.getTextLength());
|
||||
int end = Math.min(Math.max(getSyncEndOffset(), start), document.getTextLength());
|
||||
marker = document.createRangeMarker(start, end, true);
|
||||
setMarker(marker);
|
||||
myRangeMarker = marker; //make sure marker wont be gced
|
||||
//}
|
||||
}
|
||||
else if (!marker.isValid()) {
|
||||
mySyncMarkerIsValid = false;
|
||||
marker.dispose();
|
||||
setMarker(null);
|
||||
}
|
||||
}
|
||||
|
||||
// after change
|
||||
@Override
|
||||
public void unfastenBelt(int offset) {
|
||||
if (!mySyncMarkerIsValid) return;
|
||||
RangeMarker marker = getMarker();
|
||||
if (marker != null) {
|
||||
if (marker.isValid()) {
|
||||
mySyncStartOffset = marker.getStartOffset();
|
||||
mySyncEndOffset = marker.getEndOffset();
|
||||
assert mySyncEndOffset <= marker.getDocument().getTextLength();
|
||||
}
|
||||
else {
|
||||
mySyncMarkerIsValid = false;
|
||||
}
|
||||
}
|
||||
myRangeMarker = null;
|
||||
}
|
||||
|
||||
// commit
|
||||
public void documentAndPsiInSync() {
|
||||
}
|
||||
|
||||
public PsiElement restoreElement() {
|
||||
if (!mySyncMarkerIsValid) return null;
|
||||
myFile = restoreFile(myFile, myProject);
|
||||
if (myFile == null) return null;
|
||||
PsiFile file = restoreFileFromVirtual(myVirtualFile, myProject);
|
||||
if (file == null || !file.isValid()) return null;
|
||||
|
||||
final int syncStartOffset = getSyncStartOffset();
|
||||
final int syncEndOffset = getSyncEndOffset();
|
||||
|
||||
PsiElement anchor = myFile.getViewProvider().findElementAt(syncStartOffset, myFile.getLanguage());
|
||||
PsiElement anchor = file.getViewProvider().findElementAt(syncStartOffset, file.getLanguage());
|
||||
if (anchor == null) return null;
|
||||
|
||||
TextRange range = anchor.getTextRange();
|
||||
@@ -115,23 +192,46 @@ class SelfElementInfo implements SmartPointerElementInfo {
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
if (myMarker != null) {
|
||||
myMarker.dispose();
|
||||
RangeMarker marker = getMarker();
|
||||
if (marker != null) {
|
||||
marker.dispose();
|
||||
setMarker(null);
|
||||
}
|
||||
}
|
||||
|
||||
private RangeMarker getMarker() {
|
||||
Reference<RangeMarker> ref = myMarkerRef;
|
||||
return ref == null ? null : ref.get();
|
||||
}
|
||||
|
||||
private void setMarker(RangeMarker marker) {
|
||||
myMarkerRef = marker == null ? null : new SoftReference<RangeMarker>(marker);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static PsiFile restoreFile(PsiFile file,@NotNull Project project) {
|
||||
if (file == null) return null;
|
||||
if (file.isValid()) return file;
|
||||
VirtualFile virtualFile = file.getVirtualFile();
|
||||
return restoreFileFromVirtual(virtualFile, project);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static PsiFile restoreFileFromVirtual(VirtualFile virtualFile, @NotNull Project project) {
|
||||
if (virtualFile == null) return null;
|
||||
VirtualFile vParent = virtualFile.getParent();
|
||||
if (vParent == null || !vParent.isDirectory()) return null;
|
||||
String name = file.getName();
|
||||
VirtualFile child = vParent.findChild(name);
|
||||
|
||||
VirtualFile child;
|
||||
if (virtualFile.isValid()) {
|
||||
child = virtualFile;
|
||||
}
|
||||
else {
|
||||
VirtualFile vParent = virtualFile.getParent();
|
||||
if (vParent == null || !vParent.isDirectory()) return null;
|
||||
String name = virtualFile.getName();
|
||||
child = vParent.findChild(name);
|
||||
}
|
||||
if (child == null || !child.isValid()) return null;
|
||||
file = PsiManager.getInstance(project).findFile(child);
|
||||
PsiFile file = PsiManager.getInstance(project).findFile(child);
|
||||
if (file == null || !file.isValid()) return null;
|
||||
return file;
|
||||
}
|
||||
@@ -143,4 +243,32 @@ class SelfElementInfo implements SmartPointerElementInfo {
|
||||
protected int getSyncStartOffset() {
|
||||
return mySyncStartOffset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int elementHashCode() {
|
||||
return myVirtualFile.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean pointsToTheSameElementAs(SmartPointerElementInfo other) {
|
||||
if (other instanceof SelfElementInfo) {
|
||||
return myVirtualFile == ((SelfElementInfo)other).myVirtualFile
|
||||
&& myType == ((SelfElementInfo)other).myType
|
||||
&& mySyncStartOffset == ((SelfElementInfo)other).mySyncStartOffset
|
||||
&& mySyncEndOffset == ((SelfElementInfo)other).mySyncEndOffset
|
||||
;
|
||||
}
|
||||
return Comparing.equal(restoreElement(), other.restoreElement());
|
||||
}
|
||||
|
||||
@Override
|
||||
public VirtualFile getVirtualFile() {
|
||||
return myVirtualFile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Segment getSegment() {
|
||||
if (!mySyncMarkerIsValid) return null;
|
||||
return new TextRange(getSyncStartOffset(), getSyncEndOffset());
|
||||
}
|
||||
}
|
||||
|
||||
+10
@@ -17,6 +17,8 @@
|
||||
package com.intellij.psi.impl.smartPointers;
|
||||
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.util.Segment;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -25,9 +27,17 @@ interface SmartPointerElementInfo {
|
||||
Document getDocumentToSynchronize();
|
||||
|
||||
void documentAndPsiInSync();
|
||||
void fastenBelt(int offset);
|
||||
|
||||
void unfastenBelt(int offset);
|
||||
|
||||
@Nullable
|
||||
PsiElement restoreElement();
|
||||
|
||||
void dispose();
|
||||
int elementHashCode(); // must be immutable
|
||||
boolean pointsToTheSameElementAs(SmartPointerElementInfo other);
|
||||
|
||||
VirtualFile getVirtualFile();
|
||||
Segment getSegment();
|
||||
}
|
||||
|
||||
+2
-1
@@ -18,6 +18,7 @@ package com.intellij.psi.impl.smartPointers;
|
||||
|
||||
import com.intellij.openapi.extensions.ExtensionPointName;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
@@ -27,5 +28,5 @@ public interface SmartPointerElementInfoFactory {
|
||||
ExtensionPointName<SmartPointerElementInfoFactory> EP_NAME = ExtensionPointName.create("com.intellij.smartPointerElementInfoFactory");
|
||||
|
||||
@Nullable
|
||||
SmartPointerElementInfo createElementInfo(PsiElement element);
|
||||
SmartPointerElementInfo createElementInfo(@NotNull PsiElement element);
|
||||
}
|
||||
|
||||
@@ -15,11 +15,14 @@
|
||||
*/
|
||||
package com.intellij.psi.impl.smartPointers;
|
||||
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.SmartPsiElementPointer;
|
||||
|
||||
public interface SmartPointerEx<E extends PsiElement> extends SmartPsiElementPointer<E> {
|
||||
void fastenBelt();
|
||||
public interface SmartPointerEx<E extends PsiElement> extends SmartPsiElementPointer<E>, Disposable {
|
||||
void fastenBelt(int offset);
|
||||
void documentAndPsiInSync();
|
||||
void dispose();
|
||||
|
||||
void unfastenBelt(int offset);
|
||||
}
|
||||
|
||||
+82
-51
@@ -20,21 +20,21 @@ import com.intellij.lang.Language;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.PsiDocumentManagerImpl;
|
||||
import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class SmartPointerManagerImpl extends SmartPointerManager {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.smartPointers.SmartPointerManagerImpl");
|
||||
|
||||
private static final Key<ArrayList<WeakReference<SmartPointerEx>>> SMART_POINTERS_IN_PSI_FILE_KEY = Key.create(
|
||||
"SMART_POINTERS_IN_DOCUMENT_KEY");
|
||||
private static final Key<List<WeakReference<SmartPointerEx>>> SMART_POINTERS_IN_PSI_FILE_KEY = Key.create("SMART_POINTERS_IN_DOCUMENT_KEY");
|
||||
private static final Key<Boolean> BELTS_ARE_FASTEN_KEY = Key.create("BELTS_ARE_FASTEN_KEY");
|
||||
|
||||
private final Project myProject;
|
||||
@@ -43,13 +43,13 @@ public class SmartPointerManagerImpl extends SmartPointerManager {
|
||||
myProject = project;
|
||||
}
|
||||
|
||||
public static void fastenBelts(PsiFile file) {
|
||||
public static void fastenBelts(PsiFile file, int offset) {
|
||||
synchronized (file) {
|
||||
if (areBeltsFastened(file)) return;
|
||||
|
||||
file.putUserData(BELTS_ARE_FASTEN_KEY, Boolean.TRUE);
|
||||
|
||||
ArrayList<WeakReference<SmartPointerEx>> pointers = file.getUserData(SMART_POINTERS_IN_PSI_FILE_KEY);
|
||||
List<WeakReference<SmartPointerEx>> pointers = getPointers(file);
|
||||
if (pointers == null) return;
|
||||
|
||||
int index = 0;
|
||||
@@ -57,7 +57,7 @@ public class SmartPointerManagerImpl extends SmartPointerManager {
|
||||
WeakReference<SmartPointerEx> reference = pointers.get(i);
|
||||
SmartPointerEx pointer = reference.get();
|
||||
if (pointer != null) {
|
||||
pointer.fastenBelt();
|
||||
pointer.fastenBelt(offset);
|
||||
pointers.set(index++, reference);
|
||||
}
|
||||
}
|
||||
@@ -66,22 +66,46 @@ public class SmartPointerManagerImpl extends SmartPointerManager {
|
||||
for(Document document:InjectedLanguageUtil.getCachedInjectedDocuments(file)) {
|
||||
PsiFile injectedfile = psiDocumentManager.getPsiFile(document);
|
||||
if (injectedfile == null) continue;
|
||||
fastenBelts(injectedfile);
|
||||
fastenBelts(injectedfile, 0);
|
||||
}
|
||||
|
||||
int size = pointers.size();
|
||||
for (int i = size - 1; i >= index; i--) {
|
||||
pointers.remove(i);
|
||||
}
|
||||
int i = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static void unfastenBelts(PsiFile file) {
|
||||
public static void unfastenBelts(PsiFile file, int offset) {
|
||||
final Set<Language> languages = file.getViewProvider().getLanguages();
|
||||
for (Language language : languages) {
|
||||
final PsiFile f = file.getViewProvider().getPsi(language);
|
||||
synchronized (f) {
|
||||
f.putUserData(BELTS_ARE_FASTEN_KEY, null);
|
||||
f.putUserData(BELTS_ARE_FASTEN_KEY, null);
|
||||
|
||||
List<WeakReference<SmartPointerEx>> pointers = getPointers(file);
|
||||
if (pointers == null) return;
|
||||
|
||||
int index = 0;
|
||||
for (int i = 0; i < pointers.size(); i++) {
|
||||
WeakReference<SmartPointerEx> reference = pointers.get(i);
|
||||
SmartPointerEx pointer = reference.get();
|
||||
if (pointer != null) {
|
||||
pointer.unfastenBelt(offset);
|
||||
pointers.set(index++, reference);
|
||||
}
|
||||
}
|
||||
|
||||
final PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance(file.getProject());
|
||||
for(Document document:InjectedLanguageUtil.getCachedInjectedDocuments(file)) {
|
||||
PsiFile injectedfile = psiDocumentManager.getPsiFile(document);
|
||||
if (injectedfile == null) continue;
|
||||
unfastenBelts(injectedfile, 0);
|
||||
}
|
||||
|
||||
int size = pointers.size();
|
||||
for (int i = size - 1; i >= index; i--) {
|
||||
pointers.remove(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -97,7 +121,7 @@ public class SmartPointerManagerImpl extends SmartPointerManager {
|
||||
}
|
||||
|
||||
private static void _synchronizePointers(final PsiFile file) {
|
||||
ArrayList<WeakReference<SmartPointerEx>> pointers = file.getUserData(SMART_POINTERS_IN_PSI_FILE_KEY);
|
||||
List<WeakReference<SmartPointerEx>> pointers = getPointers(file);
|
||||
if (pointers == null) return;
|
||||
|
||||
int index = 0;
|
||||
@@ -124,64 +148,71 @@ public class SmartPointerManagerImpl extends SmartPointerManager {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public <E extends PsiElement> SmartPsiElementPointer<E> createSmartPsiElementPointer(E element) {
|
||||
public <E extends PsiElement> SmartPsiElementPointer<E> createSmartPsiElementPointer(@NotNull E element) {
|
||||
if (!element.isValid()) {
|
||||
LOG.error("Invalid element:" + element);
|
||||
}
|
||||
|
||||
PsiFile file = element.getContainingFile();
|
||||
|
||||
if (isSafeReparseable(file)) {
|
||||
return new IdentitySmartPointer<E>(element);
|
||||
}
|
||||
|
||||
SmartPointerEx<E> pointer = new SmartPsiElementPointerImpl<E>(myProject, element);
|
||||
initPointer(element, pointer);
|
||||
PsiFile containingFile = element.getContainingFile();
|
||||
SmartPointerEx<E> pointer = new SmartPsiElementPointerImpl<E>(myProject, element, containingFile);
|
||||
initPointer(element, pointer, containingFile);
|
||||
|
||||
return pointer;
|
||||
}
|
||||
|
||||
private static boolean isSafeReparseable(final PsiFile file) {
|
||||
return false;
|
||||
//return file != null && !(file instanceof CodeFragmentElement); TODO: needs proper processing of foreign chameleons on reparse (like javascript in HTML).
|
||||
}
|
||||
private <E extends PsiElement> void initPointer(E element, SmartPointerEx<E> pointer, PsiFile containingFile) {
|
||||
if (containingFile == null) return;
|
||||
synchronized (containingFile) {
|
||||
Document document = PsiDocumentManager.getInstance(myProject).getCachedDocument(containingFile);
|
||||
//todo
|
||||
//if (document != null) {
|
||||
// //[ven] this is a really NASTY hack; when no smart pointer is kept on UsageInfo then remove this conditional
|
||||
// if (!(element instanceof PsiFile)) {
|
||||
// PsiDocumentManagerImpl documentManager = (PsiDocumentManagerImpl)PsiDocumentManager.getInstance(myProject);
|
||||
// LOG.assertTrue(!documentManager.isUncommited(document) || documentManager.isCommittingDocument(document), "Document for : " +
|
||||
// containingFile + " is not committed");
|
||||
// }
|
||||
//}
|
||||
|
||||
private <E extends PsiElement> void initPointer(E element, SmartPointerEx<E> pointer) {
|
||||
PsiFile file = element.getContainingFile();
|
||||
if (file != null) {
|
||||
synchronized (file) {
|
||||
Document document = PsiDocumentManager.getInstance(myProject).getCachedDocument(file);
|
||||
if (document != null) {
|
||||
//[ven] this is a really NASTY hack; when no smart pointer is kept on UsageInfo then remove this conditional
|
||||
if (!(element instanceof PsiFile)) {
|
||||
PsiDocumentManagerImpl documentManager = (PsiDocumentManagerImpl)PsiDocumentManager.getInstance(myProject);
|
||||
LOG.assertTrue(!documentManager.isUncommited(document) || documentManager.isCommittingDocument(document), "Document for : " + file + " is not committed");
|
||||
}
|
||||
}
|
||||
List<WeakReference<SmartPointerEx>> pointers = getPointers(containingFile);
|
||||
if (pointers == null) {
|
||||
pointers = new ArrayList<WeakReference<SmartPointerEx>>();
|
||||
containingFile.putUserData(SMART_POINTERS_IN_PSI_FILE_KEY, pointers);
|
||||
}
|
||||
pointers.add(new WeakReference<SmartPointerEx>(pointer));
|
||||
|
||||
ArrayList<WeakReference<SmartPointerEx>> pointers = file.getUserData(SMART_POINTERS_IN_PSI_FILE_KEY);
|
||||
if (pointers == null) {
|
||||
pointers = new ArrayList<WeakReference<SmartPointerEx>>();
|
||||
file.putUserData(SMART_POINTERS_IN_PSI_FILE_KEY, pointers);
|
||||
}
|
||||
pointers.add(new WeakReference<SmartPointerEx>(pointer));
|
||||
|
||||
if (areBeltsFastened(file)) {
|
||||
pointer.fastenBelt();
|
||||
}
|
||||
if (areBeltsFastened(containingFile)) {
|
||||
pointer.fastenBelt(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static List<WeakReference<SmartPointerEx>> getPointers(@NotNull PsiFile containingFile) {
|
||||
return containingFile.getUserData(SMART_POINTERS_IN_PSI_FILE_KEY);
|
||||
}
|
||||
|
||||
private static boolean areBeltsFastened(final PsiFile file) {
|
||||
return file.getUserData(BELTS_ARE_FASTEN_KEY) == Boolean.TRUE;
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
public <E extends PsiElement> SmartPsiElementPointer<E> createLazyPointer(E element) {
|
||||
LazyPointerImpl<E> pointer = new LazyPointerImpl<E>(element);
|
||||
initPointer(element, pointer);
|
||||
return pointer;
|
||||
public <E extends PsiElement> SmartPsiElementPointer<E> createLazyPointer(@NotNull E element) {
|
||||
return createSmartPsiElementPointer(element);
|
||||
//LazyPointerImpl<E> pointer = new LazyPointerImpl<E>(element);
|
||||
//initPointer(element, pointer, element.getContainingFile());
|
||||
//return pointer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean pointToTheSameElement(@NotNull SmartPsiElementPointer pointer1, @NotNull SmartPsiElementPointer pointer2) {
|
||||
if (pointer1 instanceof SmartPsiElementPointerImpl && pointer2 instanceof SmartPsiElementPointerImpl) {
|
||||
SmartPointerElementInfo elementInfo1 = ((SmartPsiElementPointerImpl)pointer1).getElementInfo();
|
||||
SmartPointerElementInfo elementInfo2 = ((SmartPsiElementPointerImpl)pointer2).getElementInfo();
|
||||
return elementInfo1.pointsToTheSameElementAs(elementInfo2);
|
||||
}
|
||||
if (pointer1 instanceof LazyPointerImpl && pointer2 instanceof LazyPointerImpl) {
|
||||
return ((LazyPointerImpl)pointer1).pointsToTheSameElementAs((LazyPointerImpl)pointer2);
|
||||
}
|
||||
return Comparing.equal(pointer1.getElement(), pointer2.getElement());
|
||||
}
|
||||
}
|
||||
|
||||
+77
-66
@@ -21,50 +21,54 @@ import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.extensions.Extensions;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.Segment;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.PsiDocumentManagerImpl;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.ref.Reference;
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
class SmartPsiElementPointerImpl<E extends PsiElement> implements SmartPointerEx<E> {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.smartPointers.SmartPsiElementPointerImpl");
|
||||
|
||||
private E myElement;
|
||||
private SmartPointerElementInfo myElementInfo;
|
||||
private Reference<E> myElement;
|
||||
private final SmartPointerElementInfo myElementInfo;
|
||||
private final Project myProject;
|
||||
private final Class<? extends PsiElement> myElementClass;
|
||||
|
||||
public SmartPsiElementPointerImpl(Project project, E element) {
|
||||
public SmartPsiElementPointerImpl(@NotNull Project project, @NotNull E element, PsiFile containingFile) {
|
||||
myProject = project;
|
||||
ApplicationManager.getApplication().assertReadAccessAllowed();
|
||||
myElement = element;
|
||||
myElementInfo = null;
|
||||
myElement = new WeakReference<E>(element);
|
||||
myElementInfo = createElementInfo(element, containingFile);
|
||||
myElementClass = element.getClass();
|
||||
|
||||
// Assert document committed.
|
||||
PsiFile file = element.getContainingFile();
|
||||
if (file != null) {
|
||||
final PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance(project);
|
||||
if (psiDocumentManager instanceof PsiDocumentManagerImpl) {
|
||||
Document doc = psiDocumentManager.getCachedDocument(file);
|
||||
if (doc != null) {
|
||||
//[ven] this is a really NASTY hack; when no smart pointer is kept on UsageInfo then remove this conditional
|
||||
if (!(element instanceof PsiFile)) {
|
||||
LOG.assertTrue(!psiDocumentManager.isUncommited(doc) || ((PsiDocumentManagerImpl)psiDocumentManager).isCommittingDocument(doc));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//todo
|
||||
//if (containingFile != null) {
|
||||
// final PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance(project);
|
||||
// if (psiDocumentManager instanceof PsiDocumentManagerImpl) {
|
||||
// Document doc = psiDocumentManager.getCachedDocument(containingFile);
|
||||
// if (doc != null) {
|
||||
// //[ven] this is a really NASTY hack; when no smart pointer is kept on UsageInfo then remove this conditional
|
||||
// if (!(element instanceof PsiFile)) {
|
||||
// LOG.assertTrue(!psiDocumentManager.isUncommited(doc) || ((PsiDocumentManagerImpl)psiDocumentManager).isCommittingDocument(doc));
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof SmartPsiElementPointer)) return false;
|
||||
SmartPsiElementPointer pointer = (SmartPsiElementPointer)obj;
|
||||
return Comparing.equal(pointer.getElement(), getElement());
|
||||
return SmartPointerManager.getInstance(myProject).pointToTheSameElement(this, pointer);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
PsiElement element = getElement();
|
||||
return element != null ? element.hashCode() : 0;
|
||||
return myElementInfo.elementHashCode();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -74,35 +78,38 @@ class SmartPsiElementPointerImpl<E extends PsiElement> implements SmartPointerEx
|
||||
|
||||
@Nullable
|
||||
public E getElement() {
|
||||
if (myElement != null && !myElement.isValid()) {
|
||||
if (myElementInfo == null) {
|
||||
myElement = null;
|
||||
PsiElement element = getCachedElement();
|
||||
if (element != null && !element.isValid()) {
|
||||
element = null;
|
||||
}
|
||||
if (element == null && myElementInfo != null) {
|
||||
element = myElementInfo.restoreElement();
|
||||
if (element != null && (!element.getClass().equals(myElementClass) || !element.isValid())) {
|
||||
element = null;
|
||||
}
|
||||
else {
|
||||
PsiElement restored = myElementInfo.restoreElement();
|
||||
if (restored != null && (!areElementKindEqual(restored, myElement) || !restored.isValid())) {
|
||||
restored = null;
|
||||
}
|
||||
|
||||
myElement = (E) restored;
|
||||
}
|
||||
myElement = element == null ? null : new WeakReference(element);
|
||||
}
|
||||
|
||||
//if (myElementInfo != null && myElement != null) {
|
||||
// Document document = myElementInfo.getDocumentToSynchronize();
|
||||
// if (document != null && PsiDocumentManager.getInstance(myProject).isUncommited(document)) return myElement; // keep element info if document is modified
|
||||
//}
|
||||
// myElementInfo = null;
|
||||
return (E)element;
|
||||
}
|
||||
|
||||
return myElement;
|
||||
private E getCachedElement() {
|
||||
return myElement == null ? null : myElement.get();
|
||||
}
|
||||
|
||||
public PsiFile getContainingFile() {
|
||||
if (myElement != null) {
|
||||
return myElement.getContainingFile();
|
||||
E element = getCachedElement();
|
||||
if (element != null) {
|
||||
return element.getContainingFile();
|
||||
}
|
||||
VirtualFile virtualFile = myElementInfo.getVirtualFile();
|
||||
if (virtualFile != null && virtualFile.isValid()) {
|
||||
PsiFile psiFile = PsiManager.getInstance(getProject()).findFile(virtualFile);
|
||||
if (psiFile != null) return psiFile;
|
||||
}
|
||||
|
||||
final Document doc = myElementInfo == null ? null : myElementInfo.getDocumentToSynchronize();
|
||||
final Document doc = myElementInfo.getDocumentToSynchronize();
|
||||
if (doc == null) {
|
||||
final E resolved = getElement();
|
||||
return resolved != null ? resolved.getContainingFile() : null;
|
||||
@@ -110,37 +117,37 @@ class SmartPsiElementPointerImpl<E extends PsiElement> implements SmartPointerEx
|
||||
return PsiDocumentManager.getInstance(myProject).getPsiFile(doc);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private SmartPointerElementInfo createElementInfo() {
|
||||
if (myElement instanceof PsiCompiledElement) return null;
|
||||
public VirtualFile getVirtualFile() {
|
||||
return myElementInfo.getVirtualFile();
|
||||
}
|
||||
|
||||
final PsiFile containingFile = myElement.getContainingFile();
|
||||
if (containingFile == null) return null;
|
||||
if (!myElement.isPhysical()) return null;
|
||||
@Override
|
||||
public Segment getSegment() {
|
||||
return myElementInfo.getSegment();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private SmartPointerElementInfo createElementInfo(@NotNull E element, PsiFile containingFile) {
|
||||
if (element instanceof PsiCompiledElement || !element.isPhysical() || containingFile == null || element.getTextRange() == null) {
|
||||
return new HardElementInfo(element);
|
||||
}
|
||||
|
||||
for(SmartPointerElementInfoFactory factory: Extensions.getExtensions(SmartPointerElementInfoFactory.EP_NAME)) {
|
||||
final SmartPointerElementInfo result = factory.createElementInfo(myElement);
|
||||
final SmartPointerElementInfo result = factory.createElementInfo(element);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
if (myElement instanceof PsiFile) {
|
||||
return new FileElementInfo((PsiFile)myElement);
|
||||
if (element instanceof PsiFile) {
|
||||
return new FileElementInfo((PsiFile)element);
|
||||
}
|
||||
PsiDocumentManager documentManager = PsiDocumentManager.getInstance(getProject());
|
||||
Document document = documentManager.getDocument(containingFile);
|
||||
if (document == null) return null; // must be non-text file
|
||||
|
||||
if (containingFile.getContext() != null) {
|
||||
return new InjectedSelfElementInfo(myElement, document);
|
||||
return new InjectedSelfElementInfo(element, containingFile);
|
||||
}
|
||||
|
||||
return new SelfElementInfo(myElement, document);
|
||||
}
|
||||
|
||||
private static boolean areElementKindEqual(PsiElement element1, PsiElement element2) {
|
||||
return element1.getClass().equals(element2.getClass()); //?
|
||||
return new SelfElementInfo(element, containingFile);
|
||||
}
|
||||
|
||||
public void documentAndPsiInSync() {
|
||||
@@ -153,16 +160,20 @@ class SmartPsiElementPointerImpl<E extends PsiElement> implements SmartPointerEx
|
||||
public void dispose() {
|
||||
if (myElementInfo != null) {
|
||||
myElementInfo.dispose();
|
||||
myElementInfo = null;
|
||||
myElement = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void fastenBelt() {
|
||||
if (myElementInfo != null && myElement != null && myElement.isValid()) return;
|
||||
@Override
|
||||
public void unfastenBelt(int offset) {
|
||||
myElementInfo.unfastenBelt(offset);
|
||||
}
|
||||
|
||||
if (myElementInfo == null && myElement != null && myElement.isValid()) {
|
||||
myElementInfo = createElementInfo();
|
||||
}
|
||||
public void fastenBelt(int offset) {
|
||||
myElementInfo.fastenBelt(offset);
|
||||
}
|
||||
|
||||
SmartPointerElementInfo getElementInfo() {
|
||||
return myElementInfo;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,6 @@ import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.PsiManager;
|
||||
import com.intellij.psi.impl.PsiManagerEx;
|
||||
import com.intellij.psi.impl.smartPointers.SmartPointerManagerImpl;
|
||||
import com.intellij.psi.impl.source.DummyHolder;
|
||||
import com.intellij.psi.impl.source.DummyHolderFactory;
|
||||
import com.intellij.psi.impl.source.SourceTreeToPsiMap;
|
||||
@@ -192,9 +191,9 @@ public class ChangeUtil {
|
||||
final PsiManagerEx psiManager = (PsiManagerEx) manager;
|
||||
final PsiFile file = (PsiFile)changedFile.getPsi();
|
||||
|
||||
if (file.isPhysical()) {
|
||||
SmartPointerManagerImpl.fastenBelts(file);
|
||||
}
|
||||
//if (file.isPhysical()) {
|
||||
// SmartPointerManagerImpl.fastenBelts(file, changedElement.getStartOffset());
|
||||
//}
|
||||
|
||||
action.makeChange(destinationTreeChange);
|
||||
|
||||
|
||||
@@ -18,9 +18,9 @@ package com.intellij.util.ui;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.RangeMarker;
|
||||
import com.intellij.openapi.editor.markup.*;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Segment;
|
||||
import com.intellij.util.Alarm;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
|
||||
@@ -33,7 +33,7 @@ import java.util.List;
|
||||
public class RangeBlinker {
|
||||
private final Editor myEditor;
|
||||
private int myTimeToLive;
|
||||
private final List<RangeMarker> myMarkers = new ArrayList<RangeMarker>();
|
||||
private final List<Segment> myMarkers = new ArrayList<Segment>();
|
||||
private boolean show = true;
|
||||
private final Alarm myBlinkingAlarm = new Alarm();
|
||||
private final TextAttributes myAttributes;
|
||||
@@ -44,7 +44,8 @@ public class RangeBlinker {
|
||||
myEditor = editor;
|
||||
myTimeToLive = timeToLive;
|
||||
}
|
||||
public void resetMarkers(final List<RangeMarker> markers) {
|
||||
|
||||
public void resetMarkers(final List<Segment> markers) {
|
||||
removeHighlights();
|
||||
myMarkers.clear();
|
||||
stopBlinking();
|
||||
@@ -72,8 +73,7 @@ public class RangeBlinker {
|
||||
|
||||
MarkupModel markupModel = myEditor.getMarkupModel();
|
||||
if (show) {
|
||||
for (final RangeMarker rangeMarker : myMarkers) {
|
||||
if (!rangeMarker.isValid()) continue;
|
||||
for (Segment rangeMarker : myMarkers) {
|
||||
RangeHighlighter highlighter = markupModel.addRangeHighlighter(rangeMarker.getStartOffset(), rangeMarker.getEndOffset(),
|
||||
HighlighterLayer.ADDITIONAL_SYNTAX, myAttributes,
|
||||
HighlighterTargetArea.EXACT_RANGE);
|
||||
|
||||
+1
-2
@@ -17,7 +17,6 @@ package com.intellij.openapi.fileEditor.impl;
|
||||
|
||||
import com.intellij.AppTopics;
|
||||
import com.intellij.codeStyle.CodeStyleFacade;
|
||||
import com.intellij.ide.IdeEventQueue;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.command.CommandProcessor;
|
||||
import com.intellij.openapi.components.ApplicationComponent;
|
||||
@@ -608,6 +607,6 @@ public class FileDocumentManagerImpl extends FileDocumentManager implements Appl
|
||||
}
|
||||
|
||||
private void fireFileContentLoaded(final VirtualFile file, final DocumentEx document) {
|
||||
//myBus.asyncPublisher(AppTopics.FILE_DOCUMENT_SYNC).fileContentLoaded(file, document);
|
||||
myBus.syncPublisher(AppTopics.FILE_DOCUMENT_SYNC).fileContentLoaded(file, document);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ public class FileContentQueue {
|
||||
private long myTotalSize;
|
||||
|
||||
private final ArrayBlockingQueue<FileContent> myQueue = new ArrayBlockingQueue<FileContent>(256);
|
||||
private Queue<FileContent> myPushbackBuffer = new ArrayDeque<FileContent>();
|
||||
private final Queue<FileContent> myPushbackBuffer = new ArrayDeque<FileContent>();
|
||||
|
||||
public void queue(final Collection<VirtualFile> files, @Nullable final ProgressIndicator indicator) {
|
||||
final Runnable contentLoadingRunnable = new Runnable() {
|
||||
|
||||
@@ -18,7 +18,7 @@ import com.intellij.openapi.application.Application;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.command.WriteCommandAction;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.testFramework.LeakedProjectHunter;
|
||||
import com.intellij.testFramework.LeakHunter;
|
||||
import com.intellij.testFramework.LightPlatformTestCase;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import junit.framework.TestCase;
|
||||
@@ -37,7 +37,7 @@ public class _LastInSuiteTest extends TestCase {
|
||||
}.execute().throwException();
|
||||
|
||||
final Application application = ApplicationManager.getApplication();
|
||||
LeakedProjectHunter.checkProjectLeak(application);
|
||||
LeakHunter.checkProjectLeak(application);
|
||||
|
||||
UIUtil.invokeAndWaitIfNeeded(new Runnable() {
|
||||
@Override
|
||||
|
||||
+20
-12
@@ -15,11 +15,12 @@
|
||||
*/
|
||||
package com.intellij.testFramework;
|
||||
|
||||
import com.intellij.openapi.application.impl.ApplicationImpl;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.project.impl.ProjectImpl;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.UserDataHolder;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.impl.source.tree.TreeElement;
|
||||
import com.intellij.util.Processor;
|
||||
import com.intellij.util.containers.Stack;
|
||||
import com.intellij.util.io.PersistentEnumerator;
|
||||
@@ -27,6 +28,7 @@ import com.intellij.util.ui.UIUtil;
|
||||
import gnu.trove.THashMap;
|
||||
import gnu.trove.THashSet;
|
||||
import gnu.trove.TObjectHashingStrategy;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.TestOnly;
|
||||
|
||||
import javax.swing.*;
|
||||
@@ -40,7 +42,7 @@ import java.util.Set;
|
||||
/**
|
||||
* User: cdr
|
||||
*/
|
||||
public class LeakedProjectHunter {
|
||||
public class LeakHunter {
|
||||
private static final Map<Class, List<Field>> allFields = new THashMap<Class, List<Field>>();
|
||||
private static List<Field> getAllFields(Class aClass) {
|
||||
List<Field> cached = allFields.get(aClass);
|
||||
@@ -65,13 +67,13 @@ public class LeakedProjectHunter {
|
||||
}
|
||||
|
||||
private static final Set<Object> visited = new THashSet<Object>(TObjectHashingStrategy.IDENTITY);
|
||||
static class BackLink {
|
||||
Class aClass;
|
||||
Object value;
|
||||
Field field;
|
||||
BackLink backLink;
|
||||
private static class BackLink {
|
||||
private final Class aClass;
|
||||
private final Object value;
|
||||
private final Field field;
|
||||
private final BackLink backLink;
|
||||
|
||||
BackLink(Class aClass, Object value, Field field, BackLink backLink) {
|
||||
private BackLink(Class aClass, Object value, Field field, BackLink backLink) {
|
||||
this.aClass = aClass;
|
||||
this.value = value;
|
||||
this.field = field;
|
||||
@@ -89,11 +91,15 @@ public class LeakedProjectHunter {
|
||||
Class rootClass = backLink.aClass;
|
||||
List<Field> fields = getAllFields(rootClass);
|
||||
for (Field field : fields) {
|
||||
if (root instanceof Reference && "referent".equals(field.getName())) continue; // do not follow weak/soft refs
|
||||
String fieldName = field.getName();
|
||||
if (root instanceof Reference && "referent".equals(fieldName)) continue; // do not follow weak/soft refs
|
||||
Object value = field.get(root);
|
||||
if (value == null) continue;
|
||||
if (value instanceof PsiElement || value instanceof TreeElement) {
|
||||
int i = 0;
|
||||
}
|
||||
Class valueClass = value.getClass();
|
||||
if (valueClass == lookFor) {
|
||||
if (lookFor.isAssignableFrom(valueClass)) {
|
||||
BackLink newBackLink = new BackLink(valueClass, value, field, backLink);
|
||||
processor.process(newBackLink);
|
||||
}
|
||||
@@ -146,7 +152,7 @@ public class LeakedProjectHunter {
|
||||
checkLeak(root, ProjectImpl.class);
|
||||
}
|
||||
@TestOnly
|
||||
public static void checkLeak(Object root, Class suspectClass) throws Exception {
|
||||
public static void checkLeak(@NotNull Object root, @NotNull Class suspectClass) throws Exception {
|
||||
if (SwingUtilities.isEventDispatchThread()) {
|
||||
UIUtil.dispatchAllInvocationEvents();
|
||||
}
|
||||
@@ -154,7 +160,9 @@ public class LeakedProjectHunter {
|
||||
UIUtil.pump();
|
||||
}
|
||||
PersistentEnumerator.clearCacheForTests();
|
||||
toVisit.push(new BackLink(ApplicationImpl.class, root, null,null));
|
||||
toVisit.clear();
|
||||
visited.clear();
|
||||
toVisit.push(new BackLink(root.getClass(), root, null,null));
|
||||
try {
|
||||
walkObjects(new Processor<BackLink>() {
|
||||
@Override
|
||||
@@ -26,20 +26,20 @@ import com.intellij.openapi.editor.markup.TextAttributes;
|
||||
import com.intellij.openapi.fileTypes.FileType;
|
||||
import com.intellij.openapi.fileTypes.PlainSyntaxHighlighter;
|
||||
import com.intellij.openapi.fileTypes.SyntaxHighlighter;
|
||||
import com.intellij.openapi.util.Segment;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.PsiDocumentManager;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.usageView.UsageTreeColors;
|
||||
import com.intellij.usageView.UsageTreeColorsScheme;
|
||||
import com.intellij.util.Processor;
|
||||
import com.intellij.util.containers.FactoryMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.awt.*;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -60,7 +60,6 @@ public class ChunkExtractor {
|
||||
private final Lexer myLexer;
|
||||
|
||||
private abstract static class WeakFactory<T> {
|
||||
|
||||
private WeakReference<T> myRef;
|
||||
|
||||
@NotNull
|
||||
@@ -74,7 +73,6 @@ public class ChunkExtractor {
|
||||
myRef = new WeakReference<T>(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final ThreadLocal<WeakFactory<Map<PsiFile, ChunkExtractor>>> ourExtractors = new ThreadLocal<WeakFactory<Map<PsiFile, ChunkExtractor>>>() {
|
||||
@@ -95,19 +93,18 @@ public class ChunkExtractor {
|
||||
}
|
||||
};
|
||||
|
||||
public static TextChunk[] extractChunks(PsiElement element, List<RangeMarker> rangeMarkers) {
|
||||
return ourExtractors.get().getValue().get(element.getContainingFile()).extractChunks(rangeMarkers);
|
||||
public static TextChunk[] extractChunks(PsiFile file, UsageInfo2UsageAdapter usageAdapter) {
|
||||
return ourExtractors.get().getValue().get(file).extractChunks(usageAdapter);
|
||||
}
|
||||
|
||||
|
||||
private ChunkExtractor(PsiFile file) {
|
||||
private ChunkExtractor(@NotNull PsiFile file) {
|
||||
myColorsScheme = UsageTreeColorsScheme.getInstance().getScheme();
|
||||
|
||||
myDocument = PsiDocumentManager.getInstance(file.getProject()).getDocument(file);
|
||||
LOG.assertTrue(myDocument != null);
|
||||
final FileType fileType = file.getFileType();
|
||||
final SyntaxHighlighter highlighter =
|
||||
SyntaxHighlighter.PROVIDER.create(fileType, file.getProject(), file.getVirtualFile());
|
||||
final SyntaxHighlighter highlighter = SyntaxHighlighter.PROVIDER.create(fileType, file.getProject(), file.getVirtualFile());
|
||||
myHighlighter = highlighter == null ? new PlainSyntaxHighlighter() : highlighter;
|
||||
myLexer = myHighlighter.getHighlightingLexer();
|
||||
myLexer.start(myDocument.getCharsSequence());
|
||||
@@ -124,18 +121,13 @@ public class ChunkExtractor {
|
||||
return minStart == Integer.MAX_VALUE ? -1 : minStart;
|
||||
}
|
||||
|
||||
public TextChunk[] extractChunks(List<RangeMarker> rangeMarkers) {
|
||||
final ArrayList<RangeMarker> markers = new ArrayList<RangeMarker>(rangeMarkers.size());
|
||||
for (RangeMarker rangeMarker : rangeMarkers) {
|
||||
if (rangeMarker.isValid()) markers.add(rangeMarker);
|
||||
}
|
||||
int absoluteStartOffset = getStartOffset(markers);
|
||||
private TextChunk[] extractChunks(UsageInfo2UsageAdapter usageInfo2UsageAdapter) {
|
||||
int absoluteStartOffset = usageInfo2UsageAdapter.getNavigationOffset();
|
||||
assert absoluteStartOffset != -1;
|
||||
|
||||
final int lineNumber = myDocument.getLineNumber(absoluteStartOffset);
|
||||
final int columnNumber = absoluteStartOffset - myDocument.getLineStartOffset(lineNumber);
|
||||
|
||||
Collections.sort(markers, RangeMarker.BY_START_OFFSET);
|
||||
int lineStartOffset = myDocument.getLineStartOffset(lineNumber);
|
||||
int lineEndOffset = lineStartOffset < myDocument.getTextLength() ? myDocument.getLineEndOffset(lineNumber) : 0;
|
||||
if (lineStartOffset > lineEndOffset) return TextChunk.EMPTY_ARRAY;
|
||||
@@ -150,10 +142,10 @@ public class ChunkExtractor {
|
||||
lineStartOffset = Math.max(lineStartOffset, absoluteStartOffset - OFFSET_BEFORE_TO_SHOW_WHEN_LONG_LINE);
|
||||
lineEndOffset = Math.min(lineEndOffset, absoluteStartOffset + OFFSET_AFTER_TO_SHOW_WHEN_LONG_LINE);
|
||||
}
|
||||
return createTextChunks(markers, chars, lineStartOffset, lineEndOffset, result);
|
||||
return createTextChunks(usageInfo2UsageAdapter, chars, lineStartOffset, lineEndOffset, result);
|
||||
}
|
||||
|
||||
private TextChunk[] createTextChunks(final List<RangeMarker> markers,
|
||||
private TextChunk[] createTextChunks(final UsageInfo2UsageAdapter usageInfo2UsageAdapter,
|
||||
final CharSequence chars,
|
||||
int start,
|
||||
int end,
|
||||
@@ -185,7 +177,7 @@ public class ChunkExtractor {
|
||||
IElementType tokenType = lexer.getTokenType();
|
||||
TextAttributesKey[] tokenHighlights = highlighter.getTokenHighlights(tokenType);
|
||||
|
||||
processIntersectingRange(markers, chars, hiStart, hiEnd, tokenHighlights, result);
|
||||
processIntersectingRange(usageInfo2UsageAdapter, chars, hiStart, hiEnd, tokenHighlights, result);
|
||||
}
|
||||
finally {
|
||||
lexer.advance();
|
||||
@@ -195,28 +187,32 @@ public class ChunkExtractor {
|
||||
return result.toArray(new TextChunk[result.size()]);
|
||||
}
|
||||
|
||||
private void processIntersectingRange(List<RangeMarker> markers,
|
||||
CharSequence chars,
|
||||
private void processIntersectingRange(UsageInfo2UsageAdapter usageInfo2UsageAdapter,
|
||||
final CharSequence chars,
|
||||
int hiStart,
|
||||
int hiEnd,
|
||||
final int hiEnd,
|
||||
TextAttributesKey[] tokenHighlights,
|
||||
List<TextChunk> result) {
|
||||
TextAttributes originalAttrs = convertAttributes(tokenHighlights);
|
||||
int lastOffset = hiStart;
|
||||
for(RangeMarker rangeMarker: markers) {
|
||||
int usageStart = rangeMarker.getStartOffset();
|
||||
int usageEnd = rangeMarker.getEndOffset();
|
||||
if (rangeMarker.isValid() && rangeIntersect(lastOffset, hiEnd, usageStart, usageEnd)) {
|
||||
addChunk(chars, lastOffset, Math.max(lastOffset, usageStart), originalAttrs, false, result);
|
||||
addChunk(chars, Math.max(lastOffset, usageStart), Math.min(hiEnd, usageEnd), originalAttrs, true, result);
|
||||
if (usageEnd > hiEnd) {
|
||||
return;
|
||||
final List<TextChunk> result) {
|
||||
final TextAttributes originalAttrs = convertAttributes(tokenHighlights);
|
||||
final int[] lastOffset = {hiStart};
|
||||
usageInfo2UsageAdapter.processRangeMarkers(new Processor<Segment>() {
|
||||
@Override
|
||||
public boolean process(Segment segment) {
|
||||
int usageStart = segment.getStartOffset();
|
||||
int usageEnd = segment.getEndOffset();
|
||||
if (rangeIntersect(lastOffset[0], hiEnd, usageStart, usageEnd)) {
|
||||
addChunk(chars, lastOffset[0], Math.max(lastOffset[0], usageStart), originalAttrs, false, result);
|
||||
addChunk(chars, Math.max(lastOffset[0], usageStart), Math.min(hiEnd, usageEnd), originalAttrs, true, result);
|
||||
lastOffset[0] = usageEnd;
|
||||
if (usageEnd > hiEnd) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
lastOffset = usageEnd;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (lastOffset < hiEnd) {
|
||||
addChunk(chars, lastOffset, hiEnd, originalAttrs, false, result);
|
||||
});
|
||||
if (lastOffset[0] < hiEnd) {
|
||||
addChunk(chars, lastOffset[0], hiEnd, originalAttrs, false, result);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2000-2011 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.usages;
|
||||
|
||||
import com.intellij.openapi.vfs.VfsUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.usages.rules.UsageInFile;
|
||||
import com.intellij.usages.rules.UsageInFiles;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import gnu.trove.THashSet;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* User: cdr
|
||||
*/
|
||||
public class UsageDataUtil {
|
||||
@Nullable
|
||||
public static VirtualFile[] provideVirtualFileArray(Usage[] usages, UsageTarget[] usageTargets) {
|
||||
if (usages == null && usageTargets == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Set<VirtualFile> result = new THashSet<VirtualFile>();
|
||||
|
||||
if (usages != null) {
|
||||
for (Usage usage : usages) {
|
||||
if (usage instanceof UsageInFile) {
|
||||
VirtualFile file = ((UsageInFile)usage).getFile();
|
||||
if (file.isValid()) {
|
||||
result.add(file);
|
||||
}
|
||||
}
|
||||
|
||||
if (usage instanceof UsageInFiles) {
|
||||
VirtualFile[] files = ((UsageInFiles)usage).getFiles();
|
||||
for (VirtualFile file : files) {
|
||||
if (file.isValid()) {
|
||||
result.add(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (usageTargets != null) {
|
||||
for (UsageTarget usageTarget : usageTargets) {
|
||||
if (usageTarget.isValid()) {
|
||||
final VirtualFile[] files = usageTarget.getFiles();
|
||||
if (files != null) {
|
||||
ContainerUtil.addAll(result, files);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return VfsUtil.toVirtualFileArray(result);
|
||||
}
|
||||
}
|
||||
@@ -20,10 +20,8 @@ import com.intellij.openapi.actionSystem.DataKey;
|
||||
import com.intellij.openapi.actionSystem.DataSink;
|
||||
import com.intellij.openapi.actionSystem.TypeSafeDataProvider;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.RangeMarker;
|
||||
import com.intellij.openapi.editor.markup.TextAttributes;
|
||||
import com.intellij.openapi.fileEditor.*;
|
||||
import com.intellij.openapi.module.Module;
|
||||
@@ -31,28 +29,33 @@ import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.*;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.Computable;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.Segment;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.reference.SoftReference;
|
||||
import com.intellij.ui.ComputableIcon;
|
||||
import com.intellij.usageView.UsageInfo;
|
||||
import com.intellij.usages.rules.*;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.NotNullFunction;
|
||||
import com.intellij.util.Processor;
|
||||
import com.intellij.util.SmartList;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.ArrayList;
|
||||
import java.lang.ref.Reference;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author max
|
||||
*/
|
||||
public class UsageInfo2UsageAdapter implements UsageInModule, UsageInLibrary, UsageInFile, PsiElementUsage, MergeableUsage, Comparable<UsageInfo2UsageAdapter>, RenameableUsage,
|
||||
TypeSafeDataProvider {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.usages.UsageInfo2UsageAdapter");
|
||||
|
||||
public class UsageInfo2UsageAdapter implements UsageInModule,
|
||||
UsageInLibrary, UsageInFile, PsiElementUsage,
|
||||
MergeableUsage, Comparable<UsageInfo2UsageAdapter>,
|
||||
RenameableUsage, TypeSafeDataProvider, UsagePresentation {
|
||||
public static final NotNullFunction<UsageInfo, Usage> CONVERTER = new NotNullFunction<UsageInfo, Usage>() {
|
||||
@NotNull
|
||||
public Usage fun(UsageInfo usageInfo) {
|
||||
@@ -61,40 +64,33 @@ public class UsageInfo2UsageAdapter implements UsageInModule, UsageInLibrary, Us
|
||||
};
|
||||
|
||||
private final UsageInfo myUsageInfo;
|
||||
private final List<UsageInfo> myMergedUsageInfos = new SmartList<UsageInfo>(); // contains all merged infos, including myUsageInfo
|
||||
private int myLineNumber;
|
||||
private int myOffset = -1;
|
||||
protected ComputableIcon myIcon;
|
||||
private String myTooltipText;
|
||||
private List<RangeMarker> myRangeMarkers = new ArrayList<RangeMarker>();
|
||||
private TextChunk[] myTextChunks;
|
||||
private final UsagePresentation myUsagePresentation;
|
||||
private Reference<TextChunk[]> myTextChunks; // allow to be gced and recreated on-demand because it requires a lot of memory
|
||||
|
||||
public UsageInfo2UsageAdapter(final UsageInfo usageInfo) {
|
||||
public UsageInfo2UsageAdapter(@NotNull final UsageInfo usageInfo) {
|
||||
myUsageInfo = usageInfo;
|
||||
myMergedUsageInfos.add(usageInfo);
|
||||
|
||||
myUsagePresentation = ApplicationManager.getApplication().runReadAction(new Computable<UsagePresentation>() {
|
||||
public UsagePresentation compute() {
|
||||
ApplicationManager.getApplication().runReadAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
PsiElement element = getElement();
|
||||
PsiFile psiFile = element.getContainingFile();
|
||||
Document document = PsiDocumentManager.getInstance(element.getProject()).getDocument(psiFile);
|
||||
|
||||
TextRange range = element.getTextRange();
|
||||
int startOffset = range.getStartOffset() + myUsageInfo.startOffset;
|
||||
int endOffset = range.getStartOffset() + myUsageInfo.endOffset;
|
||||
Document document = getDocument();
|
||||
int startOffset = myUsageInfo.getNavigationOffset();
|
||||
|
||||
if (document != null) {
|
||||
myLineNumber = getLineNumber(document, startOffset);
|
||||
|
||||
if (endOffset > document.getTextLength()) {
|
||||
LOG.error("Invalid usage info, psiElement:" + element + " end offset: " + endOffset + " psiFile: " + psiFile.getName());
|
||||
}
|
||||
|
||||
myRangeMarkers.add(document.createRangeMarker(startOffset, endOffset));
|
||||
initChunks();
|
||||
} else { // element over light virtual file
|
||||
myTextChunks = new TextChunk[] {
|
||||
}
|
||||
else { // element over light virtual file
|
||||
TextChunk[] chunks = {
|
||||
new TextChunk(new TextAttributes(), element.getText())
|
||||
};
|
||||
myTextChunks = new SoftReference<TextChunk[]>(chunks);
|
||||
myOffset = element.getTextOffset();
|
||||
}
|
||||
|
||||
@@ -111,11 +107,10 @@ public class UsageInfo2UsageAdapter implements UsageInModule, UsageInLibrary, Us
|
||||
});
|
||||
}
|
||||
|
||||
myTooltipText = myUsageInfo.getTooltipText();
|
||||
|
||||
return new MyUsagePresentation();
|
||||
myTooltipText = usageInfo.getTooltipText();
|
||||
}
|
||||
});
|
||||
myModificationStamp = getCurrentModificationStamp();
|
||||
}
|
||||
|
||||
private static int getLineNumber(final Document document, final int startOffset) {
|
||||
@@ -124,31 +119,20 @@ public class UsageInfo2UsageAdapter implements UsageInModule, UsageInLibrary, Us
|
||||
return document.getLineNumber(startOffset);
|
||||
}
|
||||
|
||||
private void initChunks() {
|
||||
myTextChunks = ChunkExtractor.extractChunks(getElement(), myRangeMarkers);
|
||||
private TextChunk[] initChunks() {
|
||||
TextChunk[] chunks = ChunkExtractor.extractChunks(getPsiFile(), this);
|
||||
myTextChunks = new SoftReference<TextChunk[]>(chunks);
|
||||
return chunks;
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
public UsagePresentation getPresentation() {
|
||||
return myUsagePresentation;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isValid() {
|
||||
PsiElement element = getElement();
|
||||
if (element == null || !element.isValid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return markersValid();
|
||||
}
|
||||
|
||||
private boolean markersValid() {
|
||||
for (RangeMarker rangeMarker : myRangeMarkers) {
|
||||
if (!rangeMarker.isValid()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return element != null && element.isValid();
|
||||
}
|
||||
|
||||
public boolean isReadOnly() {
|
||||
@@ -162,31 +146,44 @@ public class UsageInfo2UsageAdapter implements UsageInModule, UsageInLibrary, Us
|
||||
FileEditor editor = FileEditorManager.getInstance(getProject()).getSelectedEditor(virtualFile);
|
||||
if (!(editor instanceof TextEditor)) return null;
|
||||
|
||||
return new TextEditorLocation(myUsageInfo.startOffset + getElement().getTextRange().getStartOffset(), (TextEditor)editor);
|
||||
return new TextEditorLocation(getUsageInfo().startOffset + getElement().getTextRange().getStartOffset(), (TextEditor)editor);
|
||||
}
|
||||
|
||||
public void selectInEditor() {
|
||||
if (!isValid()) return;
|
||||
Editor editor = openTextEditor(true);
|
||||
RangeMarker marker = getRangeMarker();
|
||||
Segment marker = getFirstSegment();
|
||||
editor.getSelectionModel().setSelection(marker.getStartOffset(), marker.getEndOffset());
|
||||
}
|
||||
|
||||
public void highlightInEditor() {
|
||||
if (!isValid()) return;
|
||||
|
||||
RangeMarker marker = getRangeMarker();
|
||||
Segment marker = getFirstSegment();
|
||||
SelectInEditorManager.getInstance(getProject()).selectInEditor(getFile(), marker.getStartOffset(), marker.getEndOffset(), false, false);
|
||||
}
|
||||
|
||||
public final RangeMarker getRangeMarker() {
|
||||
return myRangeMarkers.get(0);
|
||||
private Segment getFirstSegment() {
|
||||
return getUsageInfo().getSegment();
|
||||
}
|
||||
|
||||
public List<RangeMarker> getRangeMarkers() {
|
||||
return myRangeMarkers;
|
||||
// must iterate in start offset order
|
||||
public boolean processRangeMarkers(Processor<Segment> processor) {
|
||||
for (UsageInfo usageInfo : myMergedUsageInfos) {
|
||||
if (!processor.process(usageInfo.getSegment())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public Document getDocument() {
|
||||
PsiFile file = getUsageInfo().getFile();
|
||||
if (file == null) return null;
|
||||
return PsiDocumentManager.getInstance(getProject()).getDocument(file);
|
||||
}
|
||||
|
||||
|
||||
public void navigate(boolean focus) {
|
||||
if (canNavigate()) {
|
||||
openTextEditor(focus);
|
||||
@@ -207,18 +204,22 @@ public class UsageInfo2UsageAdapter implements UsageInModule, UsageInLibrary, Us
|
||||
|
||||
@Nullable
|
||||
private OpenFileDescriptor getDescriptor() {
|
||||
if (markersValid()) {
|
||||
return new OpenFileDescriptor(getProject(), getFile(), !myRangeMarkers.isEmpty() ? getRangeMarker().getStartOffset() : myOffset);
|
||||
VirtualFile file = getFile();
|
||||
if(file == null) return null;
|
||||
int offset = getNavigationOffset();
|
||||
return new OpenFileDescriptor(getProject(), file, offset);
|
||||
}
|
||||
|
||||
int getNavigationOffset() {
|
||||
Document document = getDocument();
|
||||
if (document == null) return -1;
|
||||
int offset = getUsageInfo().getNavigationOffset();
|
||||
if (offset == -1) offset = myOffset;
|
||||
if (offset >= document.getTextLength()) {
|
||||
int line = Math.max(0, Math.min(myLineNumber, document.getLineCount()));
|
||||
offset = document.getLineStartOffset(line);
|
||||
}
|
||||
else if (getFile().isValid()) {
|
||||
final Document doc = FileDocumentManager.getInstance().getDocument(getFile());
|
||||
if (doc != null) {
|
||||
int line = Math.max(0, Math.min(myLineNumber, doc.getLineCount()));
|
||||
return new OpenFileDescriptor(getProject(), getFile(), line, 0);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
return offset;
|
||||
}
|
||||
|
||||
private Project getProject() {
|
||||
@@ -239,27 +240,25 @@ public class UsageInfo2UsageAdapter implements UsageInModule, UsageInLibrary, Us
|
||||
|
||||
public Module getModule() {
|
||||
if (!isValid()) return null;
|
||||
PsiElement element = getElement();
|
||||
VirtualFile virtualFile = getFile();
|
||||
if (virtualFile == null) return null;
|
||||
|
||||
ProjectRootManager projectRootManager = ProjectRootManager.getInstance(element.getProject());
|
||||
ProjectRootManager projectRootManager = ProjectRootManager.getInstance(getProject());
|
||||
ProjectFileIndex fileIndex = projectRootManager.getFileIndex();
|
||||
return fileIndex.getModuleForFile(virtualFile);
|
||||
}
|
||||
|
||||
public OrderEntry getLibraryEntry() {
|
||||
if (!isValid()) return null;
|
||||
PsiElement element = getElement();
|
||||
PsiFile psiFile = element.getContainingFile();
|
||||
PsiFile psiFile = getPsiFile();
|
||||
VirtualFile virtualFile = getFile();
|
||||
if (virtualFile == null) return null;
|
||||
|
||||
ProjectRootManager projectRootManager = ProjectRootManager.getInstance(element.getProject());
|
||||
ProjectRootManager projectRootManager = ProjectRootManager.getInstance(getProject());
|
||||
ProjectFileIndex fileIndex = projectRootManager.getFileIndex();
|
||||
|
||||
if (psiFile instanceof PsiCompiledElement || fileIndex.isInLibrarySource(virtualFile)) {
|
||||
List<OrderEntry> orders = fileIndex.getOrderEntriesForFile(psiFile.getVirtualFile());
|
||||
List<OrderEntry> orders = fileIndex.getOrderEntriesForFile(virtualFile);
|
||||
for (OrderEntry order : orders) {
|
||||
if (order instanceof LibraryOrderEntry || order instanceof JdkOrderEntry) {
|
||||
return order;
|
||||
@@ -271,12 +270,10 @@ public class UsageInfo2UsageAdapter implements UsageInModule, UsageInLibrary, Us
|
||||
}
|
||||
|
||||
public VirtualFile getFile() {
|
||||
if (isValid()) {
|
||||
return getElement().getContainingFile().getVirtualFile();
|
||||
}
|
||||
else {
|
||||
return myUsageInfo.getVirtualFile();
|
||||
}
|
||||
return getUsageInfo().getVirtualFile();
|
||||
}
|
||||
private PsiFile getPsiFile() {
|
||||
return getUsageInfo().getFile();
|
||||
}
|
||||
|
||||
public int getLine() {
|
||||
@@ -287,22 +284,27 @@ public class UsageInfo2UsageAdapter implements UsageInModule, UsageInLibrary, Us
|
||||
if (!(other instanceof UsageInfo2UsageAdapter)) return false;
|
||||
UsageInfo2UsageAdapter u2 = (UsageInfo2UsageAdapter)other;
|
||||
if (myLineNumber != u2.myLineNumber || getFile() != u2.getFile()) return false;
|
||||
myRangeMarkers.addAll(u2.myRangeMarkers);
|
||||
myMergedUsageInfos.addAll(u2.myMergedUsageInfos);
|
||||
Collections.sort(myMergedUsageInfos, new Comparator<UsageInfo>() {
|
||||
@Override
|
||||
public int compare(UsageInfo o1, UsageInfo o2) {
|
||||
return o1.getNavigationOffset() - o2.getNavigationOffset();
|
||||
}
|
||||
});
|
||||
initChunks();
|
||||
return true;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
if (myRangeMarkers.size() > 1) {
|
||||
RangeMarker marker = getRangeMarker();
|
||||
myRangeMarkers = new ArrayList<RangeMarker>();
|
||||
myRangeMarkers.add(marker);
|
||||
if (!myMergedUsageInfos.isEmpty()) {
|
||||
myMergedUsageInfos.clear();
|
||||
myMergedUsageInfos.add(myUsageInfo);
|
||||
initChunks();
|
||||
}
|
||||
}
|
||||
|
||||
public final PsiElement getElement() {
|
||||
return myUsageInfo.getElement();
|
||||
return getUsageInfo().getElement();
|
||||
}
|
||||
|
||||
public PsiReference getReference() {
|
||||
@@ -310,7 +312,7 @@ public class UsageInfo2UsageAdapter implements UsageInModule, UsageInLibrary, Us
|
||||
}
|
||||
|
||||
public boolean isNonCodeUsage() {
|
||||
return myUsageInfo.isNonCodeUsage;
|
||||
return getUsageInfo().isNonCodeUsage;
|
||||
}
|
||||
|
||||
public UsageInfo getUsageInfo() {
|
||||
@@ -318,86 +320,20 @@ public class UsageInfo2UsageAdapter implements UsageInModule, UsageInLibrary, Us
|
||||
}
|
||||
|
||||
public int compareTo(final UsageInfo2UsageAdapter o) {
|
||||
final PsiElement element = getElement();
|
||||
final PsiFile containingFile = element == null ? null : element.getContainingFile();
|
||||
final PsiElement oElement = o.getElement();
|
||||
final PsiFile oContainingFile = oElement == null ? null : oElement.getContainingFile();
|
||||
if (containingFile == null && oContainingFile == null
|
||||
|| !Comparing.equal(containingFile, oContainingFile)) {
|
||||
VirtualFile containingFile = getFile();
|
||||
VirtualFile oContainingFile = o.getFile();
|
||||
if (containingFile == null && oContainingFile == null || !Comparing.equal(containingFile, oContainingFile)) {
|
||||
return 0;
|
||||
}
|
||||
return getRangeMarker().getStartOffset() - o.getRangeMarker().getStartOffset();
|
||||
return getFirstSegment().getStartOffset() - o.getFirstSegment().getStartOffset();
|
||||
}
|
||||
|
||||
public void rename(String newName) throws IncorrectOperationException {
|
||||
final PsiReference reference = myUsageInfo.getReference();
|
||||
final PsiReference reference = getUsageInfo().getReference();
|
||||
assert reference != null : this;
|
||||
reference.handleElementRename(newName);
|
||||
}
|
||||
|
||||
private class MyUsagePresentation implements UsagePresentation {
|
||||
private long myModificationStamp;
|
||||
|
||||
private MyUsagePresentation() {
|
||||
myModificationStamp = getCurrentModificationStamp();
|
||||
}
|
||||
|
||||
private long getCurrentModificationStamp() {
|
||||
final PsiFile containingFile = getElement().getContainingFile();
|
||||
return containingFile == null? -1L : containingFile.getModificationStamp();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public TextChunk[] getText() {
|
||||
if (isValid()) {
|
||||
// the check below makes sense only for valid PsiElement
|
||||
final long currentModificationStamp = getCurrentModificationStamp();
|
||||
if (currentModificationStamp != myModificationStamp) {
|
||||
initChunks();
|
||||
myModificationStamp = currentModificationStamp;
|
||||
}
|
||||
}
|
||||
return myTextChunks;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getPlainText() {
|
||||
if (myRangeMarkers.isEmpty()) { // element over light virtual file
|
||||
return myTextChunks[0].getText();
|
||||
}
|
||||
|
||||
int startOffset = ChunkExtractor.getStartOffset(myRangeMarkers);
|
||||
final PsiElement element = getElement();
|
||||
if (element != null && startOffset != -1) {
|
||||
final Document document = PsiDocumentManager.getInstance(element.getProject()).getDocument(element.getContainingFile());
|
||||
if (document != null) {
|
||||
int lineNumber = document.getLineNumber(startOffset);
|
||||
int lineStart = document.getLineStartOffset(lineNumber);
|
||||
int lineEnd = document.getLineEndOffset(lineNumber);
|
||||
String prefixSuffix = null;
|
||||
|
||||
if (lineEnd - lineStart > ChunkExtractor.MAX_LINE_TO_SHOW) {
|
||||
prefixSuffix = "...";
|
||||
lineStart = Math.max(startOffset - ChunkExtractor.OFFSET_BEFORE_TO_SHOW_WHEN_LONG_LINE, lineStart);
|
||||
lineEnd = Math.min(startOffset + ChunkExtractor.OFFSET_AFTER_TO_SHOW_WHEN_LONG_LINE, lineEnd);
|
||||
}
|
||||
String s = document.getCharsSequence().subSequence(lineStart, lineEnd).toString();
|
||||
if (prefixSuffix != null) s = prefixSuffix + s + prefixSuffix;
|
||||
return s;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public Icon getIcon() {
|
||||
return myIcon != null ? myIcon.getIcon() : null;
|
||||
}
|
||||
|
||||
public String getTooltipText() {
|
||||
return myTooltipText;
|
||||
}
|
||||
}
|
||||
|
||||
public static UsageInfo2UsageAdapter[] convert(UsageInfo[] usageInfos) {
|
||||
UsageInfo2UsageAdapter[] result = new UsageInfo2UsageAdapter[usageInfos.length];
|
||||
for (int i = 0; i < result.length; i++) {
|
||||
@@ -412,23 +348,78 @@ public class UsageInfo2UsageAdapter implements UsageInModule, UsageInLibrary, Us
|
||||
sink.put(UsageView.USAGE_INFO_KEY, getUsageInfo());
|
||||
}
|
||||
if (key == UsageView.USAGE_INFO_LIST_KEY) {
|
||||
ArrayList<UsageInfo> list = getSelectedInfoList();
|
||||
List<UsageInfo> list = getSelectedInfoList();
|
||||
sink.put(UsageView.USAGE_INFO_LIST_KEY, list);
|
||||
}
|
||||
}
|
||||
|
||||
private ArrayList<UsageInfo> getSelectedInfoList() {
|
||||
ArrayList<UsageInfo> list = new ArrayList<UsageInfo>();
|
||||
UsageInfo first = getUsageInfo();
|
||||
list.add(first);
|
||||
for (int i = 1; i < myRangeMarkers.size(); i++) {
|
||||
RangeMarker rangeMarker = myRangeMarkers.get(i);
|
||||
PsiElement element = first.getElement();
|
||||
if (element == null) continue;
|
||||
PsiFile file = element.getContainingFile();
|
||||
UsageInfo usageInfo = new UsageInfo(file, rangeMarker.getStartOffset(), rangeMarker.getEndOffset());
|
||||
list.add(usageInfo);
|
||||
private List<UsageInfo> getSelectedInfoList() {
|
||||
return myMergedUsageInfos;
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
for (UsageInfo usageInfo : myMergedUsageInfos) {
|
||||
usageInfo.dispose();
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private long myModificationStamp;
|
||||
private long getCurrentModificationStamp() {
|
||||
final PsiFile containingFile = getPsiFile();
|
||||
return containingFile == null ? -1L : containingFile.getModificationStamp();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public TextChunk[] getText() {
|
||||
TextChunk[] chunks = myTextChunks.get();
|
||||
if (chunks == null) {
|
||||
myModificationStamp = -1;
|
||||
}
|
||||
if (chunks == null || isValid()) {
|
||||
// the check below makes sense only for valid PsiElement
|
||||
final long currentModificationStamp = getCurrentModificationStamp();
|
||||
if (currentModificationStamp != myModificationStamp) {
|
||||
chunks = initChunks();
|
||||
myModificationStamp = currentModificationStamp;
|
||||
}
|
||||
}
|
||||
return chunks;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getPlainText() {
|
||||
//if (myRangeMarkers.isEmpty()) { // element over light virtual file
|
||||
// return myTextChunks[0].getText();
|
||||
//}
|
||||
|
||||
int startOffset = getNavigationOffset();
|
||||
final PsiElement element = getElement();
|
||||
if (element != null && startOffset != -1) {
|
||||
final Document document = getDocument();
|
||||
if (document != null) {
|
||||
int lineNumber = document.getLineNumber(startOffset);
|
||||
int lineStart = document.getLineStartOffset(lineNumber);
|
||||
int lineEnd = document.getLineEndOffset(lineNumber);
|
||||
String prefixSuffix = null;
|
||||
|
||||
if (lineEnd - lineStart > ChunkExtractor.MAX_LINE_TO_SHOW) {
|
||||
prefixSuffix = "...";
|
||||
lineStart = Math.max(startOffset - ChunkExtractor.OFFSET_BEFORE_TO_SHOW_WHEN_LONG_LINE, lineStart);
|
||||
lineEnd = Math.min(startOffset + ChunkExtractor.OFFSET_AFTER_TO_SHOW_WHEN_LONG_LINE, lineEnd);
|
||||
}
|
||||
String s = document.getCharsSequence().subSequence(lineStart, lineEnd).toString();
|
||||
if (prefixSuffix != null) s = prefixSuffix + s + prefixSuffix;
|
||||
return s;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public Icon getIcon() {
|
||||
return myIcon != null ? myIcon.getIcon() : null;
|
||||
}
|
||||
|
||||
public String getTooltipText() {
|
||||
return myTooltipText;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,10 +23,10 @@ import com.intellij.usages.UsageGroup;
|
||||
import com.intellij.usages.UsageView;
|
||||
import com.intellij.usages.UsageViewSettings;
|
||||
import com.intellij.usages.rules.MergeableUsage;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import gnu.trove.THashMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.tree.DefaultMutableTreeNode;
|
||||
import javax.swing.tree.TreeNode;
|
||||
import java.util.*;
|
||||
@@ -67,18 +67,23 @@ public class GroupNode extends Node implements Navigatable, Comparable<GroupNode
|
||||
final GroupNode node1 = node = new GroupNode(group, ruleIndex, getBuilder());
|
||||
mySubgroupNodes.put(group, node);
|
||||
|
||||
if (!getBuilder().isDetachedMode()) {
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
myTreeModel.insertNodeInto(node1, GroupNode.this, getNodeInsertionIndex(node1));
|
||||
}
|
||||
});
|
||||
}
|
||||
addNode(node1);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
}
|
||||
|
||||
void addNode(final DefaultMutableTreeNode node) {
|
||||
if (!getBuilder().isDetachedMode()) {
|
||||
UIUtil.invokeLaterIfNeeded(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
myTreeModel.insertNodeInto(node, GroupNode.this, getNodeInsertionIndex(node));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private UsageViewTreeModelBuilder getBuilder() {
|
||||
return (UsageViewTreeModelBuilder)myTreeModel;
|
||||
}
|
||||
@@ -150,7 +155,7 @@ public class GroupNode extends Node implements Navigatable, Comparable<GroupNode
|
||||
}
|
||||
|
||||
if (!getBuilder().isDetachedMode()) {
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
UIUtil.invokeLaterIfNeeded(new Runnable() {
|
||||
public void run() {
|
||||
myTreeModel.insertNodeInto(node, GroupNode.this, getNodeIndex(node));
|
||||
incrementUsageCount();
|
||||
@@ -241,7 +246,7 @@ public class GroupNode extends Node implements Navigatable, Comparable<GroupNode
|
||||
return false;
|
||||
}
|
||||
|
||||
int getNodeInsertionIndex(DefaultMutableTreeNode node) {
|
||||
private int getNodeInsertionIndex(DefaultMutableTreeNode node) {
|
||||
Enumeration children = children();
|
||||
int idx = 0;
|
||||
while (children.hasMoreElements()) {
|
||||
|
||||
@@ -29,6 +29,7 @@ import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.ui.SimpleToolWindowPanel;
|
||||
import com.intellij.openapi.ui.Splitter;
|
||||
import com.intellij.openapi.util.Condition;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.openapi.util.Factory;
|
||||
import com.intellij.openapi.util.IconLoader;
|
||||
@@ -48,8 +49,8 @@ import com.intellij.usages.rules.*;
|
||||
import com.intellij.util.Alarm;
|
||||
import com.intellij.util.EditSourceOnDoubleClickHandler;
|
||||
import com.intellij.util.Processor;
|
||||
import com.intellij.util.containers.ConcurrentHashSet;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.TransferToEDTQueue;
|
||||
import com.intellij.util.messages.MessageBusConnection;
|
||||
import com.intellij.util.ui.DialogUtil;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
@@ -67,9 +68,7 @@ import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
/**
|
||||
* @author max
|
||||
@@ -91,15 +90,12 @@ public class UsageViewImpl implements UsageView, UsageModelTracker.UsageModelTra
|
||||
private ExporterToTextFile myTextFileExporter;
|
||||
private final Alarm myUpdateAlarm = new Alarm(Alarm.ThreadToUse.SWING_THREAD);
|
||||
|
||||
private final Alarm myFlushAlarm = new Alarm(Alarm.ThreadToUse.SHARED_THREAD);
|
||||
private final UsageModelTracker myModelTracker;
|
||||
private final Set<Usage> myUsages = new ConcurrentHashSet<Usage>();
|
||||
private final Map<Usage, UsageNode> myUsageNodes = new ConcurrentHashMap<Usage, UsageNode>();
|
||||
public static final UsageNode NULL_NODE = new UsageNode(NullUsage.INSTANCE, new UsageViewTreeModelBuilder(new UsageViewPresentation(), UsageTarget.EMPTY_ARRAY));
|
||||
private final ButtonPanel myButtonPanel = new ButtonPanel();
|
||||
private volatile boolean isDisposed;
|
||||
private volatile boolean myChangesDetected = false;
|
||||
private final Queue<Usage> myUsagesToFlush = new ConcurrentLinkedQueue<Usage>();
|
||||
static final Comparator<Usage> USAGE_COMPARATOR = new Comparator<Usage>() {
|
||||
public int compare(final Usage o1, final Usage o2) {
|
||||
if (o1 == NULL_NODE || o2 == NULL_NODE) return -1;
|
||||
@@ -130,7 +126,7 @@ public class UsageViewImpl implements UsageView, UsageModelTracker.UsageModelTra
|
||||
private final GroupNode myRoot;
|
||||
private final UsageViewTreeModelBuilder myModel;
|
||||
|
||||
public UsageViewImpl(@NotNull Project project,
|
||||
public UsageViewImpl(@NotNull final Project project,
|
||||
@NotNull UsageViewPresentation presentation,
|
||||
@NotNull UsageTarget[] targets,
|
||||
Factory<UsageSearcher> usageSearcherFactory) {
|
||||
@@ -173,7 +169,7 @@ public class UsageViewImpl implements UsageView, UsageModelTracker.UsageModelTra
|
||||
});
|
||||
|
||||
if (!myPresentation.isDetachedMode()) {
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
UIUtil.invokeLaterIfNeeded(new Runnable() {
|
||||
public void run() {
|
||||
if (isDisposed) return;
|
||||
myTree.setModel(myModel);
|
||||
@@ -225,6 +221,18 @@ public class UsageViewImpl implements UsageView, UsageModelTracker.UsageModelTra
|
||||
}
|
||||
});
|
||||
}
|
||||
myTransferToEDTQueue = new TransferToEDTQueue<Usage>(new Processor<Usage>() {
|
||||
@Override
|
||||
public boolean process(Usage usage) {
|
||||
appendUsage(usage);
|
||||
return true;
|
||||
}
|
||||
}, new Condition<Object>() {
|
||||
@Override
|
||||
public boolean value(Object o) {
|
||||
return isDisposed || project.isDisposed() || com.intellij.usages.UsageViewManager.getInstance(project).searchHasBeenCancelled();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setupCentralPanel() {
|
||||
@@ -645,35 +653,13 @@ public class UsageViewImpl implements UsageView, UsageModelTracker.UsageModelTra
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
myUsages.clear();
|
||||
}
|
||||
|
||||
public void appendUsageLater(final Usage usage) {
|
||||
myUsagesToFlush.offer(usage);
|
||||
if (myUsagesToFlush.size() > 50) {
|
||||
flush();
|
||||
}
|
||||
myFlushAlarm.cancelAllRequests();
|
||||
myFlushAlarm.addRequest(new Runnable() {
|
||||
public void run() {
|
||||
flush();
|
||||
}
|
||||
}, 300);
|
||||
private final TransferToEDTQueue<Usage> myTransferToEDTQueue;
|
||||
public void appendUsageLater(Usage usage) {
|
||||
myTransferToEDTQueue.offer(usage);
|
||||
}
|
||||
|
||||
|
||||
private void flush() {
|
||||
ApplicationManager.getApplication().runReadAction(new Runnable() {
|
||||
public void run() {
|
||||
Usage usage;
|
||||
while ((usage = myUsagesToFlush.poll()) != null) {
|
||||
appendUsage(usage);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private volatile boolean myIsFirstVisibleUsageFound = false;
|
||||
|
||||
public void appendUsage(@NotNull Usage usage) {
|
||||
@@ -684,10 +670,9 @@ public class UsageViewImpl implements UsageView, UsageModelTracker.UsageModelTra
|
||||
// invoke in ReadAction to be be sure that usages are not invalidated while the tree is being built
|
||||
ApplicationManager.getApplication().assertReadAccessAllowed();
|
||||
if (!usage.isValid()) {
|
||||
// because the view is built incrementally with Alarm, the usage may be already invalid, so need filter such cases
|
||||
// because the view is built incrementally, the usage may be already invalid, so need to filter such cases
|
||||
return null;
|
||||
}
|
||||
myUsages.add(usage);
|
||||
UsageNode node = myBuilder.appendUsage(usage);
|
||||
myUsageNodes.put(usage, node == null ? NULL_NODE : node);
|
||||
if (!myIsFirstVisibleUsageFound && node != null) { //first visible usage found;
|
||||
@@ -700,7 +685,7 @@ public class UsageViewImpl implements UsageView, UsageModelTracker.UsageModelTra
|
||||
public void removeUsage(@NotNull Usage usage) {
|
||||
final UsageNode node = myUsageNodes.remove(usage);
|
||||
if (node != NULL_NODE && node != null && !myPresentation.isDetachedMode()) {
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
UIUtil.invokeLaterIfNeeded(new Runnable() {
|
||||
public void run() {
|
||||
if (isDisposed) return;
|
||||
TreeModel treeModel = myTree.getModel();
|
||||
@@ -803,6 +788,11 @@ public class UsageViewImpl implements UsageView, UsageModelTracker.UsageModelTra
|
||||
UsageViewSettings.getInstance().PREVIEW_USAGES_SPLITTER_PROPORTIONS = ((Splitter)myUsagePreviewPanel.getParent()).getProportion();
|
||||
myUsagePreviewPanel = null;
|
||||
}
|
||||
for (Usage usage : getUsages()) {
|
||||
if (usage instanceof UsageInfo2UsageAdapter) {
|
||||
((UsageInfo2UsageAdapter)usage).dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isSearchInProgress() {
|
||||
@@ -811,13 +801,12 @@ public class UsageViewImpl implements UsageView, UsageModelTracker.UsageModelTra
|
||||
|
||||
public void setSearchInProgress(boolean searchInProgress) {
|
||||
mySearchInProgress = searchInProgress;
|
||||
flush();
|
||||
if (!myPresentation.isDetachedMode()) {
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
UIUtil.invokeLaterIfNeeded(new Runnable() {
|
||||
public void run() {
|
||||
if (isDisposed) return;
|
||||
final UsageNode firstUsageNode = myModel.getFirstUsageNode();
|
||||
if (firstUsageNode != null) { //first usage;
|
||||
if (firstUsageNode != null) {
|
||||
showNode(firstUsageNode);
|
||||
}
|
||||
}
|
||||
@@ -827,7 +816,7 @@ public class UsageViewImpl implements UsageView, UsageModelTracker.UsageModelTra
|
||||
|
||||
private void showNode(final UsageNode node) {
|
||||
if (!myPresentation.isDetachedMode()) {
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
UIUtil.invokeLaterIfNeeded(new Runnable() {
|
||||
public void run() {
|
||||
if (isDisposed) return;
|
||||
TreePath usagePath = new TreePath(node.getPath());
|
||||
@@ -840,10 +829,8 @@ public class UsageViewImpl implements UsageView, UsageModelTracker.UsageModelTra
|
||||
|
||||
public void addButtonToLowerPane(@NotNull Runnable runnable, @NotNull String text) {
|
||||
int index = myButtonPanel.getComponentCount();
|
||||
|
||||
if (index > 0 && myPresentation.isShowCancelButton()) index--;
|
||||
|
||||
myButtonPanel.add(index, runnable, text);
|
||||
myButtonPanel.addButtonRunnable(index, runnable, text);
|
||||
}
|
||||
|
||||
public void addButtonToLowerPane(@NotNull final Runnable runnable, @NotNull String text, char mnemonic) {
|
||||
@@ -981,12 +968,12 @@ public class UsageViewImpl implements UsageView, UsageModelTracker.UsageModelTra
|
||||
|
||||
@NotNull
|
||||
public Set<Usage> getUsages() {
|
||||
return myUsages;
|
||||
return myUsageNodes.keySet();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<Usage> getSortedUsages() {
|
||||
List<Usage> usages = new ArrayList<Usage>(myUsages);
|
||||
List<Usage> usages = new ArrayList<Usage>(getUsages());
|
||||
Collections.sort(usages, USAGE_COMPARATOR);
|
||||
return usages;
|
||||
}
|
||||
@@ -995,9 +982,7 @@ public class UsageViewImpl implements UsageView, UsageModelTracker.UsageModelTra
|
||||
if (node instanceof UsageNode) {
|
||||
UsageNode usageNode = (UsageNode)node;
|
||||
final Usage usage = usageNode.getUsage();
|
||||
if (usage.isValid()) {
|
||||
usages.add(usage);
|
||||
}
|
||||
usages.add(usage);
|
||||
}
|
||||
|
||||
Enumeration enumeration = node.children();
|
||||
@@ -1042,7 +1027,7 @@ public class UsageViewImpl implements UsageView, UsageModelTracker.UsageModelTra
|
||||
if (nodes == null) {
|
||||
return null;
|
||||
}
|
||||
final ArrayList<Navigatable> result = new ArrayList<Navigatable>();
|
||||
final List<Navigatable> result = new ArrayList<Navigatable>();
|
||||
for (final Node node : nodes) {
|
||||
/*
|
||||
if (!node.isDataValid()) {
|
||||
@@ -1138,7 +1123,8 @@ public class UsageViewImpl implements UsageView, UsageModelTracker.UsageModelTra
|
||||
|
||||
else if (key == PlatformDataKeys.VIRTUAL_FILE_ARRAY) {
|
||||
final Set<Usage> usages = getSelectedUsages();
|
||||
VirtualFile[] data = provideVirtualFileArray(usages != null ? usages.toArray(new Usage[usages.size()]) : null, getSelectedUsageTargets());
|
||||
Usage[] ua = usages != null ? usages.toArray(new Usage[usages.size()]) : null;
|
||||
VirtualFile[] data = UsageDataUtil.provideVirtualFileArray(ua, getSelectedUsageTargets());
|
||||
sink.put(PlatformDataKeys.VIRTUAL_FILE_ARRAY, data);
|
||||
}
|
||||
|
||||
@@ -1160,43 +1146,6 @@ public class UsageViewImpl implements UsageView, UsageModelTracker.UsageModelTra
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private VirtualFile[] provideVirtualFileArray(Usage[] usages, UsageTarget[] usageTargets) {
|
||||
if (usages == null && usageTargets == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Set<VirtualFile> result = new THashSet<VirtualFile>();
|
||||
|
||||
if (usages != null) {
|
||||
for (Usage usage : usages) {
|
||||
if (usage.isValid()) {
|
||||
if (usage instanceof UsageInFile) {
|
||||
result.add(((UsageInFile)usage).getFile());
|
||||
}
|
||||
|
||||
if (usage instanceof UsageInFiles) {
|
||||
ContainerUtil.addAll(result, ((UsageInFiles)usage).getFiles());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (usageTargets != null) {
|
||||
for (UsageTarget usageTarget : usageTargets) {
|
||||
if (usageTarget.isValid()) {
|
||||
final VirtualFile[] files = usageTarget.getFiles();
|
||||
if (files != null) {
|
||||
ContainerUtil.addAll(result, files);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return VfsUtil.toVirtualFileArray(result);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class MyAutoScrollToSourceOptionProvider implements AutoScrollToSourceOptionProvider {
|
||||
@@ -1214,7 +1163,7 @@ public class UsageViewImpl implements UsageView, UsageModelTracker.UsageModelTra
|
||||
setLayout(new FlowLayout(FlowLayout.LEFT, 8, 0));
|
||||
}
|
||||
|
||||
public void add(int index, final Runnable runnable, String text) {
|
||||
public void addButtonRunnable(int index, final Runnable runnable, String text) {
|
||||
if (getBorder() == null) setBorder(IdeBorderFactory.createBorder(SideBorder.TOP));
|
||||
|
||||
final JButton button = new JButton(UIUtil.replaceMnemonicAmpersand(text));
|
||||
@@ -1328,5 +1277,4 @@ public class UsageViewImpl implements UsageView, UsageModelTracker.UsageModelTra
|
||||
public boolean isVisible(Usage usage) {
|
||||
return myBuilder != null && myBuilder.isVisible(usage);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -30,10 +30,7 @@ import com.intellij.openapi.project.DumbModeAction;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.MessageType;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.openapi.util.Factory;
|
||||
import com.intellij.openapi.util.IconLoader;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.*;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.wm.ToolWindow;
|
||||
import com.intellij.openapi.wm.ToolWindowId;
|
||||
@@ -42,6 +39,7 @@ import com.intellij.ui.content.Content;
|
||||
import com.intellij.usageView.UsageViewBundle;
|
||||
import com.intellij.usages.*;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.CommonProcessors;
|
||||
import com.intellij.util.Processor;
|
||||
import com.intellij.util.ui.RangeBlinker;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
@@ -404,7 +402,10 @@ public class UsageViewManagerImpl extends UsageViewManager {
|
||||
TextAttributes attributes = EditorColorsManager.getInstance().getGlobalScheme().getAttributes(CodeInsightColors.BLINKING_HIGHLIGHTS_ATTRIBUTES);
|
||||
|
||||
RangeBlinker rangeBlinker = new RangeBlinker(editor, attributes, 6);
|
||||
rangeBlinker.resetMarkers(usageInfo.getRangeMarkers());
|
||||
List<Segment> segments = new ArrayList<Segment>();
|
||||
CommonProcessors.CollectProcessor<Segment> processor = new CommonProcessors.CollectProcessor<Segment>(segments);
|
||||
usageInfo.processRangeMarkers(processor);
|
||||
rangeBlinker.resetMarkers(segments);
|
||||
rangeBlinker.startBlinking();
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="module" module-name="lang-api" />
|
||||
<orderEntry type="library" scope="TEST" name="JUnit4" level="project" />
|
||||
<orderEntry type="module" module-name="testFramework" scope="TEST" />
|
||||
</component>
|
||||
</module>
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ package com.intellij.openapi.util;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class TextRange {
|
||||
public class TextRange implements Segment{
|
||||
private final int myStartOffset;
|
||||
private final int myEndOffset;
|
||||
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright 2000-2011 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.containers;
|
||||
|
||||
import com.intellij.openapi.util.Condition;
|
||||
import com.intellij.util.Processor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
* Allows to process elements in the EDT.
|
||||
* Processes elements in batches, no longer than 200ms per batch, and reschedules processing later for longer batches.
|
||||
* Usage: {@link TransferToEDTQueue#offer(Object)} } : schedules element for processing in EDT (via invokeLater)
|
||||
*/
|
||||
public class TransferToEDTQueue<T> {
|
||||
private final Processor<T> myProcessor;
|
||||
private volatile boolean stopped;
|
||||
private final Condition<?> myShutUpCondition;
|
||||
|
||||
private final Queue<T> myQueue = new ConcurrentLinkedQueue<T>();
|
||||
private final AtomicBoolean invokeLaterScheduled = new AtomicBoolean();
|
||||
private static final long MAX_UNIT_OF_WORK_THRESHOLD_MS = 200; // no more than 200 ms delay
|
||||
private final Runnable myUpdateRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
boolean b = invokeLaterScheduled.compareAndSet(true, false);
|
||||
assert b;
|
||||
if (stopped || myShutUpCondition.value(null)) {
|
||||
stop();
|
||||
return;
|
||||
}
|
||||
long start = System.currentTimeMillis();
|
||||
int processed = 0;
|
||||
while (true) {
|
||||
T thing = myQueue.poll();
|
||||
if (thing == null) break;
|
||||
if (!myProcessor.process(thing)) {
|
||||
myQueue.clear();
|
||||
return;
|
||||
}
|
||||
processed++;
|
||||
long finish = System.currentTimeMillis();
|
||||
if (finish - start > MAX_UNIT_OF_WORK_THRESHOLD_MS) break;
|
||||
}
|
||||
if (!myQueue.isEmpty()) {
|
||||
scheduleUpdate();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public TransferToEDTQueue(@NotNull Processor<T> processorInEDT, @NotNull Condition<?> shutUpCondition) {
|
||||
myProcessor = processorInEDT;
|
||||
myShutUpCondition = shutUpCondition;
|
||||
}
|
||||
|
||||
public void offer(final T thing) {
|
||||
myQueue.offer(thing);
|
||||
scheduleUpdate();
|
||||
}
|
||||
|
||||
private void scheduleUpdate() {
|
||||
if (invokeLaterScheduled.compareAndSet(false, true)) {
|
||||
schedule(myUpdateRunnable);
|
||||
}
|
||||
}
|
||||
|
||||
protected void schedule(Runnable updateRunnable) {
|
||||
SwingUtilities.invokeLater(updateRunnable);
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
stopped = true;
|
||||
myQueue.clear();
|
||||
}
|
||||
}
|
||||
+28
-10
@@ -60,7 +60,7 @@ public class NavigationGutterIconBuilder<T> {
|
||||
};
|
||||
|
||||
private final Icon myIcon;
|
||||
private final NotNullFunction<T,Collection<? extends PsiElement>> myConvertor;
|
||||
private final NotNullFunction<T,Collection<? extends PsiElement>> myConverter;
|
||||
|
||||
private NotNullLazyValue<Collection<? extends T>> myTargets;
|
||||
private boolean myLazy;
|
||||
@@ -70,7 +70,7 @@ public class NavigationGutterIconBuilder<T> {
|
||||
private String myTooltipTitle;
|
||||
private GutterIconRenderer.Alignment myAlignment = GutterIconRenderer.Alignment.CENTER;
|
||||
private PsiElementListCellRenderer myCellRenderer;
|
||||
private NullableFunction<T,String> myNamer = (NullableFunction<T, String>)ElementPresentationManager.NAMER;
|
||||
private NullableFunction<T,String> myNamer = ElementPresentationManager.namer();
|
||||
public static final NotNullFunction<DomElement,Collection<? extends PsiElement>> DEFAULT_DOM_CONVERTOR = new NotNullFunction<DomElement, Collection<? extends PsiElement>>() {
|
||||
@NotNull
|
||||
public Collection<? extends PsiElement> fun(final DomElement o) {
|
||||
@@ -78,17 +78,17 @@ public class NavigationGutterIconBuilder<T> {
|
||||
}
|
||||
};
|
||||
|
||||
protected NavigationGutterIconBuilder(@NotNull final Icon icon, @NotNull NotNullFunction<T,Collection<? extends PsiElement>> convertor) {
|
||||
protected NavigationGutterIconBuilder(@NotNull final Icon icon, @NotNull NotNullFunction<T,Collection<? extends PsiElement>> converter) {
|
||||
myIcon = icon;
|
||||
myConvertor = convertor;
|
||||
myConverter = converter;
|
||||
}
|
||||
|
||||
public static NavigationGutterIconBuilder<PsiElement> create(@NotNull final Icon icon) {
|
||||
return create(icon, DEFAULT_PSI_CONVERTOR);
|
||||
}
|
||||
|
||||
public static <T> NavigationGutterIconBuilder<T> create(@NotNull final Icon icon, @NotNull NotNullFunction<T, Collection<? extends PsiElement>> convertor) {
|
||||
return new NavigationGutterIconBuilder<T>(icon, convertor);
|
||||
public static <T> NavigationGutterIconBuilder<T> create(@NotNull final Icon icon, @NotNull NotNullFunction<T, Collection<? extends PsiElement>> converter) {
|
||||
return new NavigationGutterIconBuilder<T>(icon, converter);
|
||||
}
|
||||
|
||||
public NavigationGutterIconBuilder<T> setTarget(@Nullable T target) {
|
||||
@@ -193,9 +193,10 @@ public class NavigationGutterIconBuilder<T> {
|
||||
@NotNull
|
||||
public List<SmartPsiElementPointer> compute() {
|
||||
Set<PsiElement> elements = new THashSet<PsiElement>();
|
||||
final ArrayList<SmartPsiElementPointer> list = new ArrayList<SmartPsiElementPointer>();
|
||||
for (final T target : myTargets.getValue()) {
|
||||
for (final PsiElement psiElement : myConvertor.fun(target)) {
|
||||
Collection<? extends T> targets = myTargets.getValue();
|
||||
final List<SmartPsiElementPointer> list = new ArrayList<SmartPsiElementPointer>(targets.size());
|
||||
for (final T target : targets) {
|
||||
for (final PsiElement psiElement : myConverter.fun(target)) {
|
||||
if (elements.add(psiElement)) {
|
||||
list.add(manager.createSmartPsiElementPointer(psiElement));
|
||||
}
|
||||
@@ -205,7 +206,7 @@ public class NavigationGutterIconBuilder<T> {
|
||||
}
|
||||
};
|
||||
|
||||
final boolean empty = !myLazy && pointers.getValue().isEmpty();
|
||||
final boolean empty = isEmpty();
|
||||
|
||||
if (myTooltipText == null && !myLazy) {
|
||||
final SortedSet<String> names = new TreeSet<String>();
|
||||
@@ -233,6 +234,23 @@ public class NavigationGutterIconBuilder<T> {
|
||||
return new MyNavigationGutterIconRenderer(this, myAlignment, myIcon, myTooltipText, pointers, empty);
|
||||
}
|
||||
|
||||
private boolean isEmpty() {
|
||||
if (myLazy) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Set<PsiElement> elements = new THashSet<PsiElement>();
|
||||
Collection<? extends T> targets = myTargets.getValue();
|
||||
for (final T target : targets) {
|
||||
for (final PsiElement psiElement : myConverter.fun(target)) {
|
||||
if (elements.add(psiElement)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static class MyNavigationGutterIconRenderer extends NavigationGutterIconRenderer {
|
||||
private final Alignment myAlignment;
|
||||
private final Icon myIcon;
|
||||
|
||||
@@ -132,6 +132,10 @@ public abstract class ElementPresentationManager {
|
||||
return getElementName(o);
|
||||
}
|
||||
};
|
||||
public static <T> NullableFunction<T, String> namer() {
|
||||
//noinspection unchecked
|
||||
return (NullableFunction<T, String>)NAMER;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String getElementName(Object element) {
|
||||
|
||||
Reference in New Issue
Block a user