diff --git a/java/java-impl/src/com/intellij/psi/impl/smartPointers/AnchorElementInfo.java b/java/java-impl/src/com/intellij/psi/impl/smartPointers/AnchorElementInfo.java index 19bc73f98c66..06d3c27d4c48 100644 --- a/java/java-impl/src/com/intellij/psi/impl/smartPointers/AnchorElementInfo.java +++ b/java/java-impl/src/com/intellij/psi/impl/smartPointers/AnchorElementInfo.java @@ -16,10 +16,12 @@ package com.intellij.psi.impl.smartPointers; import com.intellij.openapi.util.TextRange; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; -import com.intellij.psi.PsiIdentifier; -import com.intellij.psi.PsiJavaCodeReferenceElement; +import com.intellij.psi.*; +import com.intellij.psi.impl.source.PsiFileImpl; +import com.intellij.psi.impl.source.PsiFileWithStubSupport; +import com.intellij.psi.stubs.IStubElementType; +import com.intellij.psi.tree.IElementType; +import com.intellij.psi.tree.IStubFileElementType; import com.intellij.psi.xml.XmlToken; import com.intellij.psi.xml.XmlTokenType; import org.jetbrains.annotations.NotNull; @@ -29,12 +31,31 @@ import org.jetbrains.annotations.Nullable; * User: cdr */ class AnchorElementInfo extends SelfElementInfo { - AnchorElementInfo(@NotNull PsiElement anchor, PsiFile containingFile) { - super(anchor, containingFile); + private int stubId = -1; + private IStubElementType myStubElementType; + + AnchorElementInfo(@NotNull PsiElement anchor, @NotNull PsiFile containingFile) { + super(containingFile.getProject(), anchor.getTextRange(), anchor.getClass(), containingFile); + } + // will restore by stub index until file tree get loaded + AnchorElementInfo(@NotNull PsiElement anchor, + @NotNull PsiFileWithStubSupport containingFile, + int stubId, + @NotNull IStubElementType stubElementType) { + super(containingFile.getProject(), new TextRange(0,0), anchor.getClass(), containingFile); + this.stubId = stubId; + myStubElementType = stubElementType; + IElementType contentElementType = ((PsiFileImpl)containingFile).getContentElementType(); + assert contentElementType instanceof IStubFileElementType : contentElementType; } @Nullable public PsiElement restoreElement() { + if (stubId != -1) { + PsiFile file = SelfElementInfo.restoreFileFromVirtual(myVirtualFile, myProject); + if (!(file instanceof PsiFileWithStubSupport)) return null; + return PsiAnchor.restoreFromStubIndex((PsiFileWithStubSupport)file, stubId, myStubElementType); + } if (!mySyncMarkerIsValid) return null; PsiFile file = SelfElementInfo.restoreFileFromVirtual(myVirtualFile, myProject); if (file == null) return null; @@ -60,4 +81,17 @@ class AnchorElementInfo extends SelfElementInfo { } return null; } + + @Override + public void fastenBelt(int offset) { + PsiElement element = restoreElement(); + if (element != null) { + // switch to tree + stubId = -1; + myStubElementType = null; + PsiElement anchor = AnchorElementInfoFactory.getAnchor(element); + setRange((anchor == null ? element : anchor).getTextRange()); + } + super.fastenBelt(offset); + } } diff --git a/java/java-impl/src/com/intellij/psi/impl/smartPointers/AnchorElementInfoFactory.java b/java/java-impl/src/com/intellij/psi/impl/smartPointers/AnchorElementInfoFactory.java index c84d8f341b3c..f0c451b52595 100644 --- a/java/java-impl/src/com/intellij/psi/impl/smartPointers/AnchorElementInfoFactory.java +++ b/java/java-impl/src/com/intellij/psi/impl/smartPointers/AnchorElementInfoFactory.java @@ -17,6 +17,9 @@ package com.intellij.psi.impl.smartPointers; import com.intellij.openapi.diagnostic.Logger; import com.intellij.psi.*; +import com.intellij.psi.impl.source.PsiFileWithStubSupport; +import com.intellij.psi.stubs.IStubElementType; +import com.intellij.psi.stubs.StubTree; import com.intellij.psi.xml.XmlTag; import com.intellij.xml.util.XmlTagUtil; import org.jetbrains.annotations.NotNull; @@ -27,9 +30,24 @@ public class AnchorElementInfoFactory implements SmartPointerElementInfoFactory @Nullable public SmartPointerElementInfo createElementInfo(@NotNull PsiElement element) { + PsiFile containingFile = element.getContainingFile(); + if (element instanceof StubBasedPsiElement && containingFile instanceof PsiFileWithStubSupport) { + PsiFileWithStubSupport stubFile = (PsiFileWithStubSupport)containingFile; + StubTree stubTree = stubFile.getStubTree(); + if (stubTree != null) { + // use stubs when tree is not loaded + StubBasedPsiElement stubPsi = (StubBasedPsiElement)element; + int stubId = PsiAnchor.calcStubIndex(stubPsi); + IStubElementType myStubElementType = stubPsi.getElementType(); + if (stubId != -1) { + return new AnchorElementInfo(element, stubFile, stubId, myStubElementType); + } + } + } + PsiElement anchor = getAnchor(element); if (anchor != null) { - return new AnchorElementInfo(anchor, element.getContainingFile()); + return new AnchorElementInfo(anchor, containingFile); } return null; } @@ -55,8 +73,7 @@ public class AnchorElementInfoFactory implements SmartPointerElementInfoFactory else if (element instanceof XmlTag) { anchor = XmlTagUtil.getStartTagNameElement((XmlTag)element); } - if (anchor != null && (!anchor.isPhysical() || anchor.getTextRange()==null)) return null; + if (anchor != null && (!anchor.isPhysical() /*|| anchor.getTextRange()==null*/)) return null; return anchor; } - } diff --git a/java/java-impl/src/com/intellij/psi/impl/smartPointers/ImplicitVariableElementInfoFactory.java b/java/java-impl/src/com/intellij/psi/impl/smartPointers/ImplicitVariableElementInfoFactory.java index 403cb17c8e06..37ee533d4a27 100644 --- a/java/java-impl/src/com/intellij/psi/impl/smartPointers/ImplicitVariableElementInfoFactory.java +++ b/java/java-impl/src/com/intellij/psi/impl/smartPointers/ImplicitVariableElementInfoFactory.java @@ -16,6 +16,7 @@ package com.intellij.psi.impl.smartPointers; 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.vfs.VirtualFile; @@ -29,17 +30,19 @@ import org.jetbrains.annotations.Nullable; public class ImplicitVariableElementInfoFactory implements SmartPointerElementInfoFactory { @Nullable public SmartPointerElementInfo createElementInfo(@NotNull final PsiElement element) { - if (element instanceof ImplicitVariable) { - return new ImplicitVariableInfo((ImplicitVariable) element); + if (element instanceof ImplicitVariable && element.isValid()) { + return new ImplicitVariableInfo((ImplicitVariable) element, element.getProject()); } return null; } private static class ImplicitVariableInfo implements SmartPointerElementInfo { private final ImplicitVariable myVar; + private final Project myProject; - public ImplicitVariableInfo(ImplicitVariable var) { + public ImplicitVariableInfo(@NotNull ImplicitVariable var, @NotNull Project project) { myVar = var; + myProject = project; } public PsiElement restoreElement() { @@ -87,10 +90,16 @@ public class ImplicitVariableElementInfoFactory implements SmartPointerElementIn } @Override - public Segment getSegment() { + public Segment getRange() { PsiIdentifier psiIdentifier = myVar.getNameIdentifier(); if (psiIdentifier == null || !psiIdentifier.isValid()) return null; return psiIdentifier.getTextRange(); } + + @NotNull + @Override + public Project getProject() { + return myProject; + } } } diff --git a/java/java-impl/src/com/intellij/refactoring/migration/MigrationProcessor.java b/java/java-impl/src/com/intellij/refactoring/migration/MigrationProcessor.java index 4113edcc80b7..9f3982fc2a26 100644 --- a/java/java-impl/src/com/intellij/refactoring/migration/MigrationProcessor.java +++ b/java/java-impl/src/com/intellij/refactoring/migration/MigrationProcessor.java @@ -151,8 +151,8 @@ class MigrationProcessor extends BaseRefactoringProcessor { public MigrationMapEntry mapEntry; public MigrationUsageInfo(UsageInfo info, MigrationMapEntry mapEntry) { - super(info.getElement(), info.startOffset, info.endOffset); + super(info.getElement(), info.getRangeInElement().getStartOffset(), info.getRangeInElement().getEndOffset()); this.mapEntry = mapEntry; } } -} \ No newline at end of file +} diff --git a/java/java-impl/src/com/intellij/refactoring/migration/MigrationUtil.java b/java/java-impl/src/com/intellij/refactoring/migration/MigrationUtil.java index a77ad19d6818..a8a202f8aa4b 100644 --- a/java/java-impl/src/com/intellij/refactoring/migration/MigrationUtil.java +++ b/java/java-impl/src/com/intellij/refactoring/migration/MigrationUtil.java @@ -66,7 +66,7 @@ public class MigrationUtil { } private static void bindNonJavaReference(PsiElement bindTo, PsiElement element, UsageInfo usage) { - final TextRange range = usage.getRange(); + final TextRange range = usage.getRangeInElement(); for (PsiReference reference : element.getReferences()) { if (reference instanceof JavaClassReference) { final JavaClassReference classReference = (JavaClassReference)reference; diff --git a/java/java-impl/src/com/intellij/refactoring/typeMigration/usageInfo/TypeMigrationUsageInfo.java b/java/java-impl/src/com/intellij/refactoring/typeMigration/usageInfo/TypeMigrationUsageInfo.java index dc032be05368..a93b7434e9ee 100644 --- a/java/java-impl/src/com/intellij/refactoring/typeMigration/usageInfo/TypeMigrationUsageInfo.java +++ b/java/java-impl/src/com/intellij/refactoring/typeMigration/usageInfo/TypeMigrationUsageInfo.java @@ -4,7 +4,6 @@ */ package com.intellij.refactoring.typeMigration.usageInfo; -import com.intellij.openapi.util.Comparing; import com.intellij.psi.PsiElement; import com.intellij.usageView.UsageInfo; import org.jetbrains.annotations.NotNull; @@ -17,19 +16,6 @@ public class TypeMigrationUsageInfo extends UsageInfo { super(element); } - @Override - public boolean equals(final Object o) { - if (this == o) return true; - if (!(o instanceof TypeMigrationUsageInfo)) return false; - - final UsageInfo usageInfo = (UsageInfo)o; - - if (endOffset != usageInfo.endOffset) return false; - if (isNonCodeUsage != usageInfo.isNonCodeUsage) return false; - if (startOffset != usageInfo.startOffset) return false; - return Comparing.equal(getElement(), usageInfo.getElement()); - } - public void setExcluded(final boolean excluded) { myExcluded = excluded; } @@ -38,4 +24,4 @@ public class TypeMigrationUsageInfo extends UsageInfo { return myExcluded; } -} \ No newline at end of file +} diff --git a/java/java-impl/src/com/intellij/slicer/DuplicateMap.java b/java/java-impl/src/com/intellij/slicer/DuplicateMap.java index 3d8c79500349..f22cd07fc0a7 100644 --- a/java/java-impl/src/com/intellij/slicer/DuplicateMap.java +++ b/java/java-impl/src/com/intellij/slicer/DuplicateMap.java @@ -28,7 +28,7 @@ public class DuplicateMap { private static final TObjectHashingStrategy USAGEINFO_EQUALITY = new TObjectHashingStrategy() { public int computeHashCode(SliceUsage object) { UsageInfo info = object.getUsageInfo(); - return info.startOffset + info.endOffset * 31; + return info.getRangeInElement().hashCode(); } public boolean equals(SliceUsage o1, SliceUsage o2) { diff --git a/java/java-impl/src/com/intellij/usages/impl/rules/MethodGroupingRule.java b/java/java-impl/src/com/intellij/usages/impl/rules/MethodGroupingRule.java index c36921b48c70..6f01fe61a905 100644 --- a/java/java-impl/src/com/intellij/usages/impl/rules/MethodGroupingRule.java +++ b/java/java-impl/src/com/intellij/usages/impl/rules/MethodGroupingRule.java @@ -151,8 +151,8 @@ public class MethodGroupingRule implements UsageGroupingRule { return 0; } if (!UsageViewSettings.getInstance().IS_SORT_MEMBERS_ALPHABETICALLY) { - Segment segment1 = myMethodPointer.getSegment(); - Segment segment2 = other.myMethodPointer.getSegment(); + Segment segment1 = myMethodPointer.getRange(); + Segment segment2 = other.myMethodPointer.getRange(); if (segment1 != null && segment2 != null) { return segment1.getStartOffset() - segment2.getStartOffset(); } diff --git a/platform/lang-api/src/com/intellij/psi/IdentitySmartPointer.java b/platform/lang-api/src/com/intellij/psi/IdentitySmartPointer.java index 03cd2de5a16f..bed08c02efde 100644 --- a/platform/lang-api/src/com/intellij/psi/IdentitySmartPointer.java +++ b/platform/lang-api/src/com/intellij/psi/IdentitySmartPointer.java @@ -70,7 +70,7 @@ public class IdentitySmartPointer implements SmartPsiEleme } @Override - public Segment getSegment() { + public Segment getRange() { T element = myElement; return element == null ? null : element.getTextRange(); } diff --git a/platform/lang-api/src/com/intellij/psi/SmartPointerManager.java b/platform/lang-api/src/com/intellij/psi/SmartPointerManager.java index b67cbcb28a4c..dde78cba4f08 100644 --- a/platform/lang-api/src/com/intellij/psi/SmartPointerManager.java +++ b/platform/lang-api/src/com/intellij/psi/SmartPointerManager.java @@ -17,6 +17,7 @@ package com.intellij.psi; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.TextRange; import org.jetbrains.annotations.NotNull; /** @@ -24,6 +25,9 @@ import org.jetbrains.annotations.NotNull; * element in the PSI tree after the reparse. */ public abstract class SmartPointerManager { + @NotNull + public abstract SmartPsiFileRange createSmartPsiFileRangePointer(@NotNull PsiFile file, @NotNull TextRange range); + public static SmartPointerManager getInstance(Project project) { return ServiceManager.getService(project, SmartPointerManager.class); } diff --git a/platform/lang-api/src/com/intellij/psi/SmartPsiElementPointer.java b/platform/lang-api/src/com/intellij/psi/SmartPsiElementPointer.java index 66e847d60439..13aae3b5b91a 100644 --- a/platform/lang-api/src/com/intellij/psi/SmartPsiElementPointer.java +++ b/platform/lang-api/src/com/intellij/psi/SmartPsiElementPointer.java @@ -46,5 +46,5 @@ public interface SmartPsiElementPointer { VirtualFile getVirtualFile(); - Segment getSegment(); + Segment getRange(); } diff --git a/platform/lang-api/src/com/intellij/psi/SmartPsiFileRange.java b/platform/lang-api/src/com/intellij/psi/SmartPsiFileRange.java new file mode 100644 index 000000000000..2b2c59bed935 --- /dev/null +++ b/platform/lang-api/src/com/intellij/psi/SmartPsiFileRange.java @@ -0,0 +1,29 @@ +/* + * 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; + +import com.intellij.openapi.util.Segment; + +/** + * pointer to a PsiFile + range inside the file. + * {@link #getRange()} will return the range inside the PsiFile + */ +public interface SmartPsiFileRange extends SmartPsiElementPointer { + /** + * @return the range inside the PsiFile, or null if the range or PsiFile became invalid + */ + Segment getRange(); +} diff --git a/platform/lang-api/src/com/intellij/refactoring/util/NonCodeUsageInfo.java b/platform/lang-api/src/com/intellij/refactoring/util/NonCodeUsageInfo.java index abc4264b5909..549275dea766 100644 --- a/platform/lang-api/src/com/intellij/refactoring/util/NonCodeUsageInfo.java +++ b/platform/lang-api/src/com/intellij/refactoring/util/NonCodeUsageInfo.java @@ -61,6 +61,6 @@ public class NonCodeUsageInfo extends MoveRenameUsageInfo{ } public NonCodeUsageInfo replaceElement(PsiElement newElement) { - return new NonCodeUsageInfo(newElement, startOffset, endOffset, getReferencedElement(), newText); + return new NonCodeUsageInfo(newElement, getRangeInElement().getStartOffset(), getRangeInElement().getEndOffset(), getReferencedElement(), newText); } } diff --git a/platform/lang-api/src/com/intellij/usageView/UsageInfo.java b/platform/lang-api/src/com/intellij/usageView/UsageInfo.java index b149bf276776..0ad8d47ebf87 100644 --- a/platform/lang-api/src/com/intellij/usageView/UsageInfo.java +++ b/platform/lang-api/src/com/intellij/usageView/UsageInfo.java @@ -20,6 +20,7 @@ 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; @@ -31,8 +32,9 @@ 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; - public final int startOffset; // in navigation element - public final int endOffset; // in navigation element + + private final int startOffset; // in navigation element + private final int endOffset; // in navigation element public final boolean isNonCodeUsage; @@ -40,7 +42,6 @@ public class UsageInfo { LOG.assertTrue(element.isValid()); element = element.getNavigationElement(); SmartPointerManager smartPointerManager = SmartPointerManager.getInstance(element.getProject()); - mySmartPointer = smartPointerManager.createSmartPsiElementPointer(element); if (startOffset == -1 && endOffset == -1) { // calculate natural element range @@ -51,11 +52,22 @@ public class UsageInfo { startOffset = element.getTextOffset() - range.getStartOffset(); endOffset = range.getEndOffset() - range.getStartOffset(); } - this.startOffset = startOffset; - this.endOffset = endOffset; - this.isNonCodeUsage = isNonCodeUsage; + LOG.assertTrue(startOffset >= 0, startOffset); LOG.assertTrue(endOffset >= startOffset, endOffset-startOffset); + + if (element instanceof PsiFile) { + PsiFile file = (PsiFile)element; + mySmartPointer = smartPointerManager.createSmartPsiFileRangePointer(file, TextRange.create(startOffset, endOffset)); + this.startOffset = -1; + this.endOffset = -1; + } + else { + this.startOffset = startOffset; + this.endOffset = endOffset; + mySmartPointer = smartPointerManager.createSmartPsiElementPointer(element); + } + this.isNonCodeUsage = isNonCodeUsage; } public UsageInfo(@NotNull PsiElement element, boolean isNonCodeUsage) { @@ -85,10 +97,27 @@ public class UsageInfo { @Nullable public PsiReference getReference() { - return getElement().getReference(); + PsiElement element = getElement(); + return element == null ? null : element.getReference(); } + /** + * @deprecated for the range in element use {@link #getRangeInElement} instead, + * for the whole text range in the file covered by this usage info, use {@link #getSegment()} + */ public TextRange getRange() { + return getRangeInElement(); + } + + /** + * @return range in element + */ + @Nullable("null means range is invalid") + public TextRange getRangeInElement() { + if (mySmartPointer instanceof SmartPsiFileRange) { + return TextRange.create(mySmartPointer.getRange()); + } + return new TextRange(startOffset, endOffset); } @@ -110,13 +139,20 @@ public class UsageInfo { PsiElement element = getElement(); if (element == null) return -1; TextRange range = element.getTextRange(); - return range.getStartOffset() + startOffset; + + TextRange rangeInElement = getRangeInElement(); + if (rangeInElement == null) return -1; + return range.getStartOffset() + rangeInElement.getStartOffset(); } + 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)); + TextRange rangeInElement = getRangeInElement(); + if (rangeInElement == null) return null; + return new TextRange(range.getStartOffset() + rangeInElement.getStartOffset(), + Math.min(range.getEndOffset(), range.getStartOffset() + rangeInElement.getEndOffset())); } public Project getProject() { @@ -136,15 +172,15 @@ public class UsageInfo { if (endOffset != usageInfo.endOffset) return false; if (isNonCodeUsage != usageInfo.isNonCodeUsage) return false; - if (startOffset != usageInfo.startOffset) return false; + if (!Comparing.equal(getRangeInElement(), usageInfo.getRangeInElement())) return false; return SmartPointerManager.getInstance(getProject()).pointToTheSameElement(mySmartPointer, usageInfo.mySmartPointer); } public int hashCode() { int result = mySmartPointer != null ? mySmartPointer.hashCode() : 0; - result = 29 * result + startOffset; - result = 29 * result + endOffset; + TextRange rangeInElement = getRangeInElement(); + result = 29 * result + (rangeInElement == null ? 0 : rangeInElement.hashCode()); result = 29 * result + (isNonCodeUsage ? 1 : 0); return result; } diff --git a/platform/lang-impl/src/com/intellij/find/actions/ShowUsagesAction.java b/platform/lang-impl/src/com/intellij/find/actions/ShowUsagesAction.java index 416042161f59..6c6a402feb2f 100644 --- a/platform/lang-impl/src/com/intellij/find/actions/ShowUsagesAction.java +++ b/platform/lang-impl/src/com/intellij/find/actions/ShowUsagesAction.java @@ -202,8 +202,7 @@ public class ShowUsagesAction extends AnAction { final List usages = new ArrayList(); final Set visibleNodes = new LinkedHashSet(); Processor collect = new Processor() { - final UsageTarget[] myUsageTarget = {new PsiElement2UsageTargetAdapter(handler.getPsiElement())}; - + private final UsageTarget[] myUsageTarget = {new PsiElement2UsageTargetAdapter(handler.getPsiElement())}; public boolean process(@NotNull Usage usage) { synchronized (usages) { if (visibleNodes.size() > maxUsages) return false; diff --git a/platform/lang-impl/src/com/intellij/psi/PsiAnchor.java b/platform/lang-impl/src/com/intellij/psi/PsiAnchor.java index 808dfd9ec6e4..91a6103b95f5 100644 --- a/platform/lang-impl/src/com/intellij/psi/PsiAnchor.java +++ b/platform/lang-impl/src/com/intellij/psi/PsiAnchor.java @@ -95,7 +95,7 @@ public abstract class PsiAnchor { return new TreeRangeReference(file, textRange.getStartOffset(), textRange.getEndOffset(), element.getClass(), lang, virtualFile); } - private static int calcStubIndex(StubBasedPsiElement psi) { + public static int calcStubIndex(StubBasedPsiElement psi) { if (psi instanceof PsiFile) { return 0; } @@ -238,6 +238,41 @@ public abstract class PsiAnchor { } } + public static PsiElement restoreFromStubIndex(PsiFileWithStubSupport fileImpl, + int index, + IStubElementType elementType) { + if (fileImpl == null) return null; + StubTree tree = fileImpl.getStubTree(); + + boolean foreign = tree == null; + if (foreign) { + if (fileImpl instanceof PsiFileImpl) { + // Note: as far as this is a realization of StubIndexReference fileImpl#getContentElementType() must be instance of IStubFileElementType + tree = ((PsiFileImpl)fileImpl).calcStubTree(); + } + else { + return null; + } + } + + List> list = tree.getPlainList(); + if (index >= list.size()) return null; + StubElement stub = list.get(index); + + if (stub.getStubType() != elementType) return null; + + if (foreign) { + final PsiElement cachedPsi = ((StubBase)stub).getCachedPsi(); + if (cachedPsi != null) return cachedPsi; + + final ASTNode ast = fileImpl.findTreeForStub(tree, stub); + return ast != null ? ast.getPsi() : null; + } + else { + return stub.getPsi(); + } + } + private static class StubIndexReference extends PsiAnchor { private final VirtualFile myVirtualFile; private final Project myProject; @@ -271,37 +306,7 @@ public abstract class PsiAnchor { public PsiElement retrieve() { return ApplicationManager.getApplication().runReadAction(new NullableComputable() { public PsiElement compute() { - PsiFileWithStubSupport fileImpl = (PsiFileWithStubSupport)getFile(); - if (fileImpl == null) return null; - StubTree tree = fileImpl.getStubTree(); - - boolean foreign = tree == null; - if (foreign) { - if (fileImpl instanceof PsiFileImpl) { - // Note: as far as this is a realization of StubIndexReference fileImpl#getContentElementType() must be instance of IStubFileElementType - tree = ((PsiFileImpl)fileImpl).calcStubTree(); - } - else { - return null; - } - } - - List> list = tree.getPlainList(); - if (myIndex >= list.size()) return null; - StubElement stub = list.get(myIndex); - - if (stub.getStubType() != myElementType) return null; - - if (foreign) { - final PsiElement cachedPsi = ((StubBase)stub).getCachedPsi(); - if (cachedPsi != null) return cachedPsi; - - final ASTNode ast = fileImpl.findTreeForStub(tree, stub); - return ast != null ? ast.getPsi() : null; - } - else { - return stub.getPsi(); - } + return restoreFromStubIndex((PsiFileWithStubSupport)getFile(), myIndex, myElementType); } }); } diff --git a/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/FileElementInfo.java b/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/FileElementInfo.java index 67500e1be06b..869c49becbfd 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/FileElementInfo.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/FileElementInfo.java @@ -79,7 +79,13 @@ class FileElementInfo implements SmartPointerElementInfo { } @Override - public Segment getSegment() { + public Segment getRange() { return new TextRange(0, (int)myVirtualFile.getLength()); } + + @NotNull + @Override + public Project getProject() { + return myProject; + } } diff --git a/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/HardElementInfo.java b/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/HardElementInfo.java index 3627aeb1b0e9..0573a30cc0bb 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/HardElementInfo.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/HardElementInfo.java @@ -16,6 +16,7 @@ package com.intellij.psi.impl.smartPointers; 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.vfs.VirtualFile; @@ -28,9 +29,11 @@ import org.jetbrains.annotations.NotNull; */ class HardElementInfo implements SmartPointerElementInfo { private final PsiElement myElement; + private final Project myProject; - public HardElementInfo(@NotNull PsiElement element) { + public HardElementInfo(@NotNull Project project, @NotNull PsiElement element) { myElement = element; + myProject = project; } public Document getDocumentToSynchronize() { @@ -72,7 +75,13 @@ class HardElementInfo implements SmartPointerElementInfo { } @Override - public Segment getSegment() { + public Segment getRange() { return myElement.getTextRange(); } + + @NotNull + @Override + public Project getProject() { + return myProject; + } } diff --git a/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/InjectedSelfElementInfo.java b/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/InjectedSelfElementInfo.java index 88143db18c5c..50304ea9ee94 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/InjectedSelfElementInfo.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/InjectedSelfElementInfo.java @@ -17,6 +17,7 @@ package com.intellij.psi.impl.smartPointers; import com.intellij.injected.editor.DocumentWindow; import com.intellij.openapi.editor.Document; +import com.intellij.openapi.project.Project; import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiElement; @@ -29,14 +30,14 @@ import org.jetbrains.annotations.NotNull; class InjectedSelfElementInfo extends SelfElementInfo { private DocumentWindow myDocument; - InjectedSelfElementInfo(@NotNull PsiElement anchor, PsiFile containingFile) { - super(anchor, containingFile); + InjectedSelfElementInfo(@NotNull Project project, @NotNull PsiElement anchor, @NotNull PsiFile containingFile) { + super(project, anchor.getTextRange(), anchor.getClass(), containingFile); assert containingFile.getContext() != null; } - protected TextRange getPersistentAnchorRange(final PsiElement anchor) { - final TextRange textRange = super.getPersistentAnchorRange(anchor); - Document document = PsiDocumentManager.getInstance(anchor.getProject()).getDocument(anchor.getContainingFile()); + protected TextRange getPersistentAnchorRange(final TextRange anchor, PsiFile containingFile) { + final TextRange textRange = super.getPersistentAnchorRange(anchor, containingFile); + Document document = PsiDocumentManager.getInstance(containingFile.getProject()).getDocument(containingFile); // must be non-text file if (!(document instanceof DocumentWindow)) return textRange; diff --git a/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/LazyPointerImpl.java b/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/LazyPointerImpl.java index c22b15b37e3f..f38c9b1d4466 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/LazyPointerImpl.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/LazyPointerImpl.java @@ -141,11 +141,11 @@ public class LazyPointerImpl implements SmartPointerEx } @Override - public Segment getSegment() { + public Segment getRange() { E element = myElement; if (element != null && element.isValid()) return element.getTextRange(); SmartPsiElementPointer pointer = myPointer; - if (pointer != null) return pointer.getSegment(); + if (pointer != null) return pointer.getRange(); PsiAnchor anchor = myAnchor; if (anchor != null) { final PsiElement psiElement = anchor.retrieve(); diff --git a/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/SelfElementInfo.java b/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/SelfElementInfo.java index 2975a3c156c3..9a17e9d27c81 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/SelfElementInfo.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/SelfElementInfo.java @@ -44,22 +44,21 @@ public class SelfElementInfo implements SmartPointerElementInfo { private int mySyncStartOffset; private int mySyncEndOffset; protected boolean mySyncMarkerIsValid; - private Class myType; + private final Class myType; protected final Project myProject; + @SuppressWarnings({"UnusedDeclaration"}) private RangeMarker myRangeMarker; //maintain hard reference during modification - public SelfElementInfo(@NotNull PsiElement anchor, PsiFile containingFile) { - LOG.assertTrue(anchor.isPhysical()); - LOG.assertTrue(anchor.isValid()); - PsiFile file = anchor.getContainingFile(); - myVirtualFile = file.getVirtualFile(); - TextRange range = anchor.getTextRange(); + public SelfElementInfo(@NotNull Project project, @NotNull TextRange anchor, @NotNull Class anchorClass, @NotNull PsiFile containingFile) { + myVirtualFile = containingFile.getVirtualFile(); + myType = anchorClass; + TextRange range = anchor;//.getTextRange(); LOG.assertTrue(range != null, anchor); - range = getPersistentAnchorRange(anchor); + range = getPersistentAnchorRange(range, containingFile); - myProject = file.getProject(); + myProject = project; PsiDocumentManager documentManager = PsiDocumentManager.getInstance(myProject); - Document document = documentManager.getDocument(file); + Document document = documentManager.getDocument(containingFile); if (document == null || documentManager.isUncommited(document)) { mySyncMarkerIsValid = false; return; @@ -82,19 +81,17 @@ public class SelfElementInfo implements SmartPointerElementInfo { // LOG.error("File=" + file); //} - if (documentManager.isUncommited(document)) { - mySyncMarkerIsValid = false; - } - else { - mySyncMarkerIsValid = true; - myType = anchor.getClass(); - } + mySyncMarkerIsValid = true; + setRange(range); + } + + protected void setRange(TextRange range) { mySyncStartOffset = range.getStartOffset(); mySyncEndOffset = range.getEndOffset(); } - protected TextRange getPersistentAnchorRange(final PsiElement anchor) { - return anchor.getTextRange(); + protected TextRange getPersistentAnchorRange(final TextRange anchor, PsiFile containingFile) { + return anchor; } public Document getDocumentToSynchronize() { @@ -128,14 +125,15 @@ public class SelfElementInfo implements SmartPointerElementInfo { 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); + marker = null; } + myRangeMarker = marker; //make sure marker wont be gced } // after change @@ -267,8 +265,14 @@ public class SelfElementInfo implements SmartPointerElementInfo { } @Override - public Segment getSegment() { + public Segment getRange() { if (!mySyncMarkerIsValid) return null; return new TextRange(getSyncStartOffset(), getSyncEndOffset()); } + + @NotNull + @Override + public Project getProject() { + return myProject; + } } diff --git a/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/SmartPointerElementInfo.java b/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/SmartPointerElementInfo.java index 6c0e37657cbb..e7676494b293 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/SmartPointerElementInfo.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/SmartPointerElementInfo.java @@ -17,9 +17,11 @@ package com.intellij.psi.impl.smartPointers; import com.intellij.openapi.editor.Document; +import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Segment; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiElement; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; interface SmartPointerElementInfo { @@ -39,5 +41,6 @@ interface SmartPointerElementInfo { boolean pointsToTheSameElementAs(SmartPointerElementInfo other); VirtualFile getVirtualFile(); - Segment getSegment(); + Segment getRange(); + @NotNull Project getProject(); } diff --git a/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/SmartPointerManagerImpl.java b/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/SmartPointerManagerImpl.java index 70bd6e2f8808..62d6375086c6 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/SmartPointerManagerImpl.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/SmartPointerManagerImpl.java @@ -22,6 +22,7 @@ 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.openapi.util.TextRange; import com.intellij.psi.*; import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil; import org.jetbrains.annotations.NotNull; @@ -154,15 +155,28 @@ public class SmartPointerManagerImpl extends SmartPointerManager { } PsiFile containingFile = element.getContainingFile(); SmartPointerEx pointer = new SmartPsiElementPointerImpl(myProject, element, containingFile); - initPointer(element, pointer, containingFile); + initPointer(pointer, containingFile); return pointer; } - private void initPointer(E element, SmartPointerEx pointer, PsiFile containingFile) { + @Override + @NotNull + public SmartPsiFileRange createSmartPsiFileRangePointer(@NotNull PsiFile file, @NotNull TextRange range) { + if (!file.isValid()) { + LOG.error("Invalid element:" + file); + } + + SmartPsiFileRangePointerImpl pointer = new SmartPsiFileRangePointerImpl(file, range); + initPointer(pointer, file); + + return pointer; + } + + private void initPointer(SmartPointerEx pointer, PsiFile containingFile) { if (containingFile == null) return; synchronized (containingFile) { - Document document = PsiDocumentManager.getInstance(myProject).getCachedDocument(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 diff --git a/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/SmartPsiElementPointerImpl.java b/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/SmartPsiElementPointerImpl.java index 7893c8f4c496..b8ad64db3cb9 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/SmartPsiElementPointerImpl.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/SmartPsiElementPointerImpl.java @@ -28,23 +28,25 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.lang.ref.Reference; -import java.lang.ref.WeakReference; +import java.lang.ref.SoftReference; class SmartPsiElementPointerImpl implements SmartPointerEx { private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.smartPointers.SmartPsiElementPointerImpl"); private Reference myElement; private final SmartPointerElementInfo myElementInfo; - private final Project myProject; private final Class myElementClass; - public SmartPsiElementPointerImpl(@NotNull Project project, @NotNull E element, PsiFile containingFile) { - myProject = project; + public SmartPsiElementPointerImpl(@NotNull Project project, @NotNull E element, @Nullable PsiFile containingFile) { + this(element, createElementInfo(project, element, containingFile), element.getClass()); + } + public SmartPsiElementPointerImpl(@NotNull E element, + @NotNull SmartPointerElementInfo elementInfo, + @NotNull Class elementClass) { ApplicationManager.getApplication().assertReadAccessAllowed(); - myElement = new WeakReference(element); - myElementInfo = createElementInfo(element, containingFile); - myElementClass = element.getClass(); - + cacheElement(element); + myElementClass = elementClass; + myElementInfo = elementInfo; // Assert document committed. //todo //if (containingFile != null) { @@ -64,7 +66,7 @@ class SmartPsiElementPointerImpl implements SmartPointerEx public boolean equals(Object obj) { if (!(obj instanceof SmartPsiElementPointer)) return false; SmartPsiElementPointer pointer = (SmartPsiElementPointer)obj; - return SmartPointerManager.getInstance(myProject).pointToTheSameElement(this, pointer); + return SmartPointerManager.getInstance(getProject()).pointToTheSameElement(this, pointer); } public int hashCode() { @@ -73,29 +75,34 @@ class SmartPsiElementPointerImpl implements SmartPointerEx @NotNull public Project getProject() { - return myProject; + return myElementInfo.getProject(); } @Nullable public E getElement() { - PsiElement element = getCachedElement(); + E element = getCachedElement(); if (element != null && !element.isValid()) { element = null; } if (element == null && myElementInfo != null) { - element = myElementInfo.restoreElement(); + element = (E)myElementInfo.restoreElement(); if (element != null && (!element.getClass().equals(myElementClass) || !element.isValid())) { element = null; } - myElement = element == null ? null : new WeakReference(element); + cacheElement(element); } - return (E)element; + return element; + } + + private void cacheElement(E element) { + myElement = element == null ? null : new SoftReference(element); } private E getCachedElement() { - return myElement == null ? null : myElement.get(); + Reference ref = myElement; + return ref == null ? null : ref.get(); } public PsiFile getContainingFile() { @@ -114,7 +121,7 @@ class SmartPsiElementPointerImpl implements SmartPointerEx final E resolved = getElement(); return resolved != null ? resolved.getContainingFile() : null; } - return PsiDocumentManager.getInstance(myProject).getPsiFile(doc); + return PsiDocumentManager.getInstance(getProject()).getPsiFile(doc); } public VirtualFile getVirtualFile() { @@ -122,21 +129,23 @@ class SmartPsiElementPointerImpl implements SmartPointerEx } @Override - public Segment getSegment() { - return myElementInfo.getSegment(); + public Segment getRange() { + return myElementInfo.getRange(); } @NotNull - private SmartPointerElementInfo createElementInfo(@NotNull E element, PsiFile containingFile) { - if (element instanceof PsiCompiledElement || !element.isPhysical() || containingFile == null || element.getTextRange() == null) { - return new HardElementInfo(element); + private static SmartPointerElementInfo createElementInfo(@NotNull Project project, @NotNull E element, PsiFile containingFile) { + if (element instanceof PsiCompiledElement || !element.isPhysical() || containingFile == null) { + return new HardElementInfo(project, element); } for(SmartPointerElementInfoFactory factory: Extensions.getExtensions(SmartPointerElementInfoFactory.EP_NAME)) { final SmartPointerElementInfo result = factory.createElementInfo(element); - if (result != null) { - return result; - } + if (result != null) return result; + } + + if (element.getTextRange() == null) { + return new HardElementInfo(project, element); } if (element instanceof PsiFile) { @@ -144,10 +153,13 @@ class SmartPsiElementPointerImpl implements SmartPointerEx } if (containingFile.getContext() != null) { - return new InjectedSelfElementInfo(element, containingFile); + return new InjectedSelfElementInfo(project, element, containingFile); } - return new SelfElementInfo(element, containingFile); + LOG.assertTrue(element.isPhysical()); + LOG.assertTrue(element.isValid()); + + return new SelfElementInfo(project, element.getTextRange(), element.getClass(), containingFile); } public void documentAndPsiInSync() { diff --git a/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/SmartPsiFileRangePointerImpl.java b/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/SmartPsiFileRangePointerImpl.java new file mode 100644 index 000000000000..a1b929d06c6a --- /dev/null +++ b/platform/lang-impl/src/com/intellij/psi/impl/smartPointers/SmartPsiFileRangePointerImpl.java @@ -0,0 +1,41 @@ +/* + * 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.util.TextRange; +import com.intellij.psi.PsiFile; +import com.intellij.psi.SmartPsiFileRange; +import org.jetbrains.annotations.NotNull; + +/** + * User: cdr + */ +public class SmartPsiFileRangePointerImpl extends SmartPsiElementPointerImpl implements SmartPsiFileRange { + public SmartPsiFileRangePointerImpl(@NotNull PsiFile containingFile, @NotNull TextRange range) { + super(containingFile, createElementInfo(containingFile, range), PsiFile.class); + } + + @NotNull + private static SmartPointerElementInfo createElementInfo(@NotNull PsiFile containingFile, @NotNull TextRange range) { + return new SelfElementInfo(containingFile.getProject(), range, PsiFile.class, containingFile); + } + + @Override + public PsiFile getElement() { + if (getRange() == null) return null; // range is invalid + return super.getElement(); + } +} diff --git a/platform/lang-impl/src/com/intellij/refactoring/move/moveFilesOrDirectories/MoveFilesOrDirectoriesProcessor.java b/platform/lang-impl/src/com/intellij/refactoring/move/moveFilesOrDirectories/MoveFilesOrDirectoriesProcessor.java index d52f2c8c25c9..04592aca483d 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/move/moveFilesOrDirectories/MoveFilesOrDirectoriesProcessor.java +++ b/platform/lang-impl/src/com/intellij/refactoring/move/moveFilesOrDirectories/MoveFilesOrDirectoriesProcessor.java @@ -168,7 +168,7 @@ public class MoveFilesOrDirectoriesProcessor extends BaseRefactoringProcessor { // sort by offset descending to process correctly several usages in one PsiElement [IDEADEV-33013] Arrays.sort(usages, new Comparator() { public int compare(final UsageInfo o1, final UsageInfo o2) { - return o1.getElement() == o2.getElement() ? o2.startOffset - o1.startOffset : 0; + return o1.getElement() == o2.getElement() ? o2.getRangeInElement().getStartOffset() - o1.getRangeInElement().getStartOffset() : 0; } }); diff --git a/platform/lang-impl/src/com/intellij/refactoring/rename/RenameUtil.java b/platform/lang-impl/src/com/intellij/refactoring/rename/RenameUtil.java index 8509b9b674d0..ee01fe58425c 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/rename/RenameUtil.java +++ b/platform/lang-impl/src/com/intellij/refactoring/rename/RenameUtil.java @@ -274,14 +274,14 @@ public class RenameUtil { if (element == null) continue; final PsiFile containingFile = element.getContainingFile(); final Document document = psiDocumentManager.getDocument(containingFile); - int fileOffset = element.getTextRange().getStartOffset() + usage.startOffset; + int fileOffset = usage.getSegment().getStartOffset(); ArrayList list = docsToOffsetsMap.get(document); if (list == null) { list = new ArrayList(); docsToOffsetsMap.put(document, list); } - list.add(new UsageOffset(fileOffset, fileOffset + usage.endOffset - usage.startOffset, usage.newText)); + list.add(new UsageOffset(fileOffset, fileOffset + usage.getRangeInElement().getLength(), usage.newText)); } for (Document document : docsToOffsetsMap.keySet()) { diff --git a/platform/lang-impl/src/com/intellij/usageView/UsageViewUtil.java b/platform/lang-impl/src/com/intellij/usageView/UsageViewUtil.java index cc74f8e8e146..9c4a7e2123a0 100644 --- a/platform/lang-impl/src/com/intellij/usageView/UsageViewUtil.java +++ b/platform/lang-impl/src/com/intellij/usageView/UsageViewUtil.java @@ -21,6 +21,7 @@ import com.intellij.lang.findUsages.FindUsagesProvider; import com.intellij.lang.findUsages.LanguageFindUsages; import com.intellij.lang.injection.InjectedLanguageManager; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.ElementDescriptionUtil; import com.intellij.psi.PsiElement; @@ -120,11 +121,12 @@ public class UsageViewUtil { if (psiReference == null) continue; int injectionOffsetInMasterFile = InjectedLanguageManager.getInstance(usageElement.getProject()).injectedToHost(usageElement, usageElement.getTextOffset()); + TextRange range = usage.getRangeInElement().shiftRight(injectionOffsetInMasterFile); set.remove( NonCodeUsageInfo.create( context.getContainingFile(), - usage.startOffset + injectionOffsetInMasterFile, - usage.endOffset + injectionOffsetInMasterFile, + range.getStartOffset(), + range.getEndOffset(), ((MoveRenameUsageInfo)usage).getReferencedElement(), newTextInNonCodeUsage ) @@ -135,4 +137,4 @@ public class UsageViewUtil { } return set.toArray(new UsageInfo[set.size()]); } -} \ No newline at end of file +} diff --git a/platform/usageView/src/com/intellij/usages/ChunkExtractor.java b/platform/usageView/src/com/intellij/usages/ChunkExtractor.java index 4d6ea46f2d64..ca9ec6d39b1c 100644 --- a/platform/usageView/src/com/intellij/usages/ChunkExtractor.java +++ b/platform/usageView/src/com/intellij/usages/ChunkExtractor.java @@ -123,7 +123,7 @@ public class ChunkExtractor { private TextChunk[] extractChunks(UsageInfo2UsageAdapter usageInfo2UsageAdapter) { int absoluteStartOffset = usageInfo2UsageAdapter.getNavigationOffset(); - assert absoluteStartOffset != -1; + if (absoluteStartOffset == -1) return TextChunk.EMPTY_ARRAY; final int lineNumber = myDocument.getLineNumber(absoluteStartOffset); final int columnNumber = absoluteStartOffset - myDocument.getLineStartOffset(lineNumber); diff --git a/platform/usageView/src/com/intellij/usages/UsageInfo2UsageAdapter.java b/platform/usageView/src/com/intellij/usages/UsageInfo2UsageAdapter.java index ebb303d7a190..e45aef42e342 100644 --- a/platform/usageView/src/com/intellij/usages/UsageInfo2UsageAdapter.java +++ b/platform/usageView/src/com/intellij/usages/UsageInfo2UsageAdapter.java @@ -146,7 +146,7 @@ public class UsageInfo2UsageAdapter implements UsageInModule, FileEditor editor = FileEditorManager.getInstance(getProject()).getSelectedEditor(virtualFile); if (!(editor instanceof TextEditor)) return null; - return new TextEditorLocation(getUsageInfo().startOffset + getElement().getTextRange().getStartOffset(), (TextEditor)editor); + return new TextEditorLocation(getUsageInfo().getSegment().getStartOffset(), (TextEditor)editor); } public void selectInEditor() { @@ -283,6 +283,7 @@ public class UsageInfo2UsageAdapter implements UsageInModule, public boolean merge(MergeableUsage other) { if (!(other instanceof UsageInfo2UsageAdapter)) return false; UsageInfo2UsageAdapter u2 = (UsageInfo2UsageAdapter)other; + assert u2 != this; if (myLineNumber != u2.myLineNumber || getFile() != u2.getFile()) return false; myMergedUsageInfos.addAll(u2.myMergedUsageInfos); Collections.sort(myMergedUsageInfos, new Comparator() { @@ -296,6 +297,7 @@ public class UsageInfo2UsageAdapter implements UsageInModule, } public void reset() { + ApplicationManager.getApplication().assertIsDispatchThread(); if (!myMergedUsageInfos.isEmpty()) { myMergedUsageInfos.clear(); myMergedUsageInfos.add(myUsageInfo); @@ -372,16 +374,12 @@ public class UsageInfo2UsageAdapter implements UsageInModule, @NotNull public TextChunk[] getText() { TextChunk[] chunks = myTextChunks.get(); - if (chunks == null) { - myModificationStamp = -1; - } - if (chunks == null || isValid()) { + final long currentModificationStamp = getCurrentModificationStamp(); + boolean isModified = currentModificationStamp != myModificationStamp; + if (chunks == null || isValid() && isModified) { // the check below makes sense only for valid PsiElement - final long currentModificationStamp = getCurrentModificationStamp(); - if (currentModificationStamp != myModificationStamp) { - chunks = initChunks(); - myModificationStamp = currentModificationStamp; - } + chunks = initChunks(); + myModificationStamp = currentModificationStamp; } return chunks; } diff --git a/platform/usageView/src/com/intellij/usages/impl/UsagePreviewPanel.java b/platform/usageView/src/com/intellij/usages/impl/UsagePreviewPanel.java index 2def25aa34e5..2a0ae13c9c07 100644 --- a/platform/usageView/src/com/intellij/usages/impl/UsagePreviewPanel.java +++ b/platform/usageView/src/com/intellij/usages/impl/UsagePreviewPanel.java @@ -25,12 +25,10 @@ import com.intellij.openapi.editor.colors.EditorColors; import com.intellij.openapi.editor.colors.EditorColorsManager; import com.intellij.openapi.editor.markup.*; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.TextRange; import com.intellij.psi.*; import com.intellij.ui.IdeBorderFactory; -import com.intellij.ui.SideBorder; import com.intellij.usageView.UsageInfo; import com.intellij.usageView.UsageViewBundle; import com.intellij.util.ui.UIUtil; @@ -111,7 +109,7 @@ public class UsagePreviewPanel extends JPanel implements Disposable { TextAttributes attributes = colorManager.getGlobalScheme().getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES); TextRange elementRange = psiElement.getTextRange(); - TextRange infoRange = info.getRange(); + TextRange infoRange = info.getRangeInElement(); TextRange textRange = elementRange.intersection(infoRange); if (textRange == null) textRange = elementRange; // hack to determine element range to highlight diff --git a/platform/usageView/src/com/intellij/usages/impl/UsageViewImpl.java b/platform/usageView/src/com/intellij/usages/impl/UsageViewImpl.java index 909702fc250a..44c89df82d8c 100644 --- a/platform/usageView/src/com/intellij/usages/impl/UsageViewImpl.java +++ b/platform/usageView/src/com/intellij/usages/impl/UsageViewImpl.java @@ -472,6 +472,7 @@ public class UsageViewImpl implements UsageView, UsageModelTracker.UsageModelTra } private void rulesChanged() { + ApplicationManager.getApplication().assertIsDispatchThread(); final ArrayList states = new ArrayList(); captureUsagesExpandState(new TreePath(myTree.getModel().getRoot()), states); final List allUsages = new ArrayList(myUsageNodes.keySet()); @@ -640,7 +641,9 @@ public class UsageViewImpl implements UsageView, UsageModelTracker.UsageModelTra }); } - public void reset() { + private void reset() { + ApplicationManager.getApplication().assertIsDispatchThread(); + myUsageNodes.clear(); myIsFirstVisibleUsageFound = false; diff --git a/platform/usageView/src/com/intellij/usages/impl/UsageViewTreeCellRenderer.java b/platform/usageView/src/com/intellij/usages/impl/UsageViewTreeCellRenderer.java index c070e635a992..1c0eed3b2079 100644 --- a/platform/usageView/src/com/intellij/usages/impl/UsageViewTreeCellRenderer.java +++ b/platform/usageView/src/com/intellij/usages/impl/UsageViewTreeCellRenderer.java @@ -98,9 +98,11 @@ class UsageViewTreeCellRenderer extends ColoredTreeCellRenderer { append(UsageViewBundle.message("node.readonly") + " ", patchAttrs(node, ourReadOnlyAttributes)); } - TextChunk[] text = node.getUsage().getPresentation().getText(); - for (TextChunk textChunk : text) { - append(textChunk.getText(), patchAttrs(node, SimpleTextAttributes.fromTextAttributes(textChunk.getAttributes()))); + if (node.isValid()) { + TextChunk[] text = node.getUsage().getPresentation().getText(); + for (TextChunk textChunk : text) { + append(textChunk.getText(), patchAttrs(node, SimpleTextAttributes.fromTextAttributes(textChunk.getAttributes()))); + } } } else if (userObject instanceof String) { diff --git a/platform/usageView/testSrc/com/intellij/usages/impl/UsageViewMemoryTest.java b/platform/usageView/testSrc/com/intellij/usages/impl/UsageViewTest.java similarity index 52% rename from platform/usageView/testSrc/com/intellij/usages/impl/UsageViewMemoryTest.java rename to platform/usageView/testSrc/com/intellij/usages/impl/UsageViewTest.java index 7d56cc69b840..436b8b404afe 100644 --- a/platform/usageView/testSrc/com/intellij/usages/impl/UsageViewMemoryTest.java +++ b/platform/usageView/testSrc/com/intellij/usages/impl/UsageViewTest.java @@ -17,8 +17,10 @@ package com.intellij.usages.impl; import com.intellij.openapi.editor.Document; import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.encoding.EncodingManager; import com.intellij.openapi.vfs.encoding.EncodingManagerImpl; +import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.impl.source.PsiFileImpl; @@ -31,7 +33,7 @@ import com.intellij.util.ui.UIUtil; /** * User: cdr */ -public class UsageViewMemoryTest extends LightPlatformCodeInsightTestCase{ +public class UsageViewTest extends LightPlatformCodeInsightTestCase{ public void testUsageViewDoesNotHoldPsiFilesOrDocuments() throws Exception { PsiFile psiFile = createFile("X.java", "public class X{} //iuggjhfg"); Usage[] usages = new Usage[100]; @@ -50,6 +52,35 @@ public class UsageViewMemoryTest extends LightPlatformCodeInsightTestCase{ LeakHunter.checkLeak(usageView, Document.class); } + public void testUsageViewHandlesDocumentChange() throws Exception { + PsiFile psiFile = createFile("X.java", "public class X{ int xxx; } //comment"); + Usage usage = createUsage(psiFile, psiFile.getText().indexOf("xxx")); + + UsageView usageView = UsageViewManager.getInstance(getProject()).createUsageView(UsageTarget.EMPTY_ARRAY, new Usage[]{usage}, new UsageViewPresentation(), null); + Disposer.register(getTestRootDisposable(), usageView); + + PsiDocumentManager documentManager = PsiDocumentManager.getInstance(getProject()); + Document document = documentManager.getDocument(psiFile); + document.insertString(0, "/* sdfsdfsd */"); + documentManager.commitAllDocuments(); + int navigationOffset = ((UsageInfo2UsageAdapter)usage).getUsageInfo().getNavigationOffset(); + assertEquals(psiFile.getText().indexOf("xxx"), navigationOffset); + } + public void testTextUsageInfoHandlesDocumentChange() throws Exception { + PsiFile psiFile = createFile("X.java", "public class X{ int xxx; } //comment"); + Usage usage = new UsageInfo2UsageAdapter(new UsageInfo(psiFile, psiFile.getText().indexOf("xxx"), StringUtil.indexOfSubstringEnd(psiFile.getText(),"xxx"))); + + UsageView usageView = UsageViewManager.getInstance(getProject()).createUsageView(UsageTarget.EMPTY_ARRAY, new Usage[]{usage}, new UsageViewPresentation(), null); + Disposer.register(getTestRootDisposable(), usageView); + + PsiDocumentManager documentManager = PsiDocumentManager.getInstance(getProject()); + Document document = documentManager.getDocument(psiFile); + document.insertString(0, "/* sdfsdfsd */"); + documentManager.commitAllDocuments(); + int navigationOffset = ((UsageInfo2UsageAdapter)usage).getUsageInfo().getNavigationOffset(); + assertEquals(psiFile.getText().indexOf("xxx"), navigationOffset); + } + private static Usage createUsage(PsiFile psiFile, int offset) { PsiElement element = psiFile.findElementAt(offset % psiFile.getTextLength()); assertNotNull(element); diff --git a/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/xslt/impl/XsltStuffProvider.java b/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/xslt/impl/XsltStuffProvider.java index 8b8604763dd5..e2bbb04d571b 100644 --- a/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/xslt/impl/XsltStuffProvider.java +++ b/plugins/xpath/xpath-lang/src/org/intellij/lang/xpath/xslt/impl/XsltStuffProvider.java @@ -160,7 +160,7 @@ public class XsltStuffProvider implements UsageGroupingRuleProvider, InspectionT } else { final PsiReference[] references = u.getElement().getReferences(); for (PsiReference reference : references) { - if (reference.getRangeInElement().equals(usageInfo.getRange())) { + if (reference.getRangeInElement().equals(usageInfo.getRangeInElement())) { return buildGroup(reference.resolve(), usageInfo, false); } } diff --git a/xml/impl/src/com/intellij/psi/impl/source/xml/XmlElementImpl.java b/xml/impl/src/com/intellij/psi/impl/source/xml/XmlElementImpl.java index 1ab287a0d1c9..46ef227c7237 100644 --- a/xml/impl/src/com/intellij/psi/impl/source/xml/XmlElementImpl.java +++ b/xml/impl/src/com/intellij/psi/impl/source/xml/XmlElementImpl.java @@ -122,7 +122,13 @@ public abstract class XmlElementImpl extends CompositePsiElement implements XmlE } final XmlElement including = getUserData(INCLUDING_ELEMENT); - return including != null ? including : getAstParent().getNavigationElement(); + if (including != null) { + return including; + } + PsiElement astParent = getAstParent(); + PsiElement parentNavigation = astParent.getNavigationElement(); + if (parentNavigation.getTextOffset() == getTextOffset()) return parentNavigation; + return this; } return super.getNavigationElement(); }