mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Adds a notion of a tuple parameter. Adapts paramater and argument inspections and Ctrl+P handler to use it. Adds tests for Ctrl+P handler.
Closes PY-200.
This commit is contained in:
@@ -1,10 +1,9 @@
|
||||
package com.jetbrains.python.psi.impl;
|
||||
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.Processor;
|
||||
import com.jetbrains.python.psi.PyFunction;
|
||||
import com.jetbrains.python.psi.PyParameter;
|
||||
import com.jetbrains.python.psi.PyNamedParameter;
|
||||
import com.jetbrains.python.psi.PyParameterList;
|
||||
import com.jetbrains.python.psi.search.PySuperMethodsSearch;
|
||||
import com.jetbrains.python.psi.types.PyType;
|
||||
@@ -36,10 +35,10 @@ public class PyJavaTypeProvider implements PyTypeProvider {
|
||||
return null;
|
||||
}
|
||||
|
||||
public PyType getParameterType(final PyParameter param, final PyFunction func) {
|
||||
public PyType getParameterType(final PyNamedParameter param, final PyFunction func) {
|
||||
if (!(param.getParent() instanceof PyParameterList)) return null;
|
||||
PyParameter[] params = ((PyParameterList) param.getParent()).getParameters();
|
||||
final int index = ArrayUtil.indexOf(params, param);
|
||||
List<PyNamedParameter> params = ParamHelper.collectNamedParameters((PyParameterList) param.getParent());
|
||||
final int index = params.indexOf(param);
|
||||
if (index < 0) return null;
|
||||
final List<PyType> superMethodParameterTypes = new ArrayList<PyType>();
|
||||
PySuperMethodsSearch.search(func).forEach(new Processor<PsiElement>() {
|
||||
|
||||
@@ -48,6 +48,7 @@ INSP.must.have.first.parameter=Method must have a first parameter, usually calle
|
||||
INSP.probably.mistyped.self=Did not you mean 'self'?
|
||||
INSP.usually.named.self=Usually first parameter of a method is named 'self'
|
||||
INSP.usually.named.cls=Usually first parameter of a classmethod is named 'cls'
|
||||
INSP.first.param.must.not.be.tuple=First parameter of a non-static method must not be a tuple
|
||||
|
||||
# PyRedeclarationInspection
|
||||
INSP.NAME.redeclaration=Names redeclared without usage
|
||||
@@ -95,6 +96,14 @@ ANN.continue.outside.loop='continue' outside loop
|
||||
ANN.cant.continue.in.finally='continue' not supported inside 'finally' clause
|
||||
ANN.default.except.must.be.last=default 'except:' must be last
|
||||
|
||||
ANN.$0.both.global.and.param=Name ''{0}'' used both as a parameter and as a global
|
||||
ANN.$0.assigned.before.global.decl=Name ''{0}'' is assigned before global declaration
|
||||
|
||||
ANN.duplicate.param.name=duplicate parameter name
|
||||
ANN.starred.param.after.kwparam=* parameter after ** paremeter
|
||||
ANN.regular.param.after.starred=regular parameter after * or ** parameter
|
||||
ANN.non.default.param.after.default=non-default parameter follows default parameter
|
||||
|
||||
### parsing
|
||||
PARSE.expected.expression=expression expected
|
||||
PARSE.expected.rbracket=']' expected
|
||||
|
||||
@@ -18,10 +18,14 @@ public interface PyElementTypes {
|
||||
|
||||
TokenSet PARAMETER_LIST_SET = TokenSet.create(PARAMETER_LIST);
|
||||
|
||||
PyStubElementType<PyParameterStub, PyParameter> FORMAL_PARAMETER = new PyFormalParameterElementType();
|
||||
PyStubElementType<PyNamedParameterStub, PyNamedParameter> NAMED_PARAMETER = new PyNamedParameterElementType();
|
||||
PyStubElementType<PyTupleParameterStub, PyTupleParameter> TUPLE_PARAMETER = new PyTupleParameterElementType();
|
||||
|
||||
TokenSet PARAMETERS = TokenSet.create(NAMED_PARAMETER, TUPLE_PARAMETER);
|
||||
|
||||
PyStubElementType<PyDecoratorStub, PyDecorator> DECORATOR_CALL = new PyDecoratorCallElementType();
|
||||
|
||||
TokenSet FORMAL_PARAMETER_SET = TokenSet.create(FORMAL_PARAMETER);
|
||||
TokenSet FORMAL_PARAMETER_SET = TokenSet.create(NAMED_PARAMETER);
|
||||
|
||||
// Deprecated PyElementType DECORATED_FUNCTION_DECLARATION = new PyElementType("DECORATED_FUNCTION_DECLARATION", PyDecoratedFunctionImpl.class);
|
||||
PyElementType ARGUMENT_LIST = new PyElementType("ARGUMENT_LIST", PyArgumentListImpl.class);
|
||||
@@ -123,6 +127,6 @@ public interface PyElementTypes {
|
||||
PyElementType ELSE_PART = new PyElementType("ELSE_PART", PyElsePartImpl.class);
|
||||
|
||||
TokenSet PARTS = TokenSet.create(IF_PART_IF, IF_PART_ELIF, FOR_PART, WHILE_PART, TRY_PART, FINALLY_PART, ELSE_PART);
|
||||
TokenSet ELIFS = TokenSet.create(IF_PART_ELIF);
|
||||
TokenSet ELIFS = TokenSet.create(IF_PART_ELIF);
|
||||
|
||||
}
|
||||
|
||||
@@ -2,14 +2,15 @@ package com.jetbrains.python;
|
||||
|
||||
import com.intellij.codeInsight.lookup.LookupElement;
|
||||
import com.intellij.lang.parameterInfo.*;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.util.text.CharArrayUtil;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import static com.jetbrains.python.psi.PyCallExpression.PyMarkedFunction;
|
||||
import com.jetbrains.python.psi.impl.ParamHelper;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author yole
|
||||
@@ -20,11 +21,11 @@ public class PyParameterInfoHandler implements ParameterInfoHandler<PyArgumentLi
|
||||
return true;
|
||||
}
|
||||
public Object[] getParametersForLookup(final LookupElement item, final ParameterInfoContext context) {
|
||||
return new Object[0]; //To change body of implemented methods use File | Settings | File Templates.
|
||||
return new Object[0]; // we don't
|
||||
}
|
||||
|
||||
public Object[] getParametersForDocumentation(final PyArgumentList.AnalysisResult p, final ParameterInfoContext context) {
|
||||
return new Object[0]; //To change body of implemented methods use File | Settings | File Templates.
|
||||
return new Object[0]; // we don't
|
||||
}
|
||||
|
||||
public PyArgumentList findElementForParameterInfo(final CreateParameterInfoContext context) {
|
||||
@@ -51,17 +52,37 @@ public class PyParameterInfoHandler implements ParameterInfoHandler<PyArgumentLi
|
||||
return findArgumentList(context);
|
||||
}
|
||||
|
||||
public void updateParameterInfo(@NotNull final PyArgumentList o, final UpdateParameterInfoContext context) {
|
||||
if (context.getParameterOwner() != o) {
|
||||
/**
|
||||
<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.
|
||||
*/
|
||||
public void updateParameterInfo(@NotNull final PyArgumentList arglist, final UpdateParameterInfoContext context) {
|
||||
if (context.getParameterOwner() != arglist) {
|
||||
context.removeHint();
|
||||
return;
|
||||
}
|
||||
final int currentParameterIndex = ParameterInfoUtils.getCurrentParameterIndex(o.getNode(), context.getOffset(), PyTokenTypes.COMMA);
|
||||
context.setCurrentParameter(currentParameterIndex);
|
||||
// align offset to nearest expression; context may point to a space, etc.
|
||||
List<PyExpression> flat_args = PyUtil.flattenedParens(arglist.getArguments());
|
||||
int alleged_cursor_offset = context.getOffset(); // this is already shifted backwards to skip spaces
|
||||
PsiFile file = context.getFile();
|
||||
CharSequence chars = file.getViewProvider().getContents();
|
||||
int offset = -1;
|
||||
for (PyExpression arg : flat_args) {
|
||||
TextRange range = arg.getTextRange();
|
||||
// widen the range to include all whitespace around the arg
|
||||
int left = CharArrayUtil.shiftBackward(chars, range.getStartOffset()-1, " \t\r\n");
|
||||
int right = CharArrayUtil.shiftForwardCarefully(chars, range.getEndOffset(), " \t\r\n");
|
||||
if (left <= alleged_cursor_offset && right >= alleged_cursor_offset) {
|
||||
offset = range.getStartOffset();
|
||||
break;
|
||||
}
|
||||
}
|
||||
context.setCurrentParameter(offset);
|
||||
}
|
||||
|
||||
public String getParameterCloseChars() {
|
||||
return ",)";
|
||||
return ",()"; // lpar may mean a nested tuple param, so it's included
|
||||
}
|
||||
|
||||
public boolean tracksParameterIndex() {
|
||||
@@ -74,81 +95,119 @@ public class PyParameterInfoHandler implements ParameterInfoHandler<PyArgumentLi
|
||||
assert marked != null : "findElementForParameterInfo() did it wrong!";
|
||||
final PyFunction py_function = marked.getFunction();
|
||||
if (py_function == null) return; // resolution failed
|
||||
final PyParameter[] params = py_function.getParameterList().getParameters();
|
||||
|
||||
PyParameter[] raw_params = py_function.getParameterList().getParameters();
|
||||
final List<PyNamedParameter> n_param_list = new ArrayList<PyNamedParameter>(raw_params.length);
|
||||
final List<String> hint_texts = new ArrayList<String>(raw_params.length);
|
||||
|
||||
// param -> hint index. indexes are not contiguous, because some hints are parentheses.
|
||||
final Map<PyNamedParameter, Integer> param_indexes = new HashMap<PyNamedParameter, Integer>();
|
||||
// formatting of hints: hint index -> flags. this includes flags for parens.
|
||||
final Map<Integer, EnumSet<ParameterInfoUIContextEx.Flag>> hint_flags = new HashMap<Integer, EnumSet<ParameterInfoUIContextEx.Flag>>();
|
||||
|
||||
// build the textual picture and the list of named parameters
|
||||
ParamHelper.walkDownParamArray(
|
||||
py_function.getParameterList().getParameters(),
|
||||
new ParamHelper.ParamWalker() {
|
||||
public void enterTupleParameter(PyTupleParameter param, boolean first, boolean last) {
|
||||
hint_flags.put(hint_texts.size(), EnumSet.noneOf(ParameterInfoUIContextEx.Flag.class));
|
||||
hint_texts.add("(");
|
||||
}
|
||||
|
||||
public void leaveTupleParameter(PyTupleParameter param, boolean first, boolean last) {
|
||||
hint_flags.put(hint_texts.size(), EnumSet.noneOf(ParameterInfoUIContextEx.Flag.class));
|
||||
if (last) hint_texts.add(")");
|
||||
else hint_texts.add("),");
|
||||
}
|
||||
|
||||
public void visitNamedParameter(PyNamedParameter param, boolean first, boolean last) {
|
||||
n_param_list.add(param);
|
||||
StringBuilder strb = new StringBuilder();
|
||||
strb.append(param.getRepr(true));
|
||||
if (! last) strb.append(",");
|
||||
int hint_index = hint_texts.size();
|
||||
param_indexes.put(param, hint_index);
|
||||
hint_flags.put(hint_index, EnumSet.noneOf(ParameterInfoUIContextEx.Flag.class));
|
||||
hint_texts.add(strb.toString());
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
final PyArgumentList arglist = result.getArgumentList();
|
||||
int arg_index = context.getCurrentParameterIndex() >= 0 ? context.getCurrentParameterIndex():params.length;
|
||||
|
||||
// param texts
|
||||
String[] param_texts = new String[params.length];
|
||||
for (int i = 0; i < param_texts.length; i += 1) {
|
||||
StringBuilder strb = new StringBuilder();
|
||||
final PyParameter param = params[i];
|
||||
if (param.isKeywordContainer()) strb.append("**");
|
||||
else if (param.isPositionalContainer()) strb.append("*");
|
||||
strb.append(param.getName());
|
||||
PyExpression default_v = param.getDefaultValue();
|
||||
if (default_v != null) strb.append("=").append(PyUtil.getReadableRepr(default_v, true));
|
||||
if (i < param_texts.length-1) strb.append(",");
|
||||
param_texts[i] = strb.toString();
|
||||
final int current_param_offset = context.getCurrentParameterIndex(); // in Python mode, we get an offset here, not an index!
|
||||
|
||||
// gray out enough first parameters as implicit
|
||||
for (int i=0; i < marked.getImplicitOffset(); i += 1) {
|
||||
hint_flags.get(param_indexes.get(n_param_list.get(i))).add(ParameterInfoUIContextEx.Flag.DISABLE); // show but mark as absent
|
||||
}
|
||||
|
||||
// formatting
|
||||
Map<PyParameter, Integer> param_indexes = new HashMap<PyParameter, Integer>();
|
||||
for (int i=0; i < params.length; i += 1) param_indexes.put(params[i], i);
|
||||
EnumSet<ParameterInfoUIContextEx.Flag>[] flags = (EnumSet<ParameterInfoUIContextEx.Flag>[])Array.newInstance(EnumSet.class, params.length);
|
||||
// ^^ gotta hate the covariance issues
|
||||
for (int i =0; i < flags.length; i += 1) flags[i] = EnumSet.noneOf(ParameterInfoUIContextEx.Flag.class);
|
||||
|
||||
// disable implicit params
|
||||
for (int i=0; i < marked.getImplicitOffset(); i +=1) {
|
||||
flags[i].add(ParameterInfoUIContextEx.Flag.DISABLE); // show but mark as absent
|
||||
}
|
||||
int cur_arg_index = 0;
|
||||
for (PyExpression arg : arglist.getArguments()) {
|
||||
if (cur_arg_index == arg_index) {
|
||||
PyParameter param = result.getPlainMappedParams().get(arg);
|
||||
// highlight current param(s)
|
||||
for (PyExpression arg : PyUtil.flattenedParens(arglist.getArguments())) {
|
||||
if (arg.getTextRange().contains(current_param_offset)) {
|
||||
PyNamedParameter param = result.getPlainMappedParams().get(arg);
|
||||
if (param != null) {
|
||||
final Integer param_index = param_indexes.get(param);
|
||||
if (param_index < flags.length) {
|
||||
flags[param_index].add(ParameterInfoUIContextEx.Flag.HIGHLIGHT);
|
||||
if (param_index < hint_flags.size()) {
|
||||
hint_flags.get(param_index).add(ParameterInfoUIContextEx.Flag.HIGHLIGHT);
|
||||
}
|
||||
}
|
||||
else if (arg == result.getTupleArg()) {
|
||||
// mark all params that map to *arg
|
||||
for (PyParameter tpar : result.getTupleMappedParams()) {
|
||||
for (PyNamedParameter tpar : result.getTupleMappedParams()) {
|
||||
final Integer param_index = param_indexes.get(tpar);
|
||||
if (param_index != null && param_index.intValue() < flags.length) flags[param_index.intValue()].add(ParameterInfoUIContextEx.Flag.HIGHLIGHT);
|
||||
if (param_index != null && param_index < hint_flags.size()) {
|
||||
hint_flags.get(param_index).add(ParameterInfoUIContextEx.Flag.HIGHLIGHT);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (arg == result.getKwdArg()) {
|
||||
// mark all params that map to **arg
|
||||
for (PyParameter tpar : result.getKwdMappedParams()) {
|
||||
// mark all n_params that map to **arg
|
||||
for (PyNamedParameter tpar : result.getKwdMappedParams()) {
|
||||
final Integer param_index = param_indexes.get(tpar);
|
||||
if (param_index != null && param_index < flags.length) flags[param_index].add(ParameterInfoUIContextEx.Flag.HIGHLIGHT);
|
||||
if (param_index != null && param_index < hint_flags.size()) {
|
||||
hint_flags.get(param_index).add(ParameterInfoUIContextEx.Flag.HIGHLIGHT);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// maybe it's mapped to a nested tuple?
|
||||
List<PyNamedParameter> nparams = result.getNestedMappedParams().get(arg);
|
||||
if (nparams != null) {
|
||||
for (PyNamedParameter tpar : nparams) {
|
||||
final Integer param_index = param_indexes.get(tpar);
|
||||
if (param_index != null && param_index < hint_flags.size()) {
|
||||
hint_flags.get(param_index).add(ParameterInfoUIContextEx.Flag.HIGHLIGHT);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// else: stay unhilited
|
||||
cur_arg_index += 1;
|
||||
}
|
||||
|
||||
final String NO_PARAMS_MSG = "<No parameters>";
|
||||
String[] hints = hint_texts.toArray(new String[hint_texts.size()]);
|
||||
if (context instanceof ParameterInfoUIContextEx) {
|
||||
final ParameterInfoUIContextEx pic = (ParameterInfoUIContextEx)context;
|
||||
if (param_texts.length < 1) {
|
||||
param_texts = new String[]{NO_PARAMS_MSG};
|
||||
EnumSet<ParameterInfoUIContextEx.Flag>[] flags = new EnumSet[hint_flags.size()];
|
||||
for (int i = 0; i < flags.length; i += 1) flags[i] = hint_flags.get(i);
|
||||
if (hints.length < 1) {
|
||||
hints = new String[]{NO_PARAMS_MSG};
|
||||
flags = new EnumSet[]{EnumSet.of(ParameterInfoUIContextEx.Flag.DISABLE)};
|
||||
}
|
||||
pic.setupUIComponentPresentation(param_texts, flags, context.getDefaultParameterColor());
|
||||
pic.setupUIComponentPresentation(hints, flags, context.getDefaultParameterColor());
|
||||
}
|
||||
else { // fallback, no hilite
|
||||
StringBuffer signatureBuilder = new StringBuffer();
|
||||
if (param_texts.length > 1) {
|
||||
for (String s : param_texts) signatureBuilder.append(s);
|
||||
StringBuilder signatureBuilder = new StringBuilder();
|
||||
if (hints.length > 1) {
|
||||
for (String s : hints) signatureBuilder.append(s);
|
||||
}
|
||||
else signatureBuilder.append(NO_PARAMS_MSG);
|
||||
context.setupUIComponentPresentation(signatureBuilder.toString(), -1, 0, false, false, false,
|
||||
context.getDefaultParameterColor());
|
||||
context.setupUIComponentPresentation(
|
||||
signatureBuilder.toString(), -1, 0, false, false, false, context.getDefaultParameterColor()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ public class PythonFindUsagesProvider implements FindUsagesProvider {
|
||||
|
||||
@NotNull
|
||||
public String getType(@NotNull PsiElement element) {
|
||||
if (element instanceof PyParameter) return "parameter";
|
||||
if (element instanceof PyNamedParameter) return "parameter";
|
||||
if (element instanceof PyFunction) return "function";
|
||||
if (element instanceof PyClass) return "class";
|
||||
if (element instanceof PyReferenceExpression || element instanceof PyTargetExpression) return "variable";
|
||||
|
||||
@@ -13,6 +13,7 @@ import com.intellij.psi.PsiWhiteSpace;
|
||||
import com.jetbrains.python.PyBundle;
|
||||
import com.jetbrains.python.PythonLanguage;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.impl.ParamHelper;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
@@ -90,11 +91,17 @@ public class AddMethodQuickFix implements LocalQuickFix {
|
||||
|
||||
private static void showTemplateBuilder(PyFunction method) {
|
||||
method = CodeInsightUtilBase.forcePsiPostprocessAndRestoreElement(method);
|
||||
TemplateBuilder builder = TemplateBuilderFactory.getInstance().createTemplateBuilder(method);
|
||||
PyParameter[] parameters = method.getParameterList().getParameters();
|
||||
for (int i = 1; i < parameters.length; i++) {
|
||||
builder.replaceElement(parameters [i], parameters [i].getName());
|
||||
}
|
||||
|
||||
final TemplateBuilder builder = TemplateBuilderFactory.getInstance().createTemplateBuilder(method);
|
||||
ParamHelper.walkDownParamArray(
|
||||
method.getParameterList().getParameters(),
|
||||
new ParamHelper.ParamVisitor() {
|
||||
public void visitNamedParameter(PyNamedParameter param, boolean first, boolean last) {
|
||||
builder.replaceElement(param, param.getName());
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
builder.replaceElement(method.getStatementList(), "pass");
|
||||
|
||||
builder.run();
|
||||
|
||||
@@ -8,7 +8,7 @@ import com.intellij.psi.PsiElement;
|
||||
import com.jetbrains.python.PyBundle;
|
||||
import com.jetbrains.python.PythonLanguage;
|
||||
import com.jetbrains.python.psi.PyElementGenerator;
|
||||
import com.jetbrains.python.psi.PyParameter;
|
||||
import com.jetbrains.python.psi.PyNamedParameter;
|
||||
import com.jetbrains.python.psi.PyParameterList;
|
||||
import com.jetbrains.python.psi.PyUtil;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
@@ -41,7 +41,7 @@ public class AddSelfQuickFix implements LocalQuickFix {
|
||||
final PythonLanguage pythonLanguage = (PythonLanguage)language;
|
||||
PyElementGenerator generator = pythonLanguage.getElementGenerator();
|
||||
// TODO: generalize, move to generator
|
||||
PyParameter new_param = generator.createFromText(project, PyParameter.class, "def f(self): pass", new int[]{0, 3, 1});
|
||||
PyNamedParameter new_param = generator.createFromText(project, PyNamedParameter.class, "def f(self): pass", new int[]{0, 3, 1});
|
||||
param_list.addParameter(new_param);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.jetbrains.python.actions;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.impl.ParamHelper;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -92,13 +93,7 @@ class ImportCandidateHolder {
|
||||
parent = myImportElement.getParent();
|
||||
}
|
||||
if (myImportable instanceof PyFunction) {
|
||||
sb.append("(");
|
||||
// below: ", ".join([x.getRepr(False) for x in getParameters()])
|
||||
PyParameter[] params = ((PyFunction)myImportable).getParameterList().getParameters();
|
||||
String[] param_reprs = new String[params.length];
|
||||
for (int i=0; i < params.length; i += 1) param_reprs[i] = params[i].getRepr(false);
|
||||
PyUtil.joinSubarray(param_reprs, 0, params.length, ", ", sb);
|
||||
sb.append(")");
|
||||
ParamHelper.appendParameterList(((PyFunction)myImportable).getParameterList(), sb);
|
||||
}
|
||||
else if (myImportable instanceof PyClass) {
|
||||
PyClass[] supers = ((PyClass)myImportable).getSuperClasses();
|
||||
|
||||
@@ -9,7 +9,7 @@ import com.intellij.psi.PsiElement;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.jetbrains.python.PyBundle;
|
||||
import com.jetbrains.python.PythonLanguage;
|
||||
import com.jetbrains.python.psi.PyParameter;
|
||||
import com.jetbrains.python.psi.PyNamedParameter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
@@ -22,10 +22,10 @@ public class RenameToSelfQuickFix implements LocalQuickFix {
|
||||
|
||||
public void applyFix(@NotNull final Project project, @NotNull final ProblemDescriptor descriptor) {
|
||||
final PsiElement elt = descriptor.getPsiElement();
|
||||
if (elt != null && elt instanceof PyParameter && elt.isWritable()) {
|
||||
if (elt != null && elt instanceof PyNamedParameter && elt.isWritable()) {
|
||||
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
||||
public void run() {
|
||||
final PyParameter the_self = PythonLanguage.getInstance().getElementGenerator().createParameter(project, "self");
|
||||
final PyNamedParameter the_self = PythonLanguage.getInstance().getElementGenerator().createParameter(project, "self");
|
||||
try {
|
||||
elt.replace(the_self);
|
||||
}
|
||||
|
||||
@@ -68,13 +68,13 @@ public class PyArgumentListInspection extends LocalInspectionTool {
|
||||
// X XX debug
|
||||
PyCallExpression call_ex = node.getCallExpression();
|
||||
System.out.println(PyResolveUtil.getReadableRepr(call_ex));
|
||||
for (Map.Entry<PyExpression, PyParameter> entry : result.getPlainMappedParams().entrySet()) {
|
||||
for (Map.Entry<PyExpression, PyNamedParameter> entry : result.getPlainMappedParams().entrySet()) {
|
||||
System.out.println(
|
||||
PyResolveUtil.getReadableRepr(entry.getValue()) +
|
||||
" -> " + PyResolveUtil.getReadableRepr(entry.getKey())
|
||||
PyCallExpression call_ex = node.getCallExpression();
|
||||
System.out.println(PyResolveUtil.getReadableRepr(call_ex));
|
||||
for (Map.Entry<PyExpression, PyParameter> entry : result.getPlainMappedParams().entrySet()) {
|
||||
for (Map.Entry<PyExpression, PyNamedParameter> entry : result.getPlainMappedParams().entrySet()) {
|
||||
System.out.println(
|
||||
PyResolveUtil.getReadableRepr(entry.getValue()) +
|
||||
" -> " + PyResolveUtil.getReadableRepr(entry.getKey())
|
||||
@@ -116,16 +116,21 @@ public class PyArgumentListInspection extends LocalInspectionTool {
|
||||
PyFunction decofunc = mkfunc.getFunction();
|
||||
int first_param_offset = mkfunc.getImplicitOffset();
|
||||
PyParameter[] params = decofunc.getParameterList().getParameters();
|
||||
if (params.length < first_param_offset || params[first_param_offset-1].isKeywordContainer()) {
|
||||
// no paramaters left to pass function implicitly, or wrong param type
|
||||
PyNamedParameter alleged_first_param = params[first_param_offset-1].getAsNamed();
|
||||
if (params.length < first_param_offset || alleged_first_param == null || alleged_first_param.isKeywordContainer()) {
|
||||
// no parameters left to pass function implicitly, or wrong param type
|
||||
registerProblem(deco, PyBundle.message("INSP.func.$0.lacks.first.arg", decofunc.getName()));
|
||||
}
|
||||
else {
|
||||
// possible unfilled params
|
||||
for (int i=first_param_offset; i < params.length; i += 1) {
|
||||
PyParameter par = params[i];
|
||||
if (! par.isKeywordContainer() && ! par.isPositionalContainer() && (par.getDefaultValue() == null)) {
|
||||
registerProblem(deco, PyBundle.message("INSP.parameter.$0.unfilled", par.getName()));
|
||||
PyNamedParameter par = params[i].getAsNamed();
|
||||
// param tuples, non-starred or non-default won't do
|
||||
if (par == null || (! par.isKeywordContainer() && ! par.isPositionalContainer() && (par.getDefaultValue() == null))) {
|
||||
String par_name;
|
||||
if (par != null) par_name = par.getName();
|
||||
else par_name = "(...)"; // can't be bothered to find the first non-tuple inside it
|
||||
registerProblem(deco, PyBundle.message("INSP.parameter.$0.unfilled", par_name));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -166,7 +171,7 @@ public class PyArgumentListInspection extends LocalInspectionTool {
|
||||
if (our_node != null) {
|
||||
ASTNode close_paren = our_node.findChildByType(PyTokenTypes.RPAR);
|
||||
if (close_paren != null) {
|
||||
for (PyParameter param : result.getUnmappedParams()) {
|
||||
for (PyNamedParameter param : result.getUnmappedParams()) {
|
||||
holder.registerProblem(close_paren.getPsi(), PyBundle.message("INSP.parameter.$0.unfilled", param.getName()));
|
||||
}
|
||||
}
|
||||
|
||||
+3
-2
@@ -72,7 +72,8 @@ public class PyMethodFirstArgAssignmentInspection extends LocalInspectionTool {
|
||||
// what is our first param?
|
||||
PyParameter[] params = method.getParameterList().getParameters();
|
||||
if (params.length < 1) return null; // no params
|
||||
PyParameter first_parm = params[0];
|
||||
PyNamedParameter first_parm = params[0].getAsNamed();
|
||||
if (first_parm == null) return null;
|
||||
if (first_parm.isKeywordContainer() || first_parm.isPositionalContainer()) return null; // legal but crazy cases; back off
|
||||
final String first_param_name = first_parm.getName();
|
||||
if (first_param_name == null || first_param_name.length() < 1) return null; // ignore cases of incorrect code
|
||||
@@ -122,7 +123,7 @@ public class PyMethodFirstArgAssignmentInspection extends LocalInspectionTool {
|
||||
private void markDefinition(PyElement definer) {
|
||||
final String first_param_name = extractFirstParamName(definer);
|
||||
if (first_param_name != null && first_param_name.equals(definer.getName())) {
|
||||
complain(definer.getNode().findChildByType(PyTokenTypes.IDENTIFIER).getPsi(), first_param_name);
|
||||
complain(definer.getNode().findChildByType(PyTokenTypes.IDENTIFIER).getPsi(), first_param_name); // no NPE here, or we won't have the name
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -103,24 +103,32 @@ public class PyMethodParametersInspection extends LocalInspectionTool {
|
||||
}
|
||||
}
|
||||
else {
|
||||
String pname = params[0].getText();
|
||||
// every dup, swap, drop, or dup+drop of "self"
|
||||
@NonNls String[] mangled = {"eslf", "sself", "elf", "felf", "slef", "seelf", "slf", "sslf", "sefl", "sellf", "sef", "seef"};
|
||||
for (String typo : mangled) {
|
||||
if (typo.equals(pname)) {
|
||||
registerProblem(params[0].getNode().getPsi(), PyBundle.message("INSP.probably.mistyped.self"), new RenameToSelfQuickFix());
|
||||
return;
|
||||
PyNamedParameter first_param = params[0].getAsNamed();
|
||||
if (first_param != null) {
|
||||
String pname = first_param.getText();
|
||||
// every dup, swap, drop, or dup+drop of "self"
|
||||
@NonNls String[] mangled = {"eslf", "sself", "elf", "felf", "slef", "seelf", "slf", "sslf", "sefl", "sellf", "sef", "seef"};
|
||||
for (String typo : mangled) {
|
||||
if (typo.equals(pname)) {
|
||||
registerProblem(params[0].getNode().getPsi(), PyBundle.message("INSP.probably.mistyped.self"), new RenameToSelfQuickFix());
|
||||
return;
|
||||
}
|
||||
}
|
||||
// TODO: check for style settings
|
||||
if (decoratedJustWith(node, PyNames.CLASSMETHOD)) {
|
||||
if (!"cls".equals(pname)) {
|
||||
registerProblem(plist, PyBundle.message("INSP.usually.named.cls"));
|
||||
}
|
||||
}
|
||||
else if (!"self".equals(pname) && !decoratedJustWith(node, PyNames.STATICMETHOD)) {
|
||||
registerProblem(plist, PyBundle.message("INSP.usually.named.self"));
|
||||
}
|
||||
}
|
||||
// TODO: check for style settings
|
||||
if (decoratedJustWith(node, PyNames.CLASSMETHOD)) {
|
||||
if (!"cls".equals(pname)) {
|
||||
registerProblem(plist, PyBundle.message("INSP.usually.named.cls"));
|
||||
else { // the unusual case of a method with first tuple param
|
||||
if (!decoratedJustWith(node, PyNames.STATICMETHOD)) {
|
||||
registerProblem(plist, PyBundle.message("INSP.first.param.must.not.be.tuple"));
|
||||
}
|
||||
}
|
||||
else if (!"self".equals(pname) && !decoratedJustWith(node, PyNames.STATICMETHOD)) {
|
||||
registerProblem(plist, PyBundle.message("INSP.usually.named.self"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,7 +133,7 @@ public class FunctionParsing extends Parsing {
|
||||
myBuilder.advanceLexer();
|
||||
getExpressionParser().parseSingleExpression(false);
|
||||
}
|
||||
parameter.done(PyElementTypes.FORMAL_PARAMETER);
|
||||
parameter.done(PyElementTypes.NAMED_PARAMETER);
|
||||
}
|
||||
else {
|
||||
myBuilder.error(message("PARSE.expected.formal.param.name"));
|
||||
@@ -150,12 +150,13 @@ public class FunctionParsing extends Parsing {
|
||||
|
||||
private void parseParameterSubList() {
|
||||
assertCurrentToken(PyTokenTypes.LPAR);
|
||||
final PsiBuilder.Marker tuple = myBuilder.mark();
|
||||
myBuilder.advanceLexer();
|
||||
while (true) {
|
||||
if (myBuilder.getTokenType() == PyTokenTypes.IDENTIFIER) {
|
||||
final PsiBuilder.Marker parameter = myBuilder.mark();
|
||||
myBuilder.advanceLexer();
|
||||
parameter.done(PyElementTypes.FORMAL_PARAMETER);
|
||||
parameter.done(PyElementTypes.NAMED_PARAMETER);
|
||||
}
|
||||
else if (myBuilder.getTokenType() == PyTokenTypes.LPAR) {
|
||||
parseParameterSubList();
|
||||
@@ -170,5 +171,6 @@ public class FunctionParsing extends Parsing {
|
||||
}
|
||||
myBuilder.advanceLexer();
|
||||
}
|
||||
tuple.done(PyElementTypes.TUPLE_PARAMETER);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,9 @@ public interface PyArgumentList extends PyElement {
|
||||
void addArgumentFirst(PyExpression arg);
|
||||
void addArgumentAfter(PyExpression argument, PyExpression afterThis);
|
||||
|
||||
/**
|
||||
* @return the call expression to which this argument list belongs; not null in correctly parsed cases.
|
||||
*/
|
||||
@Nullable
|
||||
PyCallExpression getCallExpression();
|
||||
|
||||
@@ -68,7 +71,22 @@ public interface PyArgumentList extends PyElement {
|
||||
/**
|
||||
* @return A mapping parameter->argument for non-starred parameters (but includes starred argument).
|
||||
*/
|
||||
@NotNull Map<PyExpression, PyParameter> getPlainMappedParams();
|
||||
@NotNull Map<PyExpression, PyNamedParameter> getPlainMappedParams();
|
||||
|
||||
/**
|
||||
* Consider a piece of Python 2.x code:
|
||||
* <pre>
|
||||
* def f(a, (b, c,), d):
|
||||
* ...
|
||||
*
|
||||
* x = (1, 2)
|
||||
* f(10, x, 20)
|
||||
* </pre>
|
||||
* Here, argument <tt>x</tt> successfully maps to both <tt>b</tt> and <tt>c</tt> parameters.
|
||||
* This case is rare, so a separate method is introduced, to keep {@link AnalysisResult#getPlainMappedParams()} simple.
|
||||
* @return mapping of arguments to nested parameters that get collectively mapped to that argument.
|
||||
*/
|
||||
@NotNull Map<PyExpression, List<PyNamedParameter>> getNestedMappedParams();
|
||||
|
||||
/**
|
||||
* @return First *arg, or null.
|
||||
@@ -79,7 +97,7 @@ public interface PyArgumentList extends PyElement {
|
||||
/**
|
||||
* @return A list of parameters mapped to a *arg.
|
||||
*/
|
||||
@NotNull List<PyParameter> getTupleMappedParams();
|
||||
@NotNull List<PyNamedParameter> getTupleMappedParams();
|
||||
|
||||
/**
|
||||
* @return First **arg, or null.
|
||||
@@ -90,13 +108,13 @@ public interface PyArgumentList extends PyElement {
|
||||
/**
|
||||
* @return A list of parameters mapped to an **arg.
|
||||
*/
|
||||
@NotNull List<PyParameter> getKwdMappedParams();
|
||||
@NotNull List<PyNamedParameter> getKwdMappedParams();
|
||||
|
||||
/**
|
||||
* @return A list of parameters for which no arguments were found ('missing').
|
||||
*/
|
||||
@NotNull
|
||||
List<PyParameter> getUnmappedParams();
|
||||
List<PyNamedParameter> getUnmappedParams();
|
||||
|
||||
|
||||
/**
|
||||
@@ -106,7 +124,7 @@ public interface PyArgumentList extends PyElement {
|
||||
Map<PyExpression, EnumSet<ArgFlag>> getArgumentFlags();
|
||||
|
||||
/**
|
||||
* @return result of a resolveCallee() against the function call to which the paramater list belongs.
|
||||
* @return result of a resolveCallee() against the function call to which the parameter list belongs.
|
||||
*/
|
||||
@Nullable
|
||||
PyCallExpression.PyMarkedFunction getMarkedFunction();
|
||||
|
||||
@@ -66,5 +66,5 @@ public interface PyElementGenerator {
|
||||
<T> T createFromText(final Project project, Class<T> aClass, final String text);
|
||||
<T> T createFromText(final Project project, Class<T> aClass, final String text, final int[] path);
|
||||
|
||||
PyParameter createParameter(@NotNull final Project project, @NotNull String name);
|
||||
PyNamedParameter createParameter(@NotNull final Project project, @NotNull String name);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2005 Pythonid Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS"; BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.jetbrains.python.psi;
|
||||
|
||||
import com.intellij.psi.PsiNamedElement;
|
||||
import com.intellij.psi.StubBasedPsiElement;
|
||||
import com.jetbrains.python.psi.stubs.PyNamedParameterStub;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Represents a named parameter, as opposed to a tuple parameter.
|
||||
*/
|
||||
public interface PyNamedParameter extends PyParameter, PyElement, PsiNamedElement, PyExpression, StubBasedPsiElement<PyNamedParameterStub> {
|
||||
boolean isPositionalContainer();
|
||||
|
||||
boolean isKeywordContainer();
|
||||
|
||||
@Nullable
|
||||
PyExpression getDefaultValue();
|
||||
|
||||
/**
|
||||
* @param includeDefaultValue if true, include the default value after an " = ".
|
||||
* @return Canonical representation of parameter. Includes asterisks for *param and **param, and name.
|
||||
*/
|
||||
@NotNull
|
||||
String getRepr(boolean includeDefaultValue);
|
||||
}
|
||||
|
||||
@@ -1,47 +1,24 @@
|
||||
/*
|
||||
* Copyright 2005 Pythonid Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS"; BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.jetbrains.python.psi;
|
||||
|
||||
import com.intellij.psi.PsiNamedElement;
|
||||
import com.intellij.psi.StubBasedPsiElement;
|
||||
import com.jetbrains.python.psi.stubs.PyParameterStub;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Created by IntelliJ IDEA.
|
||||
* User: yole
|
||||
* Date: 29.05.2005
|
||||
* Time: 23:04:33
|
||||
* To change this template use File | Settings | File Templates.
|
||||
* Abstract function parameter; may cover either a named parameter or a tuple of parameters.
|
||||
* @see com.jetbrains.python.psi.impl.ParamHelper
|
||||
* User: dcheryasov
|
||||
* Date: Jul 5, 2009 8:30:13 PM
|
||||
*/
|
||||
public interface PyParameter extends PyElement, PsiNamedElement, PyExpression, StubBasedPsiElement<PyParameterStub> {
|
||||
boolean isPositionalContainer();
|
||||
|
||||
boolean isKeywordContainer();
|
||||
|
||||
@Nullable
|
||||
PyExpression getDefaultValue();
|
||||
public interface PyParameter extends PyElement {
|
||||
|
||||
/**
|
||||
* @param includeDefaultValue if true, include the default value after an " = ".
|
||||
* @return Canonical representation of parameter. Includes asterisks for *param and **param, and name.
|
||||
* @return the named parameter which is represented by this parameter, or null if the parameter is a tuple.
|
||||
*/
|
||||
@NotNull
|
||||
String getRepr(boolean includeDefaultValue);
|
||||
}
|
||||
@Nullable
|
||||
PyNamedParameter getAsNamed();
|
||||
|
||||
/**
|
||||
* @return the tuple parameter which is represented by this parameter, or null if the parameter is named.
|
||||
*/
|
||||
@Nullable
|
||||
PyTupleParameter getAsTuple();
|
||||
}
|
||||
|
||||
@@ -36,13 +36,6 @@ public interface PyParameterList extends PyElement, StubBasedPsiElement<PyParame
|
||||
* Adds a paramter to list, after all other parameters.
|
||||
* @param param what to add
|
||||
*/
|
||||
void addParameter(PyParameter param);
|
||||
void addParameter(PyNamedParameter param);
|
||||
|
||||
/**
|
||||
* Python 2.x allows for declarations like {@code def foo(a, (b, c))} that auto-unpack complex tuple parameters.
|
||||
* From caller side, such functions contain a smaller number of parameters, some of them unnamed and structurally constrained.
|
||||
* (This is considered evil and eschewed in Py3k.)
|
||||
* @return true if the parameter list contains a tuple-based declaration.
|
||||
*/
|
||||
boolean containsTuples();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.jetbrains.python.psi;
|
||||
|
||||
import com.intellij.psi.StubBasedPsiElement;
|
||||
import com.jetbrains.python.psi.stubs.PyTupleParameterStub;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Tuple parameter. Defines nothing; this interface is only needed for stub creation.
|
||||
*/
|
||||
public interface PyTupleParameter extends PyParameter, StubBasedPsiElement<PyTupleParameterStub> {
|
||||
|
||||
/**
|
||||
* @return the nested parameters within this tuple parameter.
|
||||
*/
|
||||
@NotNull
|
||||
PyParameter[] getContents();
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
package com.jetbrains.python.psi.impl;
|
||||
|
||||
import com.jetbrains.python.psi.PyNamedParameter;
|
||||
import com.jetbrains.python.psi.PyParameter;
|
||||
import com.jetbrains.python.psi.PyParameterList;
|
||||
import com.jetbrains.python.psi.PyTupleParameter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Parameter-related things that should not belong directly to PyParameter.
|
||||
*/
|
||||
public class ParamHelper {
|
||||
|
||||
/**
|
||||
* Runs a {@link ParamWalker ParamWalker} down the array of parameters, recursively descending into tuple parameters.
|
||||
* If the array is from PyParamaterList.getParameters(), parameters are visited in the order of textual appearance
|
||||
* @param params where to walk
|
||||
* @param walker the walker with callbacks.
|
||||
*/
|
||||
public static void walkDownParamArray(PyParameter[] params, ParamWalker walker) {
|
||||
int last = params.length-1;
|
||||
int i = 0;
|
||||
for (PyParameter param : params) {
|
||||
PyTupleParameter t_param = param.getAsTuple();
|
||||
if (t_param != null) {
|
||||
PyParameter[] nested_params = t_param.getContents();
|
||||
PyTupleParameter tpar = (PyTupleParameter)param;
|
||||
walker.enterTupleParameter(tpar, (i==0), (i == last));
|
||||
walkDownParamArray(nested_params, walker);
|
||||
walker.leaveTupleParameter(tpar, (i==0), (i == last));
|
||||
}
|
||||
else walker.visitNamedParameter(param.getAsNamed(), (i==0), (i == last));
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
public static interface ParamWalker {
|
||||
/**
|
||||
* Is called when a tuple parameter is encountered, before visiting any parameters nested in it.
|
||||
* @param param the parameter
|
||||
* @param first true iff it is the first in the list
|
||||
* @param last true it is the last in the list
|
||||
*/
|
||||
void enterTupleParameter(PyTupleParameter param, boolean first, boolean last);
|
||||
|
||||
/**
|
||||
* Is called when all nested parameters of a given tuple parameter are visited.
|
||||
* @param param the parameter
|
||||
* @param first true iff it is the first in the list
|
||||
* @param last true it is the last in the list
|
||||
*/
|
||||
void leaveTupleParameter(PyTupleParameter param, boolean first, boolean last);
|
||||
|
||||
/**
|
||||
* Is called when a named parameter is encountered.
|
||||
* @param param the parameter
|
||||
* @param first true iff it is the first in the list
|
||||
* @param last true it is the last in the list
|
||||
*/
|
||||
void visitNamedParameter(PyNamedParameter param, boolean first, boolean last);
|
||||
}
|
||||
|
||||
public static abstract class ParamVisitor implements ParamWalker {
|
||||
public void enterTupleParameter(PyTupleParameter param, boolean first, boolean last) { }
|
||||
|
||||
public void leaveTupleParameter(PyTupleParameter param, boolean first, boolean last) { }
|
||||
|
||||
public void visitNamedParameter(PyNamedParameter param, boolean first, boolean last) { }
|
||||
}
|
||||
|
||||
public static StringBuilder appendParameterList(PyParameterList plist, final StringBuilder target) {
|
||||
final String COMMA = ", ";
|
||||
target.append("(");
|
||||
walkDownParamArray(
|
||||
plist.getParameters(),
|
||||
new ParamWalker() {
|
||||
public void enterTupleParameter(PyTupleParameter param, boolean first, boolean last) {
|
||||
target.append("(");
|
||||
}
|
||||
|
||||
public void leaveTupleParameter(PyTupleParameter param, boolean first, boolean last) {
|
||||
target.append(")");
|
||||
if (! last) target.append(COMMA);
|
||||
}
|
||||
|
||||
public void visitNamedParameter(PyNamedParameter param, boolean first, boolean last) {
|
||||
target.append(param.getRepr(true));
|
||||
if (! last) target.append(COMMA);
|
||||
}
|
||||
}
|
||||
);
|
||||
target.append(")");
|
||||
return target;
|
||||
}
|
||||
|
||||
public static List<PyNamedParameter> collectNamedParameters(PyParameterList plist) {
|
||||
final List<PyNamedParameter> result = new ArrayList<PyNamedParameter>(10); // a random 'enough'
|
||||
walkDownParamArray(
|
||||
plist.getParameters(),
|
||||
new ParamWalker() {
|
||||
public void enterTupleParameter(PyTupleParameter param, boolean first, boolean last) { }
|
||||
|
||||
public void leaveTupleParameter(PyTupleParameter param, boolean first, boolean last) { }
|
||||
|
||||
public void visitNamedParameter(PyNamedParameter param, boolean first, boolean last) {
|
||||
result.add(param);
|
||||
}
|
||||
}
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -189,7 +189,7 @@ public class PyArgumentListImpl extends PyElementImpl implements PyArgumentList
|
||||
}
|
||||
}
|
||||
if (!good) {
|
||||
throw new IllegalArgumentException("Expression " + afterThis + " is not an argument (" + getArguments() + ")");
|
||||
throw new IllegalArgumentException("Expression " + afterThis + " is not an argument (" + Arrays.toString(getArguments()) + ")");
|
||||
}
|
||||
// CASES:
|
||||
ASTNode node = afterThis.getNode().getTreeNext();
|
||||
@@ -247,10 +247,11 @@ public class PyArgumentListImpl extends PyElementImpl implements PyArgumentList
|
||||
}
|
||||
|
||||
public AnalysisResult analyzeCall() {
|
||||
AnalysisResultImpl ret = new AnalysisResultImpl();
|
||||
final AnalysisResultImpl ret = new AnalysisResultImpl();
|
||||
PyExpression[] arguments = getArguments();
|
||||
// declaration-based checks
|
||||
// proper arglist is: [positional,...][name=value,...][*tuple,][**dict]
|
||||
// where "positional" may be a tuple of nested "positional" parameters, too.
|
||||
// following the spec: http://docs.python.org/ref/calls.html
|
||||
PyCallExpression call = getCallExpression();
|
||||
if (call != null) {
|
||||
@@ -259,21 +260,24 @@ public class PyArgumentListImpl extends PyElementImpl implements PyArgumentList
|
||||
if (resolved_callee != null) {
|
||||
PyFunction func = resolved_callee.getFunction();
|
||||
PyParameterList paramlist = func.getParameterList();
|
||||
if (paramlist.containsTuples()) return null; // NOTE: we're not ready to handle this yet.
|
||||
PyParameter[] params = paramlist.getParameters();
|
||||
// prepare args and slots
|
||||
List<PyExpression> unmatched_args = new LinkedList<PyExpression>();
|
||||
Collections.addAll(unmatched_args, arguments);
|
||||
Map<String, PyExpression> param_slots = new HashMap<String, PyExpression>();
|
||||
PyParameter kwd_slot = null; // the *tuple. might be just boolean, but this way debugging is easier
|
||||
PyParameter tuple_slot = null; // the **kwd
|
||||
PyNamedParameter kwd_slot = null; // the *tuple. might be just boolean, but this way debugging is easier
|
||||
PyNamedParameter tuple_slot = null; // the **kwd
|
||||
PyStarArgument kwd_arg = null;
|
||||
PyStarArgument tuple_arg = null;
|
||||
final List<PyExpression> unmatched_subargs = new LinkedList<PyExpression>(); // unmatched nested arguments will go here
|
||||
// all slots are initially empty, *x and **x are not among slots
|
||||
for (PyParameter a_param : params) {
|
||||
if (a_param.isPositionalContainer()) tuple_slot = a_param;
|
||||
else if (a_param.isKeywordContainer()) kwd_slot = a_param;
|
||||
else param_slots.put(a_param.getName(), null);
|
||||
PyNamedParameter n_param = a_param.getAsNamed();
|
||||
if (n_param != null) {
|
||||
if (n_param.isPositionalContainer()) tuple_slot = n_param;
|
||||
else if (n_param.isKeywordContainer()) kwd_slot = n_param;
|
||||
else param_slots.put(a_param.getName(), null);
|
||||
}
|
||||
}
|
||||
// look for star args
|
||||
for (PyExpression arg : arguments) {
|
||||
@@ -310,19 +314,28 @@ public class PyArgumentListImpl extends PyElementImpl implements PyArgumentList
|
||||
ListIterator<PyExpression> unmatched_arg_iter = unmatched_args.listIterator();
|
||||
// check positional args
|
||||
while (unmatched_arg_iter.hasNext() && (param_index < params.length)) {
|
||||
PyExpression arg = unmatched_arg_iter.next(); // current arg
|
||||
PyParameter param = params[param_index]; // its matching param
|
||||
if (
|
||||
arg instanceof PyKeywordArgument || arg instanceof PyStarArgument ||
|
||||
param.isKeywordContainer() || param.isPositionalContainer()
|
||||
) {
|
||||
seen_tuple_arg |= (arg == tuple_arg);
|
||||
seen_kwd_arg |= (arg == kwd_arg);
|
||||
unmatched_arg_iter.previous(); // step back
|
||||
break;
|
||||
final PyExpression arg = unmatched_arg_iter.next(); // current arg
|
||||
PyParameter a_param = params[param_index]; // its matching param
|
||||
PyNamedParameter n_param = a_param.getAsNamed();
|
||||
if (n_param != null) { // named
|
||||
if (
|
||||
arg instanceof PyKeywordArgument || arg instanceof PyStarArgument ||
|
||||
n_param.isKeywordContainer() || n_param.isPositionalContainer()
|
||||
) {
|
||||
seen_tuple_arg |= (arg == tuple_arg);
|
||||
seen_kwd_arg |= (arg == kwd_arg);
|
||||
unmatched_arg_iter.previous(); // step back
|
||||
break;
|
||||
}
|
||||
param_slots.put(n_param.getName(), arg); // it cannot yet contain this name unless function definition is broken
|
||||
ret.my_plain_mapped_params.put(arg, n_param);
|
||||
}
|
||||
else { // tuple: it may contain only positionals or other tuples.
|
||||
unmatched_arg_iter.previous(); // step back so that the visitor takes this arg again
|
||||
MyParamVisitor visitor = new MyParamVisitor(unmatched_arg_iter, ret);
|
||||
visitor.enterTuple(a_param.getAsTuple()); // will recurse as needed
|
||||
unmatched_subargs.addAll(visitor.getUnmatchedSubargs()); // what it's seen
|
||||
}
|
||||
param_slots.put(param.getName(), arg); // it cannot yet contain this name unless function definition is broken
|
||||
ret.my_plain_mapped_params.put(arg, param);
|
||||
unmatched_arg_iter.remove(); // it has been matched
|
||||
param_index += 1;
|
||||
}
|
||||
@@ -390,47 +403,56 @@ public class PyArgumentListImpl extends PyElementImpl implements PyArgumentList
|
||||
}
|
||||
}
|
||||
if (seen_tuple_arg) { // link remaining params to *arg if present
|
||||
for (PyParameter param : params) {
|
||||
final String param_name = param.getName();
|
||||
if (
|
||||
(param.getDefaultValue() == null) && // has no default value
|
||||
param_slots.containsKey(param_name) && // known as a slot
|
||||
(param_slots.get(param_name) == null) // the slot yet unfilled
|
||||
) {
|
||||
param_slots.put(param_name, tuple_arg);
|
||||
unmatched_args.remove(tuple_arg);
|
||||
for (PyParameter a_param : params) {
|
||||
PyNamedParameter n_param = a_param.getAsNamed();
|
||||
if (n_param != null) {
|
||||
final String param_name = n_param.getName();
|
||||
if (
|
||||
(n_param.getDefaultValue() == null) && // has no default value
|
||||
param_slots.containsKey(param_name) && // known as a slot
|
||||
(param_slots.get(param_name) == null) // the slot yet unfilled
|
||||
) {
|
||||
param_slots.put(param_name, tuple_arg);
|
||||
unmatched_args.remove(tuple_arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (seen_kwd_arg) { // link remaining params to **kwarg if present
|
||||
for (PyParameter param : params) {
|
||||
final String param_name = param.getName();
|
||||
if (
|
||||
(param.getDefaultValue() == null) && // has no default value
|
||||
param_slots.containsKey(param_name) && // known as a slot
|
||||
(param_slots.get(param_name) == null) // the slot yet unfilled
|
||||
) {
|
||||
param_slots.put(param_name, kwd_arg);
|
||||
unmatched_args.remove(kwd_arg);
|
||||
for (PyParameter a_param : params) {
|
||||
PyNamedParameter n_param = a_param.getAsNamed();
|
||||
if (n_param != null) {
|
||||
final String param_name = n_param.getName();
|
||||
if (
|
||||
(n_param.getDefaultValue() == null) && // has no default value
|
||||
param_slots.containsKey(param_name) && // known as a slot
|
||||
(param_slots.get(param_name) == null) // the slot yet unfilled
|
||||
) {
|
||||
param_slots.put(param_name, kwd_arg);
|
||||
unmatched_args.remove(kwd_arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// check and collect all yet unfilled params without default values
|
||||
Map<String, PyParameter> unfilled_params = new HashMap<String, PyParameter>();
|
||||
for (PyParameter param : params) {
|
||||
final String param_name = param.getName();
|
||||
if (
|
||||
(param.getDefaultValue() == null) && // has no default value
|
||||
param_slots.containsKey(param_name) && // known as a slot
|
||||
(param_slots.get(param_name) == null) // the slot yet unfilled
|
||||
) {
|
||||
if (tuple_arg != null) {
|
||||
// An *arg, if present, fills all positional params
|
||||
param_slots.put(param_name, tuple_arg);
|
||||
unmatched_args.remove(tuple_arg);
|
||||
}
|
||||
else {
|
||||
unfilled_params.put(param_name, param);
|
||||
Map<String, PyNamedParameter> unfilled_params = new HashMap<String, PyNamedParameter>();
|
||||
for (PyParameter a_param : params) {
|
||||
PyNamedParameter n_param = a_param.getAsNamed();
|
||||
if (n_param != null) {
|
||||
final String param_name = n_param.getName();
|
||||
if (
|
||||
(n_param.getDefaultValue() == null) && // has no default value
|
||||
param_slots.containsKey(param_name) && // known as a slot
|
||||
(param_slots.get(param_name) == null) // the slot yet unfilled
|
||||
) {
|
||||
if (tuple_arg != null) {
|
||||
// An *arg, if present, fills all positional params
|
||||
param_slots.put(param_name, tuple_arg);
|
||||
unmatched_args.remove(tuple_arg);
|
||||
}
|
||||
else {
|
||||
unfilled_params.put(param_name, n_param);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -445,26 +467,32 @@ public class PyArgumentListImpl extends PyElementImpl implements PyArgumentList
|
||||
}
|
||||
// maybe we did not map a star arg because all eligible params have defaults; time to be less picky now.
|
||||
if (tuple_arg != null && ret.my_tuple_mapped_params.isEmpty()) { // link remaining params to *arg if nothing else is mapped to it
|
||||
for (PyParameter param : params) {
|
||||
final String param_name = param.getName();
|
||||
if (
|
||||
param_slots.containsKey(param_name) && // known as a slot
|
||||
(param_slots.get(param_name) == null) // the slot yet unfilled
|
||||
) {
|
||||
param_slots.put(param_name, tuple_arg);
|
||||
unmatched_args.remove(tuple_arg);
|
||||
for (PyParameter a_param : params) {
|
||||
PyNamedParameter n_param = a_param.getAsNamed();
|
||||
if (n_param != null) {
|
||||
final String param_name = n_param.getName();
|
||||
if (
|
||||
param_slots.containsKey(param_name) && // known as a slot
|
||||
(param_slots.get(param_name) == null) // the slot yet unfilled
|
||||
) {
|
||||
param_slots.put(param_name, tuple_arg);
|
||||
unmatched_args.remove(tuple_arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (kwd_arg != null && ret.my_kwd_mapped_params.isEmpty()) { // link remaining params to **kwarg if nothing else is mapped to it
|
||||
for (PyParameter param : params) {
|
||||
final String param_name = param.getName();
|
||||
if (
|
||||
param_slots.containsKey(param_name) && // known as a slot
|
||||
(param_slots.get(param_name) == null) // the slot yet unfilled
|
||||
) {
|
||||
param_slots.put(param_name, kwd_arg);
|
||||
unmatched_args.remove(kwd_arg);
|
||||
for (PyParameter a_param : params) {
|
||||
PyNamedParameter n_param = a_param.getAsNamed();
|
||||
if (n_param != null) {
|
||||
final String param_name = n_param.getName();
|
||||
if (
|
||||
param_slots.containsKey(param_name) && // known as a slot
|
||||
(param_slots.get(param_name) == null) // the slot yet unfilled
|
||||
) {
|
||||
param_slots.put(param_name, kwd_arg);
|
||||
unmatched_args.remove(kwd_arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -482,48 +510,122 @@ public class PyArgumentListImpl extends PyElementImpl implements PyArgumentList
|
||||
ret.markArgument(arg, ArgFlag.IS_UNMAPPED);
|
||||
}
|
||||
// any params still unfilled?
|
||||
for (final PyParameter param : unfilled_params.values()) {
|
||||
for (final PyNamedParameter param : unfilled_params.values()) {
|
||||
// getHolder().createErrorAnnotation(close_paren, "parameter '" + param_name + "' unfilled");
|
||||
ret.my_unmapped_params.add(param);
|
||||
}
|
||||
// copy the mapping of args
|
||||
for (PyParameter param : params) {
|
||||
PyExpression arg = param_slots.get(param.getName());
|
||||
if (arg != null) {
|
||||
if (arg instanceof PyStarArgument) {
|
||||
PyStarArgument star_arg = (PyStarArgument)arg;
|
||||
if (star_arg.isKeyword()) ret.my_kwd_mapped_params.add(param);
|
||||
else ret.my_tuple_mapped_params.add(param);
|
||||
for (PyParameter a_param : params) {
|
||||
PyNamedParameter n_param = a_param.getAsNamed();
|
||||
if (n_param != null) {
|
||||
PyExpression arg = param_slots.get(n_param.getName());
|
||||
if (arg != null) {
|
||||
if (arg instanceof PyStarArgument) {
|
||||
PyStarArgument star_arg = (PyStarArgument)arg;
|
||||
if (star_arg.isKeyword()) ret.my_kwd_mapped_params.add(n_param);
|
||||
else ret.my_tuple_mapped_params.add(n_param);
|
||||
}
|
||||
else ret.my_plain_mapped_params.put(arg, n_param);
|
||||
}
|
||||
else ret.my_plain_mapped_params.put(arg, param);
|
||||
}
|
||||
}
|
||||
// copy starred args
|
||||
ret.my_kwd_arg = kwd_arg;
|
||||
ret.my_tuple_arg = tuple_arg;
|
||||
// add unmatched nested arguments
|
||||
for (PyExpression subarg : unmatched_subargs) {
|
||||
ret.my_arg_flags.put(subarg, EnumSet.of(ArgFlag.IS_UNMAPPED));
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static class MyParamVisitor extends PyElementVisitor {
|
||||
private Iterator<PyExpression> myArgIterator;
|
||||
private AnalysisResultImpl myResult;
|
||||
private List<PyExpression> myUnmatchedSubargs;
|
||||
|
||||
protected /*static*/ class AnalysisResultImpl implements AnalysisResult {
|
||||
private MyParamVisitor(Iterator<PyExpression> arg_iterator, AnalysisResultImpl ret) {
|
||||
myArgIterator = arg_iterator;
|
||||
myResult = ret;
|
||||
myUnmatchedSubargs = new ArrayList<PyExpression>(5); // arbitrary 'enough'
|
||||
}
|
||||
|
||||
protected Map<PyExpression, PyParameter> my_plain_mapped_params;
|
||||
protected PyStarArgument my_tuple_arg;
|
||||
protected PyStarArgument my_kwd_arg;
|
||||
protected List<PyParameter> my_tuple_mapped_params;
|
||||
protected List<PyParameter> my_kwd_mapped_params;
|
||||
protected List<PyParameter> my_unmapped_params;
|
||||
protected Map<PyExpression, EnumSet<ArgFlag>> my_arg_flags;
|
||||
protected PyCallExpression.PyMarkedFunction my_marked_func;
|
||||
private Collection<PyExpression> getUnmatchedSubargs() {
|
||||
return myUnmatchedSubargs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPyParameter(PyParameter node) {
|
||||
PyNamedParameter named = node.getAsNamed();
|
||||
if (named != null) enterNamed(named);
|
||||
else enterTuple(node.getAsTuple());
|
||||
}
|
||||
|
||||
public void enterTuple(PyTupleParameter param) {
|
||||
PyExpression arg = null;
|
||||
if (myArgIterator.hasNext()) arg = myArgIterator.next();
|
||||
// try to unpack a tuple expr in argument, if there's any
|
||||
PyExpression[] elements = null;
|
||||
if (arg instanceof PyParenthesizedExpression) {
|
||||
PyExpression inner_expr = ((PyParenthesizedExpression)arg).getContainedExpression();
|
||||
if (inner_expr instanceof PyTupleExpression) elements = ((PyTupleExpression)inner_expr).getElements();
|
||||
}
|
||||
else if (arg instanceof PyListLiteralExpression) {
|
||||
elements = ((PyListLiteralExpression)arg).getElements();
|
||||
}
|
||||
final PyParameter[] nested_params = param.getContents();
|
||||
if (elements != null) { // recursively map expression's tuple to parameter's.
|
||||
MyParamVisitor visitor = new MyParamVisitor(Arrays.asList(elements).iterator(), myResult);
|
||||
for (PyParameter nested : nested_params) nested.accept(visitor);
|
||||
myUnmatchedSubargs.addAll(visitor.getUnmatchedSubargs());
|
||||
}
|
||||
else { // map all what's inside to this arg
|
||||
final List<PyNamedParameter> nested_mapped = new ArrayList<PyNamedParameter>(nested_params.length);
|
||||
ParamHelper.walkDownParamArray(
|
||||
nested_params,
|
||||
new ParamHelper.ParamVisitor() {
|
||||
@Override public void visitNamedParameter(PyNamedParameter param, boolean first, boolean last) {
|
||||
nested_mapped.add(param);
|
||||
}
|
||||
}
|
||||
);
|
||||
myResult.my_nested_mapped_params.put(arg, nested_mapped);
|
||||
}
|
||||
}
|
||||
|
||||
public void enterNamed(PyNamedParameter param) {
|
||||
if (myArgIterator.hasNext()) {
|
||||
PyExpression subarg = myArgIterator.next();
|
||||
myResult.my_plain_mapped_params.put(subarg, param);
|
||||
}
|
||||
else {
|
||||
myResult.my_unmapped_params.add(param);
|
||||
}
|
||||
// ...and *arg or **arg just won't parse inside a tuple, no need to handle it here
|
||||
}
|
||||
}
|
||||
|
||||
private class AnalysisResultImpl implements PyArgumentList.AnalysisResult {
|
||||
|
||||
private Map<PyExpression, PyNamedParameter> my_plain_mapped_params; // one param per arg
|
||||
private Map<PyExpression, List<PyNamedParameter>> my_nested_mapped_params; // one arg sweeps a nested tuple of params
|
||||
private PyStarArgument my_tuple_arg; // the *arg
|
||||
private PyStarArgument my_kwd_arg; // the **arg
|
||||
private List<PyNamedParameter> my_tuple_mapped_params; // params mapped to *arg
|
||||
private List<PyNamedParameter> my_kwd_mapped_params; // params mapped to **arg
|
||||
private List<PyNamedParameter> my_unmapped_params;
|
||||
private Map<PyExpression, EnumSet<PyArgumentList.ArgFlag>> my_arg_flags; // flags of every arg
|
||||
private PyCallExpression.PyMarkedFunction my_marked_func;
|
||||
|
||||
public AnalysisResultImpl() {
|
||||
// full of empty containers
|
||||
my_plain_mapped_params = new HashMap<PyExpression, PyParameter>();
|
||||
my_tuple_mapped_params = new ArrayList<PyParameter>();
|
||||
my_kwd_mapped_params = new ArrayList<PyParameter>();
|
||||
my_unmapped_params = new ArrayList<PyParameter>();
|
||||
my_plain_mapped_params = new HashMap<PyExpression, PyNamedParameter>();
|
||||
my_nested_mapped_params = new HashMap<PyExpression, List<PyNamedParameter>>();
|
||||
my_tuple_mapped_params = new ArrayList<PyNamedParameter>();
|
||||
my_kwd_mapped_params = new ArrayList<PyNamedParameter>();
|
||||
my_unmapped_params = new ArrayList<PyNamedParameter>();
|
||||
my_arg_flags = new HashMap<PyExpression, EnumSet<ArgFlag>>();
|
||||
my_marked_func = null;
|
||||
}
|
||||
@@ -531,10 +633,15 @@ public class PyArgumentListImpl extends PyElementImpl implements PyArgumentList
|
||||
/**
|
||||
* @return A mapping argument->parameter for non-starred arguments (but includes starred parameters).
|
||||
*/
|
||||
public @NotNull Map<PyExpression, PyParameter> getPlainMappedParams() {
|
||||
public @NotNull Map<PyExpression, PyNamedParameter> getPlainMappedParams() {
|
||||
return my_plain_mapped_params;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Map<PyExpression, List<PyNamedParameter>> getNestedMappedParams() {
|
||||
return my_nested_mapped_params;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return First *arg, or null.
|
||||
*/
|
||||
@@ -545,7 +652,7 @@ public class PyArgumentListImpl extends PyElementImpl implements PyArgumentList
|
||||
/**
|
||||
* @return A list of parameters mapped to an *arg.
|
||||
*/
|
||||
public @NotNull List<PyParameter> getTupleMappedParams(){
|
||||
public @NotNull List<PyNamedParameter> getTupleMappedParams(){
|
||||
return my_tuple_mapped_params;
|
||||
}
|
||||
|
||||
@@ -559,7 +666,7 @@ public class PyArgumentListImpl extends PyElementImpl implements PyArgumentList
|
||||
/**
|
||||
* @return A list of parameters mapped to an **arg.
|
||||
*/
|
||||
public @NotNull List<PyParameter> getKwdMappedParams(){
|
||||
public @NotNull List<PyNamedParameter> getKwdMappedParams(){
|
||||
return my_kwd_mapped_params;
|
||||
}
|
||||
|
||||
@@ -567,7 +674,7 @@ public class PyArgumentListImpl extends PyElementImpl implements PyArgumentList
|
||||
* @return A list of parameters for which no arguments were found ('missing').
|
||||
*/
|
||||
public @NotNull
|
||||
List<PyParameter> getUnmappedParams(){
|
||||
List<PyNamedParameter> getUnmappedParams(){
|
||||
return my_unmapped_params;
|
||||
}
|
||||
|
||||
|
||||
@@ -276,7 +276,7 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> implement
|
||||
}
|
||||
|
||||
final List<PyTargetExpression> result = new ArrayList<PyTargetExpression>();
|
||||
// NOTE: maybe treeCrawlUp would be more precise, but currently it works well enough to not care.
|
||||
// NOTE: maybe treeCrawlUp would be more precise, but currently it works well enough; don't care.
|
||||
initMethod.getStatementList().accept(new PyRecursiveElementVisitor() {
|
||||
public void visitPyAssignmentStatement(final PyAssignmentStatement node) {
|
||||
super.visitPyAssignmentStatement(node);
|
||||
|
||||
@@ -261,7 +261,7 @@ public class PyElementGeneratorImpl implements PyElementGenerator {
|
||||
|
||||
static int[] PATH_PARAMETER = {0, 3, 1};
|
||||
|
||||
public PyParameter createParameter(@NotNull final Project project, @NotNull String name) {
|
||||
return createFromText(project, PyParameter.class, "def f("+name+"): pass", PATH_PARAMETER);
|
||||
public PyNamedParameter createParameter(@NotNull final Project project, @NotNull String name) {
|
||||
return createFromText(project, PyNamedParameter.class, "def f("+name+"): pass", PATH_PARAMETER);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,11 +35,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
import javax.swing.*;
|
||||
|
||||
/**
|
||||
* Created by IntelliJ IDEA.
|
||||
* User: yole
|
||||
* Date: 29.05.2005
|
||||
* Time: 23:01:25
|
||||
* To change this template use File | Settings | File Templates.
|
||||
* Implements PyFunction.
|
||||
*/
|
||||
public class PyFunctionImpl extends PyPresentableElementImpl<PyFunctionStub> implements PyFunction {
|
||||
public PyFunctionImpl(ASTNode astNode) {
|
||||
@@ -127,8 +123,8 @@ public class PyFunctionImpl extends PyPresentableElementImpl<PyFunctionStub> imp
|
||||
{
|
||||
/*
|
||||
if (lastParent != null && lastParent.getParent() == this) {
|
||||
final PyParameter[] params = getParameterList().getParameters();
|
||||
for (PyParameter param : params) {
|
||||
final PyNamedParameter[] params = getParameterList().getParameters();
|
||||
for (PyNamedParameter param : params) {
|
||||
if (!processor.execute(param, substitutor)) return false;
|
||||
}
|
||||
}
|
||||
@@ -171,4 +167,8 @@ public class PyFunctionImpl extends PyPresentableElementImpl<PyFunctionStub> imp
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + "('" + getName() + "')";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,10 +21,7 @@ import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.ResolveState;
|
||||
import com.intellij.psi.scope.PsiScopeProcessor;
|
||||
import com.jetbrains.python.PyElementTypes;
|
||||
import com.jetbrains.python.psi.PyElementVisitor;
|
||||
import com.jetbrains.python.psi.PyLambdaExpression;
|
||||
import com.jetbrains.python.psi.PyParameter;
|
||||
import com.jetbrains.python.psi.PyParameterList;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.types.PyType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@@ -53,11 +50,25 @@ public class PyLambdaExpressionImpl extends PyElementImpl implements PyLambdaExp
|
||||
}
|
||||
|
||||
public boolean processDeclarations(@NotNull final PsiScopeProcessor processor, @NotNull final ResolveState state, final PsiElement lastParent,
|
||||
@NotNull final PsiElement place) {
|
||||
@NotNull final PsiElement place)
|
||||
{
|
||||
// TODO: move it to PyParamList
|
||||
PyParameter[] parameters = getParameterList().getParameters();
|
||||
return processParamLayer(parameters, processor, state, lastParent);
|
||||
}
|
||||
|
||||
private boolean processParamLayer(
|
||||
@NotNull final PyParameter[] parameters,
|
||||
@NotNull final PsiScopeProcessor processor, @NotNull final ResolveState state, final PsiElement lastParent
|
||||
) {
|
||||
for(PyParameter param: parameters) {
|
||||
if (param == lastParent) continue;
|
||||
if (!processor.execute(param, state)) return false;
|
||||
PyTupleParameter t_param = param.getAsTuple();
|
||||
if (t_param != null) {
|
||||
PyParameter[] nested_params = t_param.getContents();
|
||||
if (! processParamLayer(nested_params, processor, state, lastParent)) return false;
|
||||
}
|
||||
else if (!processor.execute(param, state)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
+21
-8
@@ -24,7 +24,7 @@ import com.intellij.util.IncorrectOperationException;
|
||||
import com.jetbrains.python.PyElementTypes;
|
||||
import com.jetbrains.python.PyTokenTypes;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.stubs.PyParameterStub;
|
||||
import com.jetbrains.python.psi.stubs.PyNamedParameterStub;
|
||||
import com.jetbrains.python.psi.types.PyClassType;
|
||||
import com.jetbrains.python.psi.types.PyType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -39,19 +39,19 @@ import javax.swing.*;
|
||||
* Time: 23:04:59
|
||||
* To change this template use File | Settings | File Templates.
|
||||
*/
|
||||
public class PyParameterImpl extends PyPresentableElementImpl<PyParameterStub> implements PyParameter {
|
||||
public PyParameterImpl(ASTNode astNode) {
|
||||
public class PyNamedParameterImpl extends PyPresentableElementImpl<PyNamedParameterStub> implements PyNamedParameter {
|
||||
public PyNamedParameterImpl(ASTNode astNode) {
|
||||
super(astNode);
|
||||
}
|
||||
|
||||
public PyParameterImpl(final PyParameterStub stub) {
|
||||
super(stub, PyElementTypes.FORMAL_PARAMETER);
|
||||
public PyNamedParameterImpl(final PyNamedParameterStub stub) {
|
||||
super(stub, PyElementTypes.NAMED_PARAMETER);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getName() {
|
||||
final PyParameterStub stub = getStub();
|
||||
final PyNamedParameterStub stub = getStub();
|
||||
if (stub != null) {
|
||||
return stub.getName();
|
||||
}
|
||||
@@ -73,7 +73,7 @@ public class PyParameterImpl extends PyPresentableElementImpl<PyParameterStub> i
|
||||
}
|
||||
|
||||
public boolean isPositionalContainer() {
|
||||
final PyParameterStub stub = getStub();
|
||||
final PyNamedParameterStub stub = getStub();
|
||||
if (stub != null) {
|
||||
return stub.isPositionalContainer();
|
||||
}
|
||||
@@ -83,7 +83,7 @@ public class PyParameterImpl extends PyPresentableElementImpl<PyParameterStub> i
|
||||
}
|
||||
|
||||
public boolean isKeywordContainer() {
|
||||
final PyParameterStub stub = getStub();
|
||||
final PyNamedParameterStub stub = getStub();
|
||||
if (stub != null) {
|
||||
return stub.isKeywordContainer();
|
||||
}
|
||||
@@ -119,6 +119,14 @@ public class PyParameterImpl extends PyPresentableElementImpl<PyParameterStub> i
|
||||
return Icons.PARAMETER_ICON;
|
||||
}
|
||||
|
||||
public PyNamedParameter getAsNamed() {
|
||||
return this;
|
||||
}
|
||||
|
||||
public PyTupleParameter getAsTuple() {
|
||||
return null; // we're not a tuple
|
||||
}
|
||||
|
||||
public PyType getType() {
|
||||
if (getParent() instanceof PyParameterList) {
|
||||
PyParameterList parameterList = (PyParameterList) getParent();
|
||||
@@ -139,4 +147,9 @@ public class PyParameterImpl extends PyPresentableElementImpl<PyParameterStub> i
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + "('" + getName() + "')";
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,6 @@ package com.jetbrains.python.psi.impl;
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.jetbrains.python.PyElementTypes;
|
||||
import com.jetbrains.python.PyTokenTypes;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.stubs.PyParameterListStub;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -46,38 +45,19 @@ public class PyParameterListImpl extends PyBaseElementImpl<PyParameterListStub>
|
||||
}
|
||||
|
||||
public PyParameter[] getParameters() {
|
||||
return getStubOrPsiChildren(PyElementTypes.FORMAL_PARAMETER, new PyParameter[0]);
|
||||
return getStubOrPsiChildren(PyElementTypes.PARAMETERS, new PyParameter[0]);
|
||||
}
|
||||
|
||||
public void addParameter(final PyParameter param) {
|
||||
public void addParameter(final PyNamedParameter param) {
|
||||
PsiElement paren = getLastChild();
|
||||
if (paren != null && ")".equals(paren.getText())) {
|
||||
PyUtil.ensureWritable(this);
|
||||
ASTNode beforeWhat = paren.getNode(); // the closing bracket will be this
|
||||
ASTNode beforeWhat = paren.getNode(); // the closing paren will be this
|
||||
PyParameter[] params = getParameters();
|
||||
PyUtil.addListNode(this, param, beforeWhat, true, params.length == 0);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean containsTuples() {
|
||||
// look for an opening paren after first paren. parser makes no efort to sort them; maybe should be fixed someday.
|
||||
// find opening LPAR; some whitespace may come first
|
||||
PsiElement seeker;
|
||||
for (seeker = getFirstChild(); seeker != null; seeker = seeker.getNextSibling()) { // C invented this flexible construct, why not use it?
|
||||
ASTNode node = seeker.getNode();
|
||||
if (node != null && node.getElementType() == PyTokenTypes.LPAR) break;
|
||||
}
|
||||
// an LPAR inside
|
||||
while (seeker != null) {
|
||||
seeker = seeker.getNextSibling(); // step over the LPAR we just saw
|
||||
if (seeker != null) {
|
||||
ASTNode node = seeker.getNode();
|
||||
if (node != null && node.getElementType() == PyTokenTypes.LPAR) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Iterable<PyElement> iterateNames() {
|
||||
return new ArrayIterable<PyElement>(getParameters());
|
||||
|
||||
@@ -339,9 +339,9 @@ public class PyReferenceExpressionImpl extends PyElementImpl implements PyRefere
|
||||
* Resolves reference to possible referred elements.
|
||||
* First element is always what resolve() would return.
|
||||
* Imported module names: to module file, or {directory, '__init__.py}' for a qualifier.
|
||||
* @todo Local identifiers: a list of definitions in the most recent compound statement
|
||||
* todo Local identifiers: a list of definitions in the most recent compound statement
|
||||
* (e.g. <code>if X: a = 1; else: a = 2</code> has two definitions of <code>a</code>.).
|
||||
* @todo Identifiers not found locally: similar definitions in imported files and builtins.
|
||||
* todo Identifiers not found locally: similar definitions in imported files and builtins.
|
||||
* @see com.intellij.psi.PsiPolyVariantReference#multiResolve(boolean)
|
||||
**/
|
||||
@NotNull
|
||||
@@ -419,8 +419,8 @@ public class PyReferenceExpressionImpl extends PyElementImpl implements PyRefere
|
||||
return ResolveImportUtil.suggestImportVariants(this);
|
||||
}
|
||||
|
||||
List<Object> ret = new ArrayList<Object>();
|
||||
LookupElementFactory factory = LookupElementFactory.getInstance();
|
||||
final List<Object> ret = new ArrayList<Object>();
|
||||
final LookupElementFactory factory = LookupElementFactory.getInstance();
|
||||
|
||||
// include our own names
|
||||
final VariantsProcessor processor = new VariantsProcessor();
|
||||
@@ -433,13 +433,20 @@ public class PyReferenceExpressionImpl extends PyElementImpl implements PyRefere
|
||||
if (callee instanceof PyReferenceExpression) {
|
||||
PsiElement def = ((PyReferenceExpression)callee).resolve();
|
||||
if (def instanceof PyFunction) {
|
||||
for (PyParameter param : ((PyFunction)def).getParameterList().getParameters()) {
|
||||
if (! param.isKeywordContainer() && ! param.isPositionalContainer()) {
|
||||
LookupItem item = (LookupItem)factory.createLookupElement(param.getName() + "=");
|
||||
item.setIcon(param.getIcon(0));
|
||||
ret.add(item);
|
||||
((PyFunction)def).getParameterList().accept(
|
||||
new PyElementVisitor() {
|
||||
@Override
|
||||
public void visitPyParameter(PyParameter par) {
|
||||
PyNamedParameter n_param = par.getAsNamed();
|
||||
assert n_param != null;
|
||||
if (! n_param.isKeywordContainer() && ! n_param.isPositionalContainer()) {
|
||||
LookupItem item = (LookupItem)factory.createLookupElement(n_param.getName() + "=");
|
||||
item.setIcon(n_param.getIcon(0));
|
||||
ret.add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -517,9 +524,7 @@ public class PyReferenceExpressionImpl extends PyElementImpl implements PyRefere
|
||||
}
|
||||
}
|
||||
|
||||
if (this == place) {
|
||||
return true;
|
||||
}
|
||||
if (this == place) return true;
|
||||
return processor.execute(this, substitutor);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.jetbrains.python.psi.impl;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.stubs.IStubElementType;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.jetbrains.python.PyElementTypes;
|
||||
import com.jetbrains.python.psi.PyNamedParameter;
|
||||
import com.jetbrains.python.psi.PyParameter;
|
||||
import com.jetbrains.python.psi.PyTupleParameter;
|
||||
import com.jetbrains.python.psi.PyElementVisitor;
|
||||
import com.jetbrains.python.psi.stubs.PyTupleParameterStub;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Represents a tuple parameter as stubbed element.
|
||||
*/
|
||||
public class PyTupleParameterImpl extends PyPresentableElementImpl<PyTupleParameterStub> implements PyTupleParameter {
|
||||
|
||||
public PyTupleParameterImpl(ASTNode astNode) {
|
||||
super(astNode);
|
||||
}
|
||||
|
||||
protected PyTupleParameterImpl(final PyTupleParameterStub stub, final IStubElementType nodeType) {
|
||||
super(stub, nodeType);
|
||||
}
|
||||
|
||||
public PyTupleParameterImpl(PyTupleParameterStub stub) {
|
||||
super(stub, PyElementTypes.TUPLE_PARAMETER);
|
||||
}
|
||||
|
||||
public PyNamedParameter getAsNamed() {
|
||||
return null; // we're not named
|
||||
}
|
||||
|
||||
public PyTupleParameter getAsTuple() {
|
||||
return this;
|
||||
}
|
||||
|
||||
public PsiElement setName(@NonNls @NotNull String name) throws IncorrectOperationException {
|
||||
throw new IncorrectOperationException("Can't rename a tuple parameter to '" + name +"'");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void acceptPyVisitor(PyElementVisitor pyVisitor) {
|
||||
pyVisitor.visitPyParameter(this);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public PyParameter[] getContents() {
|
||||
return getStubOrPsiChildren(PyElementTypes.PARAMETERS, new PyParameter[0]);
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ package com.jetbrains.python.psi.impl;
|
||||
|
||||
import com.intellij.openapi.extensions.ExtensionPointName;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.jetbrains.python.psi.PyParameter;
|
||||
import com.jetbrains.python.psi.PyNamedParameter;
|
||||
import com.jetbrains.python.psi.PyFunction;
|
||||
import com.jetbrains.python.psi.types.PyType;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -17,5 +17,5 @@ public interface PyTypeProvider {
|
||||
PyType getReferenceType(PsiElement referenceTarget);
|
||||
|
||||
@Nullable
|
||||
PyType getParameterType(PyParameter param, final PyFunction func);
|
||||
PyType getParameterType(PyNamedParameter param, final PyFunction func);
|
||||
}
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* @author max
|
||||
*/
|
||||
package com.jetbrains.python.psi.impl.stubs;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.stubs.StubElement;
|
||||
import com.intellij.psi.stubs.StubOutputStream;
|
||||
import com.intellij.psi.stubs.StubInputStream;
|
||||
import com.intellij.util.io.DataInputOutputUtil;
|
||||
import com.intellij.util.io.PersistentStringEnumerator;
|
||||
import com.intellij.util.io.StringRef;
|
||||
import com.jetbrains.python.psi.PyParameter;
|
||||
import com.jetbrains.python.psi.PyStubElementType;
|
||||
import com.jetbrains.python.psi.impl.PyParameterImpl;
|
||||
import com.jetbrains.python.psi.stubs.PyParameterStub;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class PyFormalParameterElementType extends PyStubElementType<PyParameterStub, PyParameter> {
|
||||
public PyFormalParameterElementType() {
|
||||
super("FORMAL_PARAMETER");
|
||||
}
|
||||
|
||||
public PyParameter createPsi(final PyParameterStub stub) {
|
||||
return new PyParameterImpl(stub);
|
||||
}
|
||||
|
||||
public PyParameterStub createStub(final PyParameter psi, final StubElement parentStub) {
|
||||
return new PyParameterStubImpl(psi.getName(), psi.isPositionalContainer(), psi.isKeywordContainer(), parentStub);
|
||||
}
|
||||
|
||||
public PsiElement createElement(final ASTNode node) {
|
||||
return new PyParameterImpl(node);
|
||||
}
|
||||
|
||||
public void serialize(final PyParameterStub stub, final StubOutputStream dataStream)
|
||||
throws IOException {
|
||||
dataStream.writeName(stub.getName());
|
||||
dataStream.writeBoolean(stub.isKeywordContainer());
|
||||
dataStream.writeBoolean(stub.isPositionalContainer());
|
||||
}
|
||||
|
||||
public PyParameterStub deserialize(final StubInputStream dataStream, final StubElement parentStub) throws IOException {
|
||||
String name = StringRef.toString(dataStream.readName());
|
||||
boolean keyword = dataStream.readBoolean();
|
||||
boolean positional = dataStream.readBoolean();
|
||||
return new PyParameterStubImpl(name, positional, keyword, parentStub);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* @author max
|
||||
*/
|
||||
package com.jetbrains.python.psi.impl.stubs;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.stubs.StubElement;
|
||||
import com.intellij.psi.stubs.StubOutputStream;
|
||||
import com.intellij.psi.stubs.StubInputStream;
|
||||
import com.intellij.util.io.StringRef;
|
||||
import com.jetbrains.python.psi.PyNamedParameter;
|
||||
import com.jetbrains.python.psi.PyStubElementType;
|
||||
import com.jetbrains.python.psi.impl.PyNamedParameterImpl;
|
||||
import com.jetbrains.python.psi.stubs.PyNamedParameterStub;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class PyNamedParameterElementType extends PyStubElementType<PyNamedParameterStub, PyNamedParameter> {
|
||||
public PyNamedParameterElementType() {
|
||||
super("NAMED_PARAMETER");
|
||||
}
|
||||
|
||||
public PyNamedParameter createPsi(final PyNamedParameterStub stub) {
|
||||
return new PyNamedParameterImpl(stub);
|
||||
}
|
||||
|
||||
public PyNamedParameterStub createStub(final PyNamedParameter psi, final StubElement parentStub) {
|
||||
return new PyNamedParameterStubImpl(psi.getName(), psi.isPositionalContainer(), psi.isKeywordContainer(), parentStub);
|
||||
}
|
||||
|
||||
public PsiElement createElement(final ASTNode node) {
|
||||
return new PyNamedParameterImpl(node);
|
||||
}
|
||||
|
||||
public void serialize(final PyNamedParameterStub stub, final StubOutputStream dataStream)
|
||||
throws IOException {
|
||||
dataStream.writeName(stub.getName());
|
||||
dataStream.writeBoolean(stub.isKeywordContainer());
|
||||
dataStream.writeBoolean(stub.isPositionalContainer());
|
||||
}
|
||||
|
||||
public PyNamedParameterStub deserialize(final StubInputStream dataStream, final StubElement parentStub) throws IOException {
|
||||
String name = StringRef.toString(dataStream.readName());
|
||||
boolean keyword = dataStream.readBoolean();
|
||||
boolean positional = dataStream.readBoolean();
|
||||
return new PyNamedParameterStubImpl(name, positional, keyword, parentStub);
|
||||
}
|
||||
}
|
||||
+5
-5
@@ -6,16 +6,16 @@ package com.jetbrains.python.psi.impl.stubs;
|
||||
import com.intellij.psi.stubs.StubBase;
|
||||
import com.intellij.psi.stubs.StubElement;
|
||||
import com.jetbrains.python.PyElementTypes;
|
||||
import com.jetbrains.python.psi.PyParameter;
|
||||
import com.jetbrains.python.psi.stubs.PyParameterStub;
|
||||
import com.jetbrains.python.psi.PyNamedParameter;
|
||||
import com.jetbrains.python.psi.stubs.PyNamedParameterStub;
|
||||
|
||||
public class PyParameterStubImpl extends StubBase<PyParameter> implements PyParameterStub {
|
||||
public class PyNamedParameterStubImpl extends StubBase<PyNamedParameter> implements PyNamedParameterStub {
|
||||
private final String myName;
|
||||
private final boolean myPositionalContainer;
|
||||
private final boolean myKeywordContainer;
|
||||
|
||||
public PyParameterStubImpl(String name, boolean isPositionalContainer, boolean isKeywordContainer, StubElement parent) {
|
||||
super(parent, PyElementTypes.FORMAL_PARAMETER);
|
||||
public PyNamedParameterStubImpl(String name, boolean isPositionalContainer, boolean isKeywordContainer, StubElement parent) {
|
||||
super(parent, PyElementTypes.NAMED_PARAMETER);
|
||||
|
||||
myName = name;
|
||||
myPositionalContainer = isPositionalContainer;
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.jetbrains.python.psi.impl.stubs;
|
||||
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.stubs.StubElement;
|
||||
import com.intellij.psi.stubs.StubInputStream;
|
||||
import com.intellij.psi.stubs.StubOutputStream;
|
||||
import com.jetbrains.python.psi.PyStubElementType;
|
||||
import com.jetbrains.python.psi.PyTupleParameter;
|
||||
import com.jetbrains.python.psi.impl.PyTupleParameterImpl;
|
||||
import com.jetbrains.python.psi.stubs.PyTupleParameterStub;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Does actual storing and loading of tuple parameter stub. Not much to do.
|
||||
*/
|
||||
public class PyTupleParameterElementType extends PyStubElementType<PyTupleParameterStub, PyTupleParameter> {
|
||||
|
||||
public PyTupleParameterElementType() {
|
||||
super("TUPLE_PARAMETER");
|
||||
}
|
||||
|
||||
public PsiElement createElement(ASTNode node) {
|
||||
return new PyTupleParameterImpl(node);
|
||||
}
|
||||
|
||||
public PyTupleParameter createPsi(PyTupleParameterStub stub) {
|
||||
return new PyTupleParameterImpl(stub);
|
||||
}
|
||||
|
||||
public PyTupleParameterStub createStub(PyTupleParameter psi, StubElement parentStub) {
|
||||
return new PyTupleParameterStubImpl(parentStub);
|
||||
}
|
||||
|
||||
public PyTupleParameterStub deserialize(StubInputStream dataStream, StubElement parentStub) throws IOException {
|
||||
return new PyTupleParameterStubImpl(parentStub);
|
||||
}
|
||||
|
||||
public void serialize(PyTupleParameterStub stub, StubOutputStream dataStream) throws IOException {
|
||||
// nothing; children serialize themselves
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.jetbrains.python.psi.impl.stubs;
|
||||
|
||||
import com.intellij.psi.stubs.StubBase;
|
||||
import com.intellij.psi.stubs.StubElement;
|
||||
import com.jetbrains.python.PyElementTypes;
|
||||
import com.jetbrains.python.psi.PyTupleParameter;
|
||||
import com.jetbrains.python.psi.stubs.PyTupleParameterStub;
|
||||
|
||||
/**
|
||||
* Implementation does nothing but marking the element type.
|
||||
* User: dcheryasov
|
||||
* Date: Jul 6, 2009 1:33:08 AM
|
||||
*/
|
||||
public class PyTupleParameterStubImpl extends StubBase<PyTupleParameter> implements PyTupleParameterStub {
|
||||
|
||||
protected PyTupleParameterStubImpl(StubElement parent) {
|
||||
super(parent, PyElementTypes.TUPLE_PARAMETER);
|
||||
}
|
||||
|
||||
}
|
||||
+2
-2
@@ -4,9 +4,9 @@
|
||||
package com.jetbrains.python.psi.stubs;
|
||||
|
||||
import com.intellij.psi.stubs.NamedStub;
|
||||
import com.jetbrains.python.psi.PyParameter;
|
||||
import com.jetbrains.python.psi.PyNamedParameter;
|
||||
|
||||
public interface PyParameterStub extends NamedStub<PyParameter> {
|
||||
public interface PyNamedParameterStub extends NamedStub<PyNamedParameter> {
|
||||
boolean isPositionalContainer();
|
||||
boolean isKeywordContainer();
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.jetbrains.python.psi.stubs;
|
||||
|
||||
import com.intellij.psi.stubs.StubElement;
|
||||
import com.jetbrains.python.psi.PyTupleParameter;
|
||||
|
||||
/**
|
||||
* Tuple parameter stub, collects nested parameters from stubs.
|
||||
*/
|
||||
public interface PyTupleParameterStub extends StubElement<PyTupleParameter> {
|
||||
}
|
||||
@@ -22,13 +22,13 @@ import com.intellij.navigation.NavigationItem;
|
||||
import com.intellij.openapi.editor.colors.TextAttributesKey;
|
||||
import com.intellij.openapi.util.Iconable;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiNamedElement;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.ui.LayeredIcon;
|
||||
import com.intellij.util.Icons;
|
||||
import com.jetbrains.python.PyIcons;
|
||||
import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.impl.ParamHelper;
|
||||
import com.jetbrains.python.psi.impl.PyBuiltinCache;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -43,175 +43,169 @@ import java.util.Set;
|
||||
*/
|
||||
public class PyStructureViewElement implements StructureViewTreeElement {
|
||||
|
||||
private enum Visibility {
|
||||
NORMAL, // visible
|
||||
INVISIBLE, // not visible: e.g. local to function
|
||||
PRIVATE, // "__foo" in a class
|
||||
PREDEFINED // like "__init__"; only if really visible
|
||||
}
|
||||
private PyElement my_element;
|
||||
private Visibility my_visibility;
|
||||
private Icon my_icon;
|
||||
private enum Visibility {
|
||||
NORMAL, // visible
|
||||
INVISIBLE, // not visible: e.g. local to function
|
||||
PRIVATE, // "__foo" in a class
|
||||
PREDEFINED // like "__init__"; only if really visible
|
||||
}
|
||||
|
||||
public PyStructureViewElement(PyElement element, Visibility vis) {
|
||||
my_element = element;
|
||||
my_visibility = vis;
|
||||
}
|
||||
private PyElement my_element;
|
||||
private Visibility my_visibility;
|
||||
private Icon my_icon;
|
||||
|
||||
public PyStructureViewElement(PyElement element) {
|
||||
this(element, Visibility.NORMAL);
|
||||
}
|
||||
public PyElement getValue() {
|
||||
return my_element;
|
||||
}
|
||||
public PyStructureViewElement(PyElement element, Visibility vis) {
|
||||
my_element = element;
|
||||
my_visibility = vis;
|
||||
}
|
||||
|
||||
public void navigate(boolean requestFocus) {
|
||||
((NavigationItem)my_element).navigate(requestFocus);
|
||||
}
|
||||
public PyStructureViewElement(PyElement element) {
|
||||
this(element, Visibility.NORMAL);
|
||||
}
|
||||
|
||||
public boolean canNavigate() {
|
||||
return ((NavigationItem)my_element).canNavigate();
|
||||
}
|
||||
public PyElement getValue() {
|
||||
return my_element;
|
||||
}
|
||||
|
||||
public boolean canNavigateToSource() {
|
||||
return ((NavigationItem)my_element).canNavigateToSource();
|
||||
}
|
||||
public void navigate(boolean requestFocus) {
|
||||
((NavigationItem)my_element).navigate(requestFocus);
|
||||
}
|
||||
|
||||
public void setIcon(Icon icon) {
|
||||
my_icon = icon;
|
||||
}
|
||||
public boolean canNavigate() {
|
||||
return ((NavigationItem)my_element).canNavigate();
|
||||
}
|
||||
|
||||
public StructureViewTreeElement[] getChildren() {
|
||||
final Set<PyElement> childrenElements = new HashSet<PyElement>();
|
||||
my_element.acceptChildren(new PyElementVisitor() {
|
||||
@Override public void visitElement(PsiElement element) {
|
||||
if (isWorthyClassItem(element)) {
|
||||
childrenElements.add((PyElement)element);
|
||||
}
|
||||
else {
|
||||
element.acceptChildren(this);
|
||||
}
|
||||
}
|
||||
public boolean canNavigateToSource() {
|
||||
return ((NavigationItem)my_element).canNavigateToSource();
|
||||
}
|
||||
|
||||
@Override public void visitPyParameter(final PyParameter node) {
|
||||
// Do not add parameters to structure view
|
||||
}
|
||||
});
|
||||
public void setIcon(Icon icon) {
|
||||
my_icon = icon;
|
||||
}
|
||||
|
||||
StructureViewTreeElement[] children = new StructureViewTreeElement[childrenElements.size()];
|
||||
int i = 0;
|
||||
for (PyElement element : childrenElements) {
|
||||
// look at functions and predefined __names__
|
||||
Visibility vis = Visibility.NORMAL;
|
||||
if (PsiTreeUtil.getParentOfType(element, PyFunction.class) != null) {
|
||||
// whatever is defined inside a def, is hidden
|
||||
vis = Visibility.INVISIBLE;
|
||||
public StructureViewTreeElement[] getChildren() {
|
||||
final Set<PyElement> childrenElements = new HashSet<PyElement>();
|
||||
my_element.acceptChildren(new PyElementVisitor() {
|
||||
@Override
|
||||
public void visitElement(PsiElement element) {
|
||||
if (isWorthyClassItem(element)) {
|
||||
childrenElements.add((PyElement)element);
|
||||
}
|
||||
else {
|
||||
element.acceptChildren(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void visitPyParameter(final PyNamedParameter node) {
|
||||
// Do not add parameters to structure view
|
||||
}
|
||||
});
|
||||
|
||||
StructureViewTreeElement[] children = new StructureViewTreeElement[childrenElements.size()];
|
||||
int i = 0;
|
||||
for (PyElement element : childrenElements) {
|
||||
// look at functions and predefined __names__
|
||||
Visibility vis = Visibility.NORMAL;
|
||||
if (PsiTreeUtil.getParentOfType(element, PyFunction.class) != null) {
|
||||
// whatever is defined inside a def, is hidden
|
||||
vis = Visibility.INVISIBLE;
|
||||
}
|
||||
else {
|
||||
String name = element.getName();
|
||||
if (name != null && name.startsWith("__")) {
|
||||
if (PyNames.UnderscoredNames.contains(name)) {
|
||||
vis = Visibility.PREDEFINED;
|
||||
}
|
||||
else {
|
||||
String name = element.getName();
|
||||
if (name != null && name.startsWith("__")) {
|
||||
if (PyNames.UnderscoredNames.contains(name)) vis = Visibility.PREDEFINED;
|
||||
else vis = Visibility.PRIVATE;
|
||||
}
|
||||
}
|
||||
children[i] = new PyStructureViewElement(element, vis);
|
||||
if (element instanceof PyClass) {
|
||||
PyClass the_exception = PyBuiltinCache.getInstance(element.getProject()).getClass("Exception");
|
||||
final PyClass cls = (PyClass)element;
|
||||
for (PyClass anc : cls.iterateAncestors()) {
|
||||
if (anc == the_exception) {
|
||||
((PyStructureViewElement)(children[i])).setIcon(Icons.EXCEPTION_CLASS_ICON);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
|
||||
return children;
|
||||
}
|
||||
|
||||
static boolean isWorthyClassItem(PsiElement element) {
|
||||
if (element instanceof PyClass) return true;
|
||||
if (element instanceof PyFunction) return true;
|
||||
if ((element instanceof PyTargetExpression) && ((PyTargetExpression)element).getQualifier() == null) {
|
||||
PsiElement e = element.getParent();
|
||||
if (e instanceof PyAssignmentStatement) {
|
||||
e = e.getParent();
|
||||
if (e instanceof PyStatementList) {
|
||||
e = e.getParent();
|
||||
if (e instanceof PyClass) {
|
||||
return true;
|
||||
}
|
||||
vis = Visibility.PRIVATE;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
children[i] = new PyStructureViewElement(element, vis);
|
||||
if (element instanceof PyClass) {
|
||||
PyClass the_exception = PyBuiltinCache.getInstance(element.getProject()).getClass("Exception");
|
||||
final PyClass cls = (PyClass)element;
|
||||
for (PyClass anc : cls.iterateAncestors()) {
|
||||
if (anc == the_exception) {
|
||||
((PyStructureViewElement)(children[i])).setIcon(Icons.EXCEPTION_CLASS_ICON);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
|
||||
public ItemPresentation getPresentation() {
|
||||
return new ItemPresentation() {
|
||||
public String getPresentableText() {
|
||||
if (my_element instanceof PyFunction) {
|
||||
PsiElement[] children = my_element.getChildren();
|
||||
if (children.length > 0 && children [0] instanceof PyParameterList) {
|
||||
PyParameterList argList = (PyParameterList) children [0];
|
||||
StringBuilder result = new StringBuilder(((PsiNamedElement)my_element).getName());
|
||||
// TODO: reuse logic from PyParameterInfoHandler
|
||||
result.append("(");
|
||||
boolean first = true;
|
||||
for(PsiElement e: argList.getChildren()) {
|
||||
if (e instanceof PyParameter) {
|
||||
if (first) {
|
||||
first = false;
|
||||
}
|
||||
else {
|
||||
result.append(",");
|
||||
}
|
||||
PyParameter p = (PyParameter) e;
|
||||
if (p.isPositionalContainer()) {
|
||||
result.append("*");
|
||||
}
|
||||
else if (p.isKeywordContainer()) {
|
||||
result.append("**");
|
||||
}
|
||||
result.append(p.getName());
|
||||
}
|
||||
}
|
||||
result.append(")");
|
||||
return result.toString();
|
||||
}
|
||||
}
|
||||
return ((PsiNamedElement)my_element).getName();
|
||||
}
|
||||
return children;
|
||||
}
|
||||
|
||||
public @Nullable TextAttributesKey getTextAttributesKey() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public @Nullable String getLocationString() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Icon getIcon(boolean open) {
|
||||
Icon normal_icon = my_element.getIcon(Iconable.ICON_FLAG_OPEN);
|
||||
if (my_icon != null) normal_icon = my_icon; // override normal
|
||||
if (my_visibility == Visibility.NORMAL) {
|
||||
return normal_icon;
|
||||
}
|
||||
else {
|
||||
LayeredIcon icon = new LayeredIcon(2);
|
||||
icon.setIcon(normal_icon, 0);
|
||||
Icon overlay = null;
|
||||
if (my_visibility == Visibility.PRIVATE) overlay = PyIcons.PRIVATE;
|
||||
else if (my_visibility == Visibility.PREDEFINED) overlay = PyIcons.PREDEFINED;
|
||||
else if (my_visibility == Visibility.INVISIBLE) overlay = PyIcons.INVISIBLE;
|
||||
if (overlay != null) {
|
||||
icon.setIcon(overlay, 1);
|
||||
}
|
||||
return icon;
|
||||
}
|
||||
}
|
||||
};
|
||||
static boolean isWorthyClassItem(PsiElement element) {
|
||||
if (element instanceof PyClass) return true;
|
||||
if (element instanceof PyFunction) return true;
|
||||
if ((element instanceof PyTargetExpression) && ((PyTargetExpression)element).getQualifier() == null) {
|
||||
PsiElement e = element.getParent();
|
||||
if (e instanceof PyAssignmentStatement) {
|
||||
e = e.getParent();
|
||||
if (e instanceof PyStatementList) {
|
||||
e = e.getParent();
|
||||
if (e instanceof PyClass) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public ItemPresentation getPresentation() {
|
||||
return new ItemPresentation() {
|
||||
public String getPresentableText() {
|
||||
if (my_element instanceof PyFunction) {
|
||||
PsiElement[] children = my_element.getChildren();
|
||||
if (children.length > 0 && children[0] instanceof PyParameterList) {
|
||||
PyParameterList argList = (PyParameterList)children[0];
|
||||
StringBuilder result = new StringBuilder(my_element.getName());
|
||||
ParamHelper.appendParameterList(argList, result);
|
||||
return result.toString();
|
||||
}
|
||||
}
|
||||
return my_element.getName();
|
||||
}
|
||||
|
||||
public
|
||||
@Nullable
|
||||
TextAttributesKey getTextAttributesKey() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public
|
||||
@Nullable
|
||||
String getLocationString() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Icon getIcon(boolean open) {
|
||||
Icon normal_icon = my_element.getIcon(Iconable.ICON_FLAG_OPEN);
|
||||
if (my_icon != null) normal_icon = my_icon; // override normal
|
||||
if (my_visibility == Visibility.NORMAL) {
|
||||
return normal_icon;
|
||||
}
|
||||
else {
|
||||
LayeredIcon icon = new LayeredIcon(2);
|
||||
icon.setIcon(normal_icon, 0);
|
||||
Icon overlay = null;
|
||||
if (my_visibility == Visibility.PRIVATE) {
|
||||
overlay = PyIcons.PRIVATE;
|
||||
}
|
||||
else if (my_visibility == Visibility.PREDEFINED) {
|
||||
overlay = PyIcons.PREDEFINED;
|
||||
}
|
||||
else if (my_visibility == Visibility.INVISIBLE) overlay = PyIcons.INVISIBLE;
|
||||
if (overlay != null) {
|
||||
icon.setIcon(overlay, 1);
|
||||
}
|
||||
return icon;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,40 +16,50 @@
|
||||
|
||||
package com.jetbrains.python.validation;
|
||||
|
||||
import com.intellij.lang.annotation.AnnotationHolder;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.jetbrains.python.PyBundle;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.impl.ParamHelper;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Created by IntelliJ IDEA.
|
||||
* User: yole
|
||||
* Date: 13.06.2005
|
||||
* Time: 15:33:24
|
||||
* To change this template use File | Settings | File Templates.
|
||||
* Annotates errors in 'global' statements.
|
||||
*/
|
||||
public class GlobalAnnotator extends PyAnnotator {
|
||||
@Override public void visitPyGlobalStatement(final PyGlobalStatement node) {
|
||||
PyFunction function = node.getContainingElement(PyFunction.class);
|
||||
if (function != null) {
|
||||
PyParameterList paramList = function.getParameterList();
|
||||
PyParameter[] params = paramList.getParameters();
|
||||
Set<String> paramNames = new HashSet<String>();
|
||||
for (PyParameter param: params) {
|
||||
paramNames.add(param.getName());
|
||||
}
|
||||
for (PyReferenceExpression expr: node.getGlobals()) {
|
||||
if (paramNames.contains(expr.getReferencedName())) {
|
||||
getHolder().createErrorAnnotation(expr.getTextRange(), "name is used as both global and parameter");
|
||||
}
|
||||
PsiElement resolvedElement = expr.resolve();
|
||||
if (resolvedElement != null && PsiTreeUtil.isAncestor(function, resolvedElement, true)) {
|
||||
getHolder().createWarningAnnotation(expr.getTextRange(), "name '" + expr.getReferencedName() +
|
||||
"' is assigned to before global declaration");
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void visitPyGlobalStatement(final PyGlobalStatement node) {
|
||||
PyFunction function = node.getContainingElement(PyFunction.class);
|
||||
if (function != null) {
|
||||
PyParameterList paramList = function.getParameterList();
|
||||
// collect param names
|
||||
final Set<String> paramNames = new HashSet<String>();
|
||||
|
||||
ParamHelper.walkDownParamArray(
|
||||
paramList.getParameters(),
|
||||
new ParamHelper.ParamVisitor() {
|
||||
@Override
|
||||
public void visitNamedParameter(PyNamedParameter param, boolean first, boolean last) {
|
||||
paramNames.add(param.getName());
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// check globals
|
||||
final AnnotationHolder holder = getHolder();
|
||||
for (PyReferenceExpression expr : node.getGlobals()) {
|
||||
final String expr_name = expr.getReferencedName();
|
||||
if (paramNames.contains(expr_name)) {
|
||||
holder.createErrorAnnotation(expr.getTextRange(), PyBundle.message("ANN.$0.both.global.and.param", expr_name));
|
||||
}
|
||||
PsiElement resolvedElement = expr.resolve();
|
||||
if (resolvedElement != null && PsiTreeUtil.isAncestor(function, resolvedElement, true)) {
|
||||
getHolder().createWarningAnnotation(expr.getTextRange(),PyBundle.message("ANN.$0.both.global.and.param", expr_name));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,47 +17,55 @@
|
||||
package com.jetbrains.python.validation;
|
||||
|
||||
import com.intellij.util.containers.HashSet;
|
||||
import com.jetbrains.python.psi.PyParameter;
|
||||
import com.jetbrains.python.PyBundle;
|
||||
import com.jetbrains.python.psi.PyNamedParameter;
|
||||
import com.jetbrains.python.psi.PyParameterList;
|
||||
import com.jetbrains.python.psi.impl.ParamHelper;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Checks for anomalies in parameter lists of function declarations.
|
||||
* User: yole
|
||||
* Date: 12.06.2005
|
||||
*/
|
||||
public class ParameterListAnnotator extends PyAnnotator {
|
||||
@Override public void visitPyParameterList(final PyParameterList node) {
|
||||
HashSet<String> parameterNames = new HashSet<String>();
|
||||
PyParameter[] parameters = node.getParameters();
|
||||
@Override
|
||||
public void visitPyParameterList(final PyParameterList paramlist) {
|
||||
ParamHelper.walkDownParamArray(
|
||||
paramlist.getParameters(),
|
||||
new ParamHelper.ParamVisitor() {
|
||||
Set<String> parameterNames = new HashSet<String>();
|
||||
boolean hadPositionalContainer = false, hadKeywordContainer = false;
|
||||
boolean hadDefaultValue = false;
|
||||
for (PyParameter parameter: parameters) {
|
||||
if (parameterNames.contains(parameter.getName())) {
|
||||
getHolder().createErrorAnnotation(parameter, "duplicate parameter name");
|
||||
@Override
|
||||
public void visitNamedParameter(PyNamedParameter parameter, boolean first, boolean last) {
|
||||
if (parameterNames.contains(parameter.getName())) {
|
||||
getHolder().createErrorAnnotation(parameter, PyBundle.message("ANN.duplicate.param.name"));
|
||||
}
|
||||
parameterNames.add(parameter.getName());
|
||||
if (parameter.isPositionalContainer()) {
|
||||
if (hadKeywordContainer) {
|
||||
getHolder().createErrorAnnotation(parameter, PyBundle.message("ANN.starred.param.after.kwparam"));
|
||||
}
|
||||
parameterNames.add(parameter.getName());
|
||||
if (parameter.isPositionalContainer()) {
|
||||
if (hadKeywordContainer) {
|
||||
getHolder().createErrorAnnotation(parameter, "* parameter after ** paremeter");
|
||||
}
|
||||
hadPositionalContainer = true;
|
||||
hadPositionalContainer = true;
|
||||
}
|
||||
else if (parameter.isKeywordContainer()) {
|
||||
hadKeywordContainer = true;
|
||||
}
|
||||
else {
|
||||
if (hadPositionalContainer || hadKeywordContainer) {
|
||||
getHolder().createErrorAnnotation(parameter, PyBundle.message("ANN.regular.param.after.starred"));
|
||||
}
|
||||
else if (parameter.isKeywordContainer()) {
|
||||
hadKeywordContainer = true;
|
||||
if (parameter.getDefaultValue() != null) {
|
||||
hadDefaultValue = true;
|
||||
}
|
||||
else {
|
||||
if (hadPositionalContainer || hadKeywordContainer) {
|
||||
getHolder().createErrorAnnotation(parameter, "regular parameter after * or ** parameter");
|
||||
}
|
||||
if (parameter.getDefaultValue() != null) {
|
||||
hadDefaultValue = true;
|
||||
}
|
||||
else {
|
||||
if (hadDefaultValue) {
|
||||
getHolder().createErrorAnnotation(parameter, "non-default parameter follows default parameter");
|
||||
}
|
||||
}
|
||||
if (hadDefaultValue) {
|
||||
getHolder().createErrorAnnotation(parameter, PyBundle.message("ANN.non.default.param.after.default"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
# py 2.x only
|
||||
def foo(a, (b, (c, d)), e):
|
||||
pass
|
||||
|
||||
foo(<arg1>1, (<arg2>2, (<arg3>3, <arg4>4)), <arg5>5)
|
||||
@@ -0,0 +1,4 @@
|
||||
def foo(a, b, c):
|
||||
pass
|
||||
|
||||
foo(<arg1>1, <arg2>**{'b':2, <arg2a>'c':3})
|
||||
@@ -0,0 +1,4 @@
|
||||
def foo(a, b, c):
|
||||
pass
|
||||
|
||||
foo(<arg1>b=2, <arg2>**{'a':1, <arg2a>'c':3})
|
||||
@@ -0,0 +1,5 @@
|
||||
def foo(a, b, **c):
|
||||
pass
|
||||
|
||||
foo(<arg1>1, <arg2>2, <arg3>x=3, <arg4>**{'y':4})
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
def foo(a, b, **c):
|
||||
pass
|
||||
|
||||
foo(<arg1>z=1, <arg2>b=2, <arg3>a=3, <arg4>**{'y':4})
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
# py 2.x only
|
||||
def foo(a, (b, c), d):
|
||||
pass
|
||||
|
||||
foo(<arg1>1, (<arg2>2, <arg3>3), <arg4>4)
|
||||
@@ -0,0 +1,5 @@
|
||||
# py 2.x only
|
||||
def foo(a, (b, c), d):
|
||||
pass
|
||||
|
||||
foo(<arg1>1, <arg23>range(2), <arg4>4)
|
||||
@@ -0,0 +1,6 @@
|
||||
class A:
|
||||
def foo(self, a):
|
||||
pass
|
||||
|
||||
a = A()
|
||||
A.foo(<arg1>a, <arg2>1)
|
||||
@@ -0,0 +1,4 @@
|
||||
def foo(a, b, c):
|
||||
pass
|
||||
|
||||
foo(<arg1>1, <arg2>2, <arg3>3)
|
||||
@@ -0,0 +1,6 @@
|
||||
class A:
|
||||
def foo(self, a):
|
||||
pass
|
||||
|
||||
a = A()
|
||||
a.foo(<arg1>1)
|
||||
@@ -0,0 +1,4 @@
|
||||
def foo(a, b, c):
|
||||
pass
|
||||
|
||||
foo(<arg1>1, <arg2>*(2, <arg2a>3))
|
||||
@@ -0,0 +1,4 @@
|
||||
def foo(a, b, *c, **d):
|
||||
pass
|
||||
|
||||
foo(<arg1>1, <arg2>2, <arg3>3, <arg4>4, <arg5>x=1, <arg6>y=2)
|
||||
@@ -0,0 +1,4 @@
|
||||
def foo(a, b, *c):
|
||||
pass
|
||||
|
||||
foo(<arg1>1, <arg2>2, <arg3>3, <arg4>4)
|
||||
@@ -23,7 +23,7 @@ PyFile:ConditionalParenLambda.py
|
||||
PsiElement(Py:LAMBDA_KEYWORD)('lambda')
|
||||
PsiWhiteSpace(' ')
|
||||
PyParameterList
|
||||
PyParameter
|
||||
PyNamedParameter('x')
|
||||
PsiElement(Py:IDENTIFIER)('x')
|
||||
PsiElement(Py:COLON)(':')
|
||||
PsiWhiteSpace(' ')
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
PyFile:DecoratedFunction.py
|
||||
PyFunction
|
||||
PyFunction('foo')
|
||||
PyDecoratorList
|
||||
PyDecorator: @staticmethod
|
||||
PsiElement(Py:AT)('@')
|
||||
|
||||
@@ -4,7 +4,7 @@ PyFile:LambdaConditional.py
|
||||
PsiElement(Py:LAMBDA_KEYWORD)('lambda')
|
||||
PsiWhiteSpace(' ')
|
||||
PyParameterList
|
||||
PyParameter
|
||||
PyNamedParameter('x')
|
||||
PsiElement(Py:IDENTIFIER)('x')
|
||||
PsiElement(Py:COLON)(':')
|
||||
PsiWhiteSpace(' ')
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
PyFile:YieldInAssignment.py
|
||||
PyFunction
|
||||
PyFunction('f')
|
||||
PsiElement(Py:DEF_KEYWORD)('def')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(Py:IDENTIFIER)('f')
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
PyFile:YieldInAugAssignment.py
|
||||
PyFunction
|
||||
PyFunction('g')
|
||||
PsiElement(Py:DEF_KEYWORD)('def')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(Py:IDENTIFIER)('g')
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
PyFile:YieldInParentheses.py
|
||||
PyFunction
|
||||
PyFunction('f')
|
||||
PsiElement(Py:DEF_KEYWORD)('def')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(Py:IDENTIFIER)('f')
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
PyFile:YieldStatement.py
|
||||
PyFunction
|
||||
PyFunction('f')
|
||||
PsiElement(Py:DEF_KEYWORD)('def')
|
||||
PsiWhiteSpace(' ')
|
||||
PsiElement(Py:IDENTIFIER)('f')
|
||||
|
||||
@@ -0,0 +1,398 @@
|
||||
package com.jetbrains.python;
|
||||
|
||||
import com.intellij.lang.parameterInfo.CreateParameterInfoContext;
|
||||
import com.intellij.lang.parameterInfo.ParameterInfoHandler;
|
||||
import com.intellij.lang.parameterInfo.ParameterInfoUIContextEx;
|
||||
import com.intellij.lang.parameterInfo.UpdateParameterInfoContext;
|
||||
import com.intellij.openapi.application.PathManager;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.util.containers.HashSet;
|
||||
import com.jetbrains.python.psi.PyArgumentList;
|
||||
import com.jetbrains.python.psi.PyUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Tests parameter info available via ^P at call sites.
|
||||
* <br/>User: dcheryasov
|
||||
* Date: Jul 14, 2009 3:42:44 AM
|
||||
*/
|
||||
public class PyParameterInfoTest extends MarkedTestCase {
|
||||
protected String getTestDataPath() {
|
||||
return PathManager.getHomePath() + "/plugins/python/testData/paramInfo/";
|
||||
}
|
||||
|
||||
public void testSimpleFunction() throws Exception {
|
||||
Map<String, PsiElement> marks = loadTest();
|
||||
assertEquals("Test data sanity", marks.size(), 3);
|
||||
|
||||
PsiElement arg1 = marks.get("<arg1>");
|
||||
feignCtrlP(arg1.getTextOffset()).check("a,b,c", new String[]{"a,"});
|
||||
feignCtrlP(arg1.getTextOffset()+1).check("a,b,c", new String[]{"a,"});
|
||||
feignCtrlP(arg1.getTextOffset()-3).assertNotFound(); // ^P before arglist gives nothing
|
||||
|
||||
PsiElement arg2 = marks.get("<arg2>");
|
||||
feignCtrlP(arg2.getTextOffset()).check("a,b,c", new String[]{"b,"});
|
||||
feignCtrlP(arg2.getTextOffset()+1).check("a,b,c", new String[]{"b,"});
|
||||
feignCtrlP(arg2.getTextOffset()+2).check("a,b,c", new String[]{"c"}); // one too far after arg2, and we came to arg3
|
||||
|
||||
PsiElement arg3 = marks.get("<arg3>");
|
||||
feignCtrlP(arg3.getTextOffset()).check("a,b,c", new String[]{"c"});
|
||||
feignCtrlP(arg3.getTextOffset()+1).check("a,b,c", new String[]{"c"});
|
||||
feignCtrlP(arg3.getTextOffset()-1).check("a,b,c", new String[]{"c"}); // space before arg goes to that arg
|
||||
feignCtrlP(arg3.getTextOffset()+2).assertNotFound(); // ^P on a ")" gives nothing
|
||||
}
|
||||
|
||||
public void testStarredFunction() throws Exception {
|
||||
Map<String, PsiElement> marks = loadTest();
|
||||
assertEquals("Test data sanity", marks.size(), 4);
|
||||
|
||||
PsiElement arg1 = marks.get("<arg1>");
|
||||
feignCtrlP(arg1.getTextOffset()).check("a,b,*c", new String[]{"a,"});
|
||||
feignCtrlP(arg1.getTextOffset()+1).check("a,b,*c", new String[]{"a,"});
|
||||
|
||||
PsiElement arg2 = marks.get("<arg2>");
|
||||
feignCtrlP(arg2.getTextOffset()).check("a,b,*c", new String[]{"b,"});
|
||||
feignCtrlP(arg2.getTextOffset()+1).check("a,b,*c", new String[]{"b,"});
|
||||
|
||||
PsiElement arg3 = marks.get("<arg3>");
|
||||
feignCtrlP(arg3.getTextOffset()).check("a,b,*c", new String[]{"*c"});
|
||||
feignCtrlP(arg3.getTextOffset()+1).check("a,b,*c", new String[]{"*c"});
|
||||
|
||||
PsiElement arg4 = marks.get("<arg4>");
|
||||
feignCtrlP(arg4.getTextOffset()).check("a,b,*c", new String[]{"*c"});
|
||||
feignCtrlP(arg4.getTextOffset()+1).check("a,b,*c", new String[]{"*c"});
|
||||
feignCtrlP(arg4.getTextOffset()+2).assertNotFound();
|
||||
}
|
||||
|
||||
public void testKwdFunction() throws Exception {
|
||||
Map<String, PsiElement> marks = loadTest();
|
||||
assertEquals("Test data sanity", marks.size(), 4);
|
||||
|
||||
PsiElement arg1 = marks.get("<arg1>");
|
||||
feignCtrlP(arg1.getTextOffset()).check("a,b,**c", new String[]{"a,"});
|
||||
feignCtrlP(arg1.getTextOffset()+1).check("a,b,**c", new String[]{"a,"});
|
||||
|
||||
PsiElement arg2 = marks.get("<arg2>");
|
||||
feignCtrlP(arg2.getTextOffset()).check("a,b,**c", new String[]{"b,"});
|
||||
feignCtrlP(arg2.getTextOffset()+1).check("a,b,**c", new String[]{"b,"});
|
||||
|
||||
|
||||
PsiElement arg3 = marks.get("<arg3>");
|
||||
feignCtrlP(arg3.getTextOffset()).check("a,b,**c", new String[]{"**c"});
|
||||
feignCtrlP(arg3.getTextOffset()+1).check("a,b,**c", new String[]{"**c"});
|
||||
|
||||
PsiElement arg4 = marks.get("<arg4>");
|
||||
feignCtrlP(arg4.getTextOffset()).check("a,b,**c", new String[]{"**c"});
|
||||
feignCtrlP(arg4.getTextOffset()+1).check("a,b,**c", new String[]{"**c"});
|
||||
}
|
||||
|
||||
public void testKwdOutOfOrder() throws Exception {
|
||||
Map<String, PsiElement> marks = loadTest();
|
||||
assertEquals("Test data sanity", marks.size(), 4);
|
||||
|
||||
feignCtrlP(marks.get("<arg1>").getTextOffset()).check("a,b,**c", new String[]{"**c"});
|
||||
|
||||
feignCtrlP(marks.get("<arg2>").getTextOffset()).check("a,b,**c", new String[]{"b,"});
|
||||
|
||||
feignCtrlP(marks.get("<arg3>").getTextOffset()).check("a,b,**c", new String[]{"a,"});
|
||||
|
||||
feignCtrlP(marks.get("<arg1>").getTextOffset()).check("a,b,**c", new String[]{"**c"});
|
||||
}
|
||||
|
||||
public void testStarArg() throws Exception {
|
||||
Map<String, PsiElement> marks = loadTest();
|
||||
assertEquals("Test data sanity", marks.size(), 3);
|
||||
|
||||
feignCtrlP(marks.get("<arg1>").getTextOffset()).check("a,b,c", new String[]{"a,"});
|
||||
feignCtrlP(marks.get("<arg2>").getTextOffset()).check("a,b,c", new String[]{"b,","c"});
|
||||
feignCtrlP(marks.get("<arg2a>").getTextOffset()).check("a,b,c", new String[]{"b,","c"});
|
||||
}
|
||||
|
||||
public void testKwdArg() throws Exception {
|
||||
Map<String, PsiElement> marks = loadTest();
|
||||
assertEquals("Test data sanity", marks.size(), 3);
|
||||
|
||||
feignCtrlP(marks.get("<arg1>").getTextOffset()).check("a,b,c", new String[]{"a,"});
|
||||
feignCtrlP(marks.get("<arg2>").getTextOffset()).check("a,b,c", new String[]{"b,","c"});
|
||||
feignCtrlP(marks.get("<arg2a>").getTextOffset()).check("a,b,c", new String[]{"b,","c"});
|
||||
}
|
||||
|
||||
public void testKwdArgOutOfOrder() throws Exception {
|
||||
Map<String, PsiElement> marks = loadTest();
|
||||
assertEquals("Test data sanity", marks.size(), 3);
|
||||
|
||||
feignCtrlP(marks.get("<arg1>").getTextOffset()).check("a,b,c", new String[]{"b,"});
|
||||
feignCtrlP(marks.get("<arg2>").getTextOffset()).check("a,b,c", new String[]{"a,","c"});
|
||||
feignCtrlP(marks.get("<arg2a>").getTextOffset()).check("a,b,c", new String[]{"a,","c"});
|
||||
}
|
||||
|
||||
public void testStarredAndKwdFunction() throws Exception {
|
||||
Map<String, PsiElement> marks = loadTest();
|
||||
assertEquals("Test data sanity", marks.size(), 6);
|
||||
|
||||
feignCtrlP(marks.get("<arg1>").getTextOffset()).check("a,b,*c,**d", new String[]{"a,"});
|
||||
feignCtrlP(marks.get("<arg2>").getTextOffset()).check("a,b,*c,**d", new String[]{"b,"});
|
||||
feignCtrlP(marks.get("<arg3>").getTextOffset()).check("a,b,*c,**d", new String[]{"*c,"});
|
||||
feignCtrlP(marks.get("<arg4>").getTextOffset()).check("a,b,*c,**d", new String[]{"*c,"});
|
||||
feignCtrlP(marks.get("<arg5>").getTextOffset()).check("a,b,*c,**d", new String[]{"**d"});
|
||||
feignCtrlP(marks.get("<arg6>").getTextOffset()).check("a,b,*c,**d", new String[]{"**d"});
|
||||
}
|
||||
|
||||
public void testNestedArg() throws Exception {
|
||||
Map<String, PsiElement> marks = loadTest();
|
||||
assertEquals("Test data sanity", marks.size(), 4);
|
||||
|
||||
feignCtrlP(marks.get("<arg1>").getTextOffset()).check("a,(b,c),d", new String[]{"a,"});
|
||||
feignCtrlP(marks.get("<arg2>").getTextOffset()).check("a,(b,c),d", new String[]{"b,"});
|
||||
feignCtrlP(marks.get("<arg3>").getTextOffset()).check("a,(b,c),d", new String[]{"c"});
|
||||
feignCtrlP(marks.get("<arg4>").getTextOffset()).check("a,(b,c),d", new String[]{"d"});
|
||||
|
||||
feignCtrlP(marks.get("<arg2>").getTextOffset()-2).check("a,(b,c),d", new String[]{}); // before nested tuple: no arg matches
|
||||
}
|
||||
|
||||
public void testDoubleNestedArg() throws Exception {
|
||||
Map<String, PsiElement> marks = loadTest();
|
||||
assertEquals("Test data sanity", marks.size(), 5);
|
||||
|
||||
feignCtrlP(marks.get("<arg1>").getTextOffset()).check("a,(b,(c,d)),e", new String[]{"a,"});
|
||||
feignCtrlP(marks.get("<arg2>").getTextOffset()).check("a,(b,(c,d)),e", new String[]{"b,"});
|
||||
feignCtrlP(marks.get("<arg3>").getTextOffset()).check("a,(b,(c,d)),e", new String[]{"c,"});
|
||||
feignCtrlP(marks.get("<arg4>").getTextOffset()).check("a,(b,(c,d)),e", new String[]{"d"});
|
||||
feignCtrlP(marks.get("<arg5>").getTextOffset()).check("a,(b,(c,d)),e", new String[]{"e"});
|
||||
}
|
||||
|
||||
public void testNestedMultiArg() throws Exception {
|
||||
Map<String, PsiElement> marks = loadTest();
|
||||
assertEquals("Test data sanity", marks.size(), 3);
|
||||
|
||||
feignCtrlP(marks.get("<arg1>").getTextOffset()).check("a,(b,c),d", new String[]{"a,"});
|
||||
feignCtrlP(marks.get("<arg23>").getTextOffset()).check("a,(b,c),d", new String[]{"b,","c"});
|
||||
feignCtrlP(marks.get("<arg4>").getTextOffset()).check("a,(b,c),d", new String[]{"d"});
|
||||
}
|
||||
|
||||
public void testStarredParam() throws Exception {
|
||||
Map<String, PsiElement> marks = loadTest();
|
||||
assertEquals("Test data sanity", marks.size(), 2);
|
||||
|
||||
feignCtrlP(marks.get("<arg1>").getTextOffset()).check("a,b,c", new String[]{"a,"});
|
||||
feignCtrlP(marks.get("<arg23>").getTextOffset()).check("a,b,c", new String[]{"b,","c"});
|
||||
}
|
||||
|
||||
public void testStarredParamAndArg() throws Exception {
|
||||
Map<String, PsiElement> marks = loadTest();
|
||||
assertEquals("Test data sanity", marks.size(), 4);
|
||||
|
||||
feignCtrlP(marks.get("<arg1>").getTextOffset()).check("a,b,*c", new String[]{"a,"});
|
||||
feignCtrlP(marks.get("<arg2>").getTextOffset()).check("a,b,*c", new String[]{"b,"});
|
||||
feignCtrlP(marks.get("<arg3>").getTextOffset()).check("a,b,*c", new String[]{"*c"});
|
||||
feignCtrlP(marks.get("<arg4>").getTextOffset()).check("a,b,*c", new String[]{"*c"});
|
||||
}
|
||||
|
||||
|
||||
public void testSimpleMethod() throws Exception {
|
||||
Map<String, PsiElement> marks = loadTest();
|
||||
assertEquals("Test data sanity", marks.size(), 1);
|
||||
|
||||
feignCtrlP(marks.get("<arg1>").getTextOffset()).check("self,a", new String[]{"a"}, new String[]{"self,"});
|
||||
}
|
||||
|
||||
public void testSimpleClassFunction() throws Exception {
|
||||
Map<String, PsiElement> marks = loadTest();
|
||||
assertEquals("Test data sanity", marks.size(), 2);
|
||||
|
||||
feignCtrlP(marks.get("<arg1>").getTextOffset()).check("self,a", new String[]{"self,"});
|
||||
feignCtrlP(marks.get("<arg2>").getTextOffset()).check("self,a", new String[]{"a"});
|
||||
}
|
||||
|
||||
// TODO: add method tests with decorators when a mock SDK is available
|
||||
|
||||
/**
|
||||
* Imitates pressing of Ctrl+P; fails if results are not as expected.
|
||||
* @param offset offset of 'cursor' where ^P is pressed.
|
||||
* @return a {@link Collector} with collected hint info.
|
||||
* @throws Exception if it fails
|
||||
*/
|
||||
private Collector feignCtrlP(int offset) throws Exception {
|
||||
Collector collector = new Collector(getProject(), getFile(), offset);
|
||||
PyParameterInfoHandler handler = new PyParameterInfoHandler();
|
||||
collector.setParameterOwner(handler.findElementForParameterInfo(collector)); // finds arglist, sets items to show
|
||||
if (collector.getParameterOwner() != null) {
|
||||
assertEquals("Collected one analysis result", 1, collector.myItems.length);
|
||||
handler.updateParameterInfo((PyArgumentList)collector.getParameterOwner(), collector); // moves offset to correct parameter
|
||||
handler.updateUI((PyArgumentList.AnalysisResult)collector.getItemsToShow()[0], collector); // sets hint text and flags
|
||||
}
|
||||
return collector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Imitates the normal UI contexts to the extent we use it. Collects highlighting.
|
||||
*/
|
||||
private static class Collector implements ParameterInfoUIContextEx, CreateParameterInfoContext, UpdateParameterInfoContext {
|
||||
|
||||
private PsiFile myFile;
|
||||
private int myOffset;
|
||||
private int myIndex;
|
||||
private Object[] myItems;
|
||||
private Project myProject;
|
||||
private Editor myEditor;
|
||||
private PyArgumentList myParamOwner;
|
||||
private String[] myTexts;
|
||||
private EnumSet<Flag>[] myFlags;
|
||||
|
||||
private Collector(Project project, PsiFile file, int offset) {
|
||||
myProject = project;
|
||||
myEditor = null;
|
||||
myFile = file;
|
||||
myOffset = offset;
|
||||
}
|
||||
|
||||
public void setupUIComponentPresentation(String[] texts, EnumSet<Flag>[] flags, Color background) {
|
||||
assert texts.length == flags.length;
|
||||
myTexts = texts;
|
||||
myFlags = flags;
|
||||
}
|
||||
|
||||
public void setupUIComponentPresentation(String text, int highlightStartOffset, int highlightEndOffset, boolean isDisabled,
|
||||
boolean strikeout, boolean isDisabledBeforeHighlight, Color background) {
|
||||
// nothing, we don't use it
|
||||
}
|
||||
|
||||
public boolean isUIComponentEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isUIComponentEnabled(int index) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setUIComponentEnabled(boolean enabled) { }
|
||||
|
||||
public void setUIComponentEnabled(int index, boolean b) { }
|
||||
|
||||
public int getCurrentParameterIndex() {
|
||||
return myIndex;
|
||||
}
|
||||
|
||||
public void removeHint() { }
|
||||
|
||||
public void setParameterOwner(PsiElement o) {
|
||||
assertTrue("Found element is a python arglist", o == null || o instanceof PyArgumentList);
|
||||
myParamOwner = (PyArgumentList)o;
|
||||
}
|
||||
|
||||
public PsiElement getParameterOwner() {
|
||||
return myParamOwner;
|
||||
}
|
||||
|
||||
public void setHighlightedParameter(Object parameter) {
|
||||
// nothing, we don't use it
|
||||
}
|
||||
|
||||
public void setCurrentParameter(int index) {
|
||||
myIndex = index;
|
||||
}
|
||||
|
||||
public Color getDefaultParameterColor() {
|
||||
return java.awt.Color.BLACK;
|
||||
}
|
||||
|
||||
public Object[] getItemsToShow() {
|
||||
return myItems;
|
||||
}
|
||||
|
||||
public void setItemsToShow(Object[] items) {
|
||||
myItems = items;
|
||||
}
|
||||
|
||||
public void showHint(PsiElement element, int offset, ParameterInfoHandler handler) { }
|
||||
|
||||
public int getParameterListStart() {
|
||||
return 0; // we don't use it
|
||||
}
|
||||
|
||||
public Object[] getObjectsToView() {
|
||||
return null; // we don't use it
|
||||
}
|
||||
|
||||
public PsiElement getHighlightedElement() {
|
||||
return null; // we don't use it
|
||||
}
|
||||
|
||||
public void setHighlightedElement(PsiElement elements) {
|
||||
// nothing, we don't use it
|
||||
}
|
||||
|
||||
public Project getProject() {
|
||||
return myProject;
|
||||
}
|
||||
|
||||
public PsiFile getFile() {
|
||||
return myFile;
|
||||
}
|
||||
|
||||
public int getOffset() {
|
||||
return myOffset;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Editor getEditor() {
|
||||
return myEditor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if hint data look as expected.
|
||||
* @param text expected text of the hint, without formatting
|
||||
* @param highlighted expected highlighted substrings of hint
|
||||
* @param disabled expected disabled substrings of hint
|
||||
*/
|
||||
public void check(String text, String[] highlighted, String[] disabled) {
|
||||
assertEquals("Signature", text, PyUtil.joinSubarray(myTexts, 0, 1000, "", new StringBuilder()).toString());
|
||||
StringBuilder wrongs = new StringBuilder();
|
||||
// see if highlighted matches
|
||||
Set<String> highlight_set = new HashSet<String>();
|
||||
highlight_set.addAll(Arrays.asList(highlighted));
|
||||
for (int i=0; i < myTexts.length; i += 1) {
|
||||
if (myFlags[i].contains(Flag.HIGHLIGHT) && !highlight_set.contains(myTexts[i])) {
|
||||
wrongs.append("Highlighted unexpected '").append(myTexts[i]).append("'. ");
|
||||
}
|
||||
}
|
||||
for (int i=0; i < myTexts.length; i += 1) {
|
||||
if (!myFlags[i].contains(Flag.HIGHLIGHT) && highlight_set.contains(myTexts[i])) {
|
||||
wrongs.append("Not highlighted expected '").append(myTexts[i]).append("'. ");
|
||||
}
|
||||
}
|
||||
// see if disabled matches
|
||||
Set<String> disabled_set = new HashSet<String>();
|
||||
disabled_set.addAll(Arrays.asList(disabled));
|
||||
for (int i=0; i < myTexts.length; i += 1) {
|
||||
if (myFlags[i].contains(Flag.DISABLE) && !disabled_set.contains(myTexts[i])) {
|
||||
wrongs.append("Highlighted unexpected '").append(myTexts[i]).append("'. ");
|
||||
}
|
||||
}
|
||||
for (int i=0; i < myTexts.length; i += 1) {
|
||||
if (!myFlags[i].contains(Flag.DISABLE) && disabled_set.contains(myTexts[i])) {
|
||||
wrongs.append("Not disabled expected '").append(myTexts[i]).append("'. ");
|
||||
}
|
||||
}
|
||||
//
|
||||
if (wrongs.length() > 0) fail(wrongs.toString());
|
||||
}
|
||||
|
||||
public void check(String text, String[] highlighted) {
|
||||
check(text, highlighted, new String[0]);
|
||||
}
|
||||
|
||||
public void assertNotFound() {
|
||||
assertNull(myParamOwner);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -136,7 +136,7 @@ public class PyResolveTest extends ResolveTestCase {
|
||||
|
||||
public void testLambda() throws Exception {
|
||||
PsiElement targetElement = resolve();
|
||||
assertTrue(targetElement instanceof PyParameter);
|
||||
assertTrue(targetElement instanceof PyNamedParameter);
|
||||
}
|
||||
|
||||
public void testSuperField() throws Exception {
|
||||
|
||||
Reference in New Issue
Block a user