diff options
Diffstat (limited to 'python/src')
47 files changed, 1395 insertions, 287 deletions
diff --git a/python/src/META-INF/pycharm-core.xml b/python/src/META-INF/pycharm-core.xml index 4732ad084b32..a9f7824b5340 100644 --- a/python/src/META-INF/pycharm-core.xml +++ b/python/src/META-INF/pycharm-core.xml @@ -8,6 +8,9 @@ </xi:include> <xi:include href="/META-INF/RegExpPlugin.xml" xpointer="xpointer(/idea-plugin/*)"/> <xi:include href="/META-INF/SpellCheckerPlugin.xml" xpointer="xpointer(/idea-plugin/*)"/> + <xi:include href="/META-INF/RemoteServers.xml" xpointer="xpointer(/idea-plugin/*)"> + <xi:fallback/> + </xi:include> <application-components> <component> diff --git a/python/src/META-INF/python-core.xml b/python/src/META-INF/python-core.xml index 292237aac6ac..7adbc46c189c 100644 --- a/python/src/META-INF/python-core.xml +++ b/python/src/META-INF/python-core.xml @@ -589,6 +589,9 @@ <pyClassMembersProvider implementation="com.jetbrains.python.codeInsight.userSkeletons.PyUserSkeletonsClassMembersProvider"/> <typeProvider implementation="com.jetbrains.python.codeInsight.userSkeletons.PyUserSkeletonsTypeProvider"/> + <!-- typing --> + <typeProvider implementation="com.jetbrains.python.codeInsight.PyTypingTypeProvider"/> + <typeProvider implementation="com.jetbrains.python.debugger.PyCallSignatureTypeProvider"/> <pyReferenceResolveProvider implementation="com.jetbrains.python.psi.resolve.PythonBuiltinReferenceResolveProvider"/> diff --git a/python/src/com/jetbrains/python/codeInsight/PyTypingTypeProvider.java b/python/src/com/jetbrains/python/codeInsight/PyTypingTypeProvider.java new file mode 100644 index 000000000000..f03befac6c14 --- /dev/null +++ b/python/src/com/jetbrains/python/codeInsight/PyTypingTypeProvider.java @@ -0,0 +1,375 @@ +/* + * Copyright 2000-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.jetbrains.python.codeInsight; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiPolyVariantReference; +import com.intellij.psi.util.QualifiedName; +import com.jetbrains.python.PyNames; +import com.jetbrains.python.psi.*; +import com.jetbrains.python.psi.impl.PyExpressionCodeFragmentImpl; +import com.jetbrains.python.psi.impl.PyPsiUtils; +import com.jetbrains.python.psi.resolve.PyResolveContext; +import com.jetbrains.python.psi.types.*; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * @author vlan + */ +public class PyTypingTypeProvider extends PyTypeProviderBase { + private static ImmutableMap<String, String> BUILTIN_COLLECTIONS = ImmutableMap.<String, String>builder() + .put("typing.List", "list") + .put("typing.Dict", "dict") + .put("typing.Set", PyNames.SET) + .put("typing.Tuple", PyNames.TUPLE) + .build(); + + private static ImmutableSet<String> GENERIC_CLASSES = ImmutableSet.<String>builder() + .add("typing.Generic") + .add("typing.AbstractGeneric") + .add("typing.Protocol") + .build(); + + public PyType getParameterType(@NotNull PyNamedParameter param, @NotNull PyFunction func, @NotNull TypeEvalContext context) { + final PyAnnotation annotation = param.getAnnotation(); + if (annotation != null) { + // XXX: Requires switching from stub to AST + final PyExpression value = annotation.getValue(); + if (value != null) { + return getTypingType(value, context); + } + } + return null; + } + + @Nullable + @Override + public PyType getReturnType(@NotNull Callable callable, @NotNull TypeEvalContext context) { + if (callable instanceof PyFunction) { + final PyFunction function = (PyFunction)callable; + final PyAnnotation annotation = function.getAnnotation(); + if (annotation != null) { + // XXX: Requires switching from stub to AST + final PyExpression value = annotation.getValue(); + if (value != null) { + return getTypingType(value, context); + } + } + final PyType constructorType = getGenericConstructorType(function, context); + if (constructorType != null) { + return constructorType; + } + } + return null; + } + + @Nullable + private static PyType getGenericConstructorType(@NotNull PyFunction function, @NotNull TypeEvalContext context) { + if (PyUtil.isInit(function)) { + final PyClass cls = function.getContainingClass(); + if (cls != null) { + final List<PyGenericType> genericTypes = collectGenericTypes(cls, context); + + final PyType elementType; + if (genericTypes.size() == 1) { + elementType = genericTypes.get(0); + } + else if (genericTypes.size() > 1) { + elementType = PyTupleType.create(cls, genericTypes.toArray(new PyType[genericTypes.size()])); + } + else { + elementType = null; + } + + if (elementType != null) { + return new PyCollectionTypeImpl(cls, false, elementType); + } + } + } + return null; + } + + @NotNull + private static List<PyGenericType> collectGenericTypes(@NotNull PyClass cls, @NotNull TypeEvalContext context) { + boolean isGeneric = false; + for (PyClass ancestor : cls.getAncestorClasses(context)) { + if (GENERIC_CLASSES.contains(ancestor.getQualifiedName())) { + isGeneric = true; + break; + } + } + if (isGeneric) { + final ArrayList<PyGenericType> results = new ArrayList<PyGenericType>(); + // XXX: Requires switching from stub to AST + for (PyExpression expr : cls.getSuperClassExpressions()) { + if (expr instanceof PySubscriptionExpression) { + final PyExpression indexExpr = ((PySubscriptionExpression)expr).getIndexExpression(); + if (indexExpr != null) { + final PyGenericType genericType = getGenericType(indexExpr, context); + if (genericType != null) { + results.add(genericType); + } + } + } + } + return results; + } + return Collections.emptyList(); + } + + @Nullable + private static PyType getTypingType(@NotNull PyExpression expression, @NotNull TypeEvalContext context) { + final PyType unionType = getUnionType(expression, context); + if (unionType != null) { + return unionType; + } + final PyType parameterizedType = getParameterizedType(expression, context); + if (parameterizedType != null) { + return parameterizedType; + } + final PyType builtinCollection = getBuiltinCollection(expression, context); + if (builtinCollection != null) { + return builtinCollection; + } + final PyType genericType = getGenericType(expression, context); + if (genericType != null) { + return genericType; + } + final PyType functionType = getFunctionType(expression, context); + if (functionType != null) { + return functionType; + } + final PyType stringBasedType = getStringBasedType(expression, context); + if (stringBasedType != null) { + return stringBasedType; + } + return null; + } + + @Nullable + private static PyType getStringBasedType(@NotNull PyExpression expression, @NotNull TypeEvalContext context) { + if (expression instanceof PyStringLiteralExpression) { + // XXX: Requires switching from stub to AST + final String contents = ((PyStringLiteralExpression)expression).getStringValue(); + final Project project = expression.getProject(); + final PyExpressionCodeFragmentImpl codeFragment = new PyExpressionCodeFragmentImpl(project, "dummy.py", contents, false); + codeFragment.setContext(expression.getContainingFile()); + final PsiElement element = codeFragment.getFirstChild(); + if (element instanceof PyExpressionStatement) { + final PyExpression dummyExpr = ((PyExpressionStatement)element).getExpression(); + return getType(dummyExpr, context); + } + } + return null; + } + + @Nullable + private static PyType getFunctionType(@NotNull PyExpression expression, @NotNull TypeEvalContext context) { + if (expression instanceof PySubscriptionExpression) { + final PySubscriptionExpression subscriptionExpr = (PySubscriptionExpression)expression; + final PyExpression operand = subscriptionExpr.getOperand(); + final String operandName = resolveToQualifiedName(operand, context); + if ("typing.Function".equals(operandName)) { + final PyExpression indexExpr = subscriptionExpr.getIndexExpression(); + if (indexExpr instanceof PyTupleExpression) { + final PyTupleExpression tupleExpr = (PyTupleExpression)indexExpr; + final PyExpression[] elements = tupleExpr.getElements(); + if (elements.length == 2) { + final PyExpression parametersExpr = elements[0]; + if (parametersExpr instanceof PyListLiteralExpression) { + final List<PyCallableParameter> parameters = new ArrayList<PyCallableParameter>(); + final PyListLiteralExpression listExpr = (PyListLiteralExpression)parametersExpr; + for (PyExpression argExpr : listExpr.getElements()) { + parameters.add(new PyCallableParameterImpl(null, getType(argExpr, context))); + } + final PyExpression returnTypeExpr = elements[1]; + final PyType returnType = getType(returnTypeExpr, context); + return new PyCallableTypeImpl(parameters, returnType); + } + } + } + } + } + return null; + } + + @Nullable + private static PyType getUnionType(@NotNull PyExpression expression, @NotNull TypeEvalContext context) { + if (expression instanceof PySubscriptionExpression) { + final PySubscriptionExpression subscriptionExpr = (PySubscriptionExpression)expression; + final PyExpression operand = subscriptionExpr.getOperand(); + final String operandName = resolveToQualifiedName(operand, context); + if ("typing.Union".equals(operandName)) { + return PyUnionType.union(getIndexTypes(subscriptionExpr, context)); + } + } + return null; + } + + @Nullable + private static PyGenericType getGenericType(@NotNull PyExpression expression, @NotNull TypeEvalContext context) { + final PsiElement resolved = resolve(expression, context); + if (resolved instanceof PyTargetExpression) { + final PyTargetExpression targetExpr = (PyTargetExpression)resolved; + final QualifiedName calleeName = targetExpr.getCalleeName(); + if (calleeName != null && "typevar".equals(calleeName.toString())) { + // XXX: Requires switching from stub to AST + final PyExpression assigned = targetExpr.findAssignedValue(); + if (assigned instanceof PyCallExpression) { + final PyCallExpression assignedCall = (PyCallExpression)assigned; + final PyExpression callee = assignedCall.getCallee(); + if (callee != null) { + final String calleeQName = resolveToQualifiedName(callee, context); + if ("typing.typevar".equals(calleeQName)) { + final PyExpression[] arguments = assignedCall.getArguments(); + if (arguments.length > 0) { + final PyExpression firstArgument = arguments[0]; + if (firstArgument instanceof PyStringLiteralExpression) { + final String name = ((PyStringLiteralExpression)firstArgument).getStringValue(); + if (name != null) { + return new PyGenericType(name, getGenericTypeBound(arguments, context)); + } + } + } + } + + } + } + } + } + return null; + } + + @Nullable + private static PyType getGenericTypeBound(@NotNull PyExpression[] typeVarArguments, @NotNull TypeEvalContext context) { + final List<PyType> types = new ArrayList<PyType>(); + if (typeVarArguments.length > 1) { + final PyExpression secondArgument = typeVarArguments[1]; + if (secondArgument instanceof PyKeywordArgument) { + final PyKeywordArgument valuesArgument = (PyKeywordArgument)secondArgument; + final PyExpression valueExpr = PyPsiUtils.flattenParens(valuesArgument.getValueExpression()); + if (valueExpr instanceof PyTupleExpression) { + final PyTupleExpression tupleExpr = (PyTupleExpression)valueExpr; + for (PyExpression expr : tupleExpr.getElements()) { + types.add(getType(expr, context)); + } + } + } + } + return PyUnionType.union(types); + } + + @NotNull + private static List<PyType> getIndexTypes(@NotNull PySubscriptionExpression expression, @NotNull TypeEvalContext context) { + final List<PyType> types = new ArrayList<PyType>(); + final PyExpression indexExpr = expression.getIndexExpression(); + if (indexExpr instanceof PyTupleExpression) { + final PyTupleExpression tupleExpr = (PyTupleExpression)indexExpr; + for (PyExpression expr : tupleExpr.getElements()) { + types.add(getType(expr, context)); + } + } + return types; + } + + @Nullable + private static PyType getParameterizedType(@NotNull PyExpression expression, @NotNull TypeEvalContext context) { + if (expression instanceof PySubscriptionExpression) { + final PySubscriptionExpression subscriptionExpr = (PySubscriptionExpression)expression; + final PyExpression operand = subscriptionExpr.getOperand(); + final PyExpression indexExpr = subscriptionExpr.getIndexExpression(); + final PyType operandType = getType(operand, context); + if (operandType instanceof PyClassType) { + final PyClass cls = ((PyClassType)operandType).getPyClass(); + if (PyNames.TUPLE.equals(cls.getQualifiedName())) { + final List<PyType> indexTypes = getIndexTypes(subscriptionExpr, context); + return PyTupleType.create(expression, indexTypes.toArray(new PyType[indexTypes.size()])); + } + else if (indexExpr != null) { + final PyType indexType = context.getType(indexExpr); + return new PyCollectionTypeImpl(cls, false, indexType); + } + } + } + return null; + } + + @Nullable + private static PyType getBuiltinCollection(@NotNull PyExpression expression, @NotNull TypeEvalContext context) { + final String collectionName = resolveToQualifiedName(expression, context); + final String builtinName = BUILTIN_COLLECTIONS.get(collectionName); + return builtinName != null ? PyTypeParser.getTypeByName(expression, builtinName) : null; + } + + @Nullable + private static PyType getType(@NotNull PyExpression expression, @NotNull TypeEvalContext context) { + // It is possible to replace PyAnnotation.getType() with this implementation + final PyType typingType = getTypingType(expression, context); + if (typingType != null) { + return typingType; + } + final PyType type = context.getType(expression); + if (type instanceof PyClassLikeType) { + final PyClassLikeType classType = (PyClassLikeType)type; + if (classType.isDefinition()) { + return classType.toInstance(); + } + } + else if (type instanceof PyNoneType) { + return type; + } + return null; + } + + @Nullable + private static PsiElement resolve(@NotNull PyExpression expression, @NotNull TypeEvalContext context) { + if (expression instanceof PyReferenceOwner) { + final PyReferenceOwner referenceOwner = (PyReferenceOwner)expression; + final PyResolveContext resolveContext = PyResolveContext.noImplicits().withTypeEvalContext(context); + final PsiPolyVariantReference reference = referenceOwner.getReference(resolveContext); + final PsiElement element = reference.resolve(); + if (element instanceof PyFunction) { + final PyFunction function = (PyFunction)element; + if (PyUtil.isInit(function)) { + final PyClass cls = function.getContainingClass(); + if (cls != null) { + return cls; + } + } + } + return element; + } + return null; + } + + @Nullable + private static String resolveToQualifiedName(@NotNull PyExpression expression, @NotNull TypeEvalContext context) { + final PsiElement element = resolve(expression, context); + if (element instanceof PyQualifiedNameOwner) { + final PyQualifiedNameOwner qualifiedNameOwner = (PyQualifiedNameOwner)element; + return qualifiedNameOwner.getQualifiedName(); + } + return null; + } +} diff --git a/python/src/com/jetbrains/python/codeInsight/highlighting/PyHighlightExitPointsHandlerFactory.java b/python/src/com/jetbrains/python/codeInsight/highlighting/PyHighlightExitPointsHandlerFactory.java index d0bc9dcbc7ca..27522d6e7f53 100644 --- a/python/src/com/jetbrains/python/codeInsight/highlighting/PyHighlightExitPointsHandlerFactory.java +++ b/python/src/com/jetbrains/python/codeInsight/highlighting/PyHighlightExitPointsHandlerFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2013 JetBrains s.r.o. + * Copyright 2000-2014 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,30 +15,26 @@ */ package com.jetbrains.python.codeInsight.highlighting; -import com.intellij.codeInsight.TargetElementUtilBase; import com.intellij.codeInsight.highlighting.HighlightUsagesHandlerBase; -import com.intellij.codeInsight.highlighting.HighlightUsagesHandlerFactory; +import com.intellij.codeInsight.highlighting.HighlightUsagesHandlerFactoryBase; import com.intellij.openapi.editor.Editor; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.util.PsiTreeUtil; import com.jetbrains.python.psi.PyExpression; import com.jetbrains.python.psi.PyReturnStatement; +import org.jetbrains.annotations.NotNull; /** * @author oleg */ -public class PyHighlightExitPointsHandlerFactory implements HighlightUsagesHandlerFactory { - public HighlightUsagesHandlerBase createHighlightUsagesHandler(final Editor editor, final PsiFile file) { - int offset = TargetElementUtilBase.adjustOffset(file, editor.getDocument(), editor.getCaretModel().getOffset()); - PsiElement target = file.findElementAt(offset); - if (target != null) { - final PyReturnStatement returnStatement = PsiTreeUtil.getParentOfType(target, PyReturnStatement.class); - if (returnStatement != null) { - final PyExpression returnExpr = returnStatement.getExpression(); - if (returnExpr == null || !PsiTreeUtil.isAncestor(returnExpr, target, false)) { - return new PyHighlightExitPointsHandler(editor, file, target); - } +public class PyHighlightExitPointsHandlerFactory extends HighlightUsagesHandlerFactoryBase { + public HighlightUsagesHandlerBase createHighlightUsagesHandler(@NotNull Editor editor, @NotNull PsiFile file, @NotNull PsiElement target) { + final PyReturnStatement returnStatement = PsiTreeUtil.getParentOfType(target, PyReturnStatement.class); + if (returnStatement != null) { + final PyExpression returnExpr = returnStatement.getExpression(); + if (returnExpr == null || !PsiTreeUtil.isAncestor(returnExpr, target, false)) { + return new PyHighlightExitPointsHandler(editor, file, target); } } return null; diff --git a/python/src/com/jetbrains/python/codeInsight/intentions/ImportToImportFromIntention.java b/python/src/com/jetbrains/python/codeInsight/intentions/ImportToImportFromIntention.java index fc35833473f8..e76240cefb1f 100644 --- a/python/src/com/jetbrains/python/codeInsight/intentions/ImportToImportFromIntention.java +++ b/python/src/com/jetbrains/python/codeInsight/intentions/ImportToImportFromIntention.java @@ -57,10 +57,11 @@ public class ImportToImportFromIntention implements IntentionAction { private PsiElement myReferee = null; private PyImportElement myImportElement = null; private Collection<PsiReference> myReferences = null; - private boolean myHasModuleReference = false; // is anything that resolves to our imported module is just an exact reference to that module + private boolean myHasModuleReference = false; + // is anything that resolves to our imported module is just an exact reference to that module private int myRelativeLevel; // true if "from ... import" - public IntentionState(Editor editor, PsiFile file) { + public IntentionState(@NotNull Editor editor, @NotNull PsiFile file) { boolean available = false; myImportElement = findImportElement(editor, file); if (myImportElement != null) { @@ -70,10 +71,10 @@ public class ImportToImportFromIntention implements IntentionAction { available = true; } else if (parent instanceof PyFromImportStatement) { - PyFromImportStatement from_import = (PyFromImportStatement)parent; - final int relative_level = from_import.getRelativeLevel(); - if (from_import.isValid() && relative_level > 0 && from_import.getImportSource() == null) { - myRelativeLevel = relative_level; + final PyFromImportStatement fromImport = (PyFromImportStatement)parent; + final int relativeLevel = fromImport.getRelativeLevel(); + if (fromImport.isValid() && relativeLevel > 0 && fromImport.getImportSource() == null) { + myRelativeLevel = relativeLevel; available = true; } } @@ -92,25 +93,27 @@ public class ImportToImportFromIntention implements IntentionAction { assert myImportElement != null : "isAvailable() must have returned true, but myImportElement is null"; // usages of imported name are qualifiers; what they refer to? - PyReferenceExpression reference = myImportElement.getImportReferenceExpression(); - if (reference != null) { - myModuleName = PyPsiUtils.toPath(reference); + final PyReferenceExpression importReference = myImportElement.getImportReferenceExpression(); + if (importReference != null) { + myModuleName = PyPsiUtils.toPath(importReference); myQualifierName = myImportElement.getVisibleName(); - myReferee = reference.getReference().resolve(); + myReferee = importReference.getReference().resolve(); myHasModuleReference = false; if (myReferee != null && myModuleName != null && myQualifierName != null) { final Collection<PsiReference> references = new ArrayList<PsiReference>(); PsiTreeUtil.processElements(file, new PsiElementProcessor() { public boolean execute(@NotNull PsiElement element) { if (element instanceof PyReferenceExpression && PsiTreeUtil.getParentOfType(element, PyImportElement.class) == null) { - PyReferenceExpression ref = (PyReferenceExpression)element; + final PyReferenceExpression ref = (PyReferenceExpression)element; if (myQualifierName.equals(PyPsiUtils.toPath(ref))) { // filter out other names that might resolve to our target - PsiElement parent_elt = ref.getParent(); - if (parent_elt instanceof PyQualifiedExpression) { // really qualified by us, not just referencing? - PsiElement resolved = ref.getReference().resolve(); + final PsiElement parentElt = ref.getParent(); + if (parentElt instanceof PyQualifiedExpression) { // really qualified by us, not just referencing? + final PsiElement resolved = ref.getReference().resolve(); if (resolved == myReferee) references.add(ref.getReference()); } - else myHasModuleReference = true; + else { + myHasModuleReference = true; + } } } return true; @@ -123,68 +126,70 @@ public class ImportToImportFromIntention implements IntentionAction { public void invoke() { assert myImportElement != null : "isAvailable() must have returned true, but myImportElement is null"; - PyUtil.sure(myImportElement.getImportReferenceExpression()); - Project project = myImportElement.getProject(); + sure(myImportElement.getImportReferenceExpression()); + final Project project = myImportElement.getProject(); + + final PyElementGenerator generator = PyElementGenerator.getInstance(project); + final LanguageLevel languageLevel = LanguageLevel.forElement(myImportElement); // usages of imported name are qualifiers; what they refer to? try { // remember names and make them drop qualifiers - Set<String> used_names = new HashSet<String>(); + final Set<String> usedNames = new HashSet<String>(); for (PsiReference ref : myReferences) { - PsiElement elt = ref.getElement(); - PsiElement parent_elt = elt.getParent(); - used_names.add(sure(PyUtil.sure(parent_elt).getLastChild()).getText()); // TODO: find ident node more properly + final PsiElement elt = ref.getElement(); + final PsiElement parentElt = elt.getParent(); + // TODO: find ident node more properly + final String nameUsed = sure(sure(parentElt).getLastChild()).getText(); + usedNames.add(nameUsed); if (!FileModificationService.getInstance().preparePsiElementForWrite(elt)) { return; } - PsiElement next_elt = elt.getNextSibling(); - if (next_elt != null && ".".equals(next_elt.getText())) next_elt.delete(); - elt.delete(); + assert parentElt instanceof PyReferenceExpression; + final PyElement newReference = generator.createExpressionFromText(languageLevel, nameUsed); + parentElt.replace(newReference); } // create a separate import stmt for the module - PsiElement importer = myImportElement.getParent(); - PyStatement import_statement; - PyImportElement[] import_elements; + final PsiElement importer = myImportElement.getParent(); + final PyStatement importStatement; + final PyImportElement[] importElements; if (importer instanceof PyImportStatement) { - import_statement = (PyImportStatement)importer; - import_elements = ((PyImportStatement)import_statement).getImportElements(); + importStatement = (PyImportStatement)importer; + importElements = ((PyImportStatement)importStatement).getImportElements(); } else if (importer instanceof PyFromImportStatement) { - import_statement = (PyFromImportStatement)importer; - import_elements = ((PyFromImportStatement)import_statement).getImportElements(); + importStatement = (PyFromImportStatement)importer; + importElements = ((PyFromImportStatement)importStatement).getImportElements(); } else { throw new IncorrectOperationException("Not an import at all"); } - PyElementGenerator generator = PyElementGenerator.getInstance(project); - StringBuilder builder = new StringBuilder("from ").append(getDots()).append(myModuleName).append(" import "); - builder.append(StringUtil.join(used_names, ", ")); - PyFromImportStatement from_import_stmt = - generator.createFromText(LanguageLevel.getDefault(), PyFromImportStatement.class, builder.toString()); - PsiElement parent = import_statement.getParent(); + final PyFromImportStatement newImportStatement = + generator.createFromImportStatement(languageLevel, getDots() + myModuleName, StringUtil.join(usedNames, ", "), null); + final PsiElement parent = importStatement.getParent(); sure(parent); sure(parent.isValid()); - if (import_elements.length == 1) { + if (importElements.length == 1) { if (myHasModuleReference) { - parent.addAfter(from_import_stmt, import_statement); // add 'import from': we need the module imported as is + parent.addAfter(newImportStatement, importStatement); // add 'import from': we need the module imported as is } else { // replace entire existing import - sure(parent.getNode()).replaceChild(sure(import_statement.getNode()), sure(from_import_stmt.getNode())); + sure(parent.getNode()).replaceChild(sure(importStatement.getNode()), sure(newImportStatement.getNode())); // import_statement.replace(from_import_stmt); } } else { if (!myHasModuleReference) { // cut the module out of import, add a from-import. - for (PyImportElement pie : import_elements) { + for (PyImportElement pie : importElements) { if (pie == myImportElement) { PyUtil.removeListNode(pie); break; } } } - parent.addAfter(from_import_stmt, import_statement); + parent.addAfter(newImportStatement, importStatement); } } catch (IncorrectOperationException ignored) { @@ -193,18 +198,24 @@ public class ImportToImportFromIntention implements IntentionAction { } + @NotNull public String getText() { - String module_name = "?"; + String moduleName = "?"; if (myImportElement != null) { - PyReferenceExpression reference = myImportElement.getImportReferenceExpression(); - if (reference != null) module_name = PyPsiUtils.toPath(reference); + final PyReferenceExpression reference = myImportElement.getImportReferenceExpression(); + if (reference != null) { + moduleName = PyPsiUtils.toPath(reference); + } } - return PyBundle.message("INTN.convert.to.from.$0.import.$1", getDots()+module_name, "..."); + return PyBundle.message("INTN.convert.to.from.$0.import.$1", getDots() + moduleName, "..."); } + @NotNull private String getDots() { String dots = ""; - for (int i=0; i<myRelativeLevel; i+=1) dots += "."; // this generally runs 1-2 times, so it's cheaper than allocating a StringBuilder + for (int i = 0; i < myRelativeLevel; i += 1) { + dots += "."; // this generally runs 1-2 times, so it's cheaper than allocating a StringBuilder + } return dots; } } @@ -222,10 +233,15 @@ public class ImportToImportFromIntention implements IntentionAction { } @Nullable - private static PyImportElement findImportElement(Editor editor, PsiFile file) { - PyImportElement import_elt = PsiTreeUtil.getParentOfType(file.findElementAt(editor.getCaretModel().getOffset()), PyImportElement.class); - if (import_elt != null && import_elt.isValid()) return import_elt; - else return null; + private static PyImportElement findImportElement(@NotNull Editor editor, @NotNull PsiFile file) { + final PsiElement elementAtCaret = file.findElementAt(editor.getCaretModel().getOffset()); + final PyImportElement importElement = PsiTreeUtil.getParentOfType(elementAtCaret, PyImportElement.class); + if (importElement != null && importElement.isValid()) { + return importElement; + } + else { + return null; + } } public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { @@ -233,7 +249,7 @@ public class ImportToImportFromIntention implements IntentionAction { return false; } - IntentionState state = new IntentionState(editor, file); + final IntentionState state = new IntentionState(editor, file); if (state.isAvailable()) { myText = state.getText(); return true; @@ -242,7 +258,7 @@ public class ImportToImportFromIntention implements IntentionAction { } public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException { - IntentionState state = new IntentionState(editor, file); + final IntentionState state = new IntentionState(editor, file); state.invoke(); } diff --git a/python/src/com/jetbrains/python/codeInsight/intentions/PyGenerateDocstringIntention.java b/python/src/com/jetbrains/python/codeInsight/intentions/PyGenerateDocstringIntention.java index 7acfc9716fea..ca7ec65f9908 100644 --- a/python/src/com/jetbrains/python/codeInsight/intentions/PyGenerateDocstringIntention.java +++ b/python/src/com/jetbrains/python/codeInsight/intentions/PyGenerateDocstringIntention.java @@ -69,6 +69,7 @@ public class PyGenerateDocstringIntention extends BaseIntentionAction { if (function == null || statementList != null) { return false; } + if (!elementAt.equals(function.getNameNode())) return false; return isAvailableForFunction(project, function); } diff --git a/python/src/com/jetbrains/python/codeInsight/override/PyOverrideImplementUtil.java b/python/src/com/jetbrains/python/codeInsight/override/PyOverrideImplementUtil.java index 48a42753ebe5..e11f3e01e916 100644 --- a/python/src/com/jetbrains/python/codeInsight/override/PyOverrideImplementUtil.java +++ b/python/src/com/jetbrains/python/codeInsight/override/PyOverrideImplementUtil.java @@ -143,7 +143,7 @@ public class PyOverrideImplementUtil { return; } new WriteCommandAction(pyClass.getProject(), pyClass.getContainingFile()) { - protected void run(final Result result) throws Throwable { + protected void run(@NotNull final Result result) throws Throwable { write(pyClass, membersToOverride, editor, implement); } }.execute(); @@ -173,9 +173,7 @@ public class PyOverrideImplementUtil { PyPsiUtils.removeRedundantPass(statementList); if (element != null) { final PyStatementList targetStatementList = element.getStatementList(); - final int start = targetStatementList != null - ? targetStatementList.getTextRange().getStartOffset() - : element.getTextRange().getStartOffset(); + final int start = targetStatementList.getTextRange().getStartOffset(); editor.getCaretModel().moveToOffset(start); editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE); editor.getSelectionModel().setSelection(start, element.getTextRange().getEndOffset()); @@ -238,7 +236,7 @@ public class PyOverrideImplementUtil { PsiElement outerClass = PsiTreeUtil.getParentOfType(pyClass, PyClass.class, true, PyFunction.class); String className = pyClass.getName(); final List<String> nameResult = Lists.newArrayList(className); - while(outerClass instanceof PyClass) { + while(outerClass != null) { nameResult.add(0, ((PyClass)outerClass).getName()); outerClass = PsiTreeUtil.getParentOfType(outerClass, PyClass.class, true, PyFunction.class); } diff --git a/python/src/com/jetbrains/python/console/PyConsoleOptions.java b/python/src/com/jetbrains/python/console/PyConsoleOptions.java index 59ae6875d1e9..2f8eb5ee3949 100644 --- a/python/src/com/jetbrains/python/console/PyConsoleOptions.java +++ b/python/src/com/jetbrains/python/console/PyConsoleOptions.java @@ -146,6 +146,7 @@ public class PyConsoleOptions implements PersistentStateComponent<PyConsoleOptio form.setUseModuleSdk(myUseModuleSdk); form.addContentRoots(myAddContentRoots); form.addSourceRoots(myAddSourceRoots); + boolean moduleWasAutoselected = false; if (form.isUseModuleSdk() != myUseModuleSdk) { myUseModuleSdk = form.isUseModuleSdk(); diff --git a/python/src/com/jetbrains/python/console/PyDebugConsoleBuilder.java b/python/src/com/jetbrains/python/console/PyDebugConsoleBuilder.java index a99b9332c00d..7fbd432ebf2c 100644 --- a/python/src/com/jetbrains/python/console/PyDebugConsoleBuilder.java +++ b/python/src/com/jetbrains/python/console/PyDebugConsoleBuilder.java @@ -37,7 +37,7 @@ public class PyDebugConsoleBuilder extends TextConsoleBuilder { public PyDebugConsoleBuilder(final Project project, @Nullable Sdk sdk) { myProject = project; - this.mySdk = sdk; + mySdk = sdk; } public ConsoleView getConsole() { diff --git a/python/src/com/jetbrains/python/console/PydevConsoleRunner.java b/python/src/com/jetbrains/python/console/PydevConsoleRunner.java index b3ebacd83ae3..c710fd0abf4e 100644 --- a/python/src/com/jetbrains/python/console/PydevConsoleRunner.java +++ b/python/src/com/jetbrains/python/console/PydevConsoleRunner.java @@ -125,10 +125,10 @@ public class PydevConsoleRunner extends AbstractConsoleRunnerWithHistory<PythonC private Sdk mySdk; @NotNull private CommandLineArgumentsProvider myCommandLineArgumentsProvider; - private int[] myPorts; + protected int[] myPorts; private PydevConsoleCommunication myPydevConsoleCommunication; private PyConsoleProcessHandler myProcessHandler; - private PydevConsoleExecuteActionHandler myConsoleExecuteActionHandler; + protected PydevConsoleExecuteActionHandler myConsoleExecuteActionHandler; private List<ConsoleListener> myConsoleListeners = ContainerUtil.createLockFreeCopyOnWriteList(); private final PyConsoleType myConsoleType; private Map<String, String> myEnvironmentVariables; @@ -146,9 +146,9 @@ public class PydevConsoleRunner extends AbstractConsoleRunnerWithHistory<PythonC private String myConsoleTitle = null; public PydevConsoleRunner(@NotNull final Project project, - @NotNull Sdk sdk, @NotNull final PyConsoleType consoleType, - @Nullable final String workingDir, - Map<String, String> environmentVariables, String ... statementsToExecute) { + @NotNull Sdk sdk, @NotNull final PyConsoleType consoleType, + @Nullable final String workingDir, + Map<String, String> environmentVariables, String... statementsToExecute) { super(project, consoleType.getTitle(), workingDir); mySdk = sdk; myConsoleType = consoleType; @@ -281,6 +281,30 @@ public class PydevConsoleRunner extends AbstractConsoleRunnerWithHistory<PythonC return actions; } + public void runSync() { + myPorts = findAvailablePorts(getProject(), myConsoleType); + + assert myPorts != null; + + myCommandLineArgumentsProvider = createCommandLineArgumentsProvider(mySdk, myEnvironmentVariables, myPorts); + + try { + super.initAndRun(); + } + catch (ExecutionException e) { + LOG.warn("Error running console", e); + ExecutionHelper.showErrors(getProject(), Arrays.<Exception>asList(e), "Python Console", null); + } + + ProgressManager.getInstance().run(new Task.Backgroundable(getProject(), "Connecting to console", false) { + @Override + public void run(@NotNull final ProgressIndicator indicator) { + indicator.setText("Connecting to console..."); + connect(myStatementsToExecute); + } + }); + } + public void run() { UIUtil.invokeAndWaitIfNeeded(new Runnable() { @Override @@ -337,9 +361,9 @@ public class PydevConsoleRunner extends AbstractConsoleRunnerWithHistory<PythonC return ports; } - private static CommandLineArgumentsProvider createCommandLineArgumentsProvider(final Sdk sdk, - final Map<String, String> environmentVariables, - int[] ports) { + protected CommandLineArgumentsProvider createCommandLineArgumentsProvider(final Sdk sdk, + final Map<String, String> environmentVariables, + int[] ports) { final ArrayList<String> args = new ArrayList<String>(); args.add(sdk.getHomePath()); final String versionString = sdk.getVersionString(); @@ -518,8 +542,11 @@ public class PydevConsoleRunner extends AbstractConsoleRunnerWithHistory<PythonC public void initAndRun(final String... statements2execute) throws ExecutionException { super.initAndRun(); - if (handshake()) { + connect(statements2execute); + } + public void connect(final String[] statements2execute) { + if (handshake()) { ApplicationManager.getApplication().invokeLater(new Runnable() { @Override diff --git a/python/src/com/jetbrains/python/debugger/PyDebugProcess.java b/python/src/com/jetbrains/python/debugger/PyDebugProcess.java index cff49fb1f0d8..cdea41298a4e 100644 --- a/python/src/com/jetbrains/python/debugger/PyDebugProcess.java +++ b/python/src/com/jetbrains/python/debugger/PyDebugProcess.java @@ -17,6 +17,7 @@ package com.jetbrains.python.debugger; import com.google.common.collect.Lists; import com.google.common.collect.Maps; +import com.intellij.execution.console.DuplexConsoleView; import com.intellij.execution.process.ProcessEvent; import com.intellij.execution.process.ProcessHandler; import com.intellij.execution.process.ProcessListener; @@ -35,6 +36,7 @@ import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.Pair; import com.intellij.remote.RemoteProcessHandlerBase; +import com.intellij.util.ui.UIUtil; import com.intellij.xdebugger.*; import com.intellij.xdebugger.breakpoints.XBreakpoint; import com.intellij.xdebugger.breakpoints.XBreakpointHandler; @@ -43,6 +45,7 @@ import com.intellij.xdebugger.breakpoints.XLineBreakpoint; import com.intellij.xdebugger.evaluation.XDebuggerEditorsProvider; import com.intellij.xdebugger.frame.XValueChildrenList; import com.intellij.xdebugger.stepping.XSmartStepIntoHandler; +import com.jetbrains.python.console.PythonDebugLanguageConsoleView; import com.jetbrains.python.console.pydev.PydevCompletionVariant; import com.jetbrains.python.debugger.pydev.*; import com.jetbrains.python.run.PythonProcessHandler; @@ -84,6 +87,7 @@ public class PyDebugProcess extends XDebugProcess implements IPyDebugProcess, Pr private final XSmartStepIntoHandler<?> mySmartStepIntoHandler; private boolean myWaitingForConnection = false; private PyStackFrame myStackFrameBeforeResume; + private PyStackFrame myConsoleContextFrame = null; public PyDebugProcess(final @NotNull XDebugSession session, @NotNull final ServerSocket serverSocket, @@ -285,6 +289,19 @@ public class PyDebugProcess extends XDebugProcess implements IPyDebugProcess, Pr PySignatureCacheManager.getInstance(getSession().getProject()).recordSignature(myPositionConverter.convertSignature(signature)); } + @Override + public void showConsole(PyThreadInfo thread) { + myConsoleContextFrame = new PyExecutionStack(this, thread).getTopFrame(); + if (myExecutionConsole instanceof PythonDebugLanguageConsoleView) { + UIUtil.invokeLaterIfNeeded(new Runnable() { + @Override + public void run() { + ((PythonDebugLanguageConsoleView)myExecutionConsole).enableConsole(false); + } + }); + } + } + protected void afterConnect() { } @@ -545,6 +562,11 @@ public class PyDebugProcess extends XDebugProcess implements IPyDebugProcess, Pr } final PyStackFrame frame = (PyStackFrame)getSession().getCurrentStackFrame(); + + if (frame == null && myConsoleContextFrame != null) { + return myConsoleContextFrame; + } + if (frame == null) { throw new PyDebuggerException("Process is running"); } diff --git a/python/src/com/jetbrains/python/debugger/PyExecutionStack.java b/python/src/com/jetbrains/python/debugger/PyExecutionStack.java index ed48f09a99ca..7d9aba3608c2 100644 --- a/python/src/com/jetbrains/python/debugger/PyExecutionStack.java +++ b/python/src/com/jetbrains/python/debugger/PyExecutionStack.java @@ -37,7 +37,7 @@ public class PyExecutionStack extends XExecutionStack { } @Override - public XStackFrame getTopFrame() { + public PyStackFrame getTopFrame() { if (myTopFrame == null) { final List<PyStackFrameInfo> frames = myThreadInfo.getFrames(); if (frames != null) { diff --git a/python/src/com/jetbrains/python/documentation/PythonDocumentationProvider.java b/python/src/com/jetbrains/python/documentation/PythonDocumentationProvider.java index 55bc1e4c5446..913ed65f1e53 100644 --- a/python/src/com/jetbrains/python/documentation/PythonDocumentationProvider.java +++ b/python/src/com/jetbrains/python/documentation/PythonDocumentationProvider.java @@ -34,6 +34,7 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.QualifiedName; import com.intellij.util.Function; import com.jetbrains.python.PyNames; import com.jetbrains.python.codeInsight.PyCodeInsightSettings; @@ -44,13 +45,16 @@ import com.jetbrains.python.debugger.PySignatureCacheManager; import com.jetbrains.python.debugger.PySignatureUtil; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.impl.PyBuiltinCache; -import com.intellij.psi.util.QualifiedName; import com.jetbrains.python.psi.resolve.QualifiedNameFinder; -import com.jetbrains.python.psi.types.*; +import com.jetbrains.python.psi.types.PyClassType; +import com.jetbrains.python.psi.types.PyType; +import com.jetbrains.python.psi.types.PyTypeParser; +import com.jetbrains.python.psi.types.TypeEvalContext; import com.jetbrains.python.toolbox.ChainIterable; import com.jetbrains.python.toolbox.FP; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.methods.HeadMethod; +import org.apache.commons.httpclient.params.HttpConnectionManagerParams; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -414,8 +418,10 @@ public class PythonDocumentationProvider extends AbstractDocumentationProvider i return true; } HttpClient client = new HttpClient(); - client.setTimeout(5 * 1000); - client.setConnectionTimeout(5 * 1000); + HttpConnectionManagerParams params = client.getHttpConnectionManager().getParams(); + params.setSoTimeout(5 * 1000); + params.setConnectionTimeout(5 * 1000); + try { HeadMethod method = new HeadMethod(url); int rc = client.executeMethod(method); @@ -618,9 +624,7 @@ public class PythonDocumentationProvider extends AbstractDocumentationProvider i if (checkReturn) { RaiseVisitor visitor = new RaiseVisitor(); PyStatementList statementList = element.getStatementList(); - if (statementList != null) { - statementList.accept(visitor); - } + statementList.accept(visitor); if (visitor.myHasReturn) { builder.append(prefix).append("return:").append(offset); if (PyCodeInsightSettings.getInstance().INSERT_TYPE_DOCSTUB) { diff --git a/python/src/com/jetbrains/python/documentation/doctest/PyDocstringVisitorFilter.java b/python/src/com/jetbrains/python/documentation/doctest/PyDocstringVisitorFilter.java index e79d52cb1685..71af6aa30fea 100644 --- a/python/src/com/jetbrains/python/documentation/doctest/PyDocstringVisitorFilter.java +++ b/python/src/com/jetbrains/python/documentation/doctest/PyDocstringVisitorFilter.java @@ -43,7 +43,7 @@ public class PyDocstringVisitorFilter implements PythonVisitorFilter { visitorClass == PyByteLiteralInspection.class || visitorClass == PyNonAsciiCharInspection.class || visitorClass == PyPackageRequirementsInspection.class || visitorClass == PyMandatoryEncodingInspection.class || visitorClass == PyInterpreterInspection.class || visitorClass == PyDocstringTypesInspection.class || - visitorClass == PySingleQuotedDocstringInspection.class) { + visitorClass == PySingleQuotedDocstringInspection.class || visitorClass == PyClassHasNoInitInspection.class) { return false; } //annotators diff --git a/python/src/com/jetbrains/python/formatter/PythonFormattingModelBuilder.java b/python/src/com/jetbrains/python/formatter/PythonFormattingModelBuilder.java index aee9e53adff6..4b71d6abec25 100644 --- a/python/src/com/jetbrains/python/formatter/PythonFormattingModelBuilder.java +++ b/python/src/com/jetbrains/python/formatter/PythonFormattingModelBuilder.java @@ -46,13 +46,13 @@ public class PythonFormattingModelBuilder implements FormattingModelBuilderEx, C public FormattingModel createModel(@NotNull PsiElement element, @NotNull CodeStyleSettings settings, @NotNull FormattingMode mode) { + final ASTNode fileNode = element.getContainingFile().getNode(); if (DUMP_FORMATTING_AST) { - ASTNode fileNode = element.getContainingFile().getNode(); System.out.println("AST tree for " + element.getContainingFile().getName() + ":"); printAST(fileNode, 0); } final PyBlockContext context = new PyBlockContext(settings, createSpacingBuilder(settings), mode); - final PyBlock block = new PyBlock(null, element.getNode(), null, Indent.getNoneIndent(), null, context); + final PyBlock block = new PyBlock(null, fileNode, null, Indent.getNoneIndent(), null, context); if (DUMP_FORMATTING_AST) { FormattingModelDumper.dumpFormattingModel(block, 2, System.out); } diff --git a/python/src/com/jetbrains/python/inspections/PyCompatibilityInspection.java b/python/src/com/jetbrains/python/inspections/PyCompatibilityInspection.java index c8a40a095134..0f69f4f72a56 100644 --- a/python/src/com/jetbrains/python/inspections/PyCompatibilityInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyCompatibilityInspection.java @@ -200,9 +200,7 @@ public class PyCompatibilityInspection extends PyInspection { } for (int i = 0; i != myVersionsToProcess.size(); ++i) { LanguageLevel languageLevel = myVersionsToProcess.get(i); - PsiFile file = resolved.getContainingFile(); - VirtualFile virtualFile = file.getVirtualFile(); - if (virtualFile != null && ind.isInLibraryClasses(virtualFile)) { + if (PyBuiltinCache.getInstance(resolved).isBuiltin(resolved)) { if (!"print".equals(name) && !myUsedImports.contains(name) && UnsupportedFeaturesUtil.BUILTINS.get(languageLevel).contains(name)) { len = appendLanguageLevel(message, len, languageLevel); } diff --git a/python/src/com/jetbrains/python/inspections/PyStatementEffectInspection.java b/python/src/com/jetbrains/python/inspections/PyStatementEffectInspection.java index 0cc7a4abd140..a521ed76d92f 100644 --- a/python/src/com/jetbrains/python/inspections/PyStatementEffectInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyStatementEffectInspection.java @@ -72,9 +72,6 @@ public class PyStatementEffectInspection extends PyInspection { final PyTryPart tryPart = PsiTreeUtil.getParentOfType(node, PyTryPart.class); if (tryPart != null) { final PyStatementList statementList = tryPart.getStatementList(); - if (statementList == null) { - return; - } if (statementList.getStatements().length == 1 && statementList.getStatements()[0] == node) { return; } diff --git a/python/src/com/jetbrains/python/inspections/quickfix/PyDefaultArgumentQuickFix.java b/python/src/com/jetbrains/python/inspections/quickfix/PyDefaultArgumentQuickFix.java index 31723d3aab4e..db47f17f8ea8 100644 --- a/python/src/com/jetbrains/python/inspections/quickfix/PyDefaultArgumentQuickFix.java +++ b/python/src/com/jetbrains/python/inspections/quickfix/PyDefaultArgumentQuickFix.java @@ -61,29 +61,36 @@ public class PyDefaultArgumentQuickFix implements LocalQuickFix { PyStatementList list = function.getStatementList(); PyParameterList paramList = function.getParameterList(); - StringBuilder str = new StringBuilder("def foo("); + final StringBuilder functionText = new StringBuilder("def foo("); int size = paramList.getParameters().length; for (int i = 0; i != size; ++i) { PyParameter p = paramList.getParameters()[i]; if (p == param) - str.append(defName).append("=None"); + functionText.append(defName).append("=None"); else - str.append(p.getText()); + functionText.append(p.getText()); if (i != size-1) - str.append(", "); + functionText.append(", "); + } + functionText.append("):\n\tif not ").append(defName).append(":\n\t\t").append(defName).append(" = ").append(defaultValue.getText()); + final PyStatement[] statements = list.getStatements(); + PyStatement firstStatement = statements.length > 0 ? statements[0] : null; + PyFunction newFunction = elementGenerator.createFromText(LanguageLevel.forElement(function), PyFunction.class, + functionText.toString()); + if (firstStatement == null) { + function.replace(newFunction); + } + else { + final PyStatement ifStatement = newFunction.getStatementList().getStatements()[0]; + PyStringLiteralExpression docString = function.getDocStringExpression(); + if (docString != null) + list.addAfter(ifStatement, firstStatement); + else { + list.addBefore(ifStatement, firstStatement); + } + paramList.replace(elementGenerator.createFromText(LanguageLevel.forElement(defaultValue), + PyFunction.class, functionText.toString()).getParameterList()); } - str.append("):\n\tpass"); - PyIfStatement ifStatement = elementGenerator.createFromText(LanguageLevel.forElement(function), PyIfStatement.class, - "if not " + defName + ": " + defName + " = " + defaultValue.getText()); - - PyStatement firstStatement = list.getStatements()[0]; - PyStringLiteralExpression docString = function.getDocStringExpression(); - if (docString != null) - list.addAfter(ifStatement, firstStatement); - else - list.addBefore(ifStatement, firstStatement); - paramList.replace(elementGenerator.createFromText(LanguageLevel.forElement(defaultValue), - PyFunction.class, str.toString()).getParameterList()); } } } diff --git a/python/src/com/jetbrains/python/inspections/quickfix/StatementEffectFunctionCallQuickFix.java b/python/src/com/jetbrains/python/inspections/quickfix/StatementEffectFunctionCallQuickFix.java index 95daf466172e..43f4f0394015 100644 --- a/python/src/com/jetbrains/python/inspections/quickfix/StatementEffectFunctionCallQuickFix.java +++ b/python/src/com/jetbrains/python/inspections/quickfix/StatementEffectFunctionCallQuickFix.java @@ -18,14 +18,14 @@ package com.jetbrains.python.inspections.quickfix; import com.intellij.codeInspection.LocalQuickFix; import com.intellij.codeInspection.ProblemDescriptor; import com.intellij.openapi.project.Project; +import com.intellij.psi.PsiComment; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiWhiteSpace; +import com.intellij.psi.impl.source.tree.LeafPsiElement; import com.jetbrains.python.PyBundle; import com.jetbrains.python.PyNames; -import com.jetbrains.python.psi.LanguageLevel; -import com.jetbrains.python.psi.PyElementGenerator; -import com.jetbrains.python.psi.PyExpression; -import com.jetbrains.python.psi.PyReferenceExpression; +import com.jetbrains.python.PyTokenTypes; +import com.jetbrains.python.psi.*; import org.jetbrains.annotations.NotNull; /** @@ -48,15 +48,17 @@ public class StatementEffectFunctionCallQuickFix implements LocalQuickFix { PsiElement expression = descriptor.getPsiElement(); if (expression != null && expression.isWritable() && expression instanceof PyReferenceExpression) { final String expressionText = expression.getText(); - if (PyNames.PRINT.equals(expressionText) || PyNames.EXEC.equals(expressionText)) - replacePrintExec(expression); + if (PyNames.PRINT.equals(expressionText)) + replacePrint(expression); + else if (PyNames.EXEC.equals(expressionText)) + replaceExec(expression); else expression.replace(PyElementGenerator.getInstance(project).createCallExpression(LanguageLevel.forElement(expression), expressionText)); } } - private static void replacePrintExec(@NotNull final PsiElement expression) { + private static void replaceExec(@NotNull final PsiElement expression) { final PyElementGenerator elementGenerator = PyElementGenerator.getInstance(expression.getProject()); final String expressionText = expression.getText(); final StringBuilder stringBuilder = new StringBuilder(expressionText + " ("); @@ -77,14 +79,88 @@ public class StatementEffectFunctionCallQuickFix implements LocalQuickFix { RemoveUnnecessaryBackslashQuickFix.removeBackSlash(next); if (whiteSpace != null) whiteSpace.delete(); + if (next == null) { + stringBuilder.append(")"); + expression.replace(elementGenerator.createFromText(LanguageLevel.forElement(expression), PyExpression.class, + stringBuilder.toString())); + return; + } + if (next instanceof PyExpressionStatement) { + final PyExpression expr = ((PyExpressionStatement)next).getExpression(); + if (expr instanceof PyBinaryExpression) { + addInArguments(stringBuilder, (PyBinaryExpression)expr); + } + else if (expr instanceof PyTupleExpression) { + final PyExpression[] elements = ((PyTupleExpression)expr).getElements(); + if (elements.length > 1) { + if (elements[0] instanceof PyBinaryExpression) { + addInArguments(stringBuilder, (PyBinaryExpression)elements[0]); + } + stringBuilder.append(", "); + stringBuilder.append(elements[1].getText()); + } + } + else { + stringBuilder.append(next.getText()); + } + } + else { + stringBuilder.append(next.getText()); + } + next.delete(); + stringBuilder.append(")"); + expression.replace(elementGenerator.createFromText(LanguageLevel.forElement(expression), PyExpression.class, + stringBuilder.toString())); + } + + private static void addInArguments(@NotNull final StringBuilder stringBuilder, @NotNull final PyBinaryExpression binaryExpression) { + final PsiElement operator = binaryExpression.getPsiOperator(); + if (operator instanceof LeafPsiElement && ((LeafPsiElement)operator).getElementType() == PyTokenTypes.IN_KEYWORD) { + stringBuilder.append(binaryExpression.getLeftExpression().getText()); + stringBuilder.append(", "); + final PyExpression rightExpression = binaryExpression.getRightExpression(); + if (rightExpression != null) + stringBuilder.append(rightExpression.getText()); + } + } + + private static void replacePrint(@NotNull final PsiElement expression) { + final PyElementGenerator elementGenerator = PyElementGenerator.getInstance(expression.getProject()); + final String expressionText = expression.getText(); + final StringBuilder stringBuilder = new StringBuilder(expressionText + " ("); + + final PsiElement whiteSpace = expression.getContainingFile().findElementAt(expression.getTextOffset() + expression.getTextLength()); + PsiElement next = null; + if (whiteSpace instanceof PsiWhiteSpace) { + final String whiteSpaceText = whiteSpace.getText(); + if (!whiteSpaceText.contains("\n")) { + next = whiteSpace.getNextSibling(); + while (next instanceof PsiWhiteSpace && whiteSpaceText.contains("\\")) { + next = next.getNextSibling(); + } + } + } + else + next = whiteSpace; + + RemoveUnnecessaryBackslashQuickFix.removeBackSlash(next); + if (whiteSpace != null) whiteSpace.delete(); + String commentText = null; if (next != null) { - final String text = next.getText(); + final PsiElement lastChild = next.getLastChild(); + if (lastChild instanceof PsiComment) { + commentText = lastChild.getText(); + } + final String text = next instanceof PyExpressionStatement ? ((PyExpressionStatement)next).getExpression().getText() : next.getText(); + stringBuilder.append(text); - if (text.endsWith(",") && PyNames.PRINT.equals(expressionText)) - stringBuilder.append(" end=' '"); + if (text.endsWith(",")) stringBuilder.append(" end=' '"); next.delete(); } stringBuilder.append(")"); + if (commentText != null) { + stringBuilder.append(commentText); + } expression.replace(elementGenerator.createFromText(LanguageLevel.forElement(expression), PyExpression.class, stringBuilder.toString())); } diff --git a/python/src/com/jetbrains/python/inspections/unresolvedReference/PyUnresolvedReferencesInspection.java b/python/src/com/jetbrains/python/inspections/unresolvedReference/PyUnresolvedReferencesInspection.java index 4fd58629c516..b7523caf90e7 100644 --- a/python/src/com/jetbrains/python/inspections/unresolvedReference/PyUnresolvedReferencesInspection.java +++ b/python/src/com/jetbrains/python/inspections/unresolvedReference/PyUnresolvedReferencesInspection.java @@ -755,7 +755,7 @@ public class PyUnresolvedReferencesInspection extends PyInspection { return false; } - private static void addCreateMemberFromUsageFixes(PyType type, PsiReference reference, String refText, List<LocalQuickFix> actions) { + private void addCreateMemberFromUsageFixes(PyType type, PsiReference reference, String refText, List<LocalQuickFix> actions) { PsiElement element = reference.getElement(); if (type instanceof PyClassTypeImpl) { PyClass cls = ((PyClassType)type).getPyClass(); @@ -771,6 +771,7 @@ public class PyUnresolvedReferencesInspection extends PyInspection { else if (type instanceof PyModuleType) { PyFile file = ((PyModuleType)type).getModule(); actions.add(new AddFunctionQuickFix(refText, file.getName())); + addCreateClassFix(refText, element, actions); } } diff --git a/python/src/com/jetbrains/python/module/PyContentEntriesEditor.java b/python/src/com/jetbrains/python/module/PyContentEntriesEditor.java new file mode 100644 index 000000000000..8ea8b26c0b5f --- /dev/null +++ b/python/src/com/jetbrains/python/module/PyContentEntriesEditor.java @@ -0,0 +1,367 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.jetbrains.python.module; + +import com.intellij.openapi.Disposable; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.CustomShortcutSet; +import com.intellij.openapi.actionSystem.Presentation; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.options.ConfigurationException; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.roots.ContentEntry; +import com.intellij.openapi.roots.ContentFolder; +import com.intellij.openapi.roots.ModifiableRootModel; +import com.intellij.openapi.roots.impl.ContentEntryImpl; +import com.intellij.openapi.roots.impl.ContentFolderBaseImpl; +import com.intellij.openapi.roots.ui.configuration.*; +import com.intellij.openapi.roots.ui.configuration.actions.ContentEntryEditingAction; +import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.vfs.VfsUtilCore; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.pointers.VirtualFilePointer; +import com.intellij.openapi.vfs.pointers.VirtualFilePointerListener; +import com.intellij.openapi.vfs.pointers.VirtualFilePointerManager; +import com.intellij.ui.JBColor; +import com.intellij.util.EventDispatcher; +import com.intellij.util.containers.MultiMap; +import com.jetbrains.python.templateLanguages.TemplatesService; +import icons.PythonIcons; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jps.model.module.JpsModuleSourceRootType; + +import javax.swing.*; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.tree.TreeCellRenderer; +import java.awt.*; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.util.ArrayList; +import java.util.List; + +public class PyContentEntriesEditor extends CommonContentEntriesEditor { + private static final Color TEMPLATES_COLOR = JBColor.MAGENTA; + private final MultiMap<ContentEntry, VirtualFilePointer> myTemplateRoots = new MultiMap<ContentEntry, VirtualFilePointer>(); + private final Module myModule; + private Disposable myFilePointersDisposable; + + private final VirtualFilePointerListener DUMMY_LISTENER = new VirtualFilePointerListener() { + @Override + public void beforeValidityChanged(@NotNull VirtualFilePointer[] pointers) { + } + + @Override + public void validityChanged(@NotNull VirtualFilePointer[] pointers) { + } + }; + + public PyContentEntriesEditor(Module module, ModuleConfigurationState moduleConfigurationState, + JpsModuleSourceRootType<?>... rootTypes) { + super(module.getName(), moduleConfigurationState, rootTypes); + myModule = module; + reset(); + } + + @Override + protected ContentEntryTreeEditor createContentEntryTreeEditor(Project project) { + return new MyContentEntryTreeEditor(project, getEditHandlers()); + } + + @Override + protected List<ContentEntry> addContentEntries(VirtualFile[] files) { + List<ContentEntry> entries = super.addContentEntries(files); + addContentEntryPanels(entries.toArray(new ContentEntry[entries.size()])); + return entries; + } + + @Override + public void reset() { + if (myFilePointersDisposable != null) { + Disposer.dispose(myFilePointersDisposable); + } + myTemplateRoots.clear(); + + myFilePointersDisposable = Disposer.newDisposable(); + final TemplatesService instance = TemplatesService.getInstance(myModule); + if (instance != null) { + final List<VirtualFile> folders = instance.getTemplateFolders(); + for (VirtualFile folder : folders) { + ContentEntry contentEntry = findContentEntryForFile(folder); + if (contentEntry != null) { + myTemplateRoots.putValue(contentEntry, VirtualFilePointerManager.getInstance().create(folder, myFilePointersDisposable, + DUMMY_LISTENER)); + } + } + } + + if (myRootTreeEditor != null) { + ContentEntryEditor editor = myRootTreeEditor.getContentEntryEditor(); + if(editor!=null) editor.update(); + myRootTreeEditor.update(); + } + } + + @Nullable + private ContentEntry findContentEntryForFile(VirtualFile virtualFile) { + for (ContentEntry contentEntry : getModel().getContentEntries()) { + final VirtualFile file = contentEntry.getFile(); + if (file != null && VfsUtilCore.isAncestor(file, virtualFile, false)) { + return contentEntry; + } + } + return null; + } + + @Override + public void disposeUIResources() { + super.disposeUIResources(); + if (myFilePointersDisposable != null) { + Disposer.dispose(myFilePointersDisposable); + } + } + + @Override + public void apply() throws ConfigurationException { + super.apply(); + List<VirtualFile> templateRoots = getCurrentState(); + TemplatesService.getInstance(myModule).setTemplateFolders(templateRoots.toArray(new VirtualFile[templateRoots.size()])); + } + + private List<VirtualFile> getCurrentState() { + List<VirtualFile> result = new ArrayList<VirtualFile>(); + for (ContentEntry entry : myTemplateRoots.keySet()) { + for (VirtualFilePointer filePointer : myTemplateRoots.get(entry)) { + result.add(filePointer.getFile()); + } + } + return result; + } + + @Override + public boolean isModified() { + if (super.isModified()) return true; + final TemplatesService templatesService = TemplatesService.getInstance(myModule); + if (templatesService != null) { + List<VirtualFile> original = templatesService.getTemplateFolders(); + List<VirtualFile> current = getCurrentState(); + + if (!Comparing.haveEqualElements(original, current)) return true; + + } + return false; + } + + @Override + protected MyContentEntryEditor createContentEntryEditor(String contentEntryUrl) { + return new MyContentEntryEditor(contentEntryUrl, getEditHandlers()); + } + + protected class MyContentEntryEditor extends ContentEntryEditor { + private final EventDispatcher<ChangeListener> myEventDispatcher = EventDispatcher.create(ChangeListener.class); + + public MyContentEntryEditor(String contentEntryUrl, List<ModuleSourceRootEditHandler<?>> handlers) { + super(contentEntryUrl, handlers); + } + + @Override + protected ModifiableRootModel getModel() { + return PyContentEntriesEditor.this.getModel(); + } + + public void addListener(ChangeListener changeListener) { + myEventDispatcher.addListener(changeListener); + } + + public void removeListener(ChangeListener changeListener) { + myEventDispatcher.removeListener(changeListener); + } + + @Override + protected ContentRootPanel createContentRootPane() { + return new MyContentRootPanel(); + } + + @Override + public void deleteContentFolder(ContentEntry contentEntry, ContentFolder folder) { + if (folder instanceof TemplateRootFolder) { + final VirtualFile file = folder.getFile(); + if (file != null) { + removeTemplateRoot(file); + } + } + else { + super.deleteContentFolder(contentEntry, folder); + } + } + + public void addTemplateRoot(@NotNull final VirtualFile file) { + final VirtualFilePointer root = VirtualFilePointerManager.getInstance().create(file, myFilePointersDisposable, DUMMY_LISTENER); + myTemplateRoots.putValue(getContentEntry(), root); + myEventDispatcher.getMulticaster().stateChanged(new ChangeEvent(this)); + update(); + } + + public void removeTemplateRoot(@NotNull final VirtualFile file) { + final VirtualFilePointer root = getTemplateRoot(file); + if (root != null) { + myTemplateRoots.remove(getContentEntry(), root); + myEventDispatcher.getMulticaster().stateChanged(new ChangeEvent(this)); + update(); + } + } + + public boolean hasTemplateRoot(@NotNull final VirtualFile file) { + return getTemplateRoot(file) != null; + } + + @Nullable + public VirtualFilePointer getTemplateRoot(@NotNull final VirtualFile file) { + for (VirtualFilePointer filePointer : myTemplateRoots.get(getContentEntry())) { + if (Comparing.equal(filePointer.getFile(), file)) { + return filePointer; + } + } + return null; + } + + protected class MyContentRootPanel extends ContentRootPanel { + public MyContentRootPanel() { + super(MyContentEntryEditor.this, getEditHandlers()); + } + + @Override + @NotNull + protected ContentEntryImpl getContentEntry() { + //noinspection ConstantConditions + return (ContentEntryImpl)MyContentEntryEditor.this.getContentEntry(); + } + + @Override + protected void addFolderGroupComponents() { + super.addFolderGroupComponents(); + if (!myTemplateRoots.get(getContentEntry()).isEmpty()) { + final List<TemplateRootFolder> folders = new ArrayList<TemplateRootFolder>(myTemplateRoots.size()); + for (VirtualFilePointer root : myTemplateRoots.get(getContentEntry())) { + folders.add(new TemplateRootFolder(root, getContentEntry())); + } + final JComponent sourcesComponent = createFolderGroupComponent("Template Folders", + folders.toArray(new ContentFolder[folders.size()]), + TEMPLATES_COLOR, null); + this.add(sourcesComponent, new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1.0, 0.0, GridBagConstraints.NORTH, + GridBagConstraints.HORIZONTAL, new Insets(0, 0, 10, 0), 0, 0)); + } + } + } + } + + private static class MyContentEntryTreeEditor extends ContentEntryTreeEditor { + + private final ChangeListener myListener = new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + update(); + } + }; + + public MyContentEntryTreeEditor(Project project, List<ModuleSourceRootEditHandler<?>> handlers) { + super(project, handlers); + } + + @Override + public void setContentEntryEditor(ContentEntryEditor newEditor) { + PyContentEntriesEditor.MyContentEntryEditor existingEditor = getContentEntryEditor(); + if (Comparing.equal(existingEditor, newEditor)) { + return; + } + if (existingEditor != null) { + existingEditor.removeListener(myListener); + } + if (newEditor != null) { + ((PyContentEntriesEditor.MyContentEntryEditor)newEditor).addListener(myListener); + } + super.setContentEntryEditor(newEditor); + } + + @Override + public PyContentEntriesEditor.MyContentEntryEditor getContentEntryEditor() { + return (PyContentEntriesEditor.MyContentEntryEditor)super.getContentEntryEditor(); + } + + @Override + protected void createEditingActions() { + super.createEditingActions(); + + ContentEntryEditingAction a = new ContentEntryEditingAction(myTree) { + { + final Presentation templatePresentation = getTemplatePresentation(); + templatePresentation.setText("Templates"); + templatePresentation.setDescription("Template Folders"); + templatePresentation.setIcon(PythonIcons.Python.TemplateRoot); + } + + @Override + public boolean isSelected(AnActionEvent e) { + final VirtualFile[] selectedFiles = getSelectedFiles(); + return selectedFiles.length != 0 && getContentEntryEditor().hasTemplateRoot(selectedFiles[0]); + } + + @Override + public void setSelected(AnActionEvent e, boolean isSelected) { + final VirtualFile[] selectedFiles = getSelectedFiles(); + assert selectedFiles.length != 0; + + for (VirtualFile selectedFile : selectedFiles) { + boolean wasSelected = getContentEntryEditor().hasTemplateRoot(selectedFile); + if (isSelected) { + if (!wasSelected) { + getContentEntryEditor().addTemplateRoot(selectedFile); + } + } + else { + if (wasSelected) { + getContentEntryEditor().removeTemplateRoot(selectedFile); + } + } + } + } + }; + myEditingActionsGroup.add(a); + a.registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_R, InputEvent.ALT_MASK)), myTree); + } + + @Override + protected TreeCellRenderer getContentEntryCellRenderer() { + return new ContentEntryTreeCellRenderer(this, getEditHandlers()) { + @Override + protected Icon updateIcon(final ContentEntry entry, final VirtualFile file, final Icon originalIcon) { + if (getContentEntryEditor().hasTemplateRoot(file)) { + return PythonIcons.Python.TemplateRoot; + } + return super.updateIcon(entry, file, originalIcon); + } + }; + } + } + private static class TemplateRootFolder extends ContentFolderBaseImpl { + protected TemplateRootFolder(@NotNull VirtualFilePointer filePointer, @NotNull ContentEntryImpl contentEntry) { + super(filePointer, contentEntry); + } + } + +} diff --git a/python/src/com/jetbrains/python/psi/impl/PyAnnotationImpl.java b/python/src/com/jetbrains/python/psi/impl/PyAnnotationImpl.java index f3c806dac7c5..f8a4d482c2f2 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyAnnotationImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyAnnotationImpl.java @@ -16,14 +16,16 @@ package com.jetbrains.python.psi.impl; import com.intellij.lang.ASTNode; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiPolyVariantReference; import com.jetbrains.python.PyElementTypes; import com.jetbrains.python.psi.PyAnnotation; -import com.jetbrains.python.psi.PyClass; import com.jetbrains.python.psi.PyExpression; -import com.jetbrains.python.psi.PyReferenceExpression; import com.jetbrains.python.psi.stubs.PyAnnotationStub; +import com.jetbrains.python.psi.types.PyClassLikeType; +import com.jetbrains.python.psi.types.PyNoneType; +import com.jetbrains.python.psi.types.PyType; +import com.jetbrains.python.psi.types.TypeEvalContext; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * @author yole @@ -37,19 +39,26 @@ public class PyAnnotationImpl extends PyBaseElementImpl<PyAnnotationStub> implem super(stub, PyElementTypes.ANNOTATION); } + @Nullable @Override public PyExpression getValue() { return findChildByClass(PyExpression.class); } + @Nullable @Override - public PyClass resolveToClass() { - PyExpression expr = getValue(); - if (expr instanceof PyReferenceExpression) { - final PsiPolyVariantReference reference = ((PyReferenceExpression)expr).getReference(); - final PsiElement result = reference.resolve(); - if (result instanceof PyClass) { - return (PyClass) result; + public PyType getType(@NotNull TypeEvalContext context, @NotNull TypeEvalContext.Key key) { + final PyExpression value = getValue(); + if (value != null) { + final PyType type = context.getType(value); + if (type instanceof PyClassLikeType) { + final PyClassLikeType classType = (PyClassLikeType)type; + if (classType.isDefinition()) { + return classType.toInstance(); + } + } + else if (type instanceof PyNoneType) { + return type; } } return null; diff --git a/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java b/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java index 7538fbae2fd2..bb2104fc0c77 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java @@ -20,7 +20,6 @@ import com.intellij.lang.ASTNode; import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.NotNullLazyValue; -import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; import com.intellij.psi.scope.PsiScopeProcessor; import com.intellij.psi.search.LocalSearchScope; @@ -187,6 +186,11 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> implement } } } + // Heuristic: unfold Foo[Bar] to Foo for subscription expressions for superclasses + else if (expression instanceof PySubscriptionExpression) { + final PySubscriptionExpression subscriptionExpr = (PySubscriptionExpression)expression; + return subscriptionExpr.getOperand(); + } return expression; } @@ -237,27 +241,7 @@ public class PyClassImpl extends PyPresentableElementImpl<PyClassStub> implement @Nullable public String getQualifiedName() { - String name = getName(); - final PyClassStub stub = getStub(); - PsiElement ancestor = stub != null ? stub.getParentStub().getPsi() : getParent(); - while (!(ancestor instanceof PsiFile)) { - if (ancestor == null) return name; // can this happen? - if (ancestor instanceof PyClass) { - name = ((PyClass)ancestor).getName() + "." + name; - } - ancestor = stub != null ? ((StubBasedPsiElement)ancestor).getStub().getParentStub().getPsi() : ancestor.getParent(); - } - - PsiFile psiFile = ((PsiFile)ancestor).getOriginalFile(); - final PyFile builtins = PyBuiltinCache.getInstance(this).getBuiltinsFile(); - if (!psiFile.equals(builtins)) { - VirtualFile vFile = psiFile.getVirtualFile(); - if (vFile != null) { - final String packageName = QualifiedNameFinder.findShortestImportableName(this, vFile); - return packageName + "." + name; - } - } - return name; + return QualifiedNameFinder.getQualifiedName(this); } @Override diff --git a/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java b/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java index 2531b37bf8b2..86f84abb7fb6 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java @@ -184,11 +184,11 @@ public class PyFunctionImpl extends PyPresentableElementImpl<PyFunctionStub> imp } } if (context.maySwitchToAST(this) && LanguageLevel.forElement(this).isAtLeast(LanguageLevel.PYTHON30)) { - PyAnnotation anno = getAnnotation(); - if (anno != null) { - PyClass pyClass = anno.resolveToClass(); - if (pyClass != null) { - return new PyClassTypeImpl(pyClass, false); + final PyAnnotation annotation = getAnnotation(); + if (annotation != null) { + final PyType type = context.getType(annotation); + if (type != null) { + return type; } } } @@ -571,7 +571,7 @@ public class PyFunctionImpl extends PyPresentableElementImpl<PyFunctionStub> imp @Override public PyAnnotation getAnnotation() { - return findChildByClass(PyAnnotation.class); + return getStubOrPsiChild(PyElementTypes.ANNOTATION); } @NotNull @@ -699,21 +699,6 @@ public class PyFunctionImpl extends PyPresentableElementImpl<PyFunctionStub> imp @Nullable @Override public String getQualifiedName() { - String name = getName(); - if (name == null) { - return null; - } - PyClass containingClass = getContainingClass(); - if (containingClass != null) { - return containingClass.getQualifiedName() + "." + name; - } - if (PsiTreeUtil.getStubOrPsiParent(this) instanceof PyFile) { - VirtualFile virtualFile = getContainingFile().getVirtualFile(); - if (virtualFile != null) { - final String packageName = QualifiedNameFinder.findShortestImportableName(this, virtualFile); - return packageName + "." + name; - } - } - return null; + return QualifiedNameFinder.getQualifiedName(this); } } diff --git a/python/src/com/jetbrains/python/psi/impl/PyNamedParameterImpl.java b/python/src/com/jetbrains/python/psi/impl/PyNamedParameterImpl.java index bf792fef0dae..cc9178c07f9e 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyNamedParameterImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyNamedParameterImpl.java @@ -181,11 +181,11 @@ public class PyNamedParameterImpl extends PyPresentableElementImpl<PyNamedParame PyParameterList parameterList = (PyParameterList)parent; PyFunction func = parameterList.getContainingFunction(); if (func != null) { - PyAnnotation anno = getAnnotation(); - if (anno != null) { - final PyClass pyClass = anno.resolveToClass(); - if (pyClass != null) { - return new PyClassTypeImpl(pyClass, false); + final PyAnnotation annotation = getAnnotation(); + if (annotation != null) { + final PyType type = context.getType(annotation); + if (type != null) { + return type; } } StructuredDocString docString = func.getStructuredDocString(); diff --git a/python/src/com/jetbrains/python/psi/impl/PyTargetExpressionImpl.java b/python/src/com/jetbrains/python/psi/impl/PyTargetExpressionImpl.java index 545e8e86cba1..3805c8df525e 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyTargetExpressionImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyTargetExpressionImpl.java @@ -43,6 +43,7 @@ import com.jetbrains.python.psi.impl.references.PyQualifiedReference; import com.jetbrains.python.psi.impl.references.PyTargetReference; import com.jetbrains.python.psi.impl.stubs.CustomTargetExpressionStub; import com.jetbrains.python.psi.resolve.PyResolveContext; +import com.jetbrains.python.psi.resolve.QualifiedNameFinder; import com.jetbrains.python.psi.resolve.RatedResolveResult; import com.jetbrains.python.psi.stubs.PyClassStub; import com.jetbrains.python.psi.stubs.PyFunctionStub; @@ -707,4 +708,10 @@ public class PyTargetExpressionImpl extends PyPresentableElementImpl<PyTargetExp super.subtreeChanged(); myQualifiedName = null; } + + @Nullable + @Override + public String getQualifiedName() { + return QualifiedNameFinder.getQualifiedName(this); + } } diff --git a/python/src/com/jetbrains/python/psi/resolve/PyResolveUtil.java b/python/src/com/jetbrains/python/psi/resolve/PyResolveUtil.java index 838a4dbcc75a..f207169df77f 100644 --- a/python/src/com/jetbrains/python/psi/resolve/PyResolveUtil.java +++ b/python/src/com/jetbrains/python/psi/resolve/PyResolveUtil.java @@ -101,7 +101,13 @@ public class PyResolveUtil { @Nullable PsiElement roof) { // Use real context here to enable correct completion and resolve in case of PyExpressionCodeFragment!!! final PsiElement realContext = PyPsiUtils.getRealContext(element); - final ScopeOwner originalOwner = ScopeUtil.getScopeOwner(realContext); + final ScopeOwner originalOwner; + if (realContext != element && realContext instanceof PyFile) { + originalOwner = (PyFile)realContext; + } + else { + originalOwner = ScopeUtil.getScopeOwner(realContext); + } final PsiElement parent = element.getParent(); final boolean isGlobalOrNonlocal = parent instanceof PyGlobalStatement || parent instanceof PyNonlocalStatement; ScopeOwner owner = originalOwner; diff --git a/python/src/com/jetbrains/python/psi/resolve/QualifiedNameFinder.java b/python/src/com/jetbrains/python/psi/resolve/QualifiedNameFinder.java index 97a15da105c3..1c081eff9356 100644 --- a/python/src/com/jetbrains/python/psi/resolve/QualifiedNameFinder.java +++ b/python/src/com/jetbrains/python/psi/resolve/QualifiedNameFinder.java @@ -24,10 +24,14 @@ import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; import com.jetbrains.python.PyNames; +import com.jetbrains.python.codeInsight.controlflow.ScopeOwner; +import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil; import com.jetbrains.python.psi.PyClass; +import com.jetbrains.python.psi.PyElement; import com.jetbrains.python.psi.PyFile; import com.jetbrains.python.psi.PyFunction; import com.intellij.psi.util.QualifiedName; +import com.jetbrains.python.psi.impl.PyBuiltinCache; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -156,6 +160,36 @@ public class QualifiedNameFinder { return qname; } + @Nullable + public static String getQualifiedName(@NotNull PyElement element) { + final String name = element.getName(); + if (name != null) { + final ScopeOwner owner = ScopeUtil.getScopeOwner(element); + final PyBuiltinCache builtinCache = PyBuiltinCache.getInstance(element); + if (owner instanceof PyClass) { + final String classQName = ((PyClass)owner).getQualifiedName(); + if (classQName != null) { + return classQName + "." + name; + } + } + else if (owner instanceof PyFile) { + if (builtinCache.isBuiltin(element)) { + return name; + } + else { + final VirtualFile virtualFile = ((PyFile)owner).getVirtualFile(); + if (virtualFile != null) { + final String fileQName = findShortestImportableName(element, virtualFile); + if (fileQName != null) { + return fileQName + "." + name; + } + } + } + } + } + return null; + } + /** * Tries to find roots that contain given vfile, and among them the root that contains at the smallest depth. * For equal depth source root is in preference to library. diff --git a/python/src/com/jetbrains/python/psi/stubs/PyClassNameIndex.java b/python/src/com/jetbrains/python/psi/stubs/PyClassNameIndex.java index 814f450df8d2..14e1546c7e30 100644 --- a/python/src/com/jetbrains/python/psi/stubs/PyClassNameIndex.java +++ b/python/src/com/jetbrains/python/psi/stubs/PyClassNameIndex.java @@ -57,7 +57,7 @@ public class PyClassNameIndex extends StringStubIndexExtension<PyClass> { int pos = qName.lastIndexOf("."); String shortName = pos > 0 ? qName.substring(pos+1) : qName; for (PyClass pyClass : find(shortName, project, scope)) { - if (pyClass.getQualifiedName().equals(qName)) { + if (qName.equals(pyClass.getQualifiedName())) { return pyClass; } } diff --git a/python/src/com/jetbrains/python/psi/types/PyFunctionType.java b/python/src/com/jetbrains/python/psi/types/PyFunctionType.java index 4bdbae69d83d..fe3633449b74 100644 --- a/python/src/com/jetbrains/python/psi/types/PyFunctionType.java +++ b/python/src/com/jetbrains/python/psi/types/PyFunctionType.java @@ -108,10 +108,14 @@ public class PyFunctionType implements PyCallableType { */ @Nullable private PyClassTypeImpl selectFakeType(@Nullable PyExpression location, @NotNull TypeEvalContext context) { - if (location instanceof PyReferenceExpression && isBoundMethodReference(((PyReferenceExpression)location), context)) { - return PyBuiltinCache.getInstance(getCallable()).getObjectType(PyNames.FAKE_METHOD); + final String fakeClassName; + if (location instanceof PyReferenceExpression && isBoundMethodReference((PyReferenceExpression)location, context)) { + fakeClassName = PyNames.FAKE_METHOD; } - return PyBuiltinCache.getInstance(getCallable()).getObjectType(PyNames.FAKE_FUNCTION); + else { + fakeClassName = PyNames.FAKE_FUNCTION; + } + return PyBuiltinCache.getInstance(getCallable()).getObjectType(fakeClassName); } private boolean isBoundMethodReference(@NotNull PyReferenceExpression location, @NotNull TypeEvalContext context) { diff --git a/python/src/com/jetbrains/python/psi/types/PyTypeChecker.java b/python/src/com/jetbrains/python/psi/types/PyTypeChecker.java index 9881d6cc106c..2e7c58c8201c 100644 --- a/python/src/com/jetbrains/python/psi/types/PyTypeChecker.java +++ b/python/src/com/jetbrains/python/psi/types/PyTypeChecker.java @@ -428,41 +428,39 @@ public class PyTypeChecker { public static AnalyzeCallResults analyzeCall(@NotNull PyBinaryExpression expr, @NotNull TypeEvalContext context) { final PsiPolyVariantReference ref = expr.getReference(PyResolveContext.noImplicits().withTypeEvalContext(context)); final ResolveResult[] resolveResult; - if (ref != null) { - resolveResult = ref.multiResolve(false); - AnalyzeCallResults firstResults = null; - for (ResolveResult result : resolveResult) { - final PsiElement resolved = result.getElement(); - if (resolved instanceof PyTypedElement) { - final PyTypedElement typedElement = (PyTypedElement)resolved; - final PyType type = context.getType(typedElement); - if (!(type instanceof PyFunctionType)) { - return null; - } - final Callable callable = ((PyFunctionType)type).getCallable(); - final boolean isRight = PyNames.isRightOperatorName(typedElement.getName()); - final PyExpression arg = isRight ? expr.getLeftExpression() : expr.getRightExpression(); - final PyExpression receiver = isRight ? expr.getRightExpression() : expr.getLeftExpression(); - final PyParameter[] parameters = callable.getParameterList().getParameters(); - if (parameters.length >= 2) { - final PyNamedParameter param = parameters[1].getAsNamed(); - if (arg != null && param != null) { - final Map<PyExpression, PyNamedParameter> arguments = new LinkedHashMap<PyExpression, PyNamedParameter>(); - arguments.put(arg, param); - final AnalyzeCallResults results = new AnalyzeCallResults(callable, receiver, arguments); - if (firstResults == null) { - firstResults = results; - } - if (match(context.getType(param), context.getType(arg), context)) { - return results; - } + resolveResult = ref.multiResolve(false); + AnalyzeCallResults firstResults = null; + for (ResolveResult result : resolveResult) { + final PsiElement resolved = result.getElement(); + if (resolved instanceof PyTypedElement) { + final PyTypedElement typedElement = (PyTypedElement)resolved; + final PyType type = context.getType(typedElement); + if (!(type instanceof PyFunctionType)) { + return null; + } + final Callable callable = ((PyFunctionType)type).getCallable(); + final boolean isRight = PyNames.isRightOperatorName(typedElement.getName()); + final PyExpression arg = isRight ? expr.getLeftExpression() : expr.getRightExpression(); + final PyExpression receiver = isRight ? expr.getRightExpression() : expr.getLeftExpression(); + final PyParameter[] parameters = callable.getParameterList().getParameters(); + if (parameters.length >= 2) { + final PyNamedParameter param = parameters[1].getAsNamed(); + if (arg != null && param != null) { + final Map<PyExpression, PyNamedParameter> arguments = new LinkedHashMap<PyExpression, PyNamedParameter>(); + arguments.put(arg, param); + final AnalyzeCallResults results = new AnalyzeCallResults(callable, receiver, arguments); + if (firstResults == null) { + firstResults = results; + } + if (match(context.getType(param), context.getType(arg), context)) { + return results; } } } } - if (firstResults != null) { - return firstResults; - } + } + if (firstResults != null) { + return firstResults; } return null; } @@ -471,22 +469,20 @@ public class PyTypeChecker { public static AnalyzeCallResults analyzeCall(@NotNull PySubscriptionExpression expr, @NotNull TypeEvalContext context) { final PsiReference ref = expr.getReference(PyResolveContext.noImplicits().withTypeEvalContext(context)); final PsiElement resolved; - if (ref != null) { - resolved = ref.resolve(); - if (resolved instanceof PyTypedElement) { - final PyType type = context.getType((PyTypedElement)resolved); - if (type instanceof PyFunctionType) { - final Callable callable = ((PyFunctionType)type).getCallable(); - final PyParameter[] parameters = callable.getParameterList().getParameters(); - if (parameters.length == 2) { - final PyNamedParameter param = parameters[1].getAsNamed(); - if (param != null) { - final Map<PyExpression, PyNamedParameter> arguments = new LinkedHashMap<PyExpression, PyNamedParameter>(); - final PyExpression arg = expr.getIndexExpression(); - if (arg != null) { - arguments.put(arg, param); - return new AnalyzeCallResults(callable, expr.getOperand(), arguments); - } + resolved = ref.resolve(); + if (resolved instanceof PyTypedElement) { + final PyType type = context.getType((PyTypedElement)resolved); + if (type instanceof PyFunctionType) { + final Callable callable = ((PyFunctionType)type).getCallable(); + final PyParameter[] parameters = callable.getParameterList().getParameters(); + if (parameters.length == 2) { + final PyNamedParameter param = parameters[1].getAsNamed(); + if (param != null) { + final Map<PyExpression, PyNamedParameter> arguments = new LinkedHashMap<PyExpression, PyNamedParameter>(); + final PyExpression arg = expr.getIndexExpression(); + if (arg != null) { + arguments.put(arg, param); + return new AnalyzeCallResults(callable, expr.getOperand(), arguments); } } } diff --git a/python/src/com/jetbrains/python/refactoring/PyReplaceExpressionUtil.java b/python/src/com/jetbrains/python/refactoring/PyReplaceExpressionUtil.java index 2cebb3ec2720..4a8d2b9d8551 100644 --- a/python/src/com/jetbrains/python/refactoring/PyReplaceExpressionUtil.java +++ b/python/src/com/jetbrains/python/refactoring/PyReplaceExpressionUtil.java @@ -61,21 +61,26 @@ public class PyReplaceExpressionUtil implements PyElementTypes { private PyReplaceExpressionUtil() {} + /** + * @param oldExpr old expression that will be substituted + * @param newExpr new expression to substitute with + * @return whether new expression should be wrapped in parenthesis to preserve original semantics + */ public static boolean isNeedParenthesis(@NotNull final PyElement oldExpr, @NotNull final PyElement newExpr) { final PyElement parentExpr = (PyElement)oldExpr.getParent(); if (parentExpr instanceof PyArgumentList) { return newExpr instanceof PyTupleExpression; } - if (!(parentExpr instanceof PyExpression)) { + if (parentExpr instanceof PyParenthesizedExpression || !(parentExpr instanceof PyExpression)) { return false; } - int newPriority = getExpressionPriority(newExpr); - int parentPriority = getExpressionPriority(parentExpr); + final int newPriority = getExpressionPriority(newExpr); + final int parentPriority = getExpressionPriority(parentExpr); if (parentPriority > newPriority) { return true; } else if (parentPriority == newPriority && parentPriority != 0) { if (parentExpr instanceof PyBinaryExpression) { - PyBinaryExpression binaryExpression = (PyBinaryExpression)parentExpr; + final PyBinaryExpression binaryExpression = (PyBinaryExpression)parentExpr; if (isNotAssociative(binaryExpression) && oldExpr.equals(binaryExpression.getRightExpression())) { return true; } diff --git a/python/src/com/jetbrains/python/refactoring/inline/PyInlineLocalHandler.java b/python/src/com/jetbrains/python/refactoring/inline/PyInlineLocalHandler.java index 575399c61ceb..4f2dc65a22d3 100644 --- a/python/src/com/jetbrains/python/refactoring/inline/PyInlineLocalHandler.java +++ b/python/src/com/jetbrains/python/refactoring/inline/PyInlineLocalHandler.java @@ -29,18 +29,19 @@ import com.intellij.openapi.editor.markup.TextAttributes; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.TextRange; import com.intellij.openapi.wm.WindowManager; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; -import com.intellij.psi.PsiReference; -import com.intellij.psi.PsiWhiteSpace; +import com.intellij.psi.*; +import com.intellij.psi.codeStyle.CodeStyleManager; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.search.searches.ReferencesSearch; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.refactoring.RefactoringBundle; import com.intellij.refactoring.util.CommonRefactoringUtil; import com.intellij.refactoring.util.RefactoringMessageDialog; +import com.intellij.util.Function; import com.intellij.util.Query; +import com.intellij.util.containers.ContainerUtil; import com.jetbrains.python.PyBundle; import com.jetbrains.python.PyTokenTypes; import com.jetbrains.python.PythonLanguage; @@ -51,6 +52,7 @@ import com.jetbrains.python.psi.impl.PyAugAssignmentStatementImpl; import com.jetbrains.python.psi.impl.PyPsiUtils; import com.jetbrains.python.refactoring.PyDefUseUtil; import com.jetbrains.python.refactoring.PyReplaceExpressionUtil; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; @@ -61,7 +63,7 @@ import java.util.List; */ public class PyInlineLocalHandler extends InlineActionHandler { private static final Logger LOG = Logger.getInstance(PyInlineLocalHandler.class.getName()); - + private static final String REFACTORING_NAME = RefactoringBundle.message("inline.variable.title"); private static final Pair<PyStatement, Boolean> EMPTY_DEF_RESULT = Pair.create(null, false); private static final String HELP_ID = "python.reference.inline"; @@ -111,21 +113,21 @@ public class PyInlineLocalHandler extends InlineActionHandler { final PyStatement def = defPair.first; if (def == null || getValue(def) == null){ final String key = defPair.second ? "variable.has.no.dominating.definition" : "variable.has.no.initializer"; - String message = RefactoringBundle.getCannotRefactorMessage(RefactoringBundle.message(key, localName)); + final String message = RefactoringBundle.getCannotRefactorMessage(RefactoringBundle.message(key, localName)); CommonRefactoringUtil.showErrorHint(project, editor, message, REFACTORING_NAME, HELP_ID); return; } if (def instanceof PyAssignmentStatement && ((PyAssignmentStatement)def).getTargets().length > 1){ highlightManager.addOccurrenceHighlights(editor, new PsiElement[] {def}, writeAttributes, true, null); - String message = RefactoringBundle.getCannotRefactorMessage(PyBundle.message("refactoring.inline.local.multiassignment", localName)); + final String message = RefactoringBundle.getCannotRefactorMessage(PyBundle.message("refactoring.inline.local.multiassignment", localName)); CommonRefactoringUtil.showErrorHint(project, editor, message, REFACTORING_NAME, HELP_ID); return; } final PsiElement[] refsToInline = PyDefUseUtil.getPostRefs(containerBlock, local, getObject(def)); if (refsToInline.length == 0) { - String message = RefactoringBundle.message("variable.is.never.used", localName); + final String message = RefactoringBundle.message("variable.is.never.used", localName); CommonRefactoringUtil.showErrorHint(project, editor, message, REFACTORING_NAME, HELP_ID); return; } @@ -133,11 +135,10 @@ public class PyInlineLocalHandler extends InlineActionHandler { final TextAttributes attributes = EditorColorsManager.getInstance().getGlobalScheme().getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES); if (editor != null && !ApplicationManager.getApplication().isUnitTestMode()) { highlightManager.addOccurrenceHighlights(editor, refsToInline, attributes, true, null); - int occurrencesCount = refsToInline.length; - String occurencesString = RefactoringBundle.message("occurrences.string", occurrencesCount); - final String promptKey = "inline.local.variable.prompt"; - final String question = RefactoringBundle.message(promptKey, localName) + " " + occurencesString; - RefactoringMessageDialog dialog = new RefactoringMessageDialog(REFACTORING_NAME, question, HELP_ID, "OptionPane.questionIcon", true, project); + final int occurrencesCount = refsToInline.length; + final String occurrencesString = RefactoringBundle.message("occurrences.string", occurrencesCount); + final String question = RefactoringBundle.message("inline.local.variable.prompt", localName) + " " + occurrencesString; + final RefactoringMessageDialog dialog = new RefactoringMessageDialog(REFACTORING_NAME, question, HELP_ID, "OptionPane.questionIcon", true, project); dialog.show(); if (!dialog.isOK()){ WindowManager.getInstance().getStatusBar(project).setInfo(RefactoringBundle.message("press.escape.to.remove.the.highlighting")); @@ -145,11 +146,11 @@ public class PyInlineLocalHandler extends InlineActionHandler { } } - PsiFile workingFile = local.getContainingFile(); + final PsiFile workingFile = local.getContainingFile(); for (PsiElement ref : refsToInline) { final PsiFile otherFile = ref.getContainingFile(); if (!otherFile.equals(workingFile)) { - String message = RefactoringBundle.message("variable.is.referenced.in.multiple.files", localName); + final String message = RefactoringBundle.message("variable.is.referenced.in.multiple.files", localName); CommonRefactoringUtil.showErrorHint(project, editor, message, REFACTORING_NAME, HELP_ID); return; } @@ -170,9 +171,8 @@ public class PyInlineLocalHandler extends InlineActionHandler { if (editor != null) { highlightManager.addOccurrenceHighlights(editor, defs, writeAttributes, true, null); highlightManager.addOccurrenceHighlights(editor, new PsiElement[]{ref}, attributes, true, null); - String message = - RefactoringBundle - .getCannotRefactorMessage(RefactoringBundle.message("variable.is.accessed.for.writing.and.used.with.inlined", localName)); + final String message = RefactoringBundle.getCannotRefactorMessage( + RefactoringBundle.message("variable.is.accessed.for.writing.and.used.with.inlined", localName)); CommonRefactoringUtil.showErrorHint(project, editor, message, REFACTORING_NAME, HELP_ID); } WindowManager.getInstance().getStatusBar(project).setInfo(RefactoringBundle.message("press.escape.to.remove.the.highlighting")); @@ -185,7 +185,7 @@ public class PyInlineLocalHandler extends InlineActionHandler { public void run() { ApplicationManager.getApplication().runWriteAction(new Runnable() { public void run() { - PsiElement[] exprs = new PsiElement[refsToInline.length]; + final PsiElement[] exprs = new PsiElement[refsToInline.length]; final PyExpression value = prepareValue(def, localName, project); final PyExpression withParent = PyElementGenerator.getInstance(project).createExpressionFromText("(" + value.getText() + ")"); final PsiElement lastChild = def.getLastChild(); @@ -195,7 +195,7 @@ public class PyInlineLocalHandler extends InlineActionHandler { } for (int i = 0, refsToInlineLength = refsToInline.length; i < refsToInlineLength; i++) { - PsiElement element = refsToInline[i]; + final PsiElement element = refsToInline[i]; if (PyReplaceExpressionUtil.isNeedParenthesis((PyExpression)element, value)) { exprs[i] = element.replace(withParent); } else { @@ -207,6 +207,16 @@ public class PyInlineLocalHandler extends InlineActionHandler { PyPsiUtils.removeElements(next); } PyPsiUtils.removeElements(def); + + final List<TextRange> ranges = ContainerUtil.mapNotNull(exprs, new Function<PsiElement, TextRange>() { + @Override + public TextRange fun(PsiElement element) { + final PyStatement parentalStatement = PsiTreeUtil.getParentOfType(element, PyStatement.class, false); + return parentalStatement != null ? parentalStatement.getTextRange() : null; + } + }); + CodeStyleManager.getInstance(project).reformatText(workingFile, ranges); + if (editor != null && !ApplicationManager.getApplication().isUnitTestMode()) { highlightManager.addOccurrenceHighlights(editor, exprs, attributes, true, null); WindowManager.getInstance().getStatusBar(project) @@ -262,7 +272,7 @@ public class PyInlineLocalHandler extends InlineActionHandler { } @Nullable - private static PyExpression getValue(PyStatement def) { + private static PyExpression getValue(@Nullable PyStatement def) { if (def == null) return null; if (def instanceof PyAssignmentStatement) { return ((PyAssignmentStatement)def).getAssignedValue(); @@ -271,7 +281,7 @@ public class PyInlineLocalHandler extends InlineActionHandler { } @Nullable - private static PyExpression getObject(PyStatement def) { + private static PyExpression getObject(@Nullable PyStatement def) { if (def == null) return null; if (def instanceof PyAssignmentStatement) { return ((PyAssignmentStatement)def).getTargets()[0]; @@ -279,12 +289,15 @@ public class PyInlineLocalHandler extends InlineActionHandler { return ((PyAugAssignmentStatement)def).getTarget(); } - private static PyExpression prepareValue(PyStatement def, String localName, Project project) { + @NotNull + private static PyExpression prepareValue(@NotNull PyStatement def, @NotNull String localName, @NotNull Project project) { final PyExpression value = getValue(def); assert value != null; if (def instanceof PyAugAssignmentStatementImpl) { final PyAugAssignmentStatementImpl expression = (PyAugAssignmentStatementImpl)def; - String op = expression.getOperation().getText().replace('=', ' '); + final PsiElement operation = expression.getOperation(); + assert operation != null; + final String op = operation.getText().replace('=', ' '); return PyElementGenerator.getInstance(project).createExpressionFromText(localName + " " + op + value.getText() + ")"); } return value; diff --git a/python/src/com/jetbrains/python/refactoring/surround/surrounders/statements/PyWithIfElseSurrounder.java b/python/src/com/jetbrains/python/refactoring/surround/surrounders/statements/PyWithIfElseSurrounder.java index 76c4f4932038..1e1b6d0db815 100644 --- a/python/src/com/jetbrains/python/refactoring/surround/surrounders/statements/PyWithIfElseSurrounder.java +++ b/python/src/com/jetbrains/python/refactoring/surround/surrounders/statements/PyWithIfElseSurrounder.java @@ -44,7 +44,6 @@ public class PyWithIfElseSurrounder extends PyStatementSurrounder { PyElementGenerator.getInstance(project).createFromText(LanguageLevel.getDefault(), PyIfStatement.class, "if True:\n pass\nelse: pass\n"); final PsiElement parent = elements[0].getParent(); final PyStatementList statementList = ifStatement.getIfPart().getStatementList(); - assert statementList != null; statementList.addRange(elements[0], elements[elements.length - 1]); statementList.deleteChildRange(statementList.getFirstChild(), statementList.getFirstChild()); diff --git a/python/src/com/jetbrains/python/refactoring/surround/surrounders/statements/PyWithIfSurrounder.java b/python/src/com/jetbrains/python/refactoring/surround/surrounders/statements/PyWithIfSurrounder.java index 5ccca3de55e6..ac93e60af828 100644 --- a/python/src/com/jetbrains/python/refactoring/surround/surrounders/statements/PyWithIfSurrounder.java +++ b/python/src/com/jetbrains/python/refactoring/surround/surrounders/statements/PyWithIfSurrounder.java @@ -22,10 +22,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiElement; import com.intellij.util.IncorrectOperationException; -import com.jetbrains.python.psi.LanguageLevel; -import com.jetbrains.python.psi.PyElementGenerator; -import com.jetbrains.python.psi.PyIfStatement; -import com.jetbrains.python.psi.PyStatementList; +import com.jetbrains.python.psi.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -40,7 +37,6 @@ public class PyWithIfSurrounder extends PyStatementSurrounder { PyIfStatement ifStatement = PyElementGenerator.getInstance(project).createFromText(LanguageLevel.getDefault(), PyIfStatement.class, "if True:\n "); final PsiElement parent = elements[0].getParent(); final PyStatementList statementList = ifStatement.getIfPart().getStatementList(); - assert statementList != null; statementList.addRange(elements[0], elements[elements.length - 1]); ifStatement = (PyIfStatement) parent.addBefore(ifStatement, elements[0]); parent.deleteChildRange(elements[0], elements[elements.length - 1]); @@ -49,7 +45,8 @@ public class PyWithIfSurrounder extends PyStatementSurrounder { if (ifStatement == null) { return null; } - return ifStatement.getIfPart().getCondition().getTextRange(); + final PyExpression condition = ifStatement.getIfPart().getCondition(); + return condition != null ? condition.getTextRange() : null; } public String getTemplateDescription() { diff --git a/python/src/com/jetbrains/python/refactoring/surround/surrounders/statements/PyWithTryExceptSurrounder.java b/python/src/com/jetbrains/python/refactoring/surround/surrounders/statements/PyWithTryExceptSurrounder.java index 25a9f26308d3..7466def257f4 100644 --- a/python/src/com/jetbrains/python/refactoring/surround/surrounders/statements/PyWithTryExceptSurrounder.java +++ b/python/src/com/jetbrains/python/refactoring/surround/surrounders/statements/PyWithTryExceptSurrounder.java @@ -44,7 +44,6 @@ public class PyWithTryExceptSurrounder extends PyStatementSurrounder { createFromText(LanguageLevel.getDefault(), PyTryExceptStatement.class, getTemplate()); final PsiElement parent = elements[0].getParent(); final PyStatementList statementList = tryStatement.getTryPart().getStatementList(); - assert statementList != null; statementList.addRange(elements[0], elements[elements.length - 1]); statementList.getFirstChild().delete(); tryStatement = (PyTryExceptStatement)parent.addBefore(tryStatement, elements[0]); @@ -71,7 +70,6 @@ public class PyWithTryExceptSurrounder extends PyStatementSurrounder { protected TextRange getResultRange(PyTryExceptStatement tryStatement) { final PyExceptPart part = tryStatement.getExceptParts()[0]; final PyStatementList list = part.getStatementList(); - assert list != null; return list.getTextRange(); } diff --git a/python/src/com/jetbrains/python/refactoring/surround/surrounders/statements/PyWithTryFinallySurrounder.java b/python/src/com/jetbrains/python/refactoring/surround/surrounders/statements/PyWithTryFinallySurrounder.java index d457e32232c2..532fad5c5114 100644 --- a/python/src/com/jetbrains/python/refactoring/surround/surrounders/statements/PyWithTryFinallySurrounder.java +++ b/python/src/com/jetbrains/python/refactoring/surround/surrounders/statements/PyWithTryFinallySurrounder.java @@ -42,7 +42,6 @@ public class PyWithTryFinallySurrounder extends PyWithTryExceptSurrounder { final PyFinallyPart finallyPart = tryStatement.getFinallyPart(); assert finallyPart != null; final PyStatementList statementList = finallyPart.getStatementList(); - assert statementList != null; return statementList.getTextRange(); } } diff --git a/python/src/com/jetbrains/python/refactoring/surround/surrounders/statements/PyWithWhileSurrounder.java b/python/src/com/jetbrains/python/refactoring/surround/surrounders/statements/PyWithWhileSurrounder.java index 4ce663900065..a564d4ca3d3f 100644 --- a/python/src/com/jetbrains/python/refactoring/surround/surrounders/statements/PyWithWhileSurrounder.java +++ b/python/src/com/jetbrains/python/refactoring/surround/surrounders/statements/PyWithWhileSurrounder.java @@ -41,7 +41,6 @@ public class PyWithWhileSurrounder extends PyStatementSurrounder{ PyElementGenerator.getInstance(project).createFromText(LanguageLevel.getDefault(), PyWhileStatement.class, "while True:\n "); final PsiElement parent = elements[0].getParent(); final PyStatementList statementList = whileStatement.getWhilePart().getStatementList(); - assert statementList != null; statementList.addRange(elements[0], elements[elements.length - 1]); whileStatement = (PyWhileStatement) parent.addBefore(whileStatement, elements[0]); parent.deleteChildRange(elements[0], elements[elements.length - 1]); diff --git a/python/src/com/jetbrains/python/run/AbstractPythonRunConfiguration.java b/python/src/com/jetbrains/python/run/AbstractPythonRunConfiguration.java index 339f91ea7e3a..0927d2e37a3d 100644 --- a/python/src/com/jetbrains/python/run/AbstractPythonRunConfiguration.java +++ b/python/src/com/jetbrains/python/run/AbstractPythonRunConfiguration.java @@ -37,10 +37,10 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiElement; import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.PathMappingSettings; import com.intellij.util.PlatformUtils; import com.jetbrains.python.PyBundle; import com.jetbrains.python.PythonModuleTypeBase; -import com.intellij.util.PathMappingSettings; import com.jetbrains.python.psi.PyClass; import com.jetbrains.python.psi.PyFunction; import com.jetbrains.python.sdk.PythonEnvUtil; @@ -64,6 +64,7 @@ public abstract class AbstractPythonRunConfiguration<T extends AbstractRunConfig private boolean myUseModuleSdk; private boolean myAddContentRoots = true; private boolean myAddSourceRoots = true; + protected PathMappingSettings myMappingSettings; public AbstractPythonRunConfiguration(Project project, final ConfigurationFactory factory) { diff --git a/python/src/com/jetbrains/python/run/PythonCommandLineState.java b/python/src/com/jetbrains/python/run/PythonCommandLineState.java index 220939efc274..b1c768f0b012 100644 --- a/python/src/com/jetbrains/python/run/PythonCommandLineState.java +++ b/python/src/com/jetbrains/python/run/PythonCommandLineState.java @@ -141,7 +141,8 @@ public abstract class PythonCommandLineState extends CommandLineState { protected void addTracebackFilter(Project project, ConsoleView consoleView, ProcessHandler processHandler) { if (PySdkUtil.isRemote(myConfig.getSdk())) { assert processHandler instanceof RemoteProcessHandlerBase; - consoleView.addMessageFilter(new PyRemoteTracebackFilter(project, myConfig.getWorkingDirectory(), (RemoteProcessHandlerBase) processHandler)); + consoleView + .addMessageFilter(new PyRemoteTracebackFilter(project, myConfig.getWorkingDirectory(), (RemoteProcessHandlerBase)processHandler)); } else { consoleView.addMessageFilter(new PythonTracebackFilter(project, myConfig.getWorkingDirectory())); @@ -174,7 +175,8 @@ public abstract class PythonCommandLineState extends CommandLineState { GeneralCommandLine commandLine = generateCommandLine(patchers); // Extend command line - PythonRunConfigurationExtensionsManager.getInstance().patchCommandLine(myConfig, getRunnerSettings(), commandLine, getEnvironment().getRunner().getRunnerId()); + PythonRunConfigurationExtensionsManager.getInstance() + .patchCommandLine(myConfig, getRunnerSettings(), commandLine, getEnvironment().getRunner().getRunnerId()); Sdk sdk = PythonSdkType.findSdkByPath(myConfig.getInterpreterPath()); final ProcessHandler processHandler; if (PySdkUtil.isRemote(sdk)) { diff --git a/python/src/com/jetbrains/python/run/PythonRunConfiguration.java b/python/src/com/jetbrains/python/run/PythonRunConfiguration.java index 118c27beb548..5efc8d465bc1 100644 --- a/python/src/com/jetbrains/python/run/PythonRunConfiguration.java +++ b/python/src/com/jetbrains/python/run/PythonRunConfiguration.java @@ -47,8 +47,10 @@ public class PythonRunConfiguration extends AbstractPythonRunConfiguration public static final String SCRIPT_NAME = "SCRIPT_NAME"; public static final String PARAMETERS = "PARAMETERS"; public static final String MULTIPROCESS = "MULTIPROCESS"; + public static final String SHOW_COMMAND_LINE = "SHOW_COMMAND_LINE"; private String myScriptName; private String myScriptParameters; + private boolean myShowCommandLineAfterwards = false; protected PythonRunConfiguration(Project project, ConfigurationFactory configurationFactory) { super(project, configurationFactory); @@ -98,17 +100,27 @@ public class PythonRunConfiguration extends AbstractPythonRunConfiguration myScriptParameters = scriptParameters; } + public boolean showCommandLineAfterwards() { + return myShowCommandLineAfterwards; + } + + public void setShowCommandLineAfterwards(boolean showCommandLineAfterwards) { + myShowCommandLineAfterwards = showCommandLineAfterwards; + } + public void readExternal(Element element) throws InvalidDataException { PathMacroManager.getInstance(getProject()).expandPaths(element); super.readExternal(element); myScriptName = JDOMExternalizerUtil.readField(element, SCRIPT_NAME); myScriptParameters = JDOMExternalizerUtil.readField(element, PARAMETERS); + myShowCommandLineAfterwards = Boolean.parseBoolean(JDOMExternalizerUtil.readField(element, SHOW_COMMAND_LINE, "false")); } public void writeExternal(Element element) throws WriteExternalException { super.writeExternal(element); JDOMExternalizerUtil.writeField(element, SCRIPT_NAME, myScriptName); JDOMExternalizerUtil.writeField(element, PARAMETERS, myScriptParameters); + JDOMExternalizerUtil.writeField(element, SHOW_COMMAND_LINE, Boolean.toString(myShowCommandLineAfterwards)); PathMacroManager.getInstance(getProject()).collapsePathsRecursively(element); } @@ -120,6 +132,7 @@ public class PythonRunConfiguration extends AbstractPythonRunConfiguration AbstractPythonRunConfiguration.copyParams(source.getBaseParams(), target.getBaseParams()); target.setScriptName(source.getScriptName()); target.setScriptParameters(source.getScriptParameters()); + target.setShowCommandLineAfterwards(source.showCommandLineAfterwards()); } @Override diff --git a/python/src/com/jetbrains/python/run/PythonRunConfigurationForm.form b/python/src/com/jetbrains/python/run/PythonRunConfigurationForm.form index 1ba245143902..33e45b9c04ee 100644 --- a/python/src/com/jetbrains/python/run/PythonRunConfigurationForm.form +++ b/python/src/com/jetbrains/python/run/PythonRunConfigurationForm.form @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.jetbrains.python.run.PythonRunConfigurationForm"> - <grid id="27dc6" binding="myRootPanel" layout-manager="GridLayoutManager" row-count="5" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1"> + <grid id="27dc6" binding="myRootPanel" layout-manager="GridLayoutManager" row-count="6" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1"> <margin top="0" left="0" bottom="0" right="0"/> <constraints> <xy x="20" y="20" width="507" height="400"/> @@ -44,7 +44,7 @@ </component> <vspacer id="f8c96"> <constraints> - <grid row="4" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/> + <grid row="5" column="1" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/> </constraints> </vspacer> <grid id="9b4b8" binding="myCommonOptionsPlaceholder" layout-manager="BorderLayout" hgap="0" vgap="0"> @@ -63,6 +63,15 @@ <border type="none"/> <children/> </grid> + <component id="99b13" class="com.intellij.ui.components.JBCheckBox" binding="myShowCommandLineCheckbox"> + <constraints> + <grid row="4" column="0" row-span="1" col-span="2" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/> + </constraints> + <properties> + <selected value="true"/> + <text value="Show command line afterwards"/> + </properties> + </component> </children> </grid> </form> diff --git a/python/src/com/jetbrains/python/run/PythonRunConfigurationForm.java b/python/src/com/jetbrains/python/run/PythonRunConfigurationForm.java index da041f961f80..cad905fd9618 100644 --- a/python/src/com/jetbrains/python/run/PythonRunConfigurationForm.java +++ b/python/src/com/jetbrains/python/run/PythonRunConfigurationForm.java @@ -25,6 +25,7 @@ import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.ui.PanelWithAnchor; import com.intellij.ui.RawCommandLineEditor; +import com.intellij.ui.components.JBCheckBox; import com.intellij.ui.components.JBLabel; import com.jetbrains.python.debugger.PyDebuggerOptionsProvider; import org.jetbrains.annotations.NotNull; @@ -44,6 +45,7 @@ public class PythonRunConfigurationForm implements PythonRunConfigurationParams, private final AbstractPyCommonOptionsForm myCommonOptionsForm; private JComponent anchor; private final Project myProject; + private JBCheckBox myShowCommandLineCheckbox; public PythonRunConfigurationForm(PythonRunConfiguration configuration) { myCommonOptionsForm = PyCommonOptionsFormFactory.getInstance().createForm(configuration.getCommonOptionsFormData()); @@ -105,6 +107,16 @@ public class PythonRunConfigurationForm implements PythonRunConfigurationParams, } @Override + public boolean showCommandLineAfterwards() { + return myShowCommandLineCheckbox.isSelected(); + } + + @Override + public void setShowCommandLineAfterwards(boolean showCommandLineAfterwards) { + myShowCommandLineCheckbox.setSelected(showCommandLineAfterwards); + } + + @Override public JComponent getAnchor() { return anchor; } diff --git a/python/src/com/jetbrains/python/run/PythonScriptCommandLineState.java b/python/src/com/jetbrains/python/run/PythonScriptCommandLineState.java index 81d2daaee3d0..cd92fdb34638 100644 --- a/python/src/com/jetbrains/python/run/PythonScriptCommandLineState.java +++ b/python/src/com/jetbrains/python/run/PythonScriptCommandLineState.java @@ -15,11 +15,37 @@ */ package com.jetbrains.python.run; +import com.google.common.collect.Lists; +import com.intellij.execution.*; import com.intellij.execution.configurations.GeneralCommandLine; import com.intellij.execution.configurations.ParametersList; import com.intellij.execution.configurations.ParamsGroup; +import com.intellij.execution.executors.DefaultDebugExecutor; +import com.intellij.execution.process.CommandLineArgumentsProvider; +import com.intellij.execution.process.OSProcessHandler; +import com.intellij.execution.process.ProcessHandler; import com.intellij.execution.runners.ExecutionEnvironment; +import com.intellij.execution.ui.RunContentDescriptor; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.progress.Task; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.util.ArrayUtil; +import com.intellij.util.ui.UIUtil; +import com.jetbrains.python.PythonHelpersLocator; +import com.jetbrains.python.console.PyConsoleType; +import com.jetbrains.python.console.PydevConsoleRunner; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; /** * @author yole @@ -32,6 +58,34 @@ public class PythonScriptCommandLineState extends PythonCommandLineState { myConfig = runConfiguration; } + @NotNull + @Override + public ExecutionResult execute(Executor executor, final CommandLinePatcher... patchers) throws ExecutionException { + if (myConfig.showCommandLineAfterwards()) { + if (executor.getId() == DefaultDebugExecutor.EXECUTOR_ID) { + return super.execute(executor, ArrayUtil.append(patchers, new CommandLinePatcher() { + @Override + public void patchCommandLine(GeneralCommandLine commandLine) { + commandLine.getParametersList().getParamsGroup(PythonCommandLineState.GROUP_DEBUGGER).addParameterAt(1, "--cmd-line"); + } + })); + } + + PydevConsoleRunner runner = + new PythonScriptWithConsoleRunner(myConfig.getProject(), myConfig.getSdk(), PyConsoleType.PYTHON, myConfig.getWorkingDirectory(), + myConfig.getEnvs(), patchers); + + runner.runSync(); + + List<AnAction> actions = Lists.newArrayList(createActions(runner.getConsoleView(), runner.getProcessHandler())); + + return new DefaultExecutionResult(runner.getConsoleView(), runner.getProcessHandler(), actions.toArray(new AnAction[actions.size()])); + } + else { + return super.execute(executor, patchers); + } + } + @Override protected void buildCommandLineParameters(GeneralCommandLine commandLine) { ParametersList parametersList = commandLine.getParametersList(); @@ -53,4 +107,68 @@ public class PythonScriptCommandLineState extends PythonCommandLineState { } } + /** + * @author traff + */ + public class PythonScriptWithConsoleRunner extends PydevConsoleRunner { + + private CommandLinePatcher[] myPatchers; + + public PythonScriptWithConsoleRunner(@NotNull Project project, + @NotNull Sdk sdk, + @NotNull PyConsoleType consoleType, + @Nullable String workingDir, + Map<String, String> environmentVariables, + CommandLinePatcher[] patchers, + String... statementsToExecute) { + super(project, sdk, consoleType, workingDir, environmentVariables, statementsToExecute); + myPatchers = patchers; + } + + @Override + protected void createContentDescriptorAndActions() { + AnAction a = createConsoleExecAction(myConsoleExecuteActionHandler); + registerActionShortcuts(Lists.newArrayList(a), getConsoleView().getConsole().getConsoleEditor().getComponent()); + } + + @Override + protected CommandLineArgumentsProvider createCommandLineArgumentsProvider(final Sdk sdk, + final Map<String, String> environmentVariables, + int[] ports) { + final ArrayList<String> args = new ArrayList<String>(); + args.add(sdk.getHomePath()); + final String versionString = sdk.getVersionString(); + if (versionString == null || !versionString.toLowerCase().contains("jython")) { + args.add("-u"); + } + args.add(FileUtil.toSystemDependentName(PythonHelpersLocator.getHelperPath("pydev/pydev_run_in_console.py"))); + for (int port : ports) { + args.add(String.valueOf(port)); + } + + try { + GeneralCommandLine cmd = generateCommandLine(myPatchers); + args.addAll(cmd.getParametersList().getList()); + } + catch (Exception e) { + //pass + } + return new CommandLineArgumentsProvider() { + @Override + public String[] getArguments() { + return ArrayUtil.toStringArray(args); + } + + @Override + public boolean passParentEnvs() { + return false; + } + + @Override + public Map<String, String> getAdditionalEnvs() { + return addDefaultEnvironments(sdk, environmentVariables); + } + }; + } + } } diff --git a/python/src/com/jetbrains/python/sdk/PySdkListCellRenderer.java b/python/src/com/jetbrains/python/sdk/PySdkListCellRenderer.java index 1a42621dde3d..e165e59656f4 100644 --- a/python/src/com/jetbrains/python/sdk/PySdkListCellRenderer.java +++ b/python/src/com/jetbrains/python/sdk/PySdkListCellRenderer.java @@ -20,6 +20,7 @@ import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.projectRoots.SdkModificator; import com.intellij.openapi.projectRoots.SdkType; import com.intellij.openapi.util.IconLoader; +import com.intellij.openapi.util.io.FileUtil; import com.intellij.ui.LayeredIcon; import com.intellij.ui.ListCellRendererWrapper; import com.jetbrains.python.sdk.flavors.PythonSdkFlavor; @@ -113,7 +114,7 @@ public class PySdkListCellRenderer extends ListCellRendererWrapper<Object> { } } else if (new File(name).exists()) { - name = "..." + File.separator + new File(name).getParentFile().getParentFile().getName(); + name = FileUtil.getLocationRelativeToUserHome(name); } return name; } diff --git a/python/src/com/jetbrains/python/sdk/flavors/WinPythonSdkFlavor.java b/python/src/com/jetbrains/python/sdk/flavors/WinPythonSdkFlavor.java index 8e75e634755b..9d22af8c03bf 100644 --- a/python/src/com/jetbrains/python/sdk/flavors/WinPythonSdkFlavor.java +++ b/python/src/com/jetbrains/python/sdk/flavors/WinPythonSdkFlavor.java @@ -15,7 +15,9 @@ */ package com.jetbrains.python.sdk.flavors; +import com.google.common.collect.ImmutableMap; import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.io.WindowsRegistryUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; @@ -23,15 +25,17 @@ import com.intellij.openapi.vfs.newvfs.NewVirtualFile; import com.jetbrains.python.PythonHelpersLocator; import java.io.File; -import java.util.Collection; -import java.util.Set; -import java.util.TreeSet; +import java.util.*; /** * @author yole */ public class WinPythonSdkFlavor extends CPythonSdkFlavor { public static WinPythonSdkFlavor INSTANCE = new WinPythonSdkFlavor(); + private static Map<String, String> ourRegistryMap = + ImmutableMap.of("HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore", "python.exe", + "HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node\\Python\\PythonCore", "python.exe", + "HKEY_LOCAL_MACHINE\\SOFTWARE\\IronPython", "ipy.exe"); private WinPythonSdkFlavor() { } @@ -48,6 +52,7 @@ public class WinPythonSdkFlavor extends CPythonSdkFlavor { for (String name : exe_names) { findInstallations(candidates, name, "C:\\", "C:\\Program Files\\"); findInPath(candidates, name); + findInRegistry(candidates); } } @@ -59,6 +64,7 @@ public class WinPythonSdkFlavor extends CPythonSdkFlavor { public static void findInPath(Collection<String> candidates, String exeName) { final String path = System.getenv("PATH"); + if (path == null) return; for (String pathEntry : StringUtil.split(path, ";")) { if (pathEntry.startsWith("\"") && pathEntry.endsWith("\"")) { if (pathEntry.length() < 2) continue; @@ -71,6 +77,25 @@ public class WinPythonSdkFlavor extends CPythonSdkFlavor { } } + public static void findInRegistry(Collection<String> candidates) { + for (Map.Entry<String, String> entry : ourRegistryMap.entrySet()) { + final String prefix = entry.getKey(); + final String exePath = entry.getValue(); + List<String> strings = WindowsRegistryUtil.readRegistryBranch(prefix); + for (String string : strings) { + final String path = + WindowsRegistryUtil.readRegistryDefault(prefix + "\\" + string + + "\\InstallPath"); + if (path != null) { + File f = new File(path, exePath); + if (f.exists()) { + candidates.add(FileUtil.toSystemDependentName(f.getPath())); + } + } + } + } + } + private static void findSubdirInstallations(Collection<String> candidates, String rootDir, String dir_prefix, String exe_name) { VirtualFile rootVDir = LocalFileSystem.getInstance().findFileByPath(rootDir); if (rootVDir != null) { diff --git a/python/src/com/jetbrains/python/validation/CompatibilityVisitor.java b/python/src/com/jetbrains/python/validation/CompatibilityVisitor.java index f48af7ff0e73..9be15944f37e 100644 --- a/python/src/com/jetbrains/python/validation/CompatibilityVisitor.java +++ b/python/src/com/jetbrains/python/validation/CompatibilityVisitor.java @@ -295,7 +295,7 @@ public abstract class CompatibilityVisitor extends PyAnnotator { } } commonRegisterProblem(message, " not support this syntax. Raise with no arguments can only be used in an except block", - len, node, null); + len, node, null, false); // raise 1, 2, 3 len = 0; message = new StringBuilder(myCommonMessage); |