mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Merge branch 'master' of git.labs.intellij.net:idea/ultimate
This commit is contained in:
@@ -53,8 +53,6 @@
|
||||
|
||||
<applicationService serviceInterface="com.jetbrains.python.JythonManager"
|
||||
serviceImplementation="com.jetbrains.python.JythonManager"/>
|
||||
<projectService serviceInterface="com.jetbrains.python.psi.impl.PyBuiltinCache"
|
||||
serviceImplementation="com.jetbrains.python.psi.impl.PyBuiltinCache"/>
|
||||
|
||||
<referenceImporter implementation="com.jetbrains.python.validation.PythonReferenceImporter"/>
|
||||
<codeInsight.lineMarkerProvider language="Python" implementationClass="com.jetbrains.python.codeInsight.PyLineMarkerProvider"/>
|
||||
|
||||
+2
-2
@@ -13,8 +13,8 @@ import org.jetbrains.annotations.Nullable;
|
||||
* User: dcheryasov
|
||||
* Date: Jun 7, 2009 5:06:12 AM
|
||||
*/
|
||||
public class PythonDosStringFinder {
|
||||
private PythonDosStringFinder() {}
|
||||
public class PythonDocStringFinder {
|
||||
private PythonDocStringFinder() {}
|
||||
|
||||
/**
|
||||
* Looks for a doc string under given parent.
|
||||
@@ -166,7 +166,43 @@ public class PythonDocumentationProvider extends QuickDocumentationProvider {
|
||||
|
||||
cat.add(prolog_cat).addWith(TagCode, doc_cat).add(epilog_cat); // pre-assemble; then add stuff to individual cats as needed
|
||||
cat = wrapInTag("html", wrapInTag("body", cat));
|
||||
element = resolveToDocStringOwner(element, originalElement, prolog_cat);
|
||||
|
||||
// now element may contain a doc string
|
||||
if (element instanceof PyDocStringOwner) {
|
||||
String docString = null;
|
||||
PyStringLiteralExpression doc_expr = ((PyDocStringOwner) element).getDocStringExpression();
|
||||
if (doc_expr != null) docString = doc_expr.getStringValue();
|
||||
// doc of what?
|
||||
if (element instanceof PyClass) {
|
||||
PyClass cls = (PyClass)element;
|
||||
doc_cat.addWith(TagSmall, describeClass(cls, TagBold));
|
||||
}
|
||||
else if (element instanceof PyFunction) {
|
||||
PyFunction fun = (PyFunction)element;
|
||||
PyClass cls = fun.getContainingClass();
|
||||
if (cls != null) doc_cat.addWith(TagSmall, $("class ", cls.getName(), BR));
|
||||
doc_cat.add(describeFunction(fun, TagItalic, BR, TagBold, LCombUp));
|
||||
if (docString == null) {
|
||||
addInheritedDocString(fun, cls, doc_cat, epilog_cat);
|
||||
}
|
||||
}
|
||||
else if (element instanceof PyFile) {
|
||||
// what to prepend to a module description??
|
||||
}
|
||||
else { // not a func, not a class
|
||||
doc_cat.add(combUp(PyUtil.getReadableRepr(element, false)));
|
||||
}
|
||||
if (docString != null) {
|
||||
doc_cat.add(BR).add(combUpDocString(docString));
|
||||
}
|
||||
else if (prolog_cat.isEmpty() && doc_cat.isEmpty() && epilog_cat.isEmpty()) return null; // got nothing to say!
|
||||
return cat.toString();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static PsiElement resolveToDocStringOwner(PsiElement element, PsiElement originalElement, ChainIterable<String> prolog_cat) {
|
||||
// here the ^Q target is already resolved; the resolved element may point to intermediate assignments
|
||||
boolean reassignment_marked = false;
|
||||
if (element instanceof PyTargetExpression) {
|
||||
@@ -199,84 +235,66 @@ public class PythonDocumentationProvider extends QuickDocumentationProvider {
|
||||
}
|
||||
|
||||
}
|
||||
// now element may contain a doc string
|
||||
if (element instanceof PyDocStringOwner) {
|
||||
String docString = null;
|
||||
PyStringLiteralExpression doc_expr = ((PyDocStringOwner) element).getDocStringExpression();
|
||||
if (doc_expr != null) docString = doc_expr.getStringValue();
|
||||
// doc of what?
|
||||
if (element instanceof PyClass) {
|
||||
PyClass cls = (PyClass)element;
|
||||
doc_cat.addWith(TagSmall, describeClass(cls, TagBold));
|
||||
if (element instanceof PyFunction && PyNames.INIT.equals(((PyFunction)element).getName())) {
|
||||
final PyStringLiteralExpression expression = ((PyFunction)element).getDocStringExpression();
|
||||
if (expression == null) {
|
||||
PyClass containingClass = ((PyFunction) element).getContainingClass();
|
||||
if (containingClass != null) {
|
||||
element = containingClass;
|
||||
}
|
||||
}
|
||||
else if (element instanceof PyFunction) {
|
||||
PyFunction fun = (PyFunction)element;
|
||||
PyClass cls = fun.getContainingClass();
|
||||
if (cls != null) doc_cat.addWith(TagSmall, $("class ", cls.getName(), BR));
|
||||
doc_cat.add(describeFunction(fun, TagItalic, BR, TagBold, LCombUp));
|
||||
boolean not_found = true;
|
||||
if (docString == null) {
|
||||
String meth_name = fun.getName();
|
||||
if (cls != null && meth_name != null ) {
|
||||
// look for inherited and its doc
|
||||
for (PyClass ancestor : cls.iterateAncestors()) {
|
||||
PyFunction inherited = ancestor.findMethodByName(meth_name, false);
|
||||
if (inherited != null) {
|
||||
PyStringLiteralExpression doc_elt = inherited.getDocStringExpression();
|
||||
if (doc_elt != null) {
|
||||
String inherited_doc = doc_elt.getStringValue();
|
||||
if (inherited_doc.length() > 1) {
|
||||
epilog_cat
|
||||
.add(BR).add(BR)
|
||||
.add(PyBundle.message("QDOC.copied.from.$0.$1", ancestor.getName(), meth_name))
|
||||
.add(BR).add(BR)
|
||||
.addWith(TagCode, $(inherited_doc))
|
||||
;
|
||||
not_found = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
if (not_found) {
|
||||
// above could have not worked because inheritance is not searched down to 'object'.
|
||||
// for well-known methods, copy built-in doc string.
|
||||
// TODO: also handle predefined __xxx__ that are not part of 'object'.
|
||||
if (PyNames.UnderscoredAttributes.contains(meth_name)) {
|
||||
PyClassType objtype = PyBuiltinCache.getInstance(fun).getObjectType(); // old- and new-style classes share the __xxx__ stuff
|
||||
if (objtype != null) {
|
||||
PyClass objcls = objtype.getPyClass();
|
||||
if (objcls != null) {
|
||||
PyFunction obj_underscored = objcls.findMethodByName(meth_name, false);
|
||||
if (obj_underscored != null) {
|
||||
PyStringLiteralExpression predefined_doc_expr = obj_underscored.getDocStringExpression();
|
||||
String predefined_doc = predefined_doc_expr != null? predefined_doc_expr.getStringValue() : null;
|
||||
if (predefined_doc != null && predefined_doc.length() > 1) { // only a real-looking doc string counts
|
||||
doc_cat.add(predefined_doc);
|
||||
epilog_cat.add(BR).add(BR).add(PyBundle.message("QDOC.copied.from.builtin"));
|
||||
}
|
||||
}
|
||||
}
|
||||
private static void addInheritedDocString(PyFunction fun, PyClass cls, ChainIterable<String> doc_cat, ChainIterable<String> epilog_cat) {
|
||||
boolean not_found = true;
|
||||
String meth_name = fun.getName();
|
||||
if (cls != null && meth_name != null ) {
|
||||
// look for inherited and its doc
|
||||
for (PyClass ancestor : cls.iterateAncestors()) {
|
||||
PyFunction inherited = ancestor.findMethodByName(meth_name, false);
|
||||
if (inherited != null) {
|
||||
PyStringLiteralExpression doc_elt = inherited.getDocStringExpression();
|
||||
if (doc_elt != null) {
|
||||
String inherited_doc = doc_elt.getStringValue();
|
||||
if (inherited_doc.length() > 1) {
|
||||
epilog_cat
|
||||
.add(BR).add(BR)
|
||||
.add(PyBundle.message("QDOC.copied.from.$0.$1", ancestor.getName(), meth_name))
|
||||
.add(BR).add(BR)
|
||||
.addWith(TagCode, $(inherited_doc))
|
||||
;
|
||||
not_found = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (not_found) {
|
||||
// above could have not worked because inheritance is not searched down to 'object'.
|
||||
// for well-known methods, copy built-in doc string.
|
||||
// TODO: also handle predefined __xxx__ that are not part of 'object'.
|
||||
if (PyNames.UnderscoredAttributes.contains(meth_name)) {
|
||||
PyClassType objtype = PyBuiltinCache.getInstance(fun).getObjectType(); // old- and new-style classes share the __xxx__ stuff
|
||||
if (objtype != null) {
|
||||
PyClass objcls = objtype.getPyClass();
|
||||
if (objcls != null) {
|
||||
PyFunction obj_underscored = objcls.findMethodByName(meth_name, false);
|
||||
if (obj_underscored != null) {
|
||||
PyStringLiteralExpression predefined_doc_expr = obj_underscored.getDocStringExpression();
|
||||
String predefined_doc = predefined_doc_expr != null? predefined_doc_expr.getStringValue() : null;
|
||||
if (predefined_doc != null && predefined_doc.length() > 1) { // only a real-looking doc string counts
|
||||
doc_cat.add(predefined_doc);
|
||||
epilog_cat.add(BR).add(BR).add(PyBundle.message("QDOC.copied.from.builtin"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (element instanceof PyFile) {
|
||||
// what to prepend to a module description??
|
||||
}
|
||||
else { // not a func, not a class
|
||||
doc_cat.add(combUp(PyUtil.getReadableRepr(element, false)));
|
||||
}
|
||||
if (docString != null) {
|
||||
doc_cat.add(BR).add(combUpDocString(docString));
|
||||
}
|
||||
else if (prolog_cat.isEmpty() && doc_cat.isEmpty() && epilog_cat.isEmpty()) return null; // got nothing to say!
|
||||
return cat.toString();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static FP.Lambda1<String, String> LCombUp = new FP.Lambda1<String, String>() {
|
||||
@@ -332,22 +350,6 @@ public class PythonDocumentationProvider extends QuickDocumentationProvider {
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
private static StringBuilder join(String delimiter, Iterable list, StringBuilder cat) {
|
||||
boolean is_next = false;
|
||||
for (Object item : list) {
|
||||
if (is_next) cat.append(delimiter);
|
||||
else is_next = true;
|
||||
cat.append(item.toString());
|
||||
}
|
||||
return cat;
|
||||
}
|
||||
|
||||
private static String join(String delimiter, List list) {
|
||||
return join(delimiter, list, new StringBuilder()).toString();
|
||||
}
|
||||
*/
|
||||
|
||||
private static <T> Iterable<T> interleave(Iterable<T> source, T filler) {
|
||||
List<T> ret = new LinkedList<T>();
|
||||
boolean is_next = false;
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.MessageType;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiWhiteSpace;
|
||||
import com.intellij.util.Function;
|
||||
import com.jetbrains.python.PyBundle;
|
||||
import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.PythonLanguage;
|
||||
@@ -39,7 +40,7 @@ public class AddFieldQuickFix implements LocalQuickFix {
|
||||
return PyBundle.message("INSP.GROUP.python");
|
||||
}
|
||||
|
||||
private static PsiElement appendToInit(PyFunction init, String item_name, PyElementGenerator generator, Project project) {
|
||||
private static PsiElement appendToInit(PyFunction init, Function<String, PyStatement> callback) {
|
||||
// add this field as the last stmt of the constructor
|
||||
final PyStatementList stmt_list = init.getStatementList();
|
||||
PyStatement[] stmts = stmt_list.getStatements(); // NOTE: rather wasteful, consider iterable stmt list
|
||||
@@ -51,7 +52,7 @@ public class AddFieldQuickFix implements LocalQuickFix {
|
||||
if (params.length > 0) {
|
||||
self_name = params[0].getName();
|
||||
}
|
||||
PyStatement new_stmt = generator.createFromText(project, PyStatement.class, self_name + "." +item_name + " = None");
|
||||
PyStatement new_stmt = callback.fun(self_name);
|
||||
PyUtil.ensureWritable(stmt_list);
|
||||
return stmt_list.addAfter(new_stmt, last_stmt);
|
||||
}
|
||||
@@ -60,15 +61,24 @@ public class AddFieldQuickFix implements LocalQuickFix {
|
||||
// expect the descriptor to point to the unresolved identifier.
|
||||
PyClass cls = myQualifierClass;
|
||||
String item_name = myIdentifier;
|
||||
if (cls != null) {
|
||||
if (addFieldToInit(project, cls, item_name, new CreateFieldCallback(project, item_name))) return;
|
||||
}
|
||||
// somehow we failed. tell about this
|
||||
PyUtil.showBalloon(project, PyBundle.message("QFIX.failed.to.add.field"), MessageType.ERROR);
|
||||
}
|
||||
|
||||
public static boolean addFieldToInit(Project project, PyClass cls, String item_name, FieldCallback callback) {
|
||||
if (cls != null && item_name != null) {
|
||||
PyFunction init = cls.findMethodByName(PyNames.INIT, false);
|
||||
Language language = cls.getLanguage();
|
||||
if (language instanceof PythonLanguage) {
|
||||
PythonLanguage pythonLanguage = (PythonLanguage)language;
|
||||
PyElementGenerator generator = pythonLanguage.getElementGenerator();
|
||||
callback.setGenerator(generator);
|
||||
if (init != null) {
|
||||
appendToInit(init, item_name, generator, project);
|
||||
return;
|
||||
appendToInit(init, callback);
|
||||
return true;
|
||||
}
|
||||
else { // no init! boldly copy ancestor's.
|
||||
for (PyClass ancestor : cls.iterateAncestors()) {
|
||||
@@ -77,7 +87,7 @@ public class AddFieldQuickFix implements LocalQuickFix {
|
||||
}
|
||||
PyFunction new_init = createInitMethod(project, cls, init, generator);
|
||||
|
||||
appendToInit(new_init, item_name, generator, project);
|
||||
appendToInit(new_init, callback);
|
||||
new_init.add(generator.createFromText(project, PsiWhiteSpace.class, "\n\n")); // after the last line
|
||||
|
||||
PsiElement add_anchor = null;
|
||||
@@ -87,13 +97,12 @@ public class AddFieldQuickFix implements LocalQuickFix {
|
||||
cls_content.addAfter(new_init, add_anchor);
|
||||
|
||||
PyUtil.showBalloon(project, PyBundle.message("QFIX.added.constructor.$0.for.field.$1", cls.getName(), item_name), MessageType.INFO);
|
||||
return;
|
||||
return true;
|
||||
//else // well, that can't be
|
||||
}
|
||||
}
|
||||
}
|
||||
// somehow we failed. tell about this
|
||||
PyUtil.showBalloon(project, PyBundle.message("QFIX.failed.to.add.field"), MessageType.ERROR);
|
||||
return false;
|
||||
}
|
||||
|
||||
private static PyFunction createInitMethod(Project project, PyClass cls, @Nullable PyFunction ancestorInit, PyElementGenerator generator) {
|
||||
@@ -125,4 +134,27 @@ public class AddFieldQuickFix implements LocalQuickFix {
|
||||
}
|
||||
return new_init;
|
||||
}
|
||||
|
||||
public abstract static class FieldCallback implements Function<String, PyStatement> {
|
||||
protected PyElementGenerator myGenerator;
|
||||
|
||||
public void setGenerator(PyElementGenerator generator) {
|
||||
myGenerator = generator;
|
||||
}
|
||||
}
|
||||
|
||||
private static class CreateFieldCallback extends FieldCallback {
|
||||
private Project myProject;
|
||||
private String myItemName;
|
||||
|
||||
|
||||
private CreateFieldCallback(Project project, String itemName) {
|
||||
myProject = project;
|
||||
myItemName = itemName;
|
||||
}
|
||||
|
||||
public PyStatement fun(String self_name) {
|
||||
return myGenerator.createFromText(myProject, PyStatement.class, self_name + "." + myItemName + " = None");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.PsiWhiteSpace;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.jetbrains.python.PythonDosStringFinder;
|
||||
import com.jetbrains.python.PythonDocStringFinder;
|
||||
import com.jetbrains.python.PythonLanguage;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -40,7 +40,7 @@ class AddImportHelper {
|
||||
}
|
||||
// maybe we arrived at the doc comment stmt; skip over it, too
|
||||
else if (!skipped_over_imports && ! skipped_over_doc && file instanceof PyFile) {
|
||||
PsiElement doc_elt = PythonDosStringFinder.find((PyElement)file); // this gives the literal; its parent is the expr seeker may have encountered
|
||||
PsiElement doc_elt = PythonDocStringFinder.find((PyElement)file); // this gives the literal; its parent is the expr seeker may have encountered
|
||||
if (doc_elt != null && doc_elt.getParent() == seeker) {
|
||||
feeler = seeker.getNextSibling();
|
||||
seeker = feeler; // skip over doc even if there's nothing below it
|
||||
|
||||
@@ -14,12 +14,13 @@ import com.intellij.util.IncorrectOperationException;
|
||||
import com.jetbrains.python.PyBundle;
|
||||
import com.jetbrains.python.PythonLanguage;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import static com.jetbrains.python.psi.PyUtil.sure;
|
||||
import com.jetbrains.python.psi.impl.ParamHelper;
|
||||
import com.jetbrains.python.psi.types.PyClassType;
|
||||
import com.jetbrains.python.psi.types.PyType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static com.jetbrains.python.psi.PyUtil.sure;
|
||||
|
||||
/**
|
||||
* Adds a method foo to class X if X.foo() is unresolved.
|
||||
* User: dcheryasov
|
||||
@@ -73,7 +74,7 @@ public class AddMethodQuickFix implements LocalQuickFix {
|
||||
if (call_by_class) {
|
||||
if (args.length > 0) {
|
||||
PyType first_arg_type = args[0].getType();
|
||||
if (first_arg_type instanceof PyClassType && ((PyClassType)first_arg_type).getPyClass().isSublclass(cls)) {
|
||||
if (first_arg_type instanceof PyClassType && ((PyClassType)first_arg_type).getPyClass().isSubclass(cls)) {
|
||||
// class, first arg ok: instance method
|
||||
param_buf.append("self"); // NOTE: might use a name other than 'self', according to code style.
|
||||
made_instance = true;
|
||||
|
||||
@@ -19,7 +19,7 @@ public interface PyClass extends PsiNamedElement, PyStatement, NameDefiner, PyDo
|
||||
@NotNull
|
||||
PyExpression[] getSuperClassExpressions();
|
||||
|
||||
@Nullable
|
||||
@NotNull
|
||||
PsiElement[] getSuperClassElements();
|
||||
|
||||
@NotNull
|
||||
@@ -56,7 +56,7 @@ public interface PyClass extends PsiNamedElement, PyStatement, NameDefiner, PyDo
|
||||
* @param parent
|
||||
* @return True iff this and parent are the same or parent is one of our superclasses.
|
||||
*/
|
||||
boolean isSublclass(PyClass parent);
|
||||
boolean isSubclass(PyClass parent);
|
||||
|
||||
@Nullable
|
||||
PyDecoratorList getDecoratorList();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.jetbrains.python.psi.impl;
|
||||
|
||||
import com.intellij.openapi.components.ComponentManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.module.ModuleUtil;
|
||||
import com.intellij.openapi.project.Project;
|
||||
@@ -29,7 +30,6 @@ import java.util.Map;
|
||||
* Provides access to Python builtins via skeletons.
|
||||
*/
|
||||
public class PyBuiltinCache {
|
||||
|
||||
public static final @NonNls String BUILTIN_FILE = "__builtin__.py";
|
||||
|
||||
@NotNull
|
||||
@@ -59,7 +59,7 @@ public class PyBuiltinCache {
|
||||
}
|
||||
if (sdk != null) {
|
||||
// dig out the builtins file, create an instance based on it
|
||||
final String[] urls = sdk.getRootProvider().getUrls(PythonSdkType.BUITLIN_ROOT_TYPE);
|
||||
final String[] urls = sdk.getRootProvider().getUrls(PythonSdkType.BUILTIN_ROOT_TYPE);
|
||||
for (String url : urls) {
|
||||
if (url.contains(PythonSdkType.SKELETON_DIR_NAME)) {
|
||||
final String builtins_url = url + "/" + ((PythonSdkType)sdk.getSdkType()).getBuiltinsFileName(sdk);
|
||||
@@ -89,11 +89,9 @@ public class PyBuiltinCache {
|
||||
*/
|
||||
private static final Map<ComponentManager, PyBuiltinCache> ourInstanceCache = new HashMap<ComponentManager, PyBuiltinCache>();
|
||||
|
||||
private Project myProject;
|
||||
private PyFile myBuiltinsFile;
|
||||
|
||||
public PyBuiltinCache(final Project project) {
|
||||
myProject = project;
|
||||
public PyBuiltinCache() {
|
||||
}
|
||||
|
||||
private PyBuiltinCache(@Nullable final PyFile builtins) {
|
||||
|
||||
@@ -114,7 +114,14 @@ public class PyCallExpressionHelper {
|
||||
}
|
||||
// decorators?
|
||||
PyFunction method = (PyFunction)resolved; // constructor call?
|
||||
if (PyNames.INIT.equals(method.getName())) implicit_offset += 1;
|
||||
if (PyNames.INIT.equals(method.getName())) {
|
||||
String refName = us.getCallee() instanceof PyReferenceExpression
|
||||
? ((PyReferenceExpression) us.getCallee()).getReferencedName()
|
||||
: null;
|
||||
if (!PyNames.INIT.equals(refName)) { // PY-312
|
||||
implicit_offset += 1;
|
||||
}
|
||||
}
|
||||
// look for closest decorator
|
||||
PyDecoratorList decolist = method.getDecoratorList();
|
||||
if (decolist != null) {
|
||||
|
||||
@@ -104,7 +104,7 @@ public class PyCallExpressionImpl extends PyElementImpl implements PyCallExpress
|
||||
// imitate isinstance(second_arg, possible_class)
|
||||
PyClass second_class = ((PyClassType)second_type).getPyClass();
|
||||
assert second_class != null;
|
||||
if (second_class.isSublclass(first_class)) {
|
||||
if (second_class.isSubclass(first_class)) {
|
||||
// TODO: super(Foo, Bar) is a superclass of Foo directly preceding Bar in MRO
|
||||
return new PyClassType(first_class, false); // super(Foo, self) has type of Foo, modulo __get__()
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import com.intellij.util.IncorrectOperationException;
|
||||
import com.jetbrains.python.PyElementTypes;
|
||||
import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.PyTokenTypes;
|
||||
import com.jetbrains.python.PythonDosStringFinder;
|
||||
import com.jetbrains.python.PythonDocStringFinder;
|
||||
import com.jetbrains.python.codeInsight.dataflow.scope.Scope;
|
||||
import com.jetbrains.python.codeInsight.dataflow.scope.impl.ScopeImpl;
|
||||
import com.jetbrains.python.psi.*;
|
||||
@@ -90,6 +90,7 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> implement
|
||||
return PyExpression.EMPTY_ARRAY;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public PsiElement[] getSuperClassElements() {
|
||||
final PyExpression[] superExpressions = getSuperClassExpressions();
|
||||
List<PsiElement> superClasses = new ArrayList<PsiElement>();
|
||||
@@ -109,60 +110,10 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> implement
|
||||
It must be more efficient on deep and wide hierarchies, but it was more fun than efficiency that produced it.
|
||||
*/
|
||||
public Iterable<PyClass> iterateAncestors() {
|
||||
return new Iterable<PyClass>() {
|
||||
public Iterator<PyClass> iterator() {
|
||||
return new Iterator<PyClass>() {
|
||||
List<PyClass> pending = new LinkedList<PyClass>();
|
||||
Set<PyClass> seen = new HashSet<PyClass>();
|
||||
Iterator<PyClass> percolator = getSuperClassesList().iterator();
|
||||
PyClass prefetch = null;
|
||||
|
||||
public boolean hasNext() {
|
||||
// due to already-seen filtering, there's no way but to try and see.
|
||||
if (prefetch != null) return true;
|
||||
try {
|
||||
prefetch = next();
|
||||
return true;
|
||||
}
|
||||
catch (NoSuchElementException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public PyClass next() {
|
||||
iterations:
|
||||
while (true) {
|
||||
if (prefetch != null) {
|
||||
PyClass ret = prefetch;
|
||||
prefetch = null;
|
||||
return ret;
|
||||
}
|
||||
if (percolator.hasNext()) {
|
||||
PyClass it = percolator.next();
|
||||
if (seen.contains(it)) continue iterations; // loop back is equivalent to return next();
|
||||
pending.add(it);
|
||||
seen.add(it);
|
||||
return it;
|
||||
}
|
||||
else if (pending.size() > 0) {
|
||||
PyClass it = pending.get(0);
|
||||
pending.remove(0); // t, ts* = pending
|
||||
percolator = it.iterateAncestors().iterator();
|
||||
// loop back is equivalent to return next();
|
||||
}
|
||||
else throw new NoSuchElementException();
|
||||
}
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
return new AncestorsIterable(this);
|
||||
}
|
||||
|
||||
public boolean isSublclass(PyClass parent) {
|
||||
public boolean isSubclass(PyClass parent) {
|
||||
if (this == parent) return true;
|
||||
for (PyClass superclass : iterateAncestors()) {
|
||||
if (parent == superclass) return true;
|
||||
@@ -175,32 +126,35 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> implement
|
||||
}
|
||||
|
||||
protected List<PyClass> getSuperClassesList() {
|
||||
if (PyNames.FAKE_OLD_BASE.equals(getName())) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
PsiElement[] superClassElements = getSuperClassElements();
|
||||
if (superClassElements != null) {
|
||||
if (superClassElements.length > 0) {
|
||||
List<PyClass> result = new ArrayList<PyClass>();
|
||||
// maybe a bare old-style class?
|
||||
// TODO: depend on language version: py3k does not do old style classes
|
||||
PsiElement paren = PsiTreeUtil.getChildOfType(this, PyArgumentList.class).getFirstChild(); // no NPE, we always have the par expr
|
||||
if (paren != null && "(".equals(paren.getText())) { // "()" after class name, it's new style
|
||||
for(PsiElement element: superClassElements) {
|
||||
for (PsiElement element : superClassElements) {
|
||||
if (element instanceof PyClass) {
|
||||
result.add((PyClass) element);
|
||||
result.add((PyClass)element);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (! PyBuiltinCache.BUILTIN_FILE.equals(getContainingFile().getName())) { // old-style *and* not builtin object()
|
||||
else if (!PyBuiltinCache.BUILTIN_FILE.equals(getContainingFile().getName())) { // old-style *and* not builtin object()
|
||||
PyClass oldstyler = PyBuiltinCache.getInstance(this).getClass(PyNames.FAKE_OLD_BASE);
|
||||
if (oldstyler != null) result.add(oldstyler);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return new ArrayList<PyClass>(0);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public PyClass[] getSuperClasses() {
|
||||
PsiElement[] superClassElements = getSuperClassElements();
|
||||
if (superClassElements != null) {
|
||||
if (superClassElements .length > 0) {
|
||||
List<PyClass> result = new ArrayList<PyClass>();
|
||||
for(PsiElement element: superClassElements) {
|
||||
if (element instanceof PyClass) {
|
||||
@@ -331,7 +285,7 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> implement
|
||||
}
|
||||
|
||||
public PyStringLiteralExpression getDocStringExpression() {
|
||||
return PythonDosStringFinder.find(getStatementList());
|
||||
return PythonDocStringFinder.find(getStatementList());
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
@@ -388,4 +342,76 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> implement
|
||||
private static<T> T getRefValue(final SoftReference<T> reference){
|
||||
return reference != null ? reference.get() : null;
|
||||
}
|
||||
|
||||
private static class AncestorsIterable implements Iterable<PyClass> {
|
||||
private PyClassImpl myClass;
|
||||
|
||||
public AncestorsIterable(final PyClassImpl pyClass) {
|
||||
myClass = pyClass;
|
||||
}
|
||||
|
||||
public Iterator<PyClass> iterator() {
|
||||
return new AncestorsIterator(myClass);
|
||||
}
|
||||
}
|
||||
|
||||
private static class AncestorsIterator implements Iterator<PyClass> {
|
||||
List<PyClass> pending = new LinkedList<PyClass>();
|
||||
Set<PyClass> seen = new HashSet<PyClass>();
|
||||
Iterator<PyClass> percolator;
|
||||
PyClass prefetch = null;
|
||||
private PyClassImpl myAClass;
|
||||
|
||||
public AncestorsIterator(PyClassImpl aClass) {
|
||||
myAClass = aClass;
|
||||
percolator = myAClass.getSuperClassesList().iterator();
|
||||
}
|
||||
|
||||
public boolean hasNext() {
|
||||
// due to already-seen filtering, there's no way but to try and see.
|
||||
if (prefetch != null) return true;
|
||||
prefetch = getNext();
|
||||
return prefetch != null;
|
||||
}
|
||||
|
||||
public PyClass next() {
|
||||
final PyClass nextClass = getNext();
|
||||
if (nextClass == null) throw new NoSuchElementException();
|
||||
return nextClass;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private PyClass getNext() {
|
||||
iterations:
|
||||
while (true) {
|
||||
if (prefetch != null) {
|
||||
PyClass ret = prefetch;
|
||||
prefetch = null;
|
||||
return ret;
|
||||
}
|
||||
if (percolator.hasNext()) {
|
||||
PyClass it = percolator.next();
|
||||
if (seen.contains(it)) {
|
||||
continue iterations; // loop back is equivalent to return next();
|
||||
}
|
||||
pending.add(it);
|
||||
seen.add(it);
|
||||
return it;
|
||||
}
|
||||
else if (pending.size() > 0) {
|
||||
PyClass it = pending.get(0);
|
||||
pending.remove(0); // t, ts* = pending
|
||||
percolator = it.iterateAncestors().iterator();
|
||||
// loop back is equivalent to return next();
|
||||
}
|
||||
else return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.tree.TokenSet;
|
||||
import com.intellij.util.indexing.FileBasedIndex;
|
||||
import com.jetbrains.python.PyElementTypes;
|
||||
import com.jetbrains.python.PythonDosStringFinder;
|
||||
import com.jetbrains.python.PythonDocStringFinder;
|
||||
import com.jetbrains.python.PythonFileType;
|
||||
import com.jetbrains.python.PythonLanguage;
|
||||
import com.jetbrains.python.codeInsight.dataflow.scope.Scope;
|
||||
@@ -250,7 +250,7 @@ public class PyFileImpl extends PsiFileBase implements PyFile, PyExpression {
|
||||
}
|
||||
|
||||
public PyStringLiteralExpression getDocStringExpression() {
|
||||
return PythonDosStringFinder.find(this);
|
||||
return PythonDocStringFinder.find(this);
|
||||
}
|
||||
|
||||
public void subtreeChanged() {
|
||||
|
||||
@@ -27,11 +27,11 @@ import com.intellij.util.Icons;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.jetbrains.python.PyElementTypes;
|
||||
import com.jetbrains.python.PyTokenTypes;
|
||||
import com.jetbrains.python.PythonDosStringFinder;
|
||||
import com.jetbrains.python.PythonDocStringFinder;
|
||||
import com.jetbrains.python.codeInsight.controlflow.PyControlFlowBuilder;
|
||||
import com.jetbrains.python.codeInsight.dataflow.scope.Scope;
|
||||
import com.jetbrains.python.codeInsight.dataflow.scope.impl.ScopeImpl;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.codeInsight.controlflow.PyControlFlowBuilder;
|
||||
import com.jetbrains.python.psi.stubs.PyClassStub;
|
||||
import com.jetbrains.python.psi.stubs.PyFunctionStub;
|
||||
import com.jetbrains.python.toolbox.SingleIterable;
|
||||
@@ -149,7 +149,7 @@ public class PyFunctionImpl extends PyPresentableElementImpl<PyFunctionStub> imp
|
||||
}
|
||||
|
||||
public PyStringLiteralExpression getDocStringExpression() {
|
||||
return PythonDosStringFinder.find(getStatementList());
|
||||
return PythonDocStringFinder.find(getStatementList());
|
||||
}
|
||||
|
||||
protected String getElementLocation() {
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.module.ModuleUtil;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.*;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
@@ -580,7 +581,10 @@ public class ResolveImportUtil {
|
||||
) {
|
||||
if (referencedName == null) return null;
|
||||
final PsiFile file = dir.findFile(referencedName + PyNames.DOT_PY);
|
||||
if (file != null) return file;
|
||||
// findFile() does case-insensitive search, and we need exactly matching case (see PY-381)
|
||||
if (file != null && FileUtil.getNameWithoutExtension(file.getName()).equals(referencedName)) {
|
||||
return file;
|
||||
}
|
||||
final PsiDirectory subdir = dir.findSubdirectory(referencedName);
|
||||
if (subdir != null) return subdir;
|
||||
else if (! isFileOnly) {
|
||||
|
||||
@@ -4,8 +4,10 @@ import com.intellij.psi.PsiElement;
|
||||
import com.intellij.util.Processor;
|
||||
import com.intellij.util.Query;
|
||||
import com.intellij.util.QueryExecutor;
|
||||
import com.jetbrains.python.psi.PyAssignmentStatement;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.psi.PyFunction;
|
||||
import com.jetbrains.python.psi.PyTargetExpression;
|
||||
|
||||
/**
|
||||
* @author yole
|
||||
@@ -28,6 +30,12 @@ public class PyDefinitionsSearch implements QueryExecutor<PsiElement, PsiElement
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (queryParameters instanceof PyTargetExpression) { // PY-237
|
||||
final PsiElement parent = queryParameters.getParent();
|
||||
if (parent instanceof PyAssignmentStatement) {
|
||||
return consumer.process(parent);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.jetbrains.python.refactoring.classes.pullUp.PyPullUpHandler;
|
||||
import com.jetbrains.python.refactoring.classes.pushDown.PyPushDownHandler;
|
||||
import com.jetbrains.python.refactoring.extractmethod.PyExtractMethodHandler;
|
||||
import com.jetbrains.python.refactoring.introduce.constant.ConstantIntroduceHandler;
|
||||
import com.jetbrains.python.refactoring.introduce.field.FieldIntroduceHandler;
|
||||
import com.jetbrains.python.refactoring.introduce.variable.VariableIntroduceHandler;
|
||||
|
||||
/**
|
||||
@@ -26,6 +27,11 @@ public class RefactoringProvider extends DefaultRefactoringSupportProvider {
|
||||
return new ConstantIntroduceHandler();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RefactoringActionHandler getIntroduceFieldHandler() {
|
||||
return new FieldIntroduceHandler();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RefactoringActionHandler getPullUpHandler() {
|
||||
return new PyPullUpHandler();
|
||||
|
||||
@@ -122,9 +122,8 @@ public class PyClassRefactoringUtil {
|
||||
|
||||
@Nullable
|
||||
public static String prepareClassText(PyClass superClass, PyElement[] elements, boolean up, boolean ignoreNoChanges, final String preparedClassName) {
|
||||
PsiElement sibling = elements[0].getPrevSibling();
|
||||
sibling = sibling == null ? elements[0].getParent().getPrevSibling() : sibling;
|
||||
final String white = sibling.getText();
|
||||
PsiElement sibling = superClass.getPrevSibling();
|
||||
final String white = sibling != null ? "\n" + sibling.getText() + " ": "\n ";
|
||||
final StringBuilder builder = new StringBuilder("class ");
|
||||
if (preparedClassName != null) {
|
||||
builder.append(preparedClassName).append(":");
|
||||
|
||||
@@ -25,6 +25,7 @@ import com.jetbrains.python.psi.types.PyType;
|
||||
import com.jetbrains.python.refactoring.NameSuggestorUtil;
|
||||
import com.jetbrains.python.refactoring.PyRefactoringUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
@@ -46,7 +47,7 @@ abstract public class IntroduceHandler implements RefactoringActionHandler {
|
||||
}
|
||||
|
||||
public void invoke(@NotNull Project project, Editor editor, PsiFile file, DataContext dataContext) {
|
||||
performAction(project, editor, file, null, false);
|
||||
performAction(project, editor, file, null, false, false);
|
||||
}
|
||||
|
||||
public void invoke(@NotNull Project project, @NotNull PsiElement[] elements, DataContext dataContext) {
|
||||
@@ -76,7 +77,7 @@ abstract public class IntroduceHandler implements RefactoringActionHandler {
|
||||
return ArrayUtil.toStringArray(res);
|
||||
}
|
||||
|
||||
protected void performAction(@NotNull final Project project, Editor editor, PsiFile file, String name, boolean replaceAll) {
|
||||
protected void performAction(@NotNull final Project project, Editor editor, PsiFile file, String name, boolean replaceAll, boolean hasConstructor) {
|
||||
if (!CommonRefactoringUtil.checkReadOnlyStatus(file)) {
|
||||
return;
|
||||
}
|
||||
@@ -109,6 +110,11 @@ abstract public class IntroduceHandler implements RefactoringActionHandler {
|
||||
"refactoring.extractMethod");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!checkEnabled(project, editor, element1, myDialogTitle)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final PyExpression expression = (PyExpression)element1;
|
||||
|
||||
final List<PsiElement> occurrences;
|
||||
@@ -119,21 +125,32 @@ abstract public class IntroduceHandler implements RefactoringActionHandler {
|
||||
occurrences = Collections.emptyList();
|
||||
}
|
||||
String[] possibleNames = getSuggestedNames(expression);
|
||||
boolean initInConstructor = false;
|
||||
if (name == null) {
|
||||
PyIntroduceDialog dialog = new PyIntroduceDialog(project, expression, myDialogTitle, myValidator, occurrences.size(), possibleNames);
|
||||
PyIntroduceDialog dialog = new PyIntroduceDialog(project, expression, myDialogTitle, myValidator, occurrences.size(), possibleNames, getHelpId(), hasConstructor);
|
||||
dialog.show();
|
||||
if (!dialog.isOK()) {
|
||||
return;
|
||||
}
|
||||
name = dialog.getName();
|
||||
replaceAll = dialog.doReplaceAllOccurrences();
|
||||
initInConstructor = dialog.initInConstructor();
|
||||
}
|
||||
String assignmentText = name + " = " + expression.getText();
|
||||
final PyAssignmentStatement declaration =
|
||||
PythonLanguage.getInstance().getElementGenerator().createFromText(project, PyAssignmentStatement.class, assignmentText);
|
||||
final PyAssignmentStatement declaration = createDeclaration(project, assignmentText);
|
||||
|
||||
assert name != null;
|
||||
performReplace(project, declaration, expression, occurrences, name, replaceAll);
|
||||
performReplace(project, declaration, expression, occurrences, name, replaceAll, initInConstructor);
|
||||
}
|
||||
|
||||
protected abstract String getHelpId();
|
||||
|
||||
protected PyAssignmentStatement createDeclaration(Project project, String assignmentText) {
|
||||
return PythonLanguage.getInstance().getElementGenerator().createFromText(project, PyAssignmentStatement.class, assignmentText);
|
||||
}
|
||||
|
||||
protected boolean checkEnabled(Project project, Editor editor, PsiElement element1, String dialogTitle) {
|
||||
return true;
|
||||
}
|
||||
|
||||
private static List<PsiElement> getOccurrences(@NotNull final PyExpression expression) {
|
||||
@@ -152,19 +169,19 @@ abstract public class IntroduceHandler implements RefactoringActionHandler {
|
||||
@NotNull final PsiElement expression,
|
||||
@NotNull final List<PsiElement> occurrences,
|
||||
@NotNull final String name,
|
||||
final boolean replaceAll) {
|
||||
final boolean replaceAll,
|
||||
final boolean initInConstructor) {
|
||||
new WriteCommandAction(project, expression.getContainingFile()) {
|
||||
protected void run(final Result result) throws Throwable {
|
||||
final Pair<PsiElement, TextRange> data = expression.getUserData(PyPsiUtils.SELECTION_BREAKS_AST_NODE);
|
||||
if (data == null) {
|
||||
addDeclaration(expression, declaration, occurrences, replaceAll);
|
||||
addDeclaration(expression, declaration, occurrences, replaceAll, initInConstructor);
|
||||
}
|
||||
else {
|
||||
addDeclaration(data.first, declaration, occurrences, replaceAll);
|
||||
addDeclaration(data.first, declaration, occurrences, replaceAll, initInConstructor);
|
||||
}
|
||||
|
||||
PyExpressionStatement newExpression =
|
||||
PythonLanguage.getInstance().getElementGenerator().createFromText(project, PyExpressionStatement.class, name);
|
||||
PyExpressionStatement newExpression = createExpression(project, name, declaration);
|
||||
|
||||
if (replaceAll) {
|
||||
for (PsiElement occurrence : occurrences) {
|
||||
@@ -178,8 +195,14 @@ abstract public class IntroduceHandler implements RefactoringActionHandler {
|
||||
}.execute();
|
||||
}
|
||||
|
||||
protected PyExpressionStatement createExpression(Project project, String name, PyAssignmentStatement declaration) {
|
||||
return PythonLanguage.getInstance().getElementGenerator().createFromText(project, PyExpressionStatement.class, name);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected abstract PsiElement addDeclaration(@NotNull final PsiElement expression,
|
||||
@NotNull final PsiElement declaration,
|
||||
@NotNull final List<PsiElement> occurrences,
|
||||
final boolean replaceAll);
|
||||
final boolean replaceAll,
|
||||
boolean initInConstructor);
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
<properties/>
|
||||
<border type="none"/>
|
||||
<children>
|
||||
<grid id="ce21f" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||
<grid id="ce21f" layout-manager="GridLayoutManager" row-count="2" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||
<margin top="0" left="0" bottom="0" right="0"/>
|
||||
<constraints>
|
||||
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/>
|
||||
@@ -51,13 +51,21 @@
|
||||
<properties/>
|
||||
<border type="none"/>
|
||||
<children>
|
||||
<component id="c310f" class="javax.swing.JCheckBox" binding="myCheckBox">
|
||||
<component id="c310f" class="javax.swing.JCheckBox" binding="myReplaceAll">
|
||||
<constraints>
|
||||
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<selected value="false"/>
|
||||
<text resource-bundle="messages/RefactoringBundle" key="replace.all.occurences.checkbox"/>
|
||||
</properties>
|
||||
</component>
|
||||
<component id="e3d24" class="javax.swing.JCheckBox" binding="myInitialzeInConstructor">
|
||||
<constraints>
|
||||
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<selected value="false"/>
|
||||
<text value="Replace &all occurrences"/>
|
||||
<text value="CheckBox"/>
|
||||
</properties>
|
||||
</component>
|
||||
</children>
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.intellij.openapi.editor.event.DocumentListener;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.ComboBox;
|
||||
import com.intellij.openapi.ui.DialogWrapper;
|
||||
import com.intellij.refactoring.RefactoringBundle;
|
||||
import com.intellij.ui.EditorComboBoxEditor;
|
||||
import com.intellij.ui.EditorComboBoxRenderer;
|
||||
import com.intellij.ui.EditorTextField;
|
||||
@@ -28,33 +29,43 @@ public class PyIntroduceDialog extends DialogWrapper implements PyIntroduceSetti
|
||||
private JPanel myContentPane;
|
||||
private JLabel myNameLabel;
|
||||
private ComboBox myNameComboBox;
|
||||
private JCheckBox myCheckBox;
|
||||
private JCheckBox myReplaceAll;
|
||||
private JCheckBox myInitialzeInConstructor;
|
||||
|
||||
private final Project myProject;
|
||||
private final int myOccurrencesCount;
|
||||
private final IntroduceValidator myValidator;
|
||||
private final PyExpression myExpression;
|
||||
private final String myHelpId;
|
||||
|
||||
public PyIntroduceDialog(@NotNull final Project project,
|
||||
@NotNull PyExpression expression,
|
||||
@NotNull final String caption,
|
||||
@NotNull final IntroduceValidator validator,
|
||||
final int occurrencesCount,
|
||||
final String[] possibleNames) {
|
||||
final String[] possibleNames,
|
||||
final String helpId,
|
||||
boolean hasConstructor) {
|
||||
super(project, true);
|
||||
myOccurrencesCount = occurrencesCount;
|
||||
myValidator = validator;
|
||||
myProject = project;
|
||||
myExpression = expression;
|
||||
myHelpId = helpId;
|
||||
setUpNameComboBox(possibleNames);
|
||||
|
||||
setModal(true);
|
||||
setTitle(caption);
|
||||
init();
|
||||
setupDialog();
|
||||
setupDialog(hasConstructor);
|
||||
updateControls();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getHelpId() {
|
||||
return myHelpId;
|
||||
}
|
||||
|
||||
private void setUpNameComboBox(String[] possibleNames) {
|
||||
final EditorComboBoxEditor comboEditor = new StringComboboxEditor(myProject, PythonFileType.INSTANCE, myNameComboBox);
|
||||
|
||||
@@ -89,19 +100,21 @@ public class PyIntroduceDialog extends DialogWrapper implements PyIntroduceSetti
|
||||
}
|
||||
}
|
||||
|
||||
private void setupDialog() {
|
||||
myCheckBox.setMnemonic(KeyEvent.VK_A);
|
||||
private void setupDialog(boolean hasConstructor) {
|
||||
myReplaceAll.setMnemonic(KeyEvent.VK_A);
|
||||
myNameLabel.setLabelFor(myNameComboBox);
|
||||
myInitialzeInConstructor.setText(RefactoringBundle.message("initialize.in.border.title") + " " + RefactoringBundle.message("class.constructors.radio"));
|
||||
myInitialzeInConstructor.setVisible(hasConstructor);
|
||||
|
||||
// Replace occurences check box setup
|
||||
if (myOccurrencesCount > 1) {
|
||||
myCheckBox.setSelected(false);
|
||||
myCheckBox.setEnabled(true);
|
||||
myCheckBox.setText(myCheckBox.getText() + " (" + myOccurrencesCount + " occurrences)");
|
||||
myReplaceAll.setSelected(false);
|
||||
myReplaceAll.setEnabled(true);
|
||||
myReplaceAll.setText(myReplaceAll.getText() + " (" + myOccurrencesCount + " occurrences)");
|
||||
}
|
||||
else {
|
||||
myCheckBox.setSelected(false);
|
||||
myCheckBox.setEnabled(false);
|
||||
myReplaceAll.setSelected(false);
|
||||
myReplaceAll.setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,16 +144,16 @@ public class PyIntroduceDialog extends DialogWrapper implements PyIntroduceSetti
|
||||
}
|
||||
|
||||
public boolean doReplaceAllOccurrences() {
|
||||
return myCheckBox.isSelected();
|
||||
return myReplaceAll.isSelected();
|
||||
}
|
||||
|
||||
private void updateControls() {
|
||||
final boolean nameValid = myValidator.isNameValid(this);
|
||||
setOKActionEnabled(nameValid);
|
||||
if (!nameValid) {
|
||||
setErrorText(PyBundle.message("refactoring.introduce.name.error"));
|
||||
return;
|
||||
}
|
||||
setErrorText(myValidator.check(this));
|
||||
setErrorText(!nameValid ? PyBundle.message("refactoring.introduce.name.error") : null);
|
||||
}
|
||||
|
||||
public boolean initInConstructor() {
|
||||
return myInitialzeInConstructor.isSelected();
|
||||
}
|
||||
}
|
||||
|
||||
+7
-1
@@ -26,7 +26,8 @@ public class ConstantIntroduceHandler extends IntroduceHandler {
|
||||
protected PsiElement addDeclaration(@NotNull final PsiElement expression,
|
||||
@NotNull final PsiElement declaration,
|
||||
@NotNull final List<PsiElement> occurrences,
|
||||
final boolean replaceAll) {
|
||||
final boolean replaceAll,
|
||||
boolean initInConstructor) {
|
||||
PsiElement anchor;
|
||||
anchor = expression.getContainingFile();
|
||||
assert anchor instanceof PyFile;
|
||||
@@ -40,4 +41,9 @@ public class ConstantIntroduceHandler extends IntroduceHandler {
|
||||
}
|
||||
return ArrayUtil.toStringArray(names);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getHelpId() {
|
||||
return "refactoring.introduceConstant";
|
||||
}
|
||||
}
|
||||
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
package com.jetbrains.python.refactoring.introduce.field;
|
||||
|
||||
import com.intellij.openapi.actionSystem.DataContext;
|
||||
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.refactoring.RefactoringBundle;
|
||||
import com.intellij.refactoring.util.CommonRefactoringUtil;
|
||||
import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.PythonLanguage;
|
||||
import com.jetbrains.python.actions.AddFieldQuickFix;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.refactoring.introduce.IntroduceHandler;
|
||||
import com.jetbrains.python.refactoring.introduce.variable.VariableIntroduceHandler;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Dennis.Ushakov
|
||||
*/
|
||||
public class FieldIntroduceHandler extends IntroduceHandler {
|
||||
|
||||
public FieldIntroduceHandler() {
|
||||
super(new IntroduceFieldValidator(), RefactoringBundle.message("introduce.field.title"));
|
||||
}
|
||||
|
||||
public void invoke(@NotNull Project project, Editor editor, PsiFile file, DataContext dataContext) {
|
||||
performAction(project, editor, file, null, false, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean checkEnabled(Project project, Editor editor, PsiElement element1, String dialogTitle) {
|
||||
if (PyUtil.getContainingClassOrSelf(element1) == null) {
|
||||
CommonRefactoringUtil.showErrorHint(project, editor, "Cannot introduce field: not in class", dialogTitle,
|
||||
"refactoring.extractMethod");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected PsiElement addDeclaration(@NotNull PsiElement expression, @NotNull PsiElement declaration, @NotNull List<PsiElement> occurrences,
|
||||
boolean replaceAll, boolean initInConstructor) {
|
||||
final PsiElement expr = expression instanceof PyClass ? expression : expression.getParent();
|
||||
PsiElement anchor = PyUtil.getContainingClassOrSelf(expr);
|
||||
assert anchor instanceof PyClass;
|
||||
if (initInConstructor) {
|
||||
final Project project = anchor.getProject();
|
||||
final PyClass clazz = (PyClass)anchor;
|
||||
AddFieldQuickFix.addFieldToInit(project, clazz, "", new AddFieldDeclaration(project, declaration));
|
||||
final PyFunction init = clazz.findMethodByName(PyNames.INIT, false);
|
||||
final PyStatementList statements = init != null ? init.getStatementList() : null;
|
||||
return statements != null ? statements.getLastChild() : null;
|
||||
}
|
||||
return VariableIntroduceHandler.doIntroduceVariable(expression, declaration, occurrences, replaceAll);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PyExpressionStatement createExpression(Project project, String name, PyAssignmentStatement declaration) {
|
||||
final String text = declaration.getText();
|
||||
final String self_name = text.substring(0, text.indexOf('.'));
|
||||
return PythonLanguage.getInstance().getElementGenerator().createFromText(project, PyExpressionStatement.class, self_name + "." + name);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PyAssignmentStatement createDeclaration(Project project, String assignmentText) {
|
||||
return PythonLanguage.getInstance().getElementGenerator().createFromText(project, PyAssignmentStatement.class, PyNames.CANONICAL_SELF + "." + assignmentText);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getHelpId() {
|
||||
return "refactoring.introduceField";
|
||||
}
|
||||
|
||||
private static class AddFieldDeclaration extends AddFieldQuickFix.FieldCallback {
|
||||
private final Project myProject;
|
||||
private final PsiElement myDeclaration;
|
||||
|
||||
private AddFieldDeclaration(Project project, PsiElement declaration) {
|
||||
myProject = project;
|
||||
myDeclaration = declaration;
|
||||
}
|
||||
|
||||
public PyStatement fun(String self_name) {
|
||||
if (PyNames.CANONICAL_SELF.equals(self_name)) {
|
||||
return (PyStatement)myDeclaration;
|
||||
}
|
||||
final String text = myDeclaration.getText();
|
||||
return myGenerator.createFromText(myProject, PyStatement.class, text.replaceFirst(PyNames.CANONICAL_SELF + "\\.", self_name + "."));
|
||||
}
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package com.jetbrains.python.refactoring.introduce.field;
|
||||
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.jetbrains.python.PyBundle;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.psi.PyFunction;
|
||||
import com.jetbrains.python.psi.impl.PyPsiUtils;
|
||||
import com.jetbrains.python.refactoring.PyRefactoringUtil;
|
||||
import com.jetbrains.python.refactoring.introduce.IntroduceValidator;
|
||||
|
||||
/**
|
||||
* @author Dennis.Ushakov
|
||||
*/
|
||||
public class IntroduceFieldValidator extends IntroduceValidator {
|
||||
@Override
|
||||
protected String simpleCheck(String name, PsiElement psiElement) {
|
||||
if (psiElement.getUserData(PyPsiUtils.SELECTION_BREAKS_AST_NODE) != null) {
|
||||
final Pair<PsiElement,TextRange> data = psiElement.getUserData(PyPsiUtils.SELECTION_BREAKS_AST_NODE);
|
||||
psiElement = data.first;
|
||||
}
|
||||
PsiElement context = PsiTreeUtil.getParentOfType(psiElement, PyFunction.class);
|
||||
if (context == null) {
|
||||
context = PsiTreeUtil.getParentOfType(psiElement, PyClass.class);
|
||||
}
|
||||
if (context == null) {
|
||||
context = psiElement.getContainingFile();
|
||||
}
|
||||
|
||||
if (PyRefactoringUtil.collectScopeVariables(context).contains(name)) {
|
||||
return PyBundle.message("refactoring.introduce.constant.scope.error");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+11
-1
@@ -25,7 +25,12 @@ public class VariableIntroduceHandler extends IntroduceHandler {
|
||||
protected PsiElement addDeclaration(@NotNull final PsiElement expression,
|
||||
@NotNull final PsiElement declaration,
|
||||
@NotNull final List<PsiElement> occurrences,
|
||||
final boolean replaceAll) {
|
||||
final boolean replaceAll,
|
||||
boolean initInConstructor) {
|
||||
return doIntroduceVariable(expression, declaration, occurrences, replaceAll);
|
||||
}
|
||||
|
||||
public static PsiElement doIntroduceVariable(PsiElement expression, PsiElement declaration, List<PsiElement> occurrences, boolean replaceAll) {
|
||||
PyStatement anchorStatement;
|
||||
if (replaceAll) {
|
||||
final PsiElement parent = PsiTreeUtil.findCommonParent(occurrences.toArray(new PsiElement[occurrences.size()]));
|
||||
@@ -39,4 +44,9 @@ public class VariableIntroduceHandler extends IntroduceHandler {
|
||||
anchorStatement.getParent().addBefore(declaration, anchorStatement);
|
||||
return anchorStatement.getParent().getParent();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getHelpId() {
|
||||
return "refactoring.introduceVariable";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import com.intellij.openapi.projectRoots.Sdk;
|
||||
import com.intellij.openapi.projectRoots.impl.SdkListCellRenderer;
|
||||
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import static com.intellij.openapi.util.io.FileUtil.toSystemIndependentName;
|
||||
import com.intellij.ui.CollectionComboBoxModel;
|
||||
import com.intellij.ui.ColoredListCellRenderer;
|
||||
import com.intellij.ui.RawCommandLineEditor;
|
||||
@@ -16,6 +15,8 @@ import com.jetbrains.python.sdk.PythonSdkType;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -23,7 +24,7 @@ import java.util.Map;
|
||||
/**
|
||||
* @author yole
|
||||
*/
|
||||
public class PyCommonOptionsForm implements AbstractPythonRunConfigurationParams {
|
||||
public class PyCommonOptionsForm implements AbstractPythonRunConfigurationParams {
|
||||
private TextFieldWithBrowseButton myWorkingDirectoryTextField;
|
||||
private EnvironmentVariablesComponent myEnvsComponent;
|
||||
private RawCommandLineEditor myInterpreterOptionsTextField;
|
||||
@@ -56,6 +57,18 @@ public class PyCommonOptionsForm implements AbstractPythonRunConfigurationParams
|
||||
myWorkingDirectoryTextField.addBrowseFolderListener("Select Working Directory", "", configuration.getProject(),
|
||||
new FileChooserDescriptor(false, true, false, false, false, false));
|
||||
|
||||
ActionListener listener = new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
updateControls();
|
||||
}
|
||||
};
|
||||
myUseSpecifiedSdkRadioButton.addActionListener(listener);
|
||||
myUseModuleSdkRadioButton.addActionListener(listener);
|
||||
}
|
||||
|
||||
private void updateControls() {
|
||||
myModuleComboBox.setEnabled(myUseModuleSdkRadioButton.isSelected());
|
||||
myInterpreterComboBox.setEnabled(myUseSpecifiedSdkRadioButton.isSelected());
|
||||
}
|
||||
|
||||
public JPanel getMainPanel() {
|
||||
@@ -75,7 +88,7 @@ public class PyCommonOptionsForm implements AbstractPythonRunConfigurationParams
|
||||
}
|
||||
|
||||
public String getWorkingDirectory() {
|
||||
return toSystemIndependentName(myWorkingDirectoryTextField.getText().trim());
|
||||
return FileUtil.toSystemIndependentName(myWorkingDirectoryTextField.getText().trim());
|
||||
}
|
||||
|
||||
public void setWorkingDirectory(String workingDirectory) {
|
||||
@@ -122,6 +135,7 @@ public class PyCommonOptionsForm implements AbstractPythonRunConfigurationParams
|
||||
else {
|
||||
myUseSpecifiedSdkRadioButton.setSelected(true);
|
||||
}
|
||||
updateControls();
|
||||
}
|
||||
|
||||
public boolean isPassParentEnvs() {
|
||||
|
||||
@@ -79,7 +79,7 @@ public class PythonSdkType extends SdkType {
|
||||
@NotNull
|
||||
@NonNls
|
||||
public String getBuiltinsFileName(Sdk sdk) {
|
||||
final String version = getVersionString(sdk);
|
||||
final String version = sdk.getVersionString();
|
||||
if (version != null && version.startsWith("Python 3")) {
|
||||
return "builtins.py";
|
||||
}
|
||||
@@ -218,7 +218,7 @@ public class PythonSdkType extends SdkType {
|
||||
|
||||
@Override
|
||||
public SdkAdditionalData loadAdditionalData(final Sdk currentSdk, final Element additional) {
|
||||
final String[] urls = currentSdk.getRootProvider().getUrls(OrderRootType.SOURCES);
|
||||
final String[] urls = currentSdk.getRootProvider().getUrls(BUILTIN_ROOT_TYPE);
|
||||
for (String url : urls) {
|
||||
if (url.contains(SKELETON_DIR_NAME)) {
|
||||
final String path = VfsUtil.urlToPath(url);
|
||||
@@ -266,7 +266,7 @@ public class PythonSdkType extends SdkType {
|
||||
/**
|
||||
* In which root type built-in skeletons are put.
|
||||
*/
|
||||
public static final OrderRootType BUITLIN_ROOT_TYPE = OrderRootType.CLASSES;
|
||||
public static final OrderRootType BUILTIN_ROOT_TYPE = OrderRootType.CLASSES;
|
||||
|
||||
public static void setupSdkPaths(SdkModificator sdkModificator, ProgressIndicator indicator) {
|
||||
String sdk_path = sdkModificator.getHomePath();
|
||||
@@ -305,7 +305,7 @@ public class PythonSdkType extends SdkType {
|
||||
indicator.setText2("");
|
||||
}
|
||||
generateBuiltinStubs(sdk_path, stubs_path);
|
||||
sdkModificator.addRoot(LocalFileSystem.getInstance().refreshAndFindFileByPath(stubs_path), BUITLIN_ROOT_TYPE);
|
||||
sdkModificator.addRoot(LocalFileSystem.getInstance().refreshAndFindFileByPath(stubs_path), BUILTIN_ROOT_TYPE);
|
||||
}
|
||||
generateBinaryStubs(sdk_path, stubs_path, indicator);
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ package com.jetbrains.python.validation;
|
||||
|
||||
import com.intellij.lang.annotation.Annotation;
|
||||
import com.jetbrains.python.PyHighlighter;
|
||||
import com.jetbrains.python.PythonDosStringFinder;
|
||||
import com.jetbrains.python.PythonDocStringFinder;
|
||||
import com.jetbrains.python.psi.PyClass;
|
||||
import com.jetbrains.python.psi.PyFile;
|
||||
import com.jetbrains.python.psi.PyFunction;
|
||||
@@ -31,18 +31,18 @@ public class DocStringAnnotator extends PyAnnotator {
|
||||
|
||||
@Override
|
||||
public void visitPyFile(PyFile node) {
|
||||
annotateDocStringStmt(PythonDosStringFinder.find(node));
|
||||
annotateDocStringStmt(PythonDocStringFinder.find(node));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void visitPyFunction(PyFunction node) {
|
||||
annotateDocStringStmt(PythonDosStringFinder.find(node.getStatementList()));
|
||||
annotateDocStringStmt(PythonDocStringFinder.find(node.getStatementList()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPyClass(PyClass node) {
|
||||
annotateDocStringStmt(PythonDosStringFinder.find(node.getStatementList()));
|
||||
annotateDocStringStmt(PythonDocStringFinder.find(node.getStatementList()));
|
||||
}
|
||||
|
||||
private void annotateDocStringStmt(PyStringLiteralExpression stmt) {
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<html><body><code><small>class <b>Foo</b>(object)</small><br>Doc of Foo.</code></body></html>
|
||||
@@ -0,0 +1,7 @@
|
||||
# direct class doc
|
||||
class Foo(object):
|
||||
"<the_doc>Doc of Foo."
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
<the_ref>Foo()
|
||||
@@ -0,0 +1,3 @@
|
||||
from foo import Context
|
||||
c = Context()
|
||||
# <ref>
|
||||
@@ -0,0 +1,3 @@
|
||||
__all__ = ['Context']
|
||||
|
||||
from foo.context import Context
|
||||
@@ -0,0 +1 @@
|
||||
class Context(object): pass
|
||||
@@ -128,6 +128,12 @@ public class PyMultiFileResolveTest extends PyLightFixtureTestCase {
|
||||
assertEquals("silicate", ((PyStringLiteralExpression)value).getStringValue());
|
||||
}
|
||||
|
||||
public void testDirectoryVsClass() throws Exception {
|
||||
PsiElement element = doResolve();
|
||||
assertTrue(element instanceof PyClass);
|
||||
assertEquals("Context", ((PyClass) element).getName());
|
||||
}
|
||||
|
||||
private PsiFile prepareFile() throws Exception {
|
||||
String testName = getTestName(true);
|
||||
String fileName = getTestName(false) + ".py";
|
||||
|
||||
@@ -28,21 +28,22 @@ public class PyQuickDocTest extends LightMarkedTestCase {
|
||||
myProvider = new PythonDocumentationProvider();
|
||||
}
|
||||
|
||||
protected String getTestDataPath() {
|
||||
return PythonTestUtil.getTestDataPath() + "/quickdoc/";
|
||||
}
|
||||
|
||||
private void checkByHTML(String text) throws Exception {
|
||||
assertNotNull(text);
|
||||
String filePath = getTestName(false) + ".html";
|
||||
String filePath = "/quickdoc/" + getTestName(false) + ".html";
|
||||
final String fullPath = getTestDataPath() + filePath;
|
||||
final VirtualFile vFile = LocalFileSystem.getInstance().findFileByPath(fullPath.replace(File.separatorChar, '/'));
|
||||
assertNotNull("file " + filePath + " not found", vFile);
|
||||
assertNotNull("file " + fullPath + " not found", vFile);
|
||||
|
||||
String fileText = StringUtil.convertLineSeparators(VfsUtil.loadText(vFile), "\n");
|
||||
assertEquals(fileText.trim(), text.trim());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, PsiElement> loadTest() throws Exception {
|
||||
return configureByFile("/quickdoc/" + getTestName(false) + ".py");
|
||||
}
|
||||
|
||||
private void processRefDocPair() throws Exception {
|
||||
Map<String, PsiElement> marks = loadTest();
|
||||
assertEquals(2, marks.size());
|
||||
@@ -53,7 +54,7 @@ public class PyQuickDocTest extends LightMarkedTestCase {
|
||||
|
||||
PsiElement ref_elt = marks.get("<the_ref>").getParent(); // ident -> expr
|
||||
final PyDocStringOwner doc_owner = (PyDocStringOwner)((PyReferenceExpression)ref_elt).resolve();
|
||||
assertEquals(doc_owner.getDocStringExpression(), doc_elt);
|
||||
assertEquals(doc_elt, doc_owner.getDocStringExpression());
|
||||
|
||||
checkByHTML(myProvider.generateDoc(doc_owner, null));
|
||||
}
|
||||
@@ -70,6 +71,13 @@ public class PyQuickDocTest extends LightMarkedTestCase {
|
||||
processRefDocPair();
|
||||
}
|
||||
|
||||
public void testClassUndocumentedConstructor() throws Exception {
|
||||
Map<String, PsiElement> marks = loadTest();
|
||||
PsiElement ref_elt = marks.get("<the_ref>").getParent(); // ident -> expr
|
||||
final PyDocStringOwner doc_owner = (PyDocStringOwner)((PyReferenceExpression)ref_elt).resolve();
|
||||
checkByHTML(myProvider.generateDoc(doc_owner, null));
|
||||
}
|
||||
|
||||
public void testCallFunc() throws Exception {
|
||||
processRefDocPair();
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.intellij.openapi.vfs.VfsUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.testFramework.TestDataFile;
|
||||
import com.intellij.util.containers.HashMap;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
|
||||
@@ -47,12 +48,12 @@ public abstract class LightMarkedTestCase extends PyLightFixtureTestCase {
|
||||
* @return a mapping of markers to PSI elements
|
||||
* @throws Exception
|
||||
*/
|
||||
protected Map<String, PsiElement> configureByFile(@NonNls String filePath, @NonNls String markerRegexp)
|
||||
protected Map<String, PsiElement> configureByFile(@TestDataFile @NonNls String filePath, @NonNls String markerRegexp)
|
||||
throws Exception
|
||||
{
|
||||
final String fullPath = getTestDataPath() + filePath;
|
||||
final VirtualFile vFile = LocalFileSystem.getInstance().findFileByPath(fullPath.replace(File.separatorChar, '/'));
|
||||
assertNotNull("file " + filePath + " not found", vFile);
|
||||
assertNotNull("file " + fullPath + " not found", vFile);
|
||||
|
||||
String fileText = StringUtil.convertLineSeparators(VfsUtil.loadText(vFile), "\n");
|
||||
|
||||
@@ -111,6 +112,4 @@ public abstract class LightMarkedTestCase extends PyLightFixtureTestCase {
|
||||
String fname = getTestName(false) + ".py";
|
||||
return configureByFile(fname);
|
||||
}
|
||||
|
||||
protected abstract String getTestDataPath();
|
||||
}
|
||||
Reference in New Issue
Block a user