mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
usage view for text usages; smart pointers to the range inside PsiFile; use stub index to restore psiElement for files without tree loaded
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+20
-3
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+13
-4
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
+1
-15
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ public class DuplicateMap {
|
||||
private static final TObjectHashingStrategy<SliceUsage> USAGEINFO_EQUALITY = new TObjectHashingStrategy<SliceUsage>() {
|
||||
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) {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ public class IdentitySmartPointer<T extends PsiElement> implements SmartPsiEleme
|
||||
}
|
||||
|
||||
@Override
|
||||
public Segment getSegment() {
|
||||
public Segment getRange() {
|
||||
T element = myElement;
|
||||
return element == null ? null : element.getTextRange();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -46,5 +46,5 @@ public interface SmartPsiElementPointer<E extends PsiElement> {
|
||||
|
||||
VirtualFile getVirtualFile();
|
||||
|
||||
Segment getSegment();
|
||||
Segment getRange();
|
||||
}
|
||||
|
||||
@@ -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<PsiFile> {
|
||||
/**
|
||||
* @return the range inside the PsiFile, or null if the range or PsiFile became invalid
|
||||
*/
|
||||
Segment getRange();
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -202,8 +202,7 @@ public class ShowUsagesAction extends AnAction {
|
||||
final List<Usage> usages = new ArrayList<Usage>();
|
||||
final Set<UsageNode> visibleNodes = new LinkedHashSet<UsageNode>();
|
||||
Processor<Usage> collect = new Processor<Usage>() {
|
||||
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;
|
||||
|
||||
@@ -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<StubElement<?>> 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<PsiElement>() {
|
||||
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<StubElement<?>> 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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+6
-5
@@ -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;
|
||||
|
||||
@@ -141,11 +141,11 @@ public class LazyPointerImpl<E extends PsiElement> implements SmartPointerEx<E>
|
||||
}
|
||||
|
||||
@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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+4
-1
@@ -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();
|
||||
}
|
||||
|
||||
+17
-3
@@ -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<E> pointer = new SmartPsiElementPointerImpl<E>(myProject, element, containingFile);
|
||||
initPointer(element, pointer, containingFile);
|
||||
initPointer(pointer, containingFile);
|
||||
|
||||
return pointer;
|
||||
}
|
||||
|
||||
private <E extends PsiElement> void initPointer(E element, SmartPointerEx<E> 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 <E extends PsiElement> void initPointer(SmartPointerEx<E> 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
|
||||
|
||||
+38
-26
@@ -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<E extends PsiElement> implements SmartPointerEx<E> {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.smartPointers.SmartPsiElementPointerImpl");
|
||||
|
||||
private Reference<E> myElement;
|
||||
private final SmartPointerElementInfo myElementInfo;
|
||||
private final Project myProject;
|
||||
private final Class<? extends PsiElement> 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<? extends PsiElement> elementClass) {
|
||||
ApplicationManager.getApplication().assertReadAccessAllowed();
|
||||
myElement = new WeakReference<E>(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<E extends PsiElement> 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<E extends PsiElement> 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<E>(element);
|
||||
}
|
||||
|
||||
private E getCachedElement() {
|
||||
return myElement == null ? null : myElement.get();
|
||||
Reference<E> ref = myElement;
|
||||
return ref == null ? null : ref.get();
|
||||
}
|
||||
|
||||
public PsiFile getContainingFile() {
|
||||
@@ -114,7 +121,7 @@ class SmartPsiElementPointerImpl<E extends PsiElement> 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<E extends PsiElement> 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 <E extends PsiElement> 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<E extends PsiElement> 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() {
|
||||
|
||||
+41
@@ -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<PsiFile> 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();
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -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<UsageInfo>() {
|
||||
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;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -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<UsageOffset> list = docsToOffsetsMap.get(document);
|
||||
if (list == null) {
|
||||
list = new ArrayList<UsageOffset>();
|
||||
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()) {
|
||||
|
||||
@@ -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()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<UsageInfo>() {
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -472,6 +472,7 @@ public class UsageViewImpl implements UsageView, UsageModelTracker.UsageModelTra
|
||||
}
|
||||
|
||||
private void rulesChanged() {
|
||||
ApplicationManager.getApplication().assertIsDispatchThread();
|
||||
final ArrayList<UsageState> states = new ArrayList<UsageState>();
|
||||
captureUsagesExpandState(new TreePath(myTree.getModel().getRoot()), states);
|
||||
final List<Usage> allUsages = new ArrayList<Usage>(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;
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
+32
-1
@@ -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);
|
||||
+1
-1
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user