From d18d0d47ebfbd12408cb291aee92600d1798937a Mon Sep 17 00:00:00 2001 From: peter Date: Mon, 23 May 2011 19:31:45 +0200 Subject: [PATCH] a language-independent way of retrieving the original PSI element in completion --- .../completion/JavaCompletionUtil.java | 53 +---------------- .../scope/JavaCompletionProcessor.java | 4 +- .../lookup/VariableLookupItem.java | 3 +- .../psi/filters/getters/MembersGetter.java | 4 +- .../completion/CodeCompletionHandlerBase.java | 22 ++++++- .../completion/CompletionUtil.java | 58 +++++++++++++++++++ .../GroovyClassNameInsertHandler.java | 2 +- 7 files changed, 88 insertions(+), 58 deletions(-) diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java index c26ea53d17bb..11f26ab9ab5b 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java @@ -183,55 +183,6 @@ public class JavaCompletionUtil { return false; } - @NotNull - public static PsiField getOriginalElement(@NotNull PsiField field) { - final PsiClass cls = field.getContainingClass(); - if (cls != null) { - final PsiClass newParent = getOriginalElement(cls); - if (newParent != cls) { - final PsiField original = newParent.findFieldByName(field.getName(), false); - if (original != null) { - return original; - } - } - } - return field; - } - - @NotNull - public static PsiTypeParameter getOriginalElement(@NotNull PsiTypeParameter param) { - final PsiClass parent = PsiTreeUtil.getParentOfType(param, PsiClass.class, true, PsiMethod.class); - if (parent != null) { - final PsiClass newParent = getOriginalElement(parent); - if (newParent != parent) { - for (PsiTypeParameter parameter : newParent.getTypeParameters()) { - if (parameter.getName().equals(param.getName())) { - return parameter; - } - } - } - } - return param; - } - - @NotNull - public static PsiClass getOriginalElement(@NotNull PsiClass cls) { - final PsiClass containingClass = cls.getContainingClass(); - if (containingClass != null) { - final PsiClass newParent = getOriginalElement(containingClass); - if (newParent != containingClass) { - return findClassByName(cls, newParent.getInnerClasses()); - } - } - - final PsiFile containingFile = cls.getContainingFile(); - if (containingFile instanceof PsiClassOwner) { - return findClassByName(cls, ((PsiClassOwner)containingFile.getOriginalFile()).getClasses()); - } - - return cls; - } - private static PsiClass findClassByName(PsiClass defResult, PsiClass[] classes) { String name = defResult.getName(); if (name == null) return defResult; @@ -269,7 +220,7 @@ public class JavaCompletionUtil { LOG.assertTrue(psiClass.isValid()); - return new PsiImmediateClassType(getOriginalElement(psiClass), originalize(substitutor)); + return new PsiImmediateClassType(CompletionUtil.getOriginalOrSelf(psiClass), originalize(substitutor)); } public PsiType visitEllipsisType(final PsiEllipsisType ellipsisType) { @@ -300,7 +251,7 @@ public class JavaCompletionUtil { PsiSubstitutor originalSubstitutor = PsiSubstitutor.EMPTY; for (final Map.Entry entry : substitutor.getSubstitutionMap().entrySet()) { final PsiType value = entry.getValue(); - originalSubstitutor = originalSubstitutor.put(getOriginalElement(entry.getKey()), value == null ? null : originalize(value)); + originalSubstitutor = originalSubstitutor.put(CompletionUtil.getOriginalOrSelf(entry.getKey()), value == null ? null : originalize(value)); } return originalSubstitutor; } diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/scope/JavaCompletionProcessor.java b/java/java-impl/src/com/intellij/codeInsight/completion/scope/JavaCompletionProcessor.java index 2ed592b88479..bdf1c181053a 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/scope/JavaCompletionProcessor.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/scope/JavaCompletionProcessor.java @@ -16,7 +16,7 @@ package com.intellij.codeInsight.completion.scope; import com.intellij.codeInsight.CodeInsightSettings; -import com.intellij.codeInsight.completion.JavaCompletionUtil; +import com.intellij.codeInsight.completion.CompletionUtil; import com.intellij.codeInsight.daemon.ImplicitUsageProvider; import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Key; @@ -112,7 +112,7 @@ public class JavaCompletionProcessor extends BaseScopeProcessor implements Eleme } private static boolean isInitializedImplicitly(PsiField field) { - field = JavaCompletionUtil.getOriginalElement(field); + field = CompletionUtil.getOriginalOrSelf(field); for(ImplicitUsageProvider provider: ImplicitUsageProvider.EP_NAME.getExtensions()) { if (provider.isImplicitWrite(field)) { return true; diff --git a/java/java-impl/src/com/intellij/codeInsight/lookup/VariableLookupItem.java b/java/java-impl/src/com/intellij/codeInsight/lookup/VariableLookupItem.java index b348a6a58694..cd06f0e45b0d 100644 --- a/java/java-impl/src/com/intellij/codeInsight/lookup/VariableLookupItem.java +++ b/java/java-impl/src/com/intellij/codeInsight/lookup/VariableLookupItem.java @@ -3,6 +3,7 @@ package com.intellij.codeInsight.lookup; import com.intellij.codeInsight.AutoPopupController; import com.intellij.codeInsight.TailType; import com.intellij.codeInsight.completion.CodeCompletionFeatures; +import com.intellij.codeInsight.completion.CompletionUtil; import com.intellij.codeInsight.completion.InsertionContext; import com.intellij.codeInsight.completion.JavaCompletionUtil; import com.intellij.featureStatistics.FeatureUsageTracker; @@ -94,7 +95,7 @@ public class VariableLookupItem extends LookupItem implements Typed PsiReference reference = context.getFile().findReferenceAt(context.getStartOffset()); if (reference instanceof PsiReferenceExpression && !((PsiReferenceExpression) reference).isQualified()) { final PsiVariable target = JavaPsiFacade.getInstance(context.getProject()).getResolveHelper().resolveReferencedVariable(field.getName(), (PsiElement)reference); - return !field.getManager().areElementsEquivalent(target, JavaCompletionUtil.getOriginalElement(field)); + return !field.getManager().areElementsEquivalent(target, CompletionUtil.getOriginalOrSelf(field)); } return false; } diff --git a/java/java-impl/src/com/intellij/psi/filters/getters/MembersGetter.java b/java/java-impl/src/com/intellij/psi/filters/getters/MembersGetter.java index 50829b1b1bca..515019e516e8 100644 --- a/java/java-impl/src/com/intellij/psi/filters/getters/MembersGetter.java +++ b/java/java-impl/src/com/intellij/psi/filters/getters/MembersGetter.java @@ -15,7 +15,7 @@ */ package com.intellij.psi.filters.getters; -import com.intellij.codeInsight.completion.JavaCompletionUtil; +import com.intellij.codeInsight.completion.CompletionUtil; import com.intellij.codeInsight.lookup.AutoCompletionPolicy; import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.psi.*; @@ -38,7 +38,7 @@ public abstract class MembersGetter { PsiClass current = PsiTreeUtil.getContextOfType(context, PsiClass.class); while (current != null) { - current = JavaCompletionUtil.getOriginalElement(current); + current = CompletionUtil.getOriginalOrSelf(current); if (InheritanceUtil.isInheritorOrSelf(current, where, true)) { return; } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/CodeCompletionHandlerBase.java b/platform/lang-impl/src/com/intellij/codeInsight/completion/CodeCompletionHandlerBase.java index 7d70aff3caad..40e77b089f77 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/completion/CodeCompletionHandlerBase.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/CodeCompletionHandlerBase.java @@ -35,6 +35,8 @@ import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.ScrollType; import com.intellij.openapi.editor.actionSystem.EditorActionManager; +import com.intellij.openapi.editor.event.DocumentAdapter; +import com.intellij.openapi.editor.event.DocumentEvent; import com.intellij.openapi.editor.ex.DocumentEx; import com.intellij.openapi.editor.ex.RangeMarkerEx; import com.intellij.openapi.editor.ex.util.EditorUtil; @@ -66,6 +68,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; +import java.util.LinkedList; import java.util.List; import java.util.concurrent.atomic.AtomicReference; @@ -458,7 +461,7 @@ public class CodeCompletionHandlerBase implements CodeInsightActionHandler { Document document = fileCopy.getViewProvider().getDocument(); assert document != null : "no document"; - initContext.getFileCopyPatcher().patchFileCopy(fileCopy, document, initContext.getOffsetMap()); + patchFileCopy(initContext, fileCopy, document); final Document hostDocument = hostFile.getViewProvider().getDocument(); assert hostDocument != null : "no host document"; PsiDocumentManager.getInstance(hostFile.getProject()).commitDocument(hostDocument); @@ -489,6 +492,23 @@ public class CodeCompletionHandlerBase implements CodeInsightActionHandler { return context; } + private static void patchFileCopy(CompletionInitializationContext initContext, PsiFile fileCopy, Document document) { + final LinkedList events = new LinkedList(); + + final DocumentAdapter listener = new DocumentAdapter() { + @Override + public void documentChanged(DocumentEvent e) { + events.addFirst(e); + } + }; + + document.addDocumentListener(listener); + initContext.getFileCopyPatcher().patchFileCopy(fileCopy, document, initContext.getOffsetMap()); + document.removeDocumentListener(listener); + + document.putUserData(CompletionUtil.RANGE_TRANSLATION, events); + } + private boolean isAutocompleteCommonPrefixOnInvocation() { return invokedExplicitly && CodeInsightSettings.getInstance().AUTOCOMPLETE_COMMON_PREFIX; } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionUtil.java b/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionUtil.java index 967be3f5a7a2..c6b84da9655a 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionUtil.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionUtil.java @@ -24,25 +24,31 @@ import com.intellij.codeInsight.lookup.LookupItem; import com.intellij.featureStatistics.FeatureUsageTracker; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.editor.event.DocumentEvent; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.fileTypes.FileType; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.NotNullLazyValue; +import com.intellij.openapi.util.TextRange; import com.intellij.patterns.ElementPattern; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.filters.TrueFilter; +import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.containers.HashMap; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.List; + import static com.intellij.patterns.PlatformPatterns.character; public class CompletionUtil { + static final Key> RANGE_TRANSLATION = Key.create("completion.rangeTranslation"); public static final Key TAIL_TYPE_ATTR = LookupItem.TAIL_TYPE_ATTR; private static final CompletionData ourGenericCompletionData = new CompletionData() { @@ -205,4 +211,56 @@ public class CompletionUtil { offsetMap.addOffset(CompletionInitializationContext.SELECTION_END_OFFSET, tailOffset); context.setTailOffset(tailOffset); } + + @Nullable + private static Integer translateOffset(int offset, DocumentEvent event) { + if (event.getOffset() < offset && offset < event.getNewLength()) { + return null; + } + + return offset <= event.getOffset() ? offset : offset - event.getNewLength() + event.getOldLength(); + } + + @Nullable + public static PsiElement getTargetElement(LookupElement lookupElement) { + final Object object = lookupElement.getObject(); + if (object instanceof PsiElement) { + return getOriginalElement((PsiElement)object); + } + + return null; + } + + @Nullable + public static T getOriginalElement(@NotNull T psi) { + final PsiFile file = psi.getContainingFile(); + if (file != null) { + final Document document = file.getViewProvider().getDocument(); + if (document != null) { + final List translator = document.getUserData(RANGE_TRANSLATION); + if (translator != null) { + TextRange range = psi.getTextRange(); + Integer start = range.getStartOffset(); + Integer end = range.getEndOffset(); + for (DocumentEvent event : translator) { + start = translateOffset(range.getStartOffset(), event); + end = translateOffset(range.getEndOffset(), event); + if (start == null || end == null) { + return null; + } + } + + return (T)PsiTreeUtil.findElementOfClassAtRange(file.getOriginalFile(), start, end, psi.getClass()); + } + } + } + + return psi; + } + + @NotNull + public static T getOriginalOrSelf(@NotNull T psi) { + final T element = getOriginalElement(psi); + return element == null ? psi : element; + } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GroovyClassNameInsertHandler.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GroovyClassNameInsertHandler.java index d7f827909403..8045184e214b 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GroovyClassNameInsertHandler.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GroovyClassNameInsertHandler.java @@ -70,7 +70,7 @@ public class GroovyClassNameInsertHandler implements InsertHandler