/* * Copyright 2013 Guidewire Software, Inc. */ package gw.plugin.ij.debugger.evaluation; import com.intellij.codeInsight.completion.CompletionParameters; import com.intellij.codeInsight.completion.CompletionService; import com.intellij.codeInsight.completion.JavaCompletionUtil; import com.intellij.debugger.DebuggerManagerEx; import com.intellij.debugger.codeinsight.RuntimeTypeEvaluator; import com.intellij.debugger.engine.evaluation.CodeFragmentFactory; import com.intellij.debugger.engine.evaluation.TextWithImports; import com.intellij.debugger.engine.evaluation.expression.EvaluatorBuilder; 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.progress.ProgressManager; import com.intellij.openapi.project.Project; import com.intellij.psi.*; import com.intellij.util.PairFunction; import com.intellij.util.concurrency.Semaphore; import gw.plugin.ij.filetypes.GosuCodeFileType; import gw.plugin.ij.lang.psi.impl.AbstractGosuClassFileImpl; import gw.plugin.ij.lang.psi.impl.GosuFragmentFileImpl; import org.jetbrains.annotations.Nullable; import java.util.concurrent.atomic.AtomicReference; public class GosuCodeFragmentFactory extends CodeFragmentFactory { private static final class SingletonHolder { public static final GosuCodeFragmentFactory ourInstance = new GosuCodeFragmentFactory(); } public static GosuCodeFragmentFactory getInstance() { return SingletonHolder.ourInstance; } public JavaCodeFragment createPresentationCodeFragment( final TextWithImports item, final PsiElement context, final Project project ) { return createCodeFragment( item, context, project ); } public JavaCodeFragment createCodeFragment( TextWithImports item, PsiElement context, final Project project ) { final String text = item.getText(); final GosuFragmentFileImpl fragment = createExpressionCodeFragment( context, project, text ); if( item.getImports().length() > 0 ) { fragment.addImportsFromString( item.getImports() ); } 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 && containsMethodCalls( expression ) ) { final CompletionService service = CompletionService.getCompletionService(); if( service.getAdvertisementText() == null && parameters.getInvocationCount() < 2 ) { 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<PsiClass> nameRef = new AtomicReference<>(); final RuntimeTypeEvaluator worker = new RuntimeTypeEvaluator( null, expression, debuggerContext, ProgressManager.getInstance().getProgressIndicator() ) { @Override protected void typeCalculationFinished( @Nullable PsiClass type ) { nameRef.set( type ); semaphore.up(); } }; debuggerContext.getDebugProcess().getManagerThread().invoke( worker ); for( int i = 0; i < 50; i++ ) { ProgressManager.checkCanceled(); if( semaphore.waitFor( 20 ) ) { break; } } final PsiClass psiClass = nameRef.get(); if( psiClass != null ) { return JavaPsiFacade.getElementFactory( project ).createType( psiClass ); } } return null; } } ); return fragment; } public static boolean containsMethodCalls(@Nullable final PsiElement qualifier) { if (qualifier == null) return false; if (qualifier instanceof PsiMethodCallExpression || qualifier instanceof PsiNewExpression) return true; if (qualifier instanceof PsiArrayAccessExpression) { return containsMethodCalls(((PsiArrayAccessExpression)qualifier).getArrayExpression()); } return containsMethodCalls(getQualifier(qualifier)); } @Nullable static PsiElement getQualifier(final PsiElement element) { return element instanceof PsiJavaCodeReferenceElement ? ((PsiJavaCodeReferenceElement)element).getQualifier() : null; } private GosuFragmentFileImpl createExpressionCodeFragment( PsiElement context, Project project, String text ) { return new GosuFragmentFileImpl( project, text, context ); } public boolean isContextAccepted( PsiElement contextElement ) { return contextElement == null || contextElement.getContainingFile() instanceof AbstractGosuClassFileImpl; } public LanguageFileType getFileType() { return GosuCodeFileType.INSTANCE; } @Override public EvaluatorBuilder getEvaluatorBuilder() { return GosuEvaluatorBuilderImpl.instance(); } }