From 74b2c6cdda7abdaf9054e77821c9ca67a505f378 Mon Sep 17 00:00:00 2001 From: Peter Gromov Date: Fri, 11 Sep 2009 00:44:22 +0400 Subject: [PATCH] use runtime type information in debugger evaluation completion --- .../debugger/actions/SetValueAction.java | 12 +- .../JavaWithRuntimeCastSurrounder.java | 137 +++++++----------- .../codeinsight/RuntimeTypeEvaluator.java | 84 +++++++++++ .../DefaultCodeFragmentFactory.java | 62 +++++++- .../debugger/ui/EditorEvaluationCommand.java | 41 +++--- .../completion/JavaCompletionContributor.java | 8 +- .../completion/JavaCompletionUtil.java | 66 ++++++--- .../JavaSmartCompletionContributor.java | 8 +- ...erenceExpressionCompletionContributor.java | 25 +--- 9 files changed, 277 insertions(+), 166 deletions(-) create mode 100644 java/debugger/impl/src/com/intellij/debugger/codeinsight/RuntimeTypeEvaluator.java diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/SetValueAction.java b/java/debugger/impl/src/com/intellij/debugger/actions/SetValueAction.java index 1991f7ebbc55..24680f5bc01a 100644 --- a/java/debugger/impl/src/com/intellij/debugger/actions/SetValueAction.java +++ b/java/debugger/impl/src/com/intellij/debugger/actions/SetValueAction.java @@ -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() { diff --git a/java/debugger/impl/src/com/intellij/debugger/codeinsight/JavaWithRuntimeCastSurrounder.java b/java/debugger/impl/src/com/intellij/debugger/codeinsight/JavaWithRuntimeCastSurrounder.java index 0c5f5eadbbe5..3da5c1856de6 100644 --- a/java/debugger/impl/src/com/intellij/debugger/codeinsight/JavaWithRuntimeCastSurrounder.java +++ b/java/debugger/impl/src/com/intellij/debugger/codeinsight/JavaWithRuntimeCastSurrounder.java @@ -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 { - 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() { - 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() { - public String compute() { - return DebuggerUtilsEx.getQualifiedClassName(value.type().name(), project); - } - }); - } - else { - throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.surrounded.expression.null")); - } - } } } diff --git a/java/debugger/impl/src/com/intellij/debugger/codeinsight/RuntimeTypeEvaluator.java b/java/debugger/impl/src/com/intellij/debugger/codeinsight/RuntimeTypeEvaluator.java new file mode 100644 index 000000000000..1adac21f51de --- /dev/null +++ b/java/debugger/impl/src/com/intellij/debugger/codeinsight/RuntimeTypeEvaluator.java @@ -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 { + 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() { + 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() { + 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; + } +} diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/DefaultCodeFragmentFactory.java b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/DefaultCodeFragmentFactory.java index 1e412ee04371..44bcfe35f446 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/DefaultCodeFragmentFactory.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/evaluation/DefaultCodeFragmentFactory.java @@ -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() { + 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 nameRef = new AtomicReference(); + 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; } diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/EditorEvaluationCommand.java b/java/debugger/impl/src/com/intellij/debugger/ui/EditorEvaluationCommand.java index e8a6e2c5113a..03100d83a65f 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/EditorEvaluationCommand.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/EditorEvaluationCommand.java @@ -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 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 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 extends DebuggerContextCommandI 1500); } - public ProgressWindowWithNotification getProgressWindow() { - return myProgressWindow; - } - - public Editor getEditor() { - return myEditor; - } } diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionContributor.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionContributor.java index a4c32006f43a..a8f5e830d15d 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionContributor.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionContributor.java @@ -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 set = JavaCompletionUtil.processJavaReference(insertedElement, (PsiJavaReference)insertedElement.getParent(), TrueFilter.INSTANCE, true, result.getPrefixMatcher()); + final Set set = JavaCompletionUtil.processJavaReference(insertedElement, (PsiJavaReference)insertedElement.getParent(), TrueFilter.INSTANCE, true, result.getPrefixMatcher(), parameters); for (final LookupElement element : set) { result.addElement(element); 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 83caae82459b..28d3d0483d1f 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java @@ -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> DYNAMIC_TYPE_EVALUATOR = Key.create("DYNAMIC_TYPE_EVALUATOR"); static final Key QUALIFIER_TYPE_ATTR = Key.create("qualifierType"); // SmartPsiElementPointer to PsiType of "qualifier" @NonNls @@ -827,7 +829,7 @@ public class JavaCompletionUtil { } public static Set 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 set = new THashSet(); final Condition nameCondition = matcher == null ? null : new Condition() { public boolean value(String s) { @@ -839,7 +841,7 @@ public class JavaCompletionUtil { final Collection 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 set) { + private static PsiType addQualifierCastingVariants(PsiJavaReference javaReference, JavaCompletionProcessor processor, THashSet 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 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 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 castQualifier(final Project project, LookupElement item, final LookupElement to) { return LookupElementDecorator.withInsertHandler(item, new InsertHandlerDecorator() { public void handleInsert(InsertionContext context, LookupElementDecorator 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)); + } } diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaSmartCompletionContributor.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaSmartCompletionContributor.java index 8d54e6cd9efe..5c8d694bec1b 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaSmartCompletionContributor.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaSmartCompletionContributor.java @@ -189,7 +189,7 @@ public class JavaSmartCompletionContributor extends CompletionContributor { final ElementFilter filter = getReferenceFilter(element); if (filter != null) { final List 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 completeReference(final PsiElement element, PsiReference reference, final ElementFilter filter, final boolean acceptClasses) { + static Set 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(); diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/ReferenceExpressionCompletionContributor.java b/java/java-impl/src/com/intellij/codeInsight/completion/ReferenceExpressionCompletionContributor.java index 41f6f315b34f..753599ee0363 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/ReferenceExpressionCompletionContributor.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/ReferenceExpressionCompletionContributor.java @@ -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 set = JavaSmartCompletionContributor.completeReference(element, reference, filter, false); + final Set 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)); }