blob: 270d6f93f767a60c481861c6b1f6c5cfac524e81 [file] [log] [blame]
/*
* 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.CodeInsightUtilCore;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.RangeMarker;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.impl.search.AllClassesSearchExecutor;
import com.intellij.psi.impl.source.PostprocessReformattingAspect;
import com.intellij.psi.impl.source.tree.java.PsiReferenceExpressionImpl;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.util.Consumer;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.Processor;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NotNull;
import java.util.LinkedHashSet;
import java.util.Set;
/**
* Created by IntelliJ IDEA.
* User: ik
* Date: 02.12.2003
* Time: 16:49:25
* To change this template use Options | File Templates.
*/
public class AllClassesGetter {
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.completion.AllClassesGetter");
public static final InsertHandler<JavaPsiClassReferenceElement> TRY_SHORTENING = new InsertHandler<JavaPsiClassReferenceElement>() {
private void _handleInsert(final InsertionContext context, final JavaPsiClassReferenceElement item) {
final Editor editor = context.getEditor();
final PsiClass psiClass = item.getObject();
if (!psiClass.isValid()) return;
int endOffset = editor.getCaretModel().getOffset();
final String qname = psiClass.getQualifiedName();
if (qname == null) return;
if (endOffset == 0) return;
final Document document = editor.getDocument();
final PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance(psiClass.getProject());
final PsiFile file = context.getFile();
if (file.findElementAt(endOffset - 1) == null) return;
final OffsetKey key = OffsetKey.create("endOffset", false);
context.getOffsetMap().addOffset(key, endOffset);
PostprocessReformattingAspect.getInstance(context.getProject()).doPostponedFormatting();
final int newOffset = context.getOffsetMap().getOffset(key);
if (newOffset >= 0) {
endOffset = newOffset;
}
else {
LOG.error(endOffset + " became invalid: " + context.getOffsetMap() + "; inserting " + qname);
}
final RangeMarker toDelete = JavaCompletionUtil.insertTemporary(endOffset, document, " ");
psiDocumentManager.commitAllDocuments();
PsiReference psiReference = file.findReferenceAt(endOffset - 1);
boolean insertFqn = true;
if (psiReference != null) {
final PsiManager psiManager = file.getManager();
if (psiManager.areElementsEquivalent(psiClass, JavaCompletionUtil.resolveReference(psiReference))) {
insertFqn = false;
}
else if (psiClass.isValid()) {
try {
context.setTailOffset(psiReference.getRangeInElement().getEndOffset() + psiReference.getElement().getTextRange().getStartOffset());
final PsiElement newUnderlying = psiReference.bindToElement(psiClass);
if (newUnderlying != null) {
final PsiElement psiElement = CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(newUnderlying);
if (psiElement != null) {
for (final PsiReference reference : psiElement.getReferences()) {
if (psiManager.areElementsEquivalent(psiClass, JavaCompletionUtil.resolveReference(reference))) {
insertFqn = false;
break;
}
}
}
}
}
catch (IncorrectOperationException e) {
//if it's empty we just insert fqn below
}
}
}
if (toDelete.isValid()) {
document.deleteString(toDelete.getStartOffset(), toDelete.getEndOffset());
context.setTailOffset(toDelete.getStartOffset());
}
if (insertFqn) {
INSERT_FQN.handleInsert(context, item);
}
}
@Override
public void handleInsert(final InsertionContext context, final JavaPsiClassReferenceElement item) {
_handleInsert(context, item);
item.getTailType().processTail(context.getEditor(), context.getEditor().getCaretModel().getOffset());
}
};
public static final InsertHandler<JavaPsiClassReferenceElement> INSERT_FQN = new InsertHandler<JavaPsiClassReferenceElement>() {
@Override
public void handleInsert(InsertionContext context, JavaPsiClassReferenceElement item) {
final String qName = item.getQualifiedName();
if (qName != null) {
int start = context.getTailOffset() - 1;
while (start >= 0) {
final char ch = context.getDocument().getCharsSequence().charAt(start);
if (!Character.isJavaIdentifierPart(ch) && ch != '.') break;
start--;
}
context.getDocument().replaceString(start + 1, context.getTailOffset(), qName);
LOG.assertTrue(context.getTailOffset() >= 0);
}
}
};
public static void processJavaClasses(@NotNull final CompletionParameters parameters,
@NotNull final PrefixMatcher prefixMatcher,
final boolean filterByScope,
@NotNull final Consumer<PsiClass> consumer) {
final PsiElement context = parameters.getPosition();
final Project project = context.getProject();
final GlobalSearchScope scope = filterByScope ? context.getContainingFile().getResolveScope() : GlobalSearchScope.allScope(project);
Processor<PsiClass> processor = new Processor<PsiClass>() {
final Set<String> qNames = new THashSet<String>();
final boolean pkgContext = JavaCompletionUtil.inSomePackage(context);
final String packagePrefix = getPackagePrefix(context, parameters.getOffset());
@Override
public boolean process(PsiClass psiClass) {
if (parameters.getInvocationCount() < 2) {
if (PsiReferenceExpressionImpl.seemsScrambled(psiClass)) {
return true;
}
if (!StringUtil.isCapitalized(psiClass.getName()) && !Registry.is("ide.completion.show.lower.case.classes")) {
return true;
}
}
assert psiClass != null;
if (isAcceptableInContext(context, psiClass, filterByScope, pkgContext)) {
String qName = psiClass.getQualifiedName();
if (qName != null && qName.startsWith(packagePrefix) && qNames.add(qName)) {
consumer.consume(psiClass);
}
}
return true;
}
};
processJavaClasses(prefixMatcher, project, scope, processor);
}
public static void processJavaClasses(@NotNull final PrefixMatcher prefixMatcher,
@NotNull Project project,
@NotNull GlobalSearchScope scope,
@NotNull Processor<PsiClass> processor) {
final Set<String> names = new THashSet<String>(10000);
AllClassesSearchExecutor.processClassNames(project, scope, new Consumer<String>() {
@Override
public void consume(String s) {
if (prefixMatcher.prefixMatches(s)) {
names.add(s);
}
}
});
LinkedHashSet<String> sorted = CompletionUtil.sortMatching(prefixMatcher, names);
AllClassesSearchExecutor.processClassesByNames(project, scope, sorted, processor);
}
private static String getPackagePrefix(final PsiElement context, final int offset) {
final CharSequence fileText = context.getContainingFile().getViewProvider().getContents();
int i = offset - 1;
while (i >= 0) {
final char c = fileText.charAt(i);
if (!Character.isJavaIdentifierPart(c) && c != '.') break;
i--;
}
String prefix = fileText.subSequence(i + 1, offset).toString();
final int j = prefix.lastIndexOf('.');
return j > 0 ? prefix.substring(0, j) : "";
}
public static boolean isAcceptableInContext(@NotNull final PsiElement context,
@NotNull final PsiClass psiClass,
final boolean filterByScope, final boolean pkgContext) {
ProgressManager.checkCanceled();
if (!context.isValid() || !psiClass.isValid()) return false;
if (JavaCompletionUtil.isInExcludedPackage(psiClass, false)) return false;
final String qualifiedName = psiClass.getQualifiedName();
if (qualifiedName == null) return false;
if (!filterByScope && !(psiClass instanceof PsiCompiledElement)) return true;
return JavaCompletionUtil.isSourceLevelAccessible(context, psiClass, pkgContext);
}
public static JavaPsiClassReferenceElement createLookupItem(@NotNull final PsiClass psiClass,
final InsertHandler<JavaPsiClassReferenceElement> insertHandler) {
final JavaPsiClassReferenceElement item = new JavaPsiClassReferenceElement(psiClass);
item.setInsertHandler(insertHandler);
return item;
}
}