a language-independent way of retrieving the original PSI element in completion

This commit is contained in:
peter
2011-05-23 19:32:47 +02:00
parent 3b0e795955
commit d18d0d47eb
7 changed files with 88 additions and 58 deletions
@@ -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<PsiTypeParameter, PsiType> 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;
}
@@ -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;
@@ -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<PsiVariable> 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;
}
@@ -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;
}
@@ -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<DocumentEvent> events = new LinkedList<DocumentEvent>();
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;
}
@@ -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<List<DocumentEvent>> RANGE_TRANSLATION = Key.create("completion.rangeTranslation");
public static final Key<TailType> 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 extends PsiElement> T getOriginalElement(@NotNull T psi) {
final PsiFile file = psi.getContainingFile();
if (file != null) {
final Document document = file.getViewProvider().getDocument();
if (document != null) {
final List<DocumentEvent> 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 extends PsiElement> T getOriginalOrSelf(@NotNull T psi) {
final T element = getOriginalElement(psi);
return element == null ? psi : element;
}
}
@@ -70,7 +70,7 @@ public class GroovyClassNameInsertHandler implements InsertHandler<JavaPsiClassR
((GroovyFileBase)file).addImportForClass(psiClass);
return;
}
else if (aClass == JavaCompletionUtil.getOriginalElement(psiClass)) {
else if (aClass == CompletionUtil.getOriginalOrSelf(psiClass)) {
return;
}
}