diff options
Diffstat (limited to 'python/src/com/jetbrains/python')
37 files changed, 832 insertions, 621 deletions
diff --git a/python/src/com/jetbrains/python/actions/ExecuteInConsoleAction.java b/python/src/com/jetbrains/python/actions/PyExecuteSelectionAction.java index cb90d498e663..4aea63a8a196 100644 --- a/python/src/com/jetbrains/python/actions/ExecuteInConsoleAction.java +++ b/python/src/com/jetbrains/python/actions/PyExecuteSelectionAction.java @@ -32,18 +32,20 @@ import com.intellij.util.Consumer; import com.intellij.util.NotNullFunction; import com.jetbrains.python.console.PyCodeExecutor; import com.jetbrains.python.console.PydevConsoleRunner; -import com.jetbrains.python.console.RunPythonConsoleAction; +import com.jetbrains.python.console.PythonConsoleRunnerFactory; +import com.jetbrains.python.console.PythonConsoleToolWindow; import com.jetbrains.python.psi.PyFile; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Collection; +import java.util.List; -public class ExecuteInConsoleAction extends AnAction { +public class PyExecuteSelectionAction extends AnAction { public static final String EXECUTE_SELECTION_IN_CONSOLE = "Execute Selection in Console"; - public ExecuteInConsoleAction() { + public PyExecuteSelectionAction() { super(EXECUTE_SELECTION_IN_CONSOLE); } @@ -186,6 +188,12 @@ public class ExecuteInConsoleAction extends AnAction { } private static Collection<RunContentDescriptor> getConsoles(Project project) { + PythonConsoleToolWindow toolWindow = PythonConsoleToolWindow.getInstance(project); + + if (toolWindow != null) { + return toolWindow.getConsoleContentDescriptors(); + } + return ExecutionHelper.findRunningConsole(project, new NotNullFunction<RunContentDescriptor, Boolean>() { @NotNull @Override @@ -214,15 +222,34 @@ public class ExecuteInConsoleAction extends AnAction { private static void startConsole(final Project project, final Consumer<PyCodeExecutor> consumer, Module context) { - PydevConsoleRunner runner = RunPythonConsoleAction.runPythonConsole(project, context, null); - runner.addConsoleListener(new PydevConsoleRunner.ConsoleListener() { - @Override - public void handleConsoleInitialized(LanguageConsoleView consoleView) { - if (consoleView instanceof PyCodeExecutor) { - consumer.consume((PyCodeExecutor)consoleView); + final PythonConsoleToolWindow toolWindow = PythonConsoleToolWindow.getInstance(project); + + if (toolWindow != null) { + toolWindow.activate(new Runnable() { + @Override + public void run() { + List<RunContentDescriptor> descs = toolWindow.getConsoleContentDescriptors(); + + RunContentDescriptor descriptor = descs.get(0); + if (descriptor != null && descriptor.getExecutionConsole() instanceof PyCodeExecutor) { + consumer.consume((PyCodeExecutor)descriptor.getExecutionConsole()); + } } - } - }); + }); + } + else { + PythonConsoleRunnerFactory consoleRunnerFactory = PythonConsoleRunnerFactory.getInstance(); + PydevConsoleRunner runner = consoleRunnerFactory.createConsoleRunner(project, null); + runner.addConsoleListener(new PydevConsoleRunner.ConsoleListener() { + @Override + public void handleConsoleInitialized(LanguageConsoleView consoleView) { + if (consoleView instanceof PyCodeExecutor) { + consumer.consume((PyCodeExecutor)consoleView); + } + } + }); + runner.run(); + } } private static boolean canFindConsole(AnActionEvent e) { diff --git a/python/src/com/jetbrains/python/buildout/BuildoutConfigPanel.java b/python/src/com/jetbrains/python/buildout/BuildoutConfigPanel.java index 394de6160cbc..eceedeb00050 100644 --- a/python/src/com/jetbrains/python/buildout/BuildoutConfigPanel.java +++ b/python/src/com/jetbrains/python/buildout/BuildoutConfigPanel.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. @@ -81,6 +81,7 @@ public class BuildoutConfigPanel extends JPanel { myErrorPanel.add(facetErrorPanel.getComponent(), BorderLayout.CENTER); facetErrorPanel.getValidatorsManager().registerValidator(new FacetEditorValidator() { + @NotNull @Override public ValidationResult check() { if (!myFacetEnabled) { diff --git a/python/src/com/jetbrains/python/codeInsight/imports/AddImportHelper.java b/python/src/com/jetbrains/python/codeInsight/imports/AddImportHelper.java index 1c09ee24f7dd..d33e97c1e7f9 100644 --- a/python/src/com/jetbrains/python/codeInsight/imports/AddImportHelper.java +++ b/python/src/com/jetbrains/python/codeInsight/imports/AddImportHelper.java @@ -21,14 +21,14 @@ import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleUtilCore; import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.roots.ProjectRootManager; -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.IncorrectOperationException; import com.jetbrains.python.codeInsight.PyCodeInsightSettings; import com.jetbrains.python.documentation.DocStringUtil; import com.jetbrains.python.psi.*; -import com.intellij.psi.util.QualifiedName; import com.jetbrains.python.psi.resolve.QualifiedNameFinder; import com.jetbrains.python.sdk.PythonSdkType; import org.jetbrains.annotations.NotNull; @@ -36,6 +36,8 @@ import org.jetbrains.annotations.Nullable; import java.util.List; +import static com.jetbrains.python.psi.PyUtil.sure; + /** * Does the actual job of adding an import statement into a file. * User: dcheryasov @@ -47,6 +49,34 @@ public class AddImportHelper { private AddImportHelper() { } + public static void addLocalImportStatement(@NotNull PyElement element, @NotNull String name) { + final PyElementGenerator generator = PyElementGenerator.getInstance(element.getProject()); + final LanguageLevel languageLevel = LanguageLevel.forElement(element); + + final PsiElement anchor = getLocalInsertPosition(element); + final PsiElement parentElement = sure(anchor).getParent(); + if (parentElement != null) { + parentElement.addBefore(generator.createImportStatement(languageLevel, name, null), anchor); + } + } + + public static void addLocalFromImportStatement(@NotNull PyElement element, @NotNull String qualifier, @NotNull String name) { + final PyElementGenerator generator = PyElementGenerator.getInstance(element.getProject()); + final LanguageLevel languageLevel = LanguageLevel.forElement(element); + + final PsiElement anchor = getLocalInsertPosition(element); + final PsiElement parentElement = sure(anchor).getParent(); + if (parentElement != null) { + parentElement.addBefore(generator.createFromImportStatement(languageLevel, qualifier, name, null), anchor); + } + + } + + @Nullable + public static PsiElement getLocalInsertPosition(@NotNull PyElement anchor) { + return PsiTreeUtil.getParentOfType(anchor, PyStatement.class, false); + } + public enum ImportPriority { BUILTIN, THIRD_PARTY, PROJECT } @@ -81,7 +111,8 @@ public class AddImportHelper { // maybe we arrived at the doc comment stmt; skip over it, too else if (!skippedOverImports && !skippedOverDoc && file instanceof PyFile) { PsiElement doc_elt = - DocStringUtil.findDocStringExpression((PyElement)file); // this gives the literal; its parent is the expr seeker may have encountered + DocStringUtil + .findDocStringExpression((PyElement)file); // this gives the literal; its parent is the expr seeker may have encountered if (doc_elt != null && doc_elt.getParent() == feeler) { feeler = feeler.getNextSibling(); seeker = feeler; // skip over doc even if there's nothing below it @@ -147,19 +178,13 @@ public class AddImportHelper { * @param file where to operate * @param name which to import (qualified is OK) * @param asName optional name for 'as' clause + * @return whether import statement was actually added */ public static boolean addImportStatement(PsiFile file, String name, @Nullable String asName, ImportPriority priority) { - String as_clause; - if (asName == null) { - as_clause = ""; - } - else { - as_clause = " as " + asName; - } if (!(file instanceof PyFile)) { return false; } - List<PyImportElement> existingImports = ((PyFile)file).getImportTargets(); + final List<PyImportElement> existingImports = ((PyFile)file).getImportTargets(); for (PyImportElement element : existingImports) { final QualifiedName qName = element.getImportedQName(); if (qName != null && name.equals(qName.toString())) { @@ -171,7 +196,7 @@ public class AddImportHelper { final PyElementGenerator generator = PyElementGenerator.getInstance(file.getProject()); final LanguageLevel languageLevel = LanguageLevel.forElement(file); - final PyImportStatement importNodeToInsert = generator.createImportStatementFromText(languageLevel, "import " + name + as_clause); + final PyImportStatement importNodeToInsert = generator.createImportStatement(languageLevel, name, asName); try { file.addBefore(importNodeToInsert, getInsertPosition(file, name, priority)); } @@ -180,6 +205,7 @@ public class AddImportHelper { } return true; } + /** * Adds an "import ... from ..." statement below other top-level imports. * @@ -189,20 +215,20 @@ public class AddImportHelper { * @param asName optional name for 'as' clause */ public static void addImportFromStatement(PsiFile file, String from, String name, @Nullable String asName, ImportPriority priority) { - String asClause = asName == null ? "" : " as " + asName; - - final PyFromImportStatement importNodeToInsert = PyElementGenerator.getInstance(file.getProject()).createFromText( - LanguageLevel.forElement(file), PyFromImportStatement.class, "from " + from + " import " + name + asClause); + final PyElementGenerator generator = PyElementGenerator.getInstance(file.getProject()); + final LanguageLevel languageLevel = LanguageLevel.forElement(file); + final PyFromImportStatement nodeToInsert = generator.createFromImportStatement(languageLevel, from, name, asName); try { if (InjectedLanguageManager.getInstance(file.getProject()).isInjectedFragment(file)) { - final PsiElement element = file.addBefore(importNodeToInsert, getInsertPosition(file, from, priority)); + final PsiElement element = file.addBefore(nodeToInsert, getInsertPosition(file, from, priority)); PsiElement whitespace = element.getNextSibling(); - if (!(whitespace instanceof PsiWhiteSpace)) + if (!(whitespace instanceof PsiWhiteSpace)) { whitespace = PsiParserFacade.SERVICE.getInstance(file.getProject()).createWhiteSpaceFromText(" >>> "); + } file.addBefore(whitespace, element); } else { - file.addBefore(importNodeToInsert, getInsertPosition(file, from, priority)); + file.addBefore(nodeToInsert, getInsertPosition(file, from, priority)); } } catch (IncorrectOperationException e) { @@ -228,7 +254,7 @@ public class AddImportHelper { } } final PyElementGenerator generator = PyElementGenerator.getInstance(file.getProject()); - PyImportElement importElement = generator.createImportElement(LanguageLevel.forElement(file), name); + final PyImportElement importElement = generator.createImportElement(LanguageLevel.forElement(file), name); existingImport.add(importElement); return true; } @@ -239,7 +265,8 @@ public class AddImportHelper { public static void addImport(final PsiNamedElement target, final PsiFile file, final PyElement element) { final boolean useQualified = !PyCodeInsightSettings.getInstance().PREFER_FROM_IMPORT; - final PsiFileSystemItem toImport = target instanceof PsiFileSystemItem ? ((PsiFileSystemItem)target).getParent() : target.getContainingFile(); + final PsiFileSystemItem toImport = + target instanceof PsiFileSystemItem ? ((PsiFileSystemItem)target).getParent() : target.getContainingFile(); final ImportPriority priority = getImportPriority(file, toImport); final QualifiedName qName = QualifiedNameFinder.findCanonicalImportPath(target, element); if (qName == null) return; diff --git a/python/src/com/jetbrains/python/codeInsight/imports/AutoImportQuickFix.java b/python/src/com/jetbrains/python/codeInsight/imports/AutoImportQuickFix.java index 47c9ca9f8ee5..ce3c07932eb4 100644 --- a/python/src/com/jetbrains/python/codeInsight/imports/AutoImportQuickFix.java +++ b/python/src/com/jetbrains/python/codeInsight/imports/AutoImportQuickFix.java @@ -29,6 +29,7 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiFileSystemItem; import com.intellij.psi.PsiReference; +import com.intellij.psi.util.QualifiedName; import com.intellij.util.IncorrectOperationException; import com.jetbrains.python.PyBundle; import com.jetbrains.python.codeInsight.PyCodeInsightSettings; @@ -36,7 +37,6 @@ import com.jetbrains.python.psi.PyElement; import com.jetbrains.python.psi.PyFunction; import com.jetbrains.python.psi.PyImportElement; import com.jetbrains.python.psi.PyQualifiedExpression; -import com.intellij.psi.util.QualifiedName; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -138,7 +138,7 @@ public class AutoImportQuickFix implements LocalQuickFix, HighPriorityAction { myImports.size() > 1, ImportCandidateHolder.getQualifiedName(name, myImports.get(0).getPath(), myImports.get(0).getImportElement()) ); - final ImportFromExistingAction action = new ImportFromExistingAction(myNode, myImports, name, myUseQualifiedImport); + final ImportFromExistingAction action = new ImportFromExistingAction(myNode, myImports, name, myUseQualifiedImport, false); action.onDone(new Runnable() { public void run() { myExpended = true; @@ -166,11 +166,16 @@ public class AutoImportQuickFix implements LocalQuickFix, HighPriorityAction { if (!FileModificationService.getInstance().prepareFileForWrite(file)) return; if (ImportFromExistingAction.isResolved(myReference)) return; // act - ImportFromExistingAction action = new ImportFromExistingAction(myNode, myImports, getNameToImport(), myUseQualifiedImport); + ImportFromExistingAction action = createAction(); action.execute(); // assume that action runs in WriteAction on its own behalf myExpended = true; } + @NotNull + protected ImportFromExistingAction createAction() { + return new ImportFromExistingAction(myNode, myImports, getNameToImport(), myUseQualifiedImport, false); + } + public void sortCandidates() { Collections.sort(myImports); } @@ -203,4 +208,27 @@ public class AutoImportQuickFix implements LocalQuickFix, HighPriorityAction { } return false; } + + @NotNull + public AutoImportQuickFix forLocalImport() { + return new AutoImportQuickFix(myNode, myReference, myUseQualifiedImport) { + @NotNull + @Override + public String getName() { + return super.getName() + " locally"; + } + + @NotNull + @Override + public String getFamilyName() { + return "import locally"; + } + + @NotNull + @Override + protected ImportFromExistingAction createAction() { + return new ImportFromExistingAction(myNode, myImports, getNameToImport(), myUseQualifiedImport, true); + } + }; + } } diff --git a/python/src/com/jetbrains/python/codeInsight/imports/ImportCandidateHolder.java b/python/src/com/jetbrains/python/codeInsight/imports/ImportCandidateHolder.java index b60348d34938..e75df5b9a0d0 100644 --- a/python/src/com/jetbrains/python/codeInsight/imports/ImportCandidateHolder.java +++ b/python/src/com/jetbrains/python/codeInsight/imports/ImportCandidateHolder.java @@ -24,18 +24,25 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiFileSystemItem; -import com.jetbrains.python.psi.*; import com.intellij.psi.util.QualifiedName; +import com.jetbrains.python.psi.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** * An immutable holder of information for one auto-import candidate. - * User: dcheryasov - * Date: Apr 23, 2009 4:17:50 PM + * <p/> + * There can be do different flavors of such candidates: + * <ul> + * <li>Candidates based on existing imports in module. In this case {@link #getImportElement()} must return not {@code null}.</li> + * <li>Candidates not yet imported. In this case {@link #getPath()} must return not {@code null}.</li> + * </ul> + * <p/> + * + * @author dcheryasov */ // visibility is intentionally package-level -class ImportCandidateHolder implements Comparable { +class ImportCandidateHolder implements Comparable<ImportCandidateHolder> { private final PsiElement myImportable; private final PyImportElement myImportElement; private final PsiFileSystemItem myFile; @@ -43,15 +50,19 @@ class ImportCandidateHolder implements Comparable { /** * Creates new instance. - * @param importable an element that could be imported either from import element or from file. - * @param file the file which is the source of the importable + * + * @param importable an element that could be imported either from import element or from file. + * @param file the file which is the source of the importable (module for symbols, containing directory for modules and packages) * @param importElement an existing import element that can be a source for the importable. - * @param path import path for the file, as a qualified name (a.b.c) + * @param path import path for the file, as a qualified name (a.b.c) + * For top-level imported symbols it's <em>qualified name of containing module</em> (or package for __init__.py). + * For modules and packages it should be <em>qualified name of their parental package</em> + * (empty for modules and packages located at source roots). + * + * @see com.jetbrains.python.codeInsight.imports.PythonReferenceImporter#proposeImportFix */ - public ImportCandidateHolder( - @NotNull PsiElement importable, @NotNull PsiFileSystemItem file, - @Nullable PyImportElement importElement, @Nullable QualifiedName path - ) { + public ImportCandidateHolder(@NotNull PsiElement importable, @NotNull PsiFileSystemItem file, + @Nullable PyImportElement importElement, @Nullable QualifiedName path) { myFile = file; myImportable = importable; myImportElement = importElement; @@ -59,18 +70,22 @@ class ImportCandidateHolder implements Comparable { assert importElement != null || path != null; // one of these must be present } + @NotNull public PsiElement getImportable() { return myImportable; } + @Nullable public PyImportElement getImportElement() { return myImportElement; } + @NotNull public PsiFileSystemItem getFile() { return myFile; } + @Nullable public QualifiedName getPath() { return myPath; } @@ -78,15 +93,17 @@ class ImportCandidateHolder implements Comparable { /** * Helper method that builds an import path, handling all these "import foo", "import foo as bar", "from bar import foo", etc. * Either importPath or importSource must be not null. - * @param name what is ultimately imported. + * + * @param name what is ultimately imported. * @param importPath known path to import the name. - * @param source known ImportElement to import the name; its 'as' clause is used if present. + * @param source known ImportElement to import the name; its 'as' clause is used if present. * @return a properly qualified name. */ - public static String getQualifiedName(String name, QualifiedName importPath, PyImportElement source) { - StringBuilder sb = new StringBuilder(); + @NotNull + public static String getQualifiedName(@NotNull String name, @Nullable QualifiedName importPath, @Nullable PyImportElement source) { + final StringBuilder sb = new StringBuilder(); if (source != null) { - PsiElement parent = source.getParent(); + final PsiElement parent = source.getParent(); if (parent instanceof PyFromImportStatement) { sb.append(name); } @@ -95,7 +112,7 @@ class ImportCandidateHolder implements Comparable { } } else { - if (importPath.getComponentCount() > 0) { + if (importPath != null && importPath.getComponentCount() > 0) { sb.append(importPath).append("."); } sb.append(name); @@ -103,8 +120,9 @@ class ImportCandidateHolder implements Comparable { return sb.toString(); } - public String getPresentableText(String myName) { - StringBuilder sb = new StringBuilder(getQualifiedName(myName, myPath, myImportElement)); + @NotNull + public String getPresentableText(@NotNull String myName) { + final StringBuilder sb = new StringBuilder(getQualifiedName(myName, myPath, myImportElement)); PsiElement parent = null; if (myImportElement != null) { parent = myImportElement.getParent(); @@ -113,13 +131,15 @@ class ImportCandidateHolder implements Comparable { sb.append(((PyFunction)myImportable).getParameterList().getPresentableText(false)); } else if (myImportable instanceof PyClass) { - PyClass[] supers = ((PyClass)myImportable).getSuperClasses(); + final PyClass[] supers = ((PyClass)myImportable).getSuperClasses(); if (supers.length > 0) { sb.append("("); // ", ".join(x.getName() for x in getSuperClasses()) - String[] super_names = new String[supers.length]; - for (int i=0; i < supers.length; i += 1) super_names[i] = supers[i].getName(); - sb.append(StringUtil.join(super_names, ", ")); + final String[] superNames = new String[supers.length]; + for (int i = 0; i < supers.length; i += 1) { + superNames[i] = supers[i].getName(); + } + sb.append(StringUtil.join(superNames, ", ")); sb.append(")"); } } @@ -135,10 +155,9 @@ class ImportCandidateHolder implements Comparable { return sb.toString(); } - public int compareTo(Object o) { - ImportCandidateHolder rhs = (ImportCandidateHolder) o; - int lRelevance = getRelevance(); - int rRelevance = rhs.getRelevance(); + public int compareTo(@NotNull ImportCandidateHolder rhs) { + final int lRelevance = getRelevance(); + final int rRelevance = rhs.getRelevance(); if (rRelevance != lRelevance) { return rRelevance - lRelevance; } @@ -150,7 +169,7 @@ class ImportCandidateHolder implements Comparable { } int getRelevance() { - Project project = myImportable.getProject(); + final Project project = myImportable.getProject(); final PsiFile psiFile = myImportable.getContainingFile(); final VirtualFile vFile = psiFile == null ? null : psiFile.getVirtualFile(); if (vFile == null) return 0; diff --git a/python/src/com/jetbrains/python/codeInsight/imports/ImportFromExistingAction.java b/python/src/com/jetbrains/python/codeInsight/imports/ImportFromExistingAction.java index 58254d999a90..e7a8151bf52d 100644 --- a/python/src/com/jetbrains/python/codeInsight/imports/ImportFromExistingAction.java +++ b/python/src/com/jetbrains/python/codeInsight/imports/ImportFromExistingAction.java @@ -53,6 +53,7 @@ public class ImportFromExistingAction implements QuestionAction { String myName; boolean myUseQualifiedImport; private Runnable myOnDoneCallback; + private final boolean myImportLocally; /** * @param target element to become qualified as imported. @@ -60,12 +61,13 @@ public class ImportFromExistingAction implements QuestionAction { * @param name relevant name ot the target element (e.g. of identifier in an expression). * @param useQualified if True, use qualified "import modulename" instead of "from modulename import ...". */ - public ImportFromExistingAction(@NotNull PyElement target, @NotNull List<ImportCandidateHolder> sources, String name, - boolean useQualified) { + public ImportFromExistingAction(@NotNull PyElement target, @NotNull List<ImportCandidateHolder> sources, @NotNull String name, + boolean useQualified, boolean importLocally) { myTarget = target; mySources = sources; myName = name; myUseQualifiedImport = useQualified; + myImportLocally = importLocally; } public void onDone(Runnable callback) { @@ -151,25 +153,41 @@ public class ImportFromExistingAction implements QuestionAction { if (manager.isInjectedFragment(file)) { file = manager.getTopLevelFile(myTarget); } + // We are trying to import top-level module or package which thus cannot be qualified if (isRoot(item.getFile())) { - AddImportHelper.addImportStatement(file, myName, null, priority); + if (myImportLocally) { + AddImportHelper.addLocalImportStatement(myTarget, myName); + } else { + AddImportHelper.addImportStatement(file, myName, null, priority); + } } else { - String qualifiedName = item.getPath().toString(); + final String qualifiedName = item.getPath().toString(); if (myUseQualifiedImport) { String nameToImport = qualifiedName; if (item.getImportable() instanceof PsiFileSystemItem) { nameToImport += "." + myName; } - AddImportHelper.addImportStatement(file, nameToImport, null, priority); + if (myImportLocally) { + AddImportHelper.addLocalImportStatement(myTarget, nameToImport); + } + else { + AddImportHelper.addImportStatement(file, nameToImport, null, priority); + } myTarget.replace(gen.createExpressionFromText(LanguageLevel.forElement(myTarget), qualifiedName + "." + myName)); } else { - AddImportHelper.addImportFrom(file, myTarget, qualifiedName, myName, null, priority); + if (myImportLocally) { + AddImportHelper.addLocalFromImportStatement(myTarget, qualifiedName, myName); + } + else { + AddImportHelper.addImportFromStatement(file, qualifiedName, myName, null, priority); + } } } } + private void addToExistingImport(PyImportElement src) { final PyElementGenerator gen = PyElementGenerator.getInstance(myTarget.getProject()); // did user choose 'import' or 'from import'? diff --git a/python/src/com/jetbrains/python/codeInsight/imports/PyImportOptimizer.java b/python/src/com/jetbrains/python/codeInsight/imports/PyImportOptimizer.java index 5d24e3b89332..d5fd5a64495e 100644 --- a/python/src/com/jetbrains/python/codeInsight/imports/PyImportOptimizer.java +++ b/python/src/com/jetbrains/python/codeInsight/imports/PyImportOptimizer.java @@ -92,7 +92,7 @@ public class PyImportOptimizer implements ImportOptimizer { for (PyImportElement importElement : importStatement.getImportElements()) { myMissorted = true; PsiElement toImport = importElement.resolve(); - final PyImportStatement splitImport = myGenerator.createImportStatementFromText(langLevel, "import " + importElement.getText()); + final PyImportStatement splitImport = myGenerator.createImportStatement(langLevel, importElement.getText(), null); prioritize(splitImport, toImport); } } diff --git a/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibTypeProvider.java b/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibTypeProvider.java index 346d40f3273e..c28bc29455c9 100644 --- a/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibTypeProvider.java +++ b/python/src/com/jetbrains/python/codeInsight/stdlib/PyStdlibTypeProvider.java @@ -36,6 +36,8 @@ import java.util.List; import java.util.Map; import java.util.Set; +import static com.jetbrains.python.psi.PyUtil.as; + /** * @author yole */ @@ -132,6 +134,23 @@ public class PyStdlibTypeProvider extends PyTypeProviderBase { } } } + else if ("__builtin__.tuple.__add__".equals(qname) && callSite instanceof PyBinaryExpression) { + final PyBinaryExpression expression = (PyBinaryExpression)callSite; + final PyTupleType leftTupleType = as(context.getType(expression.getLeftExpression()), PyTupleType.class); + if (expression.getRightExpression() != null) { + final PyTupleType rightTupleType = as(context.getType(expression.getRightExpression()), PyTupleType.class); + if (leftTupleType != null && rightTupleType != null) { + final PyType[] elementTypes = new PyType[leftTupleType.getElementCount() + rightTupleType.getElementCount()]; + for (int i = 0; i < leftTupleType.getElementCount(); i++) { + elementTypes[i] = leftTupleType.getElementType(i); + } + for (int i = 0; i < rightTupleType.getElementCount(); i++) { + elementTypes[i + leftTupleType.getElementCount()] = rightTupleType.getElementType(i); + } + return PyTupleType.create(function, elementTypes); + } + } + } } return null; } diff --git a/python/src/com/jetbrains/python/configuration/PyIntegratedToolsConfigurable.java b/python/src/com/jetbrains/python/configuration/PyIntegratedToolsConfigurable.java index 8afd9cc4e674..fa869bfca359 100644 --- a/python/src/com/jetbrains/python/configuration/PyIntegratedToolsConfigurable.java +++ b/python/src/com/jetbrains/python/configuration/PyIntegratedToolsConfigurable.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. @@ -120,6 +120,7 @@ public class PyIntegratedToolsConfigurable implements SearchableConfigurable, No myErrorPanel.add(facetErrorPanel.getComponent(), BorderLayout.CENTER); facetErrorPanel.getValidatorsManager().registerValidator(new FacetEditorValidator() { + @NotNull @Override public ValidationResult check() { final Sdk sdk = PythonSdkType.findPythonSdk(myModule); diff --git a/python/src/com/jetbrains/python/console/PyConsoleOptions.java b/python/src/com/jetbrains/python/console/PyConsoleOptions.java index e76c2ae576b0..59ae6875d1e9 100644 --- a/python/src/com/jetbrains/python/console/PyConsoleOptions.java +++ b/python/src/com/jetbrains/python/console/PyConsoleOptions.java @@ -95,7 +95,7 @@ public class PyConsoleOptions implements PersistentStateComponent<PyConsoleOptio @Tag("console-settings") public static class PyConsoleSettings { - public String myCustomStartScript = RunPythonConsoleAction.CONSOLE_START_COMMAND; + public String myCustomStartScript = PydevConsoleRunner.CONSOLE_START_COMMAND; public String mySdkHome = null; public String myInterpreterOptions = ""; public boolean myUseModuleSdk; diff --git a/python/src/com/jetbrains/python/console/PydevConsoleRunner.java b/python/src/com/jetbrains/python/console/PydevConsoleRunner.java index 848818b95e27..b3ebacd83ae3 100644 --- a/python/src/com/jetbrains/python/console/PydevConsoleRunner.java +++ b/python/src/com/jetbrains/python/console/PydevConsoleRunner.java @@ -17,9 +17,8 @@ package com.jetbrains.python.console; import com.google.common.base.CharMatcher; import com.google.common.base.Function; -import com.google.common.base.Predicate; -import com.google.common.collect.FluentIterable; -import com.google.common.collect.Lists; +import com.google.common.base.Joiner; +import com.google.common.collect.Collections2; import com.google.common.collect.Maps; import com.intellij.execution.ExecutionException; import com.intellij.execution.ExecutionHelper; @@ -48,6 +47,8 @@ import com.intellij.openapi.editor.actionSystem.EditorWriteActionHandler; import com.intellij.openapi.editor.actions.SplitLineAction; import com.intellij.openapi.editor.ex.EditorEx; import com.intellij.openapi.fileEditor.FileDocumentManager; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleManager; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.progress.Task; @@ -58,19 +59,17 @@ import com.intellij.openapi.projectRoots.Sdk; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.Couple; import com.intellij.openapi.util.Key; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.io.StreamUtil; import com.intellij.openapi.vfs.CharsetToolkit; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.encoding.EncodingManager; -import com.intellij.openapi.wm.ToolWindow; -import com.intellij.openapi.wm.ToolWindowManager; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.impl.source.tree.FileElement; import com.intellij.remote.RemoteSshProcess; import com.intellij.testFramework.LightVirtualFile; -import com.intellij.ui.content.Content; import com.intellij.util.ArrayUtil; import com.intellij.util.IJSwingUtilities; import com.intellij.util.PathMappingSettings; @@ -94,6 +93,7 @@ import com.jetbrains.python.run.ProcessRunner; import com.jetbrains.python.run.PythonCommandLineState; import com.jetbrains.python.run.PythonTracebackFilter; import com.jetbrains.python.sdk.PySdkUtil; +import com.jetbrains.python.sdk.PythonSdkType; import com.jetbrains.python.sdk.flavors.PythonSdkFlavor; import icons.PythonIcons; import org.apache.xmlrpc.XmlRpcException; @@ -115,6 +115,9 @@ import static com.jetbrains.python.sdk.PythonEnvUtil.setPythonUnbuffered; * @author oleg */ public class PydevConsoleRunner extends AbstractConsoleRunnerWithHistory<PythonConsoleView> { + public static final String WORKING_DIR_ENV = "WORKING_DIR_AND_PYTHON_PATHS"; + public static final String CONSOLE_START_COMMAND = "import sys; print('Python %s on %s' % (sys.version, sys.platform))\n" + + "sys.path.extend([" + WORKING_DIR_ENV + "])\n"; private static final Logger LOG = Logger.getInstance(PydevConsoleRunner.class.getName()); @SuppressWarnings("SpellCheckingInspection") public static final String PYDEV_PYDEVCONSOLE_PY = "pydev/pydevconsole.py"; @@ -139,18 +142,100 @@ public class PydevConsoleRunner extends AbstractConsoleRunnerWithHistory<PythonC private static final long APPROPRIATE_TO_WAIT = 60000; private PyRemoteSdkCredentials myRemoteCredentials; - private ToolWindow myToolWindow; private String myConsoleTitle = null; - protected PydevConsoleRunner(@NotNull final Project project, + public PydevConsoleRunner(@NotNull final Project project, @NotNull Sdk sdk, @NotNull final PyConsoleType consoleType, @Nullable final String workingDir, - Map<String, String> environmentVariables) { + Map<String, String> environmentVariables, String ... statementsToExecute) { super(project, consoleType.getTitle(), workingDir); mySdk = sdk; myConsoleType = consoleType; myEnvironmentVariables = environmentVariables; + myStatementsToExecute = statementsToExecute; + } + + public static PathMappingSettings getMappings(Project project, Sdk sdk) { + PathMappingSettings mappingSettings = null; + if (PySdkUtil.isRemote(sdk)) { + PythonRemoteInterpreterManager instance = PythonRemoteInterpreterManager.getInstance(); + if (instance != null) { + //noinspection ConstantConditions + mappingSettings = + instance.setupMappings(project, (PyRemoteSdkAdditionalDataBase)sdk.getSdkAdditionalData(), null); + } + } + return mappingSettings; + } + + @NotNull + public static Pair<Sdk, Module> findPythonSdkAndModule(@NotNull Project project, @Nullable Module contextModule) { + Sdk sdk = null; + Module module = null; + PyConsoleOptions.PyConsoleSettings settings = PyConsoleOptions.getInstance(project).getPythonConsoleSettings(); + String sdkHome = settings.getSdkHome(); + if (sdkHome != null) { + sdk = PythonSdkType.findSdkByPath(sdkHome); + if (settings.getModuleName() != null) { + module = ModuleManager.getInstance(project).findModuleByName(settings.getModuleName()); + } + else { + module = contextModule; + if (module == null && ModuleManager.getInstance(project).getModules().length > 0) { + module = ModuleManager.getInstance(project).getModules()[0]; + } + } + } + if (sdk == null && settings.isUseModuleSdk()) { + if (contextModule != null) { + module = contextModule; + } + else if (settings.getModuleName() != null) { + module = ModuleManager.getInstance(project).findModuleByName(settings.getModuleName()); + } + if (module != null) { + if (PythonSdkType.findPythonSdk(module) != null) { + sdk = PythonSdkType.findPythonSdk(module); + } + } + } + else if (contextModule != null) { + if (module == null) { + module = contextModule; + } + if (sdk == null) { + sdk = PythonSdkType.findPythonSdk(module); + } + } + + if (sdk == null) { + for (Module m : ModuleManager.getInstance(project).getModules()) { + if (PythonSdkType.findPythonSdk(m) != null) { + sdk = PythonSdkType.findPythonSdk(m); + module = m; + break; + } + } + } + if (sdk == null) { + if (PythonSdkType.getAllSdks().size() > 0) { + //noinspection UnusedAssignment + sdk = PythonSdkType.getAllSdks().get(0); //take any python sdk + } + } + return Pair.create(sdk, module); + } + + public static String constructPythonPathCommand(Collection<String> pythonPath, String command) { + final String path = Joiner.on(", ").join(Collections2.transform(pythonPath, new Function<String, String>() { + @Override + public String apply(String input) { + return "'" + input.replace("\\", "\\\\").replace("'", "\\'") + "'"; + } + })); + + return command.replace(WORKING_DIR_ENV, path); } public void setStatementsToExecute(String... statementsToExecute) { @@ -196,21 +281,6 @@ public class PydevConsoleRunner extends AbstractConsoleRunnerWithHistory<PythonC return actions; } - @NotNull - public static PydevConsoleRunner createAndRun(@NotNull final Project project, - @NotNull final Sdk sdk, - @NotNull final PyConsoleType consoleType, - @Nullable final String workingDirectory, - @NotNull final Map<String, String> environmentVariables, - @Nullable final ToolWindow toolWindow, - final String... statements2execute) { - final PydevConsoleRunner consoleRunner = create(project, sdk, consoleType, workingDirectory, environmentVariables); - consoleRunner.setToolWindow(toolWindow); - consoleRunner.setStatementsToExecute(statements2execute); - consoleRunner.run(); - return consoleRunner; - } - public void run() { UIUtil.invokeAndWaitIfNeeded(new Runnable() { @Override @@ -250,16 +320,7 @@ public class PydevConsoleRunner extends AbstractConsoleRunnerWithHistory<PythonC Sdk sdk, PyConsoleType consoleType, String workingDirectory) { - return create(project, sdk, consoleType, workingDirectory, Maps.<String, String>newHashMap()); - } - - @NotNull - private static PydevConsoleRunner create(@NotNull final Project project, - @NotNull final Sdk sdk, - @NotNull final PyConsoleType consoleType, - @Nullable final String workingDirectory, - @NotNull final Map<String, String> environmentVariables) { - return new PydevConsoleRunner(project, sdk, consoleType, workingDirectory, environmentVariables); + return new PydevConsoleRunner(project, sdk, consoleType, workingDirectory, Maps.<String, String>newHashMap(), new String[]{}); } private static int[] findAvailablePorts(Project project, PyConsoleType consoleType) { @@ -501,12 +562,6 @@ public class PydevConsoleRunner extends AbstractConsoleRunnerWithHistory<PythonC return myConsoleTitle; } - @Override - protected void showConsole(Executor defaultExecutor, RunContentDescriptor contentDescriptor) { - PythonConsoleToolWindow terminalView = PythonConsoleToolWindow.getInstance(getProject()); - terminalView.init(getToolWindow(), contentDescriptor); - } - protected AnAction createRerunAction() { return new RestartAction(this); } @@ -631,10 +686,7 @@ public class PydevConsoleRunner extends AbstractConsoleRunnerWithHistory<PythonC return stopAction; } - private void clearContent(RunContentDescriptor descriptor) { - Content content = getToolWindow().getContentManager().findContent(descriptor.getDisplayName()); - assert content != null; - getToolWindow().getContentManager().removeContent(content, true); + protected void clearContent(RunContentDescriptor descriptor) { } private AnAction createConsoleStoppingAction(final AnAction generalStopAction) { @@ -792,16 +844,6 @@ public class PydevConsoleRunner extends AbstractConsoleRunnerWithHistory<PythonC } } - public ToolWindow getToolWindow() { - if (myToolWindow == null) { - myToolWindow = ToolWindowManager.getInstance(getProject()).getToolWindow(PythonConsoleToolWindowFactory.ID); - } - return myToolWindow; - } - - public void setToolWindow(ToolWindow toolWindow) { - myToolWindow = toolWindow; - } public interface ConsoleListener { void handleConsoleInitialized(LanguageConsoleView consoleView); @@ -955,20 +997,7 @@ public class PydevConsoleRunner extends AbstractConsoleRunnerWithHistory<PythonC return session; } - @Override - protected List<String> getActiveConsoleNames(final String consoleTitle) { - return FluentIterable.from( - Lists.newArrayList(PythonConsoleToolWindow.getInstance(getProject()).getToolWindow().getContentManager().getContents())).transform( - new Function<Content, String>() { - @Override - public String apply(Content input) { - return input.getDisplayName(); - } - }).filter(new Predicate<String>() { - @Override - public boolean apply(String input) { - return input.contains(consoleTitle); - } - }).toList(); + public static PythonConsoleRunnerFactory factory() { + return new PydevConsoleRunnerFactory(); } } diff --git a/python/src/com/jetbrains/python/console/PydevConsoleRunnerFactory.java b/python/src/com/jetbrains/python/console/PydevConsoleRunnerFactory.java new file mode 100644 index 000000000000..5bed751d204e --- /dev/null +++ b/python/src/com/jetbrains/python/console/PydevConsoleRunnerFactory.java @@ -0,0 +1,122 @@ +/* + * 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.console; + +import com.google.common.collect.Maps; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.module.ModuleManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.roots.ModuleRootManager; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.PathMappingSettings; +import com.jetbrains.python.buildout.BuildoutFacet; +import com.jetbrains.python.run.PythonCommandLineState; +import com.jetbrains.python.sdk.PythonEnvUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** +* @author traff +*/ +public class PydevConsoleRunnerFactory extends PythonConsoleRunnerFactory { + @Override + public PydevConsoleRunner createConsoleRunner(@NotNull Project project, + @Nullable Module contextModule) { + Pair<Sdk, Module> sdkAndModule = PydevConsoleRunner.findPythonSdkAndModule(project, contextModule); + + Module module = sdkAndModule.second; + Sdk sdk = sdkAndModule.first; + + assert sdk != null; + + PathMappingSettings mappingSettings = PydevConsoleRunner.getMappings(project, sdk); + + String[] setupFragment; + + PyConsoleOptions.PyConsoleSettings settingsProvider = PyConsoleOptions.getInstance(project).getPythonConsoleSettings(); + Collection<String> pythonPath = PythonCommandLineState.collectPythonPath(module, settingsProvider.addContentRoots(), + settingsProvider.addSourceRoots()); + + if (mappingSettings != null) { + pythonPath = mappingSettings.convertToRemote(pythonPath); + } + + String customStartScript = settingsProvider == null ? "" : settingsProvider.getCustomStartScript(); + + if (customStartScript.trim().length() > 0) { + customStartScript = "\n" + customStartScript; + } + + String selfPathAppend = PydevConsoleRunner.constructPythonPathCommand(pythonPath, customStartScript); + + String workingDir = settingsProvider.getWorkingDirectory(); + if (StringUtil.isEmpty(workingDir)) { + if (module != null && ModuleRootManager.getInstance(module).getContentRoots().length > 0) { + workingDir = ModuleRootManager.getInstance(module).getContentRoots()[0].getPath(); + } + else { + if (ModuleManager.getInstance(project).getModules().length > 0) { + VirtualFile[] roots = ModuleRootManager.getInstance(ModuleManager.getInstance(project).getModules()[0]).getContentRoots(); + if (roots.length > 0) { + workingDir = roots[0].getPath(); + } + } + } + } + + if (mappingSettings != null) { + workingDir = mappingSettings.convertToRemote(workingDir); + } + + BuildoutFacet facet = null; + if (module != null) { + facet = BuildoutFacet.getInstance(module); + } + + if (facet != null) { + List<String> path = facet.getAdditionalPythonPath(); + if (mappingSettings != null) { + path = mappingSettings.convertToRemote(path); + } + String prependStatement = facet.getPathPrependStatement(path); + setupFragment = new String[]{prependStatement, selfPathAppend}; + } + else { + setupFragment = new String[]{selfPathAppend}; + } + + Map<String, String> envs = Maps.newHashMap(settingsProvider.getEnvs()); + String ipythonEnabled = PyConsoleOptions.getInstance(project).isIpythonEnabled() ? "True" : "False"; + envs.put(PythonEnvUtil.IPYTHONENABLE, ipythonEnabled); + + + return createConsoleRunner(project, sdk, workingDir, envs, PyConsoleType.PYTHON, setupFragment); + } + + protected PydevConsoleRunner createConsoleRunner(Project project, + Sdk sdk, + String workingDir, + Map<String, String> envs, PyConsoleType consoleType, String ... setupFragment) { + return new PydevConsoleRunner(project, sdk, consoleType, workingDir, envs, setupFragment); + } +} diff --git a/python/src/com/jetbrains/python/console/PythonConsoleRunnerFactory.java b/python/src/com/jetbrains/python/console/PythonConsoleRunnerFactory.java new file mode 100644 index 000000000000..09638656df02 --- /dev/null +++ b/python/src/com/jetbrains/python/console/PythonConsoleRunnerFactory.java @@ -0,0 +1,34 @@ +/* + * 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.console; + +import com.intellij.openapi.components.ServiceManager; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** +* @author traff +*/ +public abstract class PythonConsoleRunnerFactory { + @NotNull + public static PythonConsoleRunnerFactory getInstance() { + return ServiceManager.getService(PythonConsoleRunnerFactory.class); + } + public abstract PydevConsoleRunner createConsoleRunner(@NotNull final Project project, + @Nullable Module contextModule); +} diff --git a/python/src/com/jetbrains/python/console/PythonConsoleToolWindow.java b/python/src/com/jetbrains/python/console/PythonConsoleToolWindow.java index e8c50e49280a..177c3528496f 100644 --- a/python/src/com/jetbrains/python/console/PythonConsoleToolWindow.java +++ b/python/src/com/jetbrains/python/console/PythonConsoleToolWindow.java @@ -1,8 +1,14 @@ package com.jetbrains.python.console; +import com.google.common.base.Function; +import com.google.common.base.Predicates; +import com.google.common.collect.FluentIterable; +import com.google.common.collect.Lists; import com.intellij.execution.ui.RunContentDescriptor; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.SimpleToolWindowPanel; +import com.intellij.openapi.util.ActionCallback; +import com.intellij.openapi.util.Key; import com.intellij.openapi.wm.ToolWindow; import com.intellij.openapi.wm.ToolWindowManager; import com.intellij.openapi.wm.ex.ToolWindowManagerEx; @@ -11,20 +17,25 @@ import com.intellij.openapi.wm.impl.content.ToolWindowContentUi; import com.intellij.ui.content.Content; import com.intellij.ui.content.ContentFactory; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; +import java.util.List; /** * @author traff */ public class PythonConsoleToolWindow { + public static final Key<RunContentDescriptor> CONTENT_DESCRIPTOR = Key.create("CONTENT_DESCRIPTOR"); private final Project myProject; private boolean myInitialized = false; + private ActionCallback myActivation = new ActionCallback(); + public PythonConsoleToolWindow(Project project) { myProject = project; } @@ -33,6 +44,17 @@ public class PythonConsoleToolWindow { return project.getComponent(PythonConsoleToolWindow.class); } + public List<RunContentDescriptor> getConsoleContentDescriptors() { + return FluentIterable.from(Lists.newArrayList(getToolWindow().getContentManager().getContents())) + .transform(new Function<Content, RunContentDescriptor>() { + @Override + public RunContentDescriptor apply(@Nullable Content input) { + return input != null ? input.getUserData(CONTENT_DESCRIPTOR) : null; + } + }).filter( + Predicates.notNull()).toList(); + } + public void init(final @NotNull ToolWindow toolWindow, final @NotNull RunContentDescriptor contentDescriptor) { addContent(toolWindow, contentDescriptor); @@ -42,7 +64,7 @@ public class PythonConsoleToolWindow { } } - private void doInit(final ToolWindow toolWindow) { + private void doInit(@NotNull final ToolWindow toolWindow) { myInitialized = true; toolWindow.setToHideOnEmptyContent(true); @@ -58,7 +80,8 @@ public class PythonConsoleToolWindow { if (window != null) { boolean visible = window.isVisible(); if (visible && toolWindow.getContentManager().getContentCount() == 0) { - RunPythonConsoleAction.runPythonConsole(myProject, null, toolWindow); + PydevConsoleRunner runner = PythonConsoleRunnerFactory.getInstance().createConsoleRunner(myProject, null); + runner.run(); } } } @@ -98,10 +121,11 @@ public class PythonConsoleToolWindow { private static void resetContent(RunContentDescriptor contentDescriptor, SimpleToolWindowPanel panel, Content content) { panel.setContent(contentDescriptor.getComponent()); - //panel.addFocusListener(createFocusListener(toolWindow)); content.setComponent(panel); content.setPreferredFocusableComponent(contentDescriptor.getComponent()); + + content.putUserData(CONTENT_DESCRIPTOR, contentDescriptor); } private static FocusListener createFocusListener(final ToolWindow toolWindow) { @@ -124,4 +148,13 @@ public class PythonConsoleToolWindow { private static JComponent getComponentToFocus(ToolWindow window) { return window.getContentManager().getComponent(); } + + public void initialized() { + myActivation.setDone(); + } + + public void activate(@NotNull Runnable runnable) { + myActivation.doWhenDone(runnable); + getToolWindow().activate(null); + } } diff --git a/python/src/com/jetbrains/python/console/PythonConsoleToolWindowFactory.java b/python/src/com/jetbrains/python/console/PythonConsoleToolWindowFactory.java index f042a539bc22..2a49d5efedd3 100644 --- a/python/src/com/jetbrains/python/console/PythonConsoleToolWindowFactory.java +++ b/python/src/com/jetbrains/python/console/PythonConsoleToolWindowFactory.java @@ -15,10 +15,12 @@ */ package com.jetbrains.python.console; +import com.intellij.execution.console.LanguageConsoleView; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; import com.intellij.openapi.wm.ToolWindow; import com.intellij.openapi.wm.ToolWindowFactory; +import org.jetbrains.annotations.NotNull; /** * @author traff @@ -27,7 +29,14 @@ public class PythonConsoleToolWindowFactory implements ToolWindowFactory, DumbAw public static final String ID = "Python Console"; @Override - public void createToolWindowContent(Project project, ToolWindow toolWindow) { - RunPythonConsoleAction.runPythonConsole(project, null, toolWindow); + public void createToolWindowContent(final @NotNull Project project, final @NotNull ToolWindow toolWindow) { + PydevConsoleRunner runner = PythonConsoleRunnerFactory.getInstance().createConsoleRunner(project, null); + runner.addConsoleListener(new PydevConsoleRunner.ConsoleListener() { + @Override + public void handleConsoleInitialized(LanguageConsoleView consoleView) { + PythonConsoleToolWindow.getInstance(project).initialized(); + } + }); + runner.run(); } } diff --git a/python/src/com/jetbrains/python/console/PythonConsoleView.java b/python/src/com/jetbrains/python/console/PythonConsoleView.java index 699294eb1c8d..86cb0e98e05c 100644 --- a/python/src/com/jetbrains/python/console/PythonConsoleView.java +++ b/python/src/com/jetbrains/python/console/PythonConsoleView.java @@ -29,6 +29,7 @@ import com.intellij.execution.ui.ObservableConsoleView; import com.intellij.ide.GeneralSettings; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.colors.EditorColorsScheme; @@ -119,7 +120,7 @@ public class PythonConsoleView extends JPanel implements LanguageConsoleView, Ob myExecuteActionHandler = consoleExecuteActionHandler; } - private void addSaveContentFocusListener(JComponent component){ + private void addSaveContentFocusListener(JComponent component) { component.addFocusListener(new FocusListener() { @Override public void focusGained(FocusEvent e) { @@ -129,7 +130,8 @@ public class PythonConsoleView extends JPanel implements LanguageConsoleView, Ob } @Override - public void focusLost(FocusEvent e) {} + public void focusLost(FocusEvent e) { + } }); } @@ -150,38 +152,53 @@ public class PythonConsoleView extends JPanel implements LanguageConsoleView, Ob @Override public void executeCode(final @NotNull String code, @Nullable final Editor editor) { - ProgressManager.getInstance().run(new Task.Backgroundable(null, "Executing code in console...", false) { + showConsole(new Runnable() { @Override - public void run(@NotNull final ProgressIndicator indicator) { - long time = System.currentTimeMillis(); - while (!myExecuteActionHandler.isEnabled() || !myExecuteActionHandler.canExecuteNow()) { - if (indicator.isCanceled()) { - break; - } - if (System.currentTimeMillis() - time > 1000) { - if (editor != null) { - UIUtil.invokeLaterIfNeeded(new Runnable() { - @Override - public void run() { - HintManager.getInstance().showErrorHint(editor, myExecuteActionHandler.getCantExecuteMessage()); + public void run() { + ProgressManager.getInstance().run(new Task.Backgroundable(null, "Executing code in console...", false) { + @Override + public void run(@NotNull final ProgressIndicator indicator) { + long time = System.currentTimeMillis(); + while (!myExecuteActionHandler.isEnabled() || !myExecuteActionHandler.canExecuteNow()) { + if (indicator.isCanceled()) { + break; + } + if (System.currentTimeMillis() - time > 1000) { + if (editor != null) { + UIUtil.invokeLaterIfNeeded(new Runnable() { + @Override + public void run() { + HintManager.getInstance().showErrorHint(editor, myExecuteActionHandler.getCantExecuteMessage()); + } + }); } - }); + return; + } + try { + Thread.sleep(300); + } + catch (InterruptedException ignored) { + } + } + if (!indicator.isCanceled()) { + doExecute(code); } - return; - } - try { - Thread.sleep(300); - } - catch (InterruptedException ignored) { } - } - if (!indicator.isCanceled()) { - doExecute(code); - } + }); } }); } + private void showConsole(@NotNull Runnable runnable) { + PythonConsoleToolWindow toolWindow = PythonConsoleToolWindow.getInstance(myProject); + if (toolWindow != null && !ApplicationManager.getApplication().isUnitTestMode()) { + toolWindow.getToolWindow().activate(runnable); + } + else { + runnable.run(); + } + } + private void doExecute(String code) { String codeFragment = PyConsoleIndentUtil.normalize(code, myExecuteActionHandler.getCurrentIndentSize()); diff --git a/python/src/com/jetbrains/python/console/PythonToolWindowConsoleRunner.java b/python/src/com/jetbrains/python/console/PythonToolWindowConsoleRunner.java new file mode 100644 index 000000000000..5c98bd5110b1 --- /dev/null +++ b/python/src/com/jetbrains/python/console/PythonToolWindowConsoleRunner.java @@ -0,0 +1,85 @@ +/* + * 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.console; + +import com.google.common.base.Function; +import com.google.common.base.Predicate; +import com.google.common.collect.FluentIterable; +import com.google.common.collect.Lists; +import com.intellij.execution.Executor; +import com.intellij.execution.ui.RunContentDescriptor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.openapi.wm.ToolWindow; +import com.intellij.openapi.wm.ToolWindowManager; +import com.intellij.ui.content.Content; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.Map; + +/** + * @author traff + */ +public class PythonToolWindowConsoleRunner extends PydevConsoleRunner { + private ToolWindow myToolWindow; + + public PythonToolWindowConsoleRunner(@NotNull Project project, + @NotNull Sdk sdk, + @NotNull PyConsoleType consoleType, + @Nullable String workingDir, Map<String, String> environmentVariables, + String ... statementsToExecute) { + super(project, sdk, consoleType, workingDir, environmentVariables, statementsToExecute); + } + + public ToolWindow getToolWindow() { + if (myToolWindow == null) { + myToolWindow = ToolWindowManager.getInstance(getProject()).getToolWindow(PythonConsoleToolWindowFactory.ID); + } + return myToolWindow; + } + + @Override + protected void showConsole(Executor defaultExecutor, @NotNull RunContentDescriptor contentDescriptor) { + PythonConsoleToolWindow terminalView = PythonConsoleToolWindow.getInstance(getProject()); + terminalView.init(getToolWindow(), contentDescriptor); + } + + @Override + protected void clearContent(RunContentDescriptor descriptor) { + Content content = getToolWindow().getContentManager().findContent(descriptor.getDisplayName()); + assert content != null; + getToolWindow().getContentManager().removeContent(content, true); + } + + @Override + protected List<String> getActiveConsoleNames(final String consoleTitle) { + return FluentIterable.from( + Lists.newArrayList(PythonConsoleToolWindow.getInstance(getProject()).getToolWindow().getContentManager().getContents())).transform( + new Function<Content, String>() { + @Override + public String apply(Content input) { + return input.getDisplayName(); + } + }).filter(new Predicate<String>() { + @Override + public boolean apply(String input) { + return input.contains(consoleTitle); + } + }).toList(); + } +} diff --git a/python/src/com/jetbrains/python/console/PythonToolWindowConsoleRunnerFactory.java b/python/src/com/jetbrains/python/console/PythonToolWindowConsoleRunnerFactory.java new file mode 100644 index 000000000000..240f3ab48cf2 --- /dev/null +++ b/python/src/com/jetbrains/python/console/PythonToolWindowConsoleRunnerFactory.java @@ -0,0 +1,34 @@ +/* + * 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.console; + +import com.intellij.openapi.project.Project; +import com.intellij.openapi.projectRoots.Sdk; + +import java.util.Map; + +/** + * @author traff + */ +public class PythonToolWindowConsoleRunnerFactory extends PydevConsoleRunnerFactory { + @Override + protected PydevConsoleRunner createConsoleRunner(Project project, + Sdk sdk, + String workingDir, + Map<String, String> envs, PyConsoleType consoleType, String ... setupFragment) { + return new PythonToolWindowConsoleRunner(project, sdk, consoleType, workingDir, envs, setupFragment); + } +} diff --git a/python/src/com/jetbrains/python/console/RunPythonConsoleAction.java b/python/src/com/jetbrains/python/console/RunPythonConsoleAction.java deleted file mode 100644 index 566adea43c87..000000000000 --- a/python/src/com/jetbrains/python/console/RunPythonConsoleAction.java +++ /dev/null @@ -1,240 +0,0 @@ -/* - * 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.console; - -import com.google.common.base.Function; -import com.google.common.base.Joiner; -import com.google.common.collect.Collections2; -import com.google.common.collect.Maps; -import com.intellij.openapi.actionSystem.AnAction; -import com.intellij.openapi.actionSystem.AnActionEvent; -import com.intellij.openapi.actionSystem.CommonDataKeys; -import com.intellij.openapi.actionSystem.LangDataKeys; -import com.intellij.openapi.module.Module; -import com.intellij.openapi.module.ModuleManager; -import com.intellij.openapi.project.DumbAware; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.projectRoots.Sdk; -import com.intellij.openapi.roots.ModuleRootManager; -import com.intellij.openapi.util.Pair; -import com.intellij.openapi.util.text.StringUtil; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.openapi.wm.ToolWindow; -import com.intellij.util.PathMappingSettings; -import com.jetbrains.python.buildout.BuildoutFacet; -import com.jetbrains.python.remote.PyRemoteSdkAdditionalDataBase; -import com.jetbrains.python.remote.PythonRemoteInterpreterManager; -import com.jetbrains.python.run.PythonCommandLineState; -import com.jetbrains.python.sdk.PySdkUtil; -import com.jetbrains.python.sdk.PythonEnvUtil; -import com.jetbrains.python.sdk.PythonSdkType; -import icons.PythonIcons; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.Collection; -import java.util.List; -import java.util.Map; - -/** - * @author oleg - */ -public class RunPythonConsoleAction extends AnAction implements DumbAware { - - public static final String WORKING_DIR_ENV = "WORKING_DIR_AND_PYTHON_PATHS"; - - public static final String CONSOLE_START_COMMAND = "import sys; print('Python %s on %s' % (sys.version, sys.platform))\n" + - "sys.path.extend([" + WORKING_DIR_ENV + "])\n"; - - public RunPythonConsoleAction() { - super(); - getTemplatePresentation().setIcon(PythonIcons.Python.Python); - } - - @Override - public void update(final AnActionEvent e) { - e.getPresentation().setVisible(true); - e.getPresentation().setEnabled(false); - final Project project = e.getData(CommonDataKeys.PROJECT); - if (project != null) { - Pair<Sdk, Module> sdkAndModule = findPythonSdkAndModule(project, e.getData(LangDataKeys.MODULE)); - if (sdkAndModule.first != null) { - e.getPresentation().setEnabled(true); - } - } - } - - public void actionPerformed(final AnActionEvent e) { - final Project project = e.getData(CommonDataKeys.PROJECT); - runPythonConsole(project, e.getData(LangDataKeys.MODULE), null); - } - - @NotNull - public static PydevConsoleRunner runPythonConsole(Project project, Module contextModule, @Nullable ToolWindow toolWindow) { - assert project != null : "Project is null"; - - Pair<Sdk, Module> sdkAndModule = findPythonSdkAndModule(project, contextModule); - - Module module = sdkAndModule.second; - Sdk sdk = sdkAndModule.first; - - assert sdk != null; - - PathMappingSettings mappingSettings = getMappings(project, sdk); - - String[] setupFragment; - - PyConsoleOptions.PyConsoleSettings settingsProvider = PyConsoleOptions.getInstance(project).getPythonConsoleSettings(); - Collection<String> pythonPath = PythonCommandLineState.collectPythonPath(module, settingsProvider.addContentRoots(), - settingsProvider.addSourceRoots()); - - if (mappingSettings != null) { - pythonPath = mappingSettings.convertToRemote(pythonPath); - } - - String customStartScript = settingsProvider == null ? "" : settingsProvider.getCustomStartScript(); - - if(customStartScript.trim().length() > 0){ - customStartScript = "\n" + customStartScript; - } - - String selfPathAppend = constructPythonPathCommand(pythonPath, customStartScript); - - String workingDir = settingsProvider.getWorkingDirectory(); - if (StringUtil.isEmpty(workingDir)) { - if (module != null && ModuleRootManager.getInstance(module).getContentRoots().length > 0) { - workingDir = ModuleRootManager.getInstance(module).getContentRoots()[0].getPath(); - } - else { - if (ModuleManager.getInstance(project).getModules().length > 0) { - VirtualFile[] roots = ModuleRootManager.getInstance(ModuleManager.getInstance(project).getModules()[0]).getContentRoots(); - if (roots.length > 0) { - workingDir = roots[0].getPath(); - } - } - } - } - - if (mappingSettings != null) { - workingDir = mappingSettings.convertToRemote(workingDir); - } - - BuildoutFacet facet = null; - if (module != null) { - facet = BuildoutFacet.getInstance(module); - } - - if (facet != null) { - List<String> path = facet.getAdditionalPythonPath(); - if (mappingSettings != null) { - path = mappingSettings.convertToRemote(path); - } - String prependStatement = facet.getPathPrependStatement(path); - setupFragment = new String[]{prependStatement, selfPathAppend}; - } - else { - setupFragment = new String[]{selfPathAppend}; - } - - Map<String, String> envs = Maps.newHashMap(settingsProvider.getEnvs()); - String ipythonEnabled = PyConsoleOptions.getInstance(project).isIpythonEnabled() ? "True" : "False"; - envs.put(PythonEnvUtil.IPYTHONENABLE, ipythonEnabled); - - return PydevConsoleRunner - .createAndRun(project, sdk, PyConsoleType.PYTHON, workingDir, envs, toolWindow, setupFragment); - } - - public static PathMappingSettings getMappings(Project project, Sdk sdk) { - PathMappingSettings mappingSettings = null; - if (PySdkUtil.isRemote(sdk)) { - PythonRemoteInterpreterManager instance = PythonRemoteInterpreterManager.getInstance(); - if (instance != null) { - mappingSettings = - instance.setupMappings(project, (PyRemoteSdkAdditionalDataBase)sdk.getSdkAdditionalData(), null); - } - } - return mappingSettings; - } - - @NotNull - private static Pair<Sdk, Module> findPythonSdkAndModule(Project project, Module contextModule) { - Sdk sdk = null; - Module module = null; - PyConsoleOptions.PyConsoleSettings settings = PyConsoleOptions.getInstance(project).getPythonConsoleSettings(); - String sdkHome = settings.getSdkHome(); - if (sdkHome != null) { - sdk = PythonSdkType.findSdkByPath(sdkHome); - if (settings.getModuleName() != null) { - module = ModuleManager.getInstance(project).findModuleByName(settings.getModuleName()); - } - else { - module = contextModule; - if (module == null && ModuleManager.getInstance(project).getModules().length > 0) { - module = ModuleManager.getInstance(project).getModules()[0]; - } - } - } - if (sdk == null && settings.isUseModuleSdk()) { - if (contextModule != null) { - module = contextModule; - } - else if (settings.getModuleName() != null) { - module = ModuleManager.getInstance(project).findModuleByName(settings.getModuleName()); - } - if (module != null) { - if (PythonSdkType.findPythonSdk(module) != null) { - sdk = PythonSdkType.findPythonSdk(module); - } - } - } - else if (contextModule != null) { - if (module == null) { - module = contextModule; - } - if (sdk == null) { - sdk = PythonSdkType.findPythonSdk(module); - } - } - - if (sdk == null) { - for (Module m : ModuleManager.getInstance(project).getModules()) { - if (PythonSdkType.findPythonSdk(m) != null) { - sdk = PythonSdkType.findPythonSdk(m); - module = m; - break; - } - } - } - if (sdk == null) { - if (PythonSdkType.getAllSdks().size() > 0) { - //noinspection UnusedAssignment - sdk = PythonSdkType.getAllSdks().get(0); //take any python sdk - } - } - return Pair.create(sdk, module); - } - - public static String constructPythonPathCommand(Collection<String> pythonPath, String command) { - final String path = Joiner.on(", ").join(Collections2.transform(pythonPath, new Function<String, String>() { - @Override - public String apply(String input) { - return "'" + input.replace("\\", "\\\\").replace("'", "\\'") + "'"; - } - })); - - return command.replace(WORKING_DIR_ENV, path); - } -} diff --git a/python/src/com/jetbrains/python/formatter/PyLanguageCodeStyleSettingsProvider.java b/python/src/com/jetbrains/python/formatter/PyLanguageCodeStyleSettingsProvider.java index 32ad8a31b003..4acfcbd60f74 100644 --- a/python/src/com/jetbrains/python/formatter/PyLanguageCodeStyleSettingsProvider.java +++ b/python/src/com/jetbrains/python/formatter/PyLanguageCodeStyleSettingsProvider.java @@ -89,7 +89,8 @@ public class PyLanguageCodeStyleSettingsProvider extends LanguageCodeStyleSettin BLANK_LINES); } else if (settingsType == SettingsType.WRAPPING_AND_BRACES_SETTINGS) { - consumer.showStandardOptions("KEEP_LINE_BREAKS", + consumer.showStandardOptions("RIGHT_MARGIN", + "KEEP_LINE_BREAKS", "WRAP_LONG_LINES", "ALIGN_MULTILINE_PARAMETERS", "ALIGN_MULTILINE_PARAMETERS_IN_CALLS"); diff --git a/python/src/com/jetbrains/python/inspections/PyStringFormatInspection.java b/python/src/com/jetbrains/python/inspections/PyStringFormatInspection.java index 162fa0b9bc60..40efca3663a0 100644 --- a/python/src/com/jetbrains/python/inspections/PyStringFormatInspection.java +++ b/python/src/com/jetbrains/python/inspections/PyStringFormatInspection.java @@ -41,6 +41,7 @@ import java.util.Map; import static com.jetbrains.python.inspections.PyStringFormatParser.filterSubstitutions; import static com.jetbrains.python.inspections.PyStringFormatParser.parsePercentFormat; +import static com.jetbrains.python.psi.PyUtil.as; /** * @author Alexey.Ivanov @@ -416,9 +417,9 @@ public class PyStringFormatInspection extends PyInspection { inspectValues(((PyParenthesizedExpression)rightExpression).getContainedExpression()); } else { - final PyType type = myTypeEvalContext.getType(rightExpression); + final PyClassType type = as(myTypeEvalContext.getType(rightExpression), PyClassType.class); if (type != null) { - if (myUsedMappingKeys.size() > 0 && !("dict".equals(type.getName()))) { + if (myUsedMappingKeys.size() > 0 && !PyABCUtil.isSubclass(type.getPyClass(), PyNames.MAPPING)) { registerProblem(rightExpression, PyBundle.message("INSP.format.requires.mapping")); return; } diff --git a/python/src/com/jetbrains/python/inspections/unresolvedReference/PyUnresolvedReferencesInspection.java b/python/src/com/jetbrains/python/inspections/unresolvedReference/PyUnresolvedReferencesInspection.java index c0fb3c1d620d..4fd58629c516 100644 --- a/python/src/com/jetbrains/python/inspections/unresolvedReference/PyUnresolvedReferencesInspection.java +++ b/python/src/com/jetbrains/python/inspections/unresolvedReference/PyUnresolvedReferencesInspection.java @@ -69,7 +69,6 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.util.*; -import java.util.HashSet; import static com.jetbrains.python.inspections.quickfix.AddIgnoredIdentifierQuickFix.END_WILDCARD; @@ -833,6 +832,9 @@ public class PyUnresolvedReferencesInspection extends PyInspection { else { actions.add(importFix); } + if (ScopeUtil.getScopeOwner(node) instanceof PyFunction) { + actions.add(importFix.forLocalImport()); + } } } diff --git a/python/src/com/jetbrains/python/packaging/PyPIPackageUtil.java b/python/src/com/jetbrains/python/packaging/PyPIPackageUtil.java index fbbdfa215e9c..2b8591f96d8e 100644 --- a/python/src/com/jetbrains/python/packaging/PyPIPackageUtil.java +++ b/python/src/com/jetbrains/python/packaging/PyPIPackageUtil.java @@ -239,8 +239,6 @@ public class PyPIPackageUtil { if (connection instanceof HttpsURLConnection) { ((HttpsURLConnection)connection).setSSLSocketFactory(sslContext.getSocketFactory()); } - connection.setConnectTimeout(5000); - connection.setReadTimeout(5000); InputStream is = connection.getInputStream(); Reader reader = new InputStreamReader(is); try{ diff --git a/python/src/com/jetbrains/python/psi/impl/PyElementGeneratorImpl.java b/python/src/com/jetbrains/python/psi/impl/PyElementGeneratorImpl.java index bb0e29f800a7..6356a1f92a0d 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyElementGeneratorImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyElementGeneratorImpl.java @@ -20,6 +20,7 @@ import com.google.common.collect.Queues; import com.intellij.lang.ASTNode; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; @@ -285,12 +286,6 @@ public class PyElementGeneratorImpl extends PyElementGenerator { throw new IllegalArgumentException("Invalid call expression text " + functionName); } - public PyImportStatement createImportStatementFromText(final LanguageLevel languageLevel, - final String text) { - final PsiFile dummyFile = createDummyFile(languageLevel, text); - return (PyImportStatement)dummyFile.getFirstChild(); - } - @Override public PyImportElement createImportElement(final LanguageLevel languageLevel, String name) { return createFromText(languageLevel, PyImportElement.class, "from foo import " + name, new int[]{0, 6}); @@ -427,6 +422,23 @@ public class PyElementGeneratorImpl extends PyElementGenerator { return createFromText(LanguageLevel.getDefault(), PsiWhiteSpace.class, " \n\n "); } + @NotNull + @Override + public PyFromImportStatement createFromImportStatement(@NotNull LanguageLevel languageLevel, @NotNull String qualifier, + @NotNull String name, @Nullable String alias) { + final String asClause = StringUtil.isNotEmpty(alias) ? " as " + alias : ""; + final String statement = "from " + qualifier + " import " + name + asClause; + return createFromText(languageLevel, PyFromImportStatement.class, statement); + } + + @NotNull + @Override + public PyImportStatement createImportStatement(@NotNull LanguageLevel languageLevel, @NotNull String name, @Nullable String alias) { + final String asClause = StringUtil.isNotEmpty(alias) ? " as " + alias : ""; + final String statement = "import " + name + asClause; + return createFromText(languageLevel, PyImportStatement.class, statement); + } + private static class CommasOnly extends NotNullPredicate<LeafPsiElement> { @Override protected boolean applyNotNull(@NotNull final LeafPsiElement input) { diff --git a/python/src/com/jetbrains/python/psi/types/PyFunctionType.java b/python/src/com/jetbrains/python/psi/types/PyFunctionType.java index 4b3adfd112f8..4bdbae69d83d 100644 --- a/python/src/com/jetbrains/python/psi/types/PyFunctionType.java +++ b/python/src/com/jetbrains/python/psi/types/PyFunctionType.java @@ -18,10 +18,12 @@ package com.jetbrains.python.psi.types; import com.intellij.psi.PsiElement; import com.intellij.util.ArrayUtil; import com.intellij.util.ProcessingContext; +import com.intellij.util.containers.ContainerUtil; import com.jetbrains.python.PyNames; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.impl.PyBuiltinCache; import com.jetbrains.python.psi.resolve.PyResolveContext; +import com.jetbrains.python.psi.resolve.QualifiedResolveResult; import com.jetbrains.python.psi.resolve.RatedResolveResult; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -30,6 +32,9 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import static com.jetbrains.python.psi.PyFunction.Modifier.STATICMETHOD; +import static com.jetbrains.python.psi.PyUtil.as; + /** * Type of a particular function that is represented as a {@link Callable} in the PSI tree. * @@ -74,20 +79,79 @@ public class PyFunctionType implements PyCallableType { @Nullable PyExpression location, @NotNull AccessDirection direction, @NotNull PyResolveContext resolveContext) { - final PyClassTypeImpl functionType = PyBuiltinCache.getInstance(getCallable()).getObjectType(PyNames.FAKE_FUNCTION); - if (functionType == null) { + final PyClassType delegate = selectFakeType(location, resolveContext.getTypeEvalContext()); + if (delegate == null) { return Collections.emptyList(); } - return functionType.resolveMember(name, location, direction, resolveContext); + return delegate.resolveMember(name, location, direction, resolveContext); } @Override public Object[] getCompletionVariants(String completionPrefix, PsiElement location, ProcessingContext context) { - final PyClassTypeImpl functionType = PyBuiltinCache.getInstance(getCallable()).getObjectType(PyNames.FAKE_FUNCTION); - if (functionType == null) { + final TypeEvalContext typeEvalContext = TypeEvalContext.userInitiated(location.getContainingFile()); + final PyClassType delegate; + if (location instanceof PyReferenceExpression) { + delegate = selectFakeType(((PyReferenceExpression)location).getQualifier(), typeEvalContext); + } + else { + delegate = PyBuiltinCache.getInstance(getCallable()).getObjectType(PyNames.FAKE_FUNCTION); + } + if (delegate == null) { return ArrayUtil.EMPTY_OBJECT_ARRAY; } - return functionType.getCompletionVariants(completionPrefix, location, context); + return delegate.getCompletionVariants(completionPrefix, location, context); + } + + /** + * Select either {@link PyNames#FAKE_FUNCTION} or {@link PyNames#FAKE_METHOD} fake class depending on concrete reference used and + * language level. Will fallback to fake function type. + */ + @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); + } + return PyBuiltinCache.getInstance(getCallable()).getObjectType(PyNames.FAKE_FUNCTION); + } + + private boolean isBoundMethodReference(@NotNull PyReferenceExpression location, @NotNull TypeEvalContext context) { + final PyFunction function = as(getCallable(), PyFunction.class); + final boolean isNonStaticMethod = function != null && function.getContainingClass() != null && function.getModifier() != STATICMETHOD; + if (isNonStaticMethod) { + // In Python 2 unbound methods have __method fake type + if (LanguageLevel.forElement(location).isOlderThan(LanguageLevel.PYTHON30)) { + return true; + } + final PyExpression qualifier; + if (location.isQualified()) { + qualifier = location.getQualifier(); + } + else { + final PyResolveContext resolveContext = PyResolveContext.noImplicits().withTypeEvalContext(context); + final QualifiedResolveResult resolveResult = location.followAssignmentsChain(resolveContext); + final List<PyExpression> qualifiers = resolveResult.getQualifiers(); + qualifier = ContainerUtil.isEmpty(qualifiers) ? null : qualifiers.get(qualifiers.size() - 1); + } + if (qualifier != null) { + //noinspection ConstantConditions + final PyType qualifierType = PyTypeChecker.toNonWeakType(context.getType(qualifier), context); + if (isInstanceType(qualifierType)) { + return true; + } + else if (qualifierType instanceof PyUnionType) { + for (PyType type : ((PyUnionType)qualifierType).getMembers()) { + if (isInstanceType(type)) { + return true; + } + } + } + } + } + return false; + } + + private static boolean isInstanceType(@Nullable PyType type) { + return type instanceof PyClassType && !((PyClassType)type).isDefinition(); } @Override diff --git a/python/src/com/jetbrains/python/refactoring/PyRefactoringUtil.java b/python/src/com/jetbrains/python/refactoring/PyRefactoringUtil.java index e4259d8581bb..fa2b4cf1204b 100644 --- a/python/src/com/jetbrains/python/refactoring/PyRefactoringUtil.java +++ b/python/src/com/jetbrains/python/refactoring/PyRefactoringUtil.java @@ -109,7 +109,7 @@ public class PyRefactoringUtil { final PyElementGenerator generator = PyElementGenerator.getInstance(project); final LanguageLevel langLevel = LanguageLevel.forElement(element1); final PyExpression expression = generator.createFromText(langLevel, PyAssignmentStatement.class, "z=" + selection).getAssignedValue(); - if (PsiUtilCore.hasErrorElementChild(expression) || !(expression instanceof PyBinaryExpression)) { + if (!(expression instanceof PyBinaryExpression) || PsiUtilCore.hasErrorElementChild(expression)) { return null; } final String parentText = parent.getText(); diff --git a/python/src/com/jetbrains/python/refactoring/PyReplaceExpressionUtil.java b/python/src/com/jetbrains/python/refactoring/PyReplaceExpressionUtil.java index f4cb9e6a73f3..2cebb3ec2720 100644 --- a/python/src/com/jetbrains/python/refactoring/PyReplaceExpressionUtil.java +++ b/python/src/com/jetbrains/python/refactoring/PyReplaceExpressionUtil.java @@ -47,6 +47,15 @@ import static com.jetbrains.python.inspections.PyStringFormatParser.parsePercent * @author Dennis.Ushakov */ public class PyReplaceExpressionUtil implements PyElementTypes { + /** + * This marker is added in cases where valid selection nevertheless breaks existing expression. + * It can happen in cases like (here {@code <start> and <end>} represent selection boundaries): + * <ul> + * <li>Selection conflicts with operator precedence: {@code n = 1 * <start>2 + 3<end>}</li> + * <li>Selection conflicts with operator associativity: {@code n = 1 + <start>2 + 3<end>}</li> + * <li>Part of string literal is selected: {@code s = 'green <start>eggs<end> and ham'}</li> + * </ul> + */ public static final Key<Pair<PsiElement, TextRange>> SELECTION_BREAKS_AST_NODE = new Key<Pair<PsiElement, TextRange>>("python.selection.breaks.ast.node"); diff --git a/python/src/com/jetbrains/python/refactoring/inline/PyInlineLocalHandler.java b/python/src/com/jetbrains/python/refactoring/inline/PyInlineLocalHandler.java index a582b1728d47..575399c61ceb 100644 --- a/python/src/com/jetbrains/python/refactoring/inline/PyInlineLocalHandler.java +++ b/python/src/com/jetbrains/python/refactoring/inline/PyInlineLocalHandler.java @@ -134,7 +134,7 @@ public class PyInlineLocalHandler extends InlineActionHandler { if (editor != null && !ApplicationManager.getApplication().isUnitTestMode()) { highlightManager.addOccurrenceHighlights(editor, refsToInline, attributes, true, null); int occurrencesCount = refsToInline.length; - String occurencesString = RefactoringBundle.message("occurences.string", occurrencesCount); + 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); diff --git a/python/src/com/jetbrains/python/refactoring/introduce/IntroduceHandler.java b/python/src/com/jetbrains/python/refactoring/introduce/IntroduceHandler.java index 29fc44105857..00448c8b69bd 100644 --- a/python/src/com/jetbrains/python/refactoring/introduce/IntroduceHandler.java +++ b/python/src/com/jetbrains/python/refactoring/introduce/IntroduceHandler.java @@ -69,6 +69,11 @@ import static com.jetbrains.python.inspections.PyStringFormatParser.*; abstract public class IntroduceHandler implements RefactoringActionHandler { protected static PsiElement findAnchor(List<PsiElement> occurrences) { PsiElement anchor = occurrences.get(0); + final Pair<PsiElement, TextRange> data = anchor.getUserData(PyReplaceExpressionUtil.SELECTION_BREAKS_AST_NODE); + // Search anchor in the origin file, not in dummy.py, if selection breaks statement and thus element was generated + if (data != null && occurrences.size() == 1) { + return PsiTreeUtil.getParentOfType(data.getFirst(), PyStatement.class); + } next: do { final PyStatement statement = PsiTreeUtil.getParentOfType(anchor, PyStatement.class); @@ -192,7 +197,7 @@ abstract public class IntroduceHandler implements RefactoringActionHandler { String text = expression.getText(); final Pair<PsiElement, TextRange> selection = expression.getUserData(PyReplaceExpressionUtil.SELECTION_BREAKS_AST_NODE); if (selection != null) { - text = selection.getSecond().substring(text); + text = selection.getSecond().substring(selection.getFirst().getText()); } if (expression instanceof PyCallExpression) { final PyExpression callee = ((PyCallExpression)expression).getCallee(); diff --git a/python/src/com/jetbrains/python/refactoring/introduce/constant/PyIntroduceConstantHandler.java b/python/src/com/jetbrains/python/refactoring/introduce/constant/PyIntroduceConstantHandler.java index af9ff583e3e4..44fdee8abdf7 100644 --- a/python/src/com/jetbrains/python/refactoring/introduce/constant/PyIntroduceConstantHandler.java +++ b/python/src/com/jetbrains/python/refactoring/introduce/constant/PyIntroduceConstantHandler.java @@ -24,6 +24,7 @@ import com.jetbrains.python.codeInsight.controlflow.ScopeOwner; import com.jetbrains.python.codeInsight.imports.AddImportHelper; import com.jetbrains.python.psi.PyExpression; import com.jetbrains.python.psi.PyFile; +import com.jetbrains.python.psi.PyParameterList; import com.jetbrains.python.refactoring.PyReplaceExpressionUtil; import com.jetbrains.python.refactoring.introduce.IntroduceHandler; import com.jetbrains.python.refactoring.introduce.IntroduceOperation; @@ -66,6 +67,11 @@ public class PyIntroduceConstantHandler extends IntroduceHandler { } @Override + protected boolean isValidIntroduceContext(PsiElement element) { + return super.isValidIntroduceContext(element) || PsiTreeUtil.getParentOfType(element, PyParameterList.class) != null; + } + + @Override protected String getHelpId() { return "python.reference.introduceConstant"; } diff --git a/python/src/com/jetbrains/python/run/PythonCommandLineState.java b/python/src/com/jetbrains/python/run/PythonCommandLineState.java index a4d1718bb006..220939efc274 100644 --- a/python/src/com/jetbrains/python/run/PythonCommandLineState.java +++ b/python/src/com/jetbrains/python/run/PythonCommandLineState.java @@ -87,7 +87,7 @@ public abstract class PythonCommandLineState extends CommandLineState { private Boolean myMultiprocessDebug = null; public boolean isDebug() { - return PyDebugRunner.PY_DEBUG_RUNNER.equals(getEnvironment().getRunnerId()); + return PyDebugRunner.PY_DEBUG_RUNNER.equals(getEnvironment().getRunner().getRunnerId()); } public static ServerSocket createServerSocket() throws ExecutionException { @@ -171,23 +171,13 @@ public abstract class PythonCommandLineState extends CommandLineState { * @throws ExecutionException */ protected ProcessHandler startProcess(CommandLinePatcher... patchers) throws ExecutionException { - - GeneralCommandLine commandLine = generateCommandLine(patchers); // Extend command line - RunnerSettings runnerSettings = getRunnerSettings(); - String runnerId = getEnvironment().getRunnerId(); - if (runnerId != null) { - PythonRunConfigurationExtensionsManager.getInstance().patchCommandLine(myConfig, runnerSettings, commandLine, runnerId); - } - + PythonRunConfigurationExtensionsManager.getInstance().patchCommandLine(myConfig, getRunnerSettings(), commandLine, getEnvironment().getRunner().getRunnerId()); Sdk sdk = PythonSdkType.findSdkByPath(myConfig.getInterpreterPath()); - - final ProcessHandler processHandler; if (PySdkUtil.isRemote(sdk)) { - assert sdk != null; processHandler = createRemoteProcessStarter().startRemoteProcess(sdk, commandLine, myConfig.getProject(), myConfig.getMappingSettings()); } diff --git a/python/src/com/jetbrains/python/run/PythonRunner.java b/python/src/com/jetbrains/python/run/PythonRunner.java index b95da7c62957..3007b0b7a5f9 100644 --- a/python/src/com/jetbrains/python/run/PythonRunner.java +++ b/python/src/com/jetbrains/python/run/PythonRunner.java @@ -59,9 +59,6 @@ public class PythonRunner extends DefaultProgramRunner { else { executionResult = state.execute(env.getExecutor(), this); } - if (executionResult == null) return null; - - final RunContentBuilder contentBuilder = new RunContentBuilder(this, executionResult, env); - return contentBuilder.showRunContent(contentToReuse); + return executionResult == null ? null : new RunContentBuilder(executionResult, env).showRunContent(contentToReuse); } } diff --git a/python/src/com/jetbrains/python/sdk/PythonSdkType.java b/python/src/com/jetbrains/python/sdk/PythonSdkType.java index f194f359e1a9..58de09370d37 100644 --- a/python/src/com/jetbrains/python/sdk/PythonSdkType.java +++ b/python/src/com/jetbrains/python/sdk/PythonSdkType.java @@ -910,6 +910,16 @@ public class PythonSdkType extends SdkType { return null; } + @Nullable + public static Sdk findPython2Sdk(List<Sdk> sdks) { + Collections.sort(sdks, PreferredSdkComparator.INSTANCE); + for (Sdk sdk : sdks) { + if (!getLanguageLevelForSdk(sdk).isPy3K()) { + return sdk; + } + } + return null; + } @Nullable public static Sdk findLocalCPython(@Nullable Module module) { @@ -997,7 +1007,7 @@ public class PythonSdkType extends SdkType { public static boolean isIncompleteRemote(Sdk sdk) { if (PySdkUtil.isRemote(sdk)) { //noinspection ConstantConditions - if (!((PyRemoteSdkAdditionalDataBase)sdk.getSdkAdditionalData()).isInitialized()) { + if (!((PyRemoteSdkAdditionalDataBase)sdk.getSdkAdditionalData()).isValid()) { return true; } } diff --git a/python/src/com/jetbrains/python/sdk/skeletons/PySkeletonRefresher.java b/python/src/com/jetbrains/python/sdk/skeletons/PySkeletonRefresher.java index 284a125e8736..4e4a43102202 100644 --- a/python/src/com/jetbrains/python/sdk/skeletons/PySkeletonRefresher.java +++ b/python/src/com/jetbrains/python/sdk/skeletons/PySkeletonRefresher.java @@ -20,7 +20,6 @@ import com.google.common.collect.Lists; import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer; import com.intellij.execution.ExecutionException; import com.intellij.notification.Notification; -import com.intellij.notification.NotificationListener; import com.intellij.notification.NotificationType; import com.intellij.notification.Notifications; import com.intellij.openapi.application.ApplicationManager; @@ -35,6 +34,7 @@ import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.JarFileSystem; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; @@ -58,7 +58,6 @@ import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import javax.swing.event.HyperlinkEvent; import java.awt.*; import java.io.*; import java.util.*; @@ -164,18 +163,27 @@ public class PySkeletonRefresher { else { message = PyBundle.message("sdk.errorlog.$0.mods.fail.in.$1.sdks", module_errors, errors.size()); } - Notifications.Bus.notify( - new Notification( - PythonSdkType.SKELETONS_TOPIC, PyBundle.message("sdk.some.skeletons.failed"), message, - NotificationType.WARNING, - new NotificationListener() { - @Override - public void hyperlinkUpdate(@NotNull Notification notification, @NotNull HyperlinkEvent event) { - new SkeletonErrorsDialog(errors, failedSdks).setVisible(true); - } - } - ) - ); + logErrors(errors, failedSdks, message); + } + } + + private static void logErrors(@NotNull final Map<String, List<String>> errors, @NotNull final List<String> failedSdks, + @NotNull final String message) { + LOG.warn(PyBundle.message("sdk.some.skeletons.failed")); + LOG.warn(message); + + if (failedSdks.size() > 0) { + LOG.warn(PyBundle.message("sdk.error.dialog.failed.sdks")); + LOG.warn(StringUtil.join(failedSdks, ", ")); + } + + if (errors.size() > 0) { + LOG.warn(PyBundle.message("sdk.error.dialog.failed.modules")); + for (String sdkName : errors.keySet()) { + for (String moduleName : errors.get(sdkName)) { + LOG.warn(moduleName); + } + } } } diff --git a/python/src/com/jetbrains/python/sdk/skeletons/SkeletonErrorsDialog.form b/python/src/com/jetbrains/python/sdk/skeletons/SkeletonErrorsDialog.form deleted file mode 100644 index c82d5fa91b00..000000000000 --- a/python/src/com/jetbrains/python/sdk/skeletons/SkeletonErrorsDialog.form +++ /dev/null @@ -1,61 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="com.jetbrains.python.sdk.skeletons.SkeletonErrorsDialog"> - <grid id="cbd77" binding="contentPane" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1"> - <margin top="10" left="10" bottom="10" right="10"/> - <constraints> - <xy x="48" y="54" width="436" height="409"/> - </constraints> - <properties/> - <border type="none"/> - <children> - <grid id="94766" layout-manager="GridLayoutManager" row-count="2" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1"> - <margin top="0" left="0" bottom="0" right="0"/> - <constraints> - <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="1" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/> - </constraints> - <properties/> - <border type="none"/> - <children> - <grid id="9538f" layout-manager="GridLayoutManager" row-count="1" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1"> - <margin top="0" left="0" bottom="0" right="0"/> - <constraints> - <grid row="1" column="0" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false"/> - </constraints> - <properties/> - <border type="none"/> - <children> - <component id="e7465" class="javax.swing.JButton" binding="buttonOK"> - <constraints> - <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="0" fill="0" indent="0" use-parent-layout="false"/> - </constraints> - <properties> - <text value="OK"/> - </properties> - </component> - </children> - </grid> - <scrollpane id="d5fbd" class="com.intellij.ui.components.JBScrollPane" binding="myScroller"> - <constraints> - <grid row="0" column="0" row-span="1" col-span="1" vsize-policy="7" hsize-policy="7" anchor="0" fill="3" indent="0" use-parent-layout="false"/> - </constraints> - <properties/> - <border type="none"/> - <children> - <component id="10d56" class="javax.swing.JTextPane" binding="myMessagePane"> - <constraints/> - <properties> - <editable value="false"/> - <enabled value="true"/> - </properties> - <clientProperties> - <JEditorPane.honorDisplayProperties class="java.lang.Boolean" value="true"/> - <caretAspectRatio class="java.lang.Float" value="0.04"/> - </clientProperties> - </component> - </children> - </scrollpane> - </children> - </grid> - </children> - </grid> -</form> diff --git a/python/src/com/jetbrains/python/sdk/skeletons/SkeletonErrorsDialog.java b/python/src/com/jetbrains/python/sdk/skeletons/SkeletonErrorsDialog.java deleted file mode 100644 index f6c6b45ceb44..000000000000 --- a/python/src/com/jetbrains/python/sdk/skeletons/SkeletonErrorsDialog.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * 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.sdk.skeletons; - -import com.intellij.ui.components.JBScrollPane; -import com.jetbrains.python.PyBundle; - -import javax.swing.*; -import javax.swing.border.EmptyBorder; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.util.List; -import java.util.Map; - -public class SkeletonErrorsDialog extends JDialog { - private JPanel contentPane; - private JButton buttonOK; - private JBScrollPane myScroller; - private JTextPane myMessagePane; - - public SkeletonErrorsDialog(Map<String, List<String>> errors, List<String> failed_sdks) { - setContentPane(contentPane); - setModal(true); - getRootPane().setDefaultButton(buttonOK); - - buttonOK.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - dispose(); - } - }); - - // fill data - myMessagePane.setContentType("text/html"); - myMessagePane.setBorder(new EmptyBorder(0, 0, 0, 0)); - StringBuilder sb = new StringBuilder("<html><body style='margin: 4pt;' "); - final Color foreground = getParent().getForeground(); - final Color background = getParent().getBackground(); - if (foreground != null && background != null) { - sb.append("text='").append(getHTMLColor(foreground)).append("' "); - sb.append("bgcolor='").append(getHTMLColor(background)).append("'"); - } - sb.append(">"); - - if (failed_sdks.size() > 0) { - sb.append("<h1>").append(PyBundle.message("sdk.error.dialog.failed.sdks")).append("</h1>"); - sb.append("<ul>"); - for (String sdk_name : failed_sdks) { - sb.append("<li>").append(sdk_name).append("</li>"); - } - sb.append("</ul><br>"); - } - - if (errors.size() > 0) { - sb.append("<h1>").append(PyBundle.message("sdk.error.dialog.failed.modules")).append("</h1>"); - for (String sdk_name : errors.keySet()) { - sb.append("<b>").append(sdk_name).append("</b><br>"); - sb.append("<ul>"); - for (String module_name : errors.get(sdk_name)) { - sb.append("<li>").append(module_name).append("</li>"); - } - sb.append("</ul>"); - } - sb.append(PyBundle.message("sdk.error.dialog.were.blacklisted")); - } - - sb.append("</body></html>"); - myMessagePane.setText(sb.toString()); - - setTitle(PyBundle.message("sdk.error.dialog.problems")); - - pack(); - setLocationRelativeTo(getParent()); - } - - private static String getHTMLColor(Color color) { - StringBuilder sb = new StringBuilder("#"); - sb.append(Integer.toHexString(color.getRGB() & 0xffffff)); - return sb.toString(); - } -} diff --git a/python/src/com/jetbrains/python/testing/PyRerunFailedTestsAction.java b/python/src/com/jetbrains/python/testing/PyRerunFailedTestsAction.java index cc468c5e40ac..0b922514e52e 100644 --- a/python/src/com/jetbrains/python/testing/PyRerunFailedTestsAction.java +++ b/python/src/com/jetbrains/python/testing/PyRerunFailedTestsAction.java @@ -72,7 +72,7 @@ public class PyRerunFailedTestsAction extends AbstractRerunFailedTestsAction { public RunProfileState getState(@NotNull Executor executor, @NotNull ExecutionEnvironment env) throws ExecutionException { final AbstractPythonRunConfiguration configuration = ((AbstractPythonRunConfiguration)getPeer()); return new FailedPythonTestCommandLineStateBase(configuration, env, - (PythonTestCommandLineStateBase)configuration.getState(executor, env)); + (PythonTestCommandLineStateBase)configuration.getState(executor, env)); } } |