From 8640a09ed0e60f587cbd23ce8e84d9670a7c242f Mon Sep 17 00:00:00 2001 From: Semyon Proshev Date: Wed, 13 Jul 2016 21:38:33 +0300 Subject: [PATCH] Attempt to make PythonRegexpInjector more readable --- .../regexp/PythonRegexpInjector.java | 126 +++++++++--------- 1 file changed, 64 insertions(+), 62 deletions(-) diff --git a/python/src/com/jetbrains/python/codeInsight/regexp/PythonRegexpInjector.java b/python/src/com/jetbrains/python/codeInsight/regexp/PythonRegexpInjector.java index 92628c6b2040..855f433b649f 100644 --- a/python/src/com/jetbrains/python/codeInsight/regexp/PythonRegexpInjector.java +++ b/python/src/com/jetbrains/python/codeInsight/regexp/PythonRegexpInjector.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2014 JetBrains s.r.o. + * Copyright 2000-2016 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. @@ -21,7 +21,6 @@ import com.intellij.lang.injection.MultiHostRegistrar; import com.intellij.openapi.util.Pair; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; -import com.intellij.psi.PsiPolyVariantReference; import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil; import com.intellij.psi.impl.source.tree.injected.MultiHostRegistrarImpl; import com.intellij.psi.impl.source.tree.injected.Place; @@ -72,33 +71,40 @@ public class PythonRegexpInjector implements MultiHostInjector { @Override public void getLanguagesToInject(@NotNull MultiHostRegistrar registrar, @NotNull PsiElement context) { - final PsiElement contextParent = context.getParent(); - if (PyInjectionUtil.getLargestStringLiteral(context) == context && contextParent instanceof PyArgumentList) { - final PyExpression[] args = ((PyArgumentList)contextParent).getArguments(); - int index = ArrayUtil.indexOf(args, context); - PyCallExpression call = PsiTreeUtil.getParentOfType(context, PyCallExpression.class); + final PyArgumentList argumentList = PyUtil.as(context.getParent(), PyArgumentList.class); + if (PyInjectionUtil.getLargestStringLiteral(context) == context && argumentList != null) { + final PyCallExpression call = PsiTreeUtil.getParentOfType(context, PyCallExpression.class); if (call != null) { - final PyExpression callee = call.getCallee(); - if (callee instanceof PyReferenceExpression && canBeRegexpCall(callee)) { - final PsiPolyVariantReference ref = ((PyReferenceExpression)callee).getReference(PyResolveContext.noImplicits()); - if (ref != null) { - final PsiElement element = ref.resolve(); - if (element != null && ScopeUtil.getScopeOwner(element) instanceof PyFile && - element.getContainingFile().getName().equals("re.py") && isRegexpMethod(element, index)) { - final Language language = isVerbose(call) ? PythonVerboseRegexpLanguage.INSTANCE : PythonRegexpLanguage.INSTANCE; - registrar.startInjecting(language); - final PyInjectionUtil.InjectionResult result = PyInjectionUtil.registerStringLiteralInjection(context, registrar); - if (result.isInjected()) { - registrar.doneInjecting(); - if (!result.isStrict()) { - final PsiFile file = getInjectedFile(registrar); - if (file != null) { - file.putUserData(InjectedLanguageUtil.FRANKENSTEIN_INJECTION, Boolean.TRUE); - } - } - } - } - } + final RegexpMethodDescriptor methodDescriptor = findRegexpMethodDescriptor(resolvePossibleRegexpCall(call)); + if (methodDescriptor != null && methodDescriptor.argIndex == ArrayUtil.indexOf(argumentList.getArguments(), context)) { + injectRegexpLanguage(registrar, context, isVerbose(call)); + } + } + } + } + + @Nullable + private PsiElement resolvePossibleRegexpCall(@NotNull PyCallExpression call) { + final PyExpression callee = call.getCallee(); + + if (callee instanceof PyReferenceExpression && canBeRegexpCall(callee)) { + final PyReferenceExpression referenceExpression = (PyReferenceExpression)callee; + return referenceExpression.getReference(PyResolveContext.noImplicits()).resolve(); + } + + return null; + } + + private static void injectRegexpLanguage(@NotNull MultiHostRegistrar registrar, @NotNull PsiElement context, boolean verbose) { + final Language language = verbose ? PythonVerboseRegexpLanguage.INSTANCE : PythonRegexpLanguage.INSTANCE; + registrar.startInjecting(language); + final PyInjectionUtil.InjectionResult result = PyInjectionUtil.registerStringLiteralInjection(context, registrar); + if (result.isInjected()) { + registrar.doneInjecting(); + if (!result.isStrict()) { + final PsiFile file = getInjectedFile(registrar); + if (file != null) { + file.putUserData(InjectedLanguageUtil.FRANKENSTEIN_INJECTION, Boolean.TRUE); } } } @@ -107,7 +113,7 @@ public class PythonRegexpInjector implements MultiHostInjector { @Nullable private static PsiFile getInjectedFile(@NotNull MultiHostRegistrar registrar) { // Don't add a dependency on IntelliLang here now, but this injector should become IntelliLang-based in the future - final List> result = ((MultiHostRegistrarImpl)registrar).getResult(); + final List> result = ((MultiHostRegistrarImpl)registrar).getResult(); return result == null || result.isEmpty() ? null : result.get(result.size() - 1).second; } @@ -119,51 +125,47 @@ public class PythonRegexpInjector implements MultiHostInjector { } private static boolean isVerbose(@NotNull PyCallExpression call) { - PyExpression[] arguments = call.getArguments(); - if (arguments.length <= 1) { - return false; - } - return isVerbose(arguments[arguments.length-1]); + final PyExpression[] arguments = call.getArguments(); + return arguments.length > 1 && isVerbose(arguments[arguments.length - 1]); } - private static boolean isVerbose(@Nullable PyExpression expr) { - if (expr instanceof PyKeywordArgument) { - PyKeywordArgument keywordArgument = (PyKeywordArgument)expr; - if (!"flags".equals(keywordArgument.getName())) { - return false; - } - return isVerbose(keywordArgument.getValueExpression()); + private static boolean isVerbose(@Nullable PyExpression expression) { + if (expression instanceof PyKeywordArgument) { + final PyKeywordArgument keywordArgument = (PyKeywordArgument)expression; + return "flags".equals(keywordArgument.getName()) && isVerbose(keywordArgument.getValueExpression()); } - if (expr instanceof PyReferenceExpression) { - final String flagName = ((PyReferenceExpression)expr).getReferencedName(); + if (expression instanceof PyReferenceExpression) { + final String flagName = ((PyReferenceExpression)expression).getReferencedName(); return "VERBOSE".equals(flagName) || "X".equals(flagName); } - if (expr instanceof PyBinaryExpression) { - return isVerbose(((PyBinaryExpression)expr).getLeftExpression()) || isVerbose(((PyBinaryExpression)expr).getRightExpression()); + if (expression instanceof PyBinaryExpression) { + final PyBinaryExpression binaryExpression = (PyBinaryExpression)expression; + return isVerbose(binaryExpression.getLeftExpression()) || isVerbose(binaryExpression.getRightExpression()); } return false; } - private boolean isRegexpMethod(@NotNull PsiElement element, int index) { - if (!(element instanceof PyFunction)) { - return false; + @Nullable + private RegexpMethodDescriptor findRegexpMethodDescriptor(@Nullable PsiElement element) { + if (element == null || + !(ScopeUtil.getScopeOwner(element) instanceof PyFile) || + !element.getContainingFile().getName().equals("re.py") || + !(element instanceof PyFunction)) { + return null; } - final String name = ((PyFunction)element).getName(); - for (RegexpMethodDescriptor descriptor : myDescriptors) { - if (descriptor.methodName.equals(name) && descriptor.argIndex == index) { - return true; - } - } - return false; + + final String functionName = ((PyFunction)element).getName(); + return myDescriptors + .stream() + .filter(descriptor -> descriptor.methodName.equals(functionName)) + .findAny() + .orElse(null); } private boolean canBeRegexpCall(@NotNull PyExpression callee) { - String text = callee.getText(); - for (RegexpMethodDescriptor descriptor : myDescriptors) { - if (text.endsWith(descriptor.methodName)) { - return true; - } - } - return false; + final String text = callee.getText(); + return myDescriptors + .stream() + .anyMatch(descriptor -> text.endsWith(descriptor.methodName)); } }