summaryrefslogtreecommitdiff
path: root/python
diff options
context:
space:
mode:
Diffstat (limited to 'python')
-rw-r--r--python/IntelliLang-python/IntelliLang-python.iml1
-rw-r--r--python/IntelliLang-python/resources/META-INF/intellilang-python-support.xml (renamed from python/IntelliLang-python/src/META-INF/intellilang-python-support.xml)2
-rw-r--r--python/IntelliLang-python/resources/META-INF/plugin.xml16
-rw-r--r--python/IntelliLang-python/src/com/jetbrains/python/intelliLang/PyLanguageInjectionSupport.java2
-rw-r--r--python/IntelliLang-python/src/resources/pyInjections.xml (renamed from python/IntelliLang-python/src/pyInjections.xml)0
-rw-r--r--python/build/paths.nsi2
-rw-r--r--python/build/pycharm64_community_launcher.properties3
-rw-r--r--python/build/pycharm_community_build.gant6
-rw-r--r--python/edu/build/DMG_background.pngbin0 -> 20543 bytes
-rw-r--r--python/edu/build/build.xml46
-rw-r--r--python/edu/build/paths.nsi6
-rw-r--r--python/edu/build/plugin-list.txt11
-rw-r--r--python/edu/build/pycharm_edu_build.gant393
-rw-r--r--python/edu/build/pycharm_edu_launcher.properties3
-rw-r--r--python/edu/build/python-edu-build.iml14
-rw-r--r--python/edu/build/resources/PC_instCom.icobin0 -> 17542 bytes
-rw-r--r--python/edu/build/resources/PC_uninstCom.icobin0 -> 17542 bytes
-rw-r--r--python/edu/build/resources/headerlogo.bmpbin0 -> 25818 bytes
-rw-r--r--python/edu/build/resources/logo.bmpbin0 -> 154544 bytes
-rw-r--r--python/edu/build/resources/pycharm_inst.icobin0 -> 26694 bytes
-rw-r--r--python/edu/build/resources/pycharm_uninst.icobin0 -> 26694 bytes
-rw-r--r--python/edu/build/strings.nsi13
-rw-r--r--python/edu/main_pycharm_edu.iml21
-rw-r--r--python/edu/python-educational.iml29
-rw-r--r--python/edu/resources/idea/PyCharmEduApplicationInfo.xml23
-rw-r--r--python/edu/src/META-INF/PyCharmEduPlugin.xml36
-rw-r--r--python/edu/src/com/jetbrains/python/edu/PyCharmEduInitialConfigurator.java247
-rw-r--r--python/helpers/pycharm/_bdd_utils.py201
-rw-r--r--python/helpers/pycharm/behave_runner.py242
-rw-r--r--python/helpers/pycharm/lettuce_runner.py158
-rw-r--r--python/helpers/pycharm/tcunittest.py87
-rw-r--r--python/ide/src/com/jetbrains/python/newProject/PyCharmNewProjectDialog.java6
-rw-r--r--python/ide/src/com/jetbrains/python/newProject/actions/AbstractProjectSettingsStep.java85
-rw-r--r--python/ide/src/com/jetbrains/python/newProject/actions/ProjectSpecificSettingsStep.java7
-rw-r--r--python/ide/src/com/jetbrains/python/newProject/actions/PyCharmNewProjectStep.java10
-rw-r--r--python/openapi/src/com/jetbrains/python/newProject/PythonProjectGenerator.java2
-rw-r--r--python/psi-api/src/com/jetbrains/python/psi/PyElementGenerator.java31
-rw-r--r--python/psi-api/src/com/jetbrains/python/psi/PyElsePart.java7
-rw-r--r--python/psi-api/src/com/jetbrains/python/psi/PyStatementPart.java6
-rw-r--r--python/psi-api/src/com/jetbrains/python/psi/PyWithStatement.java2
-rw-r--r--python/psi-api/src/com/jetbrains/python/psi/StructuredDocString.java9
-rw-r--r--python/python-rest/src/com/jetbrains/rest/run/RestCommandLineState.java4
-rw-r--r--python/src/META-INF/python-core.xml12
-rw-r--r--python/src/com/jetbrains/python/PyAddImportFix.java55
-rw-r--r--python/src/com/jetbrains/python/PyBundle.properties2
-rw-r--r--python/src/com/jetbrains/python/actions/ExecuteInConsoleAction.java3
-rw-r--r--python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/PySmartEnterProcessor.java2
-rw-r--r--python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/enterProcessors/PyPlainEnterProcessor.java17
-rw-r--r--python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyArgumentListFixer.java34
-rw-r--r--python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyClassFixer.java33
-rw-r--r--python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyConditionalStatementPartFixer.java53
-rw-r--r--python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyExceptFixer.java40
-rw-r--r--python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyFixer.java20
-rw-r--r--python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyForPartFixer.java66
-rw-r--r--python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyFunctionFixer.java25
-rw-r--r--python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyMissingBracesFixer.java15
-rw-r--r--python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyParameterListFixer.java30
-rw-r--r--python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyParenthesizedFixer.java19
-rw-r--r--python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyStringLiteralFixer.java50
-rw-r--r--python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyUnconditionalStatementPartFixer.java28
-rw-r--r--python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyWithFixer.java74
-rw-r--r--python/src/com/jetbrains/python/console/PydevConsoleRunner.java116
-rw-r--r--python/src/com/jetbrains/python/console/PythonConsoleToolWindow.java127
-rw-r--r--python/src/com/jetbrains/python/console/PythonConsoleToolWindowFactory.java33
-rw-r--r--python/src/com/jetbrains/python/console/RunPythonConsoleAction.java8
-rw-r--r--python/src/com/jetbrains/python/documentation/EpydocString.java7
-rw-r--r--python/src/com/jetbrains/python/documentation/PyDocstringGenerator.java11
-rw-r--r--python/src/com/jetbrains/python/documentation/PyDocumentationSettings.java24
-rw-r--r--python/src/com/jetbrains/python/documentation/SphinxDocString.java9
-rw-r--r--python/src/com/jetbrains/python/documentation/StructuredDocStringBase.java26
-rw-r--r--python/src/com/jetbrains/python/inspections/PyInspectionVisitor.java4
-rw-r--r--python/src/com/jetbrains/python/inspections/PyShadowingNamesInspection.java2
-rw-r--r--python/src/com/jetbrains/python/inspections/quickfix/PyRemoveArgumentQuickFix.java1
-rw-r--r--python/src/com/jetbrains/python/psi/PyUtil.java19
-rw-r--r--python/src/com/jetbrains/python/psi/impl/PyElementGeneratorImpl.java22
-rw-r--r--python/src/com/jetbrains/python/psi/impl/PyElsePartImpl.java14
-rw-r--r--python/src/com/jetbrains/python/psi/impl/PyFileImpl.java5
-rw-r--r--python/src/com/jetbrains/python/psi/impl/PyFunctionBuilder.java30
-rw-r--r--python/src/com/jetbrains/python/psi/impl/PyStarImportElementImpl.java21
-rw-r--r--python/src/com/jetbrains/python/psi/impl/PyStatementPartImpl.java14
-rw-r--r--python/src/com/jetbrains/python/psi/impl/PyWithStatementImpl.java10
-rw-r--r--python/src/com/jetbrains/python/psi/resolve/PyResolveUtil.java38
-rw-r--r--python/src/com/jetbrains/python/run/PyRemoteTracebackFilter.java57
-rw-r--r--python/src/com/jetbrains/python/run/PythonCommandLineState.java26
-rw-r--r--python/src/com/jetbrains/python/run/PythonRunConfiguration.java8
-rw-r--r--python/src/com/jetbrains/python/run/PythonScriptCommandLineState.java7
-rw-r--r--python/src/com/jetbrains/python/run/PythonTracebackFilter.java14
-rw-r--r--python/src/com/jetbrains/python/sdk/PyDetectedSdk.java4
-rw-r--r--python/src/com/jetbrains/python/testing/PythonTestCommandLineStateBase.java8
-rw-r--r--python/src/com/jetbrains/python/testing/pytest/PyTestCommandLineState.java2
-rw-r--r--python/testData/codeInsight/smartEnter/withExpressionMissing.py1
-rw-r--r--python/testData/codeInsight/smartEnter/withExpressionMissing_after.py1
-rw-r--r--python/testData/inspections/PyUnresolvedReferencesInspection/UnusedImportBeforeStarDunderAll/a.py3
-rw-r--r--python/testData/inspections/PyUnresolvedReferencesInspection/UnusedImportBeforeStarDunderAll/b.py0
-rw-r--r--python/testData/inspections/PyUnresolvedReferencesInspection/UnusedImportBeforeStarDunderAll/p1/__init__.py1
-rw-r--r--python/testData/inspections/PyUnresolvedReferencesInspection/UnusedImportBeforeStarDunderAll/p1/m1.py2
-rw-r--r--python/testData/inspections/PyUnresolvedReferencesInspection/UnusedImportBeforeStarImport/a.py5
-rw-r--r--python/testData/inspections/PyUnresolvedReferencesInspection/UnusedImportBeforeStarImport/m1.py1
-rw-r--r--python/testData/inspections/PyUnresolvedReferencesInspection/UnusedImportBeforeStarImport/m2.py1
-rw-r--r--python/testSrc/com/jetbrains/python/PySmartEnterTest.java5
-rw-r--r--python/testSrc/com/jetbrains/python/fixtures/PyTestCase.java44
-rw-r--r--python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java10
102 files changed, 2539 insertions, 511 deletions
diff --git a/python/IntelliLang-python/IntelliLang-python.iml b/python/IntelliLang-python/IntelliLang-python.iml
index a877c73afd91..49ab883c711d 100644
--- a/python/IntelliLang-python/IntelliLang-python.iml
+++ b/python/IntelliLang-python/IntelliLang-python.iml
@@ -4,6 +4,7 @@
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/resources" type="java-resource" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
diff --git a/python/IntelliLang-python/src/META-INF/intellilang-python-support.xml b/python/IntelliLang-python/resources/META-INF/intellilang-python-support.xml
index 44bab2c91b05..c96f0b7f2b3a 100644
--- a/python/IntelliLang-python/src/META-INF/intellilang-python-support.xml
+++ b/python/IntelliLang-python/resources/META-INF/intellilang-python-support.xml
@@ -3,7 +3,7 @@
<idea-plugin version="2">
<extensions defaultExtensionNs="org.intellij.intelliLang">
<languageSupport implementation="com.jetbrains.python.intelliLang.PyLanguageInjectionSupport"/>
- <injectionConfig config="pyInjections.xml"/>
+ <injectionConfig config="resources/pyInjections.xml"/>
</extensions>
<extensions defaultExtensionNs="com.intellij">
<patterns.patternClass className="com.jetbrains.python.patterns.PythonPatterns" alias="py"/>
diff --git a/python/IntelliLang-python/resources/META-INF/plugin.xml b/python/IntelliLang-python/resources/META-INF/plugin.xml
new file mode 100644
index 000000000000..6b2717a6e7b1
--- /dev/null
+++ b/python/IntelliLang-python/resources/META-INF/plugin.xml
@@ -0,0 +1,16 @@
+<idea-plugin version="2" xmlns:xi="http://www.w3.org/2001/XInclude">
+ <name>Python IntelliLang</name>
+ <id>org.jetbrains.plugins.python-intelliLang</id>
+ <version>VERSION</version>
+ <description>This plugin enables language injections</description>
+ <vendor>JetBrains</vendor>
+
+ <depends>com.intellij.modules.python</depends>
+ <depends>org.intellij.intelliLang</depends>
+
+ <xi:include href="/META-INF/intellilang-python-support.xml" xpointer="xpointer(/idea-plugin/*)"/>
+
+ <extensions defaultExtensionNs="com.intellij">
+ <errorHandler implementation="com.intellij.diagnostic.ITNReporter"/>
+ </extensions>
+</idea-plugin>
diff --git a/python/IntelliLang-python/src/com/jetbrains/python/intelliLang/PyLanguageInjectionSupport.java b/python/IntelliLang-python/src/com/jetbrains/python/intelliLang/PyLanguageInjectionSupport.java
index dee16d27646f..70768781ee38 100644
--- a/python/IntelliLang-python/src/com/jetbrains/python/intelliLang/PyLanguageInjectionSupport.java
+++ b/python/IntelliLang-python/src/com/jetbrains/python/intelliLang/PyLanguageInjectionSupport.java
@@ -58,6 +58,6 @@ public class PyLanguageInjectionSupport extends AbstractLanguageInjectionSupport
@Nullable
@Override
public BaseInjection findCommentInjection(@NotNull PsiElement host, @Nullable Ref<PsiElement> commentRef) {
- return null;
+ return super.findCommentInjection(host, commentRef);
}
}
diff --git a/python/IntelliLang-python/src/pyInjections.xml b/python/IntelliLang-python/src/resources/pyInjections.xml
index fc9207aeb8f9..fc9207aeb8f9 100644
--- a/python/IntelliLang-python/src/pyInjections.xml
+++ b/python/IntelliLang-python/src/resources/pyInjections.xml
diff --git a/python/build/paths.nsi b/python/build/paths.nsi
index cf4f769c5f9f..6baf09644c8b 100644
--- a/python/build/paths.nsi
+++ b/python/build/paths.nsi
@@ -2,5 +2,5 @@
!define IMAGES_LOCATION ${COMMUNITY_DIR}\python\build\resources
;!define LICENSE_FILE ${BASE_DIR}\python\license\PyCharm_Preview_License
!define PRODUCT_PROPERTIES_FILE ${BASE_DIR}\out\pycharmCE\layout\bin\idea.properties
-!define PRODUCT_VM_OPTIONS_NAME pycharm.exe.vmoptions
+!define PRODUCT_VM_OPTIONS_NAME pycharm*.exe.vmoptions
!define PRODUCT_VM_OPTIONS_FILE ${BASE_DIR}\out\pycharmCE\win\bin\${PRODUCT_VM_OPTIONS_NAME}
diff --git a/python/build/pycharm64_community_launcher.properties b/python/build/pycharm64_community_launcher.properties
new file mode 100644
index 000000000000..ff7b0ffa11c3
--- /dev/null
+++ b/python/build/pycharm64_community_launcher.properties
@@ -0,0 +1,3 @@
+IDS_JDK_ENV_VAR=PYCHARM_JDK_64
+IDS_JDK_ONLY=true
+IDS_VM_OPTIONS=-Didea.platform.prefix=PyCharmCore -Didea.no.jre.check=true -Didea.paths.selector=__PRODUCT_PATHS_SELECTOR__
diff --git a/python/build/pycharm_community_build.gant b/python/build/pycharm_community_build.gant
index 3b793ec4498f..cfaaf33f3bd0 100644
--- a/python/build/pycharm_community_build.gant
+++ b/python/build/pycharm_community_build.gant
@@ -162,11 +162,14 @@ public layoutCommunity(String classesPath, Set usedJars) {
ant.echo(message: "PC-${buildNumber}", file: "${paths.distAll}/build.txt")
def launcher = "${paths.distWin}/bin/pycharm.exe"
+ def launcher64 = "${paths.distWin}/bin/pycharm64.exe"
List resourcePaths = ["$ch/community-resources/src",
"$ch/platform/icons/src",
"$pythonCommunityHome/resources"]
buildWinLauncher("$ch", "$ch/bin/WinLauncher/WinLauncher.exe", launcher,
appInfo, "$pythonCommunityHome/build/pycharm_community_launcher.properties", system_selector, resourcePaths)
+ buildWinLauncher("$ch", "$ch/bin/WinLauncher/WinLauncher64.exe", launcher64,
+ appInfo, "$pythonCommunityHome/build/pycharm64_community_launcher.properties", system_selector, resourcePaths)
buildWinZip("${paths.artifacts}/pycharmPC-${buildNumber}.zip", [paths.distAll, paths.distWin])
@@ -328,8 +331,7 @@ private layoutWin(Map args, String target) {
}
winScripts(target, ch, "pycharm.bat", args)
- winVMOptions(target, null, "pycharm.exe")
-
+ winVMOptions(target, null, "pycharm.exe", "pycharm64.exe")
ant.copy(file: "$home/python/help/pycharmhelp.jar", todir: "$target/help", failonerror: false)
}
diff --git a/python/edu/build/DMG_background.png b/python/edu/build/DMG_background.png
new file mode 100644
index 000000000000..3b5a4b2c1b26
--- /dev/null
+++ b/python/edu/build/DMG_background.png
Binary files differ
diff --git a/python/edu/build/build.xml b/python/edu/build/build.xml
new file mode 100644
index 000000000000..913237c81f27
--- /dev/null
+++ b/python/edu/build/build.xml
@@ -0,0 +1,46 @@
+<project name="PyCharm Educational Edition" default="all">
+ <property name="project.home" value="${basedir}/../../.."/>
+ <property name="python.home" value="${basedir}"/>
+ <property name="out.dir" value="${project.home}/out"/>
+ <property name="tmp.dir" value="${project.home}/out/tmp"/>
+
+ <target name="cleanup">
+ <delete dir="${out.dir}" failonerror="false"/>
+ </target>
+
+ <target name="init">
+ <mkdir dir="${out.dir}"/>
+ <mkdir dir="${tmp.dir}"/>
+ </target>
+
+ <macrodef name="call_gant">
+ <attribute name="script" />
+ <sequential>
+ <java failonerror="true" jar="${project.home}/lib/ant/lib/ant-launcher.jar" fork="true">
+ <jvmarg line="-Xmx612m -XX:MaxPermSize=152m -Didea.build.number=${idea.build.number} &quot;-DideaPath=${idea.path}&quot;"/>
+ <sysproperty key="java.awt.headless" value="true"/>
+ <arg line="&quot;-Dgant.script=@{script}&quot;"/>
+ <arg line="&quot;-Dteamcity.build.tempDir=${tmp.dir}&quot;"/>
+ <arg line="&quot;-Didea.build.number=${idea.build.number}&quot;"/>
+ <arg line="&quot;-Didea.test.group=ALL_EXCLUDE_DEFINED&quot;"/>
+ <arg value="-f"/>
+ <arg value="${project.home}/build/gant.xml"/>
+ </java>
+ </sequential>
+ </macrodef>
+
+ <target name="build" depends="init">
+ <call_gant script="${python.home}/pycharm_edu_build.gant"/>
+ </target>
+
+ <!--<target name="plugin" depends="init">-->
+ <!--<call_gant script="${python.home}/build/python_plugin_build.gant"/>-->
+ <!--</target>-->
+ <!--
+ <target name="test" depends="init">
+ <call_gant script="${project.home}/build/scripts/tests.gant"/>
+ </target>
+ -->
+
+ <target name="all" depends="cleanup,build"/>
+</project>
diff --git a/python/edu/build/paths.nsi b/python/edu/build/paths.nsi
new file mode 100644
index 000000000000..910f2b3555b7
--- /dev/null
+++ b/python/edu/build/paths.nsi
@@ -0,0 +1,6 @@
+; Installer images
+!define IMAGES_LOCATION ${COMMUNITY_DIR}\python\build\resources
+;!define LICENSE_FILE ${BASE_DIR}\python\license\PyCharm_Preview_License
+!define PRODUCT_PROPERTIES_FILE ${BASE_DIR}\out\pycharmEDU\layout\bin\idea.properties
+!define PRODUCT_VM_OPTIONS_NAME pycharm.exe.vmoptions
+!define PRODUCT_VM_OPTIONS_FILE ${BASE_DIR}\out\pycharmEDU\win\bin\${PRODUCT_VM_OPTIONS_NAME}
diff --git a/python/edu/build/plugin-list.txt b/python/edu/build/plugin-list.txt
new file mode 100644
index 000000000000..ceaa60895918
--- /dev/null
+++ b/python/edu/build/plugin-list.txt
@@ -0,0 +1,11 @@
+svn4idea
+git4idea
+remote-servers-git
+github
+terminal
+IntelliLang
+IntelliLang-xml
+IntelliLang-js
+IntelliLang-python
+rest
+python-rest
diff --git a/python/edu/build/pycharm_edu_build.gant b/python/edu/build/pycharm_edu_build.gant
new file mode 100644
index 000000000000..8c857c159874
--- /dev/null
+++ b/python/edu/build/pycharm_edu_build.gant
@@ -0,0 +1,393 @@
+/*
+ * 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.
+ */
+import org.jetbrains.jps.LayoutInfo
+
+import static org.jetbrains.jps.idea.IdeaProjectLoader.guessHome
+
+setProperty("home", guessHome(this as Script))
+
+includeTargets << new File("${guessHome(this as Script)}/build/scripts/utils.gant")
+// signMacZip locates in ultimate_utils.gant
+ includeTargets << new File("${guessHome(this)}/ultimate/build/scripts/ultimate_utils.gant")
+includeTargets << new File("${guessHome(this)}/build/scripts/libLicenses.gant")
+
+requireProperty("buildNumber", requireProperty("build.number", snapshot))
+
+setProperty("ch", "$home")
+setProperty("pythonCommunityHome", "$ch/python")
+setProperty("pythonEduHome", "$ch/python/edu")
+
+// load ApplicationInfo.xml properties
+ant.xmlproperty(file: "$pythonEduHome/resources/idea/PyCharmEduApplicationInfo.xml", collapseAttributes: "true")
+
+setProperty("system_selector", "PyCharm${p("component.version.major")}0")
+setProperty("dryRun", false)
+setProperty("jdk16", guessJdk())
+
+//modules to compile
+setProperty("pluginFilter", new File("$pythonEduHome/build/plugin-list.txt").readLines())
+
+private List<String> pycharmPlatformApiModules() {
+ return [platformApiModules, "dom-openapi"].flatten()
+}
+
+
+private List pycharmImplementationModules() { //modules to put into pycharm.jar
+ return [platformImplementationModules, "dom-impl", "python-community", "python-ide-community", "python-educational", "python-openapi", "python-psi-api",
+ "platform-main"].flatten()
+}
+
+private List modules() {
+ return [
+ "python-pydev", "colorSchemes", pycharmPlatformApiModules(), pycharmImplementationModules(), pluginFilter
+ ].flatten()
+}
+
+private List approvedJars() {
+ def normalizedHome = ch.replace('\\', '/')
+ def normalizedPythonHome = pythonCommunityHome.replace('\\', '/')
+ return ["$normalizedHome/lib/", "$normalizedPythonHome/lib/", "$normalizedHome/xml/relaxng/lib/"]
+}
+
+class Paths {
+ final sandbox
+ final distAll
+ final distWin
+ final distMac
+ final distUnix
+ final artifacts
+ final ideaSystem
+ final ideaConfig
+
+ def Paths(String home) {
+ sandbox = "$home/out/pycharmEDU"
+
+ distAll = "$sandbox/layout"
+ distWin = "$sandbox/win"
+ distMac = "$sandbox/mac"
+ distUnix = "$sandbox/unix"
+ artifacts = "$sandbox/artifacts"
+
+ ideaSystem = "$sandbox/system"
+ ideaConfig = "$sandbox/config"
+ }
+}
+
+setProperty("paths", new Paths(home))
+setProperty("buildName", "PE-$buildNumber")
+
+target('default': "Build artifacts") {
+
+ loadProject()
+
+ projectBuilder.stage("Cleaning up sandbox folder")
+
+ projectBuilder.targetFolder = "${paths.sandbox}/classes"
+ projectBuilder.dryRun = dryRun
+
+ if (!dryRun) {
+ forceDelete(paths.sandbox)
+ ant.mkdir(dir: paths.sandbox)
+ }
+
+ ant.tstamp() {
+ format(property: "todayYear", pattern: "yyyy")
+ }
+
+ ant.patternset(id: "resources.included") {
+ include(name: "**/*.properties")
+ include(name: "fileTemplates/**/*")
+ include(name: "inspectionDescriptions/**/*")
+ include(name: "intentionDescriptions/**/*")
+ include(name: "tips/**/*")
+ include(name: "search/**/*")
+ }
+
+ ant.patternset(id: "resources.excluded") {
+ exclude(name: "**/*.properties")
+ exclude(name: "fileTemplates/**/*")
+ exclude(name: "fileTemplates")
+ exclude(name: "inspectionDescriptions/**/*")
+ exclude(name: "inspectionDescriptions")
+ exclude(name: "intentionDescriptions/**/*")
+ exclude(name: "intentionDescriptions")
+ exclude(name: "tips/**/*")
+ exclude(name: "tips")
+ }
+
+ zipSources(home, paths.artifacts)
+
+ def usedJars = buildModulesAndCollectUsedJars(modules(), approvedJars(), ["/ant/"])
+
+ layoutEducational("${paths.sandbox}/classes/production", usedJars)
+
+
+ //buildNSIS([paths.distAll, paths.distWin],
+ // "${pythonEduHome}/build/strings.nsi", "${pythonEduHome}/build/paths.nsi",
+ // "pycharm", false, true, ".py", system_selector)
+
+ def extraArgs = ["build.code": "pycharmEDU-${buildNumber}", "build.number": "PE-$buildNumber", "artifacts.path": "${paths.artifacts}"]
+ signMacZip("pycharm", extraArgs)
+ buildDmg("pycharm", "${pythonEduHome}/build/DMG_background.png", extraArgs)
+
+}
+
+public layoutEducational(String classesPath, Set usedJars) {
+ setProperty("pluginFilter", new File("$pythonEduHome/build/plugin-list.txt").readLines())
+
+ if (usedJars == null) {
+ usedJars = collectUsedJars(modules(), approvedJars(), ["/ant/"], null)
+ }
+
+ def appInfo = appInfoFile(classesPath)
+ def paths = new Paths(home)
+ buildSearchableOptions("${projectBuilder.moduleOutput(findModule("platform-resources"))}/search", [], {
+ projectBuilder.moduleRuntimeClasspath(findModule("main_pycharm_edu"), false).each {
+ ant.pathelement(location: it)
+ }
+ }, "-Didea.platform.prefix=PyCharmEdu -Didea.no.jre.check=true")
+
+ if (!dryRun) {
+ wireBuildDate("PE-${buildNumber}", appInfo)
+ }
+
+ Map args = [
+ buildNumber: "PE-${buildNumber}",
+ system_selector: system_selector,
+ ide_jvm_args: "-Didea.platform.prefix=PyCharmEdu -Didea.no.jre.check=true"]
+
+ LayoutInfo layoutInfo = layoutFull(args, paths.distAll, usedJars)
+ generateLicensesTable("$paths.artifacts/third-party-libraries.txt", layoutInfo.usedModules);
+
+ layoutWin(args, paths.distWin)
+ layoutUnix(args, paths.distUnix)
+ layoutMac(args, paths.distMac)
+
+ ant.echo(message: "PE-${buildNumber}", file: "${paths.distAll}/build.txt")
+
+ def launcher = "${paths.distWin}/bin/pycharm.exe"
+ List resourcePaths = ["$ch/community-resources/src",
+ "$ch/platform/icons/src",
+ "$pythonEduHome/resources"]
+ buildWinLauncher("$ch", "$ch/bin/WinLauncher/WinLauncher.exe", launcher,
+ appInfo, "$pythonEduHome/build/pycharm_edu_launcher.properties", system_selector, resourcePaths)
+
+ buildWinZip("${paths.artifacts}/pycharmPE-${buildNumber}.zip", [paths.distAll, paths.distWin])
+
+ String tarRoot = isEap() ? "pycharm-edu-$buildNumber" : "pycharm-edu-${p("component.version.major")}.${p("component.version.minor")}"
+ buildTarGz(tarRoot, "$paths.artifacts/pycharmPE-${buildNumber}.tar", [paths.distAll, paths.distUnix])
+
+ String macAppRoot = isEap() ? "PyCharm EDU ${p("component.version.major")}.${p("component.version.minor")} EAP.app/Contents" : "PyCharm EDU.app/Contents"
+ buildMacZip(macAppRoot, "${paths.artifacts}/pycharmEDU-${buildNumber}.sit", [paths.distAll], paths.distMac)
+}
+
+private layoutPlugins(layouts) {
+ dir("plugins") {
+ layouts.layoutPlugin("rest")
+ layouts.layoutPlugin("python-rest")
+ }
+
+ layouts.layoutCommunityPlugins(ch)
+}
+
+private String appInfoFile(String classesPath) {
+ return "$classesPath/python-educational/idea/PyCharmEduApplicationInfo.xml"
+}
+
+private layoutFull(Map args, String target, Set usedJars) {
+ def openapiModules = pycharmPlatformApiModules()
+ def superLayouts = includeFile("$ch/build/scripts/layouts.gant")
+
+ reassignAltClickToMultipleCarets("$ch")
+
+ def result = layout(target) {
+ dir("lib") {
+ jar("util.jar") {
+ module("util")
+ module("util-rt")
+ }
+
+ jar("openapi.jar") {
+ openapiModules.each { module it }
+ }
+
+ jar("annotations.jar") { module("annotations") }
+ jar("extensions.jar") { module("extensions") }
+
+ jar("pycharm.jar") {
+ pycharmImplementationModules().each {
+ module(it) {
+ exclude(name: "**/tips/**")
+ }
+ }
+ }
+
+ jar("pycharm-pydev.jar") {
+ module("python-pydev")
+ }
+
+ jar("bootstrap.jar") { module("bootstrap") }
+ jar("resources.jar") {
+ module("platform-resources")
+ module("colorSchemes")
+ }
+
+ jar("forms_rt.jar") {
+ module("forms_rt")
+ }
+
+ //noinspection GroovyAssignabilityCheck
+ jar([name: "resources_en.jar", duplicate: "preserve"]) {
+ // custom resources should go first
+ fileset(dir: "$pythonCommunityHome/resources") {
+ include(name: "**/tips/**")
+ }
+ module("platform-resources-en") {
+ ant.patternset {
+ exclude(name: "tips/images/switcher.png")
+ exclude(name: "tips/images/navigateToFilePath.gif")
+ }
+ }
+ }
+
+ jar("icons.jar") { module("icons") }
+ jar("boot.jar") { module("boot") }
+
+ usedJars.each {
+ fileset(file: it)
+ }
+
+ dir("libpty") {
+ fileset(dir: "$ch/lib/libpty") {
+ exclude(name: "*.txt")
+ }
+ }
+
+ dir("ext") {
+ fileset(dir: "$ch/lib") {
+ include(name: "cglib*.jar")
+ }
+ }
+
+ dir("src") {
+ fileset(dir: "$ch/lib/src") {
+ include(name: "trove4j_changes.txt")
+ include(name: "trove4j_src.jar")
+ }
+
+ jar("pycharm-pydev-src.zip") {
+ fileset(dir: "$pythonCommunityHome/pydevSrc")
+ }
+ jar("pycharm-openapi-src.zip") {
+ fileset(dir: "$pythonCommunityHome/openapi/src")
+ fileset(dir: "$pythonCommunityHome/psi-api/src")
+ }
+ }
+ }
+
+ dir("help") {
+ fileset(dir: "$home/python/help") {
+ include(name: "*.pdf")
+ }
+ }
+
+ dir("helpers") {
+ fileset(dir: "$pythonCommunityHome/helpers")
+ }
+
+ dir("license") {
+ fileset(dir: "$ch/license")
+ fileset(dir: "$ch") {
+ include(name: "LICENSE.txt")
+ include(name: "NOTICE.txt")
+ }
+ }
+
+ layoutPlugins(superLayouts)
+
+ dir("bin") {
+ fileset(dir: "$ch/bin") {
+ exclude(name: "appletviewer.policy")
+ include(name: "*.*")
+ }
+ }
+ }
+ patchPropertiesFile(target, args + [appendices: ["$home/build/conf/ideaJNC.properties"]])
+ return result
+}
+
+private layoutWin(Map args, String target) {
+ layout(target) {
+ dir("bin") {
+ fileset(dir: "$ch/bin/win") {
+ exclude(name: "breakgen*")
+ }
+ }
+
+ dir("skeletons") {
+ fileset(dir: "$pythonCommunityHome/skeletons") {
+ include(name: "skeletons-win*.zip")
+ }
+ }
+ }
+
+ winScripts(target, ch, "pycharm.bat", args)
+ winVMOptions(target, null, "pycharm.exe")
+
+ ant.copy(file: "$home/python/help/pycharmhelp.jar", todir: "$target/help", failonerror: false)
+}
+
+private layoutUnix(Map args, String target) {
+ layout(target) {
+ dir("bin") {
+ fileset(dir: "$ch/bin/linux") {
+ exclude(name: "libbreakgen*")
+ }
+ }
+ }
+
+ ant.copy(file: "$pythonCommunityHome/resources/PyCharmCore128.png", tofile: "$target/bin/pycharm.png")
+
+ unixScripts(target, ch, "pycharm.sh", args)
+ unixVMOptions(target, "pycharm")
+
+ ant.copy(file: "$home/python/help/pycharmhelp.jar", todir: "$target/help", failonerror: false)
+}
+
+private layoutMac(Map _args, String target) {
+ layout(target) {
+ dir("bin") {
+ fileset(dir: "$home/bin") {
+ include(name: "*.jnilib")
+ }
+ }
+
+ dir("skeletons") {
+ fileset(dir: "$pythonCommunityHome/skeletons") {
+ include(name: "skeletons-mac*.zip")
+ }
+ }
+ }
+
+ Map args = new HashMap(_args)
+ args.icns = "$pythonCommunityHome/resources/PyCharmCore.icns"
+ args.bundleIdentifier = "com.jetbrains.pycharm"
+ args.platform_prefix = "PyCharmEdu"
+ args.help_id = "PY"
+ args."idea.properties.path" = "${paths.distAll}/bin/idea.properties"
+ args."idea.properties" = ["idea.no.jre.check": true, "ide.mac.useNativeClipboard": "false"];
+ layoutMacApp(target, ch, args)
+}
diff --git a/python/edu/build/pycharm_edu_launcher.properties b/python/edu/build/pycharm_edu_launcher.properties
new file mode 100644
index 000000000000..d3554685ce3b
--- /dev/null
+++ b/python/edu/build/pycharm_edu_launcher.properties
@@ -0,0 +1,3 @@
+IDS_JDK_ENV_VAR=PYCHARM_JDK
+IDS_JDK_ONLY=false
+IDS_VM_OPTIONS=-Didea.platform.prefix=PyCharmEdu -Didea.no.jre.check=true -Didea.paths.selector=__PRODUCT_PATHS_SELECTOR__
diff --git a/python/edu/build/python-edu-build.iml b/python/edu/build/python-edu-build.iml
new file mode 100644
index 000000000000..bda177851580
--- /dev/null
+++ b/python/edu/build/python-edu-build.iml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+ <component name="NewModuleRootManager" inherit-compiler-output="true">
+ <exclude-output />
+ <content url="file://$MODULE_DIR$">
+ <sourceFolder url="file://$MODULE_DIR$" isTestSource="false" />
+ </content>
+ <orderEntry type="inheritedJdk" />
+ <orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="library" name="Groovy" level="project" />
+ <orderEntry type="module" module-name="jps-standalone-builder" />
+ </component>
+</module>
+
diff --git a/python/edu/build/resources/PC_instCom.ico b/python/edu/build/resources/PC_instCom.ico
new file mode 100644
index 000000000000..44ecec9c8755
--- /dev/null
+++ b/python/edu/build/resources/PC_instCom.ico
Binary files differ
diff --git a/python/edu/build/resources/PC_uninstCom.ico b/python/edu/build/resources/PC_uninstCom.ico
new file mode 100644
index 000000000000..13e9f96f9987
--- /dev/null
+++ b/python/edu/build/resources/PC_uninstCom.ico
Binary files differ
diff --git a/python/edu/build/resources/headerlogo.bmp b/python/edu/build/resources/headerlogo.bmp
new file mode 100644
index 000000000000..c06099264961
--- /dev/null
+++ b/python/edu/build/resources/headerlogo.bmp
Binary files differ
diff --git a/python/edu/build/resources/logo.bmp b/python/edu/build/resources/logo.bmp
new file mode 100644
index 000000000000..0f0f82da7b1e
--- /dev/null
+++ b/python/edu/build/resources/logo.bmp
Binary files differ
diff --git a/python/edu/build/resources/pycharm_inst.ico b/python/edu/build/resources/pycharm_inst.ico
new file mode 100644
index 000000000000..6c75bcdbc441
--- /dev/null
+++ b/python/edu/build/resources/pycharm_inst.ico
Binary files differ
diff --git a/python/edu/build/resources/pycharm_uninst.ico b/python/edu/build/resources/pycharm_uninst.ico
new file mode 100644
index 000000000000..a5b9ad86ae94
--- /dev/null
+++ b/python/edu/build/resources/pycharm_uninst.ico
Binary files differ
diff --git a/python/edu/build/strings.nsi b/python/edu/build/strings.nsi
new file mode 100644
index 000000000000..fa10d421024c
--- /dev/null
+++ b/python/edu/build/strings.nsi
@@ -0,0 +1,13 @@
+!define MANUFACTURER "JetBrains"
+!define MUI_PRODUCT "PyCharm Educational Edition"
+!define PRODUCT_FULL_NAME "JetBrains PyCharm Educational Edition"
+!define PRODUCT_EXE_FILE "pycharm.exe"
+!define PRODUCT_ICON_FILE "PC_instCom.ico"
+!define PRODUCT_UNINST_ICON_FILE "PC_uninstCom.ico"
+!define PRODUCT_LOGO_FILE "logo.bmp"
+!define PRODUCT_HEADER_FILE "headerlogo.bmp"
+
+; if SHOULD_SET_DEFAULT_INSTDIR != 0 then default installation directory will be directory where highest-numbered PyCharm build has been installed
+; set to 1 for release build
+!define SHOULD_SET_DEFAULT_INSTDIR "0"
+
diff --git a/python/edu/main_pycharm_edu.iml b/python/edu/main_pycharm_edu.iml
new file mode 100644
index 000000000000..2acdd8733873
--- /dev/null
+++ b/python/edu/main_pycharm_edu.iml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+ <component name="NewModuleRootManager" inherit-compiler-output="true">
+ <exclude-output />
+ <orderEntry type="inheritedJdk" />
+ <orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="module" module-name="bootstrap" />
+ <orderEntry type="module" module-name="colorSchemes" />
+ <orderEntry type="module" module-name="svn4idea" />
+ <orderEntry type="module" module-name="git4idea" />
+ <orderEntry type="module" module-name="relaxng" />
+ <orderEntry type="module" module-name="rest" />
+ <orderEntry type="module" module-name="python-helpers" />
+ <orderEntry type="module" module-name="terminal" />
+ <orderEntry type="module" module-name="python-ide-community" />
+ <orderEntry type="module" module-name="platform-main" />
+ <orderEntry type="module" module-name="ShortcutPromoter" />
+ <orderEntry type="module" module-name="python-educational" />
+ </component>
+</module>
+
diff --git a/python/edu/python-educational.iml b/python/edu/python-educational.iml
new file mode 100644
index 000000000000..92439c421c2d
--- /dev/null
+++ b/python/edu/python-educational.iml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+ <component name="NewModuleRootManager" inherit-compiler-output="true">
+ <exclude-output />
+ <content url="file://$MODULE_DIR$">
+ <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/resources" type="java-resource" />
+ <sourceFolder url="file://$MODULE_DIR$/gen" isTestSource="false" generated="true" />
+ </content>
+ <orderEntry type="inheritedJdk" />
+ <orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="module" module-name="python-openapi" exported="" />
+ <orderEntry type="module" module-name="platform-impl" />
+ <orderEntry type="module" module-name="lang-impl" />
+ <orderEntry type="library" name="Guava" level="project" />
+ <orderEntry type="module" module-name="python-pydev" />
+ <orderEntry type="library" exported="" name="XmlRPC" level="project" />
+ <orderEntry type="module" module-name="xdebugger-api" />
+ <orderEntry type="library" name="http-client-3.1" level="project" />
+ <orderEntry type="module" module-name="RegExpSupport" exported="" />
+ <orderEntry type="module" module-name="testRunner" />
+ <orderEntry type="module" module-name="smRunner" />
+ <orderEntry type="module" module-name="spellchecker" />
+ <orderEntry type="module" module-name="xdebugger-impl" />
+ <orderEntry type="module" module-name="xml-psi-impl" />
+ <orderEntry type="module" module-name="python-community" />
+ </component>
+</module>
+
diff --git a/python/edu/resources/idea/PyCharmEduApplicationInfo.xml b/python/edu/resources/idea/PyCharmEduApplicationInfo.xml
new file mode 100644
index 000000000000..46695ef29ed4
--- /dev/null
+++ b/python/edu/resources/idea/PyCharmEduApplicationInfo.xml
@@ -0,0 +1,23 @@
+<component>
+ <company name="JetBrains s.r.o." url="http://www.jetbrains.com/?fromIDE"/>
+ <version major="3" minor="0" eap="true"/>
+ <build number="__BUILD_NUMBER__" date="__BUILD_DATE__"/>
+ <logo url="/pycharm_core_logo.png" textcolor="ffffff" progressColor="ffaa16" progressY="230" progressTailIcon="/community_progress_tail.png"/>
+ <about url="/pycharm_core_about.png" logoX="300" logoY="265" logoW="75" logoH="30" foreground="ffffff" linkColor="fca11a"/>
+ <icon size32="/PyCharmCore32.png" size16="/PyCharmCore16.png" size32opaque="/PyCharmCore32.png" size12="/PyCharmCore13.png" ico="PyCharmCore.ico"/>
+ <package code="__PACKAGE_CODE__"/>
+ <names product="PyCharm" fullname="PyCharm Educational Edition" script="charm"/>
+ <install-over minbuild="0" maxbuild="0" version="1.x"/>
+
+ <welcome-screen logo-url="/PyCharmCoreWelcomeScreen.png" caption-url="PyCharmCoreWelcomeCaption.png" slogan-url="/developSlogan.png"/>
+
+ <third-party url="http://confluence.jetbrains.com/display/PYH/Third-Party+Software+Used+by+PyCharm"/>
+
+ <update-urls logo-url="/Logo_welcomeScreen.png"
+ check="http://www.jetbrains.com/updates/updates.xml"
+ patches="http://download.jetbrains.com/python/"/>
+
+ <feedback eap-url="http://www.jetbrains.com/feedback/feedback.jsp?product=PyCharm&amp;build=$BUILD&amp;timezone=$TIMEZONE&amp;eval=$EVAL"
+ release-url="http://www.jetbrains.com/feedback/feedback.jsp?product=PyCharm&amp;build=$BUILD&amp;timezone=$TIMEZONE&amp;eval=$EVAL"/>
+ <help file="pycharmhelp.jar" root="pycharm"/>
+</component>
diff --git a/python/edu/src/META-INF/PyCharmEduPlugin.xml b/python/edu/src/META-INF/PyCharmEduPlugin.xml
new file mode 100644
index 000000000000..9adf03dfd9c8
--- /dev/null
+++ b/python/edu/src/META-INF/PyCharmEduPlugin.xml
@@ -0,0 +1,36 @@
+<idea-plugin version="2" xmlns:xi="http://www.w3.org/2001/XInclude">
+ <!-- Components and extensions declared in this file work ONLY in Pycharm Educational Edition -->
+
+ <application-components>
+ <component>
+ <implementation-class>com.jetbrains.python.edu.PyCharmEduInitialConfigurator$First</implementation-class>
+ <headless-implementation-class/>
+ </component>
+ </application-components>
+
+ <xi:include href="/META-INF/pycharm-core.xml" xpointer="xpointer(/idea-plugin/*)"/>
+
+ <xi:include href="/META-INF/python-core.xml" xpointer="xpointer(/idea-plugin/*)"/>
+
+
+ <actions>
+ <group overrides="true" class="com.intellij.openapi.actionSystem.EmptyActionGroup" id="ToolsMenu"/>
+
+ <group overrides="true" class="com.intellij.openapi.actionSystem.EmptyActionGroup" id="PrintExportGroup"/>
+ <group overrides="true" id="FileMainSettingsGroup">
+ <reference id="ShowSettings"/>
+ <separator/>
+ <reference id="ExportImportGroup"/>
+ </group>
+
+ <group overrides="true" class="com.intellij.openapi.actionSystem.EmptyActionGroup" id="EditBookmarksGroup"/>
+ <group overrides="true" class="com.intellij.openapi.actionSystem.EmptyActionGroup" id="AddToFavorites"/>
+ <group overrides="true" class="com.intellij.openapi.actionSystem.EmptyActionGroup" id="AddAllToFavorites"/>
+ <action overrides="true" class="com.intellij.openapi.actionSystem.EmptyAction" id="AddToFavoritesPopup"/>
+ <action overrides="true" class="com.intellij.openapi.actionSystem.EmptyAction" id="RemoveFromFavorites"/>
+
+ <action overrides="true" class="com.intellij.openapi.actionSystem.EmptyAction" id="NewHtmlFile"/>
+
+
+ </actions>
+</idea-plugin>
diff --git a/python/edu/src/com/jetbrains/python/edu/PyCharmEduInitialConfigurator.java b/python/edu/src/com/jetbrains/python/edu/PyCharmEduInitialConfigurator.java
new file mode 100644
index 000000000000..451c5193645e
--- /dev/null
+++ b/python/edu/src/com/jetbrains/python/edu/PyCharmEduInitialConfigurator.java
@@ -0,0 +1,247 @@
+/*
+ * 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.edu;
+
+import com.intellij.application.options.InitialConfigurationDialog;
+import com.intellij.codeInsight.CodeInsightSettings;
+import com.intellij.codeInsight.intention.IntentionActionBean;
+import com.intellij.codeInsight.intention.IntentionManager;
+import com.intellij.ide.AppLifecycleListener;
+import com.intellij.ide.GeneralSettings;
+import com.intellij.ide.RecentProjectsManagerBase;
+import com.intellij.ide.SelectInTarget;
+import com.intellij.ide.ui.UISettings;
+import com.intellij.ide.util.PropertiesComponent;
+import com.intellij.ide.util.TipDialog;
+import com.intellij.notification.EventLog;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.ModalityState;
+import com.intellij.openapi.editor.ex.EditorSettingsExternalizable;
+import com.intellij.openapi.extensions.Extensions;
+import com.intellij.openapi.extensions.ExtensionsArea;
+import com.intellij.openapi.fileChooser.impl.FileChooserUtil;
+import com.intellij.openapi.fileTypes.FileTypeManager;
+import com.intellij.openapi.keymap.Keymap;
+import com.intellij.openapi.keymap.ex.KeymapManagerEx;
+import com.intellij.openapi.keymap.impl.KeymapImpl;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ProjectManager;
+import com.intellij.openapi.project.ProjectManagerAdapter;
+import com.intellij.openapi.project.ex.ProjectManagerEx;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.util.Ref;
+import com.intellij.openapi.vfs.VfsUtil;
+import com.intellij.openapi.wm.ToolWindowEP;
+import com.intellij.openapi.wm.ToolWindowId;
+import com.intellij.openapi.wm.WindowManager;
+import com.intellij.platform.DirectoryProjectConfigurator;
+import com.intellij.platform.PlatformProjectViewOpener;
+import com.intellij.psi.codeStyle.CodeStyleSettings;
+import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
+import com.intellij.util.Alarm;
+import com.intellij.util.containers.ContainerUtil;
+import com.intellij.util.messages.MessageBus;
+import com.jetbrains.python.PythonLanguage;
+import com.jetbrains.python.codeInsight.PyCodeInsightSettings;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+import java.util.Set;
+
+/**
+ * @author traff
+ */
+@SuppressWarnings({"UtilityClassWithoutPrivateConstructor", "UtilityClassWithPublicConstructor"})
+public class PyCharmEduInitialConfigurator {
+ @NonNls private static final String DISPLAYED_PROPERTY = "PyCharm.initialConfigurationShown";
+
+ @NonNls private static final String CONFIGURED = "PyCharm.InitialConfiguration";
+
+
+ public static class First {
+
+ public First() {
+ patchRootAreaExtensions();
+ }
+ }
+
+ /**
+ * @noinspection UnusedParameters
+ */
+ public PyCharmEduInitialConfigurator(MessageBus bus,
+ UISettings uiSettings,
+ CodeInsightSettings codeInsightSettings,
+ final PropertiesComponent propertiesComponent,
+ FileTypeManager fileTypeManager,
+ final ProjectManagerEx projectManager,
+ RecentProjectsManagerBase recentProjectsManager) {
+ if (!propertiesComponent.getBoolean(CONFIGURED, false)) {
+ propertiesComponent.setValue(CONFIGURED, "true");
+ recentProjectsManager.loadState(new RecentProjectsManagerBase.State());
+ propertiesComponent.setValue("toolwindow.stripes.buttons.info.shown", "true");
+ UISettings.getInstance().HIDE_TOOL_STRIPES = false;
+ uiSettings.SHOW_MEMORY_INDICATOR = false;
+ uiSettings.SHOW_DIRECTORY_FOR_NON_UNIQUE_FILENAMES = true;
+ codeInsightSettings.REFORMAT_ON_PASTE = CodeInsightSettings.NO_REFORMAT;
+
+ EditorSettingsExternalizable.getInstance().setVirtualSpace(false);
+ final CodeStyleSettings settings = CodeStyleSettingsManager.getInstance().getCurrentSettings();
+ settings.ALIGN_MULTILINE_PARAMETERS_IN_CALLS = true;
+ settings.getCommonSettings(PythonLanguage.getInstance()).ALIGN_MULTILINE_PARAMETERS_IN_CALLS = true;
+ UISettings.getInstance().SHOW_DIRECTORY_FOR_NON_UNIQUE_FILENAMES = true;
+ UISettings.getInstance().SHOW_MEMORY_INDICATOR = false;
+ final String ignoredFilesList = fileTypeManager.getIgnoredFilesList();
+ ApplicationManager.getApplication().invokeLater(new Runnable() {
+ @Override
+ public void run() {
+ ApplicationManager.getApplication().runWriteAction(new Runnable() {
+ @Override
+ public void run() {
+ FileTypeManager.getInstance().setIgnoredFilesList(ignoredFilesList + ";*$py.class");
+ }
+ });
+ }
+ });
+ PyCodeInsightSettings.getInstance().SHOW_IMPORT_POPUP = false;
+ }
+ bus.connect().subscribe(AppLifecycleListener.TOPIC, new AppLifecycleListener.Adapter() {
+ @Override
+ public void appFrameCreated(String[] commandLineArgs, @NotNull Ref<Boolean> willOpenProject) {
+ if (!propertiesComponent.isValueSet(DISPLAYED_PROPERTY)) {
+ GeneralSettings.getInstance().setShowTipsOnStartup(false);
+ showInitialConfigurationDialog();
+ propertiesComponent.setValue(DISPLAYED_PROPERTY, "true");
+ }
+ }
+
+ @Override
+ public void appStarting(Project projectFromCommandLine) {
+ patchKeymap();
+ }
+ });
+ bus.connect().subscribe(ProjectManager.TOPIC, new ProjectManagerAdapter() {
+ @Override
+ public void projectOpened(final Project project) {
+ if (project.isDefault()) return;
+ if (FileChooserUtil.getLastOpenedFile(project) == null) {
+ FileChooserUtil.setLastOpenedFile(project, VfsUtil.getUserHomeDir());
+ }
+
+ patchProjectAreaExtensions(project);
+
+ //StartupManager.getInstance(project).runWhenProjectIsInitialized(new DumbAwareRunnable() {
+ // @Override
+ // public void run() {
+ // if (project.isDisposed()) return;
+ //
+ // ToolWindowManager.getInstance(project).invokeLater(new Runnable() {
+ // int count = 0;
+ // public void run() {
+ // if (project.isDisposed()) return;
+ // if (count ++ < 3) {
+ // ToolWindowManager.getInstance(project).invokeLater(this);
+ // return;
+ // }
+ // if (!propertiesComponent.isValueSet(INIT_DB_DIALOG_DISPLAYED)) {
+ // ToolWindow toolWindow = DatabaseView.getDatabaseToolWindow(project);
+ // if (toolWindow.getType() != ToolWindowType.SLIDING) {
+ // toolWindow.activate(null);
+ // }
+ // propertiesComponent.setValue(INIT_DB_DIALOG_DISPLAYED, "true");
+ // onFirstProjectOpened(project);
+ // }
+ // }
+ // });
+ // }
+ //});
+ }
+ });
+ }
+
+ private static void onFirstProjectOpened(@NotNull final Project project) {
+ // show python console
+
+
+ GeneralSettings.getInstance().setShowTipsOnStartup(true);
+
+ // show tips once
+ final Alarm alarm = new Alarm(project);
+ alarm.addRequest(new Runnable() {
+ @Override
+ public void run() {
+ Disposer.dispose(alarm);
+ TipDialog.createForProject(project).show();
+ }
+ }, 2000, ModalityState.NON_MODAL);
+ }
+
+ private static void patchRootAreaExtensions() {
+ ExtensionsArea rootArea = Extensions.getArea(null);
+
+ for (ToolWindowEP ep : Extensions.getExtensions(ToolWindowEP.EP_NAME)) {
+ if (ToolWindowId.FAVORITES_VIEW.equals(ep.id) || ToolWindowId.TODO_VIEW.equals(ep.id) || EventLog.LOG_TOOL_WINDOW_ID.equals(ep.id)) {
+ rootArea.getExtensionPoint(ToolWindowEP.EP_NAME).unregisterExtension(ep);
+ }
+ }
+
+ for (DirectoryProjectConfigurator ep : Extensions.getExtensions(DirectoryProjectConfigurator.EP_NAME)) {
+ if (ep instanceof PlatformProjectViewOpener) {
+ rootArea.getExtensionPoint(DirectoryProjectConfigurator.EP_NAME).unregisterExtension(ep);
+ }
+ }
+
+ for (IntentionActionBean ep : Extensions.getExtensions(IntentionManager.EP_INTENTION_ACTIONS)) {
+ if ("org.intellij.lang.regexp.intention.CheckRegExpIntentionAction".equals(ep.className)) {
+ rootArea.getExtensionPoint(IntentionManager.EP_INTENTION_ACTIONS).unregisterExtension(ep);
+ }
+ }
+ }
+
+ private static void patchProjectAreaExtensions(@NotNull final Project project) {
+ for (SelectInTarget target : Extensions.getExtensions(SelectInTarget.EP_NAME, project)) {
+ if (ToolWindowId.FAVORITES_VIEW.equals(target.getToolWindowId())) {
+ Extensions.getArea(project).getExtensionPoint(SelectInTarget.EP_NAME).unregisterExtension(target);
+ }
+ }
+ }
+
+ private static void patchKeymap() {
+ Set<String> droppedActions = ContainerUtil.newHashSet(
+ "AddToFavoritesPopup", "RemoveFromFavorites",
+ "DatabaseView.ImportDataSources",
+ "CompileDirty", "Compile",
+ // hidden
+ "AddNewFavoritesList", "EditFavorites", "RemoveFromFavorites", "RenameFavoritesList", "RemoveFavoritesList");
+ KeymapManagerEx keymapManager = KeymapManagerEx.getInstanceEx();
+
+
+ for (Keymap keymap : keymapManager.getAllKeymaps()) {
+ if (keymap.canModify()) continue;
+
+ KeymapImpl keymapImpl = (KeymapImpl)keymap;
+
+ for (String id : keymapImpl.getOwnActionIds()) {
+ if (droppedActions.contains(id)) keymapImpl.clearOwnActionsId(id);
+ }
+ }
+ }
+
+ private static void showInitialConfigurationDialog() {
+ final JFrame frame = WindowManager.getInstance().findVisibleFrame();
+ new InitialConfigurationDialog(frame, "Python").show();
+ }
+}
diff --git a/python/helpers/pycharm/_bdd_utils.py b/python/helpers/pycharm/_bdd_utils.py
new file mode 100644
index 000000000000..0c92532b516c
--- /dev/null
+++ b/python/helpers/pycharm/_bdd_utils.py
@@ -0,0 +1,201 @@
+# coding=utf-8
+"""
+Tools for running BDD frameworks in python.
+You probably need to extend BddRunner (see its doc).
+
+You may also need "get_path_by_args" that gets folder (current or passed as first argument)
+"""
+import os
+import time
+import abc
+
+import tcmessages
+
+
+__author__ = 'Ilya.Kazakevich'
+
+
+def get_path_by_args(arguments):
+ """
+ :type arguments list
+ :param arguments: arguments (sys.argv)
+ :return: tuple (base_dir, what_to_run) where dir is current or first argument from argv, checking it exists
+ :rtype tuple of str
+ """
+ what_to_run = arguments[1] if len(arguments) > 1 else "."
+ base_dir = what_to_run
+ assert os.path.exists(what_to_run), "{} does not exist".format(what_to_run)
+
+ if os.path.isfile(what_to_run):
+ base_dir = os.path.dirname(what_to_run) # User may point to the file directly
+ return base_dir, what_to_run
+
+
+class BddRunner(object):
+ """
+ Extends this class, implement abstract methods and use its API to implement new BDD frameworks.
+ Call "run()" to launch it.
+ This class does the following:
+ * Gets features to run (using "_get_features_to_run()") and calculates steps in it
+ * Reports steps to Intellij or TC
+ * Calls "_run_tests()" where *you* should install all hooks you need into your BDD and use "self._" functions
+ to report tests and features. It actually wraps tcmessages but adds some stuff like duration count etc
+ :param base_dir:
+ """
+ __metaclass__ = abc.ABCMeta
+
+ def __init__(self, base_dir):
+ """
+ :type base_dir str
+ :param base_dir base directory of your project
+ """
+ super(BddRunner, self).__init__()
+ self.tc_messages = tcmessages.TeamcityServiceMessages()
+ """
+ tcmessages TeamCity/Intellij test API. See TeamcityServiceMessages
+ """
+ self.__base_dir = base_dir
+ self.__last_test_start_time = None # TODO: Doc when use
+ self.__last_test_name = None
+
+ def run(self):
+ """"
+ Runs runner. To be called right after constructor.
+ """
+ self.tc_messages.testCount(self._get_number_of_tests())
+ self.tc_messages.testMatrixEntered()
+ self._run_tests()
+
+ def __gen_location(self, location):
+ """
+ Generates location in format, supported by tcmessages
+ :param location object with "file" (relative to base_dir) and "line" fields.
+ :return: location in format file:line (as supported in tcmessages)
+ """
+ my_file = str(location.file).lstrip("/\\")
+ return "file:///{}:{}".format(os.path.normpath(os.path.join(self.__base_dir, my_file)), location.line)
+
+ def _test_undefined(self, test_name, location):
+ """
+ Mark test as undefined
+ :param test_name: name of test
+ :type test_name str
+ :param location its location
+
+ """
+ if test_name != self.__last_test_name:
+ self._test_started(test_name, location)
+ self._test_failed(test_name, message="Test undefined", details="Please define test")
+
+ def _test_skipped(self, test_name, reason, location):
+ """
+ Mark test as skipped
+ :param test_name: name of test
+ :param reason: why test was skipped
+ :type reason str
+ :type test_name str
+ :param location its location
+
+ """
+ if test_name != self.__last_test_name:
+ self._test_started(test_name, location)
+ self.tc_messages.testIgnored(test_name, "Skipped: {}".format(reason))
+ self.__last_test_name = None
+ pass
+
+ def _test_failed(self, name, message, details):
+ """
+ Report test failure
+ :param name: test name
+ :type name str
+ :param message: failure message
+ :type message str
+ :param details: failure details (probably stacktrace)
+ :type details str
+ """
+ self.tc_messages.testFailed(name, message=message, details=details)
+ self.__last_test_name = None
+
+ def _test_passed(self, name, duration=None):
+ """
+ Reports test passed
+ :param name: test name
+ :type name str
+ :param duration: time (in seconds) test took. Pass None if you do not know (we'll try to calculate it)
+ :type duration int
+ :return:
+ """
+ duration_to_report = duration
+ if self.__last_test_start_time and not duration: # And not provided
+ duration_to_report = int(time.time() - self.__last_test_start_time)
+ self.tc_messages.testFinished(name, duration=int(duration_to_report))
+ self.__last_test_start_time = None
+ self.__last_test_name = None
+
+ def _test_started(self, name, location):
+ """
+ Reports test launched
+ :param name: test name
+ :param location object with "file" (relative to base_dir) and "line" fields.
+ :type name str
+ """
+ self.__last_test_start_time = time.time()
+ self.__last_test_name = name
+ self.tc_messages.testStarted(name, self.__gen_location(location))
+
+ def _feature_or_scenario(self, is_started, name, location):
+ """
+ Reports feature or scenario launched or stopped
+ :param is_started: started or finished?
+ :type is_started bool
+ :param name: scenario or feature name
+ :param location object with "file" (relative to base_dir) and "line" fields.
+ """
+ if is_started:
+ self.tc_messages.testSuiteStarted(name, self.__gen_location(location))
+ else:
+ self.tc_messages.testSuiteFinished(name)
+
+ def _background(self, is_started, location):
+ """
+ Reports background or stopped
+ :param is_started: started or finished?
+ :type is_started bool
+ :param location object with "file" (relative to base_dir) and "line" fields.
+ """
+ self._feature_or_scenario(is_started, "Background", location)
+
+ def _get_number_of_tests(self):
+ """"
+ Gets number of tests using "_get_features_to_run()" to obtain number of features to calculate.
+ Supports backgrounds as well.
+ :return number of steps
+ :rtype int
+ """
+ num_of_steps = 0
+ for feature in self._get_features_to_run():
+ if feature.background:
+ num_of_steps += len(feature.background.steps) * len(feature.scenarios)
+ for scenario in feature.scenarios:
+ num_of_steps += len(scenario.steps)
+ return num_of_steps
+
+ @abc.abstractmethod
+ def _get_features_to_run(self):
+ """
+ Implement it! Return list of features to run. Each "feature" should have "scenarios".
+ Each "scenario" should have "steps". Each "feature" may have "background" and each "background" should have
+ "steps". Duck typing.
+ :rtype list
+ :returns list of features
+ """
+ return []
+
+ @abc.abstractmethod
+ def _run_tests(self):
+ """
+ Implement it! It should launch tests using your BDD. Use "self._" functions to report results.
+ """
+ pass
+
+
diff --git a/python/helpers/pycharm/behave_runner.py b/python/helpers/pycharm/behave_runner.py
new file mode 100644
index 000000000000..4a1b2f6557c5
--- /dev/null
+++ b/python/helpers/pycharm/behave_runner.py
@@ -0,0 +1,242 @@
+# coding=utf-8
+"""
+Behave BDD runner.
+*FIRST* param now: folder to search "features" for.
+Each "features" folder should have features and "steps" subdir.
+
+Other args are tag expressionsin format (--tags=.. --tags=..).
+See https://pythonhosted.org/behave/behave.html#tag-expression
+"""
+import functools
+import sys
+import os
+import traceback
+
+from behave.formatter.base import Formatter
+from behave.model import Step, ScenarioOutline, Feature, Scenario
+from behave.tag_expression import TagExpression
+
+import _bdd_utils
+
+
+_MAX_STEPS_SEARCH_FEATURES = 5000 # Do not look for features in folder that has more that this number of children
+_FEATURES_FOLDER = 'features' # "features" folder name.
+
+__author__ = 'Ilya.Kazakevich'
+
+from behave import configuration, runner
+from behave.formatter import formatters
+
+
+def _get_dirs_to_run(base_dir_to_search):
+ """
+ Searches for "features" dirs in some base_dir
+ :return: list of feature dirs to run
+ :rtype: list
+ :param base_dir_to_search root directory to search (should not have too many children!)
+ :type base_dir_to_search str
+
+ """
+ result = set()
+ for (step, (folder, sub_folders, files)) in enumerate(os.walk(base_dir_to_search)):
+ if os.path.basename(folder) == _FEATURES_FOLDER and os.path.isdir(folder):
+ result.add(os.path.abspath(folder))
+ if step == _MAX_STEPS_SEARCH_FEATURES: # Guard
+ err = "Folder {} is too deep to find any features folder. Please provider concrete folder".format(
+ base_dir_to_search)
+ raise Exception(err)
+ return list(result)
+
+
+def _merge_hooks_wrapper(*hooks):
+ """
+ Creates wrapper that runs provided behave hooks sequentally
+ :param hooks: hooks to run
+ :return: wrapper
+ """
+ # TODO: Wheel reinvented!!!!
+ def wrapper(*args, **kwargs):
+ for hook in hooks:
+ hook(*args, **kwargs)
+
+ return wrapper
+
+
+class _RunnerWrapper(runner.Runner):
+ """
+ Wrapper around behave native wrapper. Has nothing todo with BddRunner!
+ We need it to support dry runs (to fetch data from scenarios) and hooks api
+ """
+
+ def __init__(self, config, hooks):
+ """
+ :type config configuration.Configuration
+ :param config behave configuration
+ :type hooks dict
+ :param hooks hooks in format "before_scenario" => f(context, scenario) to load after/before hooks, provided by user
+ """
+ super(_RunnerWrapper, self).__init__(config)
+ self.dry_run = False
+ """
+ Does not run tests (only fetches "self.features") if true. Runs tests otherwise.
+ """
+ self.__hooks = hooks
+
+ def load_hooks(self, filename='environment.py'):
+ """
+ Overrides parent "load_hooks" to add "self.__hooks"
+ :param filename: env. file name
+ """
+ super(_RunnerWrapper, self).load_hooks(filename)
+ for (hook_name, hook) in self.__hooks.items():
+ hook_to_add = hook
+ if hook_name in self.hooks:
+ user_hook = self.hooks[hook_name]
+ if hook_name.startswith("before"):
+ user_and_custom_hook = [user_hook, hook]
+ else:
+ user_and_custom_hook = [hook, user_hook]
+ hook_to_add = _merge_hooks_wrapper(*user_and_custom_hook)
+ self.hooks[hook_name] = hook_to_add
+
+ def run_model(self, features=None):
+ """
+ Overrides parent method to stop (do nothing) in case of "dry_run"
+ :param features: features to run
+ :return:
+ """
+ if self.dry_run: # To stop further execution
+ return
+ return super(_RunnerWrapper, self).run_model(features)
+
+ def clean(self):
+ """
+ Cleans runner after dry run (clears hooks, features etc). To be called before real run!
+ """
+ self.dry_run = False
+ self.hooks.clear()
+ self.features = []
+
+
+class _BehaveRunner(_bdd_utils.BddRunner):
+ """
+ BddRunner for behave
+ """
+
+
+ def __process_hook(self, is_started, context, element):
+ """
+ Hook to be installed. Reports steps, features etc.
+ :param is_started true if test/feature/scenario is started
+ :type is_started bool
+ :param context behave context
+ :type context behave.runner.Context
+ :param element feature/suite/step
+ """
+ element.location.file = element.location.filename # To preserve _bdd_utils contract
+ if isinstance(element, Step):
+ # Process step
+ if is_started:
+ self._test_started(element.name, element.location)
+ elif element.status == 'passed':
+ self._test_passed(element.name, element.duration)
+ elif element.status == 'failed':
+ try:
+ trace = traceback.format_exc()
+ except Exception:
+ trace = "".join(traceback.format_tb(element.exc_traceback))
+ self._test_failed(element.name, element.error_message, trace)
+ elif element.status == 'undefined':
+ self._test_undefined(element.name, element.location)
+ else:
+ self._test_skipped(element.name, element.status, element.location)
+ elif not is_started and isinstance(element, Scenario) and element.status == 'failed':
+ # To process scenarios with undefined/skipped tests
+ for step in element.steps:
+ assert isinstance(step, Step), step
+ if step.status not in ['passed', 'failed']: # Something strange, probably skipped or undefined
+ self.__process_hook(False, context, step)
+ self._feature_or_scenario(is_started, element.name, element.location)
+ elif isinstance(element, ScenarioOutline):
+ self._feature_or_scenario(is_started, str(element.examples), element.location)
+ else:
+ self._feature_or_scenario(is_started, element.name, element.location)
+
+ def __init__(self, config, base_dir):
+ """
+ :type config configuration.Configuration
+ """
+ super(_BehaveRunner, self).__init__(base_dir)
+ self.__config = config
+ # Install hooks
+ self.__real_runner = _RunnerWrapper(config, {
+ "before_feature": functools.partial(self.__process_hook, True),
+ "after_feature": functools.partial(self.__process_hook, False),
+ "before_scenario": functools.partial(self.__process_hook, True),
+ "after_scenario": functools.partial(self.__process_hook, False),
+ "before_step": functools.partial(self.__process_hook, True),
+ "after_step": functools.partial(self.__process_hook, False)
+ })
+
+ def _run_tests(self):
+ self.__real_runner.run()
+
+
+ def __filter_scenarios_by_tag(self, scenario):
+ """
+ Filters out scenarios that should be skipped by tags
+ :param scenario scenario to check
+ :return true if should pass
+ """
+ assert isinstance(scenario, Scenario), scenario
+ expected_tags = self.__config.tags
+ if not expected_tags:
+ return True # No tags are required
+ return isinstance(expected_tags, TagExpression) and expected_tags.check(scenario.tags)
+
+
+ def _get_features_to_run(self):
+ self.__real_runner.dry_run = True
+ self.__real_runner.run()
+ features_to_run = self.__real_runner.features
+ self.__real_runner.clean() # To make sure nothing left after dry run
+
+ # Change outline scenario skeletons with real scenarios
+ for feature in features_to_run:
+ assert isinstance(feature, Feature), feature
+ scenarios = []
+ for scenario in feature.scenarios:
+ if isinstance(scenario, ScenarioOutline):
+ scenarios.extend(scenario.scenarios)
+ else:
+ scenarios.append(scenario)
+ feature.scenarios = filter(self.__filter_scenarios_by_tag, scenarios)
+
+ return features_to_run
+
+
+if __name__ == "__main__":
+ # TODO: support all other params instead
+
+ class _Null(Formatter):
+ """
+ Null formater to prevent stdout output
+ """
+ pass
+
+ command_args = list(filter(None, sys.argv[1:]))
+ my_config = configuration.Configuration(command_args=command_args)
+ formatters.register_as(_Null, "com.intellij.python.null")
+ my_config.format = ["com.intellij.python.null"] # To prevent output to stdout
+ my_config.reporters = [] # To prevent summary to stdout
+ my_config.stdout_capture = False # For test output
+ my_config.stderr_capture = False # For test output
+ (base_dir, what_to_run) = _bdd_utils.get_path_by_args(sys.argv)
+ if not my_config.paths: # No path provided, trying to load dit manually
+ if os.path.isfile(what_to_run): # File is provided, load it
+ my_config.paths = [what_to_run]
+ else: # Dir is provided, find subdirs ro run
+ my_config.paths = _get_dirs_to_run(base_dir)
+ _BehaveRunner(my_config, base_dir).run()
+
+
diff --git a/python/helpers/pycharm/lettuce_runner.py b/python/helpers/pycharm/lettuce_runner.py
index 6aaa566df719..3cd112540e5f 100644
--- a/python/helpers/pycharm/lettuce_runner.py
+++ b/python/helpers/pycharm/lettuce_runner.py
@@ -1,132 +1,112 @@
# coding=utf-8
"""
BDD lettuce framework runner
+TODO: Support other params (like tags) as well.
+Supports only 1 param now: folder to search "features" for.
"""
+import _bdd_utils
+
__author__ = 'Ilya.Kazakevich'
-import os
from lettuce.exceptions import ReasonToFail
-import time
import sys
-import tcmessages
import lettuce
from lettuce import core
-# Error message about unsupported outlines
-_NO_OUTLINE_ERROR = "Outline scenarios are not supported due to https://github.com/gabrielfalcao/lettuce/issues/451"
-
-
-class LettuceRunner(object):
+class _LettuceRunner(_bdd_utils.BddRunner):
"""
- TODO: Runs lettuce
+ Lettuce runner (BddRunner for lettuce)
"""
- def __init__(self, base_dir):
+ def __init__(self, base_dir, what_to_run):
"""
+
:param base_dir base directory to run tests in
:type base_dir: str
-
- """
- self.base_dir = base_dir
- self.runner = lettuce.Runner(base_dir)
- self.messages = tcmessages.TeamcityServiceMessages()
- self.test_start_time = None
-
- def report_tests(self):
- """
- :returns : number of tests
- :rtype : int
- """
- result = 0
- for feature_file in self.runner.loader.find_feature_files():
- feature = core.Feature.from_file(feature_file)
- for scenario in feature.scenarios:
- assert isinstance(scenario, core.Scenario), scenario
- if not scenario.outlines:
- result += len(scenario.steps)
- self.messages.testCount(result)
-
- def report_scenario_started(self, scenario):
+ :param what_to_run folder or file to run
+ :type what_to_run str
"""
- Reports scenario launched
- :type scenario core.Scenario
- :param scenario: scenario
- """
- if scenario.outlines:
- self.messages.testIgnored(scenario.name,
- _NO_OUTLINE_ERROR)
- scenario.steps = [] # Clear to prevent running. TODO: Fix when this issue fixed
- scenario.background = None # TODO: undocumented
- return
- self.report_suite(True, scenario.name, scenario.described_at)
+ super(_LettuceRunner, self).__init__(base_dir)
+ self.__runner = lettuce.Runner(what_to_run)
- def report_suite(self, is_start, name, described_at):
- """
- Reports some suite (scenario, feature, background etc) is started or stopped
- :param is_start: started or not
- :param name: suite name
- :param described_at: where it is described (file, line)
- :return:
- """
- if is_start:
- self.messages.testSuiteStarted(name, self._gen_location(described_at))
- else:
- self.messages.testSuiteFinished(name)
+ def _get_features_to_run(self):
+ super(_LettuceRunner, self)._get_features_to_run()
+ if self.__runner.single_feature: # We need to run one and only one feature
+ return [core.Feature.from_file(self.__runner.single_feature)]
- def report_step(self, is_start, step):
+ # Find all features in dir
+ features = []
+ for feature_file in self.__runner.loader.find_feature_files():
+ feature = core.Feature.from_file(feature_file)
+ assert isinstance(feature, core.Feature), feature
+ # TODO: cut out due to https://github.com/gabrielfalcao/lettuce/issues/451 Fix when this issue fixed
+ feature.scenarios = filter(lambda s: not s.outlines, feature.scenarios)
+ if feature.scenarios:
+ features.append(feature)
+ return features
+
+ def _run_tests(self):
+ super(_LettuceRunner, self)._run_tests()
+ self.__install_hooks()
+ self.__runner.run()
+
+ def __step(self, is_started, step):
"""
Reports step start / stop
- :param is_start: true if step started
:type step core.Step
:param step: step
"""
test_name = step.sentence
- if is_start:
- self.test_start_time = time.time()
- self.messages.testStarted(test_name, self._gen_location(step.described_at))
+ if is_started:
+ self._test_started(test_name, step.described_at)
elif step.passed:
- duration = 0
- if self.test_start_time:
- duration = long(time.time() - self.test_start_time)
- self.messages.testFinished(test_name, duration=duration)
- self.test_start_time = None
+ self._test_passed(test_name)
elif step.failed:
reason = step.why
assert isinstance(reason, ReasonToFail), reason
- self.messages.testFailed(test_name, message=reason.exception, details=reason.traceback)
-
- def _gen_location(self, description):
- """
- :param description: "described_at" (file, line)
- :return: location in format file:line by "described_at"
- """
- return "file:///{}/{}:{}".format(self.base_dir, description.file, description.line)
+ self._test_failed(test_name, message=reason.exception, details=reason.traceback)
+ elif step.has_definition:
+ self._test_skipped(test_name, "In lettuce, we do know the reason", step.described_at)
+ else:
+ self._test_undefined(test_name, step.described_at)
- def run(self):
+ def __install_hooks(self):
"""
- Launches runner
+ Installs required hooks
"""
- self.report_tests()
- self.messages.testMatrixEntered()
- lettuce.before.each_feature(lambda f: self.report_suite(True, f.name, f.described_at))
- lettuce.after.each_feature(lambda f: self.report_suite(False, f.name, f.described_at))
+ # Install hooks
+ lettuce.before.each_feature(
+ lambda f: self._feature_or_scenario(True, f.name, f.described_at))
+ lettuce.after.each_feature(
+ lambda f: self._feature_or_scenario(False, f.name, f.described_at))
- lettuce.before.each_scenario(lambda s: self.report_scenario_started(s))
- lettuce.after.each_scenario(lambda s: self.report_suite(False, s.name, s.described_at))
+ lettuce.before.each_scenario(
+ lambda s: self.__scenario(True, s))
+ lettuce.after.each_scenario(
+ lambda s: self.__scenario(False, s))
lettuce.before.each_background(
- lambda b, *args: self.report_suite(True, "Scenario background", b.feature.described_at))
+ lambda b, *args: self._background(True, b.feature.described_at))
lettuce.after.each_background(
- lambda b, *args: self.report_suite(False, "Scenario background", b.feature.described_at))
+ lambda b, *args: self._background(False, b.feature.described_at))
- lettuce.before.each_step(lambda s: self.report_step(True, s))
- lettuce.after.each_step(lambda s: self.report_step(False, s))
+ lettuce.before.each_step(lambda s: self.__step(True, s))
+ lettuce.after.each_step(lambda s: self.__step(False, s))
- self.runner.run()
+ def __scenario(self, is_started, scenario):
+ """
+ Reports scenario launched
+ :type scenario core.Scenario
+ :param scenario: scenario
+ """
+ if scenario.outlines:
+ scenario.steps = [] # Clear to prevent running. TODO: Fix when this issue fixed
+ scenario.background = None # TODO: undocumented
+ return
+ self._feature_or_scenario(is_started, scenario.name, scenario.described_at)
if __name__ == "__main__":
- path = sys.argv[1] if len(sys.argv) > 1 else "."
- assert os.path.exists(path), "{} does not exist".format(path)
- LettuceRunner(path).run() \ No newline at end of file
+ (base_dir, what_to_run) = _bdd_utils.get_path_by_args(sys.argv)
+ _LettuceRunner(base_dir, what_to_run).run() \ No newline at end of file
diff --git a/python/helpers/pycharm/tcunittest.py b/python/helpers/pycharm/tcunittest.py
index b6950c92a11f..99b30595a191 100644
--- a/python/helpers/pycharm/tcunittest.py
+++ b/python/helpers/pycharm/tcunittest.py
@@ -6,14 +6,16 @@ from tcmessages import TeamcityServiceMessages
PYTHON_VERSION_MAJOR = sys.version_info[0]
+
def strclass(cls):
if not cls.__name__:
return cls.__module__
return "%s.%s" % (cls.__module__, cls.__name__)
+
def smart_str(s):
- encoding='utf-8'
- errors='strict'
+ encoding = 'utf-8'
+ errors = 'strict'
if PYTHON_VERSION_MAJOR < 3:
is_string = isinstance(s, basestring)
else:
@@ -33,6 +35,7 @@ def smart_str(s):
else:
return s
+
class TeamcityTestResult(TestResult):
def __init__(self, stream=sys.stdout, *args, **kwargs):
TestResult.__init__(self)
@@ -41,42 +44,47 @@ class TeamcityTestResult(TestResult):
self.output = stream
self.messages = TeamcityServiceMessages(self.output, prepend_linebreak=True)
self.messages.testMatrixEntered()
+ self.current_failed = False
self.current_suite = None
+ self.subtest_suite = None
def find_first(self, val):
quot = val[0]
count = 1
quote_ind = val[count:].find(quot)
- while quote_ind != -1 and val[count+quote_ind-1] == "\\":
+ while quote_ind != -1 and val[count + quote_ind - 1] == "\\":
count = count + quote_ind + 1
quote_ind = val[count:].find(quot)
- return val[0:quote_ind+count+1]
+ return val[0:quote_ind + count + 1]
def find_second(self, val):
val_index = val.find("!=")
if val_index != -1:
count = 1
- val = val[val_index+2:].strip()
+ val = val[val_index + 2:].strip()
quot = val[0]
quote_ind = val[count:].find(quot)
- while quote_ind != -1 and val[count+quote_ind-1] == "\\":
+ while quote_ind != -1 and val[count + quote_ind - 1] == "\\":
count = count + quote_ind + 1
quote_ind = val[count:].find(quot)
- return val[0:quote_ind+count+1]
+ return val[0:quote_ind + count + 1]
else:
quot = val[-1]
- quote_ind = val[:len(val)-1].rfind(quot)
- while quote_ind != -1 and val[quote_ind-1] == "\\":
- quote_ind = val[:quote_ind-1].rfind(quot)
+ quote_ind = val[:len(val) - 1].rfind(quot)
+ while quote_ind != -1 and val[quote_ind - 1] == "\\":
+ quote_ind = val[:quote_ind - 1].rfind(quot)
return val[quote_ind:]
def formatErr(self, err):
exctype, value, tb = err
return ''.join(traceback.format_exception(exctype, value, tb))
- def getTestName(self, test):
+ def getTestName(self, test, is_subtest=False):
+ if is_subtest:
+ test_name = self.getTestName(test.test_case)
+ return "{} {}".format(test_name, test._subDescription())
if hasattr(test, '_testMethodName'):
if test._testMethodName == "runTest":
return str(test)
@@ -95,10 +103,13 @@ class TeamcityTestResult(TestResult):
TestResult.addSuccess(self, test)
def addError(self, test, err):
+ self.init_suite(test)
+ self.current_failed = True
TestResult.addError(self, test, err)
err = self._exc_info_to_string(err, test)
+ self.messages.testStarted(self.getTestName(test))
self.messages.testError(self.getTestName(test),
message='Error', details=err)
@@ -108,6 +119,8 @@ class TeamcityTestResult(TestResult):
return error_value.split('assert')[-1].strip()
def addFailure(self, test, err):
+ self.init_suite(test)
+ self.current_failed = True
TestResult.addFailure(self, test, err)
error_value = smart_str(err[1])
@@ -119,7 +132,7 @@ class TeamcityTestResult(TestResult):
self_find_second = self.find_second(error_value)
quotes = ["'", '"']
if (self_find_first[0] == self_find_first[-1] and self_find_first[0] in quotes and
- self_find_second[0] == self_find_second[-1] and self_find_second[0] in quotes):
+ self_find_second[0] == self_find_second[-1] and self_find_second[0] in quotes):
# let's unescape strings to show sexy multiline diff in PyCharm.
# By default all caret return chars are escaped by testing framework
first = self._unescape(self_find_first)
@@ -128,10 +141,13 @@ class TeamcityTestResult(TestResult):
first = second = ""
err = self._exc_info_to_string(err, test)
+ self.messages.testStarted(self.getTestName(test))
self.messages.testFailed(self.getTestName(test),
message='Failure', details=err, expected=first, actual=second)
def addSkip(self, test, reason):
+ self.init_suite(test)
+ self.current_failed = True
self.messages.testIgnored(self.getTestName(test), message=reason)
def __getSuite(self, test):
@@ -149,10 +165,10 @@ class TeamcityTestResult(TestResult):
try:
source_file = inspect.getsourcefile(test.__class__)
if source_file:
- source_dir_splitted = source_file.split("/")[:-1]
- source_dir = "/".join(source_dir_splitted) + "/"
+ source_dir_splitted = source_file.split("/")[:-1]
+ source_dir = "/".join(source_dir_splitted) + "/"
else:
- source_dir = ""
+ source_dir = ""
except TypeError:
source_dir = ""
@@ -163,20 +179,52 @@ class TeamcityTestResult(TestResult):
return (suite, location, suite_location)
def startTest(self, test):
+ self.current_failed = False
+ setattr(test, "startTime", datetime.datetime.now())
+
+ def init_suite(self, test):
suite, location, suite_location = self.__getSuite(test)
if suite != self.current_suite:
if self.current_suite:
self.messages.testSuiteFinished(self.current_suite)
self.current_suite = suite
self.messages.testSuiteStarted(self.current_suite, location=suite_location)
- setattr(test, "startTime", datetime.datetime.now())
- self.messages.testStarted(self.getTestName(test), location=location)
+ return location
def stopTest(self, test):
start = getattr(test, "startTime", datetime.datetime.now())
d = datetime.datetime.now() - start
- duration=d.microseconds / 1000 + d.seconds * 1000 + d.days * 86400000
- self.messages.testFinished(self.getTestName(test), duration=int(duration))
+ duration = d.microseconds / 1000 + d.seconds * 1000 + d.days * 86400000
+ if not self.subtest_suite:
+ if not self.current_failed:
+ location = self.init_suite(test)
+ self.messages.testStarted(self.getTestName(test), location=location)
+ self.messages.testFinished(self.getTestName(test), duration=int(duration))
+ else:
+ self.messages.testSuiteFinished(self.subtest_suite)
+ self.subtest_suite = None
+
+
+ def addSubTest(self, test, subtest, err):
+ suite_name = self.getTestName(test) # + " (subTests)"
+ if not self.subtest_suite:
+ self.subtest_suite = suite_name
+ self.messages.testSuiteStarted(self.subtest_suite)
+ else:
+ if suite_name != self.subtest_suite:
+ self.messages.testSuiteFinished(self.subtest_suite)
+ self.subtest_suite = suite_name
+ self.messages.testSuiteStarted(self.subtest_suite)
+
+ name = self.getTestName(subtest, True)
+ if err is not None:
+ error = self._exc_info_to_string(err, test)
+ self.messages.testStarted(name)
+ self.messages.testFailed(name, message='Failure', details=error)
+ else:
+ self.messages.testStarted(name)
+ self.messages.testFinished(name)
+
def endLastSuite(self):
if self.current_suite:
@@ -187,6 +235,7 @@ class TeamcityTestResult(TestResult):
# do not use text.decode('string_escape'), it leads to problems with different string encodings given
return text.replace("\\n", "\n")
+
class TeamcityTestRunner(object):
def __init__(self, stream=sys.stdout):
self.stream = stream
diff --git a/python/ide/src/com/jetbrains/python/newProject/PyCharmNewProjectDialog.java b/python/ide/src/com/jetbrains/python/newProject/PyCharmNewProjectDialog.java
index d186f33620e5..7570e3f05b31 100644
--- a/python/ide/src/com/jetbrains/python/newProject/PyCharmNewProjectDialog.java
+++ b/python/ide/src/com/jetbrains/python/newProject/PyCharmNewProjectDialog.java
@@ -16,9 +16,11 @@
package com.jetbrains.python.newProject;
import com.intellij.openapi.actionSystem.DefaultActionGroup;
+import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.wm.impl.welcomeScreen.CardActionsPanel;
+import com.intellij.platform.DirectoryProjectGenerator;
import com.jetbrains.python.newProject.actions.PyCharmNewProjectStep;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -42,7 +44,8 @@ public class PyCharmNewProjectDialog extends DialogWrapper {
PyCharmNewProjectDialog.this.close(OK_EXIT_CODE);
}
};
- final DefaultActionGroup root = new PyCharmNewProjectStep(runnable);
+ final DirectoryProjectGenerator[] generators = Extensions.getExtensions(DirectoryProjectGenerator.EP_NAME);
+ final DefaultActionGroup root = new PyCharmNewProjectStep(generators.length == 0 ? "Create Project" : "Select Project Type", runnable);
return new CardActionsPanel(root) {
@@ -53,6 +56,7 @@ public class PyCharmNewProjectDialog extends DialogWrapper {
@Override
public Dimension getMinimumSize() {
+ if (generators.length == 0) return new Dimension(550, 200);
return new Dimension(650, 450);
}
};
diff --git a/python/ide/src/com/jetbrains/python/newProject/actions/AbstractProjectSettingsStep.java b/python/ide/src/com/jetbrains/python/newProject/actions/AbstractProjectSettingsStep.java
index d9f3cf24f0e5..6ba283caf199 100644
--- a/python/ide/src/com/jetbrains/python/newProject/actions/AbstractProjectSettingsStep.java
+++ b/python/ide/src/com/jetbrains/python/newProject/actions/AbstractProjectSettingsStep.java
@@ -8,6 +8,7 @@ import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.Presentation;
import com.intellij.openapi.actionSystem.impl.ActionButtonWithText;
+import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
import com.intellij.openapi.project.DumbAware;
@@ -45,10 +46,12 @@ import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import javax.swing.border.Border;
+import javax.swing.border.LineBorder;
import javax.swing.event.DocumentEvent;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
+import java.awt.event.FocusEvent;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
@@ -62,9 +65,10 @@ abstract public class AbstractProjectSettingsStep extends AbstractActionWithPane
private boolean myInstallFramework;
private TextFieldWithBrowseButton myLocationField;
protected final File myProjectDirectory;
- private ActionButtonWithText myCreateButton;
+ private Button myCreateButton;
private JLabel myErrorLabel;
private AnAction myCreateAction;
+ private Sdk mySdk;
public AbstractProjectSettingsStep(DirectoryProjectGenerator projectGenerator, NullableConsumer<AbstractProjectSettingsStep> callback) {
super();
@@ -104,16 +108,24 @@ abstract public class AbstractProjectSettingsStep extends AbstractActionWithPane
@Override
public JPanel createPanel() {
- final JPanel mainPanel = new JPanel(new BorderLayout());
+ final JPanel basePanel = createBasePanel();
+ final JPanel mainPanel = new JPanel(new BorderLayout()) {
+ @Override
+ protected void paintComponent(Graphics g) {
+ myLocationField.requestFocus();
+ }
+ };
+
final JPanel scrollPanel = new JPanel(new BorderLayout());
- mainPanel.setPreferredSize(new Dimension(mainPanel.getPreferredSize().width, 400));
+ final DirectoryProjectGenerator[] generators = Extensions.getExtensions(DirectoryProjectGenerator.EP_NAME);
+ final int height = generators.length == 0 ? 150 : 400;
+ mainPanel.setPreferredSize(new Dimension(mainPanel.getPreferredSize().width, height));
myErrorLabel = new JLabel("");
myErrorLabel.setForeground(JBColor.RED);
myCreateButton = new Button(myCreateAction, myCreateAction.getTemplatePresentation());
- final JPanel panel = createBasePanel();
- scrollPanel.add(panel, BorderLayout.NORTH);
+ scrollPanel.add(basePanel, BorderLayout.NORTH);
final JPanel advancedSettings = createAdvancedSettings();
if (advancedSettings != null) {
scrollPanel.add(advancedSettings, BorderLayout.CENTER);
@@ -125,8 +137,6 @@ abstract public class AbstractProjectSettingsStep extends AbstractActionWithPane
final JPanel bottomPanel = new JPanel(new BorderLayout());
-
- myCreateButton.setPreferredSize(new Dimension(mainPanel.getPreferredSize().width, 40));
bottomPanel.add(myErrorLabel, BorderLayout.NORTH);
bottomPanel.add(myCreateButton, BorderLayout.EAST);
mainPanel.add(bottomPanel, BorderLayout.SOUTH);
@@ -150,7 +160,22 @@ abstract public class AbstractProjectSettingsStep extends AbstractActionWithPane
final FileChooserDescriptor descriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor();
myLocationField.addBrowseFolderListener("Select base directory", "Select base directory for the Project",
null, descriptor);
-
+ myLocationField.getTextField().getDocument().addDocumentListener(new DocumentAdapter() {
+ @Override
+ protected void textChanged(DocumentEvent e) {
+ if (myProjectGenerator instanceof PythonProjectGenerator) {
+ String path = myLocationField.getText().trim();
+ if (path.endsWith(File.separator)) {
+ path = path.substring(0, path.length() - File.separator.length());
+ }
+ int ind = path.lastIndexOf(File.separator);
+ if (ind != -1) {
+ String projectName = path.substring(ind + 1, path.length());
+ ((PythonProjectGenerator)myProjectGenerator).locationChanged(projectName);
+ }
+ }
+ }
+ });
final JLabel locationLabel = new JLabel("Location:");
c.gridx = 0;
c.gridy = 0;
@@ -193,13 +218,23 @@ abstract public class AbstractProjectSettingsStep extends AbstractActionWithPane
c.gridy = 1;
c.weightx = 1.;
panel.add(mySdkCombo, c);
-
+ final JPanel basePanelExtension = extendBasePanel();
+ if (basePanelExtension != null) {
+ c.gridwidth = 2;
+ c.gridy = 2;
+ c.gridx = 0;
+ panel.add(basePanelExtension, c);
+ }
registerValidators();
return panel;
}
- protected void registerValidators() {
+ @Nullable
+ protected JPanel extendBasePanel() {
+ return null;
+ }
+ protected void registerValidators() {
myLocationField.getTextField().getDocument().addDocumentListener(new DocumentAdapter() {
@Override
protected void textChanged(DocumentEvent e) {
@@ -358,7 +393,8 @@ abstract public class AbstractProjectSettingsStep extends AbstractActionWithPane
public Button(AnAction action, Presentation presentation) {
super(action, presentation, "NewProject", new Dimension(70, 50));
- myBorder = UIUtil.isUnderDarcula() ? UIUtil.getButtonBorder() : BorderFactory.createLineBorder(UIUtil.getBorderColor());
+ final Border border = new LineBorder(JBColor.border(), 1, true);
+ myBorder = UIUtil.isUnderDarcula() ? UIUtil.getButtonBorder() : border;
setBorder(myBorder);
}
@@ -368,6 +404,23 @@ abstract public class AbstractProjectSettingsStep extends AbstractActionWithPane
}
@Override
+ public boolean isFocusable() {
+ return true;
+ }
+
+ @Override
+ protected void processFocusEvent(FocusEvent e) {
+ super.processFocusEvent(e);
+ if (e.getID() == FocusEvent.FOCUS_GAINED) {
+ processMouseEvent(new MouseEvent(this, MouseEvent.MOUSE_ENTERED, System.currentTimeMillis(), 0, 0, 0, 0, false));
+
+ }
+ else if (e.getID() == FocusEvent.FOCUS_LOST) {
+ processMouseEvent(new MouseEvent(this, MouseEvent.MOUSE_EXITED, System.currentTimeMillis(), 0, 0, 0, 0, false));
+ }
+ }
+
+ @Override
public Insets getInsets() {
return new Insets(5,10,5,5);
}
@@ -377,11 +430,6 @@ abstract public class AbstractProjectSettingsStep extends AbstractActionWithPane
return SwingConstants.LEFT;
}
- @Override
- public String getToolTipText() {
- return null;
- }
-
protected void processMouseEvent(MouseEvent e) {
super.processMouseEvent(e);
if (e.getID() == MouseEvent.MOUSE_ENTERED) {
@@ -394,9 +442,14 @@ abstract public class AbstractProjectSettingsStep extends AbstractActionWithPane
}
public Sdk getSdk() {
+ if (mySdk != null) return mySdk;
return (Sdk)mySdkCombo.getComboBox().getSelectedItem();
}
+ public void setSdk(final Sdk sdk) {
+ mySdk = sdk;
+ }
+
public String getProjectLocation() {
return myLocationField.getText();
}
diff --git a/python/ide/src/com/jetbrains/python/newProject/actions/ProjectSpecificSettingsStep.java b/python/ide/src/com/jetbrains/python/newProject/actions/ProjectSpecificSettingsStep.java
index ebd0658b072c..5e60601399bb 100644
--- a/python/ide/src/com/jetbrains/python/newProject/actions/ProjectSpecificSettingsStep.java
+++ b/python/ide/src/com/jetbrains/python/newProject/actions/ProjectSpecificSettingsStep.java
@@ -15,6 +15,7 @@
*/
package com.jetbrains.python.newProject.actions;
+import com.intellij.facet.ui.ValidationResult;
import com.intellij.ide.util.projectWizard.WebProjectTemplate;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.ui.VerticalFlowLayout;
@@ -46,6 +47,12 @@ public class ProjectSpecificSettingsStep extends AbstractProjectSettingsStep imp
if (advancedSettings != null) {
final JPanel jPanel = new JPanel(new VerticalFlowLayout());
final HideableDecorator deco = new HideableDecorator(jPanel, "Mor&e Settings", false);
+ boolean isValid = checkValid();
+ deco.setOn(!isValid);
+ if (myProjectGenerator instanceof PythonProjectGenerator && !deco.isExpanded()) {
+ final ValidationResult result = ((PythonProjectGenerator)myProjectGenerator).warningValidation(getSdk());
+ deco.setOn(!result.isOk());
+ }
deco.setContentComponent(advancedSettings);
return jPanel;
}
diff --git a/python/ide/src/com/jetbrains/python/newProject/actions/PyCharmNewProjectStep.java b/python/ide/src/com/jetbrains/python/newProject/actions/PyCharmNewProjectStep.java
index b6134ed5f070..4f9f1074df68 100644
--- a/python/ide/src/com/jetbrains/python/newProject/actions/PyCharmNewProjectStep.java
+++ b/python/ide/src/com/jetbrains/python/newProject/actions/PyCharmNewProjectStep.java
@@ -62,8 +62,8 @@ import java.util.List;
public class PyCharmNewProjectStep extends DefaultActionGroup implements DumbAware {
private static final Logger LOG = Logger.getInstance(PyCharmNewProjectStep.class);
- public PyCharmNewProjectStep(@Nullable final Runnable runnable) {
- super("Select Project Type", true);
+ public PyCharmNewProjectStep(@NotNull final String name, @Nullable final Runnable runnable) {
+ super(name, true);
final NullableConsumer<AbstractProjectSettingsStep> callback = new NullableConsumer<AbstractProjectSettingsStep>() {
@Override
@@ -87,6 +87,7 @@ public class PyCharmNewProjectStep extends DefaultActionGroup implements DumbAwa
sdk = SdkConfigurationUtil.setupSdk(ProjectJdkTable.getInstance().getAllJdks(), sdkHome, PythonSdkType.getInstance(), true, null,
null);
model.addSdk(sdk);
+ settingsStep.setSdk(sdk);
try {
model.apply();
}
@@ -165,6 +166,9 @@ public class PyCharmNewProjectStep extends DefaultActionGroup implements DumbAwa
add(action);
final DirectoryProjectGenerator[] generators = Extensions.getExtensions(DirectoryProjectGenerator.EP_NAME);
+ if (generators.length == 0) {
+ action.setPopup(false);
+ }
Arrays.sort(generators, new Comparator<DirectoryProjectGenerator>() {
@Override
public int compare(DirectoryProjectGenerator o1, DirectoryProjectGenerator o2) {
@@ -188,7 +192,7 @@ public class PyCharmNewProjectStep extends DefaultActionGroup implements DumbAwa
}
public PyCharmNewProjectStep() {
- this(null);
+ this("Select Project Type", null);
}
diff --git a/python/openapi/src/com/jetbrains/python/newProject/PythonProjectGenerator.java b/python/openapi/src/com/jetbrains/python/newProject/PythonProjectGenerator.java
index 5577959178ce..1432e468f3e8 100644
--- a/python/openapi/src/com/jetbrains/python/newProject/PythonProjectGenerator.java
+++ b/python/openapi/src/com/jetbrains/python/newProject/PythonProjectGenerator.java
@@ -31,6 +31,8 @@ public abstract class PythonProjectGenerator {
myListeners.add(listener);
}
+ public void locationChanged(@NotNull final String newLocation) {}
+
public interface SettingsListener {
void stateChanged();
}
diff --git a/python/psi-api/src/com/jetbrains/python/psi/PyElementGenerator.java b/python/psi-api/src/com/jetbrains/python/psi/PyElementGenerator.java
index 9f6e7c9ff021..daa8b3063014 100644
--- a/python/psi-api/src/com/jetbrains/python/psi/PyElementGenerator.java
+++ b/python/psi-api/src/com/jetbrains/python/psi/PyElementGenerator.java
@@ -37,15 +37,17 @@ public abstract class PyElementGenerator {
public abstract PyStringLiteralExpression createStringLiteralAlreadyEscaped(String str);
-
-
/**
* Creates a string literal, adding appropriate quotes, properly escaping characters inside.
+ *
* @param destination where the literal is destined to; used to determine the encoding.
* @param unescaped the string
- * @return a newly created literal
+ * @param preferUTF8 try to use UTF8 (would use ascii if false)
+ * @return a newly created literal
*/
- public abstract PyStringLiteralExpression createStringLiteralFromString(@Nullable PsiFile destination, String unescaped);
+ public abstract PyStringLiteralExpression createStringLiteralFromString(@Nullable PsiFile destination, String unescaped,
+ boolean preferUTF8);
+
public abstract PyStringLiteralExpression createStringLiteralFromString(@NotNull String unescaped);
public abstract PyStringLiteralExpression createStringLiteral(@NotNull PyStringLiteralExpression oldElement, @NotNull String unescaped);
@@ -59,11 +61,11 @@ public abstract class PyElementGenerator {
public abstract PyBinaryExpression createBinaryExpression(String s, PyExpression expr, PyExpression listLiteral);
/**
- * @deprecated use the overload with language level specified
* @param text the text to create an expression from
* @return the expression
+ * @deprecated use the overload with language level specified
*/
- public abstract PyExpression createExpressionFromText(String text);
+ public abstract PyExpression createExpressionFromText(String text);
public abstract PyExpression createExpressionFromText(final LanguageLevel languageLevel, String text);
@@ -71,9 +73,9 @@ public abstract class PyElementGenerator {
* Adds elements to list inserting required commas.
* Method is like {@link #insertItemIntoList(PyElement, PyExpression, PyExpression)} but does not add unneeded commas.
*
- * @param list where to add
+ * @param list where to add
* @param afterThis after which element it should be added (null for add to the head)
- * @param toInsert what to insert
+ * @param toInsert what to insert
* @return newly inserted element
*/
@NotNull
@@ -92,7 +94,10 @@ public abstract class PyElementGenerator {
public abstract PyImportElement createImportElement(final LanguageLevel languageLevel, String name);
- public abstract PyFunction createProperty(final LanguageLevel languageLevel, String propertyName, String fieldName, AccessDirection accessDirection);
+ public abstract PyFunction createProperty(final LanguageLevel languageLevel,
+ String propertyName,
+ String fieldName,
+ AccessDirection accessDirection);
@NotNull
public abstract <T> T createFromText(LanguageLevel langLevel, Class<T> aClass, final String text);
@@ -103,6 +108,7 @@ public abstract class PyElementGenerator {
/**
* Creates an arbitrary PSI element from text, by creating a bigger construction and then cutting the proper subelement.
* Will produce all kinds of exceptions if the path or class would not match the PSI tree.
+ *
* @param langLevel the language level to use for parsing the text
* @param aClass class of the PSI element; may be an interface not descending from PsiElement, as long as target node can be cast to it
* @param text text to parse
@@ -122,8 +128,15 @@ public abstract class PyElementGenerator {
public abstract PsiFile createDummyFile(LanguageLevel langLevel, String contents);
public abstract PyExpressionStatement createDocstring(String content);
+
public abstract PyPassStatement createPassStatement();
@NotNull
public abstract PyDecoratorList createDecoratorList(@NotNull final String... decoratorTexts);
+
+ /**
+ * Creates new line whitespace
+ */
+ @NotNull
+ public abstract PsiElement createNewLine();
}
diff --git a/python/psi-api/src/com/jetbrains/python/psi/PyElsePart.java b/python/psi-api/src/com/jetbrains/python/psi/PyElsePart.java
index c32916242154..41b715f99aa8 100644
--- a/python/psi-api/src/com/jetbrains/python/psi/PyElsePart.java
+++ b/python/psi-api/src/com/jetbrains/python/psi/PyElsePart.java
@@ -15,17 +15,10 @@
*/
package com.jetbrains.python.psi;
-import org.jetbrains.annotations.Nullable;
-
/**
* The 'else:' part of various compound statements.
* User: dcheryasov
* Date: Mar 15, 2009 9:34:51 PM
*/
public interface PyElsePart extends PyStatementPart {
- /**
- * @return the body of the 'else' part.
- */
- @Nullable
- PyStatementList getStatementList();
}
diff --git a/python/psi-api/src/com/jetbrains/python/psi/PyStatementPart.java b/python/psi-api/src/com/jetbrains/python/psi/PyStatementPart.java
index f491a6a83688..a14a91ef9f9c 100644
--- a/python/psi-api/src/com/jetbrains/python/psi/PyStatementPart.java
+++ b/python/psi-api/src/com/jetbrains/python/psi/PyStatementPart.java
@@ -15,19 +15,19 @@
*/
package com.jetbrains.python.psi;
-import org.jetbrains.annotations.Nullable;
+import org.jetbrains.annotations.NotNull;
/**
* Abstract part of a multipart statement.
* User: dcheryasov
* Date: Mar 16, 2009 4:34:59 AM
*/
-public interface PyStatementPart extends PyElement {
+public interface PyStatementPart extends PyElement, PyStatementListContainer {
PyStatementPart[] EMPTY_ARRAY = new PyStatementPart[0];
/**
* @return the body of the part.
*/
- @Nullable
+ @NotNull
PyStatementList getStatementList();
}
diff --git a/python/psi-api/src/com/jetbrains/python/psi/PyWithStatement.java b/python/psi-api/src/com/jetbrains/python/psi/PyWithStatement.java
index 201ae3046785..b23ca63b6c95 100644
--- a/python/psi-api/src/com/jetbrains/python/psi/PyWithStatement.java
+++ b/python/psi-api/src/com/jetbrains/python/psi/PyWithStatement.java
@@ -18,6 +18,6 @@ package com.jetbrains.python.psi;
/**
* @author yole
*/
-public interface PyWithStatement extends PyStatement, NameDefiner {
+public interface PyWithStatement extends PyStatement, NameDefiner, PyStatementListContainer {
PyWithItem[] getWithItems();
}
diff --git a/python/psi-api/src/com/jetbrains/python/psi/StructuredDocString.java b/python/psi-api/src/com/jetbrains/python/psi/StructuredDocString.java
index fa8881062ac3..808ec477e89a 100644
--- a/python/psi-api/src/com/jetbrains/python/psi/StructuredDocString.java
+++ b/python/psi-api/src/com/jetbrains/python/psi/StructuredDocString.java
@@ -25,6 +25,15 @@ import java.util.List;
* @author vlan
*/
public interface StructuredDocString {
+ /**
+ * Creates parameter type documentation specific for certain doct type
+ * @param name param name
+ * @param type param type
+ * @return text to add to docsting
+ */
+ @NotNull
+ String createParameterType(@NotNull String name, @NotNull String type);
+
String getDescription();
String getSummary();
diff --git a/python/python-rest/src/com/jetbrains/rest/run/RestCommandLineState.java b/python/python-rest/src/com/jetbrains/rest/run/RestCommandLineState.java
index e421df82d4f7..e5b52b5dc3a7 100644
--- a/python/python-rest/src/com/jetbrains/rest/run/RestCommandLineState.java
+++ b/python/python-rest/src/com/jetbrains/rest/run/RestCommandLineState.java
@@ -19,7 +19,6 @@ import com.intellij.execution.ExecutionException;
import com.intellij.execution.configurations.GeneralCommandLine;
import com.intellij.execution.configurations.ParametersList;
import com.intellij.execution.configurations.ParamsGroup;
-import com.intellij.execution.filters.Filter;
import com.intellij.execution.process.ProcessAdapter;
import com.intellij.execution.process.ProcessEvent;
import com.intellij.execution.process.ProcessHandler;
@@ -33,7 +32,6 @@ import com.jetbrains.python.run.PythonProcessRunner;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
-import java.util.Collections;
/**
* User : catherine
@@ -43,7 +41,7 @@ public abstract class RestCommandLineState extends PythonCommandLineState {
public RestCommandLineState(RestRunConfiguration configuration,
ExecutionEnvironment env) {
- super(configuration, env, Collections.<Filter>emptyList());
+ super(configuration, env);
myConfiguration = configuration;
}
diff --git a/python/src/META-INF/python-core.xml b/python/src/META-INF/python-core.xml
index 88280cc2cf10..a27eb44ae3af 100644
--- a/python/src/META-INF/python-core.xml
+++ b/python/src/META-INF/python-core.xml
@@ -528,6 +528,12 @@
<!-- Packaging -->
<moduleService serviceInterface="com.jetbrains.python.packaging.PyPackageRequirementsSettings"
serviceImplementation="com.jetbrains.python.packaging.PyPackageRequirementsSettings"/>
+
+ <!-- Console -->
+ <toolWindow id="Python Console" anchor="bottom" icon=""
+ factoryClass="com.jetbrains.python.console.PythonConsoleToolWindowFactory" secondary="false"/>
+
+
</extensions>
<extensionPoints>
@@ -615,6 +621,12 @@
</component>
</project-components>
+ <project-components>
+ <component>
+ <implementation-class>com.jetbrains.python.console.PythonConsoleToolWindow</implementation-class>
+ </component>
+ </project-components>
+
<actions>
<group id="PyTypeHierarchyPopupMenu">
<reference ref="TypeHierarchyBase.BaseOnThisType"/>
diff --git a/python/src/com/jetbrains/python/PyAddImportFix.java b/python/src/com/jetbrains/python/PyAddImportFix.java
new file mode 100644
index 000000000000..c13fed13fdcb
--- /dev/null
+++ b/python/src/com/jetbrains/python/PyAddImportFix.java
@@ -0,0 +1,55 @@
+package com.jetbrains.python;
+
+import com.intellij.codeInspection.LocalQuickFix;
+import com.intellij.codeInspection.ProblemDescriptor;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.PsiElement;
+import com.jetbrains.python.codeInsight.imports.AddImportHelper;
+import com.jetbrains.python.psi.LanguageLevel;
+import com.jetbrains.python.psi.PyElementGenerator;
+import com.jetbrains.python.psi.PyFile;
+import com.jetbrains.python.psi.PyImportStatementBase;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Quick fix that adds import to file
+ *
+ * @author Ilya.Kazakevich
+ */
+public class PyAddImportFix implements LocalQuickFix {
+ @NotNull
+ private final String myImportToAdd;
+ @NotNull
+ private final PyFile myFile;
+
+ /**
+ * @param importToAdd string representing what to add (i.e. "from foo import bar")
+ * @param file where to add
+ */
+ public PyAddImportFix(@NotNull final String importToAdd, @NotNull final PyFile file) {
+ myImportToAdd = importToAdd;
+ myFile = file;
+ }
+
+ @NotNull
+ @Override
+ public String getName() {
+ return PyBundle.message("QFIX.add.import", myImportToAdd);
+ }
+
+ @NotNull
+ @Override
+ public String getFamilyName() {
+ return getName();
+ }
+
+ @Override
+ public void applyFix(@NotNull final Project project, @NotNull final ProblemDescriptor descriptor) {
+ final PyElementGenerator generator = PyElementGenerator.getInstance(project);
+ final PyImportStatementBase statement =
+ generator.createFromText(LanguageLevel.forElement(myFile), PyImportStatementBase.class, myImportToAdd);
+ final PsiElement recommendedPosition = AddImportHelper.getFileInsertPosition(myFile);
+ myFile.addAfter(statement, recommendedPosition);
+ }
+}
+
diff --git a/python/src/com/jetbrains/python/PyBundle.properties b/python/src/com/jetbrains/python/PyBundle.properties
index 5d29d3f79d50..482daa88b66c 100644
--- a/python/src/com/jetbrains/python/PyBundle.properties
+++ b/python/src/com/jetbrains/python/PyBundle.properties
@@ -32,6 +32,8 @@ QFIX.create.property=Create property
QFIX.add.encoding=Add encoding declaration
+QFIX.add.import=Add "''{0}''"
+
QFIX.NAME.parameters=Parameters of functions and methods
QFIX.rename.parameter.to.$0=Rename to ''{0}''
diff --git a/python/src/com/jetbrains/python/actions/ExecuteInConsoleAction.java b/python/src/com/jetbrains/python/actions/ExecuteInConsoleAction.java
index 58b35fe5f591..cb90d498e663 100644
--- a/python/src/com/jetbrains/python/actions/ExecuteInConsoleAction.java
+++ b/python/src/com/jetbrains/python/actions/ExecuteInConsoleAction.java
@@ -17,7 +17,6 @@ package com.jetbrains.python.actions;
import com.intellij.execution.ExecutionHelper;
import com.intellij.execution.console.LanguageConsoleView;
-import com.intellij.execution.console.LanguageConsoleViewImpl;
import com.intellij.execution.process.ProcessHandler;
import com.intellij.execution.ui.RunContentDescriptor;
import com.intellij.openapi.actionSystem.*;
@@ -215,7 +214,7 @@ public class ExecuteInConsoleAction extends AnAction {
private static void startConsole(final Project project,
final Consumer<PyCodeExecutor> consumer,
Module context) {
- PydevConsoleRunner runner = RunPythonConsoleAction.runPythonConsole(project, context);
+ PydevConsoleRunner runner = RunPythonConsoleAction.runPythonConsole(project, context, null);
runner.addConsoleListener(new PydevConsoleRunner.ConsoleListener() {
@Override
public void handleConsoleInitialized(LanguageConsoleView consoleView) {
diff --git a/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/PySmartEnterProcessor.java b/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/PySmartEnterProcessor.java
index e27f2a638899..2785b23afc56 100644
--- a/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/PySmartEnterProcessor.java
+++ b/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/PySmartEnterProcessor.java
@@ -79,7 +79,7 @@ public class PySmartEnterProcessor extends SmartEnterProcessor {
}
final PsiElement[] children = element.getChildren();
- for (PsiElement child : children) {
+ for (final PsiElement child : children) {
if (element instanceof PyStatement && child instanceof PyStatement) {
continue;
}
diff --git a/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/enterProcessors/PyPlainEnterProcessor.java b/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/enterProcessors/PyPlainEnterProcessor.java
index ac2e31252dcf..ccdcf1b93f2a 100644
--- a/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/enterProcessors/PyPlainEnterProcessor.java
+++ b/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/enterProcessors/PyPlainEnterProcessor.java
@@ -20,7 +20,9 @@ import com.intellij.openapi.editor.Editor;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import com.jetbrains.python.codeInsight.editorActions.smartEnter.SmartEnterUtil;
-import com.jetbrains.python.psi.*;
+import com.jetbrains.python.psi.PyStatementList;
+import com.jetbrains.python.psi.PyStatementListContainer;
+import com.jetbrains.python.psi.PyStatementPart;
import org.jetbrains.annotations.Nullable;
/**
@@ -32,17 +34,8 @@ import org.jetbrains.annotations.Nullable;
public class PyPlainEnterProcessor implements EnterProcessor {
@Nullable
private static PyStatementList getStatementList(PsiElement psiElement, Editor editor) {
- if (psiElement instanceof PyStatementPart) {
- return ((PyStatementPart)psiElement).getStatementList();
- }
- else if (psiElement instanceof PyFunction) {
- return ((PyFunction)psiElement).getStatementList();
- }
- else if (psiElement instanceof PyClass) {
- return ((PyClass)psiElement).getStatementList();
- }
- else if (psiElement instanceof PyWithStatement) {
- return PsiTreeUtil.getChildOfType(psiElement, PyStatementList.class);
+ if (psiElement instanceof PyStatementListContainer) {
+ return ((PyStatementListContainer)psiElement).getStatementList();
}
else {
final CaretModel caretModel = editor.getCaretModel();
diff --git a/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyArgumentListFixer.java b/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyArgumentListFixer.java
index f7d877e5bb5b..de90fa158bab 100644
--- a/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyArgumentListFixer.java
+++ b/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyArgumentListFixer.java
@@ -25,26 +25,30 @@ import com.jetbrains.python.psi.PyArgumentList;
import com.jetbrains.python.psi.PyClass;
import com.jetbrains.python.psi.PyDecorator;
import com.jetbrains.python.psi.PyUtil;
+import org.jetbrains.annotations.NotNull;
/**
* @author Alexey.Ivanov
*/
-public class PyArgumentListFixer implements PyFixer {
- public void apply(Editor editor, PySmartEnterProcessor processor, PsiElement psiElement) throws IncorrectOperationException {
- if (psiElement instanceof PyArgumentList) {
- final PsiElement rBrace = PyUtil.getChildByFilter(psiElement, PyTokenTypes.CLOSE_BRACES, 0);
- if (psiElement.getParent() instanceof PyClass || psiElement.getParent() instanceof PyDecorator) {
- final PsiElement lBrace = PyUtil.getChildByFilter(psiElement, PyTokenTypes.OPEN_BRACES, 0);
- if (lBrace != null && rBrace == null) {
- final Document document = editor.getDocument();
- document.insertString(psiElement.getTextRange().getEndOffset(), ")");
- }
+public class PyArgumentListFixer extends PyFixer<PyArgumentList> {
+ public PyArgumentListFixer() {
+ super(PyArgumentList.class);
+ }
+
+ @Override
+ public void doApply(@NotNull Editor editor, @NotNull PySmartEnterProcessor processor, @NotNull PyArgumentList arguments) throws IncorrectOperationException {
+ final PsiElement rBrace = PyUtil.getChildByFilter(arguments, PyTokenTypes.CLOSE_BRACES, 0);
+ if (arguments.getParent() instanceof PyClass || arguments.getParent() instanceof PyDecorator) {
+ final PsiElement lBrace = PyUtil.getChildByFilter(arguments, PyTokenTypes.OPEN_BRACES, 0);
+ if (lBrace != null && rBrace == null) {
+ final Document document = editor.getDocument();
+ document.insertString(arguments.getTextRange().getEndOffset(), ")");
}
- else {
- if (rBrace == null) {
- final Document document = editor.getDocument();
- document.insertString(psiElement.getTextRange().getEndOffset(), ")");
- }
+ }
+ else {
+ if (rBrace == null) {
+ final Document document = editor.getDocument();
+ document.insertString(arguments.getTextRange().getEndOffset(), ")");
}
}
}
diff --git a/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyClassFixer.java b/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyClassFixer.java
index b9846e1cb894..ce51d6d46824 100644
--- a/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyClassFixer.java
+++ b/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyClassFixer.java
@@ -17,7 +17,6 @@ package com.jetbrains.python.codeInsight.editorActions.smartEnter.fixers;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.PsiElement;
-import com.intellij.psi.tree.TokenSet;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.IncorrectOperationException;
import com.jetbrains.python.PyTokenTypes;
@@ -25,6 +24,9 @@ import com.jetbrains.python.codeInsight.editorActions.smartEnter.PySmartEnterPro
import com.jetbrains.python.psi.PyArgumentList;
import com.jetbrains.python.psi.PyClass;
import com.jetbrains.python.psi.PyUtil;
+import org.jetbrains.annotations.NotNull;
+
+import static com.jetbrains.python.psi.PyUtil.sure;
/**
* Created by IntelliJ IDEA.
@@ -32,21 +34,22 @@ import com.jetbrains.python.psi.PyUtil;
* Date: 16.04.2010
* Time: 18:41:08
*/
-public class PyClassFixer implements PyFixer {
- public void apply(Editor editor, PySmartEnterProcessor processor, PsiElement psiElement) throws IncorrectOperationException {
- if (psiElement instanceof PyClass) {
- final PsiElement colon = PyUtil.getChildByFilter(psiElement, TokenSet.create(PyTokenTypes.COLON), 0);
- if (colon == null) {
- final PyClass aClass = (PyClass)psiElement;
- final PyArgumentList argList = PsiTreeUtil.getChildOfType(aClass, PyArgumentList.class);
- int offset = argList.getTextRange().getEndOffset();
- String textToInsert = ":";
- if (aClass.getNameNode() == null) {
- processor.registerUnresolvedError(argList.getTextRange().getEndOffset() + 1);
- textToInsert = " :";
- }
- editor.getDocument().insertString(offset, textToInsert);
+public class PyClassFixer extends PyFixer<PyClass> {
+ public PyClassFixer() {
+ super(PyClass.class);
+ }
+
+ public void doApply(@NotNull Editor editor, @NotNull PySmartEnterProcessor processor, @NotNull PyClass pyClass) throws IncorrectOperationException {
+ final PsiElement colon = PyUtil.getFirstChildOfType(pyClass, PyTokenTypes.COLON);
+ if (colon == null) {
+ final PyArgumentList argList = PsiTreeUtil.getChildOfType(pyClass, PyArgumentList.class);
+ final int offset = sure(argList).getTextRange().getEndOffset();
+ String textToInsert = ":";
+ if (pyClass.getNameNode() == null) {
+ processor.registerUnresolvedError(argList.getTextRange().getEndOffset() + 1);
+ textToInsert = " :";
}
+ editor.getDocument().insertString(offset, textToInsert);
}
}
}
diff --git a/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyConditionalStatementPartFixer.java b/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyConditionalStatementPartFixer.java
index 2885c63f6345..f71c61261103 100644
--- a/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyConditionalStatementPartFixer.java
+++ b/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyConditionalStatementPartFixer.java
@@ -25,6 +25,9 @@ import com.jetbrains.python.codeInsight.editorActions.smartEnter.PySmartEnterPro
import com.jetbrains.python.psi.PyConditionalStatementPart;
import com.jetbrains.python.psi.PyExpression;
import com.jetbrains.python.psi.PyUtil;
+import org.jetbrains.annotations.NotNull;
+
+import static com.jetbrains.python.psi.PyUtil.sure;
/**
* Created by IntelliJ IDEA.
@@ -32,31 +35,35 @@ import com.jetbrains.python.psi.PyUtil;
* Date: 15.04.2010
* Time: 19:33:14
*/
-public class PyConditionalStatementPartFixer implements PyFixer {
- public void apply(Editor editor, PySmartEnterProcessor processor, PsiElement psiElement) throws IncorrectOperationException {
- if (psiElement instanceof PyConditionalStatementPart) {
- final PyConditionalStatementPart conditionalStatementPart = (PyConditionalStatementPart)psiElement;
- final PyExpression condition = conditionalStatementPart.getCondition();
- final Document document = editor.getDocument();
- final PsiElement colon = PyUtil.getChildByFilter(conditionalStatementPart, TokenSet.create(PyTokenTypes.COLON), 0);
- if (colon == null) {
- if (condition != null) {
- final PsiElement firstNonComment = PyUtil.getFirstNonCommentAfter(condition.getNextSibling());
- if (firstNonComment != null && !":".equals(firstNonComment.getNode().getText())) {
- document.insertString(firstNonComment.getTextRange().getEndOffset(), ":");
- }
- }
- else {
- final PsiElement keywordToken = PyUtil.getChildByFilter(conditionalStatementPart,
- TokenSet.create(PyTokenTypes.IF_KEYWORD, PyTokenTypes.ELIF_KEYWORD,
- PyTokenTypes.WHILE_KEYWORD), 0);
- final int offset = keywordToken.getTextRange().getEndOffset();
- document.insertString(offset, " :");
- processor.registerUnresolvedError(offset + 1);
+public class PyConditionalStatementPartFixer extends PyFixer<PyConditionalStatementPart> {
+ public PyConditionalStatementPartFixer() {
+ super(PyConditionalStatementPart.class);
+ }
+
+ @Override
+ public void doApply(@NotNull Editor editor, @NotNull PySmartEnterProcessor processor, @NotNull PyConditionalStatementPart statementPart)
+ throws IncorrectOperationException {
+ final PyExpression condition = statementPart.getCondition();
+ final Document document = editor.getDocument();
+ final PsiElement colon = PyUtil.getFirstChildOfType(statementPart, PyTokenTypes.COLON);
+ if (colon == null) {
+ if (condition != null) {
+ final PsiElement firstNonComment = PyUtil.getFirstNonCommentAfter(condition.getNextSibling());
+ if (firstNonComment != null && !":".equals(firstNonComment.getNode().getText())) {
+ document.insertString(firstNonComment.getTextRange().getEndOffset(), ":");
}
- } else if (condition == null) {
- processor.registerUnresolvedError(colon.getTextRange().getStartOffset());
}
+ else {
+ final TokenSet keywords = TokenSet.create(PyTokenTypes.IF_KEYWORD, PyTokenTypes.ELIF_KEYWORD, PyTokenTypes.WHILE_KEYWORD);
+ final PsiElement keywordToken = PyUtil.getChildByFilter(statementPart,
+ keywords, 0);
+ final int offset = sure(keywordToken).getTextRange().getEndOffset();
+ document.insertString(offset, " :");
+ processor.registerUnresolvedError(offset + 1);
+ }
+ }
+ else if (condition == null) {
+ processor.registerUnresolvedError(colon.getTextRange().getStartOffset());
}
}
}
diff --git a/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyExceptFixer.java b/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyExceptFixer.java
index 8e3532cb49b3..67d5ff5c9ee5 100644
--- a/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyExceptFixer.java
+++ b/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyExceptFixer.java
@@ -17,13 +17,15 @@ package com.jetbrains.python.codeInsight.editorActions.smartEnter.fixers;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.PsiElement;
-import com.intellij.psi.tree.TokenSet;
import com.intellij.util.IncorrectOperationException;
import com.jetbrains.python.PyTokenTypes;
import com.jetbrains.python.codeInsight.editorActions.smartEnter.PySmartEnterProcessor;
import com.jetbrains.python.psi.PyExceptPart;
import com.jetbrains.python.psi.PyExpression;
import com.jetbrains.python.psi.PyUtil;
+import org.jetbrains.annotations.NotNull;
+
+import static com.jetbrains.python.psi.PyUtil.sure;
/**
* Created by IntelliJ IDEA.
@@ -31,24 +33,26 @@ import com.jetbrains.python.psi.PyUtil;
* Date: 22.04.2010
* Time: 18:13:34
*/
-public class PyExceptFixer implements PyFixer {
- public void apply(Editor editor, PySmartEnterProcessor processor, PsiElement psiElement) throws IncorrectOperationException {
- if (psiElement instanceof PyExceptPart) {
- PyExceptPart exceptPart = (PyExceptPart)psiElement;
- final PsiElement colon = PyUtil.getChildByFilter(exceptPart, TokenSet.create(PyTokenTypes.COLON), 0);
- if (colon == null) {
- int offset = PyUtil.getChildByFilter(exceptPart,
- TokenSet.create(PyTokenTypes.EXCEPT_KEYWORD), 0).getTextRange().getEndOffset();
- final PyExpression exceptClass = exceptPart.getExceptClass();
- if (exceptClass != null) {
- offset = exceptClass.getTextRange().getEndOffset();
- }
- final PyExpression target = exceptPart.getTarget();
- if (target != null) {
- offset = target.getTextRange().getEndOffset();
- }
- editor.getDocument().insertString(offset, ":");
+public class PyExceptFixer extends PyFixer<PyExceptPart> {
+ public PyExceptFixer() {
+ super(PyExceptPart.class);
+ }
+
+ @Override
+ public void doApply(@NotNull Editor editor, @NotNull PySmartEnterProcessor processor, @NotNull PyExceptPart exceptPart) throws IncorrectOperationException {
+ final PsiElement colon = PyUtil.getFirstChildOfType(exceptPart, PyTokenTypes.COLON);
+ if (colon == null) {
+ final PsiElement exceptToken = PyUtil.getFirstChildOfType(exceptPart, PyTokenTypes.EXCEPT_KEYWORD);
+ int offset = sure(exceptToken).getTextRange().getEndOffset();
+ final PyExpression exceptClass = exceptPart.getExceptClass();
+ if (exceptClass != null) {
+ offset = exceptClass.getTextRange().getEndOffset();
+ }
+ final PyExpression target = exceptPart.getTarget();
+ if (target != null) {
+ offset = target.getTextRange().getEndOffset();
}
+ editor.getDocument().insertString(offset, ":");
}
}
}
diff --git a/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyFixer.java b/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyFixer.java
index f02831d92d36..0ebdceb20e37 100644
--- a/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyFixer.java
+++ b/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyFixer.java
@@ -19,6 +19,8 @@ import com.intellij.openapi.editor.Editor;
import com.intellij.psi.PsiElement;
import com.intellij.util.IncorrectOperationException;
import com.jetbrains.python.codeInsight.editorActions.smartEnter.PySmartEnterProcessor;
+import com.jetbrains.python.psi.PyElement;
+import org.jetbrains.annotations.NotNull;
/**
* Created by IntelliJ IDEA.
@@ -26,6 +28,20 @@ import com.jetbrains.python.codeInsight.editorActions.smartEnter.PySmartEnterPro
* Date: 15.04.2010
* Time: 17:10:33
*/
-public interface PyFixer {
- void apply(Editor editor, PySmartEnterProcessor processor, PsiElement psiElement) throws IncorrectOperationException;
+public abstract class PyFixer<T extends PyElement> {
+ private final Class<T> myClass;
+
+ public PyFixer(@NotNull Class<T> aClass) {
+ myClass = aClass;
+ }
+
+ public final void apply(@NotNull Editor editor, @NotNull PySmartEnterProcessor processor, @NotNull PsiElement element)
+ throws IncorrectOperationException {
+ if (myClass.isInstance(element)) {
+ //noinspection unchecked
+ doApply(editor, processor, (T)element);
+ }
+ }
+
+ public abstract void doApply(@NotNull Editor editor, @NotNull PySmartEnterProcessor processor, @NotNull T element);
}
diff --git a/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyForPartFixer.java b/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyForPartFixer.java
index 1b8b438c68fd..eefe5cd1fc3e 100644
--- a/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyForPartFixer.java
+++ b/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyForPartFixer.java
@@ -18,12 +18,13 @@ package com.jetbrains.python.codeInsight.editorActions.smartEnter.fixers;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.PsiElement;
-import com.intellij.psi.tree.TokenSet;
-import com.intellij.util.IncorrectOperationException;
import com.jetbrains.python.PyTokenTypes;
import com.jetbrains.python.codeInsight.editorActions.smartEnter.PySmartEnterProcessor;
import com.jetbrains.python.psi.PyForPart;
import com.jetbrains.python.psi.PyUtil;
+import org.jetbrains.annotations.NotNull;
+
+import static com.jetbrains.python.psi.PyUtil.sure;
/**
* Created by IntelliJ IDEA.
@@ -31,41 +32,42 @@ import com.jetbrains.python.psi.PyUtil;
* Date: 16.04.2010
* Time: 16:03:43
*/
-public class PyForPartFixer implements PyFixer {
- public void apply(Editor editor, PySmartEnterProcessor processor, PsiElement psiElement) throws IncorrectOperationException {
- if (psiElement instanceof PyForPart) {
- final PyForPart forPart = (PyForPart)psiElement;
- final PsiElement colon = PyUtil.getChildByFilter(psiElement, TokenSet.create(PyTokenTypes.COLON), 0);
- final Document document = editor.getDocument();
- final PsiElement forToken = PyUtil.getChildByFilter(forPart,
- TokenSet.create(PyTokenTypes.FOR_KEYWORD), 0);
- if (colon == null) {
- String textToInsert = ":";
- PsiElement sourceOrTarget = forPart.getSource();
- PsiElement positionToInsert = sourceOrTarget;
- if (sourceOrTarget == null) {
- sourceOrTarget = forPart.getTarget();
- final PsiElement inToken = PyUtil.getChildByFilter(forPart, TokenSet.create(PyTokenTypes.IN_KEYWORD), 0);
- if (inToken == null) {
- if (sourceOrTarget == null) {
- positionToInsert = forToken;
- textToInsert = " in :";
- processor.registerUnresolvedError(positionToInsert.getTextRange().getEndOffset() + 1);
- }
- else {
- positionToInsert = sourceOrTarget;
- textToInsert = " in :";
- processor.registerUnresolvedError(positionToInsert.getTextRange().getEndOffset() + 4);
- }
+public class PyForPartFixer extends PyFixer<PyForPart> {
+ public PyForPartFixer() {
+ super(PyForPart.class);
+ }
+
+ @Override
+ public void doApply(@NotNull Editor editor, @NotNull PySmartEnterProcessor processor, @NotNull PyForPart forPart) {
+ final PsiElement colon = PyUtil.getFirstChildOfType(forPart, PyTokenTypes.COLON);
+ final Document document = editor.getDocument();
+ final PsiElement forToken = PyUtil.getFirstChildOfType(forPart, PyTokenTypes.FOR_KEYWORD);
+ if (colon == null) {
+ String textToInsert = ":";
+ PsiElement sourceOrTarget = forPart.getSource();
+ PsiElement positionToInsert = sourceOrTarget;
+ if (sourceOrTarget == null) {
+ sourceOrTarget = forPart.getTarget();
+ final PsiElement inToken = PyUtil.getFirstChildOfType(forPart, PyTokenTypes.IN_KEYWORD);
+ if (inToken == null) {
+ if (sourceOrTarget == null) {
+ positionToInsert = sure(forToken);
+ textToInsert = " in :";
+ processor.registerUnresolvedError(positionToInsert.getTextRange().getEndOffset() + 1);
}
else {
- positionToInsert = inToken;
- textToInsert = " :";
- processor.registerUnresolvedError(positionToInsert.getTextRange().getEndOffset() + 1);
+ positionToInsert = sourceOrTarget;
+ textToInsert = " in :";
+ processor.registerUnresolvedError(positionToInsert.getTextRange().getEndOffset() + 4);
}
}
- document.insertString(positionToInsert.getTextRange().getEndOffset(), textToInsert);
+ else {
+ positionToInsert = inToken;
+ textToInsert = " :";
+ processor.registerUnresolvedError(positionToInsert.getTextRange().getEndOffset() + 1);
+ }
}
+ document.insertString(positionToInsert.getTextRange().getEndOffset(), textToInsert);
}
}
}
diff --git a/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyFunctionFixer.java b/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyFunctionFixer.java
index f1988e13f57a..ad959f80ccae 100644
--- a/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyFunctionFixer.java
+++ b/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyFunctionFixer.java
@@ -18,13 +18,13 @@ package com.jetbrains.python.codeInsight.editorActions.smartEnter.fixers;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.PsiElement;
-import com.intellij.psi.tree.TokenSet;
import com.intellij.util.IncorrectOperationException;
import com.jetbrains.python.PyTokenTypes;
import com.jetbrains.python.codeInsight.editorActions.smartEnter.PySmartEnterProcessor;
import com.jetbrains.python.psi.PyFunction;
import com.jetbrains.python.psi.PyParameterList;
import com.jetbrains.python.psi.PyUtil;
+import org.jetbrains.annotations.NotNull;
/**
* Created by IntelliJ IDEA.
@@ -32,16 +32,19 @@ import com.jetbrains.python.psi.PyUtil;
* Date: 16.04.2010
* Time: 16:59:07
*/
-public class PyFunctionFixer implements PyFixer {
- public void apply(Editor editor, PySmartEnterProcessor processor, PsiElement psiElement) throws IncorrectOperationException {
- if (psiElement instanceof PyFunction) {
- final PsiElement colon = PyUtil.getChildByFilter(psiElement, TokenSet.create(PyTokenTypes.COLON), 0);
- if (colon == null) {
- final PyFunction function = (PyFunction)psiElement;
- final PyParameterList parameterList = function.getParameterList();
- final Document document = editor.getDocument();
- document.insertString(parameterList.getTextRange().getEndOffset(), ":");
- }
+public class PyFunctionFixer extends PyFixer<PyFunction> {
+ public PyFunctionFixer() {
+ super(PyFunction.class);
+ }
+
+ @Override
+ public void doApply(@NotNull Editor editor, @NotNull PySmartEnterProcessor processor, @NotNull PyFunction function)
+ throws IncorrectOperationException {
+ final PsiElement colon = PyUtil.getFirstChildOfType(function, PyTokenTypes.COLON);
+ if (colon == null) {
+ final PyParameterList parameterList = function.getParameterList();
+ final Document document = editor.getDocument();
+ document.insertString(parameterList.getTextRange().getEndOffset(), ":");
}
}
}
diff --git a/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyMissingBracesFixer.java b/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyMissingBracesFixer.java
index 0d8565d6960d..eb97d2bdb372 100644
--- a/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyMissingBracesFixer.java
+++ b/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyMissingBracesFixer.java
@@ -20,6 +20,7 @@ import com.intellij.psi.PsiElement;
import com.intellij.util.IncorrectOperationException;
import com.jetbrains.python.codeInsight.editorActions.smartEnter.PySmartEnterProcessor;
import com.jetbrains.python.psi.*;
+import org.jetbrains.annotations.NotNull;
/**
* Created by IntelliJ IDEA.
@@ -27,10 +28,16 @@ import com.jetbrains.python.psi.*;
* Date: 15.04.2010
* Time: 17:55:46
*/
-public class PyMissingBracesFixer implements PyFixer {
- public void apply(Editor editor, PySmartEnterProcessor processor, PsiElement psiElement) throws IncorrectOperationException {
+public class PyMissingBracesFixer extends PyFixer<PyElement> {
+ public PyMissingBracesFixer() {
+ super(PyElement.class);
+ }
+
+ @Override
+ public void doApply(@NotNull Editor editor, @NotNull PySmartEnterProcessor processor, @NotNull PyElement psiElement)
+ throws IncorrectOperationException {
if (psiElement instanceof PySetLiteralExpression || psiElement instanceof PyDictLiteralExpression) {
- PsiElement lastChild = PyUtil.getFirstNonCommentBefore(psiElement.getLastChild());
+ final PsiElement lastChild = PyUtil.getFirstNonCommentBefore(psiElement.getLastChild());
if (lastChild != null && !"}".equals(lastChild.getText())) {
editor.getDocument().insertString(lastChild.getTextRange().getEndOffset(), "}");
}
@@ -38,7 +45,7 @@ public class PyMissingBracesFixer implements PyFixer {
else if (psiElement instanceof PyListLiteralExpression ||
psiElement instanceof PySliceExpression ||
psiElement instanceof PySubscriptionExpression) {
- PsiElement lastChild = PyUtil.getFirstNonCommentBefore(psiElement.getLastChild());
+ final PsiElement lastChild = PyUtil.getFirstNonCommentBefore(psiElement.getLastChild());
if (lastChild != null && !"]".equals(lastChild.getText())) {
editor.getDocument().insertString(lastChild.getTextRange().getEndOffset(), "]");
}
diff --git a/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyParameterListFixer.java b/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyParameterListFixer.java
index 7b5970d7baf2..69a06b8b7c48 100644
--- a/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyParameterListFixer.java
+++ b/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyParameterListFixer.java
@@ -23,6 +23,7 @@ import com.jetbrains.python.PyTokenTypes;
import com.jetbrains.python.codeInsight.editorActions.smartEnter.PySmartEnterProcessor;
import com.jetbrains.python.psi.PyParameterList;
import com.jetbrains.python.psi.PyUtil;
+import org.jetbrains.annotations.NotNull;
/**
* Created by IntelliJ IDEA.
@@ -30,19 +31,22 @@ import com.jetbrains.python.psi.PyUtil;
* Date: 16.04.2010
* Time: 17:25:46
*/
-public class PyParameterListFixer implements PyFixer {
- public void apply(Editor editor, PySmartEnterProcessor processor, PsiElement psiElement) throws IncorrectOperationException {
- if (psiElement instanceof PyParameterList) {
- final PsiElement lBrace = PyUtil.getChildByFilter(psiElement, PyTokenTypes.OPEN_BRACES, 0);
- final PsiElement rBrace = PyUtil.getChildByFilter(psiElement, PyTokenTypes.CLOSE_BRACES, 0);
- if (lBrace == null || rBrace == null) {
- final Document document = editor.getDocument();
- if (lBrace == null) {
- document.insertString(psiElement.getTextRange().getStartOffset(), "(");
- }
- else {
- document.insertString(psiElement.getTextRange().getEndOffset(), ")");
- }
+public class PyParameterListFixer extends PyFixer<PyParameterList> {
+ public PyParameterListFixer() {
+ super(PyParameterList.class);
+ }
+
+ @Override
+ public void doApply(@NotNull Editor editor, @NotNull PySmartEnterProcessor processor, @NotNull PyParameterList psiElement) throws IncorrectOperationException {
+ final PsiElement lBrace = PyUtil.getChildByFilter(psiElement, PyTokenTypes.OPEN_BRACES, 0);
+ final PsiElement rBrace = PyUtil.getChildByFilter(psiElement, PyTokenTypes.CLOSE_BRACES, 0);
+ if (lBrace == null || rBrace == null) {
+ final Document document = editor.getDocument();
+ if (lBrace == null) {
+ document.insertString(psiElement.getTextRange().getStartOffset(), "(");
+ }
+ else {
+ document.insertString(psiElement.getTextRange().getEndOffset(), ")");
}
}
}
diff --git a/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyParenthesizedFixer.java b/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyParenthesizedFixer.java
index a055375bc040..582e034c5603 100644
--- a/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyParenthesizedFixer.java
+++ b/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyParenthesizedFixer.java
@@ -20,6 +20,7 @@ import com.intellij.psi.PsiElement;
import com.intellij.util.IncorrectOperationException;
import com.jetbrains.python.codeInsight.editorActions.smartEnter.PySmartEnterProcessor;
import com.jetbrains.python.psi.PyParenthesizedExpression;
+import org.jetbrains.annotations.NotNull;
/**
* Created by IntelliJ IDEA.
@@ -27,13 +28,17 @@ import com.jetbrains.python.psi.PyParenthesizedExpression;
* Date: 15.04.2010
* Time: 17:42:08
*/
-public class PyParenthesizedFixer implements PyFixer {
- public void apply(final Editor editor, final PySmartEnterProcessor processor, final PsiElement psiElement) throws IncorrectOperationException {
- if (psiElement instanceof PyParenthesizedExpression) {
- final PsiElement lastChild = psiElement.getLastChild();
- if (lastChild != null && !")".equals(lastChild.getText())) {
- editor.getDocument().insertString(psiElement.getTextRange().getEndOffset(), ")");
- }
+public class PyParenthesizedFixer extends PyFixer<PyParenthesizedExpression> {
+ public PyParenthesizedFixer() {
+ super(PyParenthesizedExpression.class);
+ }
+
+ @Override
+ public void doApply(@NotNull Editor editor, @NotNull PySmartEnterProcessor processor, @NotNull PyParenthesizedExpression expression)
+ throws IncorrectOperationException {
+ final PsiElement lastChild = expression.getLastChild();
+ if (lastChild != null && !")".equals(lastChild.getText())) {
+ editor.getDocument().insertString(expression.getTextRange().getEndOffset(), ")");
}
}
}
diff --git a/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyStringLiteralFixer.java b/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyStringLiteralFixer.java
index 906aecd9c757..3e9925a86131 100644
--- a/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyStringLiteralFixer.java
+++ b/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyStringLiteralFixer.java
@@ -17,10 +17,10 @@ package com.jetbrains.python.codeInsight.editorActions.smartEnter.fixers;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.util.text.StringUtil;
-import com.intellij.psi.PsiElement;
import com.intellij.util.IncorrectOperationException;
import com.jetbrains.python.codeInsight.editorActions.smartEnter.PySmartEnterProcessor;
import com.jetbrains.python.psi.PyStringLiteralExpression;
+import org.jetbrains.annotations.NotNull;
/**
* Created by IntelliJ IDEA.
@@ -28,31 +28,35 @@ import com.jetbrains.python.psi.PyStringLiteralExpression;
* Date: 15.04.2010
* Time: 17:17:14
*/
-public class PyStringLiteralFixer implements PyFixer {
- public void apply(Editor editor, PySmartEnterProcessor processor, PsiElement psiElement) throws IncorrectOperationException {
- if (psiElement instanceof PyStringLiteralExpression) {
- final String text = psiElement.getText();
- if (StringUtil.startsWith(text, "\"\"\"")) {
- final int suffixLength = StringUtil.commonSuffixLength(text, "\"\"\"");
- if (suffixLength != 3) {
- editor.getDocument().insertString(psiElement.getTextRange().getEndOffset(), "\"\"\"".substring(suffixLength));
- }
+public class PyStringLiteralFixer extends PyFixer<PyStringLiteralExpression> {
+ public PyStringLiteralFixer() {
+ super(PyStringLiteralExpression.class);
+ }
+
+ @Override
+ public void doApply(@NotNull Editor editor, @NotNull PySmartEnterProcessor processor, @NotNull PyStringLiteralExpression psiElement)
+ throws IncorrectOperationException {
+ final String text = psiElement.getText();
+ if (StringUtil.startsWith(text, "\"\"\"")) {
+ final int suffixLength = StringUtil.commonSuffixLength(text, "\"\"\"");
+ if (suffixLength != 3) {
+ editor.getDocument().insertString(psiElement.getTextRange().getEndOffset(), "\"\"\"".substring(suffixLength));
}
- else if (StringUtil.startsWith(text, "\'\'\'")) {
- final int suffixLength = StringUtil.commonSuffixLength(text, "\'\'\'");
- if (suffixLength != 3) {
- editor.getDocument().insertString(psiElement.getTextRange().getEndOffset(), "\'\'\'".substring(suffixLength));
- }
+ }
+ else if (StringUtil.startsWith(text, "\'\'\'")) {
+ final int suffixLength = StringUtil.commonSuffixLength(text, "\'\'\'");
+ if (suffixLength != 3) {
+ editor.getDocument().insertString(psiElement.getTextRange().getEndOffset(), "\'\'\'".substring(suffixLength));
}
- else if (StringUtil.startsWith(text, "\"")) {
- if (!StringUtil.endsWith(text, "\"")) {
- editor.getDocument().insertString(psiElement.getTextRange().getEndOffset(), "\"");
- }
+ }
+ else if (StringUtil.startsWith(text, "\"")) {
+ if (!StringUtil.endsWith(text, "\"")) {
+ editor.getDocument().insertString(psiElement.getTextRange().getEndOffset(), "\"");
}
- else if (StringUtil.startsWith(text, "\'")) {
- if (!StringUtil.endsWith(text, "\'")) {
- editor.getDocument().insertString(psiElement.getTextRange().getEndOffset(), "\'");
- }
+ }
+ else if (StringUtil.startsWith(text, "\'")) {
+ if (!StringUtil.endsWith(text, "\'")) {
+ editor.getDocument().insertString(psiElement.getTextRange().getEndOffset(), "\'");
}
}
}
diff --git a/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyUnconditionalStatementPartFixer.java b/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyUnconditionalStatementPartFixer.java
index 57bfd39f4f85..6aace3972a1b 100644
--- a/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyUnconditionalStatementPartFixer.java
+++ b/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyUnconditionalStatementPartFixer.java
@@ -21,10 +21,10 @@ import com.intellij.psi.tree.TokenSet;
import com.intellij.util.IncorrectOperationException;
import com.jetbrains.python.PyTokenTypes;
import com.jetbrains.python.codeInsight.editorActions.smartEnter.PySmartEnterProcessor;
-import com.jetbrains.python.psi.PyElsePart;
-import com.jetbrains.python.psi.PyFinallyPart;
-import com.jetbrains.python.psi.PyTryPart;
-import com.jetbrains.python.psi.PyUtil;
+import com.jetbrains.python.psi.*;
+import org.jetbrains.annotations.NotNull;
+
+import static com.jetbrains.python.psi.PyUtil.sure;
/**
* Created by IntelliJ IDEA.
@@ -32,16 +32,20 @@ import com.jetbrains.python.psi.PyUtil;
* Date: 16.04.2010
* Time: 14:25:20
*/
-public class PyUnconditionalStatementPartFixer implements PyFixer {
- public void apply(Editor editor, PySmartEnterProcessor processor, PsiElement psiElement) throws IncorrectOperationException {
+public class PyUnconditionalStatementPartFixer extends PyFixer<PyElement> {
+ public PyUnconditionalStatementPartFixer() {
+ super(PyElement.class);
+ }
+
+ @Override
+ public void doApply(@NotNull Editor editor, @NotNull PySmartEnterProcessor processor, @NotNull PyElement psiElement)
+ throws IncorrectOperationException {
if (PyUtil.instanceOf(psiElement, PyElsePart.class, PyTryPart.class, PyFinallyPart.class)) {
- final PsiElement colon = PyUtil.getChildByFilter(psiElement, TokenSet.create(PyTokenTypes.COLON), 0);
+ final PsiElement colon = PyUtil.getFirstChildOfType(psiElement, PyTokenTypes.COLON);
if (colon == null) {
- final PsiElement keywordToken = PyUtil.getChildByFilter(psiElement,
- TokenSet.create(PyTokenTypes.ELSE_KEYWORD, PyTokenTypes.TRY_KEYWORD,
- PyTokenTypes.FINALLY_KEYWORD),
- 0);
- editor.getDocument().insertString(keywordToken.getTextRange().getEndOffset(), ":");
+ final TokenSet keywords = TokenSet.create(PyTokenTypes.ELSE_KEYWORD, PyTokenTypes.TRY_KEYWORD, PyTokenTypes.FINALLY_KEYWORD);
+ final PsiElement keywordToken = PyUtil.getChildByFilter(psiElement, keywords, 0);
+ editor.getDocument().insertString(sure(keywordToken).getTextRange().getEndOffset(), ":");
}
}
}
diff --git a/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyWithFixer.java b/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyWithFixer.java
index b6d099ef7cb5..ec236d9242f5 100644
--- a/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyWithFixer.java
+++ b/python/src/com/jetbrains/python/codeInsight/editorActions/smartEnter/fixers/PyWithFixer.java
@@ -15,65 +15,59 @@
*/
package com.jetbrains.python.codeInsight.editorActions.smartEnter.fixers;
-import com.intellij.lang.ASTNode;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.psi.PsiElement;
import com.intellij.util.IncorrectOperationException;
import com.jetbrains.python.PyTokenTypes;
import com.jetbrains.python.codeInsight.editorActions.smartEnter.PySmartEnterProcessor;
-import com.jetbrains.python.psi.PyElementType;
import com.jetbrains.python.psi.PyExpression;
+import com.jetbrains.python.psi.PyUtil;
import com.jetbrains.python.psi.PyWithItem;
import com.jetbrains.python.psi.PyWithStatement;
import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
import static com.jetbrains.python.psi.PyUtil.sure;
/**
* @author Mikhail Golubev
*/
-public class PyWithFixer implements PyFixer {
- public void apply(Editor editor, PySmartEnterProcessor processor, PsiElement psiElement) throws IncorrectOperationException {
- if (psiElement instanceof PyWithStatement) {
- final PyWithStatement withStatement = (PyWithStatement)psiElement;
- final PsiElement colonToken = getFirstChildOfType(psiElement, PyTokenTypes.COLON);
- final PsiElement withToken = getFirstChildOfType(withStatement, PyTokenTypes.WITH_KEYWORD);
- final Document document = editor.getDocument();
- if (colonToken == null) {
- int insertAt = sure(withToken).getTextRange().getEndOffset();
- String textToInsert = ":";
- final PyWithItem[] withItems = withStatement.getWithItems();
- final PyWithItem lastItem = withItems.length != 0 ? withItems[withItems.length - 1] : null;
- if (lastItem == null || lastItem.getExpression() == null) {
- textToInsert = " :";
- processor.registerUnresolvedError(insertAt + 1);
- }
- else {
- final PyExpression expression = lastItem.getExpression();
- insertAt = expression.getTextRange().getEndOffset();
- final PsiElement asToken = getFirstChildOfType(lastItem, PyTokenTypes.AS_KEYWORD);
- if (asToken != null) {
- insertAt = asToken.getTextRange().getEndOffset();
- final PyExpression target = lastItem.getTarget();
- if (target != null) {
- insertAt = target.getTextRange().getEndOffset();
- }
- else {
- textToInsert = " :";
- processor.registerUnresolvedError(insertAt + 1);
- }
+public class PyWithFixer extends PyFixer<PyWithStatement> {
+ public PyWithFixer() {
+ super(PyWithStatement.class);
+ }
+
+ @Override
+ public void doApply(@NotNull Editor editor, @NotNull PySmartEnterProcessor processor, @NotNull PyWithStatement withStatement) throws IncorrectOperationException {
+ final PsiElement colonToken = PyUtil.getFirstChildOfType(withStatement, PyTokenTypes.COLON);
+ final PsiElement withToken = PyUtil.getFirstChildOfType(withStatement, PyTokenTypes.WITH_KEYWORD);
+ final Document document = editor.getDocument();
+ if (colonToken == null) {
+ int insertAt = sure(withToken).getTextRange().getEndOffset();
+ String textToInsert = ":";
+ final PyWithItem[] withItems = withStatement.getWithItems();
+ final PyWithItem lastItem = withItems.length != 0 ? withItems[withItems.length - 1] : null;
+ if (lastItem == null || lastItem.getExpression() == null) {
+ textToInsert = " :";
+ processor.registerUnresolvedError(insertAt + 1);
+ }
+ else {
+ final PyExpression expression = lastItem.getExpression();
+ insertAt = expression.getTextRange().getEndOffset();
+ final PsiElement asToken = PyUtil.getFirstChildOfType(lastItem, PyTokenTypes.AS_KEYWORD);
+ if (asToken != null) {
+ insertAt = asToken.getTextRange().getEndOffset();
+ final PyExpression target = lastItem.getTarget();
+ if (target != null) {
+ insertAt = target.getTextRange().getEndOffset();
+ }
+ else {
+ textToInsert = " :";
+ processor.registerUnresolvedError(insertAt + 1);
}
}
- document.insertString(insertAt, textToInsert);
}
+ document.insertString(insertAt, textToInsert);
}
}
-
- @Nullable
- private static PsiElement getFirstChildOfType(@NotNull final PsiElement element, @NotNull PyElementType type) {
- final ASTNode child = element.getNode().findChildByType(type);
- return child != null ? child.getPsi() : null;
- }
}
diff --git a/python/src/com/jetbrains/python/console/PydevConsoleRunner.java b/python/src/com/jetbrains/python/console/PydevConsoleRunner.java
index 3d77ac4f62b1..848818b95e27 100644
--- a/python/src/com/jetbrains/python/console/PydevConsoleRunner.java
+++ b/python/src/com/jetbrains/python/console/PydevConsoleRunner.java
@@ -16,6 +16,10 @@
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.collect.Maps;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.ExecutionHelper;
@@ -59,11 +63,14 @@ 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;
@@ -132,6 +139,9 @@ 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,
@NotNull Sdk sdk, @NotNull final PyConsoleType consoleType,
@@ -192,8 +202,10 @@ public class PydevConsoleRunner extends AbstractConsoleRunnerWithHistory<PythonC
@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;
@@ -481,6 +493,20 @@ public class PydevConsoleRunner extends AbstractConsoleRunnerWithHistory<PythonC
}
}
+ @Override
+ protected String constructConsoleTitle(@NotNull String consoleTitle) {
+ if (myConsoleTitle == null) {
+ myConsoleTitle = super.constructConsoleTitle(consoleTitle);
+ }
+ 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);
}
@@ -583,9 +609,32 @@ public class PydevConsoleRunner extends AbstractConsoleRunnerWithHistory<PythonC
}
@Override
- protected AnAction createCloseAction(Executor defaultExecutor, RunContentDescriptor myDescriptor) {
- final AnAction generalCloseAction = super.createCloseAction(defaultExecutor, myDescriptor);
- return createConsoleStoppingAction(generalCloseAction);
+ protected AnAction createCloseAction(Executor defaultExecutor, final RunContentDescriptor descriptor) {
+ final AnAction generalCloseAction = super.createCloseAction(defaultExecutor, descriptor);
+
+ final AnAction stopAction = new DumbAwareAction() {
+ @Override
+ public void update(AnActionEvent e) {
+ generalCloseAction.update(e);
+ }
+
+ @Override
+ public void actionPerformed(AnActionEvent e) {
+ e = stopConsole(e);
+
+ clearContent(descriptor);
+
+ generalCloseAction.actionPerformed(e);
+ }
+ };
+ stopAction.copyFrom(generalCloseAction);
+ return stopAction;
+ }
+
+ private void clearContent(RunContentDescriptor descriptor) {
+ Content content = getToolWindow().getContentManager().findContent(descriptor.getDisplayName());
+ assert content != null;
+ getToolWindow().getContentManager().removeContent(content, true);
}
private AnAction createConsoleStoppingAction(final AnAction generalStopAction) {
@@ -597,26 +646,31 @@ public class PydevConsoleRunner extends AbstractConsoleRunnerWithHistory<PythonC
@Override
public void actionPerformed(AnActionEvent e) {
- if (myPydevConsoleCommunication != null) {
- final AnActionEvent furtherActionEvent =
- new AnActionEvent(e.getInputEvent(), e.getDataContext(), e.getPlace(),
- e.getPresentation(), e.getActionManager(), e.getModifiers());
- try {
- closeCommunication();
- // waiting for REPL communication before destroying process handler
- Thread.sleep(300);
- }
- catch (Exception ignored) {
- // Ignore
- }
- generalStopAction.actionPerformed(furtherActionEvent);
- }
+ e = stopConsole(e);
+
+ generalStopAction.actionPerformed(e);
}
};
stopAction.copyFrom(generalStopAction);
return stopAction;
}
+ private AnActionEvent stopConsole(AnActionEvent e) {
+ if (myPydevConsoleCommunication != null) {
+ e = new AnActionEvent(e.getInputEvent(), e.getDataContext(), e.getPlace(),
+ e.getPresentation(), e.getActionManager(), e.getModifiers());
+ try {
+ closeCommunication();
+ // waiting for REPL communication before destroying process handler
+ Thread.sleep(300);
+ }
+ catch (Exception ignored) {
+ // Ignore
+ }
+ }
+ return e;
+ }
+
protected AnAction createSplitLineAction() {
class ConsoleSplitLineAction extends EditorAction {
@@ -738,6 +792,17 @@ 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);
}
@@ -889,4 +954,21 @@ 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();
+ }
}
diff --git a/python/src/com/jetbrains/python/console/PythonConsoleToolWindow.java b/python/src/com/jetbrains/python/console/PythonConsoleToolWindow.java
new file mode 100644
index 000000000000..e8c50e49280a
--- /dev/null
+++ b/python/src/com/jetbrains/python/console/PythonConsoleToolWindow.java
@@ -0,0 +1,127 @@
+package com.jetbrains.python.console;
+
+import com.intellij.execution.ui.RunContentDescriptor;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.SimpleToolWindowPanel;
+import com.intellij.openapi.wm.ToolWindow;
+import com.intellij.openapi.wm.ToolWindowManager;
+import com.intellij.openapi.wm.ex.ToolWindowManagerEx;
+import com.intellij.openapi.wm.ex.ToolWindowManagerListener;
+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 javax.swing.*;
+import java.awt.event.FocusEvent;
+import java.awt.event.FocusListener;
+
+/**
+ * @author traff
+ */
+public class PythonConsoleToolWindow {
+
+ private final Project myProject;
+
+ private boolean myInitialized = false;
+
+ public PythonConsoleToolWindow(Project project) {
+ myProject = project;
+ }
+
+ public static PythonConsoleToolWindow getInstance(@NotNull Project project) {
+ return project.getComponent(PythonConsoleToolWindow.class);
+ }
+
+
+ public void init(final @NotNull ToolWindow toolWindow, final @NotNull RunContentDescriptor contentDescriptor) {
+ addContent(toolWindow, contentDescriptor);
+
+ if (!myInitialized) {
+ doInit(toolWindow);
+ }
+ }
+
+ private void doInit(final ToolWindow toolWindow) {
+ myInitialized = true;
+
+ toolWindow.setToHideOnEmptyContent(true);
+
+ ((ToolWindowManagerEx)ToolWindowManager.getInstance(myProject)).addToolWindowManagerListener(new ToolWindowManagerListener() {
+ @Override
+ public void toolWindowRegistered(@NotNull String id) {
+ }
+
+ @Override
+ public void stateChanged() {
+ ToolWindow window = getToolWindow();
+ if (window != null) {
+ boolean visible = window.isVisible();
+ if (visible && toolWindow.getContentManager().getContentCount() == 0) {
+ RunPythonConsoleAction.runPythonConsole(myProject, null, toolWindow);
+ }
+ }
+ }
+ });
+ }
+
+ private static void addContent(ToolWindow toolWindow, RunContentDescriptor contentDescriptor) {
+ toolWindow.getComponent().putClientProperty(ToolWindowContentUi.HIDE_ID_LABEL, "true");
+
+ Content content = toolWindow.getContentManager().findContent(contentDescriptor.getDisplayName());
+ if (content == null) {
+ content = createContent(contentDescriptor);
+ toolWindow.getContentManager().addContent(content);
+ }
+ else {
+ SimpleToolWindowPanel panel = new SimpleToolWindowPanel(false, true);
+ resetContent(contentDescriptor, panel, content);
+ }
+
+ toolWindow.getContentManager().setSelectedContent(content);
+ }
+
+ public ToolWindow getToolWindow() {
+ return ToolWindowManager.getInstance(myProject).getToolWindow(PythonConsoleToolWindowFactory.ID);
+ }
+
+ private static Content createContent(final @NotNull RunContentDescriptor contentDescriptor) {
+ SimpleToolWindowPanel panel = new SimpleToolWindowPanel(false, true);
+
+ final Content content = ContentFactory.SERVICE.getInstance().createContent(panel, contentDescriptor.getDisplayName(), false);
+ content.setCloseable(true);
+
+ resetContent(contentDescriptor, panel, content);
+
+ return content;
+ }
+
+ 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());
+ }
+
+ private static FocusListener createFocusListener(final ToolWindow toolWindow) {
+ return new FocusListener() {
+ @Override
+ public void focusGained(FocusEvent e) {
+ JComponent component = getComponentToFocus(toolWindow);
+ if (component != null) {
+ component.requestFocusInWindow();
+ }
+ }
+
+ @Override
+ public void focusLost(FocusEvent e) {
+
+ }
+ };
+ }
+
+ private static JComponent getComponentToFocus(ToolWindow window) {
+ return window.getContentManager().getComponent();
+ }
+}
diff --git a/python/src/com/jetbrains/python/console/PythonConsoleToolWindowFactory.java b/python/src/com/jetbrains/python/console/PythonConsoleToolWindowFactory.java
new file mode 100644
index 000000000000..f042a539bc22
--- /dev/null
+++ b/python/src/com/jetbrains/python/console/PythonConsoleToolWindowFactory.java
@@ -0,0 +1,33 @@
+/*
+ * 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.DumbAware;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.wm.ToolWindow;
+import com.intellij.openapi.wm.ToolWindowFactory;
+
+/**
+ * @author traff
+ */
+public class PythonConsoleToolWindowFactory implements ToolWindowFactory, DumbAware {
+ public static final String ID = "Python Console";
+
+ @Override
+ public void createToolWindowContent(Project project, ToolWindow toolWindow) {
+ RunPythonConsoleAction.runPythonConsole(project, null, toolWindow);
+ }
+}
diff --git a/python/src/com/jetbrains/python/console/RunPythonConsoleAction.java b/python/src/com/jetbrains/python/console/RunPythonConsoleAction.java
index 02a153f7a016..566adea43c87 100644
--- a/python/src/com/jetbrains/python/console/RunPythonConsoleAction.java
+++ b/python/src/com/jetbrains/python/console/RunPythonConsoleAction.java
@@ -32,6 +32,7 @@ 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;
@@ -42,6 +43,7 @@ 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;
@@ -77,11 +79,11 @@ public class RunPythonConsoleAction extends AnAction implements DumbAware {
public void actionPerformed(final AnActionEvent e) {
final Project project = e.getData(CommonDataKeys.PROJECT);
- runPythonConsole(project, e.getData(LangDataKeys.MODULE));
+ runPythonConsole(project, e.getData(LangDataKeys.MODULE), null);
}
@NotNull
- public static PydevConsoleRunner runPythonConsole(Project project, Module contextModule) {
+ public static PydevConsoleRunner runPythonConsole(Project project, Module contextModule, @Nullable ToolWindow toolWindow) {
assert project != null : "Project is null";
Pair<Sdk, Module> sdkAndModule = findPythonSdkAndModule(project, contextModule);
@@ -152,7 +154,7 @@ public class RunPythonConsoleAction extends AnAction implements DumbAware {
envs.put(PythonEnvUtil.IPYTHONENABLE, ipythonEnabled);
return PydevConsoleRunner
- .createAndRun(project, sdk, PyConsoleType.PYTHON, workingDir, envs, setupFragment);
+ .createAndRun(project, sdk, PyConsoleType.PYTHON, workingDir, envs, toolWindow, setupFragment);
}
public static PathMappingSettings getMappings(Project project, Sdk sdk) {
diff --git a/python/src/com/jetbrains/python/documentation/EpydocString.java b/python/src/com/jetbrains/python/documentation/EpydocString.java
index 132ef4924b5c..3d0fc3e89d9e 100644
--- a/python/src/com/jetbrains/python/documentation/EpydocString.java
+++ b/python/src/com/jetbrains/python/documentation/EpydocString.java
@@ -44,6 +44,13 @@ public class EpydocString extends StructuredDocStringBase {
"precondition", "postcondition", "invariant", "author", "organization", "copyright", "license", "contact", "summary", "see"
};
+ /**
+ * Empty doc (for {@link #createParameterType(String, String)} probably)
+ */
+ public EpydocString() {
+ this("");
+ }
+
public EpydocString(@NotNull String docstringText) {
super(docstringText, "@");
}
diff --git a/python/src/com/jetbrains/python/documentation/PyDocstringGenerator.java b/python/src/com/jetbrains/python/documentation/PyDocstringGenerator.java
index d09af58f4fc3..c05069098ea6 100644
--- a/python/src/com/jetbrains/python/documentation/PyDocstringGenerator.java
+++ b/python/src/com/jetbrains/python/documentation/PyDocstringGenerator.java
@@ -87,8 +87,6 @@ public class PyDocstringGenerator {
for (PyParameter functionParam : function.getParameterList().getParameters()) {
String paramName = functionParam.getName();
if (!functionParam.isSelf() && !StringUtil.isEmpty(paramName)) {
- assert paramName != null;
-
String type = signature != null ? signature.getArgTypeQualifiedName(paramName) : null;
if (type != null) {
@@ -140,12 +138,9 @@ public class PyDocstringGenerator {
final VirtualFile virtualFile = myFile.getVirtualFile();
if (virtualFile == null) return;
- OpenFileDescriptor descriptor = new OpenFileDescriptor(
- myProject, virtualFile, myDocStringOwner.getTextOffset() + myDocStringOwner.getTextLength()
- );
+ OpenFileDescriptor descriptor = new OpenFileDescriptor(myProject, virtualFile, myDocStringExpression.getTextOffset());
Editor targetEditor = FileEditorManager.getInstance(myProject).openTextEditor(descriptor, true);
if (targetEditor != null) {
- targetEditor.getCaretModel().moveToOffset(myDocStringExpression.getTextOffset());
TemplateManager.getInstance(myProject).startTemplate(targetEditor, template);
}
}
@@ -298,7 +293,7 @@ public class PyDocstringGenerator {
if (myDocStringOwner instanceof PyFunction) {
final PyStatementList statementList = ((PyFunction)myDocStringOwner).getStatementList();
final Document document = PsiDocumentManager.getInstance(myProject).getDocument(getFile());
- if (document != null && statementList != null && myFunction != null && statementList.getStatements().length != 0
+ if (document != null && myFunction != null && statementList.getStatements().length != 0
&& document.getLineNumber(statementList.getTextOffset()) != document.getLineNumber(myFunction.getTextOffset())) {
whitespace = PsiTreeUtil.getPrevSiblingOfType(statementList, PsiWhiteSpace.class);
}
@@ -411,7 +406,7 @@ public class PyDocstringGenerator {
final PyStatementList list = myFunction.getStatementList();
final Document document = PsiDocumentManager.getInstance(myProject).getDocument(getFile());
- if (document != null && list != null) {
+ if (document != null) {
if (document.getLineNumber(list.getTextOffset()) == document.getLineNumber(myFunction.getTextOffset()) ||
list.getStatements().length == 0) {
PyFunction func = elementGenerator.createFromText(LanguageLevel.forElement(myFunction),
diff --git a/python/src/com/jetbrains/python/documentation/PyDocumentationSettings.java b/python/src/com/jetbrains/python/documentation/PyDocumentationSettings.java
index 6ea65405ba1a..49a17efd28a6 100644
--- a/python/src/com/jetbrains/python/documentation/PyDocumentationSettings.java
+++ b/python/src/com/jetbrains/python/documentation/PyDocumentationSettings.java
@@ -15,7 +15,9 @@
*/
package com.jetbrains.python.documentation;
-import com.intellij.openapi.components.*;
+import com.intellij.openapi.components.PersistentStateComponent;
+import com.intellij.openapi.components.State;
+import com.intellij.openapi.components.Storage;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleServiceManager;
import com.intellij.openapi.util.text.StringUtil;
@@ -27,6 +29,7 @@ import com.jetbrains.python.psi.PyFile;
import com.jetbrains.python.psi.PyTargetExpression;
import com.jetbrains.python.psi.impl.PyPsiUtils;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
import java.util.List;
@@ -54,7 +57,7 @@ public class PyDocumentationSettings implements PersistentStateComponent<PyDocum
private boolean isFormat(PsiFile file, final String format) {
if (file instanceof PyFile) {
- PyTargetExpression expr = ((PyFile) file).findTopLevelAttribute(PyNames.DOCFORMAT);
+ PyTargetExpression expr = ((PyFile)file).findTopLevelAttribute(PyNames.DOCFORMAT);
if (expr != null) {
String docformat = PyPsiUtils.strValue(expr.findAssignedValue());
if (docformat != null) {
@@ -88,4 +91,21 @@ public class PyDocumentationSettings implements PersistentStateComponent<PyDocum
public void loadState(PyDocumentationSettings state) {
XmlSerializerUtil.copyBean(state, this);
}
+
+ /**
+ * TODO: Use this factory for the whole document infrastructure to simplify new documentation engine support
+ * Factory that returns appropriate instance of {@link StructuredDocStringBase} if specificed
+ *
+ * @return instance or null if no doctype os set
+ */
+ @Nullable
+ public StructuredDocStringBase getDocString() {
+ if (myDocStringFormat.equals(DocStringFormat.EPYTEXT)) {
+ return new EpydocString();
+ }
+ if (myDocStringFormat.equals(DocStringFormat.REST)) {
+ return new SphinxDocString();
+ }
+ return null;
+ }
}
diff --git a/python/src/com/jetbrains/python/documentation/SphinxDocString.java b/python/src/com/jetbrains/python/documentation/SphinxDocString.java
index ae5c7056a90b..c04e1c9471cf 100644
--- a/python/src/com/jetbrains/python/documentation/SphinxDocString.java
+++ b/python/src/com/jetbrains/python/documentation/SphinxDocString.java
@@ -31,7 +31,14 @@ public class SphinxDocString extends StructuredDocStringBase {
":type", ":raise", ":raises", ":var", ":cvar", ":ivar",
":return", ":returns", ":rtype", ":except", ":exception" };
- public SphinxDocString(@NotNull String docstringText) {
+ /**
+ * Empty doc (for {@link #createParameterType(String, String)} probably)
+ */
+ public SphinxDocString() {
+ this("");
+ }
+
+ public SphinxDocString(@NotNull final String docstringText) {
super(docstringText, ":");
}
diff --git a/python/src/com/jetbrains/python/documentation/StructuredDocStringBase.java b/python/src/com/jetbrains/python/documentation/StructuredDocStringBase.java
index 9f6cecba7a84..36b70ec0f76a 100644
--- a/python/src/com/jetbrains/python/documentation/StructuredDocStringBase.java
+++ b/python/src/com/jetbrains/python/documentation/StructuredDocStringBase.java
@@ -43,18 +43,21 @@ public abstract class StructuredDocStringBase implements StructuredDocString {
private static final Pattern RE_LOOSE_TAG_LINE = Pattern.compile("([a-z]+) ([a-zA-Z_0-9]*):?([^:]*)");
private static final Pattern RE_ARG_TYPE = Pattern.compile("(.*) ([a-zA-Z_0-9]+)");
- public static String[] PARAM_TAGS = new String[] { "param", "parameter", "arg", "argument" };
- public static String[] PARAM_TYPE_TAGS = new String[] { "type" };
- public static String[] VARIABLE_TAGS = new String[] { "ivar", "cvar", "var" };
+ public static String[] PARAM_TAGS = new String[]{"param", "parameter", "arg", "argument"};
+ public static String[] PARAM_TYPE_TAGS = new String[]{"type"};
+ public static String[] VARIABLE_TAGS = new String[]{"ivar", "cvar", "var"};
- public static String[] RAISES_TAGS = new String[] { "raises", "raise", "except", "exception" };
- public static String[] RETURN_TAGS = new String[] { "return", "returns" };
+ public static String[] RAISES_TAGS = new String[]{"raises", "raise", "except", "exception"};
+ public static String[] RETURN_TAGS = new String[]{"return", "returns"};
+ @NotNull
+ private final String myTagPrefix;
public enum ReferenceType {PARAMETER, PARAMETER_TYPE, KEYWORD, VARIABLE, CLASS_VARIABLE, INSTANCE_VARIABLE}
public static String TYPE = "type";
protected StructuredDocStringBase(@NotNull String docStringText, String tagPrefix) {
+ myTagPrefix = tagPrefix;
final Substring docString = new Substring(docStringText);
final List<Substring> lines = docString.splitLines();
final int nlines = lines.size();
@@ -74,6 +77,12 @@ public abstract class StructuredDocStringBase implements StructuredDocString {
}
@Override
+ @NotNull
+ public String createParameterType(@NotNull final String name, @NotNull final String type) {
+ return myTagPrefix + TYPE + String.format(" %s %s", name, type);
+ }
+
+ @Override
public String getDescription() {
return myDescription;
}
@@ -82,8 +91,9 @@ public abstract class StructuredDocStringBase implements StructuredDocString {
public String getSummary() {
final List<String> strings = StringUtil.split(StringUtil.trimLeading(myDescription), "\n", true, false);
if (strings.size() > 1) {
- if (strings.get(1).isEmpty())
+ if (strings.get(1).isEmpty()) {
return strings.get(0);
+ }
}
return "";
}
@@ -216,8 +226,8 @@ public abstract class StructuredDocStringBase implements StructuredDocString {
@Override
@Nullable
- public Substring getParamByNameAndKind(@NotNull String name, String kind) {
- for (Substring s: getTagArguments(kind)) {
+ public Substring getParamByNameAndKind(@NotNull String name, String kind) {
+ for (Substring s : getTagArguments(kind)) {
if (name.equals(s.getValue())) {
return s;
}
diff --git a/python/src/com/jetbrains/python/inspections/PyInspectionVisitor.java b/python/src/com/jetbrains/python/inspections/PyInspectionVisitor.java
index 4584f06ae8d9..6c86e72b1bcd 100644
--- a/python/src/com/jetbrains/python/inspections/PyInspectionVisitor.java
+++ b/python/src/com/jetbrains/python/inspections/PyInspectionVisitor.java
@@ -78,12 +78,12 @@ public abstract class PyInspectionVisitor extends PyElementVisitor {
protected final void registerProblem(@Nullable final PsiElement element,
@NotNull final String message,
- @NotNull final LocalQuickFix quickFix) {
+ @NotNull final LocalQuickFix... quickFixes) {
if (element == null || element.getTextLength() == 0) {
return;
}
if (myHolder != null) {
- myHolder.registerProblem(element, message, quickFix);
+ myHolder.registerProblem(element, message, quickFixes);
}
}
diff --git a/python/src/com/jetbrains/python/inspections/PyShadowingNamesInspection.java b/python/src/com/jetbrains/python/inspections/PyShadowingNamesInspection.java
index 8b6143ffe092..b2ad485c24f8 100644
--- a/python/src/com/jetbrains/python/inspections/PyShadowingNamesInspection.java
+++ b/python/src/com/jetbrains/python/inspections/PyShadowingNamesInspection.java
@@ -99,7 +99,7 @@ public class PyShadowingNamesInspection extends PyInspection {
final ScopeOwner nextOwner = ScopeUtil.getScopeOwner(owner);
if (nextOwner != null) {
final ResolveProcessor processor = new ResolveProcessor(name);
- PyResolveUtil.scopeCrawlUp(processor, nextOwner, null, name, null);
+ PyResolveUtil.scopeCrawlUp(processor, nextOwner, null, name, null, null);
final PsiElement resolved = processor.getResult();
if (resolved != null) {
final PyComprehensionElement comprehension = PsiTreeUtil.getParentOfType(resolved, PyComprehensionElement.class);
diff --git a/python/src/com/jetbrains/python/inspections/quickfix/PyRemoveArgumentQuickFix.java b/python/src/com/jetbrains/python/inspections/quickfix/PyRemoveArgumentQuickFix.java
index 0ae7ac1c7812..708e91743730 100644
--- a/python/src/com/jetbrains/python/inspections/quickfix/PyRemoveArgumentQuickFix.java
+++ b/python/src/com/jetbrains/python/inspections/quickfix/PyRemoveArgumentQuickFix.java
@@ -27,6 +27,7 @@ import com.jetbrains.python.psi.PyExpression;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
+//TODO: Remove pydoc aswell
public class PyRemoveArgumentQuickFix implements LocalQuickFix {
@NotNull
diff --git a/python/src/com/jetbrains/python/psi/PyUtil.java b/python/src/com/jetbrains/python/psi/PyUtil.java
index 877da8df6dd9..cd4e582bb6f2 100644
--- a/python/src/com/jetbrains/python/psi/PyUtil.java
+++ b/python/src/com/jetbrains/python/psi/PyUtil.java
@@ -901,6 +901,20 @@ public class PyUtil {
}
/**
+ * Returns first child psi element with specified element type or {@code null} if no such element exists.
+ * Semantically it's the same as {@code getChildByFilter(element, TokenSet.create(type), 0)}.
+ *
+ * @param element tree parent node
+ * @param type element type expected
+ * @return child element described
+ */
+ @Nullable
+ public static PsiElement getFirstChildOfType(@NotNull final PsiElement element, @NotNull PyElementType type) {
+ final ASTNode child = element.getNode().findChildByType(type);
+ return child != null ? child.getPsi() : null;
+ }
+
+ /**
* If argument is a PsiDirectory, turn it into a PsiFile that points to __init__.py in that directory.
* If there's no __init__.py there, null is returned, there's no point to resolve to a dir which is not a package.
* Alas, resolve() and multiResolve() can't return anything but a PyFile or PsiFileImpl.isPsiUpToDate() would fail.
@@ -1645,6 +1659,11 @@ public class PyUtil {
return Collections2.filter(pyMemberInfos, new ObjectPredicate(false));
}
+ public static boolean isStarImportableFrom(@NotNull String name, @NotNull PyFile file) {
+ final List<String> dunderAll = file.getDunderAll();
+ return dunderAll != null ? dunderAll.contains(name) : !name.startsWith("_");
+ }
+
/**
* Filters only pyclass object (new class)
*/
diff --git a/python/src/com/jetbrains/python/psi/impl/PyElementGeneratorImpl.java b/python/src/com/jetbrains/python/psi/impl/PyElementGeneratorImpl.java
index 744dc9731b41..bb0e29f800a7 100644
--- a/python/src/com/jetbrains/python/psi/impl/PyElementGeneratorImpl.java
+++ b/python/src/com/jetbrains/python/psi/impl/PyElementGeneratorImpl.java
@@ -24,6 +24,7 @@ import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiFileFactory;
+import com.intellij.psi.PsiWhiteSpace;
import com.intellij.psi.impl.PsiFileFactoryImpl;
import com.intellij.psi.impl.source.tree.LeafPsiElement;
import com.intellij.psi.tree.TokenSet;
@@ -86,8 +87,9 @@ public class PyElementGeneratorImpl extends PyElementGenerator {
}
+ @Override
public PyStringLiteralExpression createStringLiteralFromString(@NotNull String unescaped) {
- return createStringLiteralFromString(null, unescaped);
+ return createStringLiteralFromString(null, unescaped, true);
}
public PyStringLiteralExpression createStringLiteral(@NotNull PyStringLiteralExpression oldElement, @NotNull String unescaped) {
@@ -100,7 +102,11 @@ public class PyElementGeneratorImpl extends PyElementGenerator {
}
}
- public PyStringLiteralExpression createStringLiteralFromString(@Nullable PsiFile destination, @NotNull String unescaped) {
+
+ @Override
+ public PyStringLiteralExpression createStringLiteralFromString(@Nullable PsiFile destination,
+ @NotNull String unescaped,
+ final boolean preferUTF8) {
boolean useDouble = !unescaped.contains("\"");
boolean useMulti = unescaped.matches(".*(\r|\n).*");
String quotes;
@@ -115,7 +121,7 @@ public class PyElementGeneratorImpl extends PyElementGenerator {
VirtualFile vfile = destination == null ? null : destination.getVirtualFile();
Charset charset;
if (vfile == null) {
- charset = Charset.forName("US-ASCII");
+ charset = (preferUTF8 ? Charset.forName("UTF-8") : Charset.forName("US-ASCII"));
}
else {
charset = vfile.getCharset();
@@ -191,7 +197,7 @@ public class PyElementGeneratorImpl extends PyElementGenerator {
final LeafPsiElement[] leafs = PsiTreeUtil.getChildrenOfType(list, LeafPsiElement.class);
if (leafs != null) {
final Deque<LeafPsiElement> commas = Queues.newArrayDeque(Collections2.filter(Arrays.asList(leafs), COMMAS_ONLY));
- if (! commas.isEmpty()) {
+ if (!commas.isEmpty()) {
final LeafPsiElement lastComma = commas.getLast();
if (PsiTreeUtil.getNextSiblingOfType(lastComma, PyExpression.class) == null) { //Comma has no expression after it
lastComma.delete();
@@ -297,7 +303,7 @@ public class PyElementGeneratorImpl extends PyElementGenerator {
AccessDirection accessDirection) {
String propertyText;
if (accessDirection == AccessDirection.DELETE) {
- propertyText = "@" + propertyName +".deleter\ndef " + propertyName + "(self):\n del self." + fieldName;
+ propertyText = "@" + propertyName + ".deleter\ndef " + propertyName + "(self):\n del self." + fieldName;
}
else if (accessDirection == AccessDirection.WRITE) {
propertyText = "@" + propertyName + ".setter\ndef " + propertyName + "(self, value):\n self." + fieldName + " = value";
@@ -415,6 +421,12 @@ public class PyElementGeneratorImpl extends PyElementGenerator {
PyExpressionStatement.class, content + "\n");
}
+ @NotNull
+ @Override
+ public PsiElement createNewLine() {
+ return createFromText(LanguageLevel.getDefault(), PsiWhiteSpace.class, " \n\n ");
+ }
+
private static class CommasOnly extends NotNullPredicate<LeafPsiElement> {
@Override
protected boolean applyNotNull(@NotNull final LeafPsiElement input) {
diff --git a/python/src/com/jetbrains/python/psi/impl/PyElsePartImpl.java b/python/src/com/jetbrains/python/psi/impl/PyElsePartImpl.java
index ddd02d5310a3..8c69893c62a8 100644
--- a/python/src/com/jetbrains/python/psi/impl/PyElsePartImpl.java
+++ b/python/src/com/jetbrains/python/psi/impl/PyElsePartImpl.java
@@ -15,26 +15,16 @@
*/
package com.jetbrains.python.psi.impl;
-import com.jetbrains.python.psi.PyElsePart;
-import com.jetbrains.python.psi.PyStatementList;
-import com.jetbrains.python.PyElementTypes;
import com.intellij.lang.ASTNode;
+import com.jetbrains.python.psi.PyElsePart;
/**
* User: dcheryasov
* Date: Mar 15, 2009 9:40:35 PM
*/
-public class PyElsePartImpl extends PyElementImpl implements PyElsePart {
+public class PyElsePartImpl extends PyStatementPartImpl implements PyElsePart {
public PyElsePartImpl(ASTNode astNode) {
super(astNode);
}
-
- public PyStatementList getStatementList() {
- ASTNode n = getNode().findChildByType(PyElementTypes.STATEMENT_LISTS);
- if (n != null) {
- return (PyStatementList)n.getPsi();
- }
- return null;
- }
}
diff --git a/python/src/com/jetbrains/python/psi/impl/PyFileImpl.java b/python/src/com/jetbrains/python/psi/impl/PyFileImpl.java
index 030fcfe3d7e5..02b790622fe7 100644
--- a/python/src/com/jetbrains/python/psi/impl/PyFileImpl.java
+++ b/python/src/com/jetbrains/python/psi/impl/PyFileImpl.java
@@ -477,8 +477,9 @@ public class PyFileImpl extends PsiFileBase implements PyFile, PyExpression {
if (starImportSource != null) {
starImportSource = PyUtil.turnDirIntoInit(starImportSource);
if (starImportSource instanceof PyFile) {
- final PsiElement result = ((PyFile)starImportSource).getElementNamed(name);
- if (result != null) {
+ final PyFile file = (PyFile)starImportSource;
+ final PsiElement result = file.getElementNamed(name);
+ if (result != null && PyUtil.isStarImportableFrom(name, file)) {
return result;
}
}
diff --git a/python/src/com/jetbrains/python/psi/impl/PyFunctionBuilder.java b/python/src/com/jetbrains/python/psi/impl/PyFunctionBuilder.java
index e0cdf87b2aab..9d2be0f62205 100644
--- a/python/src/com/jetbrains/python/psi/impl/PyFunctionBuilder.java
+++ b/python/src/com/jetbrains/python/psi/impl/PyFunctionBuilder.java
@@ -23,6 +23,7 @@ import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
import com.intellij.util.ArrayUtil;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.PythonFileType;
+import com.jetbrains.python.documentation.StructuredDocStringBase;
import com.jetbrains.python.psi.*;
import org.jetbrains.annotations.NotNull;
@@ -82,14 +83,21 @@ public class PyFunctionBuilder {
/**
* Adds docstring to function. Provide doc with out of comment blocks.
*
+ *
* @param docString doc
*/
public void docString(@NotNull final String docString) {
- myDocStringLines = StringUtil.splitByLines(removeIndent(docString));
+ final String[] stringsToAdd = StringUtil.splitByLines(removeIndent(docString));
+ if (myDocStringLines == null) {
+ myDocStringLines = stringsToAdd;
+ }
+ else {
+ myDocStringLines = ArrayUtil.mergeArrays(myDocStringLines, stringsToAdd);
+ }
}
@NotNull
- private String removeIndent(@NotNull final String string) {
+ private static String removeIndent(@NotNull final String string) {
return INDENT_REMOVE_PATTERN.matcher(string).replaceAll("");
}
@@ -97,6 +105,21 @@ public class PyFunctionBuilder {
myName = name;
}
+ /**
+ * Adds param and its type to doc
+ * @param name param name
+ * @param type param type
+ * @param docStyle what docstyle to use to doc param type
+ */
+ @NotNull
+ public PyFunctionBuilder parameterWithType(@NotNull final String name,
+ @NotNull final String type,
+ @NotNull final StructuredDocStringBase docStyle) {
+ parameter(name);
+ docString(docStyle.createParameterType(name, type));
+ return this;
+ }
+
public PyFunctionBuilder parameter(String baseName) {
String name = baseName;
int uniqueIndex = 0;
@@ -173,8 +196,9 @@ public class PyFunctionBuilder {
/**
* Adds decorator with argument
+ *
* @param decoratorName decorator name
- * @param value its argument
+ * @param value its argument
*/
public void decorate(@NotNull final String decoratorName, @NotNull final String value) {
decorate(decoratorName);
diff --git a/python/src/com/jetbrains/python/psi/impl/PyStarImportElementImpl.java b/python/src/com/jetbrains/python/psi/impl/PyStarImportElementImpl.java
index 4011c233e38d..88c3134481a8 100644
--- a/python/src/com/jetbrains/python/psi/impl/PyStarImportElementImpl.java
+++ b/python/src/com/jetbrains/python/psi/impl/PyStarImportElementImpl.java
@@ -52,11 +52,8 @@ public class PyStarImportElementImpl extends PyElementImpl implements PyStarImpo
for (PsiElement importedFile : new HashSet<PsiElement>(importedFiles)) { // resolver gives lots of duplicates
final PsiElement source = PyUtil.turnDirIntoInit(importedFile);
if (source instanceof PyFile) {
- Iterable<PyElement> declaredNames = ((PyFile)source).iterateNames();
- if (((PyFile)source).getDunderAll() == null) {
- declaredNames = excludeUnderscoredNames(declaredNames);
- }
- chain.add(declaredNames);
+ final PyFile sourceFile = (PyFile)source;
+ chain.add(filterStarImportableNames(sourceFile.iterateNames(), sourceFile));
}
}
return chain;
@@ -64,15 +61,13 @@ public class PyStarImportElementImpl extends PyElementImpl implements PyStarImpo
return Collections.emptyList();
}
- private static Iterable<PyElement> excludeUnderscoredNames(Iterable<PyElement> declaredNames) {
+ @NotNull
+ private static Iterable<PyElement> filterStarImportableNames(@NotNull Iterable<PyElement> declaredNames, @NotNull final PyFile file) {
return Iterables.filter(declaredNames, new Predicate<PyElement>() {
@Override
public boolean apply(@Nullable PyElement input) {
final String name = input != null ? input.getName() : null;
- if (name != null && name.startsWith("_")) {
- return false;
- }
- return true;
+ return name != null && PyUtil.isStarImportableFrom(name, file);
}
});
}
@@ -93,11 +88,7 @@ public class PyStarImportElementImpl extends PyElementImpl implements PyStarImpo
final List<? extends RatedResolveResult> results = moduleType.resolveMember(name, null, AccessDirection.READ,
PyResolveContext.defaultContext());
final PsiElement result = results != null && !results.isEmpty() ? results.get(0).getElement() : null;
- if (result != null) {
- final List<String> all = sourceFile.getDunderAll();
- if (all != null ? !all.contains(name) : name.startsWith("_")) {
- continue;
- }
+ if (result != null && PyUtil.isStarImportableFrom(name, sourceFile) ) {
return result;
}
}
diff --git a/python/src/com/jetbrains/python/psi/impl/PyStatementPartImpl.java b/python/src/com/jetbrains/python/psi/impl/PyStatementPartImpl.java
index 46e9a785a9d4..7454d6741382 100644
--- a/python/src/com/jetbrains/python/psi/impl/PyStatementPartImpl.java
+++ b/python/src/com/jetbrains/python/psi/impl/PyStatementPartImpl.java
@@ -15,10 +15,11 @@
*/
package com.jetbrains.python.psi.impl;
-import com.jetbrains.python.psi.PyStatementPart;
-import com.jetbrains.python.psi.PyStatementList;
-import com.jetbrains.python.PyElementTypes;
import com.intellij.lang.ASTNode;
+import com.jetbrains.python.PyElementTypes;
+import com.jetbrains.python.psi.PyStatementList;
+import com.jetbrains.python.psi.PyStatementPart;
+import org.jetbrains.annotations.NotNull;
/**
* Abstract statement part implementation; extracts the statements list.
@@ -30,11 +31,8 @@ public abstract class PyStatementPartImpl extends PyElementImpl implements PySta
super(astNode);
}
+ @NotNull
public PyStatementList getStatementList() {
- ASTNode n = getNode().findChildByType(PyElementTypes.STATEMENT_LISTS);
- if (n != null) {
- return (PyStatementList)n.getPsi();
- }
- return null;
+ return childToPsiNotNull(PyElementTypes.STATEMENT_LIST);
}
}
diff --git a/python/src/com/jetbrains/python/psi/impl/PyWithStatementImpl.java b/python/src/com/jetbrains/python/psi/impl/PyWithStatementImpl.java
index 98cd8e5703d5..3af3771e8ecd 100644
--- a/python/src/com/jetbrains/python/psi/impl/PyWithStatementImpl.java
+++ b/python/src/com/jetbrains/python/psi/impl/PyWithStatementImpl.java
@@ -63,6 +63,14 @@ public class PyWithStatementImpl extends PyElementImpl implements PyWithStatemen
}
public PyWithItem[] getWithItems() {
- return childrenToPsi(WITH_ITEM, PyWithItem.EMPTY_ARRAY);
+ return childrenToPsi(WITH_ITEM, PyWithItem.EMPTY_ARRAY);
+ }
+
+ @Override
+ @NotNull
+ public PyStatementList getStatementList() {
+ final PyStatementList statementList = childToPsi(PyElementTypes.STATEMENT_LIST);
+ assert statementList != null : "Statement list missing for with statement " + getText();
+ return statementList;
}
}
diff --git a/python/src/com/jetbrains/python/psi/resolve/PyResolveUtil.java b/python/src/com/jetbrains/python/psi/resolve/PyResolveUtil.java
index da8c0d618a8b..838a4dbcc75a 100644
--- a/python/src/com/jetbrains/python/psi/resolve/PyResolveUtil.java
+++ b/python/src/com/jetbrains/python/psi/resolve/PyResolveUtil.java
@@ -38,6 +38,11 @@ import com.jetbrains.python.psi.impl.PyPsiUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
/**
* Ref resolution routines.
* User: dcheryasov
@@ -106,16 +111,17 @@ public class PyResolveUtil {
owner = outerScopeOwner;
}
}
- scopeCrawlUp(processor, owner, originalOwner, name, roof);
+ scopeCrawlUp(processor, owner, originalOwner, name, roof, realContext);
}
public static void scopeCrawlUp(@NotNull PsiScopeProcessor processor, @NotNull ScopeOwner scopeOwner, @Nullable String name,
@Nullable PsiElement roof) {
- scopeCrawlUp(processor, scopeOwner, scopeOwner, name, roof);
+ scopeCrawlUp(processor, scopeOwner, scopeOwner, name, roof, null);
}
public static void scopeCrawlUp(@NotNull PsiScopeProcessor processor, @Nullable ScopeOwner scopeOwner,
- @Nullable ScopeOwner originalScopeOwner, @Nullable String name, @Nullable PsiElement roof) {
+ @Nullable ScopeOwner originalScopeOwner, @Nullable String name, @Nullable PsiElement roof,
+ @Nullable final PsiElement anchor) {
while (scopeOwner != null) {
if (!(scopeOwner instanceof PyClass) || scopeOwner == originalScopeOwner) {
final Scope scope = ControlFlowCache.getScope(scopeOwner);
@@ -136,7 +142,31 @@ public class PyResolveUtil {
}
}
}
- for (NameDefiner definer : scope.getImportedNameDefiners()) {
+ List<NameDefiner> definers = new ArrayList<NameDefiner>(scope.getImportedNameDefiners());
+ if (anchor != null && ScopeUtil.getScopeOwner(anchor) == scopeOwner) {
+ final Comparator<NameDefiner> nearestDefinerComparator = new Comparator<NameDefiner>() {
+ @Override
+ public int compare(NameDefiner a, NameDefiner b) {
+ final boolean aIsBefore = PyPsiUtils.isBefore(a, anchor);
+ final boolean bIsBefore = PyPsiUtils.isBefore(b, anchor);
+ final int diff = a.getTextOffset() - b.getTextOffset();
+ if (aIsBefore && bIsBefore) {
+ return -diff;
+ }
+ else if (aIsBefore) {
+ return -1;
+ }
+ else if (bIsBefore) {
+ return 1;
+ }
+ else {
+ return diff;
+ }
+ }
+ };
+ Collections.sort(definers, nearestDefinerComparator);
+ }
+ for (NameDefiner definer : definers) {
if (!processor.execute(definer, ResolveState.initial())) {
found = true;
break;
diff --git a/python/src/com/jetbrains/python/run/PyRemoteTracebackFilter.java b/python/src/com/jetbrains/python/run/PyRemoteTracebackFilter.java
new file mode 100644
index 000000000000..2af1a641de83
--- /dev/null
+++ b/python/src/com/jetbrains/python/run/PyRemoteTracebackFilter.java
@@ -0,0 +1,57 @@
+/*
+ * 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.run;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.LocalFileSystem;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.remote.RemoteProcessHandlerBase;
+import com.intellij.util.PathMappingSettings;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author traff
+ */
+public class PyRemoteTracebackFilter extends PythonTracebackFilter {
+ private final RemoteProcessHandlerBase myHandler;
+
+ public PyRemoteTracebackFilter(Project project, String workingDirectory, RemoteProcessHandlerBase remoteProcessHandler) {
+ super(project, workingDirectory);
+
+ myHandler = remoteProcessHandler;
+ }
+
+ @Override
+ @Nullable
+ protected VirtualFile findFileByName(String fileName) {
+ VirtualFile vFile = super.findFileByName(fileName);
+ if (vFile != null) {
+ return vFile;
+ }
+ for (PathMappingSettings.PathMapping m : myHandler.getMappingSettings().getPathMappings()) {
+ if (m.canReplaceRemote(fileName)) {
+ VirtualFile file = LocalFileSystem.getInstance().findFileByPath(m.mapToLocal(fileName));
+ if (file != null && file.exists()) {
+ return file;
+ }
+ }
+ }
+
+
+
+ return null;
+ }
+}
diff --git a/python/src/com/jetbrains/python/run/PythonCommandLineState.java b/python/src/com/jetbrains/python/run/PythonCommandLineState.java
index 216e68c58e1d..a4d1718bb006 100644
--- a/python/src/com/jetbrains/python/run/PythonCommandLineState.java
+++ b/python/src/com/jetbrains/python/run/PythonCommandLineState.java
@@ -47,6 +47,7 @@ import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.vfs.JarFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.remote.RemoteProcessHandlerBase;
import com.intellij.util.PlatformUtils;
import com.intellij.util.containers.HashMap;
import com.jetbrains.python.PythonHelpersLocator;
@@ -80,7 +81,9 @@ public abstract class PythonCommandLineState extends CommandLineState {
public static final String GROUP_DEBUGGER = "Debugger";
public static final String GROUP_SCRIPT = "Script";
private final AbstractPythonRunConfiguration myConfig;
- private final List<Filter> myFilters;
+
+ private final List<Filter> myFilters = Lists.<Filter>newArrayList(new UrlFilter());
+
private Boolean myMultiprocessDebug = null;
public boolean isDebug() {
@@ -99,15 +102,9 @@ public abstract class PythonCommandLineState extends CommandLineState {
return serverSocket;
}
- public PythonCommandLineState(AbstractPythonRunConfiguration runConfiguration, ExecutionEnvironment env, List<Filter> filters) {
+ public PythonCommandLineState(AbstractPythonRunConfiguration runConfiguration, ExecutionEnvironment env) {
super(env);
myConfig = runConfiguration;
- myFilters = Lists.newArrayList(filters);
- addDefaultFilters();
- }
-
- protected void addDefaultFilters() {
- myFilters.add(new UrlFilter());
}
@Nullable
@@ -134,10 +131,23 @@ public abstract class PythonCommandLineState extends CommandLineState {
protected ConsoleView createAndAttachConsole(Project project, ProcessHandler processHandler, Executor executor)
throws ExecutionException {
final ConsoleView consoleView = createConsoleBuilder(project).filters(myFilters).getConsole();
+
+ addTracebackFilter(project, consoleView, processHandler);
+
consoleView.attachToProcess(processHandler);
return consoleView;
}
+ protected void addTracebackFilter(Project project, ConsoleView consoleView, ProcessHandler processHandler) {
+ if (PySdkUtil.isRemote(myConfig.getSdk())) {
+ assert processHandler instanceof RemoteProcessHandlerBase;
+ consoleView.addMessageFilter(new PyRemoteTracebackFilter(project, myConfig.getWorkingDirectory(), (RemoteProcessHandlerBase) processHandler));
+ }
+ else {
+ consoleView.addMessageFilter(new PythonTracebackFilter(project, myConfig.getWorkingDirectory()));
+ }
+ }
+
private TextConsoleBuilder createConsoleBuilder(Project project) {
if (isDebug()) {
return new PyDebugConsoleBuilder(project, PythonSdkType.findSdkByPath(myConfig.getInterpreterPath()));
diff --git a/python/src/com/jetbrains/python/run/PythonRunConfiguration.java b/python/src/com/jetbrains/python/run/PythonRunConfiguration.java
index bbba44441227..118c27beb548 100644
--- a/python/src/com/jetbrains/python/run/PythonRunConfiguration.java
+++ b/python/src/com/jetbrains/python/run/PythonRunConfiguration.java
@@ -15,11 +15,9 @@
*/
package com.jetbrains.python.run;
-import com.google.common.collect.Lists;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.Executor;
import com.intellij.execution.configurations.*;
-import com.intellij.execution.filters.Filter;
import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.openapi.components.PathMacroManager;
import com.intellij.openapi.options.SettingsEditor;
@@ -40,7 +38,6 @@ import org.jdom.Element;
import org.jetbrains.annotations.NotNull;
import java.io.File;
-import java.util.List;
/**
* @author yole
@@ -64,10 +61,7 @@ public class PythonRunConfiguration extends AbstractPythonRunConfiguration
}
public RunProfileState getState(@NotNull final Executor executor, @NotNull final ExecutionEnvironment env) throws ExecutionException {
- List<Filter> filters = Lists.newArrayList();
- filters.add(new PythonTracebackFilter(getProject(), getWorkingDirectory()));
-
- return new PythonScriptCommandLineState(this, env, filters);
+ return new PythonScriptCommandLineState(this, env);
}
public void checkConfiguration() throws RuntimeConfigurationException {
diff --git a/python/src/com/jetbrains/python/run/PythonScriptCommandLineState.java b/python/src/com/jetbrains/python/run/PythonScriptCommandLineState.java
index 53ef554838ae..81d2daaee3d0 100644
--- a/python/src/com/jetbrains/python/run/PythonScriptCommandLineState.java
+++ b/python/src/com/jetbrains/python/run/PythonScriptCommandLineState.java
@@ -18,20 +18,17 @@ package com.jetbrains.python.run;
import com.intellij.execution.configurations.GeneralCommandLine;
import com.intellij.execution.configurations.ParametersList;
import com.intellij.execution.configurations.ParamsGroup;
-import com.intellij.execution.filters.Filter;
import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.openapi.util.text.StringUtil;
-import java.util.List;
-
/**
* @author yole
*/
public class PythonScriptCommandLineState extends PythonCommandLineState {
private final PythonRunConfiguration myConfig;
- public PythonScriptCommandLineState(PythonRunConfiguration runConfiguration, ExecutionEnvironment env, List<Filter> filters) {
- super(runConfiguration, env, filters);
+ public PythonScriptCommandLineState(PythonRunConfiguration runConfiguration, ExecutionEnvironment env) {
+ super(runConfiguration, env);
myConfig = runConfiguration;
}
diff --git a/python/src/com/jetbrains/python/run/PythonTracebackFilter.java b/python/src/com/jetbrains/python/run/PythonTracebackFilter.java
index e3bd879cfe81..73a3abd222b1 100644
--- a/python/src/com/jetbrains/python/run/PythonTracebackFilter.java
+++ b/python/src/com/jetbrains/python/run/PythonTracebackFilter.java
@@ -51,10 +51,7 @@ public class PythonTracebackFilter implements Filter {
if (matcher.find()) {
String fileName = matcher.group(1).replace('\\', '/');
int lineNumber = Integer.parseInt(matcher.group(2));
- VirtualFile vFile = LocalFileSystem.getInstance().findFileByPath(fileName);
- if (vFile == null && !StringUtil.isEmptyOrSpaces(myWorkingDirectory)) {
- vFile = LocalFileSystem.getInstance().findFileByIoFile(new File(myWorkingDirectory, fileName));
- }
+ VirtualFile vFile = findFileByName(fileName);
if (vFile != null) {
OpenFileHyperlinkInfo hyperlink = new OpenFileHyperlinkInfo(myProject, vFile, lineNumber - 1);
@@ -66,4 +63,13 @@ public class PythonTracebackFilter implements Filter {
}
return null;
}
+
+ @Nullable
+ protected VirtualFile findFileByName(String fileName) {
+ VirtualFile vFile = LocalFileSystem.getInstance().findFileByPath(fileName);
+ if (vFile == null && !StringUtil.isEmptyOrSpaces(myWorkingDirectory)) {
+ vFile = LocalFileSystem.getInstance().findFileByIoFile(new File(myWorkingDirectory, fileName));
+ }
+ return vFile;
+ }
}
diff --git a/python/src/com/jetbrains/python/sdk/PyDetectedSdk.java b/python/src/com/jetbrains/python/sdk/PyDetectedSdk.java
index 098462ef44fb..f288bd99ce3b 100644
--- a/python/src/com/jetbrains/python/sdk/PyDetectedSdk.java
+++ b/python/src/com/jetbrains/python/sdk/PyDetectedSdk.java
@@ -8,4 +8,8 @@ public class PyDetectedSdk extends ProjectJdkImpl {
setHomePath(name);
}
+ @Override
+ public String getVersionString() {
+ return "";
+ }
}
diff --git a/python/src/com/jetbrains/python/testing/PythonTestCommandLineStateBase.java b/python/src/com/jetbrains/python/testing/PythonTestCommandLineStateBase.java
index 39e4e96b836b..1a5aafe59051 100644
--- a/python/src/com/jetbrains/python/testing/PythonTestCommandLineStateBase.java
+++ b/python/src/com/jetbrains/python/testing/PythonTestCommandLineStateBase.java
@@ -22,7 +22,6 @@ import com.intellij.execution.ExecutionResult;
import com.intellij.execution.Executor;
import com.intellij.execution.configurations.GeneralCommandLine;
import com.intellij.execution.configurations.ParamsGroup;
-import com.intellij.execution.filters.Filter;
import com.intellij.execution.process.ProcessHandler;
import com.intellij.execution.runners.ExecutionEnvironment;
import com.intellij.execution.testframework.TestFrameworkRunningModel;
@@ -42,12 +41,10 @@ import com.jetbrains.python.console.PythonDebugLanguageConsoleView;
import com.jetbrains.python.run.AbstractPythonRunConfiguration;
import com.jetbrains.python.run.CommandLinePatcher;
import com.jetbrains.python.run.PythonCommandLineState;
-import com.jetbrains.python.run.PythonTracebackFilter;
import com.jetbrains.python.sdk.PythonSdkType;
import org.jetbrains.annotations.NotNull;
import java.io.File;
-import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -62,7 +59,7 @@ public abstract class PythonTestCommandLineStateBase extends PythonCommandLineSt
}
public PythonTestCommandLineStateBase(AbstractPythonRunConfiguration configuration, ExecutionEnvironment env) {
- super(configuration, env, Collections.<Filter>emptyList());
+ super(configuration, env);
myConfiguration = configuration;
}
@@ -77,7 +74,6 @@ public abstract class PythonTestCommandLineStateBase extends PythonCommandLineSt
consoleProperties,
getEnvironment());
final ConsoleView consoleView = new PythonDebugLanguageConsoleView(project, PythonSdkType.findSdkByPath(myConfiguration.getInterpreterPath()), testsOutputConsoleView);
- consoleView.addMessageFilter(new PythonTracebackFilter(project, myConfiguration.getWorkingDirectory()));
consoleView.attachToProcess(processHandler);
return consoleView;
}
@@ -85,7 +81,7 @@ public abstract class PythonTestCommandLineStateBase extends PythonCommandLineSt
processHandler,
consoleProperties,
getEnvironment());
- consoleView.addMessageFilter(new PythonTracebackFilter(project, myConfiguration.getWorkingDirectory()));
+ addTracebackFilter(project, consoleView, processHandler);
return consoleView;
}
diff --git a/python/src/com/jetbrains/python/testing/pytest/PyTestCommandLineState.java b/python/src/com/jetbrains/python/testing/pytest/PyTestCommandLineState.java
index 3637f37ca91c..581e688de1d5 100644
--- a/python/src/com/jetbrains/python/testing/pytest/PyTestCommandLineState.java
+++ b/python/src/com/jetbrains/python/testing/pytest/PyTestCommandLineState.java
@@ -81,7 +81,7 @@ public class PyTestCommandLineState extends PythonTestCommandLineStateBase {
protected ConsoleView createAndAttachConsole(Project project, ProcessHandler processHandler, Executor executor)
throws ExecutionException {
final ConsoleView consoleView = super.createAndAttachConsole(project, processHandler, executor);
- consoleView.addMessageFilter(new PyTestTracebackFilter(project, myConfiguration.getWorkingDirectory()));
+ addTracebackFilter(project, consoleView, processHandler);
return consoleView;
}
}
diff --git a/python/testData/codeInsight/smartEnter/withExpressionMissing.py b/python/testData/codeInsight/smartEnter/withExpressionMissing.py
new file mode 100644
index 000000000000..25526ea4e55f
--- /dev/null
+++ b/python/testData/codeInsight/smartEnter/withExpressionMissing.py
@@ -0,0 +1 @@
+with <caret> \ No newline at end of file
diff --git a/python/testData/codeInsight/smartEnter/withExpressionMissing_after.py b/python/testData/codeInsight/smartEnter/withExpressionMissing_after.py
new file mode 100644
index 000000000000..722e64a456b8
--- /dev/null
+++ b/python/testData/codeInsight/smartEnter/withExpressionMissing_after.py
@@ -0,0 +1 @@
+with <caret>: \ No newline at end of file
diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/UnusedImportBeforeStarDunderAll/a.py b/python/testData/inspections/PyUnresolvedReferencesInspection/UnusedImportBeforeStarDunderAll/a.py
new file mode 100644
index 000000000000..6d444960ee9d
--- /dev/null
+++ b/python/testData/inspections/PyUnresolvedReferencesInspection/UnusedImportBeforeStarDunderAll/a.py
@@ -0,0 +1,3 @@
+import b
+<warning descr="Unused import statement">from p1 import *</warning>
+print(b)
diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/UnusedImportBeforeStarDunderAll/b.py b/python/testData/inspections/PyUnresolvedReferencesInspection/UnusedImportBeforeStarDunderAll/b.py
new file mode 100644
index 000000000000..e69de29bb2d1
--- /dev/null
+++ b/python/testData/inspections/PyUnresolvedReferencesInspection/UnusedImportBeforeStarDunderAll/b.py
diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/UnusedImportBeforeStarDunderAll/p1/__init__.py b/python/testData/inspections/PyUnresolvedReferencesInspection/UnusedImportBeforeStarDunderAll/p1/__init__.py
new file mode 100644
index 000000000000..31f7e1aa969c
--- /dev/null
+++ b/python/testData/inspections/PyUnresolvedReferencesInspection/UnusedImportBeforeStarDunderAll/p1/__init__.py
@@ -0,0 +1 @@
+from p1.m1 import *
diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/UnusedImportBeforeStarDunderAll/p1/m1.py b/python/testData/inspections/PyUnresolvedReferencesInspection/UnusedImportBeforeStarDunderAll/p1/m1.py
new file mode 100644
index 000000000000..3f8018db03f2
--- /dev/null
+++ b/python/testData/inspections/PyUnresolvedReferencesInspection/UnusedImportBeforeStarDunderAll/p1/m1.py
@@ -0,0 +1,2 @@
+import b
+__all__ = []
diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/UnusedImportBeforeStarImport/a.py b/python/testData/inspections/PyUnresolvedReferencesInspection/UnusedImportBeforeStarImport/a.py
new file mode 100644
index 000000000000..7495833ba2c8
--- /dev/null
+++ b/python/testData/inspections/PyUnresolvedReferencesInspection/UnusedImportBeforeStarImport/a.py
@@ -0,0 +1,5 @@
+import m1
+
+print(m1.foo)
+
+<warning descr="Unused import statement">from m2 import *</warning>
diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/UnusedImportBeforeStarImport/m1.py b/python/testData/inspections/PyUnresolvedReferencesInspection/UnusedImportBeforeStarImport/m1.py
new file mode 100644
index 000000000000..1cfbade675f2
--- /dev/null
+++ b/python/testData/inspections/PyUnresolvedReferencesInspection/UnusedImportBeforeStarImport/m1.py
@@ -0,0 +1 @@
+foo = 0
diff --git a/python/testData/inspections/PyUnresolvedReferencesInspection/UnusedImportBeforeStarImport/m2.py b/python/testData/inspections/PyUnresolvedReferencesInspection/UnusedImportBeforeStarImport/m2.py
new file mode 100644
index 000000000000..5215a01f7c64
--- /dev/null
+++ b/python/testData/inspections/PyUnresolvedReferencesInspection/UnusedImportBeforeStarImport/m2.py
@@ -0,0 +1 @@
+import m1
diff --git a/python/testSrc/com/jetbrains/python/PySmartEnterTest.java b/python/testSrc/com/jetbrains/python/PySmartEnterTest.java
index b8421c9d7e18..09e0317051a0 100644
--- a/python/testSrc/com/jetbrains/python/PySmartEnterTest.java
+++ b/python/testSrc/com/jetbrains/python/PySmartEnterTest.java
@@ -179,4 +179,9 @@ public class PySmartEnterTest extends PyTestCase {
public void testWithTargetIncomplete() {
doTest();
}
+
+ // PY-12877
+ public void testWithExpressionMissing() {
+ doTest();
+ }
}
diff --git a/python/testSrc/com/jetbrains/python/fixtures/PyTestCase.java b/python/testSrc/com/jetbrains/python/fixtures/PyTestCase.java
index 6850c3b049a8..45d582a783a8 100644
--- a/python/testSrc/com/jetbrains/python/fixtures/PyTestCase.java
+++ b/python/testSrc/com/jetbrains/python/fixtures/PyTestCase.java
@@ -15,6 +15,9 @@
*/
package com.jetbrains.python.fixtures;
+import com.intellij.codeInsight.intention.IntentionAction;
+import com.intellij.codeInspection.LocalQuickFix;
+import com.intellij.codeInspection.ex.QuickFixWrapper;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.extensions.Extensions;
import com.intellij.openapi.module.Module;
@@ -28,6 +31,7 @@ import com.intellij.openapi.roots.libraries.Library;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDocumentManager;
+import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiReference;
import com.intellij.testFramework.LightProjectDescriptor;
import com.intellij.testFramework.PlatformTestCase;
@@ -131,8 +135,32 @@ public abstract class PyTestCase extends UsefulTestCase {
}
}
- protected static void assertNotParsed(PyFile file) {
- assertNull(PARSED_ERROR_MSG, ((PyFileImpl)file).getTreeElement());
+ /**
+ * Searches for quickfix itetion by its class
+ * @param clazz quick fix class
+ * @param <T> quick fix class
+ * @return quick fix or null if nothing found
+ */
+ @Nullable
+ public <T extends LocalQuickFix> T findQuickFixByClassInIntentions(@NotNull final Class<T> clazz) {
+
+ for (final IntentionAction action : myFixture.getAvailableIntentions()) {
+ if ((action instanceof QuickFixWrapper)) {
+ final QuickFixWrapper quickFixWrapper = (QuickFixWrapper)action;
+ final LocalQuickFix fix = quickFixWrapper.getFix();
+ if (clazz.isInstance(fix)) {
+ @SuppressWarnings("unchecked")
+ final T result = (T)fix;
+ return result;
+ }
+ }
+ }
+ return null;
+ }
+
+
+ protected static void assertNotParsed(PyFile file) {
+ assertNull(PARSED_ERROR_MSG, ((PyFileImpl)file).getTreeElement());
}
/**
@@ -144,6 +172,18 @@ public abstract class PyTestCase extends UsefulTestCase {
return myFixture.findElementByText("class " + name, PyClass.class);
}
+ /**
+ * Finds some text and moves cursor to it (if found)
+ *
+ * @param testToFind text to find
+ * @throws AssertionError if element not found
+ */
+ protected void moveByText(@NotNull final String testToFind) {
+ final PsiElement element = myFixture.findElementByText(testToFind, PsiElement.class);
+ assert element != null : "No element found by text: " + testToFind;
+ myFixture.getEditor().getCaretModel().moveToOffset(element.getTextOffset());
+ }
+
protected static class PyLightProjectDescriptor implements LightProjectDescriptor {
private final String myPythonVersion;
diff --git a/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java
index 0c5e87ab1001..a0a2a1232b2e 100644
--- a/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java
+++ b/python/testSrc/com/jetbrains/python/inspections/PyUnresolvedReferencesInspectionTest.java
@@ -371,6 +371,16 @@ public class PyUnresolvedReferencesInspectionTest extends PyInspectionTestCase {
doMultiFileTest();
}
+ // PY-11472
+ public void testUnusedImportBeforeStarImport() {
+ doMultiFileTest();
+ }
+
+ // PY-13585
+ public void testUnusedImportBeforeStarDunderAll() {
+ doMultiFileTest();
+ }
+
@NotNull
@Override
protected Class<? extends PyInspection> getInspectionClass() {