package com.google.jstestdriver.idea.assertFramework.jstd; import com.google.jstestdriver.idea.util.CastUtils; import com.google.jstestdriver.idea.util.JsPsiUtils; import com.google.jstestdriver.idea.util.ObjectUtils; import com.intellij.codeHighlighting.HighlightDisplayLevel; import com.intellij.codeInspection.*; import com.intellij.lang.javascript.inspections.JSInspection; import com.intellij.lang.javascript.psi.*; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.libraries.scripting.ScriptingLibraryModel; import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElementVisitor; import com.intellij.psi.PsiPolyVariantReference; import com.intellij.psi.ResolveResult; import com.intellij.util.FileContentUtil; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; import java.util.Arrays; public class JstdDefaultAssertionFrameworkSupportInspection extends JSInspection { private static final Logger LOG = Logger.getInstance(JstdDefaultAssertionFrameworkSupportInspection.class); private static final AddJstdLibraryLocalQuickFix ADD_JSTD_LIBRARY_LOCAL_QUICK_FIX = new AddJstdLibraryLocalQuickFix(); @NotNull @Override public String getShortName() { return "JsTestDriverDefaultAssertionFrameworkSupport"; } @Override protected PsiElementVisitor createVisitor(final ProblemsHolder holder, LocalInspectionToolSession session) { return new JSElementVisitor() { @Override public void visitJSCallExpression(final JSCallExpression jsCallExpression) { JSReferenceExpression methodExpression = CastUtils.tryCast(jsCallExpression.getMethodExpression(), JSReferenceExpression.class); JSArgumentList jsArgumentList = jsCallExpression.getArgumentList(); if (methodExpression != null && jsArgumentList != null) { JSExpression[] arguments = ObjectUtils.notNull(jsArgumentList.getArguments(), JSExpression.EMPTY_ARRAY); boolean suitableSymbol = isSuitableSymbol(methodExpression.getReferencedName(), arguments); if (suitableSymbol) { boolean resolved = canBeResolved(methodExpression); if (!resolved) { holder.registerProblem( methodExpression, getDisplayName(), ProblemHighlightType.GENERIC_ERROR, TextRange.create(0, methodExpression.getTextLength()), ADD_JSTD_LIBRARY_LOCAL_QUICK_FIX ); } } } } }; } private static boolean isSuitableSymbol(String methodName, JSExpression[] arguments) { if (arguments.length < 1) { return false; } if (!JsPsiUtils.isStringElement(arguments[0])) { return false; } if ("TestCase".equals(methodName) || "AsyncTestCase".equals(methodName)) { if (arguments.length == 1) { return true; } if (arguments.length == 2 && JsPsiUtils.isObjectElement(arguments[1])) { return true; } } return false; } @Nls @NotNull @Override public String getDisplayName() { return ADD_JSTD_LIBRARY_LOCAL_QUICK_FIX.getName(); } private static boolean canBeResolved(PsiPolyVariantReference psiPolyVariantReference) { ResolveResult[] resolveResults = psiPolyVariantReference.multiResolve(false); for (ResolveResult resolveResult : resolveResults) { PsiElement resolvedElement = resolveResult.getElement(); if (resolvedElement != null && resolveResult.isValidResult()) { return true; } } return false; } private static class AddJstdLibraryLocalQuickFix implements LocalQuickFix { @NotNull @Override public String getName() { return "Add JsTestDriver assertion framework support"; } @NotNull @Override public String getFamilyName() { return getName(); } @Override public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { PsiElement psiElement = descriptor.getPsiElement(); final ScriptingLibraryModel scriptingLibraryModel = JsAssertFrameworkLibraryManager.createScriptingLibraryModelAndAssociateIt( project, project.getBaseDir() ); FileContentUtil.reparseFiles(project, Arrays.asList(psiElement.getContainingFile().getVirtualFile()), true); if (scriptingLibraryModel == null) { System.out.println("Unable to create library '" + JsAssertFrameworkLibraryManager.LIBRARY_NAME + "'"); } } } @NotNull public HighlightDisplayLevel getDefaultLevel() { return HighlightDisplayLevel.ERROR; } }