mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
EA-100059 Fixed: IOOBE: PyParameterInfoHandler.highlightNext
EA-100300 Fixed: IOOBE: PyParameterInfoHandler.updateUI
Update PyParameterInfoHandler to correctly process named parameters that could be separated by single star parameter.
Example (not reproducible in tests):
```
def foo(arg1, *, arg2): pass
foo("abc", arg2="abc")
```
This commit is contained in:
@@ -114,7 +114,7 @@ public class PyParameterInfoHandler implements ParameterInfoHandler<PyArgumentLi
|
||||
return findArgumentList(context, context.getParameterListStart());
|
||||
}
|
||||
|
||||
/**
|
||||
/*
|
||||
<b>Note: instead of parameter index, we directly store parameter's offset for later use.</b><br/>
|
||||
We cannot store an index since we cannot determine what is an argument until we actually map arguments to parameters.
|
||||
This is because a tuple in arguments may be a whole argument or map to a tuple parameter.
|
||||
@@ -182,26 +182,32 @@ public class PyParameterInfoHandler implements ParameterInfoHandler<PyArgumentLi
|
||||
if (markedCallee == null) return;
|
||||
|
||||
final List<PyParameter> parameters = PyUtil.getParameters(markedCallee.getCallable(), typeEvalContext);
|
||||
final List<PyNamedParameter> namedParameters = new ArrayList<>(parameters.size());
|
||||
final Map<Integer, PyNamedParameter> indexToNamedParameter = new HashMap<>();
|
||||
|
||||
// param -> hint index. indexes are not contiguous, because some hints are parentheses.
|
||||
final Map<PyNamedParameter, Integer> parameterToIndex = new HashMap<>();
|
||||
final Map<PyNamedParameter, Integer> parameterToHintIndex = new HashMap<>();
|
||||
// formatting of hints: hint index -> flags. this includes flags for parens.
|
||||
final Map<Integer, EnumSet<ParameterInfoUIContextEx.Flag>> hintFlags = new HashMap<>();
|
||||
|
||||
final List<String> hintsList = buildParameterListHint(parameters, namedParameters, parameterToIndex, hintFlags, typeEvalContext);
|
||||
final List<String> hintsList =
|
||||
buildParameterListHint(parameters, indexToNamedParameter, parameterToHintIndex, hintFlags, typeEvalContext);
|
||||
|
||||
final int currentParamOffset = context.getCurrentParameterIndex(); // in Python mode, we get an offset here, not an index!
|
||||
|
||||
// gray out enough first parameters as implicit (self, cls, ...)
|
||||
for (int i = 0; i < markedCallee.getImplicitOffset(); i++) {
|
||||
hintFlags.get(parameterToIndex.get(namedParameters.get(i))).add(ParameterInfoUIContextEx.Flag.DISABLE); // show but mark as absent
|
||||
if (indexToNamedParameter.containsKey(i)) {
|
||||
final PyNamedParameter parameter = indexToNamedParameter.get(i);
|
||||
hintFlags.get(parameterToHintIndex.get(parameter)).add(ParameterInfoUIContextEx.Flag.DISABLE); // show but mark as absent
|
||||
}
|
||||
}
|
||||
|
||||
final List<PyExpression> flattenedArguments = PyUtil.flattenedParensAndLists(callExpression.getArguments());
|
||||
final int lastParamIndex = collectHighlights(mapping, parameters, parameterToIndex, hintFlags, flattenedArguments, currentParamOffset);
|
||||
final int lastParamIndex =
|
||||
collectHighlights(mapping, parameters, parameterToHintIndex, hintFlags, flattenedArguments, currentParamOffset);
|
||||
|
||||
highlightNext(markedCallee, parameters, namedParameters, parameterToIndex, hintFlags, flattenedArguments.isEmpty(), lastParamIndex);
|
||||
highlightNext(markedCallee, parameters, indexToNamedParameter, parameterToHintIndex, hintFlags, flattenedArguments.isEmpty(),
|
||||
lastParamIndex);
|
||||
|
||||
String[] hints = ArrayUtil.toStringArray(hintsList);
|
||||
if (context instanceof ParameterInfoUIContextEx) {
|
||||
@@ -232,14 +238,15 @@ public class PyParameterInfoHandler implements ParameterInfoHandler<PyArgumentLi
|
||||
|
||||
private static void highlightNext(@NotNull final PyMarkedCallee marked,
|
||||
@NotNull final List<PyParameter> parameterList,
|
||||
@NotNull final List<PyNamedParameter> namedParameters,
|
||||
@NotNull final Map<PyNamedParameter, Integer> parameterToIndex,
|
||||
@NotNull final Map<Integer, PyNamedParameter> indexToNamedParameter,
|
||||
@NotNull final Map<PyNamedParameter, Integer> parameterToHintIndex,
|
||||
@NotNull final Map<Integer, EnumSet<ParameterInfoUIContextEx.Flag>> hintFlags,
|
||||
boolean isArgsEmpty, int lastParamIndex) {
|
||||
boolean canOfferNext = true; // can we highlight next unfilled parameter
|
||||
for (EnumSet<ParameterInfoUIContextEx.Flag> set : hintFlags.values()) {
|
||||
if (set.contains(ParameterInfoUIContextEx.Flag.HIGHLIGHT))
|
||||
if (set.contains(ParameterInfoUIContextEx.Flag.HIGHLIGHT)) {
|
||||
canOfferNext = false;
|
||||
}
|
||||
}
|
||||
// highlight the next parameter to be filled
|
||||
if (canOfferNext) {
|
||||
@@ -248,7 +255,7 @@ public class PyParameterInfoHandler implements ParameterInfoHandler<PyArgumentLi
|
||||
highlightIndex = marked.getImplicitOffset(); // no args, highlight first (PY-3690)
|
||||
}
|
||||
else if (lastParamIndex < parameterList.size() - 1) { // lastParamIndex not at end, or no args
|
||||
if (namedParameters.get(lastParamIndex).isPositionalContainer()) {
|
||||
if (indexToNamedParameter.get(lastParamIndex).isPositionalContainer()) {
|
||||
highlightIndex = lastParamIndex; // stick to *arg
|
||||
}
|
||||
else {
|
||||
@@ -256,12 +263,13 @@ public class PyParameterInfoHandler implements ParameterInfoHandler<PyArgumentLi
|
||||
}
|
||||
}
|
||||
else if (lastParamIndex == parameterList.size() - 1) { // we're right after the end of param list
|
||||
if (namedParameters.get(lastParamIndex).isPositionalContainer() || namedParameters.get(lastParamIndex).isKeywordContainer()) {
|
||||
final PyNamedParameter parameter = indexToNamedParameter.get(lastParamIndex);
|
||||
if (parameter.isPositionalContainer() || parameter.isKeywordContainer()) {
|
||||
highlightIndex = lastParamIndex; // stick to *arg
|
||||
}
|
||||
}
|
||||
if (highlightIndex < namedParameters.size()) {
|
||||
hintFlags.get(parameterToIndex.get(namedParameters.get(highlightIndex))).add(ParameterInfoUIContextEx.Flag.HIGHLIGHT);
|
||||
if (indexToNamedParameter.containsKey(highlightIndex)) {
|
||||
hintFlags.get(parameterToHintIndex.get(indexToNamedParameter.get(highlightIndex))).add(ParameterInfoUIContextEx.Flag.HIGHLIGHT);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -273,9 +281,10 @@ public class PyParameterInfoHandler implements ParameterInfoHandler<PyArgumentLi
|
||||
*/
|
||||
private static int collectHighlights(@NotNull final PyCallExpression.PyArgumentsMapping mapping,
|
||||
@NotNull final List<PyParameter> parameterList,
|
||||
@NotNull final Map<PyNamedParameter, Integer> parameterToIndex,
|
||||
@NotNull final Map<PyNamedParameter, Integer> parameterHintToIndex,
|
||||
@NotNull final Map<Integer, EnumSet<ParameterInfoUIContextEx.Flag>> hintFlags,
|
||||
@NotNull final List<PyExpression> flatArgs, int currentParamOffset) {
|
||||
@NotNull final List<PyExpression> flatArgs,
|
||||
int currentParamOffset) {
|
||||
final PyMarkedCallee callee = mapping.getMarkedCallee();
|
||||
assert callee != null;
|
||||
int lastParamIndex = callee.getImplicitOffset();
|
||||
@@ -292,19 +301,19 @@ public class PyParameterInfoHandler implements ParameterInfoHandler<PyArgumentLi
|
||||
final PyNamedParameter parameter = mappedParameters.get((PyExpression)seeker);
|
||||
lastParamIndex = Math.max(lastParamIndex, parameterList.indexOf(parameter));
|
||||
if (parameter != null) {
|
||||
highlightParameter(parameter, parameterToIndex, hintFlags, mustHighlight);
|
||||
highlightParameter(parameter, parameterHintToIndex, hintFlags, mustHighlight);
|
||||
}
|
||||
}
|
||||
else if (PyCallExpressionHelper.isVariadicPositionalArgument(arg)) {
|
||||
for (PyNamedParameter parameter : mapping.getParametersMappedToVariadicPositionalArguments()) {
|
||||
lastParamIndex = Math.max(lastParamIndex, parameterList.indexOf(parameter));
|
||||
highlightParameter(parameter, parameterToIndex, hintFlags, mustHighlight);
|
||||
highlightParameter(parameter, parameterHintToIndex, hintFlags, mustHighlight);
|
||||
}
|
||||
}
|
||||
else if (PyCallExpressionHelper.isVariadicKeywordArgument(arg)) {
|
||||
for (PyNamedParameter parameter : mapping.getParametersMappedToVariadicKeywordArguments()) {
|
||||
lastParamIndex = Math.max(lastParamIndex, parameterList.indexOf(parameter));
|
||||
highlightParameter(parameter, parameterToIndex, hintFlags, mustHighlight);
|
||||
highlightParameter(parameter, parameterHintToIndex, hintFlags, mustHighlight);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -312,7 +321,7 @@ public class PyParameterInfoHandler implements ParameterInfoHandler<PyArgumentLi
|
||||
if (tupleParameter != null) {
|
||||
for (PyNamedParameter parameter : getFlattenedTupleParameterComponents(tupleParameter)) {
|
||||
lastParamIndex = Math.max(lastParamIndex, parameterList.indexOf(parameter));
|
||||
highlightParameter(parameter, parameterToIndex, hintFlags, mustHighlight);
|
||||
highlightParameter(parameter, parameterHintToIndex, hintFlags, mustHighlight);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -335,30 +344,31 @@ public class PyParameterInfoHandler implements ParameterInfoHandler<PyArgumentLi
|
||||
}
|
||||
|
||||
private static void highlightParameter(@NotNull final PyNamedParameter parameter,
|
||||
@NotNull final Map<PyNamedParameter, Integer> parameterToIndex,
|
||||
@NotNull final Map<Integer, EnumSet<ParameterInfoUIContextEx.Flag>> hintFlags,
|
||||
boolean mustHighlight) {
|
||||
final Integer parameterIndex = parameterToIndex.get(parameter);
|
||||
if (mustHighlight && parameterIndex != null && parameterIndex < hintFlags.size()) {
|
||||
hintFlags.get(parameterIndex).add(ParameterInfoUIContextEx.Flag.HIGHLIGHT);
|
||||
@NotNull final Map<PyNamedParameter, Integer> parameterToHintIndex,
|
||||
@NotNull final Map<Integer, EnumSet<ParameterInfoUIContextEx.Flag>> hintFlags,
|
||||
boolean mustHighlight) {
|
||||
final Integer hintIndex = parameterToHintIndex.get(parameter);
|
||||
if (mustHighlight && hintIndex != null && hintFlags.containsKey(hintIndex)) {
|
||||
hintFlags.get(hintIndex).add(ParameterInfoUIContextEx.Flag.HIGHLIGHT);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* builds the textual picture and the list of named parameters
|
||||
*
|
||||
* @param parameters parameters of a callable
|
||||
* @param namedParameters used to collect all named parameters of callable
|
||||
* @param parameterToIndex used to collect info about parameter indexes
|
||||
* @param hintFlags mark parameter as deprecated/highlighted/strikeout
|
||||
* @param context context to be used to get parameter representation
|
||||
* @param parameters parameters of a callable
|
||||
* @param indexToNamedParameter used to collect all named parameters of callable
|
||||
* @param parameterToHintIndex used to collect info about parameter hints
|
||||
* @param hintFlags mark parameter as deprecated/highlighted/strikeout
|
||||
* @param context context to be used to get parameter representation
|
||||
*/
|
||||
private static List<String> buildParameterListHint(@NotNull List<PyParameter> parameters,
|
||||
@NotNull final List<PyNamedParameter> namedParameters,
|
||||
@NotNull final Map<PyNamedParameter, Integer> parameterToIndex,
|
||||
@NotNull final Map<Integer, PyNamedParameter> indexToNamedParameter,
|
||||
@NotNull final Map<PyNamedParameter, Integer> parameterToHintIndex,
|
||||
@NotNull final Map<Integer, EnumSet<ParameterInfoUIContextEx.Flag>> hintFlags,
|
||||
@NotNull TypeEvalContext context) {
|
||||
final List<String> hintsList = new ArrayList<>();
|
||||
final int[] currentParameterIndex = new int[]{0};
|
||||
ParamHelper.walkDownParamArray(
|
||||
parameters.toArray(new PyParameter[parameters.size()]),
|
||||
new ParamHelper.ParamWalker() {
|
||||
@@ -373,19 +383,21 @@ public class PyParameterInfoHandler implements ParameterInfoHandler<PyArgumentLi
|
||||
}
|
||||
|
||||
public void visitNamedParameter(PyNamedParameter param, boolean first, boolean last) {
|
||||
namedParameters.add(param);
|
||||
indexToNamedParameter.put(currentParameterIndex[0], param);
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
stringBuilder.append(param.getRepr(true, context));
|
||||
if (!last) stringBuilder.append(", ");
|
||||
int hintIndex = hintsList.size();
|
||||
parameterToIndex.put(param, hintIndex);
|
||||
parameterToHintIndex.put(param, hintIndex);
|
||||
hintFlags.put(hintIndex, EnumSet.noneOf(ParameterInfoUIContextEx.Flag.class));
|
||||
hintsList.add(stringBuilder.toString());
|
||||
currentParameterIndex[0]++;
|
||||
}
|
||||
|
||||
public void visitSingleStarParameter(PySingleStarParameter param, boolean first, boolean last) {
|
||||
hintFlags.put(hintsList.size(), EnumSet.noneOf(ParameterInfoUIContextEx.Flag.class));
|
||||
hintsList.add(last ? "*" : "*, ");
|
||||
currentParameterIndex[0]++;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user