mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
PyParameterInfoHandler uses PyCallExpression.mapArguments() instead of CallArgumentsMapping
Switched everything in PyParameterInfoHandler but Python 2 tuple parameters handling: 4 tests are still failing.
This commit is contained in:
@@ -140,17 +140,33 @@ public interface PyCallExpression extends PyCallSiteExpression {
|
||||
boolean isCallee(@NotNull FQNamesProvider... name);
|
||||
|
||||
class PyArgumentsMapping {
|
||||
@NotNull private final PyCallExpression myCallExpression;
|
||||
@Nullable private final PyMarkedCallee myCallee;
|
||||
@NotNull private final Map<PyExpression, PyNamedParameter> myMappedParameters;
|
||||
@NotNull private final List<PyParameter> myUnmappedParameters;
|
||||
@NotNull private final List<PyExpression> myUnmappedArguments;
|
||||
@NotNull private final List<PyNamedParameter> myParametersMappedToVariadicPositionalArguments;
|
||||
@NotNull private final List<PyNamedParameter> myParametersMappedToVariadicKeywordArguments;
|
||||
|
||||
public PyArgumentsMapping(@Nullable PyMarkedCallee markedCallee, @NotNull Map<PyExpression, PyNamedParameter> mappedParameters,
|
||||
@NotNull List<PyParameter> unmappedParameters, @NotNull List<PyExpression> unmappedArguments) {
|
||||
public PyArgumentsMapping(@NotNull PyCallExpression expression,
|
||||
@Nullable PyMarkedCallee markedCallee,
|
||||
@NotNull Map<PyExpression, PyNamedParameter> mappedParameters,
|
||||
@NotNull List<PyParameter> unmappedParameters,
|
||||
@NotNull List<PyExpression> unmappedArguments,
|
||||
@NotNull List<PyNamedParameter> parametersMappedToVariadicPositionalArguments,
|
||||
@NotNull List<PyNamedParameter> parametersMappedToVariadicKeywordArguments) {
|
||||
myCallExpression = expression;
|
||||
myCallee = markedCallee;
|
||||
myMappedParameters = mappedParameters;
|
||||
myUnmappedParameters = unmappedParameters;
|
||||
myUnmappedArguments = unmappedArguments;
|
||||
myParametersMappedToVariadicPositionalArguments = parametersMappedToVariadicPositionalArguments;
|
||||
myParametersMappedToVariadicKeywordArguments = parametersMappedToVariadicKeywordArguments;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public PyCallExpression getCallExpression() {
|
||||
return myCallExpression;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -172,6 +188,16 @@ public interface PyCallExpression extends PyCallSiteExpression {
|
||||
public List<PyExpression> getUnmappedArguments() {
|
||||
return myUnmappedArguments;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<PyNamedParameter> getParametersMappedToVariadicPositionalArguments() {
|
||||
return myParametersMappedToVariadicPositionalArguments;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<PyNamedParameter> getParametersMappedToVariadicKeywordArguments() {
|
||||
return myParametersMappedToVariadicKeywordArguments;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -26,6 +26,7 @@ import com.intellij.util.text.CharArrayUtil;
|
||||
import com.intellij.xml.util.XmlStringUtil;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.impl.ParamHelper;
|
||||
import com.jetbrains.python.psi.impl.PyCallExpressionHelper;
|
||||
import com.jetbrains.python.psi.resolve.PyResolveContext;
|
||||
import com.jetbrains.python.psi.types.TypeEvalContext;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -37,29 +38,37 @@ import static com.jetbrains.python.psi.PyCallExpression.PyMarkedCallee;
|
||||
/**
|
||||
* @author dcheryasov
|
||||
*/
|
||||
public class PyParameterInfoHandler implements ParameterInfoHandler<PyArgumentList, CallArgumentsMapping> {
|
||||
public class PyParameterInfoHandler implements ParameterInfoHandler<PyArgumentList, PyCallExpression.PyArgumentsMapping> {
|
||||
private static final String NO_PARAMS_MSG = CodeInsightBundle.message("parameter.info.no.parameters");
|
||||
|
||||
@Override
|
||||
public boolean couldShowInLookup() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] getParametersForLookup(final LookupElement item, final ParameterInfoContext context) {
|
||||
return ArrayUtil.EMPTY_OBJECT_ARRAY; // we don't
|
||||
return ArrayUtil.EMPTY_OBJECT_ARRAY;
|
||||
}
|
||||
|
||||
public Object[] getParametersForDocumentation(final CallArgumentsMapping p, final ParameterInfoContext context) {
|
||||
return ArrayUtil.EMPTY_OBJECT_ARRAY; // we don't
|
||||
@Override
|
||||
public Object[] getParametersForDocumentation(final PyCallExpression.PyArgumentsMapping p, final ParameterInfoContext context) {
|
||||
return ArrayUtil.EMPTY_OBJECT_ARRAY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PyArgumentList findElementForParameterInfo(@NotNull final CreateParameterInfoContext context) {
|
||||
PyArgumentList arglist = findArgumentList(context);
|
||||
if (arglist != null) {
|
||||
final TypeEvalContext typeEvalContext = TypeEvalContext.userInitiated(arglist.getProject(), arglist.getContainingFile());
|
||||
final PyResolveContext resolveContext = PyResolveContext.noImplicits().withTypeEvalContext(typeEvalContext);
|
||||
CallArgumentsMapping result = arglist.analyzeCall(resolveContext);
|
||||
if (result.getMarkedCallee() != null) {
|
||||
context.setItemsToShow(new Object[] { result });
|
||||
return arglist;
|
||||
PyArgumentList argumentList = findArgumentList(context);
|
||||
if (argumentList != null) {
|
||||
final PyCallExpression callExpr = argumentList.getCallExpression();
|
||||
if (callExpr != null) {
|
||||
final TypeEvalContext typeEvalContext = TypeEvalContext.userInitiated(argumentList.getProject(), argumentList.getContainingFile());
|
||||
final PyResolveContext resolveContext = PyResolveContext.noImplicits().withTypeEvalContext(typeEvalContext);
|
||||
final PyCallExpression.PyArgumentsMapping mapping = callExpr.mapArguments(resolveContext);
|
||||
if (mapping.getMarkedCallee() != null) {
|
||||
context.setItemsToShow(new Object[] { mapping });
|
||||
return argumentList;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@@ -69,10 +78,12 @@ public class PyParameterInfoHandler implements ParameterInfoHandler<PyArgumentLi
|
||||
return ParameterInfoUtils.findParentOfType(context.getFile(), context.getOffset(), PyArgumentList.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showParameterInfo(@NotNull final PyArgumentList element, @NotNull final CreateParameterInfoContext context) {
|
||||
context.showHint(element, element.getTextOffset(), this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PyArgumentList findElementForUpdatingParameterInfo(@NotNull final UpdateParameterInfoContext context) {
|
||||
return findArgumentList(context);
|
||||
}
|
||||
@@ -125,15 +136,15 @@ public class PyParameterInfoHandler implements ParameterInfoHandler<PyArgumentLi
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateUI(final CallArgumentsMapping prevResult, @NotNull final ParameterInfoUIContext context) {
|
||||
if (prevResult == null) return;
|
||||
final PyArgumentList argList = prevResult.getArgumentList();
|
||||
if (!argList.isValid()) return;
|
||||
public void updateUI(final PyCallExpression.PyArgumentsMapping oldMapping, @NotNull final ParameterInfoUIContext context) {
|
||||
if (oldMapping == null) return;
|
||||
final PyCallExpression callExpression = oldMapping.getCallExpression();
|
||||
if (!callExpression.isValid()) return;
|
||||
// really we need to redo analysis every UI update; findElementForParameterInfo isn't called while typing
|
||||
final TypeEvalContext typeEvalContext = TypeEvalContext.userInitiated(argList.getProject(), argList.getContainingFile());
|
||||
final TypeEvalContext typeEvalContext = TypeEvalContext.userInitiated(callExpression.getProject(), callExpression.getContainingFile());
|
||||
final PyResolveContext resolveContext = PyResolveContext.noImplicits().withTypeEvalContext(typeEvalContext);
|
||||
final CallArgumentsMapping argumentsMapping = argList.analyzeCall(resolveContext);
|
||||
final PyMarkedCallee marked = argumentsMapping.getMarkedCallee();
|
||||
final PyCallExpression.PyArgumentsMapping mapping = callExpression.mapArguments(resolveContext);
|
||||
final PyMarkedCallee marked = mapping.getMarkedCallee();
|
||||
if (marked == null) return; // resolution failed
|
||||
final PyCallable callable = marked.getCallable();
|
||||
|
||||
@@ -154,8 +165,8 @@ public class PyParameterInfoHandler implements ParameterInfoHandler<PyArgumentLi
|
||||
hintFlags.get(parameterToIndex.get(namedParameters.get(i))).add(ParameterInfoUIContextEx.Flag.DISABLE); // show but mark as absent
|
||||
}
|
||||
|
||||
final List<PyExpression> flattenedArgs = PyUtil.flattenedParensAndLists(argList.getArguments());
|
||||
int lastParamIndex = collectHighlights(argumentsMapping, parameterList, parameterToIndex, hintFlags, flattenedArgs, currentParamOffset);
|
||||
final List<PyExpression> flattenedArgs = PyUtil.flattenedParensAndLists(callExpression.getArguments());
|
||||
int lastParamIndex = collectHighlights(mapping, parameterList, parameterToIndex, hintFlags, flattenedArgs, currentParamOffset);
|
||||
|
||||
highlightNext(marked, parameterList, namedParameters, parameterToIndex, hintFlags, flattenedArgs.isEmpty(), lastParamIndex);
|
||||
|
||||
@@ -227,50 +238,49 @@ public class PyParameterInfoHandler implements ParameterInfoHandler<PyArgumentLi
|
||||
*
|
||||
* @return index of last parameter
|
||||
*/
|
||||
private static int collectHighlights(@NotNull final CallArgumentsMapping argumentsMapping,
|
||||
private static int collectHighlights(@NotNull final PyCallExpression.PyArgumentsMapping mapping,
|
||||
@NotNull final List<PyParameter> parameterList,
|
||||
@NotNull final Map<PyNamedParameter, Integer> parameterToIndex,
|
||||
@NotNull final Map<Integer, EnumSet<ParameterInfoUIContextEx.Flag>> hintFlags,
|
||||
@NotNull final List<PyExpression> flatArgs, int currentParamOffset) {
|
||||
final PyMarkedCallee callee = argumentsMapping.getMarkedCallee();
|
||||
final PyMarkedCallee callee = mapping.getMarkedCallee();
|
||||
assert callee != null;
|
||||
int lastParamIndex = callee.getImplicitOffset();
|
||||
final Map<PyExpression, PyNamedParameter> mappedParameters = mapping.getMappedParameters();
|
||||
for (PyExpression arg : flatArgs) {
|
||||
final boolean mustHighlight = arg.getTextRange().contains(currentParamOffset);
|
||||
PsiElement seeker = arg;
|
||||
while (!(seeker instanceof PyArgumentList) && seeker instanceof PyExpression && !argumentsMapping.getPlainMappedParams().containsKey(seeker)) {
|
||||
while (!(seeker instanceof PyArgumentList) && seeker instanceof PyExpression && !mappedParameters.containsKey(seeker)) {
|
||||
seeker = seeker.getParent(); // flattener may have flattened a tuple arg that is mapped to a plain param; find it.
|
||||
}
|
||||
if (seeker instanceof PyExpression) {
|
||||
final PyNamedParameter parameter = argumentsMapping.getPlainMappedParams().get((PyExpression)seeker);
|
||||
final PyNamedParameter parameter = mappedParameters.get((PyExpression)seeker);
|
||||
lastParamIndex = Math.max(lastParamIndex, parameterList.indexOf(parameter));
|
||||
if (parameter != null) {
|
||||
highlightParameter(parameter, parameterToIndex, hintFlags, mustHighlight);
|
||||
}
|
||||
}
|
||||
else if (arg == argumentsMapping.getTupleArg()) {
|
||||
// mark all params that map to *arg
|
||||
for (PyNamedParameter parameter : argumentsMapping.getTupleMappedParams()) {
|
||||
else if (PyCallExpressionHelper.isVariadicPositionalArgument(arg)) {
|
||||
for (PyNamedParameter parameter : mapping.getParametersMappedToVariadicPositionalArguments()) {
|
||||
lastParamIndex = Math.max(lastParamIndex, parameterList.indexOf(parameter));
|
||||
highlightParameter(parameter, parameterToIndex, hintFlags, mustHighlight);
|
||||
}
|
||||
}
|
||||
else if (arg == argumentsMapping.getKwdArg()) {
|
||||
// mark all n_params that map to **arg
|
||||
for (PyNamedParameter parameter : argumentsMapping.getKwdMappedParams()) {
|
||||
else if (PyCallExpressionHelper.isVariadicKeywordArgument(arg)) {
|
||||
for (PyNamedParameter parameter : mapping.getParametersMappedToVariadicKeywordArguments()) {
|
||||
lastParamIndex = Math.max(lastParamIndex, parameterList.indexOf(parameter));
|
||||
highlightParameter(parameter, parameterToIndex, hintFlags, mustHighlight);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// maybe it's mapped to a nested tuple?
|
||||
final List<PyNamedParameter> namedParameters = argumentsMapping.getNestedMappedParams().get(arg);
|
||||
if (namedParameters != null) {
|
||||
for (PyNamedParameter parameter : namedParameters) {
|
||||
lastParamIndex = Math.max(lastParamIndex, parameterList.indexOf(parameter));
|
||||
highlightParameter(parameter, parameterToIndex, hintFlags, mustHighlight);
|
||||
}
|
||||
}
|
||||
// TODO: Handle arguments that are mapped to tuple parameters
|
||||
//final List<PyNamedParameter> namedParameters = mapping.getNestedMappedParams().get(arg);
|
||||
//if (namedParameters != null) {
|
||||
// for (PyNamedParameter parameter : namedParameters) {
|
||||
// lastParamIndex = Math.max(lastParamIndex, parameterList.indexOf(parameter));
|
||||
// highlightParameter(parameter, parameterToIndex, hintFlags, mustHighlight);
|
||||
// }
|
||||
//}
|
||||
}
|
||||
// else: stay unhighlighted
|
||||
}
|
||||
|
||||
@@ -640,8 +640,9 @@ public class PyCallExpressionHelper {
|
||||
final PyCallExpression.PyMarkedCallee markedCallee = callExpression.resolveCallee(resolveContext, implicitOffset);
|
||||
|
||||
if (markedCallee == null || argumentList == null) {
|
||||
return new PyCallExpression.PyArgumentsMapping(null, Collections.<PyExpression, PyNamedParameter>emptyMap(),
|
||||
Collections.<PyParameter>emptyList(), Collections.<PyExpression>emptyList());
|
||||
return new PyCallExpression.PyArgumentsMapping(callExpression, null, Collections.<PyExpression, PyNamedParameter>emptyMap(),
|
||||
Collections.<PyParameter>emptyList(), Collections.<PyExpression>emptyList(),
|
||||
Collections.<PyNamedParameter>emptyList(),Collections.<PyNamedParameter>emptyList());
|
||||
}
|
||||
|
||||
boolean seenSingleStar = false;
|
||||
@@ -649,6 +650,8 @@ public class PyCallExpressionHelper {
|
||||
final TypeEvalContext context = resolveContext.getTypeEvalContext();
|
||||
final Map<PyExpression, PyNamedParameter> mappedParameters = new LinkedHashMap<PyExpression, PyNamedParameter>();
|
||||
final List<PyParameter> unmappedParameters = new ArrayList<PyParameter>();
|
||||
final List<PyNamedParameter> parametersMappedToVariadicKeywordArguments = new ArrayList<PyNamedParameter>();
|
||||
final List<PyNamedParameter> parametersMappedToVariadicPositionalArguments = new ArrayList<PyNamedParameter>();
|
||||
|
||||
final List<PyParameter> allParameters = PyUtil.getParameters(markedCallee.getCallable(), context);
|
||||
final List<PyParameter> parameters = dropImplicitParameters(allParameters, markedCallee.getImplicitOffset());
|
||||
@@ -658,7 +661,7 @@ public class PyCallExpressionHelper {
|
||||
final List<PyKeywordArgument> keywordArguments = filterKeywordArguments(arguments);
|
||||
final Pair<List<PyExpression>, List<PyExpression>> variadicPositionalArgumentsAndTheirComponents = filterVariadicPositionalArguments(arguments);
|
||||
final List<PyExpression> variadicPositionalArguments = variadicPositionalArgumentsAndTheirComponents.getFirst();
|
||||
final List<PyExpression> positionalComponentsOfVariadicArguments = variadicPositionalArgumentsAndTheirComponents.getSecond();
|
||||
final Set<PyExpression> positionalComponentsOfVariadicArguments = new LinkedHashSet<PyExpression>(variadicPositionalArgumentsAndTheirComponents.getSecond());
|
||||
final List<PyExpression> variadicKeywordArguments = filterVariadicKeywordArguments(arguments);
|
||||
|
||||
final List<PyExpression> allPositionalArguments = new ArrayList<PyExpression>();
|
||||
@@ -670,6 +673,9 @@ public class PyCallExpressionHelper {
|
||||
final PyNamedParameter namedParameter = (PyNamedParameter)parameter;
|
||||
final String parameterName = namedParameter.getName();
|
||||
if (namedParameter.isPositionalContainer()) {
|
||||
for (PyExpression argument : allPositionalArguments) {
|
||||
mappedParameters.put(argument, namedParameter);
|
||||
}
|
||||
if (variadicPositionalArguments.size() == 1) {
|
||||
mappedParameters.put(variadicPositionalArguments.get(0), namedParameter);
|
||||
}
|
||||
@@ -677,6 +683,9 @@ public class PyCallExpressionHelper {
|
||||
variadicPositionalArguments.clear();
|
||||
}
|
||||
else if (namedParameter.isKeywordContainer()) {
|
||||
for (PyKeywordArgument argument : keywordArguments) {
|
||||
mappedParameters.put(argument, namedParameter);
|
||||
}
|
||||
if (variadicKeywordArguments.size() == 1) {
|
||||
mappedParameters.put(variadicKeywordArguments.get(0), namedParameter);
|
||||
}
|
||||
@@ -688,8 +697,13 @@ public class PyCallExpressionHelper {
|
||||
if (keywordArgument != null) {
|
||||
mappedParameters.put(keywordArgument, namedParameter);
|
||||
}
|
||||
else if (variadicKeywordArguments.isEmpty() && !namedParameter.hasDefaultValue()) {
|
||||
unmappedParameters.add(namedParameter);
|
||||
else if (variadicKeywordArguments.isEmpty()) {
|
||||
if (!namedParameter.hasDefaultValue()) {
|
||||
unmappedParameters.add(namedParameter);
|
||||
}
|
||||
}
|
||||
else {
|
||||
parametersMappedToVariadicKeywordArguments.add(namedParameter);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -702,6 +716,12 @@ public class PyCallExpressionHelper {
|
||||
unmappedParameters.add(namedParameter);
|
||||
}
|
||||
else {
|
||||
if (!variadicPositionalArguments.isEmpty()) {
|
||||
parametersMappedToVariadicPositionalArguments.add(namedParameter);
|
||||
}
|
||||
if (!variadicKeywordArguments.isEmpty()) {
|
||||
parametersMappedToVariadicKeywordArguments.add(namedParameter);
|
||||
}
|
||||
mappedVariadicArgumentsToParameters = true;
|
||||
}
|
||||
}
|
||||
@@ -709,6 +729,9 @@ public class PyCallExpressionHelper {
|
||||
final PyExpression positionalArgument = next(allPositionalArguments);
|
||||
if (positionalArgument != null) {
|
||||
mappedParameters.put(positionalArgument, namedParameter);
|
||||
if (positionalComponentsOfVariadicArguments.contains(positionalArgument)) {
|
||||
parametersMappedToVariadicPositionalArguments.add(namedParameter);
|
||||
}
|
||||
}
|
||||
else if (!namedParameter.hasDefaultValue()) {
|
||||
unmappedParameters.add(namedParameter);
|
||||
@@ -750,7 +773,9 @@ public class PyCallExpressionHelper {
|
||||
unmappedArguments.addAll(variadicPositionalArguments);
|
||||
unmappedArguments.addAll(variadicKeywordArguments);
|
||||
|
||||
return new PyCallExpression.PyArgumentsMapping(markedCallee, mappedParameters, unmappedParameters, unmappedArguments);
|
||||
return new PyCallExpression.PyArgumentsMapping(callExpression, markedCallee, mappedParameters, unmappedParameters, unmappedArguments,
|
||||
parametersMappedToVariadicPositionalArguments,
|
||||
parametersMappedToVariadicKeywordArguments);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -831,11 +856,11 @@ public class PyCallExpressionHelper {
|
||||
return results;
|
||||
}
|
||||
|
||||
private static boolean isVariadicKeywordArgument(@NotNull PyExpression argument) {
|
||||
public static boolean isVariadicKeywordArgument(@NotNull PyExpression argument) {
|
||||
return argument instanceof PyStarArgument && ((PyStarArgument)argument).isKeyword();
|
||||
}
|
||||
|
||||
private static boolean isVariadicPositionalArgument(@NotNull PyExpression argument) {
|
||||
public static boolean isVariadicPositionalArgument(@NotNull PyExpression argument) {
|
||||
return argument instanceof PyStarArgument && !((PyStarArgument)argument).isKeyword();
|
||||
}
|
||||
|
||||
|
||||
@@ -29,8 +29,8 @@ import com.intellij.util.Function;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.HashSet;
|
||||
import com.jetbrains.python.fixtures.LightMarkedTestCase;
|
||||
import com.jetbrains.python.psi.CallArgumentsMapping;
|
||||
import com.jetbrains.python.psi.PyArgumentList;
|
||||
import com.jetbrains.python.psi.PyCallExpression;
|
||||
import junit.framework.Assert;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -422,7 +422,7 @@ public class PyParameterInfoTest extends LightMarkedTestCase {
|
||||
if (collector.getParameterOwner() != null) {
|
||||
Assert.assertEquals("Collected one analysis result", 1, collector.myItems.length);
|
||||
handler.updateParameterInfo((PyArgumentList)collector.getParameterOwner(), collector); // moves offset to correct parameter
|
||||
handler.updateUI((CallArgumentsMapping)collector.getItemsToShow()[0], collector); // sets hint text and flags
|
||||
handler.updateUI((PyCallExpression.PyArgumentsMapping)collector.getItemsToShow()[0], collector); // sets hint text and flags
|
||||
}
|
||||
return collector;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user