From 4a515dcda02bef5986f916721e6902985e104973 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Fri, 8 Apr 2011 14:13:06 +0400 Subject: [PATCH] IDEA-67711 --- .../impl/smartPointers/AnchorElementInfo.java | 4 +- .../com/intellij/psi/SmartPsiFileRange.java | 2 + .../ReplaceInProjectManager.java | 2 +- .../InjectedSelfElementInfo.java | 54 ++++++++++++++++--- .../impl/smartPointers/SelfElementInfo.java | 17 +++--- .../SmartPsiElementPointerImpl.java | 3 +- .../com/intellij/usages/ChunkExtractor.java | 20 ++++--- .../com/intellij/usages/UsageDataUtil.java | 2 +- .../usages/UsageInfo2UsageAdapter.java | 19 +++++-- .../intellij/usages/impl/UsageViewImpl.java | 3 +- 10 files changed, 97 insertions(+), 29 deletions(-) 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 de5f30b56da0..02612fae3d7a 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 @@ -52,12 +52,12 @@ class AnchorElementInfo extends SelfElementInfo { @Nullable public PsiElement restoreElement() { if (stubId != -1) { - PsiFile file = SelfElementInfo.restoreFileFromVirtual(myVirtualFile, myProject); + PsiFile file = SelfElementInfo.restoreFileFromVirtual(getVirtualFile(), myProject); if (!(file instanceof PsiFileWithStubSupport)) return null; return PsiAnchor.restoreFromStubIndex((PsiFileWithStubSupport)file, stubId, myStubElementType); } if (!mySyncMarkerIsValid) return null; - PsiFile file = SelfElementInfo.restoreFileFromVirtual(myVirtualFile, myProject); + PsiFile file = SelfElementInfo.restoreFileFromVirtual(getVirtualFile(), myProject); if (file == null) return null; PsiElement anchor = file.findElementAt(getSyncStartOffset()); if (anchor == null) return null; diff --git a/platform/lang-api/src/com/intellij/psi/SmartPsiFileRange.java b/platform/lang-api/src/com/intellij/psi/SmartPsiFileRange.java index 2b2c59bed935..01d89f8af3d5 100644 --- a/platform/lang-api/src/com/intellij/psi/SmartPsiFileRange.java +++ b/platform/lang-api/src/com/intellij/psi/SmartPsiFileRange.java @@ -16,6 +16,7 @@ package com.intellij.psi; import com.intellij.openapi.util.Segment; +import org.jetbrains.annotations.Nullable; /** * pointer to a PsiFile + range inside the file. @@ -25,5 +26,6 @@ public interface SmartPsiFileRange extends SmartPsiElementPointer { /** * @return the range inside the PsiFile, or null if the range or PsiFile became invalid */ + @Nullable Segment getRange(); } diff --git a/platform/lang-impl/src/com/intellij/find/replaceInProject/ReplaceInProjectManager.java b/platform/lang-impl/src/com/intellij/find/replaceInProject/ReplaceInProjectManager.java index 12e4999bdd6e..99583b9aad71 100644 --- a/platform/lang-impl/src/com/intellij/find/replaceInProject/ReplaceInProjectManager.java +++ b/platform/lang-impl/src/com/intellij/find/replaceInProject/ReplaceInProjectManager.java @@ -362,7 +362,7 @@ public class ReplaceInProjectManager { for (final Usage usage : selectedUsages) { final VirtualFile file = ((UsageInFile)usage).getFile(); - if (!file.isWritable()) { + if (file != null && !file.isWritable()) { if (readOnlyFiles == null) readOnlyFiles = new HashSet(); readOnlyFiles.add(file); } 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 a37520cc773d..5e9299408f1e 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,24 +17,64 @@ package com.intellij.psi.impl.smartPointers; import com.intellij.lang.injection.InjectedLanguageManager; import com.intellij.openapi.project.Project; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; +import com.intellij.openapi.util.Ref; +import com.intellij.openapi.util.Segment; +import com.intellij.openapi.util.TextRange; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.*; import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil; import org.jetbrains.annotations.NotNull; +import java.util.List; + /** * User: cdr */ class InjectedSelfElementInfo extends SelfElementInfo { + private final SmartPsiFileRange myPsiFileRangeInHostElement; + private final Class myClass; + InjectedSelfElementInfo(@NotNull Project project, @NotNull PsiElement anchor, - @NotNull PsiFile containingFile) { - super(project, InjectedLanguageManager.getInstance(project).injectedToHost(anchor, anchor.getTextRange()), anchor.getClass(), InjectedLanguageUtil.getTopLevelFile(containingFile)); - assert containingFile.getContext() != null; + @NotNull PsiElement context) { + super(project, context); + SmartPointerManager smartPointerManager = SmartPointerManager.getInstance(project); + myPsiFileRangeInHostElement = smartPointerManager.createSmartPsiFileRangePointer(context.getContainingFile(), InjectedLanguageManager.getInstance( + project).injectedToHost(anchor, anchor.getTextRange())); + myClass = anchor.getClass(); } @Override - protected PsiElement findAnchorAt(@NotNull PsiFile file, int syncStartOffset) { - return InjectedLanguageUtil.findInjectedElementNoCommitWithOffset(file, syncStartOffset); + public VirtualFile getVirtualFile() { + PsiElement element = restoreElement(); + if (element == null) return null; + return element.getContainingFile().getVirtualFile(); + } + + @Override + public PsiElement restoreElement() { + PsiElement host = super.restoreElement(); + if (host == null) return null; + + Segment segment = myPsiFileRangeInHostElement.getRange(); + if (segment == null) return null; + final TextRange rangeInHostElement = TextRange.create(segment); + final Ref result = new Ref(); + + InjectedLanguageUtil.enumerate(host, host.getContainingFile(), new PsiLanguageInjectionHost.InjectedPsiVisitor() { + @Override + public void visit(@NotNull PsiFile injectedPsi, @NotNull List places) { + if (result.get() != null) return; + TextRange hostRange = + InjectedLanguageManager.getInstance(getProject()).injectedToHost(injectedPsi, new TextRange(0, injectedPsi.getTextLength())); + if (hostRange.contains(rangeInHostElement)) { + TextRange rangeInside = rangeInHostElement.shiftRight(-hostRange.getStartOffset()); + PsiElement element = findElementInside(injectedPsi, rangeInside.getStartOffset(), rangeInside.getEndOffset(), myClass); + result.set(element); + } + } + }, false); + + return result.get(); } } 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 6aef9bb78e8b..6ebadcfe8f1d 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 @@ -34,7 +34,7 @@ import java.lang.ref.SoftReference; * User: cdr */ public class SelfElementInfo implements SmartPointerElementInfo { - protected final VirtualFile myVirtualFile; + private final VirtualFile myVirtualFile; private Reference myMarkerRef; // create marker only in case of live document private int mySyncStartOffset; private int mySyncEndOffset; @@ -44,6 +44,9 @@ public class SelfElementInfo implements SmartPointerElementInfo { @SuppressWarnings({"UnusedDeclaration"}) private RangeMarker myRangeMarker; //maintain hard reference during modification + protected SelfElementInfo(@NotNull Project project, @NotNull PsiElement anchor) { + this(project, anchor.getTextRange(), anchor.getClass(), anchor.getContainingFile()); + } public SelfElementInfo(@NotNull Project project, @NotNull TextRange anchor, @NotNull Class anchorClass, @NotNull PsiFile containingFile) { myVirtualFile = containingFile.getVirtualFile(); myType = anchorClass; @@ -154,7 +157,11 @@ public class SelfElementInfo implements SmartPointerElementInfo { final int syncStartOffset = getSyncStartOffset(); final int syncEndOffset = getSyncEndOffset(); - PsiElement anchor = findAnchorAt(file, syncStartOffset); + return findElementInside(file, syncStartOffset, syncEndOffset, myType); + } + + protected static PsiElement findElementInside(PsiFile file, int syncStartOffset, int syncEndOffset, Class type) { + PsiElement anchor = file.getViewProvider().findElementAt(syncStartOffset, file.getLanguage()); if (anchor == null) return null; TextRange range = anchor.getTextRange(); @@ -166,7 +173,7 @@ public class SelfElementInfo implements SmartPointerElementInfo { range = anchor.getTextRange(); } - while (range.getEndOffset() == syncEndOffset && anchor != null && !myType.equals(anchor.getClass())) { + while (range.getEndOffset() == syncEndOffset && anchor != null && !type.equals(anchor.getClass())) { anchor = anchor.getParent(); if (anchor == null || anchor.getTextRange() == null) break; range = anchor.getTextRange(); @@ -176,10 +183,6 @@ public class SelfElementInfo implements SmartPointerElementInfo { return null; } - protected PsiElement findAnchorAt(@NotNull PsiFile file, int syncStartOffset) { - return file.getViewProvider().findElementAt(syncStartOffset, file.getLanguage()); - } - @Override public void dispose() { RangeMarker marker = getMarker(); 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 82c8736f34dd..4224bd4c6521 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 @@ -153,7 +153,8 @@ class SmartPsiElementPointerImpl implements SmartPointerEx FileViewProvider viewProvider = containingFile.getViewProvider(); if (viewProvider instanceof InjectedFileViewProvider) { - return new InjectedSelfElementInfo(project, element, containingFile); + PsiElement context = containingFile.getContext(); + if (context != null) return new InjectedSelfElementInfo(project, element, context); } if (element instanceof PsiFile) { diff --git a/platform/usageView/src/com/intellij/usages/ChunkExtractor.java b/platform/usageView/src/com/intellij/usages/ChunkExtractor.java index 6aa2f991828f..a7de001cb4e9 100644 --- a/platform/usageView/src/com/intellij/usages/ChunkExtractor.java +++ b/platform/usageView/src/com/intellij/usages/ChunkExtractor.java @@ -15,6 +15,7 @@ */ package com.intellij.usages; +import com.intellij.injected.editor.DocumentWindow; import com.intellij.lexer.Lexer; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Document; @@ -26,6 +27,7 @@ import com.intellij.openapi.editor.markup.TextAttributes; import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.fileTypes.PlainSyntaxHighlighter; import com.intellij.openapi.fileTypes.SyntaxHighlighter; +import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Segment; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiDocumentManager; @@ -101,10 +103,11 @@ public class ChunkExtractor { private ChunkExtractor(@NotNull PsiFile file) { myColorsScheme = UsageTreeColorsScheme.getInstance().getScheme(); - myDocument = PsiDocumentManager.getInstance(file.getProject()).getDocument(file); + Project project = file.getProject(); + myDocument = PsiDocumentManager.getInstance(project).getDocument(file); LOG.assertTrue(myDocument != null); final FileType fileType = file.getFileType(); - final SyntaxHighlighter highlighter = SyntaxHighlighter.PROVIDER.create(fileType, file.getProject(), file.getVirtualFile()); + final SyntaxHighlighter highlighter = SyntaxHighlighter.PROVIDER.create(fileType, project, file.getVirtualFile()); myHighlighter = highlighter == null ? new PlainSyntaxHighlighter() : highlighter; myLexer = myHighlighter.getHighlightingLexer(); myLexer.start(myDocument.getCharsSequence()); @@ -125,8 +128,15 @@ public class ChunkExtractor { int absoluteStartOffset = usageInfo2UsageAdapter.getNavigationOffset(); if (absoluteStartOffset == -1) return TextChunk.EMPTY_ARRAY; - final int lineNumber = myDocument.getLineNumber(absoluteStartOffset); - final int columnNumber = absoluteStartOffset - myDocument.getLineStartOffset(lineNumber); + Document visibleDocument = myDocument instanceof DocumentWindow ? ((DocumentWindow)myDocument).getDelegate() : myDocument; + int visibleStartOffset = myDocument instanceof DocumentWindow ? ((DocumentWindow)myDocument).injectedToHost(absoluteStartOffset) : absoluteStartOffset; + + int lineNumber = myDocument.getLineNumber(absoluteStartOffset); + //int columnNumber = absoluteStartOffset - myDocument.getLineStartOffset(lineNumber); + int visibleLineNumber = visibleDocument.getLineNumber(visibleStartOffset); + int visibleColumnNumber = visibleStartOffset - visibleDocument.getLineStartOffset(visibleLineNumber); + final List result = new ArrayList(); + appendPrefix(result, visibleLineNumber, visibleColumnNumber); int lineStartOffset = myDocument.getLineStartOffset(lineNumber); int lineEndOffset = lineStartOffset < myDocument.getTextLength() ? myDocument.getLineEndOffset(lineNumber) : 0; @@ -136,8 +146,6 @@ public class ChunkExtractor { if (myLexer.getTokenStart() > absoluteStartOffset) { myLexer.start(chars); } - final List result = new ArrayList(); - appendPrefix(result, lineNumber, columnNumber); if (lineEndOffset - lineStartOffset > MAX_LINE_TO_SHOW) { lineStartOffset = Math.max(lineStartOffset, absoluteStartOffset - OFFSET_BEFORE_TO_SHOW_WHEN_LONG_LINE); lineEndOffset = Math.min(lineEndOffset, absoluteStartOffset + OFFSET_AFTER_TO_SHOW_WHEN_LONG_LINE); diff --git a/platform/usageView/src/com/intellij/usages/UsageDataUtil.java b/platform/usageView/src/com/intellij/usages/UsageDataUtil.java index 134bee458bae..40fac535ca8a 100644 --- a/platform/usageView/src/com/intellij/usages/UsageDataUtil.java +++ b/platform/usageView/src/com/intellij/usages/UsageDataUtil.java @@ -41,7 +41,7 @@ public class UsageDataUtil { for (Usage usage : usages) { if (usage instanceof UsageInFile) { VirtualFile file = ((UsageInFile)usage).getFile(); - if (file.isValid()) { + if (file != null && file.isValid()) { result.add(file); } } diff --git a/platform/usageView/src/com/intellij/usages/UsageInfo2UsageAdapter.java b/platform/usageView/src/com/intellij/usages/UsageInfo2UsageAdapter.java index 1849cd9c696f..8418677dd466 100644 --- a/platform/usageView/src/com/intellij/usages/UsageInfo2UsageAdapter.java +++ b/platform/usageView/src/com/intellij/usages/UsageInfo2UsageAdapter.java @@ -16,6 +16,7 @@ package com.intellij.usages; import com.intellij.ide.SelectInEditorManager; +import com.intellij.injected.editor.VirtualFileWindow; import com.intellij.openapi.actionSystem.DataKey; import com.intellij.openapi.actionSystem.DataSink; import com.intellij.openapi.actionSystem.TypeSafeDataProvider; @@ -79,7 +80,7 @@ public class UsageInfo2UsageAdapter implements UsageInModule, @Override public void run() { PsiElement element = getElement(); - Document document = getDocument(); + Document document = PsiDocumentManager.getInstance(getProject()).getDocument(element.getContainingFile()); int startOffset = myUsageInfo.getNavigationOffset(); if (document != null) { @@ -197,7 +198,8 @@ public class UsageInfo2UsageAdapter implements UsageInModule, } public boolean canNavigate() { - return getFile().isValid(); + VirtualFile file = getFile(); + return file != null && file.isValid(); } public boolean canNavigateToSource() { @@ -323,16 +325,27 @@ public class UsageInfo2UsageAdapter implements UsageInModule, return myUsageInfo; } + // by start offset public int compareTo(final UsageInfo2UsageAdapter o) { VirtualFile containingFile = getFile(); + int shift1 = 0; + if (containingFile instanceof VirtualFileWindow) { + shift1 = ((VirtualFileWindow)containingFile).getDocumentWindow().injectedToHost(0); + containingFile = ((VirtualFileWindow)containingFile).getDelegate(); + } VirtualFile oContainingFile = o.getFile(); + int shift2 = 0; + if (oContainingFile instanceof VirtualFileWindow) { + shift2 = ((VirtualFileWindow)oContainingFile).getDocumentWindow().injectedToHost(0); + oContainingFile = ((VirtualFileWindow)oContainingFile).getDelegate(); + } if (containingFile == null && oContainingFile == null || !Comparing.equal(containingFile, oContainingFile)) { return 0; } Segment s1 = getFirstSegment(); Segment s2 = o.getFirstSegment(); if (s1 == null || s2 == null) return 0; - return s1.getStartOffset() - s2.getStartOffset(); + return s1.getStartOffset() + shift1 - s2.getStartOffset() - shift2; } public void rename(String newName) throws IncorrectOperationException { diff --git a/platform/usageView/src/com/intellij/usages/impl/UsageViewImpl.java b/platform/usageView/src/com/intellij/usages/impl/UsageViewImpl.java index 279f6321bedf..86c51f32237f 100644 --- a/platform/usageView/src/com/intellij/usages/impl/UsageViewImpl.java +++ b/platform/usageView/src/com/intellij/usages/impl/UsageViewImpl.java @@ -909,7 +909,8 @@ public class UsageViewImpl implements UsageView, UsageModelTracker.UsageModelTra for (Usage usage : usages) { if (usage instanceof UsageInFile) { UsageInFile usageInFile = (UsageInFile)usage; - result.add(usageInFile.getFile()); + VirtualFile file = usageInFile.getFile(); + if (file != null) result.add(file); } if (usage instanceof UsageInFiles) {