LookupElement.getPsiElement

This commit is contained in:
peter
2012-07-22 15:20:34 +02:00
parent eca60281d9
commit bb7d5180ec
6 changed files with 294 additions and 291 deletions
@@ -17,6 +17,7 @@ package com.intellij.codeInsight.completion.util;
import com.intellij.codeInsight.completion.InsertionContext;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiMethod;
/**
@@ -39,16 +40,16 @@ public class MethodParenthesesHandler extends ParenthesesInsertHandler<LookupEle
public static boolean hasParams(LookupElement item, LookupElement[] allItems, final boolean overloadsMatter, final PsiMethod method) {
boolean hasParams = method.getParameterList().getParametersCount() > 0;
if (overloadsMatter){
hasParams |= hasOverloads(item, allItems, method);
hasParams |= hasOverloads(allItems, method);
}
return hasParams;
}
private static boolean hasOverloads(LookupElement item, LookupElement[] allItems, final PsiMethod method) {
private static boolean hasOverloads(LookupElement[] allItems, final PsiMethod method) {
String name = method.getName();
for (LookupElement item1 : allItems) {
final Object o = item1.getObject();
if (item.getObject() != o && o instanceof PsiMethod && ((PsiMethod)o).getName().equals(name)) {
for (LookupElement another : allItems) {
final PsiElement element = another.getPsiElement();
if (method != element && element instanceof PsiMethod && ((PsiMethod)element).getName().equals(name)) {
return true;
}
}
@@ -16,9 +16,11 @@
package com.intellij.codeInsight.lookup;
import com.intellij.codeInsight.completion.InsertionContext;
import com.intellij.navigation.PsiElementNavigationItem;
import com.intellij.openapi.util.ClassConditionKey;
import com.intellij.openapi.util.UserDataHolderBase;
import com.intellij.psi.PsiElement;
import com.intellij.psi.ResolveResult;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -47,6 +49,21 @@ public abstract class LookupElement extends UserDataHolderBase {
return this;
}
@Nullable
public PsiElement getPsiElement() {
Object o = getObject();
if (o instanceof PsiElement) {
return (PsiElement)o;
}
if (o instanceof ResolveResult) {
return ((ResolveResult)o).getElement();
}
if (o instanceof PsiElementNavigationItem) {
return ((PsiElementNavigationItem)o).getTargetElement();
}
return null;
}
public boolean isValid() {
final Object object = getObject();
if (object instanceof PsiElement) {
@@ -1,275 +1,266 @@
/*
* Copyright 2000-2009 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.codeInsight.completion;
import com.intellij.codeInsight.CodeInsightSettings;
import com.intellij.codeInsight.TailType;
import com.intellij.codeInsight.lookup.Lookup;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupItem;
import com.intellij.codeInsight.lookup.LookupValueWithPsiElement;
import com.intellij.featureStatistics.FeatureUsageTracker;
import com.intellij.navigation.PsiElementNavigationItem;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.fileTypes.FileType;
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.ResolveResult;
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 static com.intellij.patterns.PlatformPatterns.character;
public class CompletionUtil {
static final Key<OffsetTranslator> 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() {
{
final CompletionVariant variant = new CompletionVariant(PsiElement.class, TrueFilter.INSTANCE);
variant.addCompletionFilter(TrueFilter.INSTANCE, TailType.NONE);
registerVariant(variant);
}
};
private static final HashMap<FileType, NotNullLazyValue<CompletionData>> ourCustomCompletionDatas = new HashMap<FileType, NotNullLazyValue<CompletionData>>();
public static final @NonNls String DUMMY_IDENTIFIER = CompletionInitializationContext.DUMMY_IDENTIFIER;
public static final @NonNls String DUMMY_IDENTIFIER_TRIMMED = DUMMY_IDENTIFIER.trim();
public static boolean startsWith(String text, String prefix) {
//if (text.length() <= prefix.length()) return false;
return toLowerCase(text).startsWith(toLowerCase(prefix));
}
private static String toLowerCase(String text) {
CodeInsightSettings settings = CodeInsightSettings.getInstance();
switch (settings.COMPLETION_CASE_SENSITIVE) {
case CodeInsightSettings.NONE:
return text.toLowerCase();
case CodeInsightSettings.FIRST_LETTER: {
StringBuffer buffer = new StringBuffer();
buffer.append(text.toLowerCase());
if (buffer.length() > 0) {
buffer.setCharAt(0, text.charAt(0));
}
return buffer.toString();
}
default:
return text;
}
}
@Nullable
public static CompletionData getCompletionDataByElement(@Nullable final PsiElement position, @NotNull PsiFile originalFile) {
if (position == null) return null;
final FileType fileType = position.getParent().getLanguage().getAssociatedFileType();
if (fileType != null) {
final CompletionData mainData = getCompletionDataByFileType(fileType);
if (mainData != null) {
return mainData;
}
}
final CompletionData mainData = getCompletionDataByFileType(originalFile.getFileType());
return mainData != null ? mainData : ourGenericCompletionData;
}
/** @see CompletionDataEP */
@Deprecated
public static void registerCompletionData(FileType fileType, NotNullLazyValue<CompletionData> completionData) {
ourCustomCompletionDatas.put(fileType, completionData);
}
/** @see CompletionDataEP */
@Deprecated
public static void registerCompletionData(FileType fileType, final CompletionData completionData) {
registerCompletionData(fileType, new NotNullLazyValue<CompletionData>() {
@NotNull
protected CompletionData compute() {
return completionData;
}
});
}
@Nullable
public static CompletionData getCompletionDataByFileType(FileType fileType) {
for(CompletionDataEP ep: Extensions.getExtensions(CompletionDataEP.EP_NAME)) {
if (ep.fileType.equals(fileType.getName())) {
return ep.getHandler();
}
}
final NotNullLazyValue<CompletionData> lazyValue = ourCustomCompletionDatas.get(fileType);
return lazyValue == null ? null : lazyValue.getValue();
}
public static boolean shouldShowFeature(final CompletionParameters parameters, @NonNls final String id) {
if (FeatureUsageTracker.getInstance().isToBeAdvertisedInLookup(id, parameters.getPosition().getProject())) {
FeatureUsageTracker.getInstance().triggerFeatureShown(id);
return true;
}
return false;
}
public static String findJavaIdentifierPrefix(CompletionParameters parameters) {
return findJavaIdentifierPrefix(parameters.getPosition(), parameters.getOffset());
}
public static String findJavaIdentifierPrefix(final PsiElement insertedElement, final int offset) {
return findIdentifierPrefix(insertedElement, offset, character().javaIdentifierPart(), character().javaIdentifierStart());
}
public static String findReferenceOrAlphanumericPrefix(CompletionParameters parameters) {
String prefix = findReferencePrefix(parameters);
return prefix == null ? findAlphanumericPrefix(parameters) : prefix;
}
public static String findAlphanumericPrefix(CompletionParameters parameters) {
return findIdentifierPrefix(parameters.getPosition().getContainingFile(), parameters.getOffset(), character().letterOrDigit(), character().letterOrDigit());
}
public static String findIdentifierPrefix(PsiElement insertedElement, int offset, ElementPattern<Character> idPart,
ElementPattern<Character> idStart) {
if(insertedElement == null) return "";
final String text = insertedElement.getText();
final int offsetInElement = offset - insertedElement.getTextRange().getStartOffset();
int start = offsetInElement - 1;
while (start >=0 ) {
if (!idPart.accepts(text.charAt(start))) break;
--start;
}
while (start + 1 < offsetInElement && !idStart.accepts(text.charAt(start + 1))) {
start++;
}
return text.substring(start + 1, offsetInElement).trim();
}
@Nullable
public static String findReferencePrefix(CompletionParameters parameters) {
return CompletionData.getReferencePrefix(parameters.getPosition(), parameters.getOffset());
}
static InsertionContext emulateInsertion(InsertionContext oldContext, int newStart, final LookupElement item) {
final InsertionContext newContext = newContext(oldContext, item);
emulateInsertion(item, newStart, newContext);
return newContext;
}
private static InsertionContext newContext(InsertionContext oldContext, LookupElement forElement) {
final Editor editor = oldContext.getEditor();
return new InsertionContext(new OffsetMap(editor.getDocument()), Lookup.AUTO_INSERT_SELECT_CHAR, new LookupElement[]{forElement}, oldContext.getFile(), editor,
oldContext.shouldAddCompletionChar());
}
public static InsertionContext newContext(InsertionContext oldContext, LookupElement forElement, int startOffset, int tailOffset) {
final InsertionContext context = newContext(oldContext, forElement);
setOffsets(context, startOffset, tailOffset);
return context;
}
public static void emulateInsertion(LookupElement item, int offset, InsertionContext context) {
setOffsets(context, offset, offset);
final Editor editor = context.getEditor();
final Document document = editor.getDocument();
final String lookupString = item.getLookupString();
document.insertString(offset, lookupString);
editor.getCaretModel().moveToOffset(context.getTailOffset());
PsiDocumentManager.getInstance(context.getProject()).commitDocument(document);
item.handleInsert(context);
}
private static void setOffsets(InsertionContext context, int offset, final int tailOffset) {
final OffsetMap offsetMap = context.getOffsetMap();
offsetMap.addOffset(CompletionInitializationContext.START_OFFSET, offset);
offsetMap.addOffset(CompletionInitializationContext.IDENTIFIER_END_OFFSET, tailOffset);
offsetMap.addOffset(CompletionInitializationContext.SELECTION_END_OFFSET, tailOffset);
context.setTailOffset(tailOffset);
}
@Nullable
public static PsiElement getTargetElement(LookupElement lookupElement) {
Object object = lookupElement.getObject();
if (object instanceof ResolveResult) {
object = ((ResolveResult)object).getElement();
}
if (object instanceof PsiElement) {
return getOriginalElement((PsiElement)object);
}
if (object instanceof PsiElementNavigationItem) {
return ((PsiElementNavigationItem)object).getTargetElement();
}
if (object instanceof LookupValueWithPsiElement) {
final PsiElement element = ((LookupValueWithPsiElement)object).getElement();
if (element != null) return getOriginalElement(element);
}
return null;
}
@Nullable
public static <T extends PsiElement> T getOriginalElement(@NotNull T psi) {
final PsiFile file = psi.getContainingFile();
if (file != null && file != file.getOriginalFile() && psi.getTextRange() != null) {
TextRange range = psi.getTextRange();
Integer start = range.getStartOffset();
Integer end = range.getEndOffset();
final Document document = file.getViewProvider().getDocument();
if (document != null) {
final OffsetTranslator translator = document.getUserData(RANGE_TRANSLATION);
if (translator != null) {
start = translator.translateOffset(start);
end = translator.translateOffset(end);
if (start == null || end == null) {
return null;
}
}
}
//noinspection unchecked
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;
}
}
/*
* Copyright 2000-2009 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.codeInsight.completion;
import com.intellij.codeInsight.CodeInsightSettings;
import com.intellij.codeInsight.TailType;
import com.intellij.codeInsight.lookup.Lookup;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.LookupItem;
import com.intellij.codeInsight.lookup.LookupValueWithPsiElement;
import com.intellij.featureStatistics.FeatureUsageTracker;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.fileTypes.FileType;
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 static com.intellij.patterns.PlatformPatterns.character;
public class CompletionUtil {
static final Key<OffsetTranslator> 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() {
{
final CompletionVariant variant = new CompletionVariant(PsiElement.class, TrueFilter.INSTANCE);
variant.addCompletionFilter(TrueFilter.INSTANCE, TailType.NONE);
registerVariant(variant);
}
};
private static final HashMap<FileType, NotNullLazyValue<CompletionData>> ourCustomCompletionDatas = new HashMap<FileType, NotNullLazyValue<CompletionData>>();
public static final @NonNls String DUMMY_IDENTIFIER = CompletionInitializationContext.DUMMY_IDENTIFIER;
public static final @NonNls String DUMMY_IDENTIFIER_TRIMMED = DUMMY_IDENTIFIER.trim();
public static boolean startsWith(String text, String prefix) {
//if (text.length() <= prefix.length()) return false;
return toLowerCase(text).startsWith(toLowerCase(prefix));
}
private static String toLowerCase(String text) {
CodeInsightSettings settings = CodeInsightSettings.getInstance();
switch (settings.COMPLETION_CASE_SENSITIVE) {
case CodeInsightSettings.NONE:
return text.toLowerCase();
case CodeInsightSettings.FIRST_LETTER: {
StringBuffer buffer = new StringBuffer();
buffer.append(text.toLowerCase());
if (buffer.length() > 0) {
buffer.setCharAt(0, text.charAt(0));
}
return buffer.toString();
}
default:
return text;
}
}
@Nullable
public static CompletionData getCompletionDataByElement(@Nullable final PsiElement position, @NotNull PsiFile originalFile) {
if (position == null) return null;
final FileType fileType = position.getParent().getLanguage().getAssociatedFileType();
if (fileType != null) {
final CompletionData mainData = getCompletionDataByFileType(fileType);
if (mainData != null) {
return mainData;
}
}
final CompletionData mainData = getCompletionDataByFileType(originalFile.getFileType());
return mainData != null ? mainData : ourGenericCompletionData;
}
/** @see CompletionDataEP */
@Deprecated
public static void registerCompletionData(FileType fileType, NotNullLazyValue<CompletionData> completionData) {
ourCustomCompletionDatas.put(fileType, completionData);
}
/** @see CompletionDataEP */
@Deprecated
public static void registerCompletionData(FileType fileType, final CompletionData completionData) {
registerCompletionData(fileType, new NotNullLazyValue<CompletionData>() {
@NotNull
protected CompletionData compute() {
return completionData;
}
});
}
@Nullable
public static CompletionData getCompletionDataByFileType(FileType fileType) {
for(CompletionDataEP ep: Extensions.getExtensions(CompletionDataEP.EP_NAME)) {
if (ep.fileType.equals(fileType.getName())) {
return ep.getHandler();
}
}
final NotNullLazyValue<CompletionData> lazyValue = ourCustomCompletionDatas.get(fileType);
return lazyValue == null ? null : lazyValue.getValue();
}
public static boolean shouldShowFeature(final CompletionParameters parameters, @NonNls final String id) {
if (FeatureUsageTracker.getInstance().isToBeAdvertisedInLookup(id, parameters.getPosition().getProject())) {
FeatureUsageTracker.getInstance().triggerFeatureShown(id);
return true;
}
return false;
}
public static String findJavaIdentifierPrefix(CompletionParameters parameters) {
return findJavaIdentifierPrefix(parameters.getPosition(), parameters.getOffset());
}
public static String findJavaIdentifierPrefix(final PsiElement insertedElement, final int offset) {
return findIdentifierPrefix(insertedElement, offset, character().javaIdentifierPart(), character().javaIdentifierStart());
}
public static String findReferenceOrAlphanumericPrefix(CompletionParameters parameters) {
String prefix = findReferencePrefix(parameters);
return prefix == null ? findAlphanumericPrefix(parameters) : prefix;
}
public static String findAlphanumericPrefix(CompletionParameters parameters) {
return findIdentifierPrefix(parameters.getPosition().getContainingFile(), parameters.getOffset(), character().letterOrDigit(), character().letterOrDigit());
}
public static String findIdentifierPrefix(PsiElement insertedElement, int offset, ElementPattern<Character> idPart,
ElementPattern<Character> idStart) {
if(insertedElement == null) return "";
final String text = insertedElement.getText();
final int offsetInElement = offset - insertedElement.getTextRange().getStartOffset();
int start = offsetInElement - 1;
while (start >=0 ) {
if (!idPart.accepts(text.charAt(start))) break;
--start;
}
while (start + 1 < offsetInElement && !idStart.accepts(text.charAt(start + 1))) {
start++;
}
return text.substring(start + 1, offsetInElement).trim();
}
@Nullable
public static String findReferencePrefix(CompletionParameters parameters) {
return CompletionData.getReferencePrefix(parameters.getPosition(), parameters.getOffset());
}
static InsertionContext emulateInsertion(InsertionContext oldContext, int newStart, final LookupElement item) {
final InsertionContext newContext = newContext(oldContext, item);
emulateInsertion(item, newStart, newContext);
return newContext;
}
private static InsertionContext newContext(InsertionContext oldContext, LookupElement forElement) {
final Editor editor = oldContext.getEditor();
return new InsertionContext(new OffsetMap(editor.getDocument()), Lookup.AUTO_INSERT_SELECT_CHAR, new LookupElement[]{forElement}, oldContext.getFile(), editor,
oldContext.shouldAddCompletionChar());
}
public static InsertionContext newContext(InsertionContext oldContext, LookupElement forElement, int startOffset, int tailOffset) {
final InsertionContext context = newContext(oldContext, forElement);
setOffsets(context, startOffset, tailOffset);
return context;
}
public static void emulateInsertion(LookupElement item, int offset, InsertionContext context) {
setOffsets(context, offset, offset);
final Editor editor = context.getEditor();
final Document document = editor.getDocument();
final String lookupString = item.getLookupString();
document.insertString(offset, lookupString);
editor.getCaretModel().moveToOffset(context.getTailOffset());
PsiDocumentManager.getInstance(context.getProject()).commitDocument(document);
item.handleInsert(context);
}
private static void setOffsets(InsertionContext context, int offset, final int tailOffset) {
final OffsetMap offsetMap = context.getOffsetMap();
offsetMap.addOffset(CompletionInitializationContext.START_OFFSET, offset);
offsetMap.addOffset(CompletionInitializationContext.IDENTIFIER_END_OFFSET, tailOffset);
offsetMap.addOffset(CompletionInitializationContext.SELECTION_END_OFFSET, tailOffset);
context.setTailOffset(tailOffset);
}
@Nullable
public static PsiElement getTargetElement(LookupElement lookupElement) {
PsiElement psiElement = lookupElement.getPsiElement();
if (psiElement != null) {
return getOriginalElement(psiElement);
}
Object object = lookupElement.getObject();
if (object instanceof LookupValueWithPsiElement) {
final PsiElement element = ((LookupValueWithPsiElement)object).getElement();
if (element != null) return getOriginalElement(element);
}
return null;
}
@Nullable
public static <T extends PsiElement> T getOriginalElement(@NotNull T psi) {
final PsiFile file = psi.getContainingFile();
if (file != null && file != file.getOriginalFile() && psi.getTextRange() != null) {
TextRange range = psi.getTextRange();
Integer start = range.getStartOffset();
Integer end = range.getEndOffset();
final Document document = file.getViewProvider().getDocument();
if (document != null) {
final OffsetTranslator translator = document.getUserData(RANGE_TRANSLATION);
if (translator != null) {
start = translator.translateOffset(start);
end = translator.translateOffset(end);
if (start == null || end == null) {
return null;
}
}
}
//noinspection unchecked
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;
}
}
@@ -31,8 +31,7 @@ public class LookupElementProximityWeigher extends CompletionWeigher {
return PsiProximityComparator.getProximity(new NullableComputable<PsiElement>() {
@Override
public PsiElement compute() {
Object object = item.getObject();
return object instanceof PsiElement ? (PsiElement)object : null;
return item.getPsiElement();
}
}, location.getCompletionParameters().getPosition(), location.getProcessingContext());
}
@@ -73,14 +73,9 @@ public class DefaultLookupItemRenderer extends LookupElementRenderer<LookupItem>
return ((Iconable)o).getIcon(Iconable.ICON_FLAG_VISIBILITY);
}
if (o instanceof LookupValueWithPsiElement) {
o = ((LookupValueWithPsiElement)o).getElement();
}
if (o instanceof PsiElement) {
final PsiElement element = (PsiElement)o;
if (element.isValid()) {
return element.getIcon(Iconable.ICON_FLAG_VISIBILITY);
}
final PsiElement element = item.getPsiElement();
if (element != null && element.isValid()) {
return element.getIcon(Iconable.ICON_FLAG_VISIBILITY);
}
return null;
}
@@ -78,7 +78,7 @@ class GrCompletionWithLibraryTest extends GroovyCompletionTestBase {
public void testEachMethodForRanges() throws Throwable {doBasicTest();}
public void testEachMethodForEnumRanges() throws Throwable {doBasicTest();}
public void testPrintlnSpace() { checkCompletion 'print<caret>', ' ', "print <caret>" }
public void testPrintlnSpace() { checkCompletion 'print<caret>', 'l ', "println <caret>" }
public void testHashCodeSpace() { checkCompletion 'if ("".h<caret>', ' ', 'if ("".hashCode() <caret>' }
public void testTwoMethodWithSameName() {