Merge branch 'master' of git.labs.intellij.net:idea/ultimate

This commit is contained in:
Dmitry Cheryasov
2010-02-01 21:43:21 +02:00
35 changed files with 551 additions and 235 deletions
@@ -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"/>
@@ -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 &amp;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();
}
}
@@ -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";
}
}
@@ -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 + "."));
}
}
}
@@ -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;
}
}
@@ -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&nbsp;of&nbsp;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();
}