use runtime type information in debugger evaluation completion

This commit is contained in:
Peter Gromov
2009-09-11 00:44:22 +04:00
parent 7a9f607304
commit 74b2c6cdda
9 changed files with 277 additions and 166 deletions
@@ -354,19 +354,20 @@ public class SetValueAction extends DebuggerAction {
PsiFile psiFile = PsiDocumentManager.getInstance(debuggerContext.getProject()).getPsiFile(editor.getDocument());
EditorEvaluationCommand evaluationCommand = new EditorEvaluationCommand(getEditor(), psiFile, debuggerContext) {
final ProgressWindowWithNotification progressWindow = new ProgressWindowWithNotification(true, getProject());
EditorEvaluationCommand evaluationCommand = new EditorEvaluationCommand(getEditor(), psiFile, debuggerContext, progressWindow) {
public void threadAction() {
try {
evaluate();
}
catch(EvaluateException e) {
getProgressWindow().cancel();
progressWindow.cancel();
}
catch(ProcessCanceledException e) {
getProgressWindow().cancel();
progressWindow.cancel();
}
finally{
if (!getProgressWindow().isCanceled()) {
if (!progressWindow.isCanceled()) {
DebuggerInvocationUtil.swingInvokeLater(debuggerContext.getProject(), new Runnable() {
public void run() {
comboBox.addRecent(text);
@@ -389,7 +390,7 @@ public class SetValueAction extends DebuggerAction {
InvalidTypeException,
EvaluateException,
IncompatibleThreadStateException {
if(!getProgressWindow().isCanceled()) {
if(!progressWindow.isCanceled()) {
setValueRunnable.setValue(evaluationContext, newValue);
node.calcValue();
}
@@ -408,7 +409,6 @@ public class SetValueAction extends DebuggerAction {
}
};
final ProgressWindowWithNotification progressWindow = evaluationCommand.getProgressWindow();
progressWindow.addListener(new ProgressIndicatorListenerAdapter() {
//should return whether to stop processing
public void stopped() {
@@ -5,31 +5,22 @@ import com.intellij.codeInsight.generation.surroundWith.JavaExpressionSurrounder
import com.intellij.debugger.DebuggerBundle;
import com.intellij.debugger.DebuggerInvocationUtil;
import com.intellij.debugger.DebuggerManagerEx;
import com.intellij.debugger.EvaluatingComputable;
import com.intellij.debugger.engine.ContextUtil;
import com.intellij.debugger.engine.evaluation.EvaluateException;
import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil;
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
import com.intellij.debugger.engine.evaluation.expression.EvaluatorBuilderImpl;
import com.intellij.debugger.engine.evaluation.expression.ExpressionEvaluator;
import com.intellij.debugger.impl.DebuggerContextImpl;
import com.intellij.debugger.impl.DebuggerSession;
import com.intellij.debugger.impl.DebuggerUtilsEx;
import com.intellij.debugger.ui.DebuggerExpressionComboBox;
import com.intellij.debugger.ui.EditorEvaluationCommand;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.application.Result;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.ScrollType;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.util.ProgressWindowWithNotification;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.util.IncorrectOperationException;
import com.sun.jdi.Value;
import org.jetbrains.annotations.Nullable;
/**
* User: lex
@@ -46,99 +37,71 @@ public class JavaWithRuntimeCastSurrounder extends JavaExpressionSurrounder {
public boolean isApplicable(PsiExpression expr) {
PsiFile file = expr.getContainingFile();
if (!(file instanceof PsiCodeFragment)) return false;
return file.getUserData(DebuggerExpressionComboBox.KEY) != null;
if (file.getUserData(DebuggerExpressionComboBox.KEY) == null) {
return false;
}
return RuntimeTypeEvaluator.isSubtypeable(expr);
}
public TextRange surroundExpression(Project project, Editor editor, PsiExpression expr) throws IncorrectOperationException {
DebuggerContextImpl debuggerContext = (DebuggerManagerEx.getInstanceEx(project)).getContext();
DebuggerSession debuggerSession = debuggerContext.getDebuggerSession();
if (debuggerSession != null) {
SurroundWithCastWorker worker = new SurroundWithCastWorker(editor, expr, debuggerContext);
worker.getProgressWindow().setTitle(DebuggerBundle.message("title.evaluating"));
debuggerContext.getDebugProcess().getManagerThread().startProgress(worker, worker.getProgressWindow());
final ProgressWindowWithNotification progressWindow = new ProgressWindowWithNotification(true, expr.getProject());
SurroundWithCastWorker worker = new SurroundWithCastWorker(editor, expr, debuggerContext, progressWindow);
progressWindow.setTitle(DebuggerBundle.message("title.evaluating"));
debuggerContext.getDebugProcess().getManagerThread().startProgress(worker, progressWindow);
}
return null;
}
private class SurroundWithCastWorker extends EditorEvaluationCommand<String> {
public SurroundWithCastWorker(Editor editor, PsiExpression expression, DebuggerContextImpl context) {
super(editor, expression, context);
private class SurroundWithCastWorker extends RuntimeTypeEvaluator {
private final Editor myEditor;
public SurroundWithCastWorker(Editor editor, PsiExpression expression, DebuggerContextImpl context, final ProgressIndicator indicator) {
super(editor, expression, context, indicator);
myEditor = editor;
}
protected void executeWriteCommand(final Project project, final Runnable runnable) {
DebuggerInvocationUtil.invokeLater(project, new Runnable() {
public void run() {
ApplicationManager.getApplication().runWriteAction(new Runnable() {
public void run() {
CommandProcessor.getInstance().executeCommand(project, runnable, CodeInsightBundle.message("command.name.surround.with.runtime.cast"), null);
}
});
}
}, getProgressWindow().getModalityState());
}
public void threadAction() {
final String type;
try {
type = evaluate();
}
catch (ProcessCanceledException e) {
@Override
protected void typeCalculationFinished(@Nullable final String type) {
if (type == null) {
return;
}
catch (EvaluateException e) {
return;
}
final Project project = myElement.getProject();
hold();
executeWriteCommand(project, new Runnable() {
final Project project = myElement.getProject();
DebuggerInvocationUtil.invokeLater(project, new Runnable() {
public void run() {
try {
LOG.assertTrue(type != null);
new WriteCommandAction(project, CodeInsightBundle.message("command.name.surround.with.runtime.cast")) {
protected void run(Result result) throws Throwable {
try {
LOG.assertTrue(type != null);
PsiElementFactory factory = JavaPsiFacade.getInstance(myElement.getProject()).getElementFactory();
PsiParenthesizedExpression parenth = (PsiParenthesizedExpression) factory.createExpressionFromText("((" + type + ")expr)", null);
PsiTypeCastExpression cast = (PsiTypeCastExpression) parenth.getExpression();
cast.getOperand().replace(myElement);
parenth = (PsiParenthesizedExpression)JavaCodeStyleManager.getInstance(project).shortenClassReferences(parenth);
PsiExpression expr = (PsiExpression) myElement.replace(parenth);
TextRange range = expr.getTextRange();
getEditor().getSelectionModel().setSelection(range.getStartOffset(), range.getEndOffset());
getEditor().getCaretModel().moveToOffset(range.getEndOffset());
getEditor().getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
}
catch (IncorrectOperationException e) {
// OK here. Can be caused by invalid type like one for proxy starts with . '.Proxy34'
}
finally{
release();
}
PsiElementFactory factory = JavaPsiFacade.getInstance(myElement.getProject()).getElementFactory();
PsiParenthesizedExpression parenth =
(PsiParenthesizedExpression)factory.createExpressionFromText("((" + type + ")expr)", null);
PsiTypeCastExpression cast = (PsiTypeCastExpression)parenth.getExpression();
cast.getOperand().replace(myElement);
parenth = (PsiParenthesizedExpression)JavaCodeStyleManager.getInstance(project).shortenClassReferences(parenth);
PsiExpression expr = (PsiExpression)myElement.replace(parenth);
TextRange range = expr.getTextRange();
myEditor.getSelectionModel().setSelection(range.getStartOffset(), range.getEndOffset());
myEditor.getCaretModel().moveToOffset(range.getEndOffset());
myEditor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
}
catch (IncorrectOperationException e) {
// OK here. Can be caused by invalid type like one for proxy starts with . '.Proxy34'
}
finally {
release();
}
}
}.execute();
}
});
}, myProgressIndicator.getModalityState());
}
protected String evaluate(final EvaluationContextImpl evaluationContext) throws EvaluateException {
final Project project = evaluationContext.getProject();
ExpressionEvaluator evaluator = DebuggerInvocationUtil.commitAndRunReadAction(project, new EvaluatingComputable<ExpressionEvaluator>() {
public ExpressionEvaluator compute() throws EvaluateException {
return EvaluatorBuilderImpl.getInstance().build(myElement, ContextUtil.getSourcePosition(evaluationContext));
}
});
final Value value = evaluator.evaluate(evaluationContext);
if(value != null){
return ApplicationManager.getApplication().runReadAction(new Computable<String>() {
public String compute() {
return DebuggerUtilsEx.getQualifiedClassName(value.type().name(), project);
}
});
}
else {
throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.surrounded.expression.null"));
}
}
}
}
@@ -0,0 +1,84 @@
package com.intellij.debugger.codeinsight;
import com.intellij.debugger.ui.EditorEvaluationCommand;
import com.intellij.debugger.impl.DebuggerContextImpl;
import com.intellij.debugger.impl.DebuggerUtilsEx;
import com.intellij.debugger.engine.evaluation.EvaluateException;
import com.intellij.debugger.engine.evaluation.EvaluationContextImpl;
import com.intellij.debugger.engine.evaluation.EvaluateExceptionUtil;
import com.intellij.debugger.engine.evaluation.expression.ExpressionEvaluator;
import com.intellij.debugger.engine.evaluation.expression.EvaluatorBuilderImpl;
import com.intellij.debugger.engine.ContextUtil;
import com.intellij.debugger.DebuggerInvocationUtil;
import com.intellij.debugger.EvaluatingComputable;
import com.intellij.debugger.DebuggerBundle;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.util.Computable;
import com.intellij.psi.*;
import com.sun.jdi.Value;
import org.jetbrains.annotations.Nullable;
/**
* @author peter
*/
public abstract class RuntimeTypeEvaluator extends EditorEvaluationCommand<String> {
public RuntimeTypeEvaluator(@Nullable Editor editor, PsiElement expression, DebuggerContextImpl context, final ProgressIndicator indicator) {
super(editor, expression, context, indicator);
}
public void threadAction() {
String type = null;
try {
type = evaluate();
}
catch (ProcessCanceledException ignored) {
}
catch (EvaluateException ignored) {
}
finally {
typeCalculationFinished(type);
}
}
protected abstract void typeCalculationFinished(@Nullable String type);
protected String evaluate(final EvaluationContextImpl evaluationContext) throws EvaluateException {
final Project project = evaluationContext.getProject();
ExpressionEvaluator evaluator = DebuggerInvocationUtil.commitAndRunReadAction(project, new EvaluatingComputable<ExpressionEvaluator>() {
public ExpressionEvaluator compute() throws EvaluateException {
return EvaluatorBuilderImpl.getInstance().build(myElement, ContextUtil.getSourcePosition(evaluationContext));
}
});
final Value value = evaluator.evaluate(evaluationContext);
if(value != null){
return ApplicationManager.getApplication().runReadAction(new Computable<String>() {
public String compute() {
return DebuggerUtilsEx.getQualifiedClassName(value.type().name(), project);
}
});
}
else {
throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.surrounded.expression.null"));
}
}
public static boolean isSubtypeable(PsiExpression expr) {
final PsiType type = expr.getType();
if (type instanceof PsiPrimitiveType) {
return false;
}
if (type instanceof PsiClassType) {
final PsiClass psiClass = ((PsiClassType)type).resolve();
if (psiClass != null && psiClass.hasModifierProperty(PsiModifier.FINAL)) {
return false;
}
}
return true;
}
}
@@ -4,15 +4,26 @@
*/
package com.intellij.debugger.engine.evaluation;
import com.intellij.codeInsight.completion.CompletionParameters;
import com.intellij.codeInsight.completion.JavaCompletionUtil;
import com.intellij.codeInsight.completion.CompletionService;
import com.intellij.debugger.DebuggerManagerEx;
import com.intellij.debugger.codeinsight.RuntimeTypeEvaluator;
import com.intellij.debugger.impl.DebuggerContextImpl;
import com.intellij.debugger.impl.DebuggerSession;
import com.intellij.debugger.ui.DebuggerExpressionComboBox;
import com.intellij.openapi.fileTypes.LanguageFileType;
import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.JavaCodeFragment;
import com.intellij.psi.JavaPsiFacade;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiElementFactory;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.util.PairFunction;
import com.intellij.util.concurrency.Semaphore;
import org.jetbrains.annotations.Nullable;
import java.util.concurrent.atomic.AtomicReference;
/**
* @author Eugene Zhuravlev
@@ -31,7 +42,7 @@ public class DefaultCodeFragmentFactory implements CodeFragmentFactory {
return createCodeFragment(item, context, project);
}
public JavaCodeFragment createCodeFragment(TextWithImports item, PsiElement context, Project project) {
public JavaCodeFragment createCodeFragment(TextWithImports item, PsiElement context, final Project project) {
final PsiElementFactory elementFactory = JavaPsiFacade.getInstance(project).getElementFactory();
final String text = item.getText();
@@ -50,6 +61,47 @@ public class DefaultCodeFragmentFactory implements CodeFragmentFactory {
fragment.setVisibilityChecker(JavaCodeFragment.VisibilityChecker.EVERYTHING_VISIBLE);
//noinspection HardCodedStringLiteral
fragment.putUserData(DebuggerExpressionComboBox.KEY, "DebuggerComboBoxEditor.IS_DEBUGGER_EDITOR");
fragment.putCopyableUserData(JavaCompletionUtil.DYNAMIC_TYPE_EVALUATOR, new PairFunction<PsiExpression, CompletionParameters, PsiType>() {
public PsiType fun(PsiExpression expression, CompletionParameters parameters) {
if (!RuntimeTypeEvaluator.isSubtypeable(expression)) {
return null;
}
if (parameters.getInvocationCount() == 1 && JavaCompletionUtil.containsMethodCalls(expression)) {
final CompletionService service = CompletionService.getCompletionService();
if (service.getAdvertisementText() == null) {
service.setAdvertisementText("Invoke completion once more to see runtime type variants");
}
return null;
}
final DebuggerContextImpl debuggerContext = DebuggerManagerEx.getInstanceEx(project).getContext();
DebuggerSession debuggerSession = debuggerContext.getDebuggerSession();
if (debuggerSession != null) {
final Semaphore semaphore = new Semaphore();
semaphore.down();
final AtomicReference<String> nameRef = new AtomicReference<String>();
final RuntimeTypeEvaluator worker =
new RuntimeTypeEvaluator(null, expression, debuggerContext, ProgressManager.getInstance().getProgressIndicator()) {
@Override
protected void typeCalculationFinished(@Nullable String type) {
nameRef.set(type);
semaphore.up();
}
};
debuggerContext.getDebugProcess().getManagerThread().invoke(worker);
semaphore.waitFor(1000);
final String className = nameRef.get();
if (className != null) {
final PsiClass psiClass = JavaPsiFacade.getInstance(project).findClass(className, GlobalSearchScope.allScope(project));
if (psiClass != null) {
return JavaPsiFacade.getElementFactory(project).createType(psiClass);
}
}
}
return null;
}
});
return fragment;
}
@@ -11,27 +11,25 @@ import com.intellij.debugger.engine.events.DebuggerContextCommandImpl;
import com.intellij.debugger.impl.DebuggerContextImpl;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.util.ProgressWindowWithNotification;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiElement;
import org.jetbrains.annotations.Nullable;
/**
* Created by IntelliJ IDEA.
* User: lex
* Date: Mar 15, 2004
* Time: 4:07:59 PM
* To change this template use File | Settings | File Templates.
* @author lex
*/
public abstract class EditorEvaluationCommand<T> extends DebuggerContextCommandImpl {
protected final PsiElement myElement;
private final Editor myEditor;
private final ProgressWindowWithNotification myProgressWindow;
@Nullable private final Editor myEditor;
protected final ProgressIndicator myProgressIndicator;
private final DebuggerContextImpl myDebuggerContext;
public EditorEvaluationCommand(Editor editor, PsiElement expression, DebuggerContextImpl context) {
public EditorEvaluationCommand(@Nullable Editor editor, PsiElement expression, DebuggerContextImpl context,
final ProgressIndicator indicator) {
super(context);
Project project = expression.getProject();
myProgressWindow = new ProgressWindowWithNotification(true, project);
myProgressIndicator = indicator;
myEditor = editor;
myElement = expression;
myDebuggerContext = (DebuggerManagerEx.getInstanceEx(project)).getContext();
@@ -44,20 +42,22 @@ public abstract class EditorEvaluationCommand<T> extends DebuggerContextCommandI
protected abstract T evaluate(EvaluationContextImpl evaluationContext) throws EvaluateException;
public T evaluate() throws EvaluateException {
getProgressWindow().setText(DebuggerBundle.message("progress.evaluating", myElement.getText()));
myProgressIndicator.setText(DebuggerBundle.message("progress.evaluating", myElement.getText()));
try {
T result = evaluate(myDebuggerContext.createEvaluationContext());
if(getProgressWindow().isCanceled()) throw new ProcessCanceledException();
if (myProgressIndicator.isCanceled()) throw new ProcessCanceledException();
return result;
} catch (final EvaluateException e) {
DebuggerInvocationUtil.invokeLater(myDebuggerContext.getProject(), new Runnable() {
public void run() {
showEvaluationHint(myEditor, myElement, e);
}
}, myProgressWindow.getModalityState());
if (myEditor != null) {
DebuggerInvocationUtil.invokeLater(myDebuggerContext.getProject(), new Runnable() {
public void run() {
showEvaluationHint(myEditor, myElement, e);
}
}, myProgressIndicator.getModalityState());
}
throw e;
}
}
@@ -71,11 +71,4 @@ public abstract class EditorEvaluationCommand<T> extends DebuggerContextCommandI
1500);
}
public ProgressWindowWithNotification getProgressWindow() {
return myProgressWindow;
}
public Editor getEditor() {
return myEditor;
}
}
@@ -170,7 +170,7 @@ public class JavaCompletionContributor extends CompletionContributor {
if (ANNOTATION_ATTRIBUTE_NAME.accepts(insertedElement)) {
ApplicationManager.getApplication().runReadAction(new Runnable() {
public void run() {
completeAnnotationAttributeName(_result, file, insertedElement, completionData);
completeAnnotationAttributeName(_result, file, insertedElement, parameters);
}
});
_result.stopHere();
@@ -191,7 +191,7 @@ public class JavaCompletionContributor extends CompletionContributor {
(PsiJavaReference) reference,
new ElementExtractorFilter(filter),
checkAccess,
result.getPrefixMatcher())) {
result.getPrefixMatcher(), parameters)) {
if (isSwitchLabel) {
result.addElement(TailTypeDecorator.createDecorator(element, TailType.createSimpleTailType(':')));
} else {
@@ -257,7 +257,7 @@ public class JavaCompletionContributor extends CompletionContributor {
}
private static void completeAnnotationAttributeName(CompletionResultSet result, PsiFile file, PsiElement insertedElement,
JavaAwareCompletionData completionData) {
CompletionParameters parameters) {
PsiNameValuePair pair = PsiTreeUtil.getParentOfType(insertedElement, PsiNameValuePair.class);
PsiAnnotationParameterList parameterList = (PsiAnnotationParameterList)pair.getParent();
PsiAnnotation anno = (PsiAnnotation)parameterList.getParent();
@@ -275,7 +275,7 @@ public class JavaCompletionContributor extends CompletionContributor {
}
if (showClasses && insertedElement.getParent() instanceof PsiReferenceExpression) {
final Set<LookupElement> set = JavaCompletionUtil.processJavaReference(insertedElement, (PsiJavaReference)insertedElement.getParent(), TrueFilter.INSTANCE, true, result.getPrefixMatcher());
final Set<LookupElement> set = JavaCompletionUtil.processJavaReference(insertedElement, (PsiJavaReference)insertedElement.getParent(), TrueFilter.INSTANCE, true, result.getPrefixMatcher(), parameters);
for (final LookupElement element : set) {
result.addElement(element);
@@ -11,10 +11,10 @@ import com.intellij.codeInsight.guess.GuessManager;
import com.intellij.codeInsight.lookup.*;
import com.intellij.featureStatistics.FeatureUsageTracker;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.*;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.editor.Document;
import static com.intellij.patterns.PlatformPatterns.psiElement;
import com.intellij.patterns.PsiElementPattern;
import com.intellij.psi.*;
@@ -35,6 +35,7 @@ import com.intellij.psi.xml.XmlToken;
import com.intellij.psi.xml.XmlTokenType;
import com.intellij.util.ArrayUtil;
import com.intellij.util.NullableFunction;
import com.intellij.util.PairFunction;
import com.intellij.util.containers.HashMap;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NonNls;
@@ -45,6 +46,7 @@ import java.util.*;
public class JavaCompletionUtil {
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.completion.JavaCompletionUtil");
public static final Key<PairFunction<PsiExpression, CompletionParameters, PsiType>> DYNAMIC_TYPE_EVALUATOR = Key.create("DYNAMIC_TYPE_EVALUATOR");
static final Key<PsiType> QUALIFIER_TYPE_ATTR = Key.create("qualifierType"); // SmartPsiElementPointer to PsiType of "qualifier"
@NonNls
@@ -827,7 +829,7 @@ public class JavaCompletionUtil {
}
public static Set<LookupElement> processJavaReference(PsiElement element, PsiJavaReference javaReference, ElementFilter elementFilter,
final boolean checkAccess, @Nullable final PrefixMatcher matcher) {
final boolean checkAccess, @Nullable final PrefixMatcher matcher, CompletionParameters parameters) {
final THashSet<LookupElement> set = new THashSet<LookupElement>();
final Condition<String> nameCondition = matcher == null ? null : new Condition<String>() {
public boolean value(String s) {
@@ -839,7 +841,7 @@ public class JavaCompletionUtil {
final Collection<CompletionElement> plainResults = processor.getResults();
final PsiType qualifierType = processor.getQualifierType();
PsiType castedQualifierType = addQualifierCastingVariants(javaReference, processor, set);
PsiType castedQualifierType = addQualifierCastingVariants(javaReference, processor, set, parameters);
boolean mayHighlight = qualifierType != null && (castedQualifierType == null || !qualifierType.isAssignableFrom(castedQualifierType));
@@ -854,35 +856,52 @@ public class JavaCompletionUtil {
}
@Nullable
private static PsiType addQualifierCastingVariants(PsiJavaReference javaReference, JavaCompletionProcessor processor, THashSet<LookupElement> set) {
private static PsiType addQualifierCastingVariants(PsiJavaReference javaReference, JavaCompletionProcessor processor, THashSet<LookupElement> set, CompletionParameters parameters) {
if (javaReference instanceof PsiReferenceExpression) {
final PsiReferenceExpression refExpr = (PsiReferenceExpression)javaReference;
final PsiExpression qualifier = refExpr.getQualifierExpression();
if (qualifier != null) {
final Project project = qualifier.getProject();
final PsiType type = GuessManager.getInstance(project).getDataFlowCastedExpressionType(qualifier);
final PairFunction<PsiExpression, CompletionParameters, PsiType> evaluator = refExpr.getContainingFile().getCopyableUserData(DYNAMIC_TYPE_EVALUATOR);
PsiType type = null;
if (evaluator != null) {
type = evaluator.fun(qualifier, parameters);
}
if (type == null) {
type = GuessManager.getInstance(project).getDataFlowCastedExpressionType(qualifier);
}
if (type != null) {
processor.clear();
final String newText = "((" + type.getCanonicalText() + ") " + qualifier.getText() + ")." + refExpr.getReferenceName();
final PsiExpression newRef = JavaPsiFacade.getElementFactory(project).createExpressionFromText(newText, refExpr);
((PsiReferenceExpression)newRef).processVariants(processor);
final LookupElement castItem = PsiTypeLookupItem.createLookupItem(type);
for (CompletionElement completionElement : processor.getResults()) {
final LookupElement item = createLookupElement(completionElement, type);
if (item != null) {
set.add(highlightIfNeeded(type, castQualifier(project, item, castItem)));
}
}
return type;
return addQualifierCastingVariants(processor, refExpr, type, set);
}
}
}
return null;
}
private static PsiType addQualifierCastingVariants(JavaCompletionProcessor processor, PsiReferenceExpression refExpr,
PsiType castTo,
THashSet<LookupElement> set) {
Project project = refExpr.getProject();
PsiExpression qualifier = refExpr.getQualifierExpression();
assert qualifier != null;
final String newText = "((" + castTo.getCanonicalText() + ") " + qualifier.getText() + ")." + refExpr.getReferenceName();
final PsiExpression newRef = JavaPsiFacade.getElementFactory(project).createExpressionFromText(newText, refExpr);
((PsiReferenceExpression)newRef).processVariants(processor);
final LookupElement castItem = PsiTypeLookupItem.createLookupItem(castTo);
for (CompletionElement completionElement : processor.getResults()) {
final LookupElement item = createLookupElement(completionElement, castTo);
if (item != null) {
set.add(highlightIfNeeded(castTo, castQualifier(project, item, castItem)));
}
}
return castTo;
}
private static LookupElementDecorator<LookupElement> castQualifier(final Project project, LookupElement item, final LookupElement to) {
return LookupElementDecorator.withInsertHandler(item, new InsertHandlerDecorator<LookupElement>() {
public void handleInsert(InsertionContext context, LookupElementDecorator<LookupElement> item) {
@@ -999,4 +1018,15 @@ public class JavaCompletionUtil {
ret.setAttribute(LookupItem.TAIL_TEXT_SMALL_ATTR, "");
return ret;
}
@Nullable
static PsiElement getQualifier(final PsiElement element) {
return element instanceof PsiJavaCodeReferenceElement ? ((PsiJavaCodeReferenceElement)element).getQualifier() : null;
}
public static boolean containsMethodCalls(@Nullable final PsiElement qualifier) {
if (qualifier == null) return false;
if (qualifier instanceof PsiMethodCallExpression) return true;
return containsMethodCalls(getQualifier(qualifier));
}
}
@@ -189,7 +189,7 @@ public class JavaSmartCompletionContributor extends CompletionContributor {
final ElementFilter filter = getReferenceFilter(element);
if (filter != null) {
final List<ExpectedTypeInfo> infos = Arrays.asList(getExpectedTypes(parameters));
for (final LookupElement item : completeReference(element, reference, filter, true)) {
for (final LookupElement item : completeReference(element, reference, filter, true, parameters)) {
if (AFTER_THROW_NEW.accepts(element)) {
((LookupItem)item).setAttribute(LookupItem.DONT_CHECK_FOR_INNERS, "");
if (item.getObject() instanceof PsiClass) {
@@ -203,7 +203,7 @@ public class JavaSmartCompletionContributor extends CompletionContributor {
}
else if (INSIDE_TYPECAST.accepts(element)) {
final ReturnTypeFilter rfilter = new ReturnTypeFilter(new GeneratorFilter(AssignableToFilter.class, new CastTypeGetter()));
for (final LookupElement item : completeReference(element, reference, rfilter, false)) {
for (final LookupElement item : completeReference(element, reference, rfilter, false, parameters)) {
result.addElement(item);
}
}
@@ -626,7 +626,7 @@ public class JavaSmartCompletionContributor extends CompletionContributor {
result.addElement(decorate(item, infos));
}
static Set<LookupElement> completeReference(final PsiElement element, PsiReference reference, final ElementFilter filter, final boolean acceptClasses) {
static Set<LookupElement> completeReference(final PsiElement element, PsiReference reference, final ElementFilter filter, final boolean acceptClasses, CompletionParameters parameters) {
if (reference instanceof PsiMultiReference) {
reference = ContainerUtil.findInstance(((PsiMultiReference) reference).getReferences(), PsiJavaReference.class);
}
@@ -651,7 +651,7 @@ public class JavaSmartCompletionContributor extends CompletionContributor {
ReflectionCache.isAssignable(CandidateInfo.class, hintClass) ||
ReflectionCache.isAssignable(PsiKeyword.class, hintClass);
}
}, true, null);
}, true, null, parameters);
}
return Collections.emptySet();
@@ -95,14 +95,14 @@ public class ReferenceExpressionCompletionContributor extends ExpressionSmartCom
final PsiReference reference = element.getContainingFile().findReferenceAt(offset);
if (reference != null) {
final ElementFilter filter = getReferenceFilter(element, false, false);
final Set<LookupElement> set = JavaSmartCompletionContributor.completeReference(element, reference, filter, false);
final Set<LookupElement> set = JavaSmartCompletionContributor.completeReference(element, reference, filter, false, parameters);
for (final LookupElement item : set) {
result.addElement(item);
}
if (parameters.getInvocationCount() >= 2) {
ElementFilter baseFilter = getReferenceFilter(element, true, false);
for (final LookupElement baseItem : JavaSmartCompletionContributor.completeReference(element, reference, baseFilter, false)) {
for (final LookupElement baseItem : JavaSmartCompletionContributor.completeReference(element, reference, baseFilter, false, parameters)) {
addSecondCompletionVariants(element, reference, baseItem, parameters, result);
}
@@ -127,11 +127,11 @@ public class ReferenceExpressionCompletionContributor extends ExpressionSmartCom
PsiType itemType = JavaCompletionUtil.getLookupElementType(baseItem);
if (itemType == null) return;
final PsiElement qualifier = getQualifier(reference.getElement());
final PsiElement qualifier = JavaCompletionUtil.getQualifier(reference.getElement());
final PsiType expectedType = parameters.getExpectedType();
if (!OBJECT_METHOD_PATTERN.accepts(object) || allowGetClass(object, parameters)) {
if (!itemType.equalsToText(CommonClassNames.JAVA_LANG_STRING)) {
addChainedCallVariants(element, baseItem, result, itemType, expectedType);
addChainedCallVariants(element, baseItem, result, itemType, expectedType, parameters);
}
}
@@ -191,11 +191,6 @@ public class ReferenceExpressionCompletionContributor extends ExpressionSmartCom
return false;
}
@Nullable
private static PsiElement getQualifier(final PsiElement element) {
return element instanceof PsiJavaCodeReferenceElement ? ((PsiJavaCodeReferenceElement)element).getQualifier() : null;
}
private static void addArraysAsListConversions(final PsiElement element, final String prefix, final PsiType itemType, final CompletionResultSet result,
@Nullable PsiElement qualifier,
final PsiType expectedType) throws IncorrectOperationException {
@@ -261,7 +256,7 @@ public class ReferenceExpressionCompletionContributor extends ExpressionSmartCom
}
final String bracketSpace = getSpace(CodeStyleSettingsManager.getSettings(element.getProject()).SPACE_WITHIN_BRACKETS);
if (object instanceof PsiVariable && !containsMethodCalls(qualifier)) {
if (object instanceof PsiVariable && !JavaCompletionUtil.containsMethodCalls(qualifier)) {
final PsiVariable variable = (PsiVariable)object;
addToArrayConversion(element, prefix,
"new " + componentType.getCanonicalText() +
@@ -303,16 +298,10 @@ public class ReferenceExpressionCompletionContributor extends ExpressionSmartCom
return qualifier == null ? "" : qualifier.getText() + ".";
}
private static boolean containsMethodCalls(@Nullable final PsiElement qualifier) {
if (qualifier == null) return false;
if (qualifier instanceof PsiMethodCallExpression) return true;
return containsMethodCalls(getQualifier(qualifier));
}
private static void addChainedCallVariants(final PsiElement place, final LookupElement qualifierItem,
final CompletionResultSet result,
PsiType qualifierType,
final PsiType expectedType) throws IncorrectOperationException {
final PsiType expectedType, CompletionParameters parameters) throws IncorrectOperationException {
final PsiElementFactory elementFactory = JavaPsiFacade.getInstance(place.getProject()).getElementFactory();
final String typeText = qualifierType instanceof PsiEllipsisType ? ((PsiEllipsisType)qualifierType).getComponentType().getCanonicalText() + "[]" : qualifierType.getCanonicalText();
final JavaCodeFragment block = elementFactory.createCodeBlockCodeFragment(typeText + " xxx;xxx.xxx;", place, false);
@@ -324,7 +313,7 @@ public class ReferenceExpressionCompletionContributor extends ExpressionSmartCom
final PsiReferenceExpression mockRef = (PsiReferenceExpression) expressionStatement.getExpression();
final ElementFilter filter = getReferenceFilter(place, false, true);
for (final LookupElement item : JavaSmartCompletionContributor.completeReference(place, mockRef, filter, false)) {
for (final LookupElement item : JavaSmartCompletionContributor.completeReference(place, mockRef, filter, false, parameters)) {
if (shoudChain(place, qualifierType, expectedType, item)) {
result.addElement(JavaChainLookupElement.chainElements(qualifierItem, item));
}